Skip to content

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

由于ab分别是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

注意!

这可能是不可取的,原因有两个:

  1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
  2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。

执行原地操作的方法:可以使用切片表示法将操作的结果分配给先前分配的数组,例如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 + YX += 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)

Comments