Ch01-03.構建神經網絡(Pytorch入門)

使用torch.nn包來構建神經網絡
上一講是autograd,nn包以來autograd包來定義模型并求導,一個nn.Module包含各個層和一個forward(input)方法,該方法返回output
以下為定義一個網絡的例子

定義網絡和處理輸入,調用backward

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 input image channel, 6 output channels, 5*5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y=Wx+b
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        sefl.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2,2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2,2))
        # If the size is a square you can only specify a single number 
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        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()
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函數(用于計算梯度)會被autograde自動創建,可以在forward函數中使用任何針對Tensor的操作,net.parameters() 返回可被學習的參數(權重)列表和值。

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

測試隨機輸入32*32,

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

將所有參數的梯度緩存清零,然后進行隨機梯度的的反向傳播:

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

Note注意點

torch.nn只支持小批量輸入,整個torch.nn包都只支持小批量樣本,不支持單個樣本
如果是單個樣本,需要用 input.unsqueeze(0)來添加他的維數。

回顧目前為止用到的類:

  1. torch.Tensor: 一個自動調用backward()實現 支持自動梯度計算的多維數組,并且保存關于這個向量的梯度
  2. nn.Module: 神經網絡模塊。封裝參數,移動到GPU上運行、導出、加載等
  3. nn.Parameter: 一種變量, 當把它賦值給一個Module時,被自動注冊為一個參數
  4. autograde.Function: 實現一個自動求導操作前向和反向定義,每個變量操作至少創建一個函數節點,每個Tensor的操作都會創建一個接受到創建Tensor和編碼其歷史信息的函數Function節點。

損失函數

一個損失函數接受一對(output, target)作為輸入,計算一個值來估計網絡的輸出和目標值相差多少。
nn包中有很多不同的損失函數,nn.MSELoss是一個比較簡單的損失函數,它計算輸出和目標間的均方誤差。例如

output = net(input)
target = torch.randn(10)   # 隨機值作為樣例
target = target.view(1, -1) # 是target和output的shape一樣
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
# tensor(0.8109, grad_fn=<MseLossBackward>)

現在,如果在反向過程中跟隨loss , 使用它的 .grad_fn 屬性,將看到如下所示的計算圖。
::
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
所以,當我們調用 loss.backward()時,整張計算圖都會 根據loss進行微分,而且圖中所有設置為requires_grad=True的張量 將會擁有一個隨著梯度累積的.grad 張量。

為了說明,讓我們向后退幾步:

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
# <MseLossBackward object at 0x7f3b49fe2470>
# <AddmmBackward object at 0x7f3bb05f17f0>
# <AccumulateGrad object at 0x7f3b4a3c34e0>

反向傳播

調用loss.backward()獲得反向傳播的誤差,但是在調用前需要清除已存在的梯度,否則梯度將被累加到已存在的梯度。

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
# tensor([0., 0., 0., 0., 0., 0.])
# conv1.bias.grad after backward
# tensor([ 0.0051,  0.0042,  0.0026,  0.0152, -0.0040, -0.0036])

更新網絡權重

在實踐中最簡單的權重更新規則是隨機梯度下降(SGD)
weight = weight - learning_rate * gradient
我們可以使用簡單的Python代碼實現這個規則:

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

PyTorch中構建了一個包torch.optim實現其他的不同的更新規則,例如SGD, Nesterov-SGD, Adam, RMSPROP,等。

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

注意optimizer.zero_grad() 手動將梯度緩沖區設置為0, 這是因為按照backprop部分中的說明累積的。

多批次訓練網絡

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2): # epoch number
      running_loss = 0.0
      for i, data in enumerate(trainloader, 0):
          # 獲取輸入
          inputs, labels = data
          #梯度置0
          optimizer.zero_grad()
          # 正向傳播,反向傳播,優化
          outputs = net(inputs)
          loss = criterion(outputs, labels)
          loss.backward()
          optimizer.step()
          # 打印狀態信息
         running_loss += loss.item()
         if i%2000 == 1999:  #每2000打印一次
            pint('[%d, %5d] loss: %.3f' % 
                  (epoch+1, i+1, running_loss/2000))
            running_loss = 0.0 
# 網絡預測
outputs = net(test_images)
_, predicted = torch.max(outputs, 1)
# 整個統計準確率
correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

在GPU上訓練

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 確認我們的電腦支持CUDA,然后顯示CUDA信息:
print(device)

net.to(device)
#inputs, targets和images也要轉換
inputs, labels = inputs.to(device), labels.to(device)

多GPU訓練,后續的 數據并行處理 中介紹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容