Как использовать BatchNormalization с тензорным потоком?

У меня проблемы с использованием пакетной нормализации с тензорным потоком. Я построил следующую модель:

def weight_variable(kernal_shape):
    weights = tf.get_variable(name='weights', shape=kernal_shape, dtype=tf.float32, trainable=True,
                        initializer=tf.truncated_normal_initializer(stddev=0.02))
    return weights
def bias_variable(shape):
    initial = tf.constant(0.0, shape=shape)
    return tf.Variable(initial)

# return 1 conv layer
def conv_layer(x, w_shape, b_shape, is_training, padding='SAME'):
    W = weight_variable(w_shape)
    tf.summary.histogram("weights", W)

    b = bias_variable(b_shape)
    tf.summary.histogram("biases", b)

    # Note that I used a stride of 2 on purpose in order not to use max pool layer.
    conv = tf.nn.conv2d(x, W, strides=[1, 2, 2, 1], padding=padding) + b
    conv = tf.contrib.layers.batch_norm(conv, scale=True, is_training=is_training)

    activations = tf.nn.relu(conv)

    tf.summary.histogram("activations", activations)

    return activations

# return deconv layer
def deconv_layer(x, w_shape, b_shape, is_training, padding="SAME", activation='relu'):
    W = weight_variable(w_shape)
    tf.summary.histogram("weights", W)

    b = bias_variable(b_shape)
    tf.summary.histogram('biases', b)

    x_shape = tf.shape(x)
    # output shape: [batch_size, h * 2, w * 2, input_shape from w].
    out_shape = tf.stack([x_shape[0], x_shape[1] * 2, x_shape[2] * 2, w_shape[2]])
    # Note that I have used a stride of 2 since I used a stride of 2 in conv layer.

    conv_trans = tf.nn.conv2d_transpose(x, W, out_shape, [1, 2, 2, 1], padding=padding) + b
    conv_trans = tf.contrib.layers.batch_norm(conv_trans, scale=True, is_training=is_training)

    if activation == 'relu':
        transposed_activations = tf.nn.relu(conv_trans)
    else:
        transposed_activations = tf.nn.sigmoid(conv_trans)

    tf.summary.histogram("transpose_activation", transposed_activations)
    return transposed_activations

def model(input):
    with tf.variable_scope('conv1'):
        conv1 = conv_layer(input, [4, 4, 3, 32], [32], is_training=phase_train)  # image size: [56, 56]
    with tf.variable_scope('conv2'):
        conv2 = conv_layer(conv1, [4, 4, 32, 64], [64], is_training=phase_train)  # image size: [28, 28]
    with tf.variable_scope('conv3'):
        conv3 = conv_layer(conv2, [4, 4, 64, 128], [128], is_training=phase_train)  # image size: [14, 14]
    with tf.variable_scope('conv4'):
        conv4 = conv_layer(conv3, [4, 4, 128, 256], [256], is_training=phase_train)  # image size: [7, 7]
        conv4_reshaped = tf.reshape(conv4, [batch_size * num_participants, 7 * 7 * 256], name='conv4_reshaped')

    w_c_mu = tf.Variable(tf.truncated_normal([7 * 7 * 256, latent_dim], stddev=0.1), name='weight_fc_mu')
    b_c_mu = tf.Variable(tf.constant(0.1, shape=[latent_dim]), name='biases_fc_mu')
    w_c_sig = tf.Variable(tf.truncated_normal([7 * 7 * 256, latent_dim], stddev=0.1), name='weight_fc_sig')
    b_c_sig = tf.Variable(tf.constant(0.1, shape=[latent_dim]), name='biases_fc_sig')
    epsilon = tf.random_normal([1, latent_dim])

    tf.summary.histogram('weights_c_mu', w_c_mu)
    tf.summary.histogram('biases_c_mu', b_c_mu)
    tf.summary.histogram('weights_c_sig', w_c_sig)
    tf.summary.histogram('biases_c_sig', b_c_sig)

    with tf.variable_scope('mu'):
        mu = tf.nn.bias_add(tf.matmul(conv4_reshaped, w_c_mu), b_c_mu)
        tf.summary.histogram('mu', mu)

    with tf.variable_scope('stddev'):
        stddev = tf.nn.bias_add(tf.matmul(conv4_reshaped, w_c_sig), b_c_sig)
        tf.summary.histogram('stddev', stddev)

    with tf.variable_scope('z'):
        # This formula was adopted from the following paper: http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7979344
        latent_var = mu + tf.multiply(tf.sqrt(tf.exp(stddev)), epsilon)
        tf.summary.histogram('features_sig', stddev)

    with tf.variable_scope('GRU'):
        print(latent_var.get_shape().as_list())
        latent_var = tf.reshape(latent_var, shape=[int(batch_size / 100)* num_participants, time_steps, latent_dim])

        cell = tf.nn.rnn_cell.GRUCell(cell_size)   # state_size of cell_size.
        H, C = tf.nn.dynamic_rnn(cell, latent_var, dtype=tf.float32)  # H size: [batch_size * num_participants, SEQLEN, cell_size]
        H = tf.reshape(H, [batch_size * num_participants, cell_size])

    with tf.variable_scope('output'):
        # output layer.
        w_output = tf.Variable(tf.truncated_normal([cell_size, 1], mean=0, stddev=0.01, dtype=tf.float32, name='w_output'))
        tf.summary.histogram('w_output', w_output)
        b_output = tf.get_variable('b_output', shape=[1], dtype=tf.float32,
                                   initializer=tf.constant_initializer(0.0))
        predictions = tf.add(tf.matmul(H, w_output), b_output, name='softmax_output')
        tf.summary.histogram('output', predictions)

        var_list = [v for v in tf.global_variables() if 'GRU' in v.name]
        var_list.append([w_output, b_output])

    return predictions, var_list

Кроме того, я восстанавливаю параметры модели следующим образом:

saver_torestore = tf.train.Saver()

with tf.Session() as sess:
    train_writer = tf.summary.FileWriter(events_path, sess.graph)
    merged = tf.summary.merge_all()

    to_run_list = [merged, RMSE]

    # Initialize `iterator` with training data.
    sess.run(init_op)

    # Note that the last name "Graph_model" is the name of the saved checkpoints file => the ckpt is saved
    # under tensorboard_logs.
    ckpt = tf.train.get_checkpoint_state(
        os.path.dirname(model_path))
    if ckpt and ckpt.model_checkpoint_path:
        saver_torestore.restore(sess, ckpt.model_checkpoint_path)
        print('checkpoints are saved!!!')
    else:
        print('No stored checkpoints')

    counter = 0
    for _ in range(num_epoch):
        sess.run(iterator.initializer)
        print('epoch:', _)

        # This while loop will run indefinitly until the end of the first epoch
        while True:
            try:
                summary, loss_ = sess.run(to_run_list, feed_dict={phase_train: False})

                print('loss: ' + str(loss_))

                losses.append(loss_)
                counter += 1

                train_writer.add_summary(summary, counter)

            except tf.errors.OutOfRangeError:
                print('error, ignore ;) ')
                break

     print('average losses:', np.average(losses))
     train_writer.close()

Я удостоверяюсь, что переменные сохранены. Итак, я выполнил следующую команду:

def assign_values_to_batchNorm():
    vars = [v for v in tf.global_variables() if "BatchNorm" in v.name and "Adam" not in v.name]
    file_names = [(v.name[:-2].replace("/", "_") + ".txt") for v in vars]
    for var, file_name in zip(vars, file_names):
        lst = open(file_name).read().split(";")[:-1]
        print(lst)
        values = list(map(np.float32, lst))
        tf.assign(var, values)

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

И я вызвал assign_values_to_batchNorm() в сеансе. Я получил некоторые значения => Кажется, что скользящая средняя, ​​скользящая дисперсия, гамма и бетта сохранены.

Теперь обратите внимание, что я работаю над Windows 10, и у меня есть версия tensorflow 1.3.

Итак, всякий раз, когда я запускаю summary, loss_ = sess.run(to_run_list, feed_dict={phase_train: True}) в рамках сеанса, после инициализации/восстановления всех переменных я получаю RMSE 0,022, что является той же ошибкой, достигнутой в конце обучения модели. Теперь, если я установлю для phase_train значение false, я получу RMSE 0,038. Обратите внимание, что пока я просто тестирую сеть. Поэтому, несмотря на то, что я использую обучающий набор данных для тестирования, моя цель состояла в том, чтобы просто проверить поведение сети во время обучения/тестирования. Так что это так странно, я думаю, для меня. Обратите внимание, что фаза является заполнителем. У меня это в коде следующим образом:

