神经网络的优化(2)---- 滑动平均 ema

版权声明:未经同意,严禁转载 https://blog.csdn.net/pengchengliu/article/details/88296835

                                                                                                                                            点击此处返回总目录

这一节分享另一个优化方法:滑动平均。有些书里把它叫做“影子值”。滑动平均记录了每个参数一段时间内过往值的平均。由于滑动平均值不仅表现了当前值,还表现了过去一段时间内的平均值,这样可以增加模型的泛化性。

滑动平均通常针对所有参数进行优化,包括所有的w和b。

滑动平均的感觉就好比给参数加了个影子,参数变化,影子缓慢追随。

              

滑动平均值是这样计算的:

             

影子等于衰减率乘以影子,加上(1-衰减率)乘以参数。衰减率等于MOVING_AVERAGE_DECAY与(1+轮数)/(10+轮数)中小的那一个。

其中滑动平均衰减率MOVING_AVERAGE_DECAY是一个超参数。影子的初值等于参数的初值。

我们来看这个例子:

             

衰减率的第一个参数MOVING_AVERAGE_DECAY是个超参数,一般会给一个比较大的值,比如0.99。参数w1在初始化时赋值为0。轮数global_step定义为0。由于影子的初值等于参数初值,所以w1的滑动平均值初值也为0。

当参数w1被更新时,比如更新为1时,影子会慢慢追随。通过计算,w1的滑动平均值=min{0.99,(1+0)/(10+0)} * 0 + (1-min{0.99,(1+0)/(10+0)})*1=0.1*0 + 0.9*1 = 0.9

当100轮,参数更新为10时。w1的滑动平均值=1.644

再次运行时,w1的滑动平均值为2.328

再次运行时,为2.956

可见,滑动平均像影子一样,缓慢追随者参数变化。

在TensorFlow里,我们用这样三句话描述滑动平均:

              

第一句话:ema=tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)。有两个参数,第一个参数是滑动平均衰减率,是个超参数,一般会给一个比较大的值。第二个参数是当前轮数,和其他程序公用一个轮数计数器global_step。

第二句话:ema.apply([]) 定义了对括号里列出的参数求滑动平均。我们在实际应用中,会写ema.apply(tf.trainable_variables())。用tf.trainable_variable()函数可以自动把所有待训练的参数汇总成列表。

第三句话是 :with...这句话。在工程应用中,我们常把计算滑动平均ema_op和训练过程train_step绑定在一起运行,使他们合成一个训练节点。用train_op = tf.no_op(name='train') 这句话实现。我们在下节手写识别的应用中,会用到这个功能。

用ema.average()可以返回某些参数的滑动平均值。

                         

              

我们通过代码实现一下:

#coding:utf-8
import tensorflow as tf

#定义变量及滑动平均类

w1=tf.Variable(0,dtype=tf.float32)      #初值为0.0。这个代码就是不断更新w1参数,优化w1参数,滑动平均做了w1的影子
global_step = tf.Variable(0,trainable=False)   #迭代轮数,初值为0,不可被优化(训练)
MOVING_AVERAGE_DECAY = 0.99                    

ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,global_step)         #第一句

ema_op = ema.apply(tf.trainable_variables())         #第二句。每次运行ema_op时,对所有参数求滑动平均

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    print(sess.run([w1,ema.average(w1)]))               #打印出当前参数和滑动平均。
   
    sess.run(tf.assign(w1,1))                                    #人为模拟参数更新。参数w1的值赋为1
    sess.run(ema_op)
    print(sess.run([w1,ema.average(w1)]))
    
    sess.run(tf.assign(global_step,100))                  #人为修改轮数。
    sess.run(tf.assign(w1,10))                                  #人为修改参数。
    sess.run(ema_op)
    print(sess.run([w1,ema.average(w1)]))
    
    sess.run(ema_op)                                               #多执行几次,可以看到缓慢跟随。
    print(sess.run([w1,ema.average(w1)]))
    
    sess.run(ema_op)
    print(sess.run([w1,ema.average(w1)]))
    
    sess.run(ema_op)
    print(sess.run([w1,ema.average(w1)]))

运行结果:

[0.0, 0.0]                                                                #刚开始时的值。
[1.0, 0.9]                                                                #参数更新为1时
[10.0, 1.6445453]                                                  #100轮的时候
[10.0, 2.3281732]                                                  #继续执行
[10.0, 2.955868]
[10.0, 3.532206]

可以看到滑动平均值追随参数变化的过程。

猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/88296835