關于torch.nn:
使?Pytorch來構建神經?絡, 主要的?具都在torch.nn包中.
nn依賴于autograd來定義模型, 并對其?動求導.
構建神經?絡的典型流程:
- 定義?個擁有可學習參數的神經?絡
- 遍歷訓練數據集
- 處理輸?數據使其流經神經?絡
- 計算損失值
- 將?絡參數的梯度進?反向傳播
- 以?定的規則更新?絡的權重
我們?先定義?個Pytorch實現的神經?絡:
# 導?若??具包
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, 卷積核??3*3self.conv1 = nn.Conv2d(1, 6, 3)# 定義第?層卷積神經?絡, 輸?通道維度=6, 輸出通道維度=16, 卷積核??3*3self.conv2 = nn.Conv2d(6, 16, 3)# 定義三層全連接?絡self.fc1 = nn.Linear(16 * 6 * 6, 120)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 = 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 xdef num_flat_features(self, x):# 計算size, 除了第0個維度上的batch_sizesize = x.size()[1:]num_features = 1for s in size:num_features *= sreturn num_features
net = Net()
print(net)
輸出結果:
Net((conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))(conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))(fc1): Linear(in_features=576, 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)
)
注意:
模型中所有的可訓練參數, 可以通過net.parameters()來獲得.
params = list(net.parameters())
print(len(params))
print(params[0].size())
輸出結果:
10
torch.Size([6, 1, 3, 3])
輸出結果:
假設圖像的輸?尺?為32 * 32:
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
輸出結果:
tensor([[ 0.1242, 0.1194, -0.0584, -0.1140, 0.0661, 0.0191, -0.0966,
0.0480, 0.0775, -0.0451]], grad_fn=<AddmmBackward>)
有了輸出張量后, 就可以執?梯度歸零和反向傳播的操作了.
net.zero_grad()
out.backward(torch.randn(1, 10))
注意:
- torch.nn構建的神經?絡只?持mini-batches的輸?, 不?持單?樣本的輸?.
- ?如: nn.Conv2d 需要?個4D Tensor, 形狀為(nSamples, nChannels, Height, Width). 如果你的輸?只有單?樣本形式, 則需要執?input.unsqueeze(0), 主動將3D Tensor擴充成4D Tensor.
損失函數
- 損失函數的輸?是?個輸?的pair: (output, target), 然后計算出?個數值來評估output和target之間的差距??.
- 在torch.nn中有若?不同的損失函數可供使?, ?如nn.MSELoss就是通過計算均?差損失來評估輸?和?標值之間的差距.
應?nn.MSELoss計算損失的?個例?:
output = net(input)
target = torch.randn(10)
# 改變target的形狀為?維張量, 為了和output匹配
target = target.view(1, -1)
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
輸出結果:
tensor(1.1562, 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的Tensors都將參與梯度求導的運算, 并將梯度累加到Tensors中的.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
輸出結果:

反向傳播(backpropagation)
在Pytorch中執?反向傳播?常簡便, 全部的操作就是loss.backward().
在執?反向傳播之前, 要先將梯度清零, 否則梯度會在不同的批次數據之間被累加.
執??個反向傳播的?例?:
# Pytorch中執?梯度清零的代碼
net.zero_grad()
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
# Pytorch中執?反向傳播的代碼
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.0002, 0.0045, 0.0017, -0.0099, 0.0092, -0.0044])
更新?絡參數
- 更新參數最簡單的算法就是SGD(隨機梯度下降).
- 具體的算法公式表達式為: weight = weight - learning_rate * gradient
?先?傳統的Python代碼來實現SGD如下:
learning_rate = 0.01
for f in net.parameters():f.data.sub_(f.grad.data * learning_rate)
然后使?Pytorch官?推薦的標準代碼如下:
# ?先導?優化器的包, optim中包含若?常?的優化算法, ?如SGD, Adam等
import torch.optim as optim
# 通過optim創建優化器對象
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 將優化器執?梯度清零的操作
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
# 對損失值執?反向傳播的操作
loss.backward()
# 參數的更新通過??標準代碼來執?
optimizer.step()
?節總結
- 學習了構建?個神經?絡的典型流程:
- 定義?個擁有可學習參數的神經?絡
- 遍歷訓練數據集
- 處理輸?數據使其流經神經?絡
- 計算損失值
- 將?絡參數的梯度進?反向傳播
- 以?定的規則更新?絡的權重
- 學習了損失函數的定義:
- 采?torch.nn.MSELoss()計算均?誤差.
- 通過loss.backward()進?反向傳播計算時, 整張計算圖將對loss進??動求導, 所有屬性
- requires_grad=True的Tensors都將參與梯度求導的運算, 并將梯度累加到Tensors中
- 的.grad屬性中.
- 學習了反向傳播的計算?法:
- 在Pytorch中執?反向傳播?常簡便, 全部的操作就是loss.backward().
- 在執?反向傳播之前, 要先將梯度清零, 否則梯度會在不同的批次數據之間被累加.
- net.zero_grad()
- loss.backward()
- 學習了參數的更新?法:
- 定義優化器來執?參數的優化與更新.
- optimizer = optim.SGD(net.parameters(), lr=0.01)
- 通過優化器來執?具體的參數更新.
- optimizer.step()