tensorflow || 滑动平均的理解--tf.train.ExponentialMovingAverage

1 滑动平均的理解

滑动平均(exponential moving average),或者叫做指数加权平均(exponentially weighted moving average),可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。


已知变量 v v v t t t 时刻记为 v t v_t vt θ t \theta_t θt 为变量 v v v t t t时刻的取值。

1.1 在不使用滑动平均模型时

v t = θ t v_t=\theta_t vt=θt


1.2 在使用滑动平均模型时

v t = β ∗ v t − 1 + ( 1 − β ) ∗ θ t v_t = \beta *v_{t-1}+(1-\beta)*\theta_t vt=βvt1+(1β)θt  其中, β ∈ [ 0 , 1 ) \beta \in[0,1) β[0,1)。当 β = 0 \beta=0 β=0 相当于没有使用滑动平均

【Andrew Ng】在【Course 2 Improving Deep Neural Networks】中讲到, t t t 时刻变量 v v v 的滑动平均值大致等于过去的 1 / ( 1 − β ) 1/(1-\beta) 1/(1β) 个时刻 θ \theta θ 值的平均。

例如, β = 0.9 \beta=0.9 β=0.9 θ \theta θ t t t 滑动平均值为 前10个时刻的 θ \theta θ的平均; β = 0.99 \beta=0.99 β=0.99 θ \theta θ t t t 滑动平均值为 前100个时刻的 θ \theta θ的平均;


1.3 优化后的滑动平均

上面的结论在滑动平均初期相差较大,所以有了 Bias correction,将 v t v_t vt 除以 ( 1 − β t ) (1-\beta^t) (1βt) 修正对均值的估计。
v t = β ∗ v t + ( 1 − β ) ∗ θ t v_t = \beta *v_{t}+(1-\beta)*\theta_t vt=βvt+(1β)θt v _ b i a s e d t = v t / ( 1 − β t ) v\_{biased}_t=v_t/(1-\beta^t) v_biasedt=vt/(1βt)
对于上面的公式,可以看出: t t t 越大, ( 1 − β t ) (1-\beta^t) (1βt) 约接近1,即 v t ≈ v _ b i a s e d t v_t \approx v\_{biased}_t vtv_biasedt


1.4 滑动平均的特点

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


4.5 三种方式数据的示意图:

该图片借用参考连接的博客的图
在这里插入图片描述

2 tensorflow 中使用滑动平均

2.1 tf.train.ExponentialMovingAverage 的介绍

滑动平均可以看做是变量的过去某一段时间的均值,相比对变量直接赋值而言,滑动平均得到的值在图像上更加平缓光滑,抖动更小,不会因为某次的异常取值而使得滑动平均值波动很大。
tensorflow中提供了tf.train.ExponentialMovingAverage 来实现滑动平均。

tf.train.ExponentialMovingAverage.__init__(
                               decay, 
                               num_updates=None,
                               name='ExponentialMovingAverage')

ExponentialMovingAverage 对每一个变量(variable)会维护一个影子变量(shadow_variable),这个影子变量的初始值就是相应变量的初始值,而每次运行变量更新时,影子变量的值就会更新: s h a d o w _ v a r i a b l e = d e c a y ∗ s h a d o w _ c a r i a b l e + ( 1 − d e c a y ) ∗ v a r i a b l e shadow\_{variable}=decay*shadow\_{cariable}+(1-decay)*variable shadow_variable=decayshadow_cariable+(1decay)variable

  • decay】衰减率,即公式中的 β \beta β。这个衰减率将用于控制影子变量的更新速度。decay越大影子变量越趋于稳定,在实际运用中,decay一般会设为非常接近1的数值(eg. 0.99 or 0.996)。

  • num_updates】为了使影子变量在训练前期可以更快的更新,每次使用的衰减率将是 m i n ( d e c a y , 1 + n u m _ u p d a t a 10 + n u m _ u p d a t a ) min(decay,\frac{1+num\_updata}{10+num\_updata}) min(decay,10+num_updata1+num_updata)

  • .apply() :表明对哪些变量,计算并记录其影子变量。
    注意:当apply() 的入参为空时,默认对 tf.trainable_variables()创建影子变量,并添加到.moving_average_variables()以及tf.global_variables()中。
    一般apply()是 tf.trainable_variables()

  • .average():获取某个变量的影子变量的值

  • .average_name():获取某个变量的影子变量的名字

  • .variables_to_restore():返回的是字典,长度等于未使用tf.train.ExponentialMovingAverage 时的所有变量的个数。传入参数最好与apply()的入参保持一致(建议一定传参且与 apply() 的入参保持一致,自己在未传参或传参不一致遇到问题,这里不细说)。传参时返回的内容包含:
    1-- 【key】apply作用变量对应的影子变量的名字 【value】apply作用的变量
    2-- 【key】其余变量的名字【value】名字对应的其余变量


