在另外一个朋友的博客里见到了对于滑动平均的学名称号,叫做一阶滞后滤波法:
如此高大尚的名字,背后就是一种保守决策策略来降低震荡带来的风险,在频谱上就是去掉高频信号,而压缩成低频信号,显得更平滑和稳定。目的无外乎几种:
- 降低周期性的干扰,更容易收敛;
- 在波动频率较高的场合有很好的效果,避免高频震荡波动。
而在TensorFlow中提供了tf.train.ExponentialMovingAverage 来实现滑动平均模型,在采用随机梯度下降算法训练神经网络时,使用其可以提高模型在测试数据上的健壮性(robustness)。
TensorFlow下的 tf.train.ExponentialMovingAverage 需要提供一个衰减率decay。该衰减率用于控制模型更新的速度。该衰减率用于控制模型更新的速度,ExponentialMovingAverage 对每一个待更新的变量(variable)都会维护一个影子变量(shadow variable)。影子变量的初始值就是这个变量的初始值,
shadow_variable=decay×shadow_variable+(1−decay)×variable
影子变量其实是提供了一阶滞后滤波的结果,能够让高频震荡信息被过滤到,在输出上变得更平滑和稳定,在迭代的前期或许不如直接的数值更新来得快,但是在越发接近收敛点的时候,会出现反复周期震荡,导致更不容易收敛,所以滤波函数会胜在对收敛点附近的迭代工作。那是否能做一个假设就是迭代次数越多约接近收敛点,在初始时,尽量保持高频部分(a比较小,1-a接近1),而随着迭代次数逐步降低频度而保留低频部分呢, 也就是decay和 step成正相关,从而有人设计出滑动平均模型里对decay的更新机制如下:
在滑动平滑模型中, decay 决定了模型更新的速度,越大越趋于稳定。实际运用中,decay 一般会设置为十分接近 1 的常数(0.999或0.9999)。为了使得模型在训练的初始阶段更新得更快,ExponentialMovingAverage 还提供了 num_updates 参数来动态设置 decay 的大小:
decay=min{decay,1+num_updates10+num_updates}
为什么说这背后也有社会哲学呢,道德经第八章有言:交易之道,刚者易折。惟有至阴至柔,方可纵横天下。天下柔弱者莫如水,然上善若水。
过于快速的变化,反而不是我们走到目标的最短路径,在试错的路上,开始时可以根据反馈结果大步改革,但是随着越来越趋近如目标,或者有了更多的积累后,历史的经验不能完全被遗弃掉而是选择优秀的部分作为传承扮演越来越重要的作用。组织里那些后期的行动保守派可能是代表了一些历史沉淀,要识别出来也并不是坏事,反而可能让我们更稳定的前行。
# -*-coding:utf-8 -*-tensorflow 1.4.0-
import tensorflow as tf
v = tf.Variable(0, dtype=tf.float32)
step = tf.Variable(0, trainable=False)
# a calss for exponentailMovingAverage
ema = tf.train.ExponentialMovingAverage(0.99, step)
maintain_average_op = ema.apply([v])
with tf.Session() as sess:
init_op = tf.initialize_all_variables()
sess.run(init_op)
print sess.run([v, ema.average(v)]) # output [0.0, 0.0]
# update the value of v to 5
sess.run(v.assign(5))
sess.run(maintain_average_op)
print sess.run([v, ema.average(v)]) # output [5.0, 4.5]
# update the value of v to 10, and the step to 1000, taste the difference
sess.run(step.assign(1000))
sess.run(v.assign(10))
sess.run(maintain_average_op)
print sess.run([v, ema.average(v)]) # output [10.0, 4.555]
sess.run(maintain_average_op)
print sess.run([v, ema.average(v)]) # output [10.0, 4.60945]
Debug小贴士:
sess.run 后面的参数列表要用[v, ema.average(v)] ,中括号框起来,没有留意的时候就会报错,所run中需要input是array