Как использовать пакетную нормализацию Tensorflow с GradientTape?

Предположим, у нас есть простая модель Keras, использующая BatchNormalization:

model = tf.keras.Sequential([
                     tf.keras.layers.InputLayer(input_shape=(1,)),
                     tf.keras.layers.BatchNormalization()
])

Как на самом деле использовать его с GradientTape? Следующее, похоже, не работает, поскольку оно не обновляет скользящие средние?

# model training... we want the output values to be close to 150
for i in range(1000):
  x = np.random.randint(100, 110, 10).astype(np.float32)
  with tf.GradientTape() as tape:
    y = model(np.expand_dims(x, axis=1))
    loss = tf.reduce_mean(tf.square(y - 150))
  grads = tape.gradient(loss, model.variables)
  opt.apply_gradients(zip(grads, model.variables))

В частности, если вы просматриваете скользящие средние, они остаются прежними (проверьте model.variables, средние значения всегда равны 0 и 1). Я знаю, что можно использовать .fit() и .predict(), но я хотел бы использовать GradientTape и не знаю, как это сделать. В некоторых версиях документации предлагается обновить update_ops, но, похоже, это не работает в активном режиме.

В частности, следующий код не выведет ничего близкого к 150 после приведенного выше обучения.

x = np.random.randint(200, 210, 100).astype(np.float32)
print(model(np.expand_dims(x, axis=1)))

person Zuza    schedule 13.06.2019    source источник
comment
увидеть обновление ответа   -  person y.selivonchyk    schedule 15.06.2019
comment
Я согласен с вами (комментарий об обучении и оценке), поэтому кажется, что .fit() работает? Любая идея, как заставить его работать в моих условиях?   -  person Zuza    schedule 16.06.2019


Ответы (3)


с режимом градиентной ленты Слой BatchNormalization следует вызывать с аргументом training=True

пример:

inp = KL.Input( (64,64,3) )
x = inp
x = KL.Conv2D(3, kernel_size=3, padding='same')(x)
x = KL.BatchNormalization()(x, training=True)
model = KM.Model(inp, x)

тогда движущиеся переменные правильно обновляются

>>> model.layers[2].weights[2]
<tf.Variable 'batch_normalization/moving_mean:0' shape=(3,) dtype=float32, numpy
=array([-0.00062087,  0.00015137, -0.00013239], dtype=float32)>
person iperov    schedule 27.09.2019

Я просто сдаюсь. Я потратил немного времени, пытаясь понять модель, которая выглядит так:

model = tf.keras.Sequential([
                     tf.keras.layers.BatchNormalization(),
])

И я сдаюсь, потому что это выглядит так: введите здесь описание изображения

Моя интуиция подсказывала, что BatchNorm в наши дни не так прост, как раньше, и именно поэтому он масштабирует исходный дистрибутив, но не так много нового дистрибутива (а это позор), но ни у кого нет на это времени.

Правка: причина такого поведения в том, что BN только вычисляет моменты и нормализует партии во время обучения. Во время обучения он поддерживает средние средние значения и отклонения, и как только вы переключаетесь на оценку, параметры используются как константы. т. е. оценка не должна зависеть от нормализации, поскольку оценка может использоваться даже для одного входа и не может полагаться на статистику партии. Поскольку константы рассчитываются для другого дистрибутива, вы получаете более высокую ошибку при оценке.

person y.selivonchyk    schedule 14.06.2019
comment
:) Тем не менее спасибо за попытку. Кстати, как ты сделал эту схему? - person Zuza; 14.06.2019
comment
Использование тензорборда. Но мне пришлось отказаться от жадного выполнения и переключиться на сеансы для этого. - person y.selivonchyk; 14.06.2019

В режиме Gradient Tape вы обычно найдете такие градиенты, как:

with tf.GradientTape() as tape:
    y_pred = model(features)
    loss = your_loss_function(y_pred, y_true)
    gradients = tape.gradient(loss, model.trainable_variables)

train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))

Однако, если ваша модель содержит слой BatchNormalization или Dropout (или любой слой с разными фазами обучения/тестирования), то tf не сможет построить график.

Хорошей практикой было бы явно использовать параметр trainable при получении выходных данных из модели. При оптимизации используйте model(features, trainable=True), а при прогнозировании используйте model(features, trainable=False), чтобы явно выбрать фазу обучения/тестирования при использовании таких слоев.

Для фазы PREDICT и EVAL используйте

training = (mode == tf.estimator.ModeKeys.TRAIN)
y_pred = model(features, trainable=training)

Для фазы TRAIN используйте

with tf.GradientTape() as tape:
    y_pred = model(features, trainable=training)
    loss = your_loss_function(y_pred, y_true)
    gradients = tape.gradient(loss, model.trainable_variables)

train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))

Обратите внимание, что ответ иперова также работает, за исключением того, что вам нужно будет вручную установить фазу обучения для этих слоев.

x = BatchNormalization()(x, training=True)
x = Dropout(rate=0.25)(x, training=True)

x = BatchNormalization()(x, training=False)
x = Dropout(rate=0.25)(x, training=False)

Я бы рекомендовал иметь одну функцию get_model, которая возвращает модель, изменяя при этом фазу с помощью параметра training при вызове модели.

Примечание:

Если вы используете model.variables при поиске градиентов, вы получите это предупреждение

Gradients do not exist for variables 
['layer_1_bn/moving_mean:0', 
'layer_1_bn/moving_variance:0', 
'layer_2_bn/moving_mean:0', 
'layer_2_bn/moving_variance:0'] 
when minimizing the loss.

Это можно решить, вычислив градиенты только для обучаемых переменных. Замените model.variables на model.trainable_variables

person Saravanabalagi Ramachandran    schedule 20.12.2019