模型参数的访问、初始化和共享 讨论区


#22

第三点应该是bug,应该是gluon里面的parameter.py实现中忽略了bias (但是我没太看懂 :sweat_smile:, 主要initializer类应该是没问题的)

可以采用一种蛮无聊的强制初始化参数:
net[0].bias.initialize(init=MyInit(), force_reinit=True) (此时bias也会被赋予5~10,因此如果采用这种方式需要再写一个MyInit类)

第一点是为了“输出更全的信息”:这在后面教程中比如 y=net(x)时会输出每层的输出信息


#23

关于第三个问题,如果两个层共用一个参数,那么求梯度的时候会发生什么?
我觉得前向传播会将参数乘两次,所以在反传是也应该分别求出梯度,分别更新参数,但是第二次会覆盖第一次的结果,所以我们只能看到一个值。
没想好怎么测试,怎样在每次w变换是print(w)呢?


#24


nn.Dense中的shape为什么要写成shape=(units, in_units)而不是shape=(in_units, units),原因是因为表达式是这样Y = XW^T + b写的吗?如果写成后一种形式岂不是不需要加转置了?这样不是更快了吗?


#25

练习2.如何对每个层使用不同的初始化函数 (整理的方法)

根据大家讨论,整理两种方法可以去实现

1.先构建网络,重新为每一层初始化

net = nn.Sequential()
with net.name_scope():
    net.add(nn.Dense(4, in_units=4, activation='relu'))
    net.add(nn.Dense(2, in_units=4))

print(net, '\n')

print('original weight')

net.initialize()
params = net.collect_params()
print(params, '\n')

print(net[0].weight.data())
print(net[1].weight.data())


print('\n' ,'initialize for every layer')
# you should change 'sequential1_...'
params['sequential1_dense0_weight'].initialize(init=init.One(), force_reinit=True)
params['sequential1_dense1_weight'].initialize(init=init.Xavier(), force_reinit=True)

print(net[0].weight.data())
print(net[1].weight.data())

2.构建网络时,为每一层初始化

net = nn.Sequential()
with net.name_scope():
    net.add(nn.Dense(4, in_units=4, activation='relu', weight_initializer = init.One()))
    net.add(nn.Dense(2, in_units=4, weight_initializer = init.Xavier()))
print(net, '\n')

net.initialize()
print(net[0].weight.data())
print(net[1].weight.data())

说明: 网络构建完后,又使用 net.initialize(), 好像不会影响网络内部的每一层的初始化


#26

很好奇initialize()函数默认的initializer(即default_init参数)是什么。查了下文档好像并没有说得很清楚。


#27

之前能运行的代码现在突然不能运行了


#28

关于练习3,用了个简单的例子试了下,发现如果两个层共用一个参数,那么求梯度的时候,首先两层的梯度是一样的,其次更新参数时,共享的参数只被更新了一次,验证如下:

定义初始化方法:

class MyInit(init.Initializer):
    def __init__(self):
        super(MyInit, self).__init__()
        self._verbose = True
    def _init_weight(self, _, arr):
        # 初始化权重,使用out=arr后我们不需指定形状
        print('init weight', arr.shape)
        nd.random.uniform(low=5, high=10, out=arr)

建造网络:

net = nn.Sequential()
with net.name_scope():
    net.add(nn.Dense(4, in_units=4, activation="relu"))
    net.add(nn.Dense(4, in_units=4, activation="relu", params=net[-1].params))
    net.add(nn.Dense(1, in_units=4))

初始化后打印:

net.initialize(MyInit())
print(net[0].weight.data())
print(net[1].weight.data())

[[ 5.46189547  7.81379843  6.59319878  7.55513477]
 [ 8.01868248  5.74653292  6.95085335  5.98721313]
 [ 6.27645302  8.57763481  8.91821766  5.43609715]
 [ 7.39816093  8.00399208  9.88701534  9.65355492]]
<NDArray 4x4 @cpu(0)>
[[ 5.46189547  7.81379843  6.59319878  7.55513477]
 [ 8.01868248  5.74653292  6.95085335  5.98721313]
 [ 6.27645302  8.57763481  8.91821766  5.43609715]
 [ 7.39816093  8.00399208  9.88701534  9.65355492]]
<NDArray 4x4 @cpu(0)>

更新一次参数,使用sgd:

from mxnet import gluon
from mxnet import autograd
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})
a = nd.random_normal(shape=(4, 4))
with autograd.record():
    y = net(a)
y.backward()
trainer.step(1)

打印此时的梯度和参数:

print(net[0].weight.grad())
print(net[1].weight.grad())

