MXNet模型参数的访问、初始化、自定义初始化、共享

        训练模型的目的其实就是获得一系列优化好了的参数,自从我们从最开始学习感知机的时候,发现就是在调参数,为了得到我们想要的结果,我们在有意的设计权重值和偏差值对吧,再到后面的神经网络的学习,其实都是在为了找到更好的参数而做各种方法的学习。

访问模型参数

        在MXNet框架中,如何去访问模型里面的参数就显得很重要了,比如怎么访问权重值、偏差值、梯度值等。

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)

print(net)
print(net[0].params)
print(type(net[0].params))

'''
Sequential(
  (0): Dense(20 -> 256, Activation(relu))
  (1): Dense(256 -> 10, linear)
)
dense42_ (
  Parameter dense42_weight (shape=(256, 20), dtype=float32)
  Parameter dense42_bias (shape=(256,), dtype=float32)
)
<class 'mxnet.gluon.parameter.ParameterDict'>
'''

既然这些参数是字典[ParameterDict]类型,那当然可以按照字典的访问方式来访问,net[0].params[‘dense42_weight’],不过这个缺点就在于,每次运行之后的key值是递增变化的,很不方便,所以使用另外一种更好的方法 net[0].weight

获取权重参数的值
net[0].weight.data()

获取权重梯度的值(由于还没有进行反向传播计算,所以值都为0)
net[0].weight.grad()

获取输出层的偏差值
net[1].bias.data()

获取所有嵌套层所包含的所有参数
net.collect_params()

通过正则表达式的筛选来获取想要的参数
如:获取权重的所有参数(名称以weight结尾)
net.collect_params(‘.*weight’)
sequential28_ (
  Parameter dense56_weight (shape=(256, 20), dtype=float32)
  Parameter dense57_weight (shape=(10, 256), dtype=float32)
)

初始化模型参数

        前面代码中的net.initialize()初始化是默认的,权重参数在[-0.07,0.07]之间均匀分布的随机数,偏差值为0,当然我们也可以指定参数初始化的方法。
将权重初始化为均值为0,标准差为0.01的正态分布

net.initialize(init=init.Normal(sigma=0.01),force_reinit=True)
print(net[0].weight.data()[0])
[ 0.00454064 -0.00097203  0.00402088 -0.00739811  0.00281988  0.00965765
 -0.00093962 -0.00144071  0.00085085 -0.01153225  0.00842997  0.00462589
 -0.001109   -0.01724745  0.00777816 -0.00330855  0.01251164  0.00357965
 -0.00691929 -0.00489703]
<NDArray 20 @cpu(0)>

初始化权重参数为常数

net.initialize(init=init.Constant(2),force_reinit=True)
print(net[0].weight.data()[0])
[2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
<NDArray 20 @cpu(0)>

更多的初始化方法可以dir(init)查看

自定义初始化参数

除了init模块提供的一些初始化方法之外,我们还可以自己定义参数初始化的方法,重写_init_weight方法:

class MyInit(init.Initializer):
    def _init_weight(self,name,data):
        print(name,data.shape)
        data[:]=nd.random.uniform(low=-10,high=10,shape=data.shape)
        data *= data.abs()>=5 #保留绝对值大于等于5的值,其余为0

net.initialize(MyInit(),force_reinit=True)
print(net[0].weight.data()[0])

dense82_weight (256, 20)
dense83_weight (10, 256)

[-0.        -6.2531586 -0.        -9.615191  -0.         5.410777
  0.        -5.572157  -6.006543  -7.731353   7.2811546 -0.
  0.        -6.851571   9.39822   -0.         9.741699  -8.219797
 -6.29636   -0.       ]
<NDArray 20 @cpu(0)>

 还可以通过set_data函数来直接修改模型参数,如:将第一层的权重值加1

wdata[:]=net[0].weight.data()
print(wdata[0])
net[0].weight.set_data(wdata+1)
print(wdata[0])

[ 9.04821   -0.        -5.4276333  9.508186   6.7440395  8.918404
 -0.        -0.         9.5848675  6.2877026  6.996353  -0.
  8.098387   0.         0.        -0.        -0.        -6.066473
 -6.0494995  0.       ]
<NDArray 20 @cpu(0)>

[10.04821    1.        -4.4276333 10.508186   7.7440395  9.918404
  1.         1.        10.5848675  7.2877026  7.996353   1.
  9.098387   1.         1.         1.         1.        -5.066473
 -5.0494995  1.       ]
<NDArray 20 @cpu(0)>

共享模型参数

可以在指定层选择特定参数,当层选择同样参数的时候,在前向计算和反向传播时都会共享相同的参数

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))
net.initialize()

X=nd.random.uniform(shape=(2,20))
net(X)
print(net[1].weight.data()[0])
print(net[2].weight.data()[0])

[-0.05463018  0.00432042  0.01488315 -0.05806737  0.02845045  0.02319488
  0.01887009 -0.06585113]
<NDArray 8 @cpu(0)>

[-0.05463018  0.00432042  0.01488315 -0.05806737  0.02845045  0.02319488
  0.01887009 -0.06585113]
<NDArray 8 @cpu(0)>

延后初始化(DeferredInitialization)

        在MXNet框架中,大家也发现了,对于全连接层都没有指定输入个数,只指定了输出个数,这样的好处就是我们构造层的时候不需要去推算每层输入的个数,但是也只有输入个数确定了,才可以做前向计算,才能真正的初始化参数,这种情况,我们叫做延后初始化,就是说只有获得足够信息时才去执行的一种行为。 

class MyInit(init.Initializer):
    def _init_weight(self,name,data):
        print('Init',name,data.shape)
    
net=nn.Sequential()
net.add(nn.Dense(256,activation='relu'),nn.Dense(10))
net.initialize(init=MyInit())

这种情况没有打印任何信息,调用initialize函数并没有初始化参数。
我们做一次前向计算:

X=nd.random.uniform(shape=(2,20))
Y=net(X)

'''
Init dense12_weight (256, 20)
Init dense13_weight (10, 256)
'''

避免延后初始化

        虽然延后初始化提供了便利,但在某些情况下也造成了困惑,比如第一次前向计算之前,我们就没有办法获取和修改参数,不能灵活的操作模型参数,因此,我们经常需要额外做一次前向计算让参数初始化。如果在调用initialize函数时知道参数的形状,也可以避免延后初始化:
1、强制初始化,前提条件是已经初始化的模型

​​​​​​​net.initialize(init=MyInit(),force_reinit=True)

2、知道了输入个数

net.add(nn.Dense(256,in_units=20,activation='relu'),nn.Dense(10,in_units=256))
net.initialize(init=MyInit())

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年5月17日
下一篇 2022年5月17日

相关推荐