模拟训练时候,tf.train.ExponentialMovingAverage 使用的效果
import tensorflow as tf

v1 = tf.Variable(1, dtype=tf.float32, name="data1")
v2 = tf.Variable(2, dtype=tf.float32, name="data2")
v3 = tf.Variable(3, dtype=tf.float32, trainable=False, name="data3")
v4 = tf.Variable(4, dtype=tf.float32, trainable=False, name="data4")
AA = tf.global_variables().copy()

ema = tf.train.ExponentialMovingAverage(0.99)
maintain_averages_op = ema.apply(tf.trainable_variables())

BB = tf.global_variables().copy()
###########==============================print for test, starting ==============
print("0============")
for i in range(len(AA)):
   print(AA[i])
#<tf.Variable 'data1:0' shape=() dtype=float32_ref>
#<tf.Variable 'data2:0' shape=() dtype=float32_ref>
#<tf.Variable 'data3:0' shape=() dtype=float32_ref>
#<tf.Variable 'data4:0' shape=() dtype=float32_ref>
print("1============")
for i in range(len(BB)):
   print(BB[i])
#<tf.Variable 'data1:0' shape=() dtype=float32_ref>
#<tf.Variable 'data2:0' shape=() dtype=float32_ref>
#<tf.Variable 'data3:0' shape=() dtype=float32_ref>
#<tf.Variable 'data4:0' shape=() dtype=float32_ref>
#<tf.Variable 'data1/ExponentialMovingAverage:0' shape=() dtype=float32_ref>
#<tf.Variable 'data2/ExponentialMovingAverage:0' shape=() dtype=float32_ref>
print("2============")
A = ema.variables_to_restore(tf.trainable_variables())
key = list(A.keys())
for i in range(len(A)):
   print(key[i],":",A[key[i]])
#data2/ExponentialMovingAverage : <tf.Variable 'data2:0' shape=() dtype=float32_ref>
#data1/ExponentialMovingAverage : <tf.Variable 'data1:0' shape=() dtype=float32_ref>
#data3 : <tf.Variable 'data3:0' shape=() dtype=float32_ref>
#data4 : <tf.Variable 'data4:0' shape=() dtype=float32_ref>

###########==============================print for test, endding ==============

saver = tf.train.Saver()
with tf.Session() as sess:
   # 初始化
   sess.run(tf.global_variables_initializer())
   print(sess.run([v1, ema.average(v1)]))  # [1.0, 1.0]
   print(ema.average_name(v1))             # data1/ExponentialMovingAverage
   

   # 更新变量v1的取值
   sess.run(tf.assign(v1, 5))
   sess.run(tf.assign(v2, 15))

   sess.run(maintain_averages_op)
   print(sess.run([v1, ema.average(v1)]))    #  [5.0, 1.04]
   print(sess.run([v2, ema.average(v2)]))    #  [15.0, 2.1299999]

   saver.save(sess,'./checkpoint_dir/mymodel')

模拟测试时候,tf.train.ExponentialMovingAverage 使用的效果
v1 = tf.Variable(1, dtype=tf.float32, name="data1")
v2 = tf.Variable(2, dtype=tf.float32, name="data2")
v3 = tf.Variable(3, dtype=tf.float32, trainable=False, name="data3")
v4 = tf.Variable(4, dtype=tf.float32, trainable=False, name="data4")

