Tensorflow: Camada de norma de lote fácil de usar.

Criado em 16 fev. 2016  ·  127Comentários  ·  Fonte: tensorflow/tensorflow

Muitos não especialistas estão usando o seguinte código http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow?answertab=votes#tab -top.

Seria bom ter uma camada de norma de lote oficial, dada sua importância no treinamento de DNNs.

contributions welcome docs-bug

Comentários muito úteis

Todos 127 comentários

Estou trabalhando em algumas partes disso.

Acho que algo está errado com essa camada. no treinamento tudo está bem e as perdas diminuem muito bem. mas nos testes eu obtenho precisão zero.
A propósito, nos testes quando eu uso is_training = False, recebo zero acc.
Eu sei que a normalização em lote se comporta de maneira diferente no treinamento e na fase de teste, conforme descrito em Como a normalização em lote se comporta de maneira diferente no tempo de treinamento e no tempo de teste?

Mesmo aqui, experimentei algum comportamento inesperado com is_training = False. Qual é a maneira correta de alterar este sinalizador? No momento, estou usando um tf.cond porque ele não leva tf.placeholders sozinho.

@pawni Você deve usar um booleano Python para is_training . Não pode ser tf.cond .

@ppwwyyxx bem, estou fazendo tf.cond(placeholder, batch_norm(.., is_training = True), batch_norm(.., is_training = False)) ou alguém deveria fazer batch_norm(.., is_training=variable) e mudar isso fora do gráfico quando necessário?

Oh, pensei que você estava ganhando batch_norm(.., is_training=tf.cond(placeholder)) , o que é incorreto.
Seu método atual também pode ter problemas. Você precisará verificar se os dois batch_norm op que você criou compartilham o mesmo escopo, caso contrário, eles não compartilharão as estatísticas de média / variação subjacentes.

Para fazer isso, o argumento reuse pode ajudar, mas não tenho certeza porque uso minha própria versão da camada bn.

Estou usando o mesmo escopo e reuse=True . Parece funcionar às vezes, mas não tenho certeza. Seria ótimo se a camada pudesse ser adicionada à documentação com uma breve explicação sobre como lidar da melhor forma com a mudança do treinamento para o teste.

@sguada FYI

Atualmente batch_norm requer um booleano python, mas estamos trabalhando para adicionar a opção de passar um Tensor.

@pawni Se você não quiser se preocupar com a atualização de moving_mean e moving_variance, defina updates_collections = None para ter certeza de que eles estão atualizados no local, caso contrário, você precisa ter certeza de que update_ops adicionado a tf.GraphKeys.UPDATE_OPS são executados durante o treinamento.

Acho que tensorflow precisa de 2 métodos hiper que mudam o estado do modelo, algo como tocha. alterar o estado do modelo . Eu acho que é muito simples.

existe um pequeno script com um NN muito simples que mostra qual é a maneira adequada de usar essa camada BN "oficial"? Eu realmente aprecio isso.

desculpe se isso é um pouco repetitivo, mas parece que a API fala sobre BN em uma interface diferente: https://www.tensorflow.org/versions/r0.9/api_docs/python/nn.html#batch_normalization

não é essa a forma oficial de usar o BN? Estou confuso sobre como usá-lo e o SO parece estar desatualizado e então há uma camada em um link diferente da API, exatamente como fazer isso? Não estou certo se devo ir ao SO ou perguntar aqui.

desculpe pelo spam, mas o que há de errado em apenas usar algo assim:

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

então é simples dizer ao tensorflow qual usar com um dicionário de feed como em:

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

já que não está claro se a implementação será alterada, eu queria dar uma sugestão (observe que é fácil estender para convoluções e outras coisas que eu simplesmente não colei esse código).

@pawni @ppwwyyxx vocês decidiram se deveriam usar a reutilização para true para resolver o problema de escopo?

@ brando90 atualmente estou fazendo 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))

No entanto, acho que o # 3265 basicamente deseja implementá-lo assim. Uma referência poderia ser a implementação de dropout aqui: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/layers/python/layers/layers.py#L433 -L435

Quando updates_collections = None, as atualizações acontecem no local e é mais fácil usar um tf.cond () para permitir que is_training seja um Tensor um pouco mais complicado é quando as atualizações são atrasadas e o update_ops é executado mais tarde.
Vou tentar obter a primeira parte em breve.

@ brando90 @pawni o código dele funciona bem, mas tem que mudar como abaixo

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))  

E quando executado em treinamento ou tempo de teste,

# 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, mas como # 3265 diz que será ótimo se tf.contrib.layers.batch_norm obtiver is_training variável como uma tf.plcaeholer .

@nmhkahn @pawni obrigado pelos trechos de código. Eles foram muito úteis para adicionar normalização em lote à minha rede de convolução. O treinamento parece funcionar muito bem. O teste, não. Em algumas versões do código, as precisões de treinamento são muito maiores do que as de teste, o que provavelmente significa que não estou compartilhando parâmetros de normalização em lote. Em outras versões do código, recebo "ValueError: VarScope conv1 / beta já existe, não permitido. Você quis dizer definir reuse = True no VarScope?" o que parece indicar que estou tentando reaprender o parâmetro ... quando estava tentando reutilizar.

Alguém pode fornecer um exemplo de como chamar a função "def BatchNorm" durante o treinamento e teste para que o compartilhamento de variáveis ​​aconteça corretamente.

Obrigado por qualquer ajuda.

ATUALIZAÇÃO em 25 de julho de 2016:

@nmhkahn @pawni obrigado por seus comentários. Depois de olhar mais de perto o código em contrib, percebi qual era o meu problema. Durante o treinamento e o teste, estamos atualizando ou reutilizando quatro variáveis ​​(beta, gama, moving_mean e moving_variance). Para torná-los únicos, tive de definir um escopo por camada. Eu fiz assim:

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

onde batch_norm_layer é semelhante aos exemplos de @nmhkahn @pawni , conv2d_stride2_valid é apenas um def para definir uma camada convolucional e W_conv1 e b_conv1 são variáveis ​​que sustentam os pesos e vieses. Provavelmente, poderia remover o termo de polarização porque estamos usando a normalização em lote.

A rede está funcionando bem agora. Percebi depois de traçar as precisões no modo de treinamento e teste que as precisões de teste começam a subir após as precisões de treinamento. Em retrospecto, faz sentido, já que estamos coletando estatísticas de conjuntos de dados para teste. Mas parecia que eu estava fazendo algo errado durante meus testes iniciais. Obrigado por seus comentários e por disponibilizar a normalização em lote para a comunidade.

@nmhkahn em que difere da sugestão de pawni?

@ brando90 Tive um pequeno erro na minha versão que foi corrigido por nmhkahn (alterando isTraining para is_training )

@diegoAtAlpine Encontrei os mesmos problemas - mas não sei por que isso acontece. No entanto, o ValueError deve ser resolvido pelo snippet de código. Não tem certeza de como você deseja ver como chamá-lo, visto que os exemplos de nmhkahn parecem funcionar?

@nmhkahn @pawni @ quando você faz:

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

isso não significa que você está usando is_training como um espaço reservado? As pessoas comentaram que querem que is_training seja um titular de placer, mas isso é o que eu tinha para a minha versão:

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

não é correto?

Já estendi tf.contrib.layers.batch_norm para permitir a passagem de um tensor ou um espaço reservado para is_training. Ele será incorporado ao TF contrib em breve.

Agora disponível em
https://github.com/tensorflow/tensorflow/commit/9da5fc8e6425cabd61fc36f0dcc1823a093d5c1d#diff -94bbcef0ec8a5cdef55f705e99c2b2ed

sou só eu ou a adição dessa camada BN retarda visivelmente o treinamento de uma única época?

@ brando90 Também retarda o treinamento para mim, mas acho que isso é esperado, pois é necessário calcular algumas estatísticas. E sua versão parece boa para mim.

BatchNorm está atualmente muito lento (por causa de todas as estatísticas calculadas), mas eles estão trabalhando para adicionar um op cudnn batchnorm como disse aqui .

@nmhkahn pergunta rápida. Quando você escreveu (para teste):

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

em teoria, bx e by pode ser qualquer conjunto de dados? ou seja, ainda pode ser o conjunto de treinamento mesmo que não estejamos treinando? (ou seja, apenas para rastrear o erro do trem)

@ brando90 você está certo.

Também estou confuso sobre is_training e reusar sinalizadores. Eu criei um programa seguindo o exemplo CIFAR, onde meu código é estruturado como em CIFAR:

  • Inferência
  • Perda
  • Trem

E estou executando em um modo multi-gpu (para treinamento).
Portanto, tenho um script para treinamento (semelhante a cifar10_multigpu.py) e um para teste (semelhante a cifar10_eval.py).
então

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()

A inferência acontece com a função MyModel. (abaixo está um exemplo da função, na verdade eu uso mais camadas e neurônios).

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

Desejo realizar a nomalização em lote. Então, quando eu fiz:

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)

Recebi o seguinte erro na fase de treinamento:
Bnormalização / beta variável não existe, não permitida. Você quis dizer definir reuse = None no VarScope?

Pelo que tenho lido neste tópico na fase de treinamento, devo usar reuse = None. Eu entendi esta parte correta? Se isso for verdade, como estou usando duas GPUS, devo reutilizar = Nenhum na primeira GPU e reutilizar = Verdadeiro na segunda? Ou, como estou fazendo tf.get_variable_scope (). Reuse_variables (), ele toma conta de si mesmo?

Finalmente, na fase de teste, devo ter is_training = False e reuse = True?

Qualquer ajuda é muito apreciada.

Agora tf.contrib.layers.batch_norm aceita um Tensor, Variável ou Marcador de posição como is_training

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

É normal que a normalização em lote torne meus experimentos piores ? Eu tentei em uma rede NN de 2 camadas com base no tutorial para iniciantes MNIST e obtive resultados piores consistentemente quando BN está presente: com BN (um com escala e centro treinado e o outro não) a precisão é 0,8423, 0,8221 e sem BN a precisão é 0,9477.

Meu script está presente aqui https://github.com/brando90/tensor_flow_experiments/blob/master/tf_tutorials/beginner_tutorial_MNIST_BN.py

Alguém já passou por esses problemas ou o BN é assim e preciso fazer outra coisa para que funcione?

A versão mais recente de tf.contrib.layers.batch_norm agora aceita um espaço reservado para is_training, portanto, não precisa fazer isso sozinho.

Mas o que é importante é que você passe updates_collections = None para que moving_mean e moving_variance sejam atualizados no local, caso contrário, você precisará reunir o update_ops e certificar-se de que sejam executados.

Eu gostaria de encorajá-lo a usar tf.contrib.layers ou tf.contrib.slim para construir seu 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 Eu mudei meu antigo onde digo manualmente para treinar ou não (baseado em um tf.cond) e agora parece que a precisão é de até ~ 95 novamente. Por que eu precisei alterar updates_collections para None? Você se importa em me explicar por que isso deu uma grande diferença de precisão? Parece uma alteração não trivial (deve None ser o valor padrão, se é tão importante?). Obrigado! :)

Além disso, percebi que você disse que era um espaço reservado e não precisei fazer isso manualmente. No entanto, quando passei um marcador de posição para is_training, ele disse

TypeError: Using a tf.Tensor as a Python bool is not allowed. Use se t não for Nenhum: instead of se t: to test if a tensor is defined, and use the logical TensorFlow ops to test the value of a tensor.

e apontou para o código batch_norm. Talvez seja bom mostrar como esse espaço reservado deve ser usado, porque parece que não entendo como ele deve ser usado. Obrigado! :)

@ brando90
A parte relevante do código está aqui L227-256 .

Como você notará, existe uma instrução with ops.control_dependencies que força as atualizações. Acredito que para o código ser usado "direto da caixa", o padrão deve ser Nenhum.

Quanto ao meu comentário acima 1122 , descobri que tf.get_variable_scope (). Reuse_variables () cuida do problema, portanto, na fase de treinamento, o argumento reuso de batch_norm deve ser Nenhum. Tem a ver com a declaração variable_op_scope (leia sua documentação em tensorflow)

Uso de batch_norm com 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})

O problema anterior era que você não estava atualizando moving_mean e moving_variance após cada etapa, quando updates_collections é None, ele força as atualizações como parte do cálculo.
No entanto, quando uma rede tem muitas camadas batch_norm, é mais eficiente coletar todas as operações de atualização e executá-las juntas, de forma que cada camada não precise esperar a conclusão da atualização.

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])

Houve algum progresso na aceleração das normas de lote?

Eu estava tentando usar a norma de lote com um NN densamente conectado de 2 camadas com o conjunto de dados (achatar) MNIST (e unidades relu) para a tarefa de codificação automática e continuo recebendo um erro NaN. Alguém sabe por que isso pode ser? Isso é possível com BN? parece duvidoso, mas não poderia ser minha configuração de aprendizagem, taxa etc. (mas eu presumo que não deveria, porque BN deve ser meio ruim para isso)

@sguada Não estou entendendo a maneira correta de usar batch_norm especialmente no que diz respeito ao sinalizador updates_collections . Se entendi corretamente, se o sinalizador é None a rede não é eficiente, então devo deixar updates_collections=tf.GraphKeys.UPDATE_OPS e coletar todas as atualizações batch_norm e executá-las juntas.

Você coleta as atualizações batch_norms fazendo: update_ops = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS)) .

Tenho muitos modelos diferentes que usam camadas batch_norm diferentes, isso não funcionaria certo ?:

#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])

Você poderia explicar essa parte com mais detalhes? Muito obrigado.

Basta colocá-lo em chaves de coleção 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])

No entanto, a documentação parece estar desatualizada. Diz para fazer o seguinte:

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)

Mas:

  • _tf.group () _ não aceita uma lista. Substituí-o por _tf.tuple () _
  • Não sei como acessar _control_flow_ops.with_dependencies () _. Eu vi outros exemplos usando apenas tf.with_dependecies (), mas não posso fazer isso com Tensorflow 0.10. Encontrei aqui: _tf.python.control_flow_ops.with_dependencies () _

EDITAR:

A documentação deve ser atualizada para s.th. como isso:

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)

EDIT 2:

Depois de fazer algumas execuções na minha rede, devo dizer que não consigo ver nenhuma diferença de desempenho entre usar _updates_collections = None_ em contraste com buscar manualmente _tf.GraphKeys.UPDATE_OPS_ durante a construção do gráfico . Mesmo com uso intenso de normalização em lote (no total, my _tf.get_collection (tf.GraphKeys.UPDATE_OPS) _ retorna 140 Update-Ops, todos eles são apenas BN-ops)

Edit: Difícil dizer, se meus resultados estiverem corretos, mas toda a rede parece ser 1,5x mais rápida. Pelo que eu sei, as estatísticas BN são calculadas na CPU, não na GPU até agora.

Alguém de vocês pode ver algum benefício de desempenho também? Por favor, compartilhe seus resultados :)

Voltando ao problema de desempenho, a camada de norma de lote atual se beneficia do uso da GPU? Alguém já experimentou os benefícios das GPUs com essa implementação de norma em lote?

Desculpe pelo spam, mas a documentação não explica realmente como usar este BN com convolução (talvez deva ser fornecido em algum lugar?). Em suma, como ele descobre que deve aplicar e aprender os mesmos parâmetros por recurso (em vez de por ativação)?

(Existe pelo menos um snippet de código para fazer isso?)

O wrapper batch_norm fino normaliza sobre a última dimensão de seu tensor de entrada. Portanto, se for um tensor de entrada 2D vindo de uma camada totalmente conectada, ele normaliza em lote e, portanto, executa a normalização por ativação. Se for um tensor 4D vindo de uma convolução, ele normalizará nas três primeiras dimensões (lote, largura, profundidade) e, assim, realizará a normalização por recurso. @sguada talvez seja um pouco mais descritivo sobre isso.

@nmhkahn Com relação ao seu snippet de código, posso perguntar por que reuse definido como None quando is_training=True ? Isso não acionaria o parâmetro de escala gamma e o parâmetro de deslocamento beta seria reinicializado em cada etapa de treinamento? Eu pensei no artigo original, beta e gamma são "aprendidos junto com os parâmetros do modelo original". Para fazer isso, eles não deveriam ser inicializados apenas uma vez e então reutilizados em todas as etapas de treinamento?

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))

Agradeço muito o trabalho que a equipe TF colocou aqui para tornar batch_norm disponível e eficaz. Pela minha pesquisa, este tópico é o melhor recurso para como usá-lo. Existem muitos problemas e ideias diferentes voando por aqui, e é difícil descobrir o conselho de consenso para o caso padrão mais simples de como usar a camada batch_norm. Acho que seria muito útil expandir a documentação para especificar o uso recomendado exato.

Minha melhor tentativa de descobrir isso me levou ao seguinte 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))

Em seguida, defino is_training_ph como True para treinamento e False para teste. Isso não funciona para mim. O modelo treina bem, mas o desempenho do teste é terrível. Em contraste, se eu mantiver is_training_ph = True para o tempo de teste, funciona muito bem. Portanto, estou supondo que ainda tenho um problema de escopo, que não está encontrando as variáveis ​​existentes adequadas.

@ davek44 Estou usando a mesma estrutura de código que você está usando e observei a mesma coisa: quando liga is_training=True durante a fase de treinamento e desliga is_training=False para a fase de validação e / ou teste, o modelo treina bem como o artigo descrito (o modelo converge mais rápido e consegui usar uma taxa de aprendizado maior), porém o desempenho do teste é péssimo. Se eu ativar is_training=True o tempo todo, o modelo treina da mesma forma que sem inserir a camada de norma de lote. Ainda não descobri o que fiz de errado. Estou planejando usar o TensorBoard para monitorar os parâmetros. Você poderia atualizar se diagnosticar a causa desse comportamento?

tf.contrib.layers.batch_norm pode receber tensor como is_training, portanto, não é necessário fazer nada de 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'),

Vejo o mesmo desempenho de teste ruim com esse código.

Sem mais detalhes é impossível saber, meu palpite é que você treina apenas para algumas iterações, então o moving_mean e o moving_average ainda não convergiram.

Você pode alterar o batch_size durante o teste para ver como o desempenho diminui conforme você diminui o tamanho do lote.

Vejo o mesmo desempenho de teste ruim com esse código.

Eu tive exatamente o mesmo problema com tf.slim batchnorm ou com tf.cond e input is_training como um espaço reservado.
No primeiro caso, ao investigar o modelo treinado, descobri que a média móvel e a variância móvel consistem em zeros.
No último caso, a média móvel e a variância parecem mais razoáveis ​​(com valores diferentes), mas se eu usar is_training = False no tempo de teste, o desempenho também é muito ruim. Usando is_training = True, funciona melhor, mas acho que só usa a média móvel e a variância dentro do lote de teste.

@nmduc @ davek44 Escrevi um código para rastrear a média móvel e a variância móvel calculada em tf.contrib.layers.batch_norm durante o treinamento e teste. Descobri que o valor de decay importa muito (eles usam decaimento exponencial para calcular a média móvel e a variância móvel), com uma configuração decay próxima de 1,0 (ou seja, decay=.999 ), a média móvel cai para um valor mais próximo de 0. Fiz 2 execuções de teste com exatamente o mesmo código, mas configurações decay em tf.contrib.layers.batch_norm e minhas precisões de validação / teste pareciam mais razoáveis.

Os resultados da execução do teste com decay=0.9
screen shot 2016-11-16 at 1 51 51 pm

Os resultados da execução do teste com decay=0.999 ( decay=0.999 é a configuração padrão em tf.contrib.layers.batch_norm )
screen shot 2016-11-16 at 2 03 58 pm

(também parece que um valor de decaimento maior exigiria que o modelo treinasse por mais tempo para ver a mudança na precisão da validação)

Sim, isso consertou. Obrigado por compartilhar sua análise @zhongyuk!

Eu encorajo os desenvolvedores a considerarem fazer decay = 0,9 o padrão. Mesmo 0,99 não funciona bem para mim. Esse é o valor padrão na implementação do Torch também; veja o parâmetro momentum em https://github.com/torch/nn/blob/master/BatchNormalization.lua

@zhongyuk Muito obrigado por compartilhar. Trabalha para mim agora.

Isso parece importante. @sguada , devemos considerar o curso de ação correto aqui antes de 1.0. No curto prazo, uma das partes interessadas pode me enviar um PR documentando o fato de que decay pode ter que ser reduzido significativamente quando houver um desempenho de avaliação fraco? Tenho certeza de que nunca precisei ajustar esse parâmetro, mas pode ser um efeito colateral da configuração distribuída.

Poderíamos alterar o padrão para 0,9 ou documentar melhor seu impacto em conjuntos de dados menores ou poucas atualizações.
@vincentvanhoucke em nossa configuração distribuída geralmente fazemos milhões de atualizações, então está tudo bem, no entanto, em outros casos como este aqui, que faz apenas algumas centenas de atualizações, faz uma grande diferença:
Por exemplo, usar decaimento = 0,999 tem uma tendência de 0,36 após 1000 atualizações, mas essa tendência cai para 0,000045 após 10.000 atualizações e para 0,0 após 50000 atualizações.

Só queria observar que também tenho o problema de desempenho de teste insatisfatório, especificamente usando tamanhos de lote pequenos (qualquer coisa menor que 10 em vez dos 200 que usei para o treinamento diminui a precisão do teste). Usei um tf.placeholder para alternar entre o modo de teste / treinamento.

É ótimo que essa camada de normalização de lote funcione para uma melhor convergência de treinamento, mas se você não pode aplicar o modelo na produção, não há muito sentido em usá-lo. Alguém pode confirmar um bom desempenho de teste com amostras de dados pequenas ou únicas usando essa camada de norma de lote?

Posso confirmar que o desempenho do teste é bom ao usar is_training = False com pequenos lotes e até mesmo com batch_size = 1, pois não está usando a estatística do lote, mas a estatística aprendida durante o treinamento. Só precisa ter certeza de que as estatísticas convergiram com queda padrão = 0,999, o que implica pelo menos 50k atualizações.

Para acompanhar a confirmação do desenvolvedor do TF, acompanho a convergência das estatísticas com duas configurações decay (e batch_size de treinamento = 1). Com decay=0.99 , as estatísticas convergem (tendência <0,001) após 550 ~ 600 etapas de aprendizado / atualizações. Com decay=0.9 , as estatísticas convergem (biase <0,001) dentro de 100 etapas de aprendizado / atualizações.

@sguada obrigado, isso também significa que a saída é realmente independente do tamanho do lote? porque estou percebendo mudanças muito pequenas com grande impacto na minha precisão (talvez minha definição de desempenho seja afetada mais facilmente por essa pequena mudança). Para ser mais preciso, todos os valores em meu tensor de saída de 128 dimensões aumentam de forma que o comprimento total do vetor é quase linear com o tamanho do lote. Por valor, isso não é muita diferença, mas tem um grande impacto ao calcular distâncias vetoriais em espaços latentes.

@zhongyuk obrigado, decay=0.9 , então deveria ter convergido e testar o desempenho usando tamanhos de lote grandes é bom. Mas mesmo se não fizesse, isso resultaria em uma diferença entre treinar um teste? Eu estaria vendo um desempenho ruim durante o treinamento e teste se não tivesse convergido, certo?

Vou investigar mais um pouco e ver se consigo reproduzir o problema em outra tarefa. Obrigado pelo feedback rápido até agora!

@dominikandreas Se o seu fraco desempenho no teste for causado pela não convergência das estatísticas, você verá um desempenho de treinamento razoavelmente bom, mas um desempenho ruim no teste. Porque durante o treinamento, a normalização do lote é feita usando apenas as estatísticas do lote de treinamento. No entanto, durante o tempo de teste, ele está usando as estatísticas de média móvel de todos os lotes de treinamento para normalizar o tensor de entrada.

Encontrei um erro no meu código, a normalização em lote está funcionando bem agora :-) obrigado pelo seu apoio

Olá @zhongyuk , como você rastreou a média móvel e a variação?
Obrigado!

@rogertrullo Geralmente eu configuro o TensorBoard para rastrear médias e variações móveis. Além disso, também tentei buscar estatísticas por meio de tf.get_variable("moving_mean") dentro do escopo durante o treinamento e referência para monitorar o viés.

Oi,
Eu tenho o mesmo problema que outro descrito que tenho bons resultados de treinamento, mas a validação / teste é ruim depois de usar batch_norm.
Eu uso a função assim:
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)
o valor de decaimento é 0,9
eu preciso definir o sinalizador de reutilização?
Terei todo o prazer por qualquer ajuda.

Tenho usado batch_norm conforme descrito neste tópico (com um tf.bool para treinamento; e ops.GraphKeys.UPDATE_OPS) e tudo funciona.

Ao salvar e restaurar usando:
saver = tf.train.Saver ()
funciona,

mas ao salvar usando:
saver = tf.train.Saver (tf.trainable_variables () + [global_step])
para que eu possa economizar espaço de armazenamento (não salvando os gradientes, etc.)
na restauração, há um erro:
"uninitialized value unpool4 / convc / bn / moving_mean"

Obviamente, isso ocorre porque moving_mean (e eu acho que moving_variance) não foi salvo para nenhuma das camadas. Como tenho muitos deles (aninhados em várias camadas) - qual a forma mais eficiente de adicioná-los à lista de valores a serem salvos? Além disso, dado que essas são variáveis ​​treináveis, por que não são adicionadas à coleção trainable_variables?

@mshunshin a média móvel e a variância não são variáveis ​​treináveis: não há gradientes chegando até eles, eles estão apenas acumulando estatísticas em minibatches de exemplos.
Para salvá-los / restaurá-los, você pode usar tf.global_variables ()

para mim, as coisas começaram a funcionar quando usei este wrapper:
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 o uso de escopos e reutilização não está claro neste tópico para minha opinião.

Muito Obrigado. Com tf.global_variables () os arquivos salvos são muito maiores, pois acho que inclui os gradientes; no final usei:

saver = tf.train.Saver ([x para x em tf.global_variables () se 'Adam' não estiver em x.name])

e porque o gerenciador de sessão init não os inicializa corretamente:

sess.run (tf.variables_initializer ([x para x em tf.global_variables () se 'Adam' em x.name]))

(Usando tf.train.AdamOptimizer)

Você também pode usar tf.model_variables () que contém as variáveis ​​do modelo, isto é, moving_mean

@sguada Desculpe por incomodá-lo, mas é possível dar um exemplo de como usar slim.batch_norm quando combinado com slim.conv2d / slim.fully_connect em readme.md?

Estou usando slim.batch_norm, mas obtenho bom desempenho de treinamento e desempenho de validação / teste ruim. Eu acho que deve ser devido ao uso impróprio de reuse ou scope ou alguns outros parâmetros. Embora haja muitos problemas na normalização em lote, é difícil encontrar um trecho de código completo sobre como usá-lo, esp. para saber como passar diferentes parâmetros em diferentes fases.

Digamos, em meu código mnist_bn , eu controlei dependências usando tf.GraphKeys.UPDATE_OPS e configurei is_training como um espaço reservado. Mas o desempenho de validação ainda é ruim se eu alimentar {is_training: False}.

Eu apreciaria muito se houvesse um exemplo de normalização de lote oficial e completo (o que significa treinamento, validação, teste estão incluídos).

Agradeço antecipadamente!

Oi,
você precisa definir um escopo diferente para cada vez que usar a norma de lote e dar a ele a entrada de reutilização de acordo com a fase de treinamento / teste (VERDADEIRO quando testar FALSO quando treinar) que funciona para mim.

@ishaybee Obrigado pela ajuda. Encontrei meu problema = = É devido à inicialização a frio de moving_mean / moving_variance.

Como não treinei etapas suficientes, a média / variância móvel estimada não é tão estável. O resultado acabou sendo: o modelo tem um desempenho muito bom no treinamento de minilotes (você sabe que no início a perda diminui rapidamente), mas o desempenho de validação é errático (porque a média / variância da população estimada não é estável o suficiente).

Quando treinei o modelo por mais tempo, a precisão da validação também se tornou mais bonita.

Outra coisa importante é, certifique-se de usar slim.learning.create_train_op para criar operações de trem . Não use tf nativo tf.train.GradientDescentOptimizer(0.1).minimize(loss) .

Portanto, a resposta é: estou usando a normalização em lote corretamente, mas não entendi totalmente sua dinâmica durante o treinamento.

================
O que mais:

  1. Aqui está um exemplo completo de como usar a camada BN no conjunto de dados MNIST.
  2. Usar um valor de decaimento menor irá acelerar a fase de aquecimento. O decaimento padrão é 0,999, para pequenos conjuntos de dados como MNIST, você pode escolher 0,99 ou 0,95, e aquece em um curto período de tempo.

@soloice , observe, que tal comentar o seguinte parâmetro é passado dentro da camada para chamar batch_norm:

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

Sem updates_collections definido como Nenhum (significa que as atualizações são feitas no local dentro do BatchNorm), não espero que a camada circundante (por exemplo, conv2d) execute de alguma forma tf.GraphKeys.UPDATE_OPS necessário para que a camada BatchNorm atualize a média e portanto, poderá ser executado em dados de teste posteriormente.

Ou você pode tentar executar UPDATE_OPS explicitamente como um aqui

    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)

Update - descobri que citei exatamente o seu código e você usa UPDATE_OPS.

Quanto à "partida a frio", como você pode ver na discussão acima, diminuir o decaimento médio de execução do BatchNorm (parâmetro de entrada) do padrão 0,999 para algo como 0,95 pode acelerar a inicialização

@pavelbulanov É muito gentil da sua parte me ajudar com isso! Vou tentar um valor menor de decay para ver como isso ajuda.

