逐层指定学习率

为不同的层指定相应的学习率可能会有些用处,印象中,这个在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的参数,于是要检查下之前的设置是否合适。

  1. Trainer在step时调用optimizer中的updater,在update中,首先调用_get_lr获得Paramlr*:
#  _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)。

原文地址:https://www.cnblogs.com/chenyliang/p/7451824.html