Tensorflow: Capa de norma por lotes fácil de usar.

Creado en 16 feb. 2016  ·  127Comentarios  ·  Fuente: tensorflow/tensorflow

Muchos no expertos están utilizando el siguiente código http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow?answertab=votes#tab -top.

Sería bueno tener una capa de norma de lote oficial dada su importancia en el entrenamiento de DNN.

contributions welcome docs-bug

Comentario más útil

Todos 127 comentarios

Estoy trabajando en algunas partes de eso.

Creo que algo anda mal con esta capa. en el entrenamiento todo está bien y la pérdida disminuye muy bien. pero en las pruebas obtengo una precisión cero.
Por cierto, en las pruebas cuando uso is_training = False, obtengo cero acc.
Sé que la normalización por lotes se comporta de manera diferente en la fase de entrenamiento y prueba, como se describe en ¿Cómo se comporta de manera diferente la normalización por lotes en el tiempo de entrenamiento y de prueba?

Lo mismo aquí, he experimentado un comportamiento inesperado con is_training = False. ¿Cuál es la forma correcta de cambiar esta bandera? Actualmente estoy usando un tf.cond porque no toma tf.placeholders por sí solo.

@pawni Tienes que usar un booleano de Python para is_training . No puede ser un tf.cond .

@ppwwyyxx bueno, estoy haciendo tf.cond(placeholder, batch_norm(.., is_training = True), batch_norm(.., is_training = False)) o ¿se supone que uno solo debe hacer un batch_norm(.., is_training=variable) y cambiar eso fuera del gráfico cuando sea necesario?

Oh, pensé que estabas haciendo batch_norm(.., is_training=tf.cond(placeholder)) , lo cual es incorrecto.
Tu camino actual también puede tener problemas. Deberá verificar dos veces que las dos operaciones batch_norm que creó comparten el mismo alcance; de ​​lo contrario, no compartirán las estadísticas de media / varianza subyacentes.

Para hacer esto, el argumento reuse podría ayudar, pero no estoy seguro porque uso mi propia versión de la capa bn.

Estoy usando el mismo alcance y reuse=True . A veces parece funcionar, pero no estoy muy seguro. Sería genial si la capa pudiera agregarse a la documentación con una breve explicación sobre cómo manejar mejor el cambio de entrenamiento a prueba.

@sguada FYI

Actualmente, batch_norm requiere un booleano de python, pero estamos trabajando para agregar la opción de pasar un tensor.

@pawni Si no quiere preocuparse por la actualización de Moving_mean y Moving_variance, configure updates_collections = None para asegurarse de que se actualicen en su lugar; de lo contrario, debe asegurarse de que los update_ops agregados a tf.GraphKeys.UPDATE_OPS se ejecuten durante el entrenamiento.

Creo que tensorflow necesita 2 hiper métodos que cambian el estado del modelo, algo así como antorcha. cambiar el estado del modelo . Creo que es muy sencillo.

¿Existe un pequeño script con un NN muy simple que muestre cuál es la forma correcta de usar esta capa BN "oficial"? Realmente lo agradecería.

lo siento si esto es un poco repetitivo, pero parece que la API habla de BN en una interfaz diferente: https://www.tensorflow.org/versions/r0.9/api_docs/python/nn.html#batch_normalization

¿No es esa la forma oficial de usar BN? Estoy confundido sobre cómo usarlo y el SO parece estar desactualizado y luego hay una capa en un enlace diferente de la API, ¿cómo se hace exactamente esto? No tengo claro si ir a SO o preguntar aquí.

perdón por el spam, pero qué hay de malo en usar algo como esto:

def standard_batch_norm(l, x, n_out, phase_train, scope='BN'):
    """
    Batch normalization on feedforward maps.
    Args:
        x:           Vector
        n_out:       integer, depth of input maps
        phase_train: boolean tf.Varialbe, true indicates training phase
        scope:       string, variable scope
    Return:
        normed:      batch-normalized maps
    """
    with tf.variable_scope(scope+l):
        #beta = tf.Variable(tf.constant(0.0, shape=[n_out], dtype=tf.float64 ), name='beta', trainable=True, dtype=tf.float64 )
        #gamma = tf.Variable(tf.constant(1.0, shape=[n_out],dtype=tf.float64 ), name='gamma', trainable=True, dtype=tf.float64 )
        init_beta = tf.constant(0.0, shape=[n_out], dtype=tf.float64)
        init_gamma = tf.constant(1.0, shape=[n_out],dtype=tf.float64)
        beta = tf.get_variable(name='beta'+l, dtype=tf.float64, initializer=init_beta, regularizer=None, trainable=True)
        gamma = tf.get_variable(name='gamma'+l, dtype=tf.float64, initializer=init_gamma, regularizer=None, trainable=True)
        batch_mean, batch_var = tf.nn.moments(x, [0], name='moments')
        ema = tf.train.ExponentialMovingAverage(decay=0.5)

        def mean_var_with_update():
            ema_apply_op = ema.apply([batch_mean, batch_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(batch_mean), tf.identity(batch_var)

        mean, var = tf.cond(phase_train, mean_var_with_update, lambda: (ema.average(batch_mean), ema.average(batch_var)))
        normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
    return normed

entonces es simple decirle a tensorflow cuál usar con un diccionario de feeds como en:

feed_dict = {x: Xminibatch, y_: Yminibatch, phase_train: True}
sess.run(fetches=[merged,train_step], feed_dict=feed_dict)

Dado que no está claro si la implementación cambiará, quería dar una sugerencia (tenga en cuenta que es fácil extenderlo a convoluciones y cosas, simplemente no pegué ese código).

@pawni @ppwwyyxx , chicos, ¿decidieron si tenían que usar la reutilización en verdadero para resolver el problema del alcance?

@ brando90 actualmente estoy haciendo algo como:

def BatchNorm(inputT, is_training=True, scope=None):
    return tf.cond(isTraining,
                lambda: batch_norm(inputT, is_training=True,
                                   center=False, updates_collections=None, scope=scope),
                lambda: batch_norm(inputT, is_training=False,
                                   updates_collections=None, center=False, scope=scope, reuse = True))

Sin embargo, creo que # 3265 básicamente querría implementarlo así. Una referencia podría ser la implementación de abandono aquí: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/layers/python/layers/layers.py#L433 -L435

Cuando updates_collections = None, las actualizaciones ocurren en el lugar y es más fácil usar un tf.cond () para permitir que is_training sea un Tensor un poco más complicado es cuando las actualizaciones se retrasan y los update_ops se ejecutan más tarde.
Intentaré conseguir la primera parte pronto.

@ brando90 @pawni su código funciona bien, pero tiene que cambiar como se muestra a continuación

def BatchNorm(inputT, is_training=True, scope=None):
    # Note: is_training is tf.placeholder(tf.bool) type
    return tf.cond(is_training,  
                lambda: batch_norm(inputT, is_training=True,  
                                   center=False, updates_collections=None, scope=scope),  
                lambda: batch_norm(inputT, is_training=False,  
                                   updates_collections=None, center=False, scope=scope, reuse = True))  

Y cuando se ejecuta en tiempo de entrenamiento o prueba,

# when training 
sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=True})  

# when test 
sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=False})  

Este código funciona, pero como el # 3265 dice que sería genial si tf.contrib.layers.batch_norm obtiene is_training variable como tf.plcaeholer .

@nmhkahn @pawni gracias por los fragmentos de código. Fueron muy útiles para agregar la normalización por lotes a mi red de convolución. La formación parece funcionar muy bien. Las pruebas no lo son. En algunas versiones del código, las precisiones de entrenamiento son mucho más altas que las de prueba, lo que probablemente significa que no estoy compartiendo parámetros de normalización por lotes. En otras versiones del código obtengo "ValueError: Variable conv1 / beta ya existe, no permitido. ¿Quisiste establecer reuse = True en VarScope?" lo que parece indicar que estoy tratando de volver a aprender el parámetro ... cuando estaba tratando de reutilizarlo.

¿Alguien puede proporcionar un ejemplo de cómo llamar a la función "def BatchNorm" durante el entrenamiento y las pruebas para que el intercambio de variables se realice correctamente?

Gracias por cualquier ayuda.

ACTUALIZACIÓN 25 de julio de 2016:

@nmhkahn @pawni gracias por tus comentarios. Después de mirar más de cerca el código en contrib, me di cuenta de cuál era mi problema. Durante el entrenamiento y las pruebas, estamos actualizando o reutilizando cuatro variables (beta, gamma, Moving_mean y Moving_variance). Para hacerlos únicos, tuve que establecer un alcance por capa. Lo hice así:

conv1 = tf.nn.relu (batch_norm_layer (conv2d_stride2_valid (data, W_conv1) + b_conv1, train_phase, scope = "conv1"))

donde batch_norm_layer es similar a los ejemplos de @nmhkahn @pawni , conv2d_stride2_valid es solo una definición para definir una capa convolucional, y W_conv1 y b_conv1 son variables que mantienen los pesos y sesgos. Probablemente podría eliminar el término de sesgo porque estamos usando la normalización por lotes.

La red está funcionando bien ahora. Después de graficar las precisiones en el modo de entrenamiento y prueba, noté que las precisiones de las pruebas comienzan a subir después de las precisiones de entrenamiento. En retrospectiva, tiene sentido ya que estamos recopilando estadísticas de conjuntos de datos para realizar pruebas. Pero parecía como si estuviera haciendo algo mal durante mis pruebas iniciales. Gracias por sus comentarios y por poner a disposición de la comunidad la normalización por lotes.

@nmhkahn ¿en qué se diferencia de la sugerencia de pawni?

@ brando90 Tuve un pequeño error en mi versión que fue corregido por nmhkahn (cambiando isTraining a is_training )

@diegoAtAlpine Encontré los mismos problemas, aunque no estoy seguro de por qué este es el caso. Sin embargo, el ValueError debería resolverse mediante el fragmento de código. ¿No está seguro de cómo desea llamarlo, ya que los ejemplos de nmhkahn parecen hacer el trabajo?

@nmhkahn @pawni @ cuando lo hagas:

sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=True})

¿no significa eso que estás usando is_training como marcador de posición? La gente ha comentado que quieren que is_training sea ​​un poseedor de placer, pero eso es lo que tenía para mi versión:

def batch_norm_layer(x,train_phase,scope_bn):

    bn_train = batch_norm(x, decay=0.999, center=True, scale=True,
    is_training=True,
    reuse=None, # is this right?
    trainable=True,
    scope=scope_bn)
    bn_inference = batch_norm(x, decay=0.999, center=True, scale=True,
    is_training=False,
    reuse=True, # is this right?
    trainable=True,
    scope=scope_bn)
    z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
    return z

¿Eso no es correcto?

Ya extendí tf.contrib.layers.batch_norm para permitir pasar un tensor o un marcador de posición para is_training. Pronto se fusionará con TF contrib.

Ahora disponible en
https://github.com/tensorflow/tensorflow/commit/9da5fc8e6425cabd61fc36f0dcc1823a093d5c1d#diff -94bbcef0ec8a5cdef55f705e99c2b2ed

¿Soy solo yo o agregar esta capa BN ralentiza notablemente el entrenamiento de una sola época?

@ brando90 También me ralentiza el entrenamiento, pero creo que esto es lo esperado ya que necesita calcular algunas estadísticas. Y tu versión me queda bien.

BatchNorm es actualmente muy lento (debido a todas las estadísticas calculadas), pero están trabajando para agregar una operación cudnn batchnorm como se dice aquí .

@nmhkahn pregunta rápida. Cuando escribiste (para probar):

sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=False})

en teoría, ¿pueden ser bx y by cualquier conjunto de datos? es decir, ¿todavía puede ser el conjunto de entrenamiento aunque no estemos entrenando? (es decir, solo para rastrear el error del tren)

@ brando90 tienes razón.

También estoy confundido con respecto a is_training y reutilizar banderas. He creado un programa siguiendo el ejemplo de CIFAR, donde mi código está estructurado como en CIFAR:

  • Inferencia
  • Pérdida
  • Tren

Y lo estoy ejecutando en una forma de múltiples gpu (para entrenamiento).
Entonces tengo un script para entrenamiento (similar a cifar10_multigpu.py) y otro para pruebas (similar a cifar10_eval.py).
Entonces

for ii in xrange(2):  # Num of GPU
  with tf.device('/gpu:%d' % ii):
    with tf.name_scope('device_%d' % ii) as scope:

      data_batch, label_batch = factory.GetShuffleBatch(batch_size)

      unnormalized_logits = factory.MyModel(dataBatch=data_batch, numClasses=numClasses,
                                                 isTraining=True)

      More stuff happening
      tf.get_variable_scope().reuse_variables()

La inferencia ocurre con la función MyModel. (a continuación se muestra un ejemplo de la función, en realidad utilizo más capas y neuronas).

def MyModel(data_batch, num_classes, feature_dim):

  # Hidden Layer 1
  with tf.variable_scope('hidden1') as scope:
    weights = variable_on_cpu('weights',[feature_dim, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden1 = tf.nn.relu(tf.matmul(data_batch, weights) + biases, name=scope.name)

  # Hidden Layer 2
  with tf.variable_scope('hidden2') as scope:
    weights = variable_on_cpu('weights',[256, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases, name=scope.name)

  # output, unnormalized softmax
  with tf.variable_scope('softmax_unnorm') as scope:

    weights = variable_on_cpu('weights', [256, num_classes], tf.truncated_normal_initializer(stddev=1/num_classes))
    biases = variable_on_cpu('biases', [num_classes], tf.constant_initializer(0.0))
    softmax_un = tf.add(tf.matmul(hidden2, weights), biases, name=scope.name)

  return softmax_un

Quiero realizar una nomalización por lotes. Entonces, cuando lo hice:

def MyModel(data_batch, num_classes, feature_dim, isTraining):

  with tf.variable_scope('bnormalization') as scope:
    norm_data_batch = tcl.batch_norm(inputs=dataBatch, epsilon=0.0001, is_training=isTraining, 
                                      reuse=True, scope=scope)

  # Hidden Layer 1
  with tf.variable_scope('hidden1') as scope:
    weights = variable_on_cpu('weights',[feature_dim, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden1 = tf.nn.relu(tf.matmul(data_batch, weights) + biases, name=scope.name)

Recibí el siguiente error en la fase de entrenamiento:
La anormalización variable / beta no existe, no se permite. ¿Quería establecer reuse = None en VarScope?

Por lo que he estado leyendo en este hilo en la fase de entrenamiento, debería usar reuse = None. ¿Tengo esta parte correcta? Si esto es cierto, dado que estoy usando dos GPUS, ¿debería reutilizar = Ninguno en la primera GPU y reutilizar = Verdadero en la segunda? ¿O como estoy haciendo tf.get_variable_scope (). Reuse_variables () se encarga de sí mismo?

Finalmente, en la fase de prueba, ¿debería tener is_training = False y reuse = True?

Cualquier ayuda es muy apreciada.

Ahora tf.contrib.layers.batch_norm acepta un tensor, variable o marcador de posición como is_training

https://github.com/tensorflow/tensorflow/commit/9da5fc8e6425cabd61fc36f0dcc1823a093d5c1d#diff -94bbcef0ec8a5cdef55f705e99c2b2ed

¿Es normal que la normalización por lotes empeore mis experimentos? Lo probé en una red NN de 2 capas basada en el tutorial para principiantes de MNIST y constantemente obtengo peores resultados cuando BN está presente: con BN (uno con escala y centro entrenado y el otro no) la precisión es 0.8423, 0.8221 y sin BN la precisión 0.9477.

Mi script está presente aquí https://github.com/brando90/tensor_flow_experiments/blob/master/tf_tutorials/beginner_tutorial_MNIST_BN.py

¿Alguien ha experimentado estos problemas o BN es así y necesito hacer algo más para que funcione?

La última versión de tf.contrib.layers.batch_norm ahora acepta un marcador de posición para is_training, por lo que no es necesario que lo haga usted mismo.

Pero lo que es importante es que pase updates_collections = None para que Moving_mean y Moving_variance se actualicen en el lugar; de lo contrario, deberá recopilar update_ops y asegurarse de que se ejecuten.

Me gustaría animarte a usar tf.contrib.layers o tf.contrib.slim para construir tu modelo.

slim = tf.contrib.slim

def build_NN_two_hidden_layers(x, is_training):
 batch_norm_params = {'is_training': is_training, 'decay': 0.9, 'updates_collections': None}
 with slim.arg_scope([slim.fully_connected], 
    activation_fn=tf.nn.relu,
    weigths_initializer=tf.contrib.layers.xavier_initializer(),
    biases_initializer=tf.constant_initializer(0.1),
    normalizer_fn=slim.batch_norm,
    normalizer_params=batch_norm_params):
   net = slim.fully_connected(x, 50, scope='A1')
   net = slim.fully_connected(net, 49, scope='A2')
   y = slim.fully_connected(net, 10, activation_fn=tf.nn.softmax, normalizer_fn=None, scope='A3')
 return y


@sguada Cambié mi anterior donde le digo manualmente que entrene o no (basado en un tf.cond) y ahora parece que la precisión es de ~ 95 nuevamente. ¿Por qué necesitaba cambiar updates_collections a None? ¿Te importaría explicarme por qué eso dio una diferencia de precisión tan grande? Parece un cambio no trivial (¿Debería Ninguno ser su valor predeterminado si es tan importante?). ¡Gracias! :)

Además, noté que dijiste que era un marcador de posición y que no necesitaba hacerlo manualmente. Sin embargo, cuando pasé un marcador de posición para is_training, decía

TypeError: Using a tf.Tensor as a Python bool is not allowed. Use si t no es Ninguno: instead of si t: to test if a tensor is defined, and use the logical TensorFlow ops to test the value of a tensor.

y apuntó al código batch_norm. Quizás podría ser bueno mostrar cómo se debe usar este marcador de posición porque parece que no entiendo cómo se supone que debe usarse. ¡Gracias! :)

@ brando90
La parte relevante del código está aquí L227-256 .

Como notará, hay una declaración with ops.control_dependencies que fuerza las actualizaciones. Creo que para que el código se utilice "desde el primer momento", el valor predeterminado debería ser Ninguno.

En cuanto a mi comentario anterior a 1122 , descubrí que tf.get_variable_scope (). Reuse_variables () se encarga del problema, por lo que en la fase de entrenamiento el argumento reutilización de batch_norm debería ser Ninguno. Tiene que ver con la sentencia variable_op_scope (lee su documentación en tensorflow)

Uso de batch_norm con tf.placeholder

x = tf.placeholder(tf.float32, [None, 784])
is_training = tf.placeholder(tf.bool, [], name='is_training')
y = build_NN_two_hidden_layers(x, is_training)

# For training
sess.run(y, {is_training: True, x: train_data})

# For eval
sess.run(y, {is_training: False, x: eval_data})

El problema anterior era que no estaba actualizando moving_mean y moving_variance después de cada paso, cuando updates_collections es None, fuerza las actualizaciones como parte del cálculo.
Sin embargo, cuando una red tiene muchas capas de batch_norm, es más eficiente recopilar todas las operaciones de actualización y ejecutarlas juntas, por lo que cada capa no necesita esperar a que finalice la actualización.

y = build_model_with_batch_norm(x, is_training)
update_ops = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))

sess.run([y, update_ops])

¿Se ha avanzado en la aceleración de la norma de lotes?

Estaba tratando de usar la norma por lotes con un NN de 2 capas densamente conectado con el conjunto de datos (aplanar) MNIST (y unidades relu) para la tarea de codificación automática y sigo recibiendo un error de NaN. ¿Alguien sabe por qué podría ser esto? ¿Es esto posible alguna vez con BN? parece sospechoso, pero no podría ser mi configuración de aprendizaje, tasa, etc. (pero supongo que no debería porque BN debería estar un poco molesto con esto)

@sguada No entiendo la forma correcta de usar batch_norm especialmente con respecto a la bandera updates_collections . Si entendí correctamente si la bandera es None la red no es eficiente, entonces debería dejar updates_collections=tf.GraphKeys.UPDATE_OPS y luego debería recopilar todas las actualizaciones batch_norm y ejecutarlas juntas.

Usted recopila las actualizaciones de batch_norms haciendo: update_ops = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS)) .

Tengo muchos modelos diferentes que usan diferentes capas de batch_norm, esto no funcionaría, ¿verdad ?:

#model 1
y1 = build_model_with_batch_norm(x, is_training)
update_ops1 = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
sess.run([y1, update_ops1])
#model 2
y2 = build_model_with_batch_norm(x, is_training)
update_ops2 = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
sess.run([y2, update_ops2])

¿Podrías explicar esta parte con un poco más de detalles? Muchas gracias.

Solo póngalo en claves de colección separadas:

# While building your 1st model...
tf.contrib.layers.batch_norm(..., updates_collection="updates-model1")

# same for 2nd model with key "updates-model2"
#model 1
y1 = build_model_with_batch_norm(x, is_training)
update_ops1 = tf.group(tf.get_collection("updates-model1"))
sess.run([y1, update_ops1])
#model 2
y2 = build_model_with_batch_norm(x, is_training)
update_ops2 = tf.group(tf.get_collection("updates-model1"))
sess.run([y2, update_ops2])

Sin embargo, la documentación parece estar desactualizada. Le dice que haga lo siguiente:

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
if update_ops:
    updates = tf.group(update_ops)
    total_loss = control_flow_ops.with_dependencies([updates], total_loss)

Pero:

  • _tf.group () _ no acepta una lista. Lo reemplacé con _tf.tuple () _
  • No sé cómo acceder a _control_flow_ops.with_dependencies () _. He visto otros ejemplos usando tf.with_dependecies (), pero no puedo hacer eso con Tensorflow 0.10. Lo encontré aquí: _tf.python.control_flow_ops.with_dependencies () _

EDITAR:

La documentación debe actualizarse a s.th. Me gusta esto:

from tensorflow.python import control_flow_ops

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
if update_ops:
    updates = tf.tuple(update_ops)
    total_loss = control_flow_ops.with_dependencies(updates, total_loss)

EDITAR 2:

Después de hacer algunas ejecuciones en mi red, debo decir que no puedo ver ninguna diferencia de rendimiento entre el uso de _updates_collections = None_ en contraste con la búsqueda manual de _tf.GraphKeys.UPDATE_OPS_ durante la construcción del gráfico . Incluso con un uso intensivo de la normalización por lotes (en total, my _tf.get_collection (tf.GraphKeys.UPDATE_OPS) _ devuelve 140 Update-Ops, todos ellos son solo BN-ops)

Editar: Es difícil de decir, si mis resultados son correctos, pero toda la red parece ser 1.5 veces más rápida. Hasta donde yo sé, las estadísticas BN se calculan en la CPU, no en la GPU hasta ahora.

¿Alguien de ustedes también puede ver algún beneficio en el rendimiento? Comparte tus resultados :)

Volviendo al problema del rendimiento, ¿la capa de norma de lotes actual se beneficia en absoluto del uso de la GPU? ¿Alguien ha experimentado los beneficios de las GPU con esta implementación de la norma por lotes?

Perdón por el spam, pero la documentación realmente no explica cómo usar este BN con convolución (¿tal vez debería proporcionarse en alguna parte?). En resumen, ¿cómo se da cuenta de que debe aplicar y aprender los mismos parámetros por función (en lugar de por activación)?

(¿Hay al menos un fragmento de código para hacer esto?)

La envoltura delgada batch_norm se normaliza sobre la última dimensión de su tensor de entrada. Entonces, si es un tensor de entrada 2D que proviene de una capa completamente conectada, se normaliza por lotes y, por lo tanto, realiza una normalización por activación. Si es un tensor 4D que proviene de una convolución, se normalizará sobre las tres primeras dimensiones (lote, ancho, profundidad) y, por lo tanto, realizará la normalización por característica. @sguada tal vez sea un poco más descriptivo sobre esto.

@nmhkahn Con respecto a su fragmento de código, ¿puedo preguntar por qué reuse establece en None cuando is_training=True ? ¿No desencadenaría eso el parámetro de escala gamma y el parámetro de compensación beta se reinicializarían en cada paso de entrenamiento? Pensé que en el documento original, beta y gamma se "aprenden junto con los parámetros del modelo original". Para hacer eso, ¿no deberían inicializarse solo una vez y luego reutilizarse en todos los pasos de entrenamiento?

tf.cond(is_training, lambda: batch_norm(inputT, is_training=True, updates_collections=None, scope=scope), lambda: batch_norm(inputT, is_training=False, updates_collections=None, scope=scope, reuse = True))

Agradezco enormemente el trabajo que el equipo de TF ha realizado aquí para hacer que batch_norm esté disponible y sea efectivo. Desde mi búsqueda, este hilo es el mejor recurso sobre cómo usarlo. Hay muchos problemas e ideas diferentes que vuelan por aquí, y es difícil averiguar el consejo de consenso para el caso estándar más simple de cómo usar la capa batch_norm. Creo que sería muy útil ampliar la documentación para especificar el uso recomendado exacto.

Mi mejor intento de averiguarlo me llevó al siguiente código:

is_training_ph = tf.placeholder(tf.bool)
...
with tf.variable_scope('bn_test_layer') as vs:
    layer_output = tf.cond(is_training_ph,
        lambda: tf.contrib.layers.batch_norm(layer_input, is_training=True, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope=vs),
        lambda: tf.contrib.layers.batch_norm(layer_input, is_training=False, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope=vs, reuse=True))

