PyTorch是一个基于Python的科学计算库,它有以下特点:
类似于 NumPy,但是它可以使用 GPU 
可以用它定义深度学习模型,可以灵活地进行深度学习模型的训练和使用 
 
Tensor 类似与 NumPy 的 ndarray,唯一的区别是 Tensor 可以在 GPU 上加速运算。
1 2 import  torchimport  numpy as  np
构造一个未初始化的 5x3 矩阵:
Results:
tensor([[1.0194e-38, 9.1837e-39, 8.4490e-39],
        [9.6429e-39, 8.4490e-39, 9.6429e-39],
        [9.2755e-39, 1.0286e-38, 9.0919e-39],
        [8.9082e-39, 9.2755e-39, 8.4490e-39],
        [1.0194e-38, 9.0919e-39, 8.4490e-39]])
 
构建一个随机初始化的矩阵:
Results:
tensor([[0.1782, 0.5218, 0.1660],
        [0.4009, 0.0275, 0.8139],
        [0.6904, 0.4813, 0.7811],
        [0.4067, 0.6289, 0.5081],
        [0.0305, 0.9687, 0.0834]])
 
构建一个全部为 0,类型为 long 的矩阵:
1 2 3 4 x = torch.zeros(5 ,3 ,dtype=torch.long) x1 = torch.zeros(5 ,3 ).long()  x x2.dtype 
Results:
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
torch.int64
 
从数据直接直接构建 tensor:
1 2 x = torch.tensor([5.5 ,3 ]) x 
Results:
tensor([5.5000, 3.0000])
 
1 矩阵
1 2 x = x.new_ones(5 ,3 , dtype=torch.double) x 
Results:
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
 
randn_like
也可以从一个已有的tensor构建一个tensor。这些方法会重用原来tensor的特征,例如,数据类型,除非提供新的数据。
1 2 x = torch.randn_like(x, dtype=torch.float ) x 
Results:
tensor([[-0.3863, -1.4149, -0.1054],
        [ 2.3531,  0.2044, -1.5104],
        [ 0.2127, -0.5231, -0.7806],
        [-0.9173, -0.0242,  0.1667],
        [-1.3101,  1.2451, -0.3665]])
 
得到 tensor 的形状:
Results:
torch.Size([5, 3])
torch.Size([5, 3])
 
 
Notes : torch.Size 返回的是一个tuple
 
有很多种tensor运算。我们先介绍加法运算。
加法
1 2 3 4 5 6 x + y  torch.add(x, y)  result = torch.empty(5 ,3 ) torch.add(x, y, out=result) 
Results:
tensor([[-0.0267, -1.1529,  0.7544],
        [ 2.8445,  1.0922, -1.3458],
        [ 0.6769, -0.2879, -0.7209],
        [-0.1200,  0.8094,  0.3603],
        [-0.3267,  2.1897, -0.3474]])
 
in-place加法
Results:
tensor([[-0.0267, -1.1529,  0.7544],                [ 2.8445,  1.0922, -1.3458],                [ 0.6769, -0.2879, -0.7209],                [-0.1200,  0.8094,  0.3603],                [-0.3267,  2.1897, -0.3474]])
Note : 任何 in-place 的运算都会以 _ 结尾。举例来说:x.copy_(y), x.t_(), 这些 in-place 方法会改变变量 x。
 
Index
各种类似 NumPy 的 indexing 都可以在 PyTorch tensor 上面使用。
Results:
tensor([[ 0.2044, -1.5104],        [-0.5231, -0.7806],        [-0.0242,  0.1667],        [ 1.2451, -0.3665]])
 
Resizing
如果希望 resize/reshape 一个 tensor,可以使用 torch.view:
1 2 3 4 x = torch.randn(4 ,4 ) y = x.view(16 ) z = x.view(-1 ,8 ) z 
Results:
tensor([[ 1.1355, -1.1149, -0.1322, -0.8217,  0.7920,  0.6061,  0.7453,  1.1177],
        [ 0.7566,  1.3975, -0.8014,  0.5999, -0.1476, -0.5695, -1.3861, -0.4741]])
 
