一. 訪問模型參數
對于使用Sequential類構造的神經網絡,我們可以通過方括號[]來訪問網絡的任一層。回憶一下上一節中提到的Sequential類與Block類的繼承關系。
對于Sequential實例中含模型參數的層,我們可以通過Block類的params屬性來訪問該層包含的所有參數。
下面,訪問多層感知機net中隱藏層的所有參數。索引0表示隱藏層為Sequential實例最先添加的層。
net[0].params, type(net[0].params)
可以看到,我們得到了一個由參數名稱映射到參數實例的字典(類型為ParameterDict類)。其中權重參數的名稱為dense0_weight,它由net[0]的名稱(dense0_)和自己的變量名(weight)組成。而且可以看到,該參數的形狀為(256, 20),且數據類型為32位浮點數(float32)。為了訪問特定參數,我們既可以通過名字來訪問字典里的元素,也可以直接使用它的變量名。下面兩種方法是等價的,但通常后者的代碼可讀性更好。
net[0].params['dense0_weight'], net[0].weight
Gluon里參數類型為Parameter類,它包含參數和梯度的數值,可以分別通過data函數和grad函數來訪問。因為我們隨機初始化了權重,所以權重參數是一個由隨機數組成的形狀為(256, 20)的NDArray。
net[0].weight.data()
權重梯度
net[0].weight.grad()
輸出層的偏差值。
net[1].bias.data()
最后,我們可以使用collect_params函數來獲取net變量所有嵌套(例如通過add函數嵌套)的層所包含的所有參數。它返回的同樣是一個由參數名稱到參數實例的字典。
net.collect_params()
二. 初始化模型參數
權重參數元素為[-0.07, 0.07]之間均勻分布的隨機數,偏差參數則全為0。但我們經常需要使用其他方法來初始化權重。MXNet的init模塊里提供了多種預設的初始化方法。在下面的例子中,我們將權重參數初始化成均值為0、標準差為0.01的正態分布隨機數,并依然將偏差參數清零。
# 非首次對模型初始化需要指定force_reinit為真
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
net[0].weight.data()[0]
下面使用常數來初始化權重參數。
net.initialize(init=init.Constant(1), force_reinit=True)
net[0].weight.data()[0]
如果只想對某個特定參數進行初始化,我們可以調用Parameter類的initialize函數,它與Block類提供的initialize函數的使用方法一致。下例中我們對隱藏層的權重使用Xavier隨機初始化方法。
net[0].weight.initialize(init=init.Xavier(), force_reinit=True)
net[0].weight.data()[0]
三. 共享模型參數
在有些情況下,我們希望在多個層之間共享模型參數。“模型構造”一節介紹了如何在Block類的forward函數里多次調用同一個層來計算。這里再介紹另外一種方法,它在構造層的時候指定使用特定的參數。如果不同層使用同一份參數,那么它們在前向計算和反向傳播時都會共享相同的參數。在下面的例子里,我們讓模型的第二隱藏層(shared變量)和第三隱藏層共享模型參數。
net = nn.Sequential()
shared = nn.Dense(8, activation='relu')
net.add(nn.Dense(8, activation='relu'),shared,nn.Dense(8, activation='relu', params=shared.params),nn.Dense(10))
net.initialize()X = nd.random.uniform(shape=(2, 20))
net(X)net[1].weight.data()[0] == net[2].weight.data()[0]
我們在構造第三隱藏層時通過params來指定它使用第二隱藏層的參數。因為模型參數里包含了梯度,所以在反向傳播計算時,第二隱藏層和第三隱藏層的梯度都會被累加在shared.params.grad()里。