上一小節(jié)講的是怎么自定義初始化參數(shù)。
這一節(jié)是看怎么自定義層。

這樣可以想一下之前接觸的樓層是什么。比如nn.Linear,nn.ReLU等。他們的作用就是作為某一層的處理。他們兩個(gè)的區(qū)別在于前者有參數(shù),后者是沒有參數(shù)列表的。那現(xiàn)在我們也來實(shí)現(xiàn)一些有參數(shù)和沒有參數(shù)列表的層操作。

import torchimport torch.nn.functional as Ffrom torch import nn

不帶參數(shù)的層

class CenteredLayer(nn.Module):    def __init__(self):        super().__init__()    def forward(self, X):        return X - X.mean()

我們也只需要定義前向傳播就可以了。這個(gè)自建一層的作用是讓每一個(gè)特征量都減去其平均值。

layer = CenteredLayer()X = torch.arange(5)*0.1print(layer(X))
>>tensor([-0.2000, -0.1000,  0.0000,  0.1000,  0.2000])

經(jīng)過測(cè)試我們可以看到這個(gè)層是完全有效的。

那如果將其放到復(fù)雜的模型之中呢。

net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())Y = torch.rand(10, 8)print(net(Y).mean().data)
>>tensor(7.8231e-09)

好吧,這個(gè)模型其實(shí)并不復(fù)雜,它只有兩層。第一個(gè)是一個(gè)線性層。第二個(gè)就是我們的自定義層。

生成一組隨機(jī)的測(cè)試數(shù)據(jù)Y。然后使用我們構(gòu)建的網(wǎng)絡(luò)對(duì)外進(jìn)行計(jì)算,然后輸出其結(jié)果的平均值。

不出意外結(jié)果應(yīng)該是0。雖然這里顯示的不是0。這是因?yàn)楦↑c(diǎn)數(shù)的存儲(chǔ)精度問題,你當(dāng)然可以把這個(gè)極小的數(shù)近似看作它是0。

至于結(jié)果為什么失靈,這是一個(gè)數(shù)學(xué)問題,會(huì)去列幾個(gè)數(shù)字自己算一下就明白了。

帶參數(shù)的層

class MyLinear(nn.Module):    def __init__(self, in_units, units):        super().__init__()        self.weight = nn.Parameter(torch.ones(in_units, units))        self.bias = nn.Parameter(torch.zeros(units,))    def forward(self, X):        linear = torch.matmul(X, self.weight.data) + self.bias.data        return F.relu(linear)

這個(gè)租賃一層是自定義實(shí)現(xiàn)了一個(gè)全鏈接層。這個(gè)層里的參數(shù)需要用到權(quán)重和偏置,在計(jì)算之后最后返回再使用ReLU激活函數(shù)。

linear = MyLinear(5, 3)print(linear.weight.data)
>>tensor([[ 1.0599,  0.3885,  1.2025],        [-1.8313,  0.2097, -1.6529],        [ 1.4119,  0.2675, -0.4148],        [ 0.2596, -0.0319,  1.9548],        [-1.2874,  1.0776,  0.5804]])

輸出它的權(quán)重看一下,確實(shí)是能生成5×3的權(quán)重矩陣。

X = torch.rand(2, 5)linear(X)
>>tensor([[2.3819, 2.3819, 2.3819],        [1.8295, 1.8295, 1.8295]])

單層測(cè)試結(jié)果也沒有問題。

net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))net(torch.rand(2, 64))
>>tensor([[0.4589],        [0.0000]])

將其放在網(wǎng)絡(luò)中結(jié)果也沒有問題。

現(xiàn)在我來放一段對(duì)比代碼,就是我們自己寫的這個(gè)層和pytorch人家寫的層該怎么實(shí)現(xiàn)同樣的功能。

net1 = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))net2 = nn.Sequential(nn.Linear(64,8),                     nn.ReLU(),                     nn.Linear(8,1),                     nn.ReLU())def init(m):    if type(m)==nn.Linear:        nn.init.ones_(m.weight)        nn.init.zeros_(m.bias)net2.apply(init)Y = torch.rand(4, 64)print(net1(Y).data)print(net2(Y).data)
>>tensor([[270.5055],        [253.7892],        [238.7834],        [258.4998]])tensor([[270.5055],        [253.7892],        [238.7834],        [258.4998]])

這樣乍一看是不是兩個(gè)結(jié)果完全一樣。

相對(duì)于pytorch自帶的實(shí)現(xiàn)來說,這個(gè)不需要你寫一個(gè)加權(quán)重的過程,也不需要你再加一個(gè)ReLU層。

這樣看起來很省事,但是實(shí)際中不建議你自己實(shí)現(xiàn)pytorch之中已經(jīng)有的功能。因?yàn)槭褂萌思业姆椒ㄓ?jì)算效率更高。