1. 程式人生 > >1 pytorch60分鐘入門教程

1 pytorch60分鐘入門教程

1 Tensors

from __future__ import print_function
import torch
x = torch.Tensor(5, 3)  # 構造一個未初始化的5*3的矩陣
x = torch.rand(5, 3)  # 構造一個隨機初始化的矩陣
x
 0.9643  0.2740  0.9700
 0.2375  0.8547  0.1793
 0.2462  0.8887  0.0271
 0.8668  0.6014  0.9562
 0.8588  0.3883  0.3741
[torch.FloatTensor of size 5x3]
x.size()
torch.Size([5, 3])
y = torch.rand(5,3)
y
 0.3458  0.1517  0.1397
 0.6764  0.6408  0.0139
 0.6116  0.4172  0.8836
 0.9197  0.6072  0.0751
 0.7214  0.0613  0.4052
[torch.FloatTensor of size 5x3]
x+y
 1.3101  0.4257  1.1097
 0.9139  1.4955  0.1932
 0.8578  1.3060  0.9108
 1.7865  1.2086  1.0312
 1.5802  0.4495  0.7793
[torch.FloatTensor of size 5x3]
torch.add(x,y)
 1.3101  0.4257  1.1097
 0.9139  1.4955  0.1932
 0.8578  1.3060  0.9108
 1.7865  1.2086  1.0312
 1.5802  0.4495  0.7793
[torch.FloatTensor of size 5x3]
z = x+y
z
 1.3101  0.4257  1.1097
 0.9139  1.4955  0.1932
 0.8578  1.3060  0.9108
 1.7865  1.2086  1.0312
 1.5802  0.4495  0.7793
[torch.FloatTensor of size 5x3]
result = torch.Tensor(5, 3) # 語法一
torch.add(x, y, out=result) # 語法二
result
 1.3101  0.4257  1.1097
 0.9139  1.4955  0.1932
 0.8578  1.3060  0.9108
 1.7865  1.2086  1.0312
 1.5802  0.4495  0.7793
[torch.FloatTensor of size 5x3]
y.add_(x) # 將y與x相加

# 特別註明:任何可以改變tensor內容的操作都會在方法名後加一個下劃線'_'
# 例如:x.copy_(y), x.t_(), 這倆都會改變x的值。
y
 1.3101  0.4257  1.1097
 0.9139  1.4955  0.1932
 0.8578  1.3060  0.9108
 1.7865  1.2086  1.0312
 1.5802  0.4495  0.7793
[torch.FloatTensor of size 5x3]
x[:1]
 0.9643  0.2740  0.9700
[torch.FloatTensor of size 1x3]

2 Tensor與numpy的轉換

注意Torch的Tensor和numpy的array會共享他們的儲存空間,修改一個會導致另外的一個也被修改。

a = torch.ones(5)
b = a.numpy()
a,b
(
  1
  1
  1
  1
  1
 [torch.FloatTensor of size 5],
 array([ 1.,  1.,  1.,  1.,  1.], dtype=float32))
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
a,b
(array([ 1.,  1.,  1.,  1.,  1.]), 
  1
  1
  1
  1
  1
 [torch.DoubleTensor of size 5])
# 另外除了CharTensor之外,所有的tensor都可以在CPU運算和GPU預算之間相互轉換
# 使用CUDA函式來將Tensor移動到GPU上
# 當CUDA可用時會進行GPU的運算
if torch.cuda.is_available():
    x = x.cuda()
    y = y.cuda()
    x + y

3 PyTorch中的神經網路

PyTorch中所有的神經網路都來自於autograd包

autograd自動梯度計算,這是一個執行時定義的框架,這意味著你的反向傳播是根據你程式碼執行的方式來定義的,因此每一輪迭代都可以各不相同。

from torch.autograd import Variable
x = Variable(torch.ones(2,2),requires_grad = True)
y = x + 2
y.creator
<torch.autograd._functions.basic_ops.AddConstant at 0x3dad3f0>
z = y*y*3
out = z.mean()
out
Variable containing:
 27
[torch.FloatTensor of size 1]
out.backward()
x.grad
Variable containing:
 4.5000  4.5000
 4.5000  4.5000
[torch.FloatTensor of size 2x2]
x = torch.randn(3)
# print(x)
x = Variable(x, requires_grad = True)
y = x * 2
while y.data.norm() < 1000:
    y = y * 2
#     print (y)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
x.grad
Variable containing:
  204.8000
 2048.0000
    0.2048
[torch.FloatTensor of size 3]

這裡y.backward(gradients)這句話有什麼用呢???

y, y.data, y.data.norm()
(Variable containing:
  1546.1327
  -304.6176
   642.7925
 [torch.FloatTensor of size 3], 
  1546.1327
  -304.6176
   642.7925
 [torch.FloatTensor of size 3], 1701.9108254060725)

4 神經網路

使用 torch.nn 包可以進行神經網路的構建。

現在你對autograd有了初步的瞭解,而nn建立在autograd的基礎上來進行模型的定義和微分。

nn.Module中包含著神經網路的層,同時forward(input)方法能夠將output進行返回。

舉個例子,來看一下這個數字影象分類的神經網路。

一個典型的神經網路的訓練過程是這樣的:

  1. 定義一個有著可學習的引數(或者權重)的神經網路
  2. 對著一個輸入的資料集進行迭代:
  3. 用神經網路對輸入進行處理
  4. 計算代價值 (對輸出值的修正到底有多少)
  5. 將梯度傳播回神經網路的引數中
  6. 更新網路中的權重
  7. 通常使用簡單的更新規則: weight = weight + learning_rate * gradient
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5) # 1 input image channel, 6 output channels, 5x5 square convolution kernel
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1   = nn.Linear(16*5*5, 120) # an affine operation: y = Wx + b
        self.fc2   = nn.Linear(120, 84)
        self.fc3   = nn.Linear(84, 10)

    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv2(x)), 2) # If the size is a square you can only specify a single number
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:] # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
net
Net (
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear (400 -> 120)
  (fc2): Linear (120 -> 84)
  (fc3): Linear (84 -> 10)
)

x = x.view(-1, self.num_flat_features(x))應該是把x打平吧

僅僅需要定義一個forward函式就可以了,backward會自動地生成。

你可以在forward函式中使用所有的Tensor中的操作。

模型中可學習的引數會由net.parameters()返回。m

params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight

input = Variable(torch.randn(1, 1, 32, 32))
out = net(input)
10
torch.Size([6, 1, 5, 5])
out
Variable containing:
 0.0648  0.0148  0.0333  0.0013  0.0563 -0.0156  0.0543  0.1504 -0.0774 -0.0231
[torch.FloatTensor of size 1x10]

複習一下前面我們學到的:

torch.Tensor - 一個多維陣列

autograd.Variable - 改變Tensor並且記錄下來操作的歷史記錄。和Tensor擁有相同的API,以及backward()的一些API。同時包含著和張量相關的梯度。

nn.Module - 神經網路模組。便捷的資料封裝,能夠將運算移往GPU,還包括一些輸入輸出的東西。

nn.Parameter - 一種變數,當將任何值賦予Module時自動註冊為一個引數。

autograd.Function - 實現了使用自動求導方法的前饋和後饋的定義。每個Variable的操作都會生成至少一個獨立的Function節點,與生成了Variable的函式相連之後記錄下操作歷史。

到現在我們已經明白的部分:

定義了一個神經網路。

處理了輸入以及實現了反饋。

仍然沒整的:

計算代價。

更新網路中的權重。

5 計算每個引數的梯度

output = net(input)
target = Variable(torch.range(1, 10))  # a dummy target, for example
criterion = nn.MSELoss()
loss = criterion(output, target)
loss
Variable containing:
 38.2952
[torch.FloatTensor of size 1]

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss

print(loss.creator) # MSELoss
print(loss.creator.previous_functions[0][0]) # Linear
print(loss.creator.previous_functions[0][0].previous_functions[0][0]) # ReLU
<torch.nn._functions.thnn.auto.MSELoss object at 0x31dd6878>
<torch.nn._functions.linear.Linear object at 0x31dd6790>
<torch.nn._functions.thnn.auto.Threshold object at 0x31dd66a8>
# 現在我們應當呼叫loss.backward(), 之後來看看 conv1's在進行反饋之後的偏置梯度如何
net.zero_grad() # 歸零操作
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
conv1.bias.grad before backward
None
conv1.bias.grad after backward
Variable containing:
 0.2046
 0.0389
-0.0529
-0.0108
-0.0941
-0.0869
[torch.FloatTensor of size 6]

第一層的bias的個數剛好是6個,這裡已經給出了每個引數的梯度,這樣就可以以固定的學習率來更新了。感覺深度學習框架的牛逼之處就在於寫好了自動求梯度的東西了麼?

loss.backward就可以計算每一層的梯度了,更新還沒解決。

6 更新引數

最簡單的更新的規則是隨機梯度下降法(SGD):

weight = weight - learning_rate * gradient

我們可以用簡單的python來表示:

learning_rate = 0.01

for f in net.parameters():

f.data.sub_(f.grad.data * learning_rate)

可以用torch.optim來實現

import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(),lr = 0.01)

# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output,target)
loss.backward()
optimizer.step()# Does the update

總結一下

  1. 輸入:
input = Variable(torch.randn(1, 1, 32, 32))
  1. 輸出:
out = net(input)
  1. 網路結構
class Net(nn.Module):

    def __init__(self):

    def forward(self, x):

    barkward自己會完成
  1. 更新
optimizer = optim.SGD(net.parameters(),lr = 0.01)

optimizer.zero_grad()

loss.backward()

optimizer.step()# Does the update