1. 程式人生 > >神經網路優化(二) - 滑動平均

神經網路優化(二) - 滑動平均

1 滑動平均概述

滑動平均(也稱為 影子值 ):記錄了每一個引數一段時間內過往值的平均,增加了模型的泛化性。

滑動平均通常針對所有引數進行優化:W 和 b,

簡單地理解,滑動平均像是給引數加了一個影子,引數變化,影子緩慢追隨。

滑動平均的表示公式為

影子 = 衰減率 * 影子 + ( 1 - 衰減率 ) * 引數

滑動平均值 = 衰減率 * 滑動平均值 + ( 1 - 衰減率 )* 引數

備註

影子初值 = 引數初值

衰減率 = min{ MOVING_AVERAGE_DECAY, (1+輪數) / (10 + 輪數 ) }

示例:

MOVING_AVERAGE_DECAY

為 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

 

2 滑動平均在Tensorflow中的表示方式

第一步 例項化滑動平均類ema

ema = tf.train.ExponentialMovingAverage(
    MOVING_AVERAGE_DECAY(滑動平均衰減率),
    global_step(輪數計數器,表示當前輪數)
)

備註:

MOVING_AVERAGE_DECAY 滑動平均衰減率是超引數,一般設定的值比較大;

global_step - 輪數計數器,表示當前輪數,這個引數與其他計數器公用。

第二步 求算滑動平均節點ema_op

ema_op = ema.apply([])

ema.apply([ ]) 函式表示對 [ ] 中的所有數值求滑動平均。

示例:

ema_op = ema.apply(tf.trainable_variables())

每當執行此程式碼時,會對所以待優化引數進行求滑動平均運算。

第三步 具體實現方式

在工程應用中,我們通常會將計算滑動平均 ema_op 和訓練過程 train_step 繫結在一起執行,使其合成一個訓練節點,實現的程式碼如下

with tf.control_dependencies([ train_step, ema_op ]):
  train_op = tf.no_op(name = 'train')

 

另外:

檢視某引數的滑動平均值

函式ema.average(引數名) --->  返回 ’ 引數名 ’ 的滑動平均值,

3 示例程式碼

# 待優化引數w1,不斷更新w1引數,求w1的滑動平均(影子)

import tensorflow as tf

# 1. 定義變數及滑動平均類

# 定義一個32位浮點變數並賦初值為0.0,
w1 = tf.Variable(0, dtype=tf.float32)

# 輪數計數器,表示NN的迭代輪數,賦初始值為0,同時不可被優化(不引數訓練)
global_step = tf.Variable(0, trainable=False)

# 設定衰減率為0.99
MOVING_AVERAGE_DECAY = 0.99

# 例項化滑動平均類
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)

# ema.apply()函式中的引數為待優化更新列表
# 每執行sess.run(ema_op)時,會對函式中的引數求算滑動平均值
# tf.trainable_variables()函式會自動將所有待訓練的引數彙總為待列表
# 因該段程式碼中僅有w1一個引數,ema_op = ema.apply([w1])與下段程式碼等價
ema_op = ema.apply(tf.trainable_variables())


# 2. 檢視不同迭代中變數取值的變化。
with tf.Session() as sess:
    # 初始化
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    # 用ema.average(w1)獲取w1滑動平均值 (要執行多個節點,作為列表中的元素列出,寫在sess.run中)
    # 打印出當前引數w1和w1滑動平均值
    print("current global_step:", sess.run(global_step))
    print("current w1", sess.run([w1, ema.average(w1)]))

    # 引數w1的值賦為1
    sess.run(tf.assign(w1, 1))
    sess.run(ema_op)
    print("current global_step:", sess.run(global_step))
    print("current w1", sess.run([w1, ema.average(w1)]))

    # 更新global_step和w1的值,模擬出輪數為100時,引數w1變為10, 以下程式碼global_step保持為100,每次執行滑動平均操作,影子值會更新 
    sess.run(tf.assign(global_step, 100))
    sess.run(tf.assign(w1, 10))
    sess.run(ema_op)
    print("current global_step:", sess.run(global_step))
    print("current w1:", sess.run([w1, ema.average(w1)]))

    # 每次sess.run會更新一次w1的滑動平均值
    sess.run(ema_op)
    print("current global_step:", sess.run(global_step))
    print("current w1:", sess.run([w1, ema.average(w1)]))

    sess.run(ema_op)
    print("current global_step:", sess.run(global_step))
    print("current w1:", sess.run([w1, ema.average(w1)]))

    sess.run(ema_op)
    print("current global_step:" , sess.run(global_step))
    print("current w1:", sess.run([w1, ema.average(w1)]))

    sess.run(ema_op)
    print("current global_step:" , sess.run(global_step))
    print("current w1:", sess.run([w1, ema.average(w1)]))

執行

current global_step: 0
current w1 [0.0, 0.0]
current global_step: 0
current w1 [1.0, 0.9]
current global_step: 100
current w1: [10.0, 1.6445453]
current global_step: 100
current w1: [10.0, 2.3281732]
current global_step: 100
current w1: [10.0, 2.955868]
current global_step: 100
current w1: [10.0, 3.532206]
current global_step: 100
current w1: [10.0, 4.061389]