取得数值
如果你有一个只有一个元素的 tensor,使用 .item() 方法可以把里面的 value 变成 Python 数值。
1 2 3 x = torch.randn(1 ) x x.item() 
Results:
tensor([-1.4227])
-1.422684907913208
 
转置
Results:
tensor([[ 1.1355,  0.7566],        [-1.1149,  1.3975],        [-0.1322, -0.8014],        [-0.8217,  0.5999],        [ 0.7920, -0.1476],        [ 0.6061, -0.5695],        [ 0.7453, -1.3861],        [ 1.1177, -0.4741]])
 
 
更多阅读 
各种 Tensor operations, 包括 transposing, indexing, slicing,PyTorch Documentation .
在 Torch Tensor 和 NumPy array 之间相互转化非常容易。
Note : Torch Tensor和 NumPy array 会共享内存,所以改变其中一项也会改变另一项。
使用.to方法,Tensor可以被移动到别的device上。
1 2 3 4 5 6 7 if  torch.cuda.is_available():    device = torch.device("cuda" )         y = torch.ones_like(x, device=device)         x = x.to(device)         z = x + y         print(z)         print(z.to("cpu" , torch.double))     
Results:
tensor([-0.4227], device='cuda:0')
tensor([-0.4227], dtype=torch.float64)
1 y.to("cpu" ).data.numpy()y.cpu().data.numpy() 
Results:
array([1.], dtype=float32)
一个全连接ReLU神经网络,一个隐藏层,没有 bias。用来从 x 预测 y,使用 L2 Loss。
h = W 1 X h = W_1X h = W 1  X a = m a x ( 0 , h ) a = max(0, h) a = m a x ( 0 , h ) y h a t = W 2 a y_{hat} = W_2a y h a t  = W 2  a  
这一实现完全使用numpy来计算前向神经网络,loss,和反向传播。
forward pass 
loss 
backward pass 
 