[[   24.1047821    351.96694946   918.98065186   780.11938477]
 [  135.66175842   464.44342041  1091.39978027   975.60351562]
 [   -4.66412354   381.2038269   1031.24487305   860.86657715]
 [   80.98666382   409.02435303  1006.10632324   879.13439941]]
<NDArray 4x4 @cpu(0)>
[[   24.1047821    351.96694946   918.98065186   780.11938477]
 [  135.66175842   464.44342041  1091.39978027   975.60351562]
 [   -4.66412354   381.2038269   1031.24487305   860.86657715]
 [   80.98666382   409.02435303  1006.10632324   879.13439941]]

print(net[0].weight.data())
print(net[1].weight.data())

[[   3.05141711  -27.38289833  -85.30486298  -70.45680237]
 [  -5.54749393  -40.69781113 -102.18912506  -91.57314301]
 [   6.74286556  -29.5427494   -94.20626831  -80.6505661 ]
 [  -0.70050526  -32.89844513  -90.72361755  -78.2598877 ]]
<NDArray 4x4 @cpu(0)>
[[   3.05141711  -27.38289833  -85.30486298  -70.45680237]
 [  -5.54749393  -40.69781113 -102.18912506  -91.57314301]
 [   6.74286556  -29.5427494   -94.20626831  -80.6505661 ]
 [  -0.70050526  -32.89844513  -90.72361755  -78.2598877 ]]
<NDArray 4x4 @cpu(0)>

拿参数的第一个元素举例:首先两个共享参数的梯度都是24.10;其次,因为3.05=5.46-0.1*24.10,所以共享参数只更新了1次而不是2次。
以上是我的见解,如有错误的地方希望各位同仁们指出。


#29

运行

w = net[0].weight
b = net[0].bias
print('name: ', net[0].name, '\nweight: ', w, '\nbias: ', b)

输出结果如下:

name:  sequential0_dense0 
weight:  
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-7-b64c5c7d25c9> in <module>()
      1 w = net[0].weight
      2 b = net[0].bias
----> 3 print('name: ', net[0].name, '\nweight: ', w, '\nbias: ', b)

~/anaconda3/envs/gluon/lib/python3.6/site-packages/mxnet/gluon/parameter.py in __repr__(self)
    118     def __repr__(self):
    119         s = 'Parameter {name} (shape={shape}, dtype={dtype})'
--> 120         return s.format(**self.__dict__)
    121 
    122     @property

KeyError: 'shape'

出问题了?

#30

这是mxnet的一个bug,这里修复了,就是把~/anaconda3/envs/gluon/lib/python3.6/site-packages/mxnet/gluon/parameter.py的第120行:

return s.format(**self.__dict__)

改成

return s.format(name=self.name, shape=self.shape, dtype=self.dtype)

#32

在第一种方法里,也可以用:
net[0].params.initialize(init=init.One(),force_reinit=True)
net[1].params.initialize(init=init.Xavier, force_reinit=True)


#33

共享参数

net.add(nn.Dense(4, activation="relu", params=net[-1].params))

上面代码中net[-1]的-1代表什么?


#35

我这边是基于Python2内核的,可以119行改成

s = ‘Parameter {name} (shape={_shape}, dtype={dtype})’


#36

请问是哪出现了问题?我也出现了类似的错误。


#38

你可以用 net[0].params().weight来访问,这样更直观


#39

Uniform, 参见 https://github.com/apache/incubator-mxnet/blob/master/python/mxnet/gluon/parameter.py#L273


#40

可以再看下梯度是怎么计算的?就是说,这个梯度到底是哪一层的?


#41

最后一层


#42

参数复用指的是参数共享吗?例子中
net = nn.Sequential()
with net.name_scope():
net.add(nn.Dense(4, activation=“relu”))
net.add(nn.Dense(4, activation=“relu”))
net.add(nn.Dense(4, activation=“relu”, params=net[-1].params))
net.add(nn.Dense(2))
net[-1]的weight对应shape是24,而net[-1]的weight对应shape是44,这个怎么实现参数共享呢?不知道我哪里理解错了?


#43

文档中的default_init为None


#44

此处的params=net[-1].params指的是最后一层,这里的最后一层,指的是建立好的net的最后一层。对于net.add(nn.Dense(4, activation=“relu”, params=net[-1].params))这一层来说,建立好的net的最后一层就是net.add(nn.Dense(4, activation=“relu”)),而不是net.add(nn.Dense(2)),因为此时net.add(nn.Dense(2))还没有建立,所以最后一层指的是该层之前建立好的最后一层,而不是整个net完全建立好的最后一层。