phase_train = tf.placeholder(dtype=tf.bool, name='phase')

Кроме того, вот фрагмент кода для оптимизатора:

with tf.name_scope('optimizer'):
    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    with tf.control_dependencies(update_ops):
        optimizer = tf.train.AdamOptimizer(0.00001).minimize(RMSE) 

Основная проблема: RMSE = 0,038, когда фаза = False, и 0,022, когда фаза = True.

Любая помощь высоко ценится!!


person I. A    schedule 03.03.2018    source источник
comment
Я все еще не понимаю, в чем ваша проблема/ожидание. Насколько я знаю, запуск сети с пакетной нормой в режиме обучения обновит текущие средние значения, поэтому, если вы сделаете это один раз, вы, вероятно, больше не получите тех же результатов, даже если вы снова запустите ту же группу в тестовом режиме. после.   -  person mikkola    schedule 06.03.2018
comment
Опять же, проблема в том, что я еще не использовал набор данных для тестирования. Я обучил сеть с набором обучающих данных, а затем еще раз запустил ее с набором обучающих данных, в то время как для Phase_train установлено значение False. И я получил RMSE, указанный выше   -  person I. A    schedule 06.03.2018
comment
tf.assign(var, values) возвращает операцию, что означает, что она на самом деле не присваивает values var, если вы не запускаете эту операцию в рамках сеанса. Но я не уверен, что это проблема, потому что saver_torestore.restore по умолчанию должен загружать все переменные, включая moving_means и moving_vars. Еще один вопрос о полученном вами RMSE: вы тестируете все обучающие данные? вы тренируетесь со всеми тренировочными данными на каждой итерации вместо использования мини-пакета?   -  person LI Xuhong    schedule 06.03.2018
comment
Да, я тестирую все данные обучения за 1 эпоху, используя мини-пакет. И я тренировался со всеми тренировочными данными за 150 эпох, используя мини-пакет. Обратите внимание, что набор данных состоит из лиц. Так что я не уверен, что работа с лицами имеет какое-то значение??   -  person I. A    schedule 06.03.2018
comment
Сколько примеров в мини-партии при обучении и тестировании? Есть ли разница во входных данных предварительной обработки между этапом обучения и этапом тестирования? И почему вы думаете, что RMSE=0,038 и RMSE=0,022 сильно отличаются? Каких результатов вы ожидаете? (Они не будут такими же, как сказано в первом комментарии, вы, вероятно, больше не получите тех же результатов.)   -  person LI Xuhong    schedule 07.03.2018
comment
@ Семь, ну, я использую одинаковое количество примеров в каждой партии как для обучения, так и для тестирования. Кроме того, я пытаюсь выполнить регрессию, когда моя цель находится между -1 и +1. Поэтому я хочу быть как можно ближе к цели, что в моем случае имеет значение RMSE 0,022 и 0,038. Надеюсь, это может ответить на ваши опасения?   -  person I. A    schedule 07.03.2018


Ответы (2)


Переформулировка вопроса

Похоже, ваша забота заключается в следующем: вы сохраняете все то же самое (данные обучения, количество пакетов, количество эпох в ваших данных обучения, инициализацию и т. д.), за исключением значения phase_train. Когда phase_train=True выполнение обучения дает RMSE 0,022, а когда phase_train=False выполнение обучения дает RMSE 0,038, и вы думаете, что независимо от значения phase_train RMSE должно быть одинаковым (либо 0,022, либо 0,038). Пожалуйста, дайте мне знать, если это не то, что вы имели в виду.

Ответ на вопрос

Ответ здесь заключается в том, что RMSE должен отличаться при phase_train=True по сравнению с phase_train=False. Давайте посмотрим, почему это ожидается.

Вы настраиваете свой график следующим образом:

conv1 = conv_layer(input, [4, 4, 3, 32], [32], is_training=phase_train)

Затем, если мы посмотрим на ваш код в функции conv_layer(...), вы используете переменную is_training следующим образом:

conv = tf.contrib.layers.batch_norm(conv, scale=True, is_training=is_training)

Теперь давайте посмотрим на документацию для tf.contrib.layers.batch_norm (https://www.tensorflow.org/api_docs/python/tf/contrib/layers/batch_norm):

is_training: находится ли слой в режиме обучения. В режиме обучения он будет накапливать статистику моментов в moving_mean и moving_variance, используя экспоненциальное скользящее среднее с заданным спадом. Когда он не находится в режиме обучения, он будет использовать значения moving_mean и moving_variance.

Как видно из документации, is_training=True вызывает другую функциональность по сравнению с is_training=False. В частности, когда is_training=True, константа нормализации вычисляется с использованием затухания, а когда is_training=False затухания нет. Ваш код делает разные вещи, когда вы переключаете значение is_training, поэтому ваша ошибка RMSE отличается.

Если вы столкнетесь с другими проблемами, подобными этой, проверка документации Tensorflow может помочь объяснить неожиданные результаты. Кроме того, не рекомендуется обучать вашу модель с флагом is_training, установленным где-то на False.

Зачем использовать Decay, если is_training=True?

Вам может быть интересно, почему Tensorflow добавляет затухание, когда is_training=True. Ответ заключается в том, что когда вы тренируетесь, веса в вашей нейронной сети обновляются, чтобы становиться все лучше и лучше. Это означает, что среднее значение и дисперсия в ваших более ранних обновлениях очень неточны, а среднее значение и дисперсия в ваших более поздних обновлениях очень точны. Поскольку более ранние обновления неточны, вы хотите, чтобы они меньше говорили о том, как должны обновляться веса нейронной сети, поэтому вы уменьшаете на 0,999 при каждом последующем обновлении.

Например, когда is_training=True, среднее значение и дисперсия вашего первого обновления весов в 0.999^10000 ~ 0.000045 так же значительны, как среднее значение и дисперсия вашего 10 000-го обновления. Это имеет смысл, потому что при вашем первом обновлении ваши веса по существу случайны и определенно не дают среднего значения и дисперсии, столь же значимых, как среднее значение и дисперсия при 10000-м обновлении.

Когда is_training=False, это означает, что вы говорите Tensorflow, что уже изучили соответствующие веса в своей нейронной сети. Вы говорите Tensorflow, что вы уже все обучили, веса имеют смысл, а среднее значение и дисперсия, которые вы получаете в своей пакетной норме, имеют смысл. Так что не надо ничего портить.

Это объяснение будет соответствовать вашей ошибке RMSE. Если вы тренируетесь с is_training=False, вы неэффективны, придавая большее значение случайным весам, которыми вы инициализировали свою нейронную сеть, поэтому ваша окончательная модель не будет такой хорошей. И, как вы заметили, ваша среднеквадратичная ошибка при прогоне is_training=False выше, чем ваша среднеквадратичная ошибка при прогоне is_training=True.

person user2570465    schedule 11.03.2018
comment
Так вы имеете в виду, что я должен установить затухание на меньшее значение, когда is_training=False? Если да, то существует ли идеальное значение? спасибо за помощь - person I. A; 11.03.2018
comment
Как сказано в документации, всякий раз, когда is_training=False, он игнорирует затухание. Нет смысла использовать затухание, когда is_training=False. Распад имеет значение только тогда, когда is_training=True. Даже если вы установите затухание на меньшее значение, оно все равно будет игнорироваться, если is_training=False. - person user2570465; 11.03.2018
comment
так что я согласен с вами. Но проблема в том, что даже после инициализации модели с помощью sess.run(init_op) я восстанавливаю весь граф с помощью saver_torestore.restore. Следовательно, я не инициализирую случайным образом, как вы упомянули. Пожалуйста, поправьте меня, если я ошибаюсь. Спасибо. - person I. A; 11.03.2018
comment
Допустим, вы удалили все свои контрольные точки. Тогда с какой контрольной точки можно восстановить? Даже если вы восстанавливаетесь с контрольной точки, в самом начале у вас не было контрольных точек, поэтому вы должны были инициализироваться случайным образом. У вас есть initializer=tf.truncated_normal_initializer(stddev=0.02) в вашем коде для инициализации веса. Кроме того, контрольные точки подобны резервным копиям на случай, если вы перетренируетесь или ваш код выйдет из строя. Не следует выполнять повторную инициализацию с контрольной точки на каждой итерации обучения. Ваш график может сохранять состояние во время работы. Можете ли вы опубликовать свой код, который запускает различные итерации графика? - person user2570465; 11.03.2018
comment
Хорошо, пожалуйста, посмотрите отредактированный код выше под tf.Session() as sess: - person I. A; 11.03.2018
comment
В вашем отредактированном коде вы инициализируете в начале контрольную точку, если она существует. Затем вы тренируете: while True: ... summary, loss_ = sess.run(...). На каждой итерации цикла while ваш оптимизатор Адама будет выполнять обратное распространение и слегка изменять веса нейронной сети. И если у вас есть is_training=True, бэкпроп будет учитывать испорченные средние значения и отклонения в норме партии. Если у вас есть is_training=False, обратное распространение будет учитывать нормальное среднее значение и дисперсию. Опять же, ваш код работает по-разному в зависимости от настройки is_training. - person user2570465; 11.03.2018
comment
И, как сказано в документации Tensorflow, нет способа получить ошибку is_training=True RMSE с ошибкой is_training=False RMSE в вашей ситуации, потому что они не тренируются одинаково. - person user2570465; 11.03.2018
comment
Я думаю, это неправда. Чтобы выполнить обратное распространение и изменить веса, вы должны запустить операцию оптимизатора, то есть sess.run(optimizer), а в моем случае этого не происходит. Поэтому я просто распространяю пакет изображений и смотрю на RMSE. Во-вторых, чтобы прокомментировать ваш предыдущий комментарий, обратите внимание, что при восстановлении переменных любые значения, которые веса принимают в начале, будут заменены. Чтобы проиллюстрировать это, я попытался запустить модель без sess.run(init_op) и получил тот же ответ. Так что все же я думаю, что вы не поняли ошибку. Еще раз спасибо!! - person I. A; 11.03.2018
comment
Думаю, мы немного сбились с пути. Когда я увидел эпохи, я предположил, что вы тренируете свои данные. Я предполагаю, что это было не совсем правильно: вы просто снова и снова вычисляете одно и то же RMSE... что было не интуитивно понятно для меня. Так что забудьте о том, как вы работаете с графиком. Это все еще из-за фактора распада. В режиме is_training=True, когда вы запускаете RMSE, который запускает batch_norm, вы умножаете движущуюся статистику на 0,99. В режиме is_training=False вы не умножаете на 0,99. Вы по-прежнему делаете разные вещи, когда переключаете is_training. - person user2570465; 12.03.2018
comment
Я думаю, что это становится очень долгой дискуссией сейчас. Я закончу на этом: вы переключаете is_training, и документация по тензорному потоку говорит нам, что это меняет поведение. Надеюсь, вы решите свою проблему. - person user2570465; 12.03.2018

Поэтому я подумал, что может возникнуть проблема с использованием слоя пакетной нормализации. Поэтому я создал простую модель и обучил ее набору данных MNIST. Итак, у нас есть 2 сценария, в первом случае обучение модели с пакетной нормой, во втором случае обучение без пакетной нормы.

Теперь, если мы сравним результат тестирования с пакетной нормой и без нее, мы увидим, что мы получаем более высокую точность или меньшие потери при использовании BN. Помните, что модель, включающая BN, при тестировании фазы устанавливается в false. Таким образом, мы можем сделать вывод, что модель с BN лучше, чем без BN.

Во-вторых, если мы рассмотрим модель, обученную с пакетной нормализацией. Теперь, если мы сравним потери на тестовом наборе (при установке для фазы значения True, с одной стороны, и False, с другой стороны), мы придем к выводу, что достигнем лучших результатов, установив для фазы значение True. Потому что интуитивно использование статистики текущего пакета более точно, чем статистика обучающего набора данных.

В заключение, моя проблема возникает после обучения модели с пакетной нормализацией и тестирования модели при установке для фазы значения True, а затем False. Таким образом, мы наверняка получим лучшие потери (ниже), установив для фазы значение true, а не false.

person I. A    schedule 23.03.2018