numpy ndarray 是一个普通的 n 维 array。它不知道任何关于深度学习或者梯度 ( gradient) 的知识,也不知道计算图 (computation graph),只是一种用来计算数学运算的数据结构。
1 N, D_in, H, D_out = 64 , 1000 , 100 , 10  
Results:
0 30767183.85959570550 19309.70472025325100 927.7120364511059150 65.96723021925985200 5.451127574769099250 0.4979445419484826300 0.04972286885320139350 0.0053918752712101775400 0.0006287349147432031450 7.778529921424317e-05
1 h = x.dot(w1)h_relu = np.maximum(h, 0 )  
Results:
array([[-2.31700647e-05, -6.28731777e-05,  4.77222071e-05,         1.18624504e-05, -4.80998287e-05,  1.52820208e-06,         6.16901569e-06, -8.37944967e-05, -2.06058305e-06,         1.23783963e-06]])
可以发现,两者相差非常小,模型训练较为成功。
这次我们使用 PyTorch tensors 来创建前向神经网络,计算损失,以及反向传播。
一个 PyTorch Tensor 很像一个 numpy 的 ndarray。但是它和 numpy ndarray 最大的区别是,PyTorch Tensor 可以在 CPU 或者 GPU 上运算。如果想要在 GPU 上运算,就需要把 Tensor 换成 cuda 类型。
与 numpy 不同的方法:
mm: 矩阵相乘,对应于 dotclamp: 夹子,将值夹在两者之间,对应于 maximumpow: 平方,对应于 np.squaret: 转置,对应于 .tclone: 复制,对应于 copy 
1 N, D_in, H, D_out = 64 , 1000 , 100 , 10  
0 31964224.050 8884.4111328125100 245.90003967285156150 12.271601676940918200 0.7670953273773193250 0.05322442948818207300 0.0041369786486029625350 0.0005290982662700117400 0.00013705584569834173450 5.566925392486155e-05
简单的autograd
1 x = torch.tensor(1. , requires_grad=True )w = torch.tensor(2. , requires_grad=True )b = torch.tensor(3. , requires_grad=True )y = w*x + b 
Results:
tensor(1.)tensor(2.)tensor(1.)
PyTorch 的一个重要功能就是 autograd,也就是说只要定义了 forward pass (前向神经网络),计算了 loss 之后,PyTorch 可以自动求导计算模型所有参数的梯度。
一个 PyTorch 的 Tensor 表示计算图中的一个节点。如果 x 是一个 Tensor 并且 x.requires_grad=True 那么 x.grad 是另一个储存着 x 当前梯度(相对于一个 scalar,常常是 loss)的向量。
1 N, D_in, H, D_out = 64 , 1000 , 100 , 10  
Results:
0 40281260.050 16265.544921875100 770.28857421875150 60.70436477661133200 5.996867656707764250 0.6809620261192322300 0.08519172668457031350 0.011621471494436264400 0.0019496456952765584450 0.0004842539201490581
这次我们使用 PyTorch 中 nn 这个库来构建网络。用 PyTorch autograd 来构建计算图和计算 gradients,然后 PyTorch 会帮我们自动计算 gradient。
1 import  torch.nn as  nnN, D_in, H, D_out = 64 , 1000 , 100 , 10 
Results:
0 37942064.050 13557.75390625100 482.86322021484375150 27.34326934814453200 1.863478183746338250 0.1420218050479889300 0.01187755074352026350 0.0013162594987079501400 0.00026860935031436384450 9.016783587867394e-05
model[0].weight 可得到模型中第一层的权重,bia 可得到偏置项。
Results:
Parameter containing:tensor([[-0.0218,  0.0212,  0.0243,  ...,  0.0230,  0.0247,  0.0168],    [-0.0144,  0.0177, -0.0221,  ...,  0.0161,  0.0098, -0.0172],    [ 0.0086, -0.0122, -0.0298,  ..., -0.0236, -0.0187,  0.0295],    ...,    [ 0.0266, -0.0008, -0.0141,  ...,  0.0018,  0.0319, -0.0129],    [ 0.0296, -0.0005,  0.0115,  ...,  0.0141, -0.0088, -0.0106],    [ 0.0289, -0.0077,  0.0239,  ..., -0.0166, -0.0156, -0.0235]],   requires_grad=True)
这一次我们不再手动更新模型的 weights,而是使用 optim 这个包来帮助我们更新参数。
1 import  torch.nn as  nnN, D_in, H, D_out = 64 , 1000 , 100 , 10 
Results:
0 40010880.050 8527.947265625100 276.97821044921875150 18.40827751159668200 1.5830364227294922250 0.15364140272140503300 0.016014395281672478350 0.0019916763994842768400 0.0004044498491566628450 0.0001368314551655203
我们可以定义一个模型,这个模型继承自 nn.Module 类。如果需要定义一个比 Sequential 模型更加复杂的模型,就需要定义 nn.Module 模型。
1 import  torch.nn as  nnN, D_in, H, D_out = 64 , 1000 , 100 , 10 
Results:
  0 692.8556518554688  100 50.59251403808594  200 0.6749072074890137  300 0.01346816960722208  400 0.0009340611286461353
FizzBuzz 是一个简单的小游戏。游戏规则如下:从 1 开始往上数数,当遇到 3 的倍数的时候,说 fizz,当遇到 5 的倍数,说 buzz,当遇到 15 的倍数,就说 fizzbuzz,其他情况下则正常数数。
我们可以写一个简单的小程序来决定要返回正常数值还是 fizz, buzz 或者 fizzbuzz。
Resutls:
12buzzfizzfizzbuzz
我们首先定义模型的输入与输出(训练数据)
1 import  numpy as  npimport torchNUM_DIGITS = 10 
然后我们用PyTorch定义模型
1 2 3 4 5 6 7 8 9 10 11 NUM_HIDDEN = 100  model = torch.nn.Sequential(     torch.nn.Linear(NUM_DIGITS, NUM_HIDDEN),     torch.nn.ReLU(),     torch.nn.Linear(NUM_HIDDEN, 4 ) ) device = torch.device("cuda"  if  torch.cuda.is_available() else  "cpu" ) model = model.to(device) 
为了让我们的模型学会 FizzBuzz 这个游戏,我们需要定义一个损失函数,和一个优化算法。 
这个优化算法会不断优化(降低)损失函数,使得模型的在该任务上取得尽可能低的损失值。 
损失值低往往表示我们的模型表现好,损失值高表示我们的模型表现差。 
由于 FizzBuzz 游戏本质上是一个分类问题,我们选用 Cross Entropyy Loss 函数。 
优化函数我们选用 Stochastic Gradient Descent。 
 
1 2 loss_fn = torch.nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr = 0.05 ) 
以下是模型的训练代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 BATCH_SIZE = 128  for  epoch in  range (10000 ):    for  start in  range (0 , len (trX), BATCH_SIZE):         end = start + BATCH_SIZE         batchX = trX[start:end].to(device)         batchY = trY[start:end].to(device)         y_pred = model(batchX)         loss = loss_fn(y_pred, batchY)         optimizer.zero_grad()         loss.backward()         optimizer.step()          loss = loss_fn(model(trX.to(device)), trY.to(device)).cpu().item()     if  epoch%1000  == 0 :         print('Epoch:' , epoch, 'Loss:' , loss) 
Results:
Epoch: 0 Loss: 1.1463667154312134
Epoch: 1000 Loss: 0.4962996244430542
Epoch: 2000 Loss: 0.15307655930519104
Epoch: 3000 Loss: 0.07736020535230637
Epoch: 4000 Loss: 0.044787853956222534
Epoch: 5000 Loss: 0.029076775535941124
Epoch: 6000 Loss: 0.020609091967344284
Epoch: 7000 Loss: 0.01560244057327509
Epoch: 8000 Loss: 0.012386537156999111
Epoch: 9000 Loss: 0.010154682211577892
最后我们用训练好的模型尝试在 1 到 100 这些数字上玩 FizzBuzz 游戏
1 2 3 4 5 6 7 testX = torch.Tensor([binary_encode(i, NUM_DIGITS) for  i in  range (1 , 101 )]) with  torch.no_grad():    testY = model(testX.to(device)) predictions = zip (range (1 , 101 ), list (testY.max (1 )[1 ].data.tolist())) print([fizz_buzz_decode(i, x) for  (i, x) in  predictions]) 
Results:
['1', '2', 'fizz', '4', 'buzz', 'fizz', '7', '8', 'fizz', '10', '11',
  'fizz', '13', '14', 'fizzbuzz', '16', '17', 'fizz', '19', 'buzz',
  'fizz', '22', '23', 'fizz', 'buzz', '26', 'fizz', '28', '29',
  'fizzbuzz', '31', '32', 'fizz', '34', 'buzz', 'fizz', '37', '38',
  'fizz', 'buzz', '41', '42', '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', 'fizz', '82', '83', '84', 'buzz',
  '86', 'fizz', '88', '89', 'fizzbuzz', '91', '92', '93', '94',
  'buzz', 'fizz', '97', '98', 'fizz', 'buzz']
1 print(np.sum (testY.cpu().max (1 )[1 ].numpy() == np.array([fizz_buzz_encode(i) for  i in  range (1 ,101 )])))testY.cpu().max (1 )[1 ].numpy() == np.array([fizz_buzz_encode(i) for  i in  range (1 ,101 )]) 
Results:
95
array([ 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, 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, False,  True,  True,  True,        
        True,  True,  True,  True,  True,  True,  True,  True,  True,        
        True,  True, False,  True,  True,  True,  True,  True,  True,        
        True,  True, False,  True,  True,  True,  True,  True,  True,        
        True])