逐层指定学习率
为不同的层指定相应的学习率可能会有些用处,印象中,这个在caffe里面是在定义网络时指定的,用mxnet后一直没用到过。这放一些字符,以备查。
接口
思路来自于官网。
但在optimizer.py
中,相应的接口为set_lr_mult(self, args_lr_mult)
。
其思路是:在 mod._optimizer 内部定义一个字典以记录每个参数的学习率乘子。
值得注意的是,从程序上来看,这个接口并不是按照层为单位进行学习率设定,而是对具体的参数进行设置(比如:conv1_weight
)。
Nov 5, 2017
试验的几次都把怀疑目标指向了这个地方,今早测试了下。问题出在set_lr_mult
传入的dictionary
上,其内容应当包括所有要调整的参数,不能使用单个多次指定的方式。
测试
# updated on Nov 5, 2017
import mxnet as mx
M,N=3,3
num_filter=1
kernel=mx.nd.array([ [1,2,3],[1,2,3],[1,2,3] ])
d=mx.sym.Variable('data')
conv1=mx.sym.Convolution(data=d,kernel=(3,3),num_filter=num_filter,no_bias=False,name='conv1')
loss=mx.sym.MakeLoss(data=conv1)
bch_kernel=kernel.reshape((1,1,M,N))
arg_params={'conv1_weight': bch_kernel}
mod=mx.mod.Module(symbol=loss,data_names=('data',),label_names=None)
mod.bind(data_shapes=[ ('data',[1,1,M,N]),])
mod.init_params()
mod.init_optimizer()
###################################################
################ Solution I #########################
###################################################
mod._optimizer.set_lr_mult({'conv1_weight':0}) # 单个多次指定->失败
mod._optimizer.set_lr_mult({'conv1_bias':0})
mod._optimizer.sym.list_arguments()
#['data', 'conv1_weight']
mod._optimizer.sym.attr_dict()
#{'conv1': {'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}, 'conv1_weight': {'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}}
### test ###############
mod.forward(mx.io.DataBatch([bch_kernel]))
mod.get_outputs()[0].asnumpy()
#array([[[[ 0.06028102]]]], dtype=float32)
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.forward(mx.io.DataBatch([bch_kernel]))
mod.get_outputs()[0].asnumpy()
#array([[[[-1.61971903]]]], dtype=float32) !test failed
###################################################
################ Solution II #########################
###################################################
mod._optimizer.set_lr_mult({'conv1_bias':0,'conv1_weight':0}) # 一次指定完成
mod._optimizer.sym.list_arguments()
#['data', 'conv1_weight']
mod._optimizer.sym.attr_dict()
#{'conv1': {'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}, 'conv1_weight': {'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}}
### test ###############
mod.forward(mx.io.DataBatch([bch_kernel]))
mod.get_outputs()[0].asnumpy()
#array([[[[ 0.06028102]]]], dtype=float32)
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.backward()
mod.update()
mod.forward(mx.io.DataBatch([bch_kernel]))
mod.get_outputs()[0].asnumpy()
#array([[[[ 0.06028102]]]], dtype=float32) !test OK~
Jul 31, 2018
最近又要用到这方面的借口了,现在是gluon的江湖,一切向之看齐。
gluon推出了两个关于优化的接口,一个是固有的 optimizer,另一个是新加入的Trainer。
通常同Trainer作为训练时的接口,但这两个类都接受Opt的参数,于是要检查下之前的设置是否合适。
- Trainer在step时调用optimizer中的updater,在update中,首先调用_get_lr获得Param的lr*:
# _get_lr(self, index): from optimizer.py
¦ if index in self.param_dict:
¦ ¦ lr *= self.param_dict[index].lr_mult
¦ elif index in self.lr_mult:
¦ ¦ lr *= self.lr_mult[index]
¦ elif index in self.idx2name:
¦ ¦ lr *= self.lr_mult.get(self.idx2name[index], 1.0)
所以直接在ParamDict里面设置就好:
用这个:
import mxnet as mx
model = mx.gluon.nn.Sequential()
with model.name_scope():
model.add(mx.gluon.nn.Embedding(30, 10))
¦ model.add(mx.gluon.rnn.LSTM(20))
¦ ¦ model.add(mx.gluon.nn.Dense(5, flatten=False))
d1 = model.collect_params()
model = mx.gluon.rnn.SequentialRNNCell()
with model.name_scope():
model.add(mx.gluon.rnn.LSTMCell(20))
¦ model.add(mx.gluon.rnn.LSTMCell(20))
d2 = model.collect_params()
d = mx.gluon.ParamDict()
d1.setattr('lr_mult',.01) # 直接设置 lr_mult
d2.setattr('lr_mult',10)
d.update(d1)
d.update(d1)
d['sequential0_dense0_weight'].lr_mult # 合并后无影响
d['sequentialrnncell0_lstm0_i2h_weight'].lr_mult
推测mxnet一贯使用引用手法,所以直接合并几个model的ParamDict,不会影响,但合并对于使用统一的Trainer接口却是必要的(不然会需要多个Trainer对应不同的model part)。