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