Нормализация партии в tf.keras не рассчитывает среднее и среднее отклонение - PullRequest
5 голосов
/ 29 марта 2019

Похожий вопрос без ответа был задан здесь . Я тестирую один алгоритм обучения с глубоким подкреплением, который использует keras backend в tenorflow Я не очень знаком с tf.keras, но все же хотел бы добавить слои нормализации партии. Поэтому я пытаюсь использовать tf.keras.layers.BatchNormalization(), но он не обновляет средние значения и отклонения, потому что update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) пусто.

Использование обычного tf.layers.batch_normalization, кажется, работает нормально. Однако, поскольку полный алгоритм несколько сложен, Мне нужно найти способ использовать tf.keras.

Стандартный tf слой batch_normed = tf.layers.batch_normalization(hidden, training=True) обновляет средние значения, поскольку update_ops не является пустым:

[
    <tf.Operation 'batch_normalization/AssignMovingAvg' type=AssignSub>, 
    <tf.Operation 'batch_normalization/AssignMovingAvg_1' type=AssignSub>, 
    <tf.Operation 'batch_normalization_1/AssignMovingAvg' type=AssignSub>, 
    <tf.Operation 'batch_normalization_1/AssignMovingAvg_1' type=AssignSub>
]

Минимальный пример, который не работает:

import tensorflow as tf
import numpy as np

tf.reset_default_graph()
graph = tf.get_default_graph()
tf.keras.backend.set_learning_phase(True)

input_shapes = [(3, )]
hidden_layer_sizes = [16, 16]

inputs = [
    tf.keras.layers.Input(shape=input_shape)
    for input_shape in input_shapes
]

concatenated = tf.keras.layers.Lambda(
    lambda x: tf.concat(x, axis=-1)
)(inputs)

out = concatenated
for units in hidden_layer_sizes:      
    hidden = tf.keras.layers.Dense(
    units, activation=None
    )(out)
    batch_normed = tf.keras.layers.BatchNormalization()(hidden, training=True)
    #batch_normed = tf.layers.batch_normalization(hidden, training=True)
    out = tf.keras.layers.Activation('relu')(batch_normed)

out = tf.keras.layers.Dense(
    units=1, activation='linear'
)(out)


data = np.random.rand(100,3)
with tf.Session(graph=graph) as sess:
    sess.run(tf.global_variables_initializer())

    for i in range(10):

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

    sess.run(update_ops,  {inputs[0]: data})
    sess.run(out, {inputs[0]: data})

    variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                          scope='batch_normalization')
    bn_gamma, bn_beta, bn_moving_mean, bn_moving_variance = [], [], [], []
    for variable in variables:
        val = sess.run(variable)
        nv = np.linalg.norm(val)
        if 'gamma' in variable.name:
            bn_gamma.append(nv)
        if 'beta' in variable.name:
            bn_beta.append(nv)
        if 'moving_mean' in variable.name:
            bn_moving_mean.append(nv)
        if 'moving_variance' in variable.name:
            bn_moving_variance.append(nv)

        diagnostics = {
            'bn_Q_gamma': np.mean(bn_gamma),
            'bn_Q_beta': np.mean(bn_beta),
            'bn_Q_moving_mean': np.mean(bn_moving_mean),
            'bn_Q_moving_variance': np.mean(bn_moving_variance),
        }

    print(diagnostics)

Вывод следующий (вы можете видеть, что moving_mean и moving_variance не меняются):

{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}

Хотя ожидаемый результат выглядит примерно так (прокомментируйте строку с batch_normed исчислением, используя tf.keras и раскомментируйте тот, что ниже):

{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0148749575, 'bn_Q_moving_variance': 3.966927}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.029601166, 'bn_Q_moving_variance': 3.934192}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.04418011, 'bn_Q_moving_variance': 3.9017918}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.05861327, 'bn_Q_moving_variance': 3.8697228}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0729021, 'bn_Q_moving_variance': 3.8379822}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.08704803, 'bn_Q_moving_variance': 3.8065662}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.10105251, 'bn_Q_moving_variance': 3.7754717}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.11491694, 'bn_Q_moving_variance': 3.7446957}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.12864274, 'bn_Q_moving_variance': 3.7142346}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.14223127, 'bn_Q_moving_variance': 3.6840856}

Примечание

Даже с tf.layers.batch_normalization есть что-то подозрительное. Стандартный tf подход tf.control_dependencies:

    with tf.control_dependencies(update_ops):
        sess.run(out, {inputs[0]: data})

, который я помещаю вместо следующих двух строк в коде выше:

    sess.run(update_ops,  {inputs[0]: data})
    sess.run(out, {inputs[0]: data})

производит bn_Q_moving_mean = 0.0 и bn_Q_moving_variance = 4.0

Ответы [ 2 ]

5 голосов
/ 31 марта 2019

Это потому, что tf.keras.layers.BatchNormalization наследуется от tf.keras.layers.Layer. API Keras обрабатывает операции обновления как часть своего соответствия и оценивает циклы. Это, в свою очередь, означает, что он не будет обновлять коллекцию tf.GraphKeys.UPDATE_OPS без нее.

Итак, чтобы заставить его работать, вам нужно обновить его вручную

hidden = tf.keras.layers.Dense(units, activation=None)(out)
batch_normed = tf.keras.layers.BatchNormalization(trainable=True) 
layer = batch_normed(hidden)

Это создает отдельный экземпляр класса

tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)

А для этого обновления нужна коллекция. Также посмотрите https://github.com/tensorflow/tensorflow/issues/25525

1 голос
/ 15 июля 2019
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

это может решить

tf.control_dependencies(update_ops)

ошибку.

, если использовать

tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)

возврат

tf.get_collection(tf.GraphKeys.UPDATE_OPS)

это список в списке, такой же как [[что-то]]

и использование

tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

возвращение

tf.get_collection(tf.GraphKeys.UPDATE_OPS)

равно [что-то1, что-то2, ...]

я думаю, что это решение.

но выход другой, и я не знаю, что правда.

...