ema = tf.train.ExponentialMovingAverage(0.99)
maintain_averages_op = ema.apply(tf.trainable_variables())

# saver = tf.train.Saver(ema.variables_to_restore(tf.trainable_variables()))
saver = tf.train.Saver(tf.global_variables())

# saver = tf.train.import_meta_graph('./checkpoint_dir/mymodel.meta')
with tf.Session() as sess:
   init_op = tf.global_variables_initializer()
   sess.run(init_op)
   saver.restore(sess,'./checkpoint_dir/mymodel')
   for i in range(len(tf.global_variables())):
       print(tf.global_variables()[i])
   for i in range(len(tf.global_variables())):
       print(sess.run(sess.graph.get_tensor_by_name(tf.global_variables()[i].name)))

此时使用saver = tf.train.Saver(tf.global_variables()):Saver中传入的是所有变量,所以无论是原始的变量,还是对应的影子变量,都是按照正常的方式restore。打印结果为
<tf.Variable ‘data1:0’ shape=() dtype=float32_ref> 5.0
<tf.Variable ‘data2:0’ shape=() dtype=float32_ref> 15.0
<tf.Variable ‘data3:0’ shape=() dtype=float32_ref> 3.0
<tf.Variable ‘data4:0’ shape=() dtype=float32_ref> 4.0
<tf.Variable ‘data1/ExponentialMovingAverage:0’ shape=() dtype=float32_ref> 1.04
<tf.Variable ‘data2/ExponentialMovingAverage:0’ shape=() dtype=float32_ref> 2.1299999

当使用saver = tf.train.Saver(ema.variables_to_restore(tf.trainable_variables())):Saver中传入的是 ema.variables_to_restore() 返回的字典。这样就会将影子变量的值,传入到原本变量中,影子变量被复制原本的初始值。打印结果为
<tf.Variable ‘data1:0’ shape=() dtype=float32_ref> 1.04
<tf.Variable ‘data2:0’ shape=() dtype=float32_ref> 2.1299999
<tf.Variable ‘data3:0’ shape=() dtype=float32_ref> 3.0
<tf.Variable ‘data4:0’ shape=() dtype=float32_ref> 4.0
<tf.Variable ‘data1/ExponentialMovingAverage:0’ shape=() dtype=float32_ref> 1.0
<tf.Variable ‘data2/ExponentialMovingAverage:0’ shape=() dtype=float32_ref> 2.0

这里要注意tf.train.Saver()的用法,该api的入参,为dict、list

  • list:只保存或恢复list包含的变量的权重信息
  • dict:将key中名字的变量值,作为value变量的值restore,然后key中名字的变量值被初始化。

2.2 tf.train.ExponentialMovingAverage 在神经网络中的使用

构建训练模型时
moving_ave = tf.train.ExponentialMovingAverage(0.99, global_step).apply(tf.trainable_variables())

first_train_op = tf.train.AdamOptimizer(self.learn_rate).minimize(self.loss,
                                                     var_list=first_stage_trainable_var_list)                                            
#计算bn中的滑动均值和滑动方差
with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
   # 进行神经网络的反向传播
   with tf.control_dependencies([first_train_op]):
       # 计算网络可训练参数的影子变量
       with tf.control_dependencies([moving_ave]):
           self.train_op = tf.no_op()

# 一般在神经网络训练阶段,建议将所有的变量,也就是 `tf.global_variables()` 进行保存
saver  = tf.train.Saver(tf.global_variables(), max_to_keep=100)
神经网络预测时

.variables_to_restore() 的使用:表明返回计算的影子变量的tensor,

       sess  = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))

       ema_obj = tf.train.ExponentialMovingAverage(0.99)
       saver = tf.train.Saver(ema_obj.variables_to_restore())  # 使用网络参数的滑动均值进行
       saver.restore(sess, weight_file)

参考链接:https://www.cnblogs.com/wuliytTaotao/p/9479958.html

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值