【tensorflow】在不同层上设置不同的学习率,fine-tuning

最近一直在研究tensorflow的fine-tuning,TF关于fine-tuning这块的资料真的没有Caffe多。经过这几天的琢磨,差不多也明白了。感谢网上为数不多的开源资料。

主要方法有如下两种:

一、从graph处入手

这个方案的灵感来自于:https://www.cnblogs.com/sikyadjy/p/6861692.html

这里我重新概括下原作者的三步走:

1) 先输出层的参数变量

variables_names = [v.name for v in tf.trainable_variables()]
values = sess.run(variables_names)
for k, v in zip(variables_names, values):
    print "Variable: ", k
    print "Shape: ", v.shape
    print v

2)设置前后层的学习率
比如要对前20层的参数使用较低的学习率微调(20层大概有40种参数,20个weight,20个bia)

var1 = tf.trainable_variables()[0:40]
var2 = tf.trainable_variables()[40:]
train_op1 = GradientDescentOptimizer(0.00001).minimize(loss, var_list=var1) 
train_op2 = GradientDescentOptimizer(0.0001).minimize(loss, var_list=var2)
train_op = tf.group(train_op1, train_op2)

3)加载预训练的model进行fine-tuning

注意:(Note:)
在我的实验中,按照原作者的方法,我把预训练的ckpt加载的时侯,总是出错。
总结了一下,原因可能出现在graph中,在fine-tuning中,设置了两个train_op1, train_op2,然而我预训练好的model中只有一个train_op,在这里可能无法对应,导致加载权重的时候出错。

更改如下:在预训练的时候也设置成两个train_op,不过将学习率调整成一致。重新训练model,然后便可以加载了。

总结:这种方法明显会有限制,导致之前的pretrained的model的graph必须按照fine-tuning来调节

二、从反向梯度入手
这个灵感来自于:http://blog.csdn.net/liyuan123zhouhui/article/details/69569493

这里重新搬运下原来作者的代码:

#tensorflow 中从ckpt文件中恢复指定的层或将指定的层不进行恢复:
#tensorflow 中不同的layer指定不同的学习率

with tf.Graph().as_default():
        #存放的是需要恢复的层参数
        variables_to_restore = []
        #存放的是需要训练的层参数名,这里是没恢复的需要进行重新训练,实际上恢复了的参数也可以训练
        variables_to_train = []
        for var in slim.get_model_variables():
            excluded = False
            for exclusion in fine_tune_layers:
            #比如fine tune layer中包含logits,bottleneck
                if var.op.name.startswith(exclusion):
                    excluded = True
                    break
            if not excluded:
                variables_to_restore.append(var)
                #print('var to restore :',var)
            else:
                variables_to_train.append(var)
                #print('var to train: ',var)


        #这里省略掉一些步骤,进入训练步骤:
        #将variables_to_train,需要训练的参数给optimizer 的compute_gradients函数
        grads = opt.compute_gradients(total_loss, variables_to_train)
        #这个函数将只计算variables_to_train中的梯度
        #然后将梯度进行应用:
        apply_gradient_op = opt.apply_gradients(grads, global_step=global_step)
        #也可以直接调用opt.minimize(total_loss,variables_to_train)
        #minimize只是将compute_gradients与apply_gradients封装成了一个函数,实际上还是调用的这两个函数
        #如果在梯度里面不同的参数需要不同的学习率,那么可以:

        capped_grads_and_vars = []#[(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]
        #update_gradient_vars是需要更新的参数,使用的是全局学习率
        #对于不是update_gradient_vars的参数,将其梯度更新乘以0.0001,使用基本上不动
        for grad in grads:
            for update_vars  in update_gradient_vars:
                if grad[1]==update_vars:
                    capped_grads_and_vars.append((grad[0],grad[1]))
                else:
                    capped_grads_and_vars.append((0.0001*grad[0],grad[1]))

        apply_gradient_op = opt.apply_gradients(capped_grads_and_vars, global_step=global_step)

        #在恢复模型时:

        with sess.as_default():

            if pretrained_model:
                print('Restoring pretrained model: %s' % pretrained_model)
                init_fn = slim.assign_from_checkpoint_fn(
                pretrained_model,
                variables_to_restore)
                init_fn(sess)
            #这样就将指定的层参数没有恢复

这个操作无需定义两个train_op,只不过对一个minimize优化函数进行拆分成了Optimizer.compute_gradient(), Optimizer.apply_gradients()两个部分。

操作如下:
1. 先计算反向梯度: Optimizer.compute_gradient()
2. 对模型仅仅微调的参数的相应梯度进行 multiply 一个微小的数,减小梯度更新,通过一个变量capped_grads_and_vars进行存储。
3. 将梯度加入graph中流动,则Optimizer.apply_gradients()。

两种方法对比起来,下一种明显便捷许多,当然还是要看情况而定。

猜你喜欢

转载自blog.csdn.net/shwan_ma/article/details/78881961