《动手学深度学习 Pytorch版》 2.1 数据操作

2.1.1 数据操作

注意此处 import 的不是 pytorch 而是 torch。Torch 是采用 lua 语言实现的,从某种程度上说,PyTorch 是 Torch 的 Python 版本。(当然还是有部分差别的)

import torch

张量(tensor)可以说是机器学习中的数组,直观的看张量像是封装了数组以及一系列相关操作的数据结构。

一个轴的张量对应数学上的向量(vector),两个轴的张量对应数学上的矩阵(matrix),两个轴以上的张量还没有特定的数学名称。

可以调用 arange 函数创建指定范围、元素类型的行向量。除非额外指定,否则张量默认存储在内存中并采用基于CPU的运算。

可以通过张量的 shape 属性访问张量的形状,即张量由外向内的维度。

可以调用 numel 函数获取张量中的元素数量。

x = torch.arange(12)
x, x.shape, x.numel()

(tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
torch.Size([12]),
12)

可以调用 reshape 函数改变张量的形状而不改变元素数量与元素值。其中参数可用 -1 替代可自行计算补全参数

X = x.reshape(3, 4) # (3, 4)等价于(-1, 4)等价于(3, -1)
X, X.shape, X.numel()

(tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]]),
torch.Size([3, 4]),
12)

可以调用 zeros 函数和 ones 函数创建指定形状的元素全0/1的张量。

可以调用 randn 函数创建指定形状的元素从均值为0、标准差为1的正态分布中随机采样的张量。

当然也可以调用 tensor 函数直接使用列表创建张量。

torch.zeros((2,3,4)), torch.ones((2,3,4))

(tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],

[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]]),
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],

[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]]))

torch.randn(3, 4), torch.randn((2,3,4))

(tensor([[ 1.6222, 0.7231, -1.4024, 0.7588],
[-0.1125, 0.5207, 0.6089, 1.2352],
[ 2.0539, 1.1926, 0.5572, 0.6947]]),
tensor([[[-0.7136, -1.2191, -1.2900, -1.5955],
[ 0.4796, -1.0602, -0.7268, -0.4906],
[ 0.5334, -0.3188, -1.2223, 1.4670]],

[[ 0.3615, 0.4890, -1.0624, -0.3882],
[ 0.1696, -1.2423, 0.4243, 0.7088],
[-0.2190, 1.1008, 1.2330, 0.3669]]]))

torch.tensor([[2, 1, 4, 3], [9, 1, 4, 3], [2, 1, 0, 3]])

tensor([[2, 1, 4, 3],
[9, 1, 4, 3],
[2, 1, 0, 3]])

2.1.2 运算符

常见的标准运算符都可以进行按元素(elementwise)运算,按元素方式可以应用于更多的计算,包括 exp 函数。

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y, torch.exp(x) # **是求幂运算

(tensor([ 3., 4., 6., 10.]),
tensor([-1., 0., 2., 6.]),
tensor([ 2., 4., 8., 16.]),
tensor([0.5000, 1.0000, 2.0000, 4.0000]),
tensor([ 1., 4., 16., 64.]),
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03]))

可以调用 cat 函数把多个张量按某个轴连接(concatenate)在一起。

X = torch.arange(12, dtype=torch.float32).reshape((3, 4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
X, Y, torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)

(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
tensor([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 1.]]),
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[ 2., 1., 4., 3.],
[ 1., 2., 3., 4.],
[ 4., 3., 2., 1.]]),
tensor([[ 0., 1., 2., 3., 2., 1., 4., 3.],
[ 4., 5., 6., 7., 1., 2., 3., 4.],
[ 8., 9., 10., 11., 4., 3., 2., 1.]]))

可以通过逻辑运算符构建二元张量,例如可以使用 == 符号比较每个位置元素的值。

调用 sum 函数会对张量中所有元素求和后产生一个单元素张量。

X == Y

tensor([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])

X.sum()

tensor(66.)

2.1.3 广播机制

在某些情况下,不同形状的张量可以通过广播机制(broadcasting mechanism)自动扩展以进行按元素操作。

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b, a + b

(tensor([[0],
[1],
[2]]),
tensor([[0, 1]]),
tensor([[0, 1],
[1, 2],
[2, 3]]))

2.1.4 索引和切片

和任何的其他的Python数组一样,张量可以通过索引访问、写入等操作。

X, X[-1], X[1:3]

(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
tensor([ 8., 9., 10., 11.]),
tensor([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]))

X[1, 2] = 9
X

tensor([[ 0., 1., 2., 3.],
[ 4., 5., 9., 7.],
[ 8., 9., 10., 11.]])

X[0:2, :] = 12 # 访问第1行和第2行的所有元素
X

tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])

2.1.5 节省内存

直接使用 Y = Y + X 则会在运算过程中给Y重新申请内存。
执行原地操作有两种方式:

  • 使用切片表示法将操作结果分配给原数组,例如Y[ : ] = <expression>
  • 也可以使用 += 运算符进行原地操作
before = id(Y)
Y = Y + X # 新Y会重新分配内存
id(Y) == before

False

Z = torch.zeros_like(Y)
print("id(Z):", id(Z))
Z[:] = X + Y # 使用切片表示法将操作结果分配给原数组以实现原地操作
print("id(Z):", id(Z))

id(Z): 2695200905792
id(Z): 2695200905792

before = id(X)
X += Y # 使用 += 运算符进行原地操作
id(X) == before

True

2.1.6 转换为其他Python对象

PyTorch张量可以很容易转换为NumPy张量。要将大小为1的张量转换为Python标量可以调用 item 函数,也可以使用Python内置的强制类型转换。

A = X.numpy()
B = torch.tensor(A)
type(A), type(B)

(numpy.ndarray, torch.Tensor)

a = torch.tensor([3.5])
a, a.item(), float(a), int(a)

(tensor([3.5000]), 3.5, 3.5, 3)

练习

(1)将 == 替换为 > 或 < 得到的仍是按元素进行逻辑运算的结果。

X < Y, X > Y

(tensor([[False, False, False, False],
[False, False, False, False],
[False, False, False, False]]),
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True]]))


(2)可广播的一对张量需满足以下规则:

  • 每个张量至少有一个维度。
  • 迭代维度尺寸时,从尾部的维度开始,维度尺寸满足以下某一情况:
    • 相等,
    • 其中一个张量的维度尺寸为 1 ,
    • 其中一个张量不存在这个维度。
x = torch.ones(5,3,4,1)
y = torch.ones(  3,1,1)
(x + y).shape # 可广播

torch.Size([5, 3, 4, 1])

x = torch.empty((0,))
y = torch.empty(2, 2)
# (x + y).shape # 不可广播 不满足第一条规则
x = torch.ones(5, 2, 4, 1)
y = torch.ones(   3, 1, 1)
# (x + y).shape # 不可广播 不满足第二条规则
posted @ 2023-05-23 10:46  AncilunKiang  阅读(127)  评论(0)    收藏  举报