================
Atualização: usar uma pequena deterioração (digamos, 0,9 ou 0,95) ajuda muito. A perda de validação diminui muito rapidamente quando eu defino decay como 0,9. No entanto, a desvantagem da pequena redução é que seu intervalo efetivo é pequeno: o resultado é dominado por algumas amostras recentes, portanto, não é uma boa estimativa da média / variância da população. É necessário equilibrar entre o início rápido (pequena redução) e um alcance efetivo mais longo (grande redução).

Oi,
Tentei implementar uma camada de normalização em lote com a ajuda das sugestões neste problema, mas ainda tenho um erro> 70% na validação e no teste ... Eu tenho uma redução menor para chamadas sem treinamento ...

Aqui está meu 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)
    )

Agradeço antecipadamente.

@Alexivia Parece que você está usando duas camadas de normalização de lote diferentes? Você deve usar apenas uma camada BN (é claro, com um parâmetro is_training ).

Obrigado pelo seu conselho @soloice.
Tentei agora apenas com os parâmetros is_training e 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)

ainda não obtive bons resultados de validação e teste ...> 70% ...

Oi,
por favor, veja meu invólucro acima.
você deve usar "com tf.variable_scope (escopo, reutilizar = reutilizar):" Eu acho.

Olá @ishaybee ,
Segui seu conselho, agora meu código é:

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)

e eu alimentei is_training e reuse através do feed_dict, mas agora recebo o erro ValueError("The reuse parameter must be True or False or None.")

tente alimentar a reutilização como uma variável python (entrada do modelo) e como espaço reservado.

Tentei fazer isso e agora parei de reclamar do valor ... mas acho que o valor do marcador não está sendo usado, porque não vejo nenhuma mudança se forçar os valores para a função batch_norm e no TensorBoard não conectado ao gráfico ... (ver imagem em anexo)
screen shot 2017-04-03 at 19 54 54

Meu código é assim agora:
Wrapper de normalização de lote

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)

Definição 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)

Treinamento

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)

Validação

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

Embora is_traning possa ser um placeholder, a reutilização deve ser um bool, e não pode ser um tensor nem um placeholder.

Não tenho certeza do que você está tentando fazer, na maioria dos casos, usar valores estáticos resolvem o problema. Por exemplo, este padrão funciona bem:

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 você precise alterar o comportamento do modelo dinamicamente, você não precisa usar um espaço reservado para is_training. O truque é construir o modelo duas vezes, mas compartilhando as variáveis ​​na segunda vez.

Obrigada @sguada ! Depois de aplicar suas sugestões, eu finalmente fiz funcionar!

Seria útil se a documentação da API 1.0 refletisse que você precisa adicionar manualmente as operações de atualização ao gráfico. Sendo um usuário tf mais novo, descobri que meu erro de teste era louco e então tive que gastar uma boa quantidade de tempo depurando meu gráfico até que percebi que a normalização de lote era o problema. Então, tive que gastar mais tempo descobrindo que, por padrão, as variáveis ​​que rastreiam os momentos não são atualizadas, a menos que você use uma função contrib para otimização. Como na versão 1.0 não há opção para definir update_collections como None, não há nenhum indicador na documentação de que isso possa ser um problema. Além disso, parece que faz sentido ter um parâmetro para adicionar as dependências do fluxo de controle à operação que é executada no caso de treinamento.

@danrsc Exatamente. O uso da camada BN é bastante confuso. Sugeri adicionar documentos ou um tutorial oficial completo sobre normalização em lote, mas infelizmente não obtive resposta = =

Totalmente de acordo. Eu acho que o uso do BN é muito complicado e a documentação atualmente está além de inadequada. Isso deve ser corrigido para uma camada tão comumente usada.

Reabertura para visibilidade dos problemas de documentação.

@sguada atribuindo a você para triagem. Pode valer a pena contratar um redator de tecnologia para o caso.

Fiquei confuso com este problema na semana passada e desperdicei 3 dias de treinamento ... Espero que os documentos possam ser corrigidos em breve e um exemplo oficial de normalização em lote possa ser adicionado aos documentos da API.

@sguada Percebi que você disse "tf.contrib.layers.batch_norm pode pegar tensor como is_training, então não precisa fazer nada de especial".
Porém, o comentário no código é
Se is_training não tem um valor constante, porque é um Tensor ,
# a Variable ou Placeholder então is_training_value será Nenhum e
# needs_moments será verdade.
Isso significa que nees_moments será verdadeiro mesmo na fase de teste se eu definir is_training como um espaço reservado?
Pelo que eu sei, os momentos não são necessários durante o teste.

Portanto, se is_training é Variable ou Placeholder , isso significa que pode mudar, então o gráfico para calcular os momentos é necessário, então a camada o constrói.
Então, em tempo de execução, dependendo do valor sendo True ou False usaria o lote moments ou moving_mean e moving_variance .

Portanto, durante o teste, você definiria o valor para False e moments não será usado.

@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

Eu construo batchnorm como esta, no entanto, a média móvel e a variável móvel são atualizadas durante o teste, não consigo encontrar o motivo.

Tentei criar dois modelos como @sguada disse, no entanto, meu modelo onde is_training = False simplesmente trava.

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

Acho que talvez devesse haver um exemplo concreto de como fazer uma norma de lote com uma rede totalmente conectada, bem como com CNNs. É uma pena que eu treinei modelos por dias esperando que as coisas funcionassem antes de ver que todo mundo tentando usar esse recurso enlouqueceu.

Curiosamente, leva zilhões de anos para restaurar o modelo após o treinamento com batch_norm também. Provavelmente esperará até o TF 2.0 para tentar algo assim novamente.

@MisayaZ você não precisa criar duas camadas batch_norm, você pode apenas passar train_phase (assumindo que é um tf.bool) para batch_norm. Além disso, você está passando UPDATE_OPS_COLLECTION variables_collections, que muda quais coleções são as variáveis ​​adicionadas.

O seguinte deve funcionar:

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

@OktayGardener não tem certeza de qual modelo você está tentando criar, parece que as variáveis ​​não são salvas em seu ponto de verificação.

batch_norm também funciona com camadas 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 Obrigado, construí uma rede com bathnorm que é implementado como você mencionou acima

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

