Pytorch 基础使用
约 887 个字 228 行代码 预计阅读时间 6 分钟
2.1 Pytorch
- 导入相关包
In [1]: import torch
得知我们用Pytorch处理数据,且torch是包就可以了。
下面引入一些数据处理中的概念:
- 张量表示一个由数值组成的数组
- 可能有多个维度:
- 具有一个轴的张量对应数学上的向量(vector)
- 具有两个轴的张量对应数学上的矩阵(matrix)
- 具有两个轴以上的张量没有特殊的数学名称。
- 创建行向量:
arange
命令:
In [2]: x=torch.arange(12)
In [4]: x
Out[4]: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
一些计算方法:
In [5]: x.shape # Tensor.shape, 计算形状
Out[5]: torch.Size([12])
In [7]: x.numel() # numel()函数用来计算个数
Out[7]: 12
它们的区别:详见下面这个张量:
In [18]: X
Out[18]:
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
In [19]: X.shape
Out[19]: torch.Size([4, 3])
In [20]: X.numel()
Out[20]: 12
- 改变x的形状,
reshape
函数 - 由于x中的元素个数是一直的,知道一条边,一定能知道另一条边,这样其实可以相除得到解。
In [8]: x.reshape(3,4)
Out[8]:
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [9]: x.reshape(3,-1)
Out[9]:
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [10]: x.reshape(4,-1)
Out[10]:
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
reshape
和之前接触的pandas
库的很多函数一样,貌似他们都不能对原数值进行改变。
In [14]: x.reshape(4,-1)
Out[14]:
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
In [15]: x.shape
Out[15]: torch.Size([12])
In [16]: x
Out[16]: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
- 新建向量的方式
In [21]: torch.zeros((2,3,4))
Out[21]:
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.]]])
In [22]: torch.ones((2,3,4))
Out[22]:
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.]]])
In [23]: torch.randn(3,4)
Out[23]:
tensor([[-0.1029, 0.6583, -1.2416, 0.5249],
[-0.0954, -0.0340, -1.6645, 0.0284],
[-0.2822, -0.1775, 0.5885, -0.4513]])
# 也可以强制赋值
In [24]: torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
Out[24]:
tensor([[2, 1, 4, 3],
[1, 2, 3, 4],
[4, 3, 2, 1]])
张量之间的加减运算: 逐项进行实数之间的操作:
In [25]: x = torch.tensor([1.0, 2, 4, 8])
In [26]: y = torch.tensor([2, 2, 2, 2])
In [27]: x+y
Out[27]: tensor([ 3., 4., 6., 10.])
In [28]: x-y
Out[28]: tensor([-1., 0., 2., 6.])
In [29]: x*y
Out[29]: tensor([ 2., 4., 8., 16.])
In [30]: x/y
Out[30]: tensor([0.5000, 1.0000, 2.0000, 4.0000])
In [31]: x**y
Out[31]: tensor([ 1., 4., 16., 64.])
In [32]: torch.exp(x)
Out[32]: tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])
还可以执行线性代数运算,包括向量点积和矩阵乘法
张量的连结:
In [33]: 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]])
...: torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
Out[33]:
(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.]]))
布尔值组成的张量:
In [34]: X == Y
Out[34]:
tensor([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])
对张量中的所有元素进行求和,会产生一个单元素张量。
In [35]: X.sum()
Out[35]: tensor(66.)
-
广播机制
-
通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
- 对生成的数组执行按元素操作。
In [36]: a = torch.arange(3).reshape((3,1))
In [37]: b = torch.arange(2).reshape((1,2))
In [39]: a
Out[39]:
tensor([[0],
[1],
[2]])
In [40]: b
Out[40]: tensor([[0, 1]])
此时,执行a+b
由于a
和b
分别是3×1和1×2矩阵,如果让它们相加,它们的形状不匹配。 我们将两个矩阵_广播_为一个更大的3×2矩阵,如下所示:矩阵a
将复制列, 矩阵b
将复制行,然后再按元素相加
如果有值的话:
In [42]: a=torch.ones((3,1))
In [43]: b=torch.ones((1,2))
In [44]: a+b
Out[44]:
tensor([[2., 2.],
[2., 2.],
[2., 2.]])
索引和切片:
和python中数组的规则是一样的
In [45]: X
Out[45]:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
In [46]: X[-1]
Out[46]: tensor([ 8., 9., 10., 11.])
In [47]: X[1:3]
Out[47]:
tensor([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
除读取外,我们还可以通过指定索引来将元素写入矩阵。
语法:X[行标,列标]
In [48]: X[1, 2] = 9
...: X
Out[48]:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 9., 7.],
[ 8., 9., 10., 11.]])
- 为多个元素赋值相同的值: 索引所有元素,然后为它们赋值。
- 例如:
[0:2, :]
访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。 - 虽然我们讨论的是矩阵的索引,但这也适用于向量和超过2个维度的张量。
- 例如:
In [49]: X[0:2, :] = 12
In [50]: X
Out[50]:
tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])
Pytorch在运行过程中,会通过动态分配变量内存的方式节省空间/
-> Y的值如果有所变化,调用id()
的结果是不一样的。
In [54]: Y
Out[54]:
tensor([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 1.]])
In [55]: before = id(Y)
In [56]: before
Out[56]: 1706136657088
In [57]: Y=Y+X
In [58]: after = id(Y)
In [59]: after
Out[59]: 1706133978432
In [60]: before == after
Out[60]: False
注意!
这可能是不可取的,原因有两个:
- 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
- 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
执行原地操作的方法:可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>
In [61]: Z = torch.zeros_like(Y)
In [62]: Z
Out[62]:
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
In [63]: print('id(Z):',id(Z))
id(Z): 1706136530176
In [64]: Z[:]=X+Y
In [65]: print('id(Z):',id(Z))
id(Z): 1706136530176
如果在后续计算中没有重复使用X
, 我们也可以使用X[:] = X + Y
或X += Y
来减少操作的内存开销
In [66]: before = id(X)
...: X += Y
...: id(X) == before
Out[66]: True
张量和numpy
数组之间的转换
In [67]: A=X.numpy()
In [68]: B=torch.tensor(A)
In [69]: type(A),type(B)
Out[69]: (numpy.ndarray, torch.Tensor)
- 要将大小为1的张量转换为Python标量,我们可以调用
item
函数或Python的内置函数。In [70]: a = torch.tensor([3.5])
...: a, a.item(), float(a), int(a)
Out[70]: (tensor([3.5000]), 3.5, 3.5, 3)
如果是numpy的话,还是矢量?只有a.item()
这样的运算才能==完全只提出数字==。
In [71]: a=torch.tensor([3.5])
In [72]: A=a.numpy()
In [73]: A
Out[73]: array([3.5], dtype=float32)