Tensorflow: использование пакетной нормализации дает плохие (неустойчивые) потери и точность проверки

Я пытаюсь использовать пакетную нормализацию с помощью tf.layers.batch_normalization () и мой код выглядит так:

def create_conv_exp_model(fingerprint_input, model_settings, is_training):


  # Dropout placeholder
  if is_training:
    dropout_prob = tf.placeholder(tf.float32, name='dropout_prob')

  # Mode placeholder
  mode_placeholder = tf.placeholder(tf.bool, name="mode_placeholder")

  he_init = tf.contrib.layers.variance_scaling_initializer(mode="FAN_AVG")

  # Input Layer
  input_frequency_size = model_settings['bins']
  input_time_size = model_settings['spectrogram_length']
  net = tf.reshape(fingerprint_input,
                   [-1, input_time_size, input_frequency_size, 1],
                   name="reshape")
  net = tf.layers.batch_normalization(net, 
                                      training=mode_placeholder,
                                      name='bn_0')

  for i in range(1, 6):
    net = tf.layers.conv2d(inputs=net,
                           filters=8*(2**i),
                           kernel_size=[5, 5],
                           padding='same',
                           kernel_initializer=he_init,
                           name="conv_%d"%i)
    net = tf.layers.batch_normalization(net,
                                        training=mode_placeholder,
                                        name='bn_%d'%i)
    with tf.name_scope("relu_%d"%i):
      net = tf.nn.relu(net)
    net = tf.layers.max_pooling2d(net, [2, 2], [2, 2], 'SAME', 
                                  name="maxpool_%d"%i)

  net_shape = net.get_shape().as_list()
  net_height = net_shape[1]
  net_width = net_shape[2]
  net = tf.layers.conv2d( inputs=net,
                          filters=1024,
                          kernel_size=[net_height, net_width],
                          strides=(net_height, net_width),
                          padding='same',
                          kernel_initializer=he_init,
                          name="conv_f")
  net = tf.layers.batch_normalization( net, 
                                        training=mode_placeholder,
                                        name='bn_f')
  with tf.name_scope("relu_f"):
    net = tf.nn.relu(net)

  net = tf.layers.conv2d( inputs=net,
                          filters=model_settings['label_count'],
                          kernel_size=[1, 1],
                          padding='same',
                          kernel_initializer=he_init,
                          name="conv_l")

  ### Squeeze
  squeezed = tf.squeeze(net, axis=[1, 2], name="squeezed")

  if is_training:
    return squeezed, dropout_prob, mode_placeholder
  else:
    return squeezed, mode_placeholder

А моя ступенька поезда выглядит так:

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
  optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate_input)
  gvs = optimizer.compute_gradients(cross_entropy_mean)
  capped_gvs = [(tf.clip_by_value(grad, -2., 2.), var) for grad, var in gvs]
  train_step = optimizer.apply_gradients(gvs))

Во время тренировки кормлю график:

train_summary, train_accuracy, cross_entropy_value, _, _ = sess.run(
    [
        merged_summaries, evaluation_step, cross_entropy_mean, train_step,
        increment_global_step
    ],
    feed_dict={
        fingerprint_input: train_fingerprints,
        ground_truth_input: train_ground_truth,
        learning_rate_input: learning_rate_value,
        dropout_prob: 0.5,
        mode_placeholder: True
    })

Во время проверки

validation_summary, validation_accuracy, conf_matrix = sess.run(
                [merged_summaries, evaluation_step, confusion_matrix],
                feed_dict={
                    fingerprint_input: validation_fingerprints,
                    ground_truth_input: validation_ground_truth,
                    dropout_prob: 1.0,
                    mode_placeholder: False
                })

Мои кривые потерь и точности (оранжевый - тренировка, синий - проверка): График потерь в зависимости от количества итераций, График зависимости точности от количества итераций

Потеря валидации (и точность) кажется очень неустойчивой. Моя реализация пакетной нормализации неверна? Или это нормально для пакетной нормализации, и мне нужно дождаться новых итераций?


person Zafarullah Mahmood    schedule 30.12.2017    source источник
comment
Нормально ли работает, если удалить BatchNorm? Похоже, это могло быть вызвано многими причинами.   -  person mxbi    schedule 30.12.2017
comment
@mxbi он работает нормально, когда я удаляю BatchNorm. Я обучил модель BatchNorm за 32 эпохи, но потеря валидации не уменьшилась.   -  person Zafarullah Mahmood    schedule 30.12.2017
comment
Это все еще проблема?   -  person gab    schedule 23.10.2019


Ответы (3)


Вам нужно передать is_training в tf.layers.batch_normalization(..., training=is_training), иначе он пытается нормализовать мини-пакеты вывода, используя статистику мини-пакетов вместо статистики обучения, что неверно.

person Alexandre Passos    schedule 31.12.2017
comment
не передавайте mode_placeholder, передавайте логическое значение, так как графики разные - person Alexandre Passos; 01.01.2018

В основном нужно проверить две вещи.

1. Вы уверены, что правильно используете пакетную нормализацию (BN) в поездной операции?

Если вы читаете документацию по слою:

Примечание: во время обучения необходимо обновить moving_mean и moving_variance. По умолчанию операции обновления помещаются в tf.GraphKeys.UPDATE_OPS, поэтому их нужно добавить как зависимость к train_op. Кроме того, не забудьте добавить любые операции batch_normalization перед получением коллекции update_ops. В противном случае update_ops будет пустым, и обучение / вывод не будет работать должным образом.

Например:

x_norm = tf.layers.batch_normalization(x, training=training)

# ...
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
     train_op = optimizer.minimize(loss)

2. В противном случае попробуйте снизить "импульс" в BN.

Фактически, во время обучения BN использует два скользящих средних среднего и дисперсии, которые, как предполагается, аппроксимируют статистику населения. Среднее значение и дисперсия инициализируются значениями 0 и 1 соответственно, а затем, шаг за шагом, они умножаются на значение импульса (по умолчанию 0,99) и добавляются новое значение * 0,01. Во время вывода (теста) нормализация использует эту статистику. По этой причине этим значениям требуется немного времени, чтобы прийти к «реальному» среднему значению и дисперсии данных.

Источник:

https://www.tensorflow.org/api_docs/python/tf/layers/batch_normalization

https://github.com/keras-team/keras/issues/7265

https://github.com/keras-team/keras/issues/3366

Оригинал статьи BN можно найти здесь:

https://arxiv.org/abs/1502.03167

person gab    schedule 16.11.2018

Я также наблюдал колебания потери валидации при добавлении нормы партии перед ReLU. Мы обнаружили, что перемещение нормы партии после того, как ReLU решило проблему.

person deuxpierres    schedule 01.06.2020