Luego configuro is_training_ph en True para el entrenamiento y False para las pruebas. Esto no me funciona. El modelo se entrena bien, pero el rendimiento de la prueba es terrible. Por el contrario, si mantengo is_training_ph = True durante el tiempo de prueba, funciona muy bien. Por lo tanto, supongo que todavía tengo un problema de alcance, por lo que no encuentra las variables existentes adecuadas.

@ davek44 Estoy usando el mismo marco de código que estás usando y observé lo mismo: cuando enciende is_training=True durante la fase de entrenamiento y apaga is_training=False para la fase de validación y / o prueba, el modelo se entrena bien como el documento descrito (el modelo converge más rápido y pude usar una tasa de aprendizaje mayor), sin embargo, el rendimiento de las pruebas es terrible. Si enciendo is_training=True todo el tiempo, el modelo se entrena igual que sin insertar la capa de norma por lotes. No he descubierto qué hice mal, planeo usar TensorBoard para monitorear los parámetros. ¿Podría actualizar si diagnostica la causa de este comportamiento?

tf.contrib.layers.batch_norm puede tomar tensor como is_training, por lo que no es necesario hacer nada especial.

is_training_ph = tf.placeholder(tf.bool)

outputs = tf.contrib.layers.batch_norm(layer_input, is_training=is_training_ph, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope='batch_norm'),

Veo el mismo rendimiento de prueba deficiente con ese código.

Sin más detalles es imposible saberlo, mis conjeturas son que solo entrenas para unas pocas iteraciones, por lo que el Moving_mean y el Moving_average aún no han convergido.

Puede cambiar el tamaño de lote durante la prueba para ver cómo se degrada el rendimiento a medida que reduce el tamaño del lote.

Veo el mismo rendimiento de prueba deficiente con ese código.

Tuve exactamente el mismo problema con tf.slim batchnorm o con tf.cond y la entrada is_training como marcador de posición.
En el primer caso, al investigar el modelo entrenado, descubrí que la media móvil y la varianza móvil consisten en ceros.
En el último caso, la media móvil y la varianza parecen más razonables (con valores diferentes), pero si utilizo is_training = False en el tiempo de prueba, el rendimiento también es realmente malo. Usando is_training = True, funciona mejor, pero creo que solo usa la media móvil y la varianza dentro del lote de prueba.

@nmduc @ davek44 Escribí un código para rastrear la media móvil y la varianza móvil calculada en tf.contrib.layers.batch_norm durante el entrenamiento y las pruebas. Descubrí que el valor de decay importa mucho (usan el decaimiento exponencial para calcular el promedio móvil y la varianza móvil), con una configuración de decay más cercana a 1.0 (es decir, decay=.999 ), la media móvil cae a un valor más cercano a 0. Hice 2 pruebas con el mismo código exacto pero diferentes configuraciones decay en tf.contrib.layers.batch_norm , y mi validación / precisión de prueba parecía más razonable.

Los resultados de la ejecución de prueba con decay=0.9
screen shot 2016-11-16 at 1 51 51 pm

Los resultados de la ejecución de prueba con decay=0.999 ( decay=0.999 es la configuración predeterminada en tf.contrib.layers.batch_norm )
screen shot 2016-11-16 at 2 03 58 pm

(también parece que un valor de decaimiento más grande requeriría que el modelo se entrenara más tiempo para ver un cambio en la precisión de la validación)

Sí, eso lo solucionó. ¡Gracias por compartir su análisis @zhongyuk!

Animo a los desarrolladores a considerar hacer decay = 0.9 como predeterminado. Incluso 0,99 no me funciona bien. Ese también es el valor predeterminado en la implementación de Torch; ver el parámetro de impulso en https://github.com/torch/nn/blob/master/BatchNormalization.lua

@zhongyuk Muchas gracias por compartir. Funciona para mi ahora.

Esto parece importante. @sguada deberíamos considerar el curso de acción correcto aquí antes de 1.0. A corto plazo, ¿una de las partes interesadas puede enviarme un PR que documente el hecho de que decay podría tener que reducirse significativamente cuando experimente un rendimiento de evaluación deficiente? Estoy bastante seguro de que nunca he tenido que modificar ese parámetro, pero podría ser un efecto secundario de la configuración distribuida.

Podríamos cambiar el valor predeterminado a 0.9 o documentar mejor su impacto en conjuntos de datos más pequeños o pocas actualizaciones.
@vincentvanhoucke en nuestra configuración distribuida generalmente hacemos millones de actualizaciones, así que está bien, sin embargo, en otros casos como el de aquí, que hace solo unos pocos cientos de actualizaciones, hace una gran diferencia:
Por ejemplo, el uso de decaimiento = 0.999 tiene un sesgo de 0.36 después de 1000 actualizaciones, pero ese sesgo baja a 0.000045 después de 10000 actualizaciones y a 0.0 después de 50000 actualizaciones.

Solo quería señalar que también tengo el problema del rendimiento deficiente de la prueba, específicamente al usar lotes pequeños (cualquier cosa menor a 10 en lugar de los 200 que usé para el entrenamiento disminuye la precisión de la prueba). He usado un tf.placeholder para cambiar entre el modo de prueba / entrenamiento.

Es genial que esta capa de normalización por lotes funcione para una mejor convergencia de entrenamiento, pero si no puede aplicar el modelo en producción, no tiene mucho sentido usarlo. ¿Alguien puede confirmar un buen rendimiento de la prueba con muestras de datos pequeñas o individuales utilizando esta capa de norma por lotes?

Puedo confirmar que el rendimiento de la prueba es bueno cuando se usa is_training = False con lotes pequeños e incluso con batch_size = 1, ya que no se usa la estadística del lote, sino la estadística aprendida durante el entrenamiento. Solo necesita asegurarse de que las estadísticas hayan convergido con el decaimiento predeterminado = 0.999, lo que implica al menos 50k actualizaciones.

Para continuar con la confirmación del desarrollador de TF, realizo un seguimiento de la convergencia de las estadísticas con dos configuraciones decay (y entrenamiento batch_size = 1). Con decay=0.99 , las estadísticas convergen (sesgo <0,001) después de 550 ~ 600 pasos de aprendizaje / actualizaciones. Con decay=0.9 , las estadísticas convergen (biase <0.001) dentro de los 100 pasos de aprendizaje / actualizaciones.

@sguada gracias, ¿eso también significa que la salida es realmente independiente del tamaño del lote? porque estoy notando cambios muy leves con un gran impacto en mi precisión (tal vez mi definición de rendimiento se vea más fácilmente afectada por este pequeño cambio). Para ser precisos, todos los valores en mi tensor de salida de 128 dimensiones aumentan de tal manera que la longitud total del vector escala casi linealmente con el tamaño del lote. Por valor, esto no es una gran diferencia, pero tiene un gran impacto cuando se calculan distancias vectoriales en espacios latentes.

@zhongyuk gracias, he ejecutado alrededor de 5000 actualizaciones con decay=0.9 , por lo que debería haber convergido y probar el rendimiento con lotes grandes está bien. Pero incluso si no fuera así, ¿habría una diferencia entre entrenar una prueba? Vería un mal rendimiento durante el entrenamiento y las pruebas si no hubiera convergido, ¿verdad?

Investigaré un poco más y veré si puedo reproducir el problema en otra tarea. ¡Gracias por la retroalimentación rápida hasta ahora!

@dominikandreas Si su bajo rendimiento en las pruebas se debe a que las estadísticas no convergen, verá un rendimiento de entrenamiento razonablemente bueno pero un rendimiento de prueba deficiente. Porque durante el entrenamiento, la normalización por lotes se realiza utilizando únicamente las estadísticas del lote de entrenamiento. Sin embargo, durante el tiempo de prueba, utiliza las estadísticas de promedio móvil de todos los lotes de entrenamiento para normalizar el tensor de entrada.

Encontré un error en mi código, la normalización por lotes está funcionando bien ahora :-) gracias por su apoyo

Hola @zhongyuk , ¿cómo hiciste un seguimiento de la media móvil y la varianza?
¡Gracias!

@rogertrullo Generalmente configuro TensorBoard para rastrear la media móvil y la varianza. Aparte de eso, también intenté obtener estadísticas a través de tf.get_variable("moving_mean") dentro del alcance durante el entrenamiento y la referencia para monitorear el sesgo.

