總結一下相關概念:
- torch.Tensor - 一個近似多維數組的數據結構
- autograd.Variable - 改變Tensor並且記錄下來操作的歷史記錄。和Tensor擁有相同的API,以及backward()的一些API。同時包含着和張量相關的梯度
- nn.Module - 神經網絡模塊,便捷的數據封裝,能夠將運算移往GPU,還包括一些輸入輸出的東西
- nn.Parameter - 一種變量(Variable),當將任何值賦予Module時自動注冊為一個參數
- autograd.Function - 實現了使用自動求導方法的前饋和后饋的定義。每個Variable的操作都會生成至少一個獨立的Function節點,與生成了Variable的函數相連之后記錄下操作歷史
導入庫:
# Author : Hellcat # Time : 2018/2/10 import torch as t import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torch.autograd import Variable
torch.nn:網絡層
torch.nn.functional:激活函數、池化函數歸於此模塊
pytorch中的網絡層是class,而tensorflow
print(t.nn.Conv2d)
<class 'torch.nn.modules.conv.Conv2d'>
print(tf.nn.conv2d)
<function conv2d at 0x000001A33CC44510>
網絡主體:
net網絡要使用class並繼承父類才行,因而有一些自帶的方法
net.parameters():返回全部的參數值,迭代器
net.named_parameters():返回參數名稱和值,迭代器
net.參數名:就是參數變量,Variable,可以直接查看data和grad等等
class Net(nn.Module): def __init__(self): # nn.Module子類的函數必須在構造函數中執行父類的構造函數 # 等價於nn.Model.__init__(self) super(Net,self).__init__() # 輸入1通道,輸出6通道,卷積核5*5 self.conv1 = nn.Conv2d(1, 6, 5) # 定義卷積層:輸入6張特征圖,輸出16張特征圖,卷積核5x5 self.conv2 = nn.Conv2d(6,16,5) # 定義全連接層:線性連接(y = Wx + b),16*5*5個節點連接到120個節點上 self.fc1 = nn.Linear(16*5*5,120) # 定義全連接層:線性連接(y = Wx + b),120個節點連接到84個節點上 self.fc2 = nn.Linear(120,84) # 定義全連接層:線性連接(y = Wx + b),84個節點連接到10個節點上 self.fc3 = nn.Linear(84,10) # 定義向前傳播函數,並自動生成向后傳播函數(autograd) def forward(self,x): # 輸入x->conv1->relu->2x2窗口的最大池化->更新到x x = F.max_pool2d(F.relu(self.conv1(x)),(2,2)) # 輸入x->conv2->relu->2x2窗口的最大池化->更新到x x = F.max_pool2d(F.relu(self.conv2(x)),2) # view函數將張量x變形成一維向量形式,總特征數不變,為全連接層做准備 x = x.view(x.size()[0], -1) # 輸入x->fc1->relu,更新到x x = F.relu(self.fc1(x)) # 輸入x->fc2->relu,更新到x x = F.relu(self.fc2(x)) # 輸入x->fc3,更新到x x = self.fc3(x) return x if __name__ == "__main__": 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) (fc2): Linear(in_features=120, out_features=84) (fc3): Linear(in_features=84, out_features=10) ) """ # 返回參數值:順序和下面的named一致 params = list(net.parameters()) print(len(params)) """ 10 """ # net.named_parameters(): ((參數名稱,參數屬性),……) for name, parameters in net.named_parameters(): print(name, ":", parameters.size()) """ conv1.weight : torch.Size([6, 1, 5, 5]) conv1.bias : torch.Size([6]) conv2.weight : torch.Size([16, 6, 5, 5]) conv2.bias : torch.Size([16]) fc1.weight : torch.Size([120, 400]) fc1.bias : torch.Size([120]) fc2.weight : torch.Size([84, 120]) fc2.bias : torch.Size([84]) fc3.weight : torch.Size([10, 84]) fc3.bias : torch.Size([10]) """
模擬單次向前&向后傳播:
# #########網絡傳播過程模擬######### # 輸入如果沒有batch數,則少一維度,Tensor,unsqueeze()可以為張量擴維 input_ = Variable(t.randn(1, 1, 32, 32)) out = net(input_) print(out.size()) """ torch.Size([1, 10]) """ # net.zero_grad() # 輸出值為10個標量(一個向量),所以需要指定每個標量梯度的權重 # out.backward(t.ones(1,10))
注意: torch.nn 只接受小批量的數據
整個torch.nn包只接受那種小批量樣本的數據,而非單個樣本。 例如,nn.Conv2d能夠結構一個四維的TensornSamples x nChannels x Height x Width。
如果你拿的是單個樣本,使用input.unsqueeze(0)來加一個假維度就可以了。
維度是[batch,channel,height,width]。
Loss函數構建
# #########Loss設計######### target = Variable(t.arange(0,10)) # Loss需要先實例化,然后是callable的實例 loss_fn = nn.MSELoss() # 均方誤差 loss = loss_fn(out, target) print(loss) net.zero_grad() print("反向傳播之前:", net.conv1.bias.grad) loss.backward() print("反向傳播之后:", net.conv1.bias.grad)
反向傳播之前: None
反向傳播之后: Variable containing:
-0.1330
-0.0888
-0.0101
-0.0186
0.0462
0.0317
[torch.FloatTensor of size 6]
優化器構建
# #########優化器設計######### print(net.parameters()) """ <generator object Module.parameters at 0x0000021B525BE888> """ # 初始化優化器 optimizer = optim.SGD(net.parameters(), lr=0.01) optimizer.zero_grad() # 效果等同net.zero_grad() output = net(input_) loss = loss_fn(output, target) loss.backward() print("反向傳播之前:", net.conv1.bias.data) optimizer.step() print("反向傳播之后:", net.conv1.bias.data)
反向傳播之前:
-0.1702
0.1192
0.1349
0.1307
-0.0141
-0.0558
[torch.FloatTensor of size 6]
反向傳播之后:
-0.1689
0.1201
0.1350
0.1309
-0.0146
-0.0561
[torch.FloatTensor of size 6]