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=β∗vt−1+(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 vt≈v_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=decay∗shadow_cariable+(1−decay)∗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