Hola,
Tengo el mismo problema que otros descritos, tengo buenos resultados de entrenamiento, pero la validación / prueba es mala después de usar batch_norm.
Yo uso la función así:
conv_normed1 = tf.contrib.layers.batch_norm (conv1 + block1_layer3_1_biases, updates_collections = None, scale = True, decay = batch_norm_decay, center = True, is_training = is_training)
el valor de decaimiento es 0.9
¿Necesito configurar la bandera de reutilización?
Estaré encantado de recibir ayuda.

He estado usando batch_norm como se describe en este hilo (con un tf.bool para entrenamiento; y ops.GraphKeys.UPDATE_OPS) y todo funciona.

Al guardar y restaurar usando:
saver = tf.train.Saver ()
funciona,

pero al guardar usando:
saver = tf.train.Saver (tf.trainable_variables () + [global_step])
para que pueda ahorrar espacio de almacenamiento (al no guardar los degradados, etc.)
en la restauración hay un error:
"valor no inicializado unpool4 / convc / bn / moving_mean"

Obviamente, esto se debe a que moving_mean (y supongo que moving_variance) no se ha guardado para ninguna de las capas. Como tengo muchos de ellos (anidados en muchas capas), ¿cuál es la forma más eficiente de agregarlos a la lista de valores que se guardarán? Además, dado que estas son variables entrenables, ¿por qué no se agregan a la colección trainable_variables?

La media móvil y la varianza de
Para guardarlos / restaurarlos, puede usar tf.global_variables ()

Para mí, las cosas comenzaron a funcionar cuando usé este contenedor:
def batch_norm_wrapper(x, phase, decay, scope, reuse): with tf.variable_scope(scope, reuse=reuse): normed = tf.contrib.layers.batch_norm(x, center=True, scale=True, decay=decay, is_training=phase, scope='bn',updates_collections=None, reuse=reuse) return normed
todo el uso de alcances y la reutilización no está claro en este hilo para mi opinión.

Muchas gracias. Con tf.global_variables () los archivos guardados son mucho más grandes, ya que creo que incluye los degradados; al final usé:

saver = tf.train.Saver ([x para x en tf.global_variables () si 'Adam' no está en x.name])

y porque el administrador de sesiones init no los inicializa correctamente:

sess.run (tf.variables_initializer ([x para x en tf.global_variables () si 'Adam' en x.name]))

(Usando tf.train.AdamOptimizer)

También puede usar tf.model_variables () que contiene las variables del modelo, es decir, moving_mean

@sguada Perdón por molestarlo, pero ¿es posible hacer un ejemplo sobre cómo usar slim.batch_norm cuando se combina con slim.conv2d / slim.fully_connect en readme.md?

Estoy usando slim.batch_norm, pero obtengo un buen rendimiento de entrenamiento y un rendimiento deficiente de validación / prueba. Creo que debe deberse a un uso inadecuado de reuse o scope o algunos otros parámetros. Aunque hay muchos problemas en la normalización de lotes, es difícil encontrar un fragmento de código completo sobre cómo usarlo, especialmente. para saber cómo pasar diferentes parámetros en diferentes fases.

Digamos, en mi código mnist_bn , tf.GraphKeys.UPDATE_OPS y configuré is_training como marcador de posición. Pero el rendimiento de la validación sigue siendo deficiente si proporciono {is_training: False}.

Le agradecería mucho si hubiera un ejemplo de normalización por lotes oficial y completo (lo que significa que la capacitación, la validación y las pruebas están incluidas).

¡Gracias de antemano!

Hola,
debe establecer un alcance diferente para cada vez que use la norma por lotes y darle la entrada de reutilización de acuerdo con la fase de entrenamiento / prueba (VERDADERO cuando se prueba FALSO cuando se entrena) que funciona para mí.

@ishaybee Gracias por tu ayuda. Encontré mi problema = = Se debe al inicio en frío de moving_mean / moving_variance.

Como no he entrenado suficientes pasos, la media / varianza móvil estimada no es tan estable. El resultado resulta ser: el modelo funciona bastante bien en el entrenamiento de mini lotes (al principio, la pérdida disminuye rápidamente), pero el rendimiento de validación es errático (porque la media / varianza estimada de la población no es lo suficientemente estable).

Cuando entrené el modelo por más tiempo, la precisión de la validación también se vuelve más bonita.

Otra cosa importante es asegurarse de usar slim.learning.create_train_op para crear la operación de tren . No utilice tf native tf.train.GradientDescentOptimizer(0.1).minimize(loss) .

Entonces, la respuesta es, estoy usando la normalización por lotes correctamente, pero no he entendido completamente su dinámica durante el entrenamiento.

================
Y lo que es más:

  1. Aquí hay un ejemplo completo sobre cómo usar la capa BN en el conjunto de datos MNIST.
  2. Usar un valor de caída más pequeño acelerará la fase de calentamiento. El decaimiento predeterminado es 0.999, para pequeños conjuntos de datos como MNIST, puede elegir 0.99 o 0.95, y se calienta en poco tiempo.

@soloice , observe, cómo en el comentario se pasa el siguiente parámetro dentro de la capa para llamar a batch_norm:

batch_norm_params = {'is_training': is_training, 'decay': 0.9, 'updates_collections': None}

Sin updates_collections establecido en Ninguno (por lo que las actualizaciones medias se realizan en su lugar dentro de BatchNorm), no esperaré que la capa circundante (por ejemplo, conv2d) ejecute tf.GraphKeys.UPDATE_OPS necesario para que la capa BatchNorm actualice la media en ejecución y por lo tanto, podrá ejecutar los datos de prueba más tarde.

O puede intentar ejecutar UPDATE_OPS usted mismo explícitamente como uno aquí

    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    if update_ops:
        updates = tf.group(*update_ops)
        cross_entropy = control_flow_ops.with_dependencies([updates], cross_entropy)

Actualización: descubrí que cité exactamente su código y usted usa UPDATE_OPS.

En cuanto al "arranque en frío", como se ve arriba en la discusión, la disminución de la caída promedio en ejecución de BatchNorm (parámetro de entrada) de 0.999 por defecto a algo como 0.95 puede acelerar el inicio

@pavelbulanov ¡ Es muy amable de su parte ayudarme con esto! Probaré con un valor menor de decay para ver cómo ayuda esto.

================
Actualización: usar un pequeño decaimiento (digamos, 0.9 o 0.95) ayuda mucho. La pérdida de validación disminuye muy rápidamente cuando establezco decay en 0.9. Sin embargo, el inconveniente de la desintegración pequeña es que su rango efectivo es pequeño: el resultado está dominado por algunas muestras recientes, por lo que no es una buena estimación de la media / varianza de la población. Es necesario equilibrar entre el inicio rápido (decaimiento pequeño) y un rango efectivo más largo (decaimiento grande).

Hola,
Traté de implementar una capa de normalización por lotes con la ayuda de las sugerencias en este problema, pero todavía tengo un error> 70% en la validación y las pruebas ... Tengo una disminución más baja para las llamadas que no son de entrenamiento ...

Aquí está mi código:

def BatchNorm(inputT, is_training=False, scope=None):
  return tf.cond(
    is_training,
    lambda: tf.contrib.layers.batch_norm(inputT, is_training=True,  reuse=None, decay=0.999, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope),
    lambda: tf.contrib.layers.batch_norm(inputT, is_training=False, reuse=True, decay=0.900, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope)
    )

Gracias de antemano.

@Alexivia ¿ Parece que está utilizando dos capas de normalización por lotes diferentes? Debe usar solo una capa BN (por supuesto, con is_training parámetro

Gracias por tu consejo @soloice.
Lo intenté ahora con diferentes parámetros is_training y reuse :

lambda: tf.contrib.layers.batch_norm(inputT, is_training=True,  reuse=None, decay=0.9, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope),
lambda: tf.contrib.layers.batch_norm(inputT, is_training=False, reuse=True, decay=0.9, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope)

todavía no obtengo buenos resultados de validación y prueba ...> 70% ...

Hola,
por favor vea mi envoltorio arriba.
debería usar "con tf.variable_scope (alcance, reutilización = reutilización):" Creo.

Hola @ishaybee ,
Seguí tu consejo, ahora mi código es:

def BatchNorm(inputT, is_training=False, reuse=True, scope=None):
  with tf.variable_scope(scope, reuse=reuse):
    return tf.contrib.layers.batch_norm(inputT, is_training=is_training, reuse=reuse, scope=scope, updates_collections=None, decay=0.9, center=True, scale=True)

y alimento is_training y reuse través del feed_dict, pero ahora obtengo el error ValueError("The reuse parameter must be True or False or None.")

intente alimentar la reutilización como una variable de Python (entrada del modelo) y como marcador de posición.

Intenté eso, y ahora dejó de quejarse del valor ... pero creo que el valor del marcador de posición no se está usando, porque no veo ningún cambio si fuerzo los valores a la función batch_norm , y en TensorBoard no lo es conectado al gráfico ... (ver imagen adjunta)
screen shot 2017-04-03 at 19 54 54

Mi código es así ahora:
Envoltorio de normalización por lotes

def BatchNorm(inputT, is_training=False, reuse=None, scope=None):
  with tf.variable_scope(scope):
    return tf.contrib.layers.batch_norm(inputT, is_training=is_training, reuse=reuse, scope=scope, updates_collections=None, decay=0.9, center=True, scale=True)

Definición de modelo

def model(data, train=False, is_training=False, reuse=None):
  # 1st conv layer
  with tf.name_scope('conv1') as scope:
    conv = tf.nn.conv2d(
    <...>
    norm = BatchNorm(pool, is_training=is_training, reuse=reuse, scope=scope)

Formación

feed_dict = {train_data_node: batch_data,
      train_labels_node: batch_labels,
      is_training: True,
      reuse: None}
  # Run the optimizer to update weights.
  sess.run(optimizer, feed_dict=feed_dict)

Validación

batch_predictions = sess.run(eval_prediction, feed_dict={eval_data: data[-EVAL_BATCH_SIZE:, ...], is_training: False, reuse: True})

Aunque is_traning can, la reutilización de un marcador de posición tiene que ser un bool y no puede ser un tensor ni un marcador de posición.

No estoy seguro de qué está tratando de hacer, en la mayoría de los casos, el uso de valores estáticos resuelve el problema. Por ejemplo, este patrón funciona bien:

def model(data, is_training=False, reuse=None, scope='my_model'):
  # Define a variable scope to contain all the variables of your model
  with tf.variable_scope(scope, 'model', data, reuse=reuse):
    # 1 layer
    net = tf.contrib.layers.conv2d(data, ....)
    ....
    net = tf.contrib.layers.batch_norm(net, is_training)
   return net

train_outputs = model(train_data, is_training=True)
eval_outputs = model(eval_data, is_training=False, reuse=True)

eval_predictions = sess.run(eval_outputs, feed_dict={eval_data: data[-EVAL_BATCH_SIZE:, ...]})

A menos que necesite cambiar el comportamiento del modelo de forma dinámica, no necesita usar un marcador de posición para is_training. El truco consiste en construir el modelo dos veces, pero compartiendo las variables la segunda vez.

¡Gracias @sguada ! Después de aplicar tus sugerencias, ¡finalmente lo hice funcionar!

Sería útil si la documentación de API 1.0 reflejara que necesita agregar manualmente operaciones de actualización al gráfico. Al ser un usuario de tf más nuevo, descubrí que mi error de prueba era una locura y luego tuve que pasar una buena cantidad de tiempo depurando mi gráfico hasta que me di cuenta de que la normalización por lotes era el problema. Luego tuve que dedicar más tiempo a descubrir que, de forma predeterminada, las variables que rastrean los momentos no se actualizan a menos que use una función contrib para la optimización. Dado que en 1.0 no hay una opción para establecer update_collections en None, no hay ningún indicador en la documentación de que esto pueda ser un problema. Además, parece que podría tener sentido tener un parámetro para agregar las dependencias del flujo de control a la operación que se ejecuta en el caso de entrenamiento.

@danrsc Exactamente. El uso de la capa BN es bastante confuso. Sugerí agregar documentos o un tutorial oficial completo sobre la normalización por lotes, pero desafortunadamente no obtuve respuesta = =

Completamente de acuerdo. Creo que el uso de BN es muy complicado y la documentación actualmente es más que inadecuada. Esto debería corregirse para una capa de uso tan común.

Reapertura para visibilidad de los problemas de documentación.

@sguada asignándote para triaging. Podría valer la pena contratar a un redactor técnico sobre el caso.

Este problema me confundió la semana pasada y desperdicié 3 días de capacitación ... Espero que los documentos se puedan solucionar pronto y que se pueda agregar un ejemplo oficial de normalización por lotes en los documentos de la API.

@sguada He notado que dijiste "tf.contrib.layers.batch_norm puede tomar tensor como is_training, así que no necesitas hacer nada especial".
Sin embargo, el comentario en el código es
Si is_training no tiene un valor constante, porque es un Tensor ,
# a Variable o Placeholder entonces is_training_value será None y
# needs_moments será cierto.
¿Significa que nees_moments será verdadero incluso en la fase de prueba si configuro is_training como marcador de posición?
Hasta donde yo sé, los momentos no son necesarios durante las pruebas.

Entonces, si is_training es Variable o Placeholder , significa que puede cambiar, por lo que se necesita el gráfico para calcular los momentos, por lo que la capa lo construye.
Luego, en tiempo de ejecución, dependiendo de que el valor sea True o False usaría el lote moments o moving_mean y moving_variance .

Por lo tanto, durante la prueba, establecería el valor en False y no se usará moments .

@sguada @ brando90

def batch_norm_layer(self, x,train_phase, scope_bn):
        bn_train = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=True,
        reuse=None,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        bn_inference = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=False,
        reuse=True,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
        return z

Construyo batchnorm como este, sin embargo, la media móvil y la variable móvil se actualizan durante la prueba, no puedo encontrar la razón.

Intenté crear dos modelos como dijo @sguada , sin embargo, mi modelo donde is_training = False simplemente falla.

W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_5/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_6/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_7/biases not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_6/biases not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_7/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key history_embeddings_1 not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key global_step_1 not found in checkpoint

Siento que tal vez debería haber un ejemplo concreto de cómo hacer una norma por lotes con una red completamente conectada, así como con CNN. Apesta que haya entrenado modelos durante días esperando que las cosas funcionen antes de ver que todos los que intentan usar esta función se vuelven locos.

Curiosamente, también se necesitan trillones de años para restaurar el modelo después de entrenar con batch_norm. Lo más probable es que espere hasta TF 2.0 para volver a intentar algo como esto.

@MisayaZ no necesita crear dos capas batch_norm, simplemente puede pasar train_phase (asumiendo que es un tf.bool) a batch_norm. También está pasando UPDATE_OPS_COLLECTION variables_collections, que cambia las colecciones a las que se agregan las variables.

Lo siguiente debería funcionar:

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                             is_training=train_phase, scope=scope_bn)

@OktayGardener no está seguro de qué modelo está tratando de crear, parece que las variables no se guardan en su punto de control.

batch_norm también funciona con capas totalmente conectadas.

slim = tf.contrib.slim
def model(data, is_training=False, reuse=None, scope='my_model'):
  # Define a variable scope to contain all the variables of your model
  with tf.variable_scope(scope, 'model', data, reuse=reuse):
    # Configure arguments of fully_connected layers
    with slim.arg_scope([slim.fully_connected],
                        activation_fn=tf.nn.relu,
                        normalizer_fn=slim.batch_nom):
      # Configure arguments of batch_norm layers
      with slim.arg_scope([slim.batch_norm],
                          decay=0.9,  # Adjust decay to the number of iterations
                          update_collections=None, # Make sure updates happen automatically
                          is_training=is_training, # Switch behavior from training to non-training):
        net = slim.fully_connected(data, 100, scope='fc1')
        net = slim.fully_connected(net, 200, scope='fc2')
        ....
        # Don't use activation_fn nor batch_norm in the last layer        
        net = slim.fully_connected(net, 10, activation_fn=None, normalizer_fn=None, scope='fc10')
       return net

@sguada Gracias, construyo una red con bathnorm que se implementa como mencionaste anteriormente

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                             is_training=train_phase, scope=scope_bn)

la velocidad es lenta, utilizo el punto de referencia de tensorflow para obtener el tiempo de cálculo de la siguiente manera:
I tensorflow / core / util / stat_summarizer.cc: 392] ============================== Top por tiempo de cálculo === ===========================
I tensorflow / core / util / stat_summarizer.cc: 392] [tipo de nodo] [inicio] [primero] [promedio de ms] [%] [cdf%] [mem KB] [Nombre]
I tensorflow / core / util / stat_summarizer.cc: 392] Conv2D 106.164 51.354 51.004 23.145% 23.145% 692.224 conv8 / Conv2D
I tensorflow / core / util / stat_summarizer.cc: 392] Conv2D 85.187 19.115 19.283 8.750% 31.896% 692.224 conv7 / Conv2D
I tensorflow / core / util / stat_summarizer.cc: 392] SquaredDifference 11.967 15.105 14.331 6.503% 38.399% 11075.584 conv1 / batch_norm / momentos / suficientes_estadísticas / SquaredDifference
I tensorflow / core / util / stat_summarizer.cc: 392] Mul 11.970 14.162 13.495 6.124% 44.523% 11075.584 conv1 / batch_norm / batchnorm / mul_1
I tensorflow / core / util / stat_summarizer.cc: 392] Conv2D 3.948 8.170 7.986 3.624% 48.146% 11075.584 conv1 / Conv2D
I tensorflow / core / util / stat_summarizer.cc: 392] Sub 11.960 10.176 7.943 3.604% 51.751% 11075.584 conv1 / batch_norm / momentos / suficientes_estadísticas /
I tensorflow / core / util / stat_summarizer.cc: 392] SquaredDifference 45.570 5.908 7.177 3.257% 55.007% 5537.792 conv2 / batch_norm / momentos / suficientes_estadísticas / SquaredDifference
I tensorflow / core / util / stat_summarizer.cc: 392] Mul 45.574 7.755 6.902 3.132% 58.140% 5537.792 conv2 / batch_norm / batchnorm / mul_1
I tensorflow / core / util / stat_summarizer.cc: 392] Conv2D 40.692 5.408 4.845 2.199% 60.338% 5537.792 conv2 / Conv2D
I tensorflow / core / util / stat_summarizer.cc: 392] Sub 45.563 6.067 4.784 2.171% 62.509% 5537.792 con

No entiendo por qué se ejecutan algunas operaciones en el momento durante la prueba y cuestan mucho tiempo, como conv1 / batch_norm / momentos / suficientes_estadísticas / SquaredDifference.

El momento no es necesario en la prueba, ¿por qué se ejecutan algunas operaciones en el momento?

Hola,

Usando la capa batch_norm en contrib.layers , obtengo nan como salida para el gráfico de validación mientras el gráfico del tren se ejecuta sin problemas. ¿Hay algo que me pueda perder?

Estoy usando:

def batchnormlayer(inputs, numout, train_model):
    with tf.variable_scope("batch_norm") as scope_bn:
        epsilon = 1e-3
        return tf.contrib.layers.batch_norm(inputs, decay=0.9, updates_collections=None,
                                            scale=True, scope=scope_bn,
                                            is_training=train_model, epsilon=epsilon,
                                            fused=True, reuse=scope_bn.reuse)

Gracias

Como seguimiento, estoy reutilizando 16 capas de batch_norm.
Sin embargo, descubrí que la reutilización de 4 capas funciona.

Me acabo de dar cuenta de que si mato el proceso de flujo tensorial y lo reinicio, mi error empeora durante algunas épocas (es decir, peor de lo que debería ser en el último punto de control). También observo que si elimino batch_norm, este problema desaparece. Después de mirar el código por un tiempo, creo que esto puede deberse a que los valores de las variables no se restauran a partir de las variables de sombra como lo serían si se usara la clase ExponentialMovingAverages para administrar las medias móviles. Esto también significa que si uso un proceso separado para evaluar, obtengo el último valor de la variable y no el promedio móvil. ¿Estoy interpretando esto correctamente y es este el comportamiento previsto? Parece que desea restaurar los valores de la variable de sombra ...

Capté el problema, la variación móvil en mi caso se vuelve negativa después de algunas iteraciones.

La salida del tensor: Model/clip_logits/batch_norm/moving_variance:0 presente en tf.model_variables() es

Moving variance (shape = (101,)) = 
[ 214.70379639   95.36338043    0.57885742  189.49542236  102.72473145
  137.14886475  286.57333374  111.06427002  154.98750305  167.75219727
  207.83955383  211.14007568  158.23495483  171.61665344  116.81361389
  115.77380371   43.59399796  137.75064087  181.75245667  161.37339783
  215.21934509   92.88521576  191.23846436  336.3946228   259.85919189
  299.47039795  186.23222351  165.19311523  262.82446289  170.11567688
  233.56843567  209.35050964  115.96807861  154.34109497  295.5770874
  123.6055603   295.76187134  296.88583374  240.88217163  247.32983398
   87.15661621  217.69897461  133.00698853   -4.80375671  344.77462769
  291.50601196  117.77174377  265.83712769  207.90093994  194.186203
  220.21418762  178.03738403  115.27571869  196.62184143  228.8089447
  191.53205872  331.36807251  151.55435181  197.2951355   179.67504883
  181.09727478   90.09922791  173.30133057  102.6836853   160.9434967
  236.59512329  168.05305481  403.36340332   41.14326096  185.93409729
  130.57434082  266.31509399  101.44387817  163.88059998  290.25015259
  244.52597046  229.86647034  158.14352417  202.68774414  187.78227234
  248.78218079  126.0978241   171.41891479  274.40740967  119.84254456
  202.53045654  200.20608521  214.04730225  111.53284454  222.03184509
  244.81187439  172.23052979  187.09806824  194.62802124  255.26345825
  293.63598633  307.91036987  210.86982727  308.88919067  144.94792175
  229.69013977]

Como puede ver, hay una variación negativa para una de las dimensiones. Como es esto posible ?
PS La capa de norma por lotes se utiliza justo después de la última capa completamente conectada de la red y antes de softmax.

@ raghavgoyal14 ¿lo estás usando con fusionado = Verdadero? Tuve un problema similar y desapareció cuando usé la versión fusionada

@abred : Sí, usé fused=True , mismo problema.

@sguada Hola, sguada, tengo un problema.
La definición de contrib.layers.batch_norm en tensorflow:
def batch_norm (entradas,
decaimiento = 0.999,
centro = Verdadero,
scale = False,
épsilon = 0,001,
activacion_fn = Ninguno,
param_initializers = Ninguno,
param_regularizers = Ninguno,
updates_collections = ops.GraphKeys.UPDATE_OPS,
is_training = True,
reutilizar = Ninguno,
variables_collections = Ninguno,
Outputs_collections = Ninguno,
entrenable = Verdadero,
batch_weights = Ninguno,
fusionado = Falso,
formato_datos = DATA_FORMAT_NHWC,
zero_debias_moving_mean = Falso,
alcance = Ninguno,
renorm = Falso,
renorm_clipping = Ninguno,
renorm_decay = 0,99):
escala: si es Verdadero, multiplique por gamma. Si es falso, gamma es
no utilizado. Cuando la siguiente capa es lineal (también, por ejemplo, nn.relu), esto puede ser
deshabilitado ya que la escala se puede realizar en la siguiente capa.

Si utilizo tf.contrib.layers.batch_norm (input, scale = False), "scale = False" significa si la gamma es cero en "y = gamma * x + beta" durante el entrenamiento. Muchas gracias.

Cuando scale = False, gamma es una constante 1.

@ppwwyyxx Muchas gracias por tu ayuda. Utilizo tf.contrib.layers.batch_norm (input, scale = False) en Tensorflow, y ahora estoy convirtiendo el batchnorm de Tensorflow en Caffe. ¿Cómo configurar el parámetro de BatchNormLayer y ScaleLayer en Caffe?
Muchas gracias.

@MisayaZ Estaba teniendo el mismo comportamiento al usar Batchnorm con un marcador de posición para "is_training". Veo en la traza que los momentos se están calculando incluso en el tiempo de prueba, así que decidí ir al código fuente y encontré esto:

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `needs_moments` will be true.
    is_training_value = utils.constant_value(is_training)
    need_moments = is_training_value is None or is_training_value
    if need_moments:
        # here it defines the moments

Parece que cuando "is_training" es una variable o un marcador de posición, los momentos se definen y también se calculan en tiempo de ejecución, incluso cuando estableces el marcador de posición en "Falso". Hubiera preferido dejarlo como marcador de posición porque así puedo hacer pruebas periódicas durante el entrenamiento sin redefinir el gráfico, pero decidí usarlo como una constante y definir diferentes comportamientos para tren vs prueba, y ahora no se calculan los momentos en el momento de la prueba.

@ tano297 Gracias. Ahora también uso 'is_training' como constante. Déjelo como marcador de posición y haga pruebas periódicas que cambiarán el valor de la media móvil y la varianza móvil. Y el tiempo de inferencia será más largo porque calculará la media y la varianza de las entradas y actualizará la media móvil y la varianza móvil. La forma correcta de realizar pruebas es definir diferentes comportamientos para entrenar y probar, como mencionaste.

@ tano297 @MisayaZ
pero no el "smart_cond" en

is_training_value = utils.constant_value(is_training)
need_updates = is_training_value is None or is_training_value
if need_updates:
  ...
  outputs = utils.smart_cond(is_training, _force_updates, no_updates)

asegúrese de que las actualizaciones solo se calculen y apliquen si is_training se evalúa como True?

@abred Sí, de hecho, pero te refieres a la línea 391, donde hace la actualización de la media móvil dentro de _fused_batch_norm ():

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `need_updates` will be true.
    is_training_value = utils.constant_value(is_training)
    need_updates = is_training_value is None or is_training_value
    if need_updates:
        ...
        outputs = utils.smart_cond(is_training, _force_updates, no_updates)
        ...

Estoy hablando de la línea 753 dentro de batch_norm ():

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `needs_moments` will be true.
    is_training_value = utils.constant_value(is_training)
    need_moments = is_training_value is None or is_training_value
    if need_moments:
        ...
        mean, variance = utils.smart_cond(is_training,
                                          _force_updates,
                                          moving_vars_fn) 
        ...

La condición inteligente en ese caso (en lo que a mí respecta) decide si actualizar o no los promedios móviles, pero los momentos aún se calculan.

@ tano297 tienes razón en eso, estaba en el lugar equivocado, pero aún así:
la línea 755-770 calcula los momentos, pero los momentos solo se usan en _force_updates que solo se ejecuta si is_training se evalúa como Verdadero, ¿no es así?
Y por lo tanto

mean, variance = utils.smart_cond(is_training, _force_updates, moving_vars_fn) 

debe ser equivalente a la línea 804:

mean, variance = moving_mean, moving_variance

si is_training se evalúa como Falso y, por lo tanto, la parte de "momentos" del gráfico nunca se usa y, por lo tanto, no debe ejecutarse

pero no lo he probado, así que podría estar equivocado al respecto :)

@ tano297 @ te crió bien. La media móvil y la varianza móvil se cambian cuando utilicé batchnorm como este:

def batch_norm_layer(self, x,train_phase, scope_bn):
        bn_train = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=True,
        reuse=None,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        bn_inference = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=False,
        reuse=True,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
        return z

Si usa lo siguiente:

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                         is_training=train_phase, scope=scope_bn)

La media móvil y la variación móvil no cambiarán durante la prueba, pero la velocidad es muy lenta.

Hola @zhongyuk ,

También encontré el problema de que podía obtener buenos resultados cuando usaba is_training = True tanto para el entrenamiento como para la inferencia, pero obtenía malos resultados cuando configuraba is_training = False durante la inferencia (peor que en el caso de usar is_training = True). De acuerdo con su análisis, si lo entiendo correctamente, simplemente estableciendo decay = 0.9 en BN puede resolver este problema. Estoy en lo cierto?

Por cierto, ¿necesito volver a entrenar el modelo usando decay = 0.9 desde cero? ¿O reanudar el entrenamiento desde el punto de control (es decir, entrenado cuando decaimiento = 0.999) también está bien?

¡Gracias!

@nmduc @ davek44

Hola, también encontré el problema de que podía obtener buenos resultados al usar is_training = True tanto para el entrenamiento como para la inferencia, pero obtenía malos resultados cuando configuraba is_training = False durante la inferencia (peor que en el caso de usar is_training = True). ¿Han resuelto este problema, chicos? ¡Gracias!

@tyshiwo Acabo de establecer decay = 0.9 para batch_norm y funciona bien hasta ahora.

Estaba confundido después de todos estos comentarios sobre cómo usar correctamente Batch Norm: así que esto es lo que tengo. Por favor corrígeme si estoy equivocado.

batch_norm = tf.contrib.layers.batch_norm(conv, center=True, scale=True, reuse=phase_train_py, scope='bn', is_training=is_training)

donde phase_train_py es una variable booleana de Python e is_training es un marcador de posición que toma una variable booleana. Supongo que usar tf.cond es incorrecto, de lo contrario, la función vendría con parámetros booleanos. En otras palabras, si tf.cond es verdadera, entonces deberíamos una función batch_norm para entrenamiento y otra para probar. Entonces, los desarrolladores nos permiten cambiar estas variables booleanas para cambiar el comportamiento de la función. Entonces, lo que estoy haciendo es: establecer phase_train_py en False mientras entreno mientras is_training en True. Y todo lo contrario durante la prueba. Dado que solo podemos cambiar tensores o marcadores de posición con sess.run , cambié phase_train_py intencionalmente antes de ejecutar el gráfico. Ex:

if condition: phase_train_py = False sess.run(to_run_list, feed_dict={phase_train: True}) else: phase_train_py = True sess.run(to_run_list, feed_dict={phase_train: False})

++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
TAL VEZ NECESITES LEER ESTO
++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

Parece que todavía hay problemas con TF v1.3. Estoy seguro de que noté los siguientes detalles, pero aún así no pude usar el tf.contrib.layers.batch_norm oficial, con is_training=False durante la evaluación (pero cuando mantengo is_training=True sin cambios durante la evaluación, es Okay):
1. decay , la media móvil exponencial es en realidad un filtro alfa en el procesamiento de la señal, el tiempo de convergencia es de aproximadamente 1 / (1-decaimiento) pasos del tren. Para decaimiento = 0.999, necesita 1 / 0.001 = 1000 pasos para converger. Por lo tanto, configure el decaimiento apropiado para los números de sus pasos de entrenamiento.

  1. usando un marcador de posición para cambiar entre la evaluación del tren y la prueba
  2. use updates_collections=None si no desea agregar dependencias de control de la operación de actualización a train_op
  3. establezca reuse en el valor apropiado.

Parece que la única forma de usar el batch_norm oficial es construir dos gráficos, uno para entrenamiento y otro para evaluación, con is_training=True y is_training=False , respectivamente. De esta manera, no es necesario cambiar dinámicamente entre entrenamiento y evaluación. Pero esta es una forma estúpida ya que necesita construir más de un gráfico.

Finalmente, escribo un promedio móvil por mí mismo, ¡y encuentro que funcionó! Es el siguiente (basado en el código de la web y modificado por mí mismo)

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("gamma", shape[-1], initializer=tf.constant_initializer(1.0), trainable=True)
        # beta: a trainable shift value
        beta = tf.get_variable("beta", shape[-1], initializer=tf.constant_initializer(0.0), trainable=True)
        moving_avg = tf.get_variable("moving_avg", shape[-1], initializer=tf.constant_initializer(0.0), trainable=False)
        moving_var = tf.get_variable("moving_var", 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),
    )

Simplemente use la función bn_layer_top durante la construcción de un gráfico, el parámetro is_training es un tf.placeholder
. Entonces, puede cambiar el marcador de posición a Verdadero durante el entrenamiento y Falso durante la evaluación, con feed_dict .

Espero que ayude a la comunidad.

Cuando use slim.batch_norm, asegúrese de usar "slim.learning.create_train_op" en lugar de "tf.train.GradientDecentOptimizer (lr) .minimize (loss)" u otro optimizador. Pruébelo para ver si funciona.

@vincentvanhoucke Escribiste en otra publicación de este hilo:

La envoltura delgada batch_norm se normaliza sobre la última dimensión de su tensor de entrada. Entonces, si es un tensor de entrada 2D que proviene de una capa completamente conectada, se normaliza por lotes y, por lo tanto, realiza una normalización por activación. Si es un tensor 4D que proviene de una convolución, se normalizará sobre las tres primeras dimensiones (lote, ancho, profundidad) y, por lo tanto, realizará la normalización por característica. @sguada tal vez sea un poco más descriptivo sobre esto.

¿Quiere decir con "slim batch_norm wrapper" la función tf.contrib.layers.batch_norm ? Si es así, sugiero agregar esta información al texto de documentación de esta función. Por lo tanto, queda muy claro que esta función realiza la normalización por lotes exactamente como se describe en el documento ... tanto para FC-Layer como para Conv2D-Layer. Por el momento solo existe el texto "Se puede usar como función normalizadora para conv2d y totalmente_conectado", donde no está claro si esto está relacionado con el tema del eje de normalización.

@ZahlGraf Con

@vincentvanhoucke
Creé un PR con una descripción más detallada, principalmente basado en su declaración en este hilo:
https://github.com/tensorflow/tensorflow/pull/15653

Elimine al cesionario, ya que este problema invita a contribuciones externas. De lo contrario, elimine la etiqueta contributions welcome . Gracias.

Elimine al cesionario, ya que este problema invita a contribuciones externas. De lo contrario, elimine la etiqueta contributions welcome . Gracias.

Cerrar este error desde que se solucionó la solicitud original para agregar una capa de norma por lotes. Algunos de los problemas más recientes con la documentación parecen tener sus propios RP
Si ve algún problema con batch_norm, haga una pregunta en StackOverflow o abra otro problema.

¿Fue útil esta página
0 / 5 - 0 calificaciones