BN層的添加實戰

對於一個小白,從瞭解Batch Normalization(後面簡稱BN)到正確使用BN,可謂路漫漫兮。在此做一個記錄。

網上搜索關於BN最多的就是原理推導,相關論文出處。

例如:

http://blog.csdn.net/Fate_fjh/article/details/53375881

https://www.jianshu.com/p/0312e04e4e83

但是這個並不能幫助我們實際的使用,對於需要迅速用起來的夥伴幫助不大。我們工程師相信的是先用起來,再去研究原理!呵呵!

有一些文章介紹的BN層的實現,也有代碼示例,但能順利跑起來的寥寥。因爲使用BN不像卷積層那樣,寫個層的實現就可以了。由於BN層會包含兩個可訓練參數以及兩個不可訓練參數,所以涉及到在train代碼中如何保存的關鍵問題,以及在inference代碼中如何加載的問題。有相關博客介紹到這一步了,很有幫助。

例如:

https://www.cnblogs.com/hrlnw/p/7227447.html

本以爲別人都說這麼明白了,抄一抄不是很容易的事情嗎。可以上的代碼是不能讓你正確完成BN功能的。也不知是抄錯了,還是別人漏掉了一些關鍵環節。總之你的moving_mean/moving_variance好像就是不太對。基本上中文網頁很難在找到這個問題的解了。

現在你需要搜索的關鍵字可能要變成BN/參數保存/平均滑動等等了。還好tensorflow的github中有了線索:

https://github.com/tensorflow/tensorflow/issues/14809

https://github.com/tensorflow/tensorflow/issues/15250

可見有很多人確實無法正確使用BN功能,然而最有用的一個issues是:

https://github.com/tensorflow/tensorflow/issues/1122#issuecomment-280325584

在這裏,我拼湊成了一個完整能用的BN功能代碼,解決了我好久的痛苦,讓我興奮一下。

知識來源於網絡,奉獻給網絡。不敢獨享這一成果,再此分享給大家。

-----------------------------------------------------------------華麗的分割線----------------------------------------------------------------------------

整個BN功能的實現需要分三個部分:1.BN層實現;2.訓練時更新和完成後保存;3.預測時加載。

1.BN層實現:

如果你接觸了一段時間後,這裏你至少應該知道BN的三種實現方式了,但是我只成功了其中的一種,希望其他朋友能夠補充完善。

def bn_layer(x, scope, is_training, epsilon=0.001, decay=0.99, reuse=None):

    """

    Performs a batch normalization layer

    Args:

        x: input tensor

        scope: scope name

        is_training: python boolean value

        epsilon: the variance epsilon - a small float number to avoid dividing by 0

        decay: the moving average decay

    Returns:

        The ops of a batch normalization layer

    """

    with tf.variable_scope(scope, reuse=reuse):

        shape = x.get_shape().as_list()

        # gamma: a trainable scale factor

        gamma = tf.get_variable(scope+"_gamma", shape[-1], initializer=tf.constant_initializer(1.0), trainable=True)

        # beta: a trainable shift value

        beta = tf.get_variable(scope+"_beta", shape[-1], initializer=tf.constant_initializer(0.0), trainable=True)

        moving_avg = tf.get_variable(scope+"_moving_mean", shape[-1], initializer=tf.constant_initializer(0.0), trainable=False)

        moving_var = tf.get_variable(scope+"_moving_variance", shape[-1], initializer=tf.constant_initializer(1.0), trainable=False)

        if is_training:

            # tf.nn.moments == Calculate the mean and the variance of the tensor x

            avg, var = tf.nn.moments(x, np.arange(len(shape)-1), keep_dims=True)

            avg=tf.reshape(avg, [avg.shape.as_list()[-1]])

            var=tf.reshape(var, [var.shape.as_list()[-1]])

            #update_moving_avg = moving_averages.assign_moving_average(moving_avg, avg, decay)

            update_moving_avg=tf.assign(moving_avg, moving_avg*decay+avg*(1-decay))

            #update_moving_var = moving_averages.assign_moving_average(moving_var, var, decay)

            update_moving_var=tf.assign(moving_var, moving_var*decay+var*(1-decay))

            control_inputs = [update_moving_avg, update_moving_var]

        else:

            avg = moving_avg

            var = moving_var

            control_inputs = []

        with tf.control_dependencies(control_inputs):

            output = tf.nn.batch_normalization(x, avg, var, offset=beta, scale=gamma, variance_epsilon=epsilon)

    return output

def bn_layer_top(x, scope, is_training, epsilon=0.001, decay=0.99):

    """

    Returns a batch normalization layer that automatically switch between train and test phases based on the

    tensor is_training

    Args:

        x: input tensor

        scope: scope name

        is_training: boolean tensor or variable

        epsilon: epsilon parameter - see batch_norm_layer

        decay: epsilon parameter - see batch_norm_layer

    Returns:

        The correct batch normalization layer based on the value of is_training

    """

    #assert isinstance(is_training, (ops.Tensor, variables.Variable)) and is_training.dtype == tf.bool

    return tf.cond(

        is_training,

        lambda: bn_layer(x=x, scope=scope, epsilon=epsilon, decay=decay, is_training=True, reuse=None),

        lambda: bn_layer(x=x, scope=scope, epsilon=epsilon, decay=decay, is_training=False, reuse=True),

    )

這裏的參數epsilon=0.001, decay=0.99可以自行調整。

 

2.訓練時更新和完成後保存:

在訓練的代碼中增加如下代碼:

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

with tf.control_dependencies(update_ops):

    train = tf.train.AdamOptimizer(learning_rate=lr).minimize(cost)

這個是用於更新參數的。

var_list = tf.trainable_variables()

g_list = tf.global_variables()

bn_moving_vars = [gfor gin g_listif 'moving_mean' in g.name]

bn_moving_vars += [gfor gin g_listif 'moving_variance' in g.name]

var_list += bn_moving_vars

train_saver = tf.train.Saver(var_list=var_list)

這個是用於保存bn不可訓練的參數。

3.預測時加載:

# get moving avg

var_list = tf.trainable_variables()

g_list = tf.global_variables()

bn_moving_vars = [gfor gin g_listif 'moving_mean' in g.name]

bn_moving_vars += [gfor gin g_listif 'moving_variance' in g.name]

var_list += bn_moving_vars

saver = tf.train.Saver(var_list=var_list)

ckpt_path =""

saver.restore(sess, ckpt_path)

這樣就可以找到checkpoint中的參數了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章