a velocidade é lenta, eu uso o benchmark de tensorflow para obter o tempo de cálculo conforme abaixo:
I tensorflow / core / util / stat_summarizer.cc: 392] ==================================== Início por tempo de computação === ===============================
I tensorflow / core / util / stat_summarizer.cc: 392] [tipo de nó] [iniciar] [primeiro] [média ms] [%] [cdf%] [mem KB] [Nome]
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 / moment / enough_statistics / 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 / moment / enough_statistics / Sub
I tensorflow / core / util / stat_summarizer.cc: 392] SquaredDifference 45.570 5.908 7.177 3.257% 55.007% 5537.792 conv2 / batch_norm / moment / enough_statistics / 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

Não entendo porque alguns op in moment são executados durante o teste e custam muito tempo, como conv1 / batch_norm / moment / enough_statistics / SquaredDifference.

O momento não é necessário no teste, por que algumas operações em momento são executadas?

Oi,

Usando a camada batch_norm em contrib.layers , estou recebendo nan como uma saída para o gráfico de validação enquanto o gráfico do trem funciona perfeitamente. Existe algo que eu possa estar perdendo?

Estou a usar:

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)

obrigado

Como acompanhamento, estou reutilizando 16 camadas de batch_norm.
No entanto, descobri que reutilizar 4 camadas funciona.

Acabei de notar que, se eu matar o processo tensorflow e reiniciá-lo, meu erro piorará por alguns períodos (ou seja, pior do que deveria ser no último ponto de verificação). Também observo que, se eu remover batch_norm, esse problema desaparece. Depois de examinar o código por um tempo, acho que pode ser porque os valores das variáveis ​​não são restaurados a partir das variáveis ​​sombra como seriam se a classe ExponentialMovingAverages fosse usada para gerenciar as médias móveis. Isso também significa que, se eu usar um processo separado para avaliar, estou obtendo o último valor da variável e não a média móvel. Estou interpretando isso corretamente e esse é o comportamento pretendido? Parece que você deseja que os valores da variável sombra sejam restaurados ...

Percebi o problema, a variação móvel no meu caso torna-se negativa após algumas iterações.

A saída do tensor: Model/clip_logits/batch_norm/moving_variance:0 presente em tf.model_variables() é

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 você pode ver, há variação negativa para uma das dimensões. Como isso é possível ?
PS A camada de norma de lote é usada logo após a última camada totalmente conectada da rede e antes de softmax.

@ raghavgoyal14 você está usando com

@abred : Sim, usei fused=True , mesmo problema.

@sguada Olá, sguada, estou com um problema.
A definição de contrib.layers.batch_norm em tensorflow:
def batch_norm (entradas,
decadência = 0,999,
center = True,
escala = Falso,
epsilon = 0,001,
ativação_fn = Nenhum,
param_initializers = None,
param_regularizers = None,
updates_collections = ops.GraphKeys.UPDATE_OPS,
is_training = True,
reutilizar = Nenhum,
variables_collections = None,
outputs_collections = None,
treinável = Verdadeiro,
batch_weights = Nenhum,
fundido = falso,
data_format = DATA_FORMAT_NHWC,
zero_debias_moving_mean = False,
escopo = Nenhum,
renorm = False,
renorm_clipping = Nenhum,
renorm_decay = 0,99):
escala: se for verdadeiro, multiplique por gama. Se for falso, gama é
não usado. Quando a próxima camada é linear (também por exemplo, nn.relu), isso pode ser
desativado, pois o dimensionamento pode ser feito pela próxima camada.

Se eu usar tf.contrib.layers.batch_norm (input, scale = False), "scale = False" significa se o gama é zero em "y = gamma * x + beta" durante o treinamento. Muito obrigado.

Quando escala = False, gama é uma constante 1.

@ppwwyyxx Muito obrigado por sua ajuda. Eu uso tf.contrib.layers.batch_norm (input, scale = False) no Tensorflow e agora estou convertendo a batchnorm do Tensorflow para Caffe. Como definir os parâmetros de BatchNormLayer e ScaleLayer no Caffe?
Muito obrigado.

@MisayaZ Eu estava tendo o mesmo comportamento usando o Batchnorm com um espaço reservado para "is_training". Vejo no trace que os momentos estão sendo calculados mesmo na hora do teste, então decidi entrar no código-fonte e encontrei isto:

    # 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 quando "is_training" é uma variável ou um espaço reservado, os momentos são definidos e também os calcula em tempo de execução, mesmo quando você define o espaço reservado para "Falso". Eu teria preferido deixá-lo como um espaço reservado porque desta forma eu posso fazer testes periódicos durante o treinamento sem redefinir o gráfico, mas decidi usá-lo como uma constante e definir comportamentos diferentes para treino vs teste, e agora os momentos não são calculados na hora do teste.

@ tano297 Obrigado. Agora também uso 'is_training' como uma constante. Deixe-o como um espaço reservado e faça testes periódicos para alterar o valor da média móvel e da variação móvel. E o tempo de inferência será mais longo, pois calculará a média e a variância das entradas e atualizará a média móvel e a variância móvel. A maneira certa de fazer o teste é definir comportamentos diferentes para treinar e testar como você mencionou.

@ tano297 @MisayaZ
mas não o "smart_cond" em

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)

certifique-se de que as atualizações sejam calculadas e aplicadas apenas se is_training for avaliado como True?

@abred Sim, de fato, mas você está se referindo à linha 391, onde faz a atualização da média móvel em _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)
        ...

Estou falando sobre a linha 753 em 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) 
        ...

A condição inteligente nesse caso (no que me diz respeito) decide se deve ou não atualizar as médias móveis, mas os momentos ainda são calculados.

@ tano297 você está certo sobre isso, eu estava no lugar errado, mas ainda assim:
a linha 755-770 calcula os momentos, mas os momentos são usados ​​apenas em _force_updates que só é executado se is_training for avaliado como True, não são?
E assim

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

deve ser equivalente à linha 804:

mean, variance = moving_mean, moving_variance

if is_training é avaliado como False e, portanto, a parte dos "momentos" do gráfico nunca é usada e, portanto, não deve ser executada

mas não testei, então posso estar errado sobre isso :)

@ tano297 @abred você certo. A média móvel e a variância móvel são alteradas quando usei batchnorm como esta:

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

Se você usar o seguinte:

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

A média móvel e a variância móvel não serão alteradas durante o teste, mas a velocidade é muito lenta.

Olá @zhongyuk ,

Eu também encontrei o problema de que eu poderia obter bons resultados ao usar is_training = True para treinamento e inferência, mas obter resultados ruins ao definir is_training = False durante a inferência (pior do que o caso usando is_training = True). De acordo com sua análise, se bem entendi, simplesmente definindo decaimento = 0,9 em BN pode resolver este problema. Estou certo?

BTW, eu preciso treinar novamente o modelo usando decaimento = 0,9 do zero? Ou retomar o treinamento do ponto de verificação (ou seja, treinado quando decadência = 0,999) também está ok?

Obrigado!

@nmduc @ davek44

Olá, também encontrei o problema de que poderia obter bons resultados ao usar is_training = True para treinamento e inferência, mas obter resultados ruins ao definir is_training = False durante a inferência (pior do que o caso usando is_training = True). Vocês resolveram esse problema? Obrigado!

@tyshiwo Acabei de definir decay = 0,9 para batch_norm e está funcionando bem até agora.

Fiquei confuso depois de todos esses comentários sobre como usar corretamente o Batch Norm: Então aqui está o que eu tenho. Por favor me corrija se eu estiver errado.

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

onde phase_train_py é uma variável booleana python e is_training é um espaço reservado para uma variável booleana. Eu acho que usar tf.cond está errado, caso contrário, a função veio com parâmetros booleanos. Em outras palavras, se tf.cond for verdadeiro, então devemos uma função batch_norm para treinamento e outra para teste. Portanto, os desenvolvedores nos permitem alterar essas variáveis ​​booleanas para alterar o comportamento da função. Portanto, o que estou fazendo é: definir phase_train_py como Falso durante o treinamento, enquanto is_training como Verdadeiro. E o oposto durante o teste. Como só podemos mudar tensores ou marcadores de posição com sess.run , mudei phase_train_py intencionalmente antes de executar o 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})

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++
TALVEZ VOCÊ PRECISA LER ISTO
++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++

Parece que ainda há problemas com o TF v1.3. Tenho certeza de que observo os seguintes detalhes, mas ainda não consegui usar o tf.contrib.layers.batch_norm oficial, com is_training=False durante a avaliação (mas quando mantenho is_training=True inalterado durante a avaliação, é Está bem):
1. decay , a média móvel exponencial é na verdade um filtro alfa no processamento do sinal, o tempo para convergir é de aproximadamente 1 / (1-decaimento) etapas do trem. Para decaimento = 0,999, você precisa de 1 / 0,001 = 1000 passos para convergir. Portanto, defina a redução apropriada para seus números de etapas de treinamento.

  1. usando espaço reservado para alternar entre avaliação de trem e teste
  2. use updates_collections=None se você não quiser adicionar dependências de controle de update op para train_op
  3. defina reuse para o valor apropriado.

Parece que a única maneira de usar o batch_norm oficial é construir dois gráficos, um para treinar e outro para avaliação, com is_training=True e is_training=False , respectivamente. Dessa forma, você não precisa alternar dinamicamente entre o treinamento e a avaliação. Mas esta é uma maneira estúpida, pois você precisa construir mais de um gráfico.

Finalmente, escrevo uma média móvel sozinho e acho que funcionou! É o seguinte (com base no código da web e modificado por mim)

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),
    )

Basta usar a função bn_layer_top durante a construção de um gráfico, o parâmetro is_training é um tf.placeholder
. Então você está livre para mudar o placeholder para True durante o treinamento e False durante a avaliação, com feed_dict .

Espero que ajude a comunidade.

Ao usar slim.batch_norm, certifique-se de usar "slim.learning.create_train_op" em vez de "tf.train.GradientDecentOptimizer (lr) .minimize (loss)" ou outro otimizador. Experimente para ver se funciona!

@vincentvanhoucke Você escreveu em outra postagem neste tópico:

O wrapper batch_norm fino normaliza sobre a última dimensão de seu tensor de entrada. Portanto, se for um tensor de entrada 2D vindo de uma camada totalmente conectada, ele normaliza em lote e, portanto, executa a normalização por ativação. Se for um tensor 4D vindo de uma convolução, ele normalizará nas três primeiras dimensões (lote, largura, profundidade) e, assim, realizará a normalização por recurso. @sguada talvez seja um pouco mais descritivo sobre isso.

Você quer dizer com "slim batch_norm wrapper" a função tf.contrib.layers.batch_norm ? Em caso afirmativo, sugiro adicionar essas informações ao texto de documentação desta função. Assim, fica muito claro que esta função realiza a normalização do lote exatamente como descrito no artigo ... para ambos FC-Layer e Conv2D-Layer. No momento existe apenas o texto "Pode ser usado como uma função normalizadora para conv2d e fully_connected.", Onde não está claro se isso está relacionado ao tópico do eixo de normalização.

@ZahlGraf Fico feliz em considerar um PR que esclareça a documentação. Estamos nisso há tanto tempo que não tenho mais uma boa noção do que é óbvio ou não, e gostaria de esclarecer a documentação para alguém com uma nova perspectiva sobre o assunto.

@vincentvanhoucke
Eu criei um PR com uma descrição mais detalhada, principalmente com base em sua declaração neste tópico:
https://github.com/tensorflow/tensorflow/pull/15653

Remova o cessionário, pois este problema é um convite a contribuições externas. Caso contrário, remova o rótulo contributions welcome . Obrigado.

Remova o cessionário, pois este problema é um convite a contribuições externas. Caso contrário, remova o rótulo contributions welcome . Obrigado.

Fechando este bug, uma vez que a solicitação original para adicionar uma camada de norma de lote foi resolvida. Alguns dos problemas mais recentes com a documentação parecem ter seus próprios PRs
Se você encontrar algum problema com batch_norm, faça uma pergunta no StackOverflow ou abra outro problema.

Esta página foi útil?
0 / 5 - 0 avaliações