PyTorch 笔记(14)— nn.module 实现简单感知机和多层感知机
autograd 實(shí)現(xiàn)了自動(dòng)微分系統(tǒng),然而對(duì)深度學(xué)習(xí)來說過于底層,而本節(jié)將介紹 nn 模塊,是構(gòu)建于 autograd 之上的神經(jīng)網(wǎng)絡(luò)模塊。
1. 簡單感知機(jī)
使用 autograd 可實(shí)現(xiàn)深度學(xué)習(xí)模型,但其抽象程度較低,如果用其來實(shí)現(xiàn)深度學(xué)習(xí)模型,則需要編寫的代碼量極大。在這種情況下,torch.nn 應(yīng)運(yùn)而生,其是專門為深度學(xué)習(xí)設(shè)計(jì)的模塊。
torch.nn 的核心數(shù)據(jù)結(jié)構(gòu)是 Module ,它是一個(gè)抽象的概念,既可以表示神經(jīng)網(wǎng)絡(luò)中的某個(gè)層(layer),也可以表示一個(gè)包含很多層的神經(jīng)網(wǎng)絡(luò)。
在實(shí)際使用中,最常見的做法繼承 nn.Module ,撰寫自己的網(wǎng)絡(luò)層。
下面先來看看如何使用 nn.Module 實(shí)現(xiàn)自己的全連接層。全連接層,又名仿射層,輸入 y 和輸入 x 滿足y=xW+b ,W 和 b 是可學(xué)習(xí)的參數(shù)。
import torch as t
from torch import nnclass Linear(nn.Module):def __init__(self, input_features, out_features):super(Linear, self).__init__() # 等價(jià)于 nn.Module.__init__(self)self.w = nn.Parameter(t.randn(input_features, out_features))self.b = nn.Parameter(t.randn(out_features))def forward(self, x):x = x.mm(self.w)return x + self.b.expand_as(x)layer = Linear(4, 3)x = t.randn(2, 4)
output = layer(x)
print outputfor name, parameter in layer.named_parameters():print name, parameter
output 輸出為 :
tensor([[ 1.5752, 0.6730, -0.0763],[-0.7037, -0.6641, -2.3261]], grad_fn=<ThAddBackward>)
name, parameter 輸出為:
w Parameter containing:
tensor([[-1.0459, -0.1899, 0.2202],[ 1.5751, 0.0613, 1.7350],[-0.2644, 0.7728, 1.4141],[-0.3739, -0.4349, -0.0984]], requires_grad=True)
b Parameter containing:
tensor([1.3054, 0.3063, 0.4375], requires_grad=True)
可見,全連接層的實(shí)現(xiàn)非常簡單,但需注意以下幾點(diǎn):
- 自定義層
Linear必須繼承nn.Module,并且在其構(gòu)造函數(shù)中需調(diào)用nn.Module的構(gòu)造函數(shù),即super(Linear,self).__init()__或nn.Module.__init(self)__; - 在構(gòu)造函數(shù)
__init__中必須自己定義可學(xué)習(xí)的參數(shù),并封裝成Parameter,如在本例中我們把w和b封裝成Parameter。Parameter是一種特殊的Variable,但其默認(rèn)需要求導(dǎo)(requires_grad=True); forward函數(shù)實(shí)現(xiàn)前向傳播過程,其輸入可以是一個(gè)或多個(gè)variable,對(duì)x的任何操作也必須是variable支持的操作。- 無須寫反向傳播函數(shù),因其前向傳播都是對(duì)
variable進(jìn)行操作,nn.Module能夠利用autograd自動(dòng)實(shí)現(xiàn)反向傳播,這一點(diǎn)比Function簡單許多。 - 使用時(shí),直觀上可將
layer看成數(shù)學(xué)概念中的函數(shù),調(diào)用layer(input)即可得到input對(duì)應(yīng)的結(jié)果。它等價(jià)于layers.__call(input)__,在__call__函數(shù)中,主要調(diào)用的是layer.forward(x)。所以在實(shí)際使用中應(yīng)盡量使用layer(x)而不是使用layer.forward(x)。 Module中的可學(xué)習(xí)參數(shù)可以通過named_parameters()或者parameters()返回迭代器,前者會(huì)給每個(gè)parameter附上名字,使其更具有辨識(shí)度。
可見,利用 Module 實(shí)現(xiàn)的全連接層,比利用 Function 實(shí)現(xiàn)的更簡單,因其不再需要寫反向傳播函數(shù)。
2. 多層感知機(jī)
Module 能夠自動(dòng)檢測到自己的 parameter ,并將其作為學(xué)習(xí)參數(shù)。除了 parameter,Module 還包含子Module ,主 Module 能夠遞歸查找子 Module 中的 parameter 。下面再來看看稍微復(fù)雜一點(diǎn)的網(wǎng)絡(luò):多層感知機(jī)。
多層感知機(jī)的網(wǎng)絡(luò)結(jié)構(gòu)如圖所示。它由兩個(gè)全連接層組成,采用 sigmoid 函數(shù)作為激活函數(shù)(圖中沒有畫出)。
實(shí)現(xiàn)代碼如下:
import torch as t
from torch import nnclass Linear(nn.Module):def __init__(self, input_features, out_features):super(Linear, self).__init__() # 等價(jià)于 nn.Module.__init__(self)self.w = nn.Parameter(t.randn(input_features, out_features))self.b = nn.Parameter(t.randn(out_features))def forward(self, x):x = x.mm(self.w)return x + self.b.expand_as(x)class Perceptron(nn.Module):def __init__(self, in_features, hidden_features, out_features):nn.Module.__init__(self)self.layer1 = Linear(in_features, hidden_features) # 此處的 Linear 前面自定義的全連接層self.layer2 = Linear(hidden_features, out_features)def forward(self, x):x = self.layer1(x)x = t.sigmoid(x)return self.layer2(x)perception = Perceptron(3,4,1)
for name, param in perception.named_parameters():print(name, param.size())
輸出結(jié)果:
layer1.w torch.Size([3, 4])
layer1.b torch.Size([4])
layer2.w torch.Size([4, 1])
layer2.b torch.Size([1])
可見,即使是稍復(fù)雜的多層感知機(jī),其實(shí)現(xiàn)依舊很簡單。這里需要注意以下兩個(gè)知識(shí)點(diǎn)。
-
構(gòu)造函數(shù)
__init__中,可利用前面自定義的Linear層(Module)作為當(dāng)前Module對(duì)象的一個(gè)子Module,它的可學(xué)習(xí)參數(shù),也會(huì)成為當(dāng)前Module的可學(xué)習(xí)參數(shù)。 -
在前向傳播函數(shù)中,我們有意識(shí)地將輸出變量都命名為
x,是為了能讓Python回收一些中間層的輸出,從而節(jié)省內(nèi)存。但并不是所有的中間結(jié)果都會(huì)被回收,有些variable雖然名字被覆蓋,但其在反向傳播時(shí)仍需要用到,此時(shí)Python的內(nèi)存回收模塊將通過檢查引用計(jì)數(shù),不會(huì)回收這一部分內(nèi)存。
Module 中 parameter 的全局命名規(guī)范如下:
Parameter直接命名。例如self.param_name = nn.Parameter(t.randn(3,4)),命名為param_name。- 子
Module中的parameter,會(huì)在其名字之前加上當(dāng)前Module的名字。例如self.sub_module = SubModule(),SubModule中有個(gè)parameter的名字也叫作param_name,那么二者拼接而成的parameter name就是sub_module.param_name。
為了方便用戶使用,PyTorch 實(shí)現(xiàn)了神經(jīng)網(wǎng)絡(luò)中絕大多數(shù)的 layer ,這些 layer 都繼承于 nn.Module ,封裝了可學(xué)習(xí)參數(shù) parameter ,并實(shí)現(xiàn)了 forward 函數(shù),且專門針對(duì) GPU 運(yùn)算進(jìn)行了 CuDNN 優(yōu)化,其速度和性能都十分優(yōu)異。
- 構(gòu)造函數(shù)的參數(shù),如
nn.Linear(in_features,out_features,bias),需關(guān)注這三個(gè)參數(shù)的作用。 - 屬性、可學(xué)習(xí)參數(shù)和子
Module。如nn.Linear中有weight和bias兩個(gè)可學(xué)習(xí)參數(shù),不包含子Module。 - 輸入輸出的形狀,如
nn.Linear的輸入形狀是(N,input_features),輸出形狀為(N,output_features),N是batch_size。
這些自定義 layer 對(duì)輸入形狀都有假設(shè):輸入的不是單個(gè)數(shù)據(jù),而是一個(gè) batch 。若想輸入一個(gè)數(shù)據(jù),必須調(diào)用 unsqueeze(0) 函數(shù)將數(shù)據(jù)偽裝成 batch_size=1 的 batch 。
總結(jié)
以上是生活随笔為你收集整理的PyTorch 笔记(14)— nn.module 实现简单感知机和多层感知机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 钻石烟多少钱啊?
- 下一篇: PyTorch 笔记(15)— 分别使用