PyTorch是一个基于Python的科学计算库,它有以下特点:
- 类似于NumPy,但是它可以使用GPU
- 可以用它定义深度学习模型,可以灵活地进行深度学习模型的训练和使用
Tensor
Tensor类似于numpy的ndarray,唯一的区别是tensor可以在gpu上加速计算。
1 | from __future__ import print_function |
构造一个初始化5 * 3 的矩阵:
1 | x = torch.empty(5, 3) |
tensor([[1.2584e-23, 4.5800e-41, 5.5303e-22],
[4.5800e-41, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 4.2603e-21, 1.4013e-45],
[5.5193e-22, 4.5800e-41, 0.0000e+00]])
构建一个随机初始化的矩阵:
1 | # rand 生成的都是正数,randn生成的数有正有负 |
tensor([[0.9556, 0.5058, 0.0706],
[0.2486, 0.8324, 0.8027],
[0.4677, 0.9293, 0.5427],
[0.7236, 0.9158, 0.4789],
[0.2861, 0.2748, 0.8777]])
tensor([[ 1.8956, 0.2862, 0.0363],
[ 0.8295, -0.8973, -0.7135],
[-0.7953, 0.7180, -0.0232],
[-0.3304, -0.0801, -0.1258],
[ 0.3730, -0.0505, 0.9129]])
构建一个全部为0,类型为long的矩阵:
1 | x = torch.zeros(5, 3, dtype=torch.long) |
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
从数据直接直接构建tensor:
1 | x = torch.tensor([5.5, 3]) |
tensor([5.5000, 3.0000])
也可以从一个已有的tensor构建一个tensor。这些方法会重用原来tensor的特征,例如,数据类型,除非提供新的数据。
1 | # 构建新的矩阵的同时可以改变形状, 数据类型 |
tensor([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]], dtype=torch.float64)
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]], dtype=torch.float64)
tensor([[-0.3154, -0.2669, -0.0327, -0.0288, 0.6338],
[ 1.8162, 1.5946, -1.3593, -0.8494, 1.5819],
[-0.2012, -1.8754, -0.4515, -0.6153, 0.0842]])
得到tensor的形状:
1 | #返回的是一个tuple, torch.Size是一个tuple |
torch.Size([3, 5]) <class 'torch.Size'>
Attention
torch.Size is tuple
Operations
有很多种tensor运算。我们先介绍加法运算。
1 | y = torch.rand(3, 5) |
tensor([[-0.1155, 0.1429, 0.2812, 0.0049, 1.1452],
[ 1.8705, 2.3386, -1.1029, 0.1432, 2.5273],
[ 0.2852, -1.7390, 0.1234, -0.5170, 0.1819]])
另一种着加法的写法
1 | print(torch.add(x, y)) |
tensor([[-0.1155, 0.1429, 0.2812, 0.0049, 1.1452],
[ 1.8705, 2.3386, -1.1029, 0.1432, 2.5273],
[ 0.2852, -1.7390, 0.1234, -0.5170, 0.1819]])
加法:把输出作为一个变量
1 | res = x + y |
tensor([[-0.1155, 0.1429, 0.2812, 0.0049, 1.1452],
[ 1.8705, 2.3386, -1.1029, 0.1432, 2.5273],
[ 0.2852, -1.7390, 0.1234, -0.5170, 0.1819]])
tensor([[-0.1155, 0.1429, 0.2812, 0.0049, 1.1452],
[ 1.8705, 2.3386, -1.1029, 0.1432, 2.5273],
[ 0.2852, -1.7390, 0.1234, -0.5170, 0.1819]])
in-place加法
1 | # add x to y |
tensor([[0.1999, 0.4098, 0.3139, 0.0337, 0.5115],
[0.0543, 0.7440, 0.2564, 0.9927, 0.9454],
[0.4864, 0.1365, 0.5750, 0.0983, 0.0976]])
tensor([[-0.1155, 0.1429, 0.2812, 0.0049, 1.1452],
[ 1.8705, 2.3386, -1.1029, 0.1432, 2.5273],
[ 0.2852, -1.7390, 0.1234, -0.5170, 0.1819]])
注意
任何in-place的运算都会以``_``结尾。 举例来说:``x.copy_(y)``, ``x.t_()``, 会改变 ``x``。
各种类似NumPy的indexing都可以在PyTorch tensor上面使用。
1 | print(x) |
tensor([[-0.3154, -0.2669, -0.0327, -0.0288, 0.6338],
[ 1.8162, 1.5946, -1.3593, -0.8494, 1.5819],
[-0.2012, -1.8754, -0.4515, -0.6153, 0.0842]])
tensor([-0.2669, 1.5946, -1.8754])
Resizing: 如果你希望resize/reshape一个tensor,可以使用torch.view:
1 | # 直接使用reshape返回的是一个新的tensor |
tensor([[-0.3154, -0.2669, -0.0327],
[-0.0288, 0.6338, 1.8162],
[ 1.5946, -1.3593, -0.8494],
[ 1.5819, -0.2012, -1.8754],
[-0.4515, -0.6153, 0.0842]])
tensor([[-0.3154, -0.2669, -0.0327, -0.0288, 0.6338],
[ 1.8162, 1.5946, -1.3593, -0.8494, 1.5819],
[-0.2012, -1.8754, -0.4515, -0.6153, 0.0842]])
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
1 | x = torch.tensor([1]) |
1
-0.4120529592037201
更多阅读
各种Tensor operations, 包括transposing, indexing, slicing,
mathematical operations, linear algebra, random numbers在
<https://pytorch.org/docs/torch>
.
Numpy和Tensor之间的转化
在Torch Tensor和NumPy array之间相互转化非常容易。
Torch Tensor和NumPy array会共享内存,所以改变其中一项也会改变另一项。
把Torch Tensor转变成NumPy Array
1 | a = torch.ones(5) |
tensor([1., 1., 1., 1., 1.])
1 | b = a.numpy() |
[1. 1. 1. 1. 1.]
改变numpy array里面的值。
1 | # 猜测:原数据地址都是一样的只是形式不同而已 |
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
把NumPy ndarray转成Torch Tensor
1 | import numpy as np |
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
所有CPU上的Tensor都支持转成numpy或者从numpy转成Tensor。
CUDA Tensors
使用.to
方法,Tensor可以被移动到别的device上。
1 | # let us run this cell only if CUDA is available |
热身: 用numpy实现两层神经网络
一个全连接ReLU神经网络,一个隐藏层,没有bias。用来从x预测y,使用L2 Loss。
这一实现完全使用numpy来计算前向神经网络,loss,和反向传播。
numpy ndarray是一个普通的n维array。它不知道任何关于深度学习或者梯度(gradient)的知识,也不知道计算图(computation graph),只是一种用来计算数学运算的数据结构。
1 | import numpy as np |
0 23734775.983870674
1 18164484.466070224
2 14837301.198169785
3 12230863.71148485
…………
498 0.00021707495985364252
499 0.00020994768522520618
PyTorch: Tensors
这次我们使用PyTorch tensors来创建前向神经网络,计算损失,以及反向传播。
一个PyTorch Tensor很像一个numpy的ndarray。但是它和numpy ndarray最大的区别是,PyTorch Tensor可以在CPU或者GPU上运算。如果想要在GPU上运算,就需要把Tensor换成cuda类型。
1 | import torch |
0 23538196.0
1 18919592.0
…………
498 0.00025007393560372293
499 0.0002459314709994942
简单的autograd
1 | # Create tensors. requires_grad=True. Default False |
tensor(2.)
tensor(1.)
tensor(1.)
PyTorch: Tensor和autograd
PyTorch的一个重要功能就是autograd,也就是说只要定义了forward pass(前向神经网络),计算了loss之后,PyTorch可以自动求导计算模型所有参数的梯度。
一个PyTorch的Tensor表示计算图中的一个节点。如果x
是一个Tensor并且x.requires_grad=True
那么x.grad
是另一个储存着x
当前梯度(相对于一个scalar,常常是loss)的向量。
1 | import torch |
0 24256240.0
1 20202350.0
…………
498 0.0007593631744384766
499 0.0007417545421048999
PyTorch:nn
这次我们使用PyTorch中nn这个库来构建网络。
用PyTorch autograd来构建计算图和计算gradients,
然后PyTorch会帮我们自动计算gradient。
1 | import torch |
0 699.1046752929688
1 647.9535522460938
…………
498 9.313514510722598e-07
499 9.014782449412451e-07
PyTorch: optim
这一次我们不再手动更新模型的weights,而是使用optim这个包来帮助我们更新参数。
optim这个package提供了各种不同的模型优化方法,包括SGD+momentum, RMSProp, Adam等等。
1 | import torch |
0 711.3644409179688
1 694.257080078125
…………
498 2.771603502260689e-10
499 2.650134556247963e-10
PyTorch: 自定义 nn Modules
我们可以定义一个模型,这个模型继承自nn.Module类。如果需要定义一个比Sequential模型更加复杂的模型,就需要定义nn.Module模型。
1 | import torch |
0 684.3630981445312
1 634.8236083984375
…………
498 1.6948175471043214e-05
499 1.6512673028046265e-05
FizzBuzz
FizzBuzz是一个简单的小游戏。游戏规则如下:从1开始往上数数,当遇到3的倍数的时候,说fizz,当遇到5的倍数,说buzz,当遇到15的倍数,就说fizzbuzz,其他情况下则正常数数。
我们可以写一个简单的小程序来决定要返回正常数值还是fizz, buzz 或者 fizzbuzz。
1 | def fizz_buzz_encode(num): |
我们首先创建训练数据:
1 | import torch |
然后我们用PyTorch定义模型
1 | # Define the model |
- 为了让我们的模型学会FizzBuzz这个游戏,我们需要定义一个损失函数,和一个优化算法。
- 这个优化算法会不断优化(降低)损失函数,使得模型的在该任务上取得尽可能低的损失值。
- 损失值低往往表示我们的模型表现好,损失值高表示我们的模型表现差。
- 由于FizzBuzz游戏本质上是一个分类问题,我们选用Cross Entropyy Loss函数。
- 优化函数我们选用Stochastic Gradient Descent。
1 | loss_fn = torch.nn.CrossEntropyLoss() |
以下是模型的训练代码
1 | BATCH_SIZE = 128 |
epoch: 0 loss: 1.1895811557769775
epoch: 1 loss: 1.1572147607803345
…………
epoch: 5097 loss: 0.02661093883216381
epoch: 5098 loss: 0.026590632274746895
epoch: 5099 loss: 0.026588384062051773
…………
epoch: 8016 loss: 0.011458768509328365
epoch: 8017 loss: 0.011454952880740166
epoch: 8018 loss: 0.011453792452812195
…………
epoch: 9998 loss: 0.00786476582288742
epoch: 9999 loss: 0.00786191038787365
最后我们用训练好的模型尝试在1到100这些数字上玩FizzBuzz游戏
1 | testX = torch.Tensor([binary_encode(i) for i in range(1, 101)]) |
显示最后玩的结果:
1 | for num, index in predictions: |
1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz 21 22 23 fizz buzz 26 fizz 28 29 fizzbuzz 31 32 fizz 34 buzz fizz 37 38 fizz buzz 41 fizz 43 44 fizzbuzz 46 47 fizz 49 buzz fizz 52 53 fizz buzz 56 fizz 58 59 fizzbuzz 61 62 fizz 64 buzz fizz 67 68 69 buzz 71 fizz 73 74 fizzbuzz 76 77 fizz 79 buzz 81 82 83 84 buzz 86 87 88 89 fizzbuzz 91 92 93 94 buzz fizz 97 98 fizz buzz
计算总和, 和哪些错误
1 | # 分类问题使用tensor.max(1) 返回最大值的index |
94
1 | pred_Y.max(1)[1].numpy() == np.array([fizz_buzz_encode(i) for i in range(1, 101)]) |
array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, False, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, False, True, True, True,
True, True, True, True, True, True, True, True, False,
True, True, False, True, True, False, True, True, True,
True, True, False, True, True, True, True, True, True,
True])
1 | pred_Y.max(1) |
torch.return_types.max(
values=tensor([ 5.9531, 5.9475, 5.8658, 7.1499, 10.3761, 8.2045, 8.1089, 6.6718,
5.6398, 7.5722, 6.4976, 9.0869, 8.1241, 8.0379, 8.4377, 7.2665,
7.3191, 7.8744, 5.6651, 7.6147, 2.2065, 7.8375, 4.2420, 6.6890,
3.5175, 9.0680, 8.0666, 8.6170, 8.5703, 7.5231, 7.3321, 6.3975,
3.3405, 7.5249, 8.5877, 7.6400, 5.8178, 7.7374, 4.2418, 6.5080,
7.3046, 6.7514, 2.8480, 8.3515, 5.0900, 8.7313, 6.0416, 8.3804,
8.2009, 4.7483, 4.1110, 4.2541, 3.0316, 4.7115, 8.0126, 8.8917,
2.6943, 6.2795, 8.0649, 5.5002, 7.3595, 6.0792, 3.9122, 6.3986,
6.1323, 6.6845, 7.4449, 7.3432, 4.2463, 8.6458, 8.3745, 8.8894,
7.9096, 8.1622, 3.5034, 7.2827, 8.8453, 7.8358, 5.6428, 6.7707,
1.3065, 5.7157, 3.3958, 4.8879, 5.4819, 7.6762, 4.3674, 7.3603,
8.4167, 6.3594, 4.1352, 8.4632, 5.4859, 5.7701, 6.6684, 8.2688,
8.7533, 5.7605, 5.5259, 9.0591]),
indices=tensor([0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 0, 0, 0, 1,
2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1,
0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 1, 0, 0, 0, 2, 0, 1,
0, 0, 3, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, 1,
0, 0, 1, 2]))
1 | pred_Y.max(1)[1] |
tensor([0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 0, 0, 0, 1,
2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1,
0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1, 0, 2, 1, 0, 0, 0, 2, 0, 1,
0, 0, 3, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, 1,
0, 0, 1, 2])