『MXNet』第三彈_Gluon模型參數


MXNet中含有init包,它包含了多種模型初始化方法。

from mxnet import init, nd
from mxnet.gluon import nn

net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize()

x = nd.random.uniform(shape=(2,20))
y = net(x)

一、訪問模型參數

我們知道可以通過[]來訪問Sequential類構造出來的網絡的特定層。對於帶有模型參數的層,我們可以通過Block類的params屬性來得到它包含的所有參數。例如我們查看隱藏層的參數:

print(net[0].params)
print(net[0].collect_params())
dense0_ (
  Parameter dense0_weight (shape=(256, 20), dtype=float32)
  Parameter dense0_bias (shape=(256,), dtype=float32)
)
dense0_ (
  Parameter dense0_weight (shape=(256, 20), dtype=float32)
  Parameter dense0_bias (shape=(256,), dtype=float32)
)

 有意思的是,

print(net.params)
print(net.collect_params())
sequential0_ (

)
sequential0_ (
  Parameter dense0_weight (shape=(256, 20), dtype=float32)
  Parameter dense0_bias (shape=(256,), dtype=float32)
  Parameter dense1_weight (shape=(10, 256), dtype=float32)
  Parameter dense1_bias (shape=(10,), dtype=float32)
)

 為了訪問特定參數,我們既可以通過名字來訪問字典里的元素,也可以直接使用它的變量名。下面兩種方法是等價的,但通常后者的代碼可讀性更好,

(net[0].params['dense0_weight'], net[0].weight)

 (Parameter dense0_weight (shape=(256, 20), dtype=float32),

Parameter dense0_weight (shape=(256, 20), dtype=float32))

Parameter類

具有方法:.data()/.grad()/.set_data()/.initialize()……
其包含參數權重和它對應的梯度,它們可以分別通過 datagrad函數來訪問:
net[0].weight.data()
net[0].weight.grad()

聲明方法一

至於Parameter類,是指mxnet.gluon.Parameter,聲明需要名字尺寸

my_param = gluon.Parameter('exciting_parameter_yay', shape=(3, 3))
my_param.initialize()

聲明方法二

我們還可以使用Block自帶的ParameterDict類的成員變量params。顧名思義,這是一個由字符串類型的參數名字映射到Parameter類型的模型參數的字典。

我們可以通過get函數從ParameterDict創建Parameter,同樣的聲明需要名字尺寸

params = gluon.ParameterDict()
params.get("param2", shape=(2, 3))
params
(
  Parameter param2 (shape=(2, 3), dtype=<class 'numpy.float32'>)
)

形如net.params返回的就是一個ParameterDict類,自己的層class書寫時,就是用這個方式創建參數並被層class感知(收錄進層隸屬的ParameterDict類中)。

二、初始化模型參數

當使用默認的模型初始化,Gluon會將權重參數元素初始化為 [-0.07, 0.07]之間均勻分布的隨機數,偏差參數則全為0. 但經常我們需要使用其他的方法來初始話權重,MXNet的 init模塊里提供了多種預設的初始化方法。例如下面例子我們將權重參數初始化成均值為0,標准差為0.01的正態分布隨機數。
# 非首次對模型初始化需要指定 force_reinit
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
  • mxnet.init模塊
  • force_reinit為了防止用戶失誤將參數全部取消

自定義初始化函數

有時候我們需要的初始化方法並沒有在init模塊中提供,這時我們有兩種方法來自定義參數初始化。

一種是實現一個Initializer類的子類

使得我們可以跟前面使用init.Normal那樣使用它。在這個方法里,我們只需要實現_init_weight這個函數,將其傳入的NDArray修改成需要的內容。下面例子里我們把權重初始化成[-10,-5][5,10]兩個區間里均勻分布的隨機數,教程只講了weight初始化,后面查看源碼還有bias的,以及一些其他的子方法,理論上用時再研究,不過方法二明顯更簡單易用……

class MyInit(init.Initializer):
    def _init_weight(self, name, data):
        print('Init', name, data.shape)
        data[:] = nd.random.uniform(low=-10, high=10, shape=data.shape)
        data *= data.abs() >= 5

net.initialize(MyInit(), force_reinit=True)
net[0].weight.data()[0]
Init dense0_weight (256, 20)
Init dense1_weight (10, 256)
[-5.36596727  7.57739449  8.98637581 -0.          8.8275547   0.
  5.98405075 -0.          0.          0.          7.48575974 -0.         -0.
  6.89100075  6.97887039 -6.11315536  0.          5.46652031 -9.73526287
  9.48517227]
<NDArray 20 @cpu(0)>

第二種方法是我們通過Parameter類的set_data函數

可以直接改寫模型參數。例如下例中我們將隱藏層參數在現有的基礎上加1.

net[0].weight.set_data(net[0].weight.data()+1)
net[0].weight.data()[0]
[ -4.36596727   8.57739449   9.98637581   1.           9.8275547    1.
   6.98405075   1.           1.           1.           8.48575974   1.           1.
   7.89100075   7.97887039  -5.11315536   1.           6.46652031
  -8.73526287  10.48517227]
<NDArray 20 @cpu(0)>

三、共享模型參數

在有些情況下,我們希望在多個層之間共享模型參數。我們在“模型構造”這一節看到了如何在Block類里forward函數里多次調用同一個類來完成。這里將介紹另外一個方法,它在構造層的時候指定使用特定的參數。如果不同層使用同一份參數,那么它們不管是在前向計算還是反向傳播時都會共享共同的參數。

原理:層函數params API接收其他層函數的params屬性即可

在下面例子里,我們讓模型的第二隱藏層和第三隱藏層共享模型參數:

from mxnet import nd
from mxnet.gluon import nn

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]

 [ 1. 1. 1. 1. 1. 1. 1. 1.]

<NDArray 8 @cpu(0)>

我們在構造第三隱藏層時通過params來指定它使用第二隱藏層的參數。由於模型參數里包含了梯度,所以在反向傳播計算時,第二隱藏層和第三隱藏層的梯度都會被累加在shared.params.grad()里。

四、模型延后初始化

從下面jupyter可以看到模型參數實際初始化的時機:既不是調用initialize時,也不是沒次運行時,僅僅第一次送入數據運行時會調用函數進行參數初始化,所以MXNet不需要定義指定輸入數據尺寸的關鍵也在這里。

立即初始化參數

基於以上,當模型獲悉數據尺寸時不會發生延后,

  • 已經運行過,指定重新初始化時不會延后
  • 定義過程中,指定in_units的模型不會延后

代碼如下,

最后一格代碼塊中,第二個dense層去掉in_uints的話僅僅第一個dense會立即初始化。

數據形狀改變時網絡行為

如果在下一次net(x)前改變x形狀,包括批量大小和特征大小,會發生什么?

批量大小改變不影響,特征大小改變會報錯。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM