滑动平均(影子值)

滑动平均(影子值):记录了每个参数一段时间内过往值得平均,增加了模型得泛化性。

  • 针对所有参数:w和b (像是给参数加了影子,参数变化,影子缓慢追随)
  • 影子 = 衰减率* 影子+(1-衰减率)* 参数 ; 影子的初值=参数的滑动平均初值;
  • 衰减率=min{move_average_decay,((1+轮数)/(10+轮数))};其中move_average_decay为滑动平均衰减率(超参数),滑动平均衰减率指的是衰减率的一个一段时间里的所有值的平均值。

滑动平均基本原理:

滑动平均(指数加权平均),可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。滑动平均法是重在实现“重近轻远”的原则,通过对数据加以小权,近期数据给予较大权数,远期数据给予较少的权数,目的在于强化近期数据的作用,弱化远期数据的影响。

具体请移步​​百度百科​​。

滑动平均的好处:
占内存少,不需要保存过去10个或者100个历史 θ 值,就能够估计其均值。【当然,滑动平均不如将历史值全保存下来计算均值准确,但后者占用更多内存和计算成本更高】

例子:

假设滑动平均衰减率为0.99,参数w1为0,轮数global_step=0,w1的滑动平均值(影子)为0。

  • 参数w1更新为1,则w1滑动平均值=min(0.99,1/10)* 0+(1-min(0.99,1/10)* 1) = 0.9
  • 轮数global_step为100时,参数w1被梯度下降更新为10,则w1滑动平均值=min(0.99,101/110)* 0.9+(1-min(0.99,101/110))* 10 = 1.644
  • 再次运行,w1滑动平均值=min(0.99,101/110)* 1.644+(1-min(0.99,101/110))* 10 = 2.328
  • 再次运行,w1滑动平均值=2.956

tf如何实现滑动平均?:

  • ema = tf.train.ExponentialMovingAverage(滑动平均衰减率moving_average_decay,当前轮数global_step) #滑动平均衰减率一般给比较大的值。
  • ema_op = ema.apply(tf.trainable_variables(参数)) #tf.trainable_variables定义了把所有参数汇总成列表,ema.apply训练了所有参数的滑动平均。
  • with tf.control_dependencies([train_step,ema_op]): #工程应用中把训练滑动平均和训练过程合并为一个节点。
    train_op = tf.no_op(name=‘train’)

【解释在代码中!!!】

定义变量以及滑动平均类:

# 定义一个32位浮点变量,初始值为0.0,代码目的是不断的更新w1参数,优化w1参数,滑动平均做了个w1的影子。
w1 = tf.Variable(0,dtype=tf.float32)
# 定义num_updates(NN的迭代轮数),初始值为0,不可被训练优化,这个参数不训练。
global_step = tf.Variable(0,trainable=False)

# 实例化滑动平均类,给衰减率为0.99,当前轮数global_step
moving_average_decay = 0.99
ema = tf.train.ExponentialMovingAverage(moving_average_decay,global_step)

实现神经网络的基本结构:

# ema.apply之后的括号里更新列表,每次运行sess.run(ema_op)时,对更新列表中的元素求滑动平均值。
# 在实际应用中会使用tf.trainable_variables()自动将所有待训练的参数汇总为列表。
# ema_op = ema.apply([w1])
ema_op = ema.apply(tf.trainable_variables())

# 查看不同迭代中变量取值的变化
with tf.Session() as sess:
init_op = tf.global_variables_initializer() #变量初始化
sess.run(init_op)
#使用ema.average(w1)获取w1的滑动平均值,(要运行多节点,作为列表中元素列出,写在sess.run()中)
#打印当前(最初)参数w1和w1滑动平均值
print(sess.run([w1,ema.average(w1)]))
#参数w1的值赋值为1
sess.run(tf.assign(w1,1))
sess.run(ema_op)
print(sess.run([w1,ema.average(w1)]))

#更新step和w1的值,模拟100轮迭代之后,参数w1变为10
sess.run(tf.assign(global_step,100))
sess.run(tf.assign(w1,10))
sess.run(ema_op)
#每次sess.run()一次会更新一次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)]))

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)]))

sess.run(ema_op)
print(sess.run([w1,ema.average(w1)]))