1. 程式人生 > >10 分鐘理解 PyTorch 程式碼

10 分鐘理解 PyTorch 程式碼

PyTorch 是一個新的深度學習框架. 本文的內容基於 Justin Johnson 的 教程, 如果想要有更多瞭解或有更多時間的話建議仔細研究一下.

PyTorch 主要包含 4 個包 (package):

  1. torch: 一個通用性的陣列庫, 與 Numpy 類似, 當 tensor 型別被轉化(torch.cuda.TensorFloat)的時候可以在 GPU 上進行計算.

  2. torch.autograd: 一個用來構建計算圖來自動求梯度的包.

  3. torch.nn: 一個包含常見 layer 和 cost function 的 神經網路庫.

  4. torch.optim: 包含像 SGD, Adam 這樣常見優化演算法的一個優化包.

匯入工作

你可以像下面這樣匯入 PyTorch:

import torch # arrays on GPU
import torch.autograd as autograd #build a computational graph
import torch.nn as nn ## neural net library
import torch.nn.functional as F ## most non-linearities are here
import torch.optim as optim # optimization package

使用 torch array 代替 numpy ndarray -> 提供在 GPU 上的線性代數運算支援

PyTorch 提供了類似 Numpy array 的多維陣列, 當資料型別可以被轉化為 (torch.cuda.TensorFloat) 時就可以放到 GPU 上進行處理. 多維陣列和它相關的一些函式都是通用的科學計算工具.

# 2 matrices of size 2x3 into a 3d tensor 2x2x3
d=[[[1., 2.,3.],[4.,5.,6.]],[[7.,8.,9.],[11.,12.,13.]]]
d=torch.Tensor(d) # array from python list
print "shape of the tensor:",d.size()

# the first index is the depth
z=d[0]+d[1] print "adding up the two matrices of the 3d tensor:",z
shape of the tensor: torch.Size([2, 2, 3])
adding up the two matrices of the 3d tensor:
  8  10  12
 15  17  19
[torch.FloatTensor of size 2x3]
# a heavily used operation is reshaping of tensors using .view()
print d.view(2,-1) #-1 makes torch infer the second dim
  1   2   3   4   5   6
  7   8   9  11  12  13
[torch.FloatTensor of size 2x6]

torch.autograd -> 建立一個計算圖並自動計算梯度

第二個特性是 autograd 包, 它能夠定義一個計算圖以便於我們能夠自動計算梯度. 在計算圖中, 一個節點就是一個數組, 一條邊就是在陣列上的一個操作. 為了建立一個計算圖, 我們需要在函式裡面封裝一個數組來建立一個節點 (torch.autograd.Variable()). 然後在該節點上的所有操作將會被定義為邊, 操作的結果將會成為計算圖中新的節點. 計算圖中的每個節點都有一個 node.data 屬性, 它是一個多維陣列. 還有一個 node.grad 屬性, 它是某個標量的梯度 (node.grad 同時也是一個 .Variable()). 在定義好圖以後, 只需一個命令 (loss.backward()) 就可以計算圖中所有節點的 loss 梯度.

  • 使用 torch.autograd.Variable() 可以將一個 Tensor 轉化成為計算圖中一個節點.
    • 通過 x.data 來獲取它的值
    • 通過 x.grad 來獲取的梯度
  • 在 .Variable() 上施加操作來生成圖中的邊
# d is a tensor not a node, to create a node based on it:
x= autograd.Variable(d, requires_grad=True)
print "the node's data is the tensor:", x.data.size()
print "the node's gradient is empty at creation:", x.grad # the grad is empty right now
the node's data is the tensor: torch.Size([2, 2, 3])
the node's gradient is empty at creation: None
# do operation on the node to make a computational graph
y= x+1
z=x+y
s=z.sum()
print s.creator
<torch.autograd._functions.reduce.Sum object at 0x7f1e59988790>
# calculate gradients
s.backward()
print "the variable now has gradients:",x.grad
the variable now has gradients: Variable containing:
(0 ,.,.) =
  2  2  2
  2  2  2

(1 ,.,.) =
  2  2  2
  2  2  2
[torch.FloatTensor of size 2x2x3]

torch.nn 包含了各種神經網路的 layer (對一個 tensor 行的線性對映) + (非線性) -> 無須手動控制 tensor 和引數即可構建一個神經網路計算圖

第三個特性是一個高層次的神經網路庫 (torch.nn), 它抽象出了神經網路的 layer 中所有的引數處理, 使得能夠幾個命令就可以定義一個神經網路 (比如, torch.nn.conv). 這個包同樣也帶有常用的 loss function (比如, torch.nn.MSEloss). 我們以定義一個模型容器開始, 比如使用 (torch.nn.Sequential) 有一系列層的模型, 並且按順序列出我們想要的層. 這個庫處理其他的所有事情; 我們可以通過 model.parameters() 來獲取引數 (Variables()).

# linear transformation of a 2x5 matrix into a 2x3 matrix
linear_map=nn.Linear(5,3)
print "using randomly initialized params:", linear_map.parameters
using randomly initialized params: <bound method Linear.parameters of Linear (5 -> 3)>
# data has 2 examples with 5 features and 3 target
data=torch.randn(2,5) # training
y=autograd.Variable(torch.randn(2,3)) # target
# make a node
x=autograd.Variable(data, requires_grad=True)
# apply transformation to a node creates a computational graph
a=linear_map(x)
z=F.relu(a)
o=F.softmax(z)
print "output of softmax as a probability distribution:", o.data.view(1,-1)

# loss function
loss_func=nn.MSELoss() #instantiate loss function
L=loss_func(z,y) # calculateMSE loss between output and target
print "Loss:", L
output of softmax as a probability distribution:
 0.2092  0.1979  0.5929  0.4343  0.3038  0.2619
[torch.FloatTensor of size 1x6]

Loss: Variable containing:
 2.9838
[torch.FloatTensor of size 1]

我們也可以通過子集 torch.nn.Module 自定義 layer, 實現一個接受一個 Variable() 作為輸入, 並輸出一個 Variable()forward() 函式. 我們也可以定義一個隨時間變化的 layer 來建立一個動態網路!

  • 當自定義一個 layer, 需要實現 2 個函式:
    • 首先需要繼承 init 函式, 然後 layer 中的所有引數必須被定義為類變數 (self.x)
    • 在 forward 函式裡面, 我們傳遞輸入, 在輸入上施加操作並進行輸出. 輸入需要時一個 autograd.Variable() 以便於 pytorch 能夠構建 layer 的計算圖.
class Log_reg_classifier(nn.Module):
    def __init__(self, in_size,out_size):
        super(Log_reg_classifier,self).__init__() #always call parent's init
        self.linear=nn.Linear(in_size, out_size) #layer parameters

    def forward(self,vect):
        return F.log_softmax(self.linear(vect)) #

torch.optim 可以進行優化 -> 我們通過 torch.nn 構建一個計算圖, 通過 torch.autograd 來計算梯度, 然後輸入到 torch.optim 來更新網路引數

第四個特性是一個配合 NN 庫使用的優化包 (torch.optim). 這個庫包含了一些像 Adam, RMSprop 的 optimizer. 我們定義一個 optimizer 並傳入網路引數和學習率 (opt = torch.optim.Adam(model.parameters(), lr=learning_rate), 然後我們可以呼叫 opt.step() 對於我們的引數做一步更新.

optimizer=optim.SGD(linear_map.parameters(),lr=1e-2) # instantiate optimizer with model params + learning rate

# epoch loop: we run following until convergence
optimizer.zero_grad() # make gradients zero
L.backward(retain_variables=True)
optimizer.step()
print L
Variable containing:
 2.9838
[torch.FloatTensor of size 1]

構建一個神經網路十分容易, 下面是一個完整示例:

# define model
model = Log_reg_classifier(10,2)

# define loss function
loss_func=nn.MSELoss()

# define optimizer
optimizer=optim.SGD(model.parameters(),lr=1e-1)

# send data through model in minibatches for 10 epochs
for epoch in range(10):
    for minibatch, target in data:
        model.zero_grad() # pytorch accumulates gradients, making them zero for each minibatch

        #forward pass
        out=model(autograd.Variable(minibatch))

        #backward pass
        L=loss_func(out,target) #calculate loss
        L.backward() # calculate gradients
        optimizer.step() # make an update step