DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ | NEURAL NETWORKS

Deep_RS發表於2022-01-21

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

現在你已經對autograd有所瞭解,nn依賴 autograd 定義模型並對其求微分。nn.Module 包括層,和一個返回 output 的方法 - forward(input)

例如,看看這個對數字圖片進行分類的網路:

convnet

這是一個簡單的前饋網路。它接受輸入,通過一層接一層,最後輸出。

一個典型的神經網路訓練過程如下:

  • 定義神經網路,幷包括一些可學習的引數(或權重)
  • 通過輸入資料集迭代
  • 通過網路處理輸入
  • 計算損失(輸出和真值的差距)
  • 將梯度反向傳播至神經網路的引數
  • 更新神經網路權重,通常使用簡單的更新規則:weight = weight - learning_rate * gradient

定義網路

讓我們定義這個網路:

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 1個輸入圖片通道,6個輸出通道,5x5平方卷積核
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # 一個仿射變換操作:y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5是特徵圖維度
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 最大池化:(2,2)視窗
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 如果視窗大小是正方形,可以使用單個數字
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = torch.flatten(x, 1) # 將除了batch維度的所有維度展平
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
net = Net()
print(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(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

你僅僅需要定義 forward 函式,backward 函式(梯度計算)會自動使用 autograd 定義。你可以在 forward 函式上使用任何Tensor操作。

net.parameters() 返回模型的可學習引數

params = list(net.parameters())
print(len(params))
print(param[0].size()) # conv1的權重引數

輸出:

10
torch.size([6, 1, 5, 5])

讓我們試試一個隨機的32x32的輸入。注意:該網路(LeNet)的輸入大小是32x32。為在MNIST上使用該網路,請將圖片縮放至32x32

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

輸出:

tensor([[-0.1380, -0.0528,  0.0736,  0.1018,  0.0066, -0.1454,  0.0366, -0.0692,
          0.0182,  0.0003]], grad_fn=<AddmmBackward0>)

使所有引數的梯度快取歸零,並反向傳播 一個隨機梯度:

net.zero_grad()
out.backward(torch.randn(1, 10))

注意:torch.nn僅支援mini-batches。整個 torch.nn包僅支援樣本的mini-batch輸入,而不是單個sample。例如,nn.Conv2d將接受一個4DTensor:nSamples x nChannels x Height x width。如果是單個樣本,利用 input.unsqueeze(0) 新增一個假的batch維度即可。

在繼續之前,讓我們回顧一下所有你迄今為止看到的所有類。

Recap:

  • torch.Tensor - 一個multi-dimensional array,支援autograd操作,如 backward()。還持有關於tensor的梯度。
  • nn.Module - 神經網路模型。封裝引數的一個便捷的途徑,並且可將它們移動到GPU,輸出、載入等。
  • nn.Parameter - 當tensor作為屬性賦給 Module時,自動註冊為引數。
  • autograd.Function - autograd的前向和後向定義的具體實現。每一個 Tensor 操作建立最少一個 Function 節點,並連線到建立 Tensor 和對其歷史進行編碼的函式。

以上,我們介紹了:

  • Defining a neural network
  • Processing inputs and calling backward

還剩:

  • 計算損失
  • 更新網路權重

損失函式

損失函式接受input的(output,target)對,計算評估output距離target的值。

在nn包中有多種不同的loss function,一個簡單的損失函式是:nn.MSELoss,它計算input和target的均方誤差。

例如:

output = net(input)
target = torch.randn(10) # 一個假的target
target = target.view(1, -1) # 使其與output保持形狀一致
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

輸出:

tensor(1.3339, grad_fn=<MseLossBackward0>)

現在,如果你在後向傳播中跟蹤 loss,使用它的 .grad_fn 屬性,你將會看到類似下面的計算圖:

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

因此,當我們呼叫 loss.backward(),整個圖將被求有關神經網路引數的導數,並且圖中所有 requires_grad=True的Tensors將持有梯度Tensor - .grad

為了說明,讓我們檢視幾步backward:

print(loss.grad_fn) # MSELoss
print(loss.grad_fn.next_functions[0][0]) # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # Relu

輸出:

<MseLossBackward0 object at 0x7efad9c382b0>
<AddmmBackward0 object at 0x7efad9c386d8>
<AccumulateGrad object at 0x7efad9c386d8>

Backprop

為了使誤差反向傳播,我們需要做的僅僅是 loss.backward()。但是你需要清楚現有的梯度,否則梯度將會累積到已有的梯度之中。

現在呼叫 loss.backward(),並檢視conv1的偏置在反向傳播前後的梯度。

net.zero_grad() # 將所有引數的梯度快取設定為0

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
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0061, -0.0024, -0.0051, -0.0073,  0.0014,  0.0074])

現在,我們已經知道了如何使用損失函式。

Read Later:

神經網路包中包含多種模型和損失函式,用以組成深度神經網路的構建塊,完整的文件列表在

還剩最後一部分:

  • 更新網路的權重

更新權重

在實踐中最簡單的更新方法是隨機梯度下降(SGD)

weight = weight - learning * gradient

我們可以使用簡單的Python程式碼實現SGD:

learning_rate = 0.1
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

但是,當你使用神經網路時,你想使用多種不同的更新規則,如SGD、Nesterov-SGD、Adam、RMSProp,etc。為了實現這個,我們構建了一個小型的包:torch.optim,可以實現以上所有方法。使用起來也非常簡單。

import torch.optim as optim

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

# 在訓練迴圈中:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update

相關文章