¿Dónde llamo a la función BatchNormalization en Keras?

167

Si quiero usar la función BatchNormalization en Keras, ¿debo llamarla solo una vez al principio?

Leí esta documentación para ello: http://keras.io/layers/normalization/

No veo dónde se supone que debo llamarlo. A continuación se muestra mi código intentando usarlo:

model = Sequential()
keras.layers.normalization.BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None)
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

Pregunto porque si ejecuto el código con la segunda línea, incluida la normalización por lotes, y si ejecuto el código sin la segunda línea, obtengo resultados similares. Entonces, o no estoy llamando a la función en el lugar correcto, o supongo que no hace mucha diferencia.

pr338
fuente

Respuestas:

225

Solo para responder a esta pregunta con un poco más de detalle, y como dijo Pavel, la Normalización de lotes es solo otra capa, por lo que puede usarla como tal para crear la arquitectura de red deseada.

El caso de uso general es usar BN entre las capas lineales y no lineales en su red, ya que normaliza la entrada a su función de activación, de modo que esté centrado en la sección lineal de la función de activación (como Sigmoid). Hay una pequeña discusión al respecto aquí

En su caso anterior, esto podría verse así:


# import BatchNormalization
from keras.layers.normalization import BatchNormalization

# instantiate model
model = Sequential()

# we can think of this chunk as the input layer
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the hidden layer    
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the output layer
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('softmax'))

# setting up the optimization of our weights 
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)

# running the fitting
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

Espero que esto aclare las cosas un poco más.

Lucas Ramadán
fuente
25
Para su información, aparentemente la normalización por lotes funciona mejor en la práctica después de la función de activación
Claudiu
10
Hola @Claudiu, ¿te importaría ampliar este FYI? Parece contradecir directamente la respuesta anterior.
Ben Ogorek
77
@benogorek: claro, básicamente lo basé completamente en los resultados aquí donde colocar la norma del lote después del relu funcionó mejor. FWIW No he tenido éxito al aplicarlo de una forma u otra en la única red que he probado
Claudiu
32
Interesante. Solo para seguir, si continúa leyendo en ese resumen, dice que su mejor modelo [GoogLeNet128_BN_lim0606] en realidad tiene la capa BN ANTES de la ReLU. Entonces, mientras que BN después de la Activación podría mejorar la precisión en un caso aislado, cuando se construye todo el modelo, antes se realiza mejor. Probablemente es posible que colocar BN después de la activación pueda mejorar la precisión, pero es probable que dependa del problema.
Lucas Ramadan
77
@ CarlThomé tipo de. Vea este comentario reddit de ReginaldIII por ejemplo. Afirman: "BN está normalizando la distribución de características que salen de una convolución, algunas [de] estas características pueden ser negativas [y] truncadas por una no linealidad como ReLU. Si normaliza antes de la activación, está incluyendo estos valores negativos en la normalización inmediatamente antes de eliminarlos del espacio de características. BN después de la activación normalizará las características positivas sin sesgar estadísticamente con características que no llegan a la siguiente capa convolucional ".
mab
60

Este hilo es engañoso. Intenté comentar la respuesta de Lucas Ramadan, pero todavía no tengo los privilegios correctos, así que lo pondré aquí.

La normalización por lotes funciona mejor después de la función de activación, y aquí o aquí es por qué: se desarrolló para evitar el cambio interno de covariable. El cambio covariable interno ocurre cuando la distribución de las activacionesde una capa cambia significativamente durante el entrenamiento. La normalización de lote se usa para que la distribución de las entradas (y estas entradas son literalmente el resultado de una función de activación) a una capa específica no cambie con el tiempo debido a las actualizaciones de parámetros de cada lote (o al menos, permite que cambie de manera ventajosa). Utiliza estadísticas de lote para hacer la normalización, y luego usa los parámetros de normalización de lote (gamma y beta en el documento original) "para asegurarse de que la transformación insertada en la red pueda representar la transformación de identidad" (cita del documento original). Pero el punto es que estamos tratando de normalizar las entradas a una capa, por lo que siempre debe ir inmediatamente antes de la siguiente capa en la red. Si eso o no

jmancuso
fuente
27
Acabo de ver en la clase deeplearning.ai que Andrew Ng dice que hay un debate sobre esto en la comunidad de Deep Learning. Prefiere aplicar la normalización de lotes antes que la no linealidad.
shahensha 01 de
3
@kRazzyR Quise decir que el profesor Andrew Ng habló sobre este tema en sus clases de aprendizaje profundo sobre deeplearning.ai Dijo que la comunidad está dividida en la forma correcta de hacer las cosas y que prefería aplicar la normalización por lotes antes de aplicar la no linealidad.
shahensha
3
@jmancuso, BN se aplica antes de la activación. Del documento en sí, la ecuación es g(BN(Wx + b))dónde gestá la función de activación.
yashgarg1232
43

Este hilo tiene un debate considerable sobre si BN debe aplicarse antes de la no linealidad de la capa actual o a las activaciones de la capa anterior.

Aunque no hay una respuesta correcta, los autores de Batch Normalization dicen que debe aplicarse inmediatamente antes de la no linealidad de la capa actual. El motivo (citado del artículo original):

"Agregamos la transformación BN inmediatamente antes de la no linealidad, al normalizar x = Wu + b. También podríamos haber normalizado las entradas de capa u, pero dado que u es probable que sea la salida de otra no linealidad, es probable que la forma de su distribución cambie durante entrenar y restringir su primer y segundo momento no eliminaría el cambio covariable. Por el contrario, Wu + b es más probable que tenga una distribución simétrica y no dispersa, que es "más gaussiana" (Hyv¨arinen y Oja, 2000) "normalizar es probable que produzca activaciones con una distribución estable".

user12340
fuente
3
En mi propia experiencia personal, no hace una gran diferencia, pero siendo todo lo demás igual, siempre he visto que BN funciona un poco mejor cuando la normalización por lotes se aplica antes de la no linealidad (antes de la función de activación).
Brad Hesse
31

Keras ahora admite la use_bias=Falseopción, por lo que podemos guardar algunos cálculos escribiendo como

model.add(Dense(64, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('tanh'))

o

model.add(Convolution2D(64, 3, 3, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('relu'))
dontloo
fuente
qué model.add(BatchNormalization())diferente demodel.add(BatchNormalization(axis=bn_axis))
kRazzy R
@kRazzR no difiere si lo está utilizando tensorflowcomo back-end. Está escrito aquí porque copió esto desde el keras.applicationsmódulo, donde bn_axisse debe especificar con el fin de apoyar tanto channels_firsty channels_lastformatos.
ldavid
9
¿Alguien puede explicar cómo se relaciona esto con la pregunta OP? (Soy bastante principiante en NNs, así que tal vez me pierda algo.)
Pepacz
30

Casi se ha convertido en una tendencia ahora tener un Conv2Dseguido de un ReLuseguido de una BatchNormalizationcapa. Así que hice una pequeña función para llamarlos a todos a la vez. Hace que la definición del modelo se vea mucho más limpia y fácil de leer.

def Conv2DReluBatchNorm(n_filter, w_filter, h_filter, inputs):
    return BatchNormalization()(Activation(activation='relu')(Convolution2D(n_filter, w_filter, h_filter, border_mode='same')(inputs)))
stochastic_zeitgeist
fuente
77
tal vez empujar esto a keras?
sachinruk
6

Es otro tipo de capa, por lo que debe agregarla como una capa en un lugar apropiado de su modelo

model.add(keras.layers.normalization.BatchNormalization())

Vea un ejemplo aquí: https://github.com/fchollet/keras/blob/master/examples/kaggle_otto_nn.py

Pavel Surmenok
fuente
1
Después de agregar BatchNormalization, el val_acc dejó de aumentar cada época. Val_acc permaneció estancado en el mismo número después de cada época después de agregar BatchNormalization. Pensé que se suponía que Batch Normalization aumentaría el val_acc. ¿Cómo sé si funciona correctamente? ¿Sabes qué puede haber causado esto?
pr338
desafortunadamente el enlace ya no es válido :(
user2324712
Hay copias de ese ejemplo en los tenedores de Keras (por ejemplo, github.com/WenchenLi/kaggle/blob/master/otto/keras/… ), pero no sé por qué se eliminó del repositorio original de Keras, y si el El código es compatible con las últimas versiones de Keras.
Pavel Surmenok
4

La normalización por lotes se utiliza para normalizar la capa de entrada, así como las capas ocultas ajustando la media y la escala de las activaciones. Debido a este efecto de normalización con capa adicional en redes neuronales profundas, la red puede usar una mayor tasa de aprendizaje sin desaparecer o explotar gradientes. Además, la normalización por lotes regulariza la red de modo que sea más fácil generalizar y, por lo tanto, no es necesario utilizar el abandono para mitigar el sobreajuste.

Justo después de calcular la función lineal usando say, Dense () o Conv2D () en Keras, usamos BatchNormalization () que calcula la función lineal en una capa y luego agregamos la no linealidad a la capa usando Activación ().

from keras.layers.normalization import BatchNormalization
model = Sequential()
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, 
validation_split=0.2, verbose = 2)

¿Cómo se aplica la normalización por lotes?

Supongamos que hemos ingresado a [l-1] en una capa l. También tenemos los pesos W [l] y la unidad de polarización b [l] para la capa l. Deje que [l] sea el vector de activación calculado (es decir, después de agregar la no linealidad) para la capa l y z [l] sea el vector antes de agregar la no linealidad

  1. Usando un [l-1] y W [l] podemos calcular z [l] para la capa l
  2. Por lo general, en la propagación de avance añadiremos la unidad de sesgo a la z [l] en esta etapa como esta z [l] + b [l], pero en la Normalización de lotes, este paso de adición de b [l] no es necesario y no se requiere Se utiliza el parámetro b [l].
  3. Calcular z [l] significa y restarlo de cada elemento
  4. Divida (z [l] - media) usando la desviación estándar. Llámalo Z_temp [l]
  5. Ahora defina nuevos parámetros γ y β que cambiarán la escala de la capa oculta de la siguiente manera:

    z_norm [l] = γ.Z_temp [l] + β

En este extracto de código, Dense () toma a [l-1], usa W [l] y calcula z [l]. Luego, la BatchNormalization () inmediata realizará los pasos anteriores para dar z_norm [l]. Y luego la Activación inmediata () calculará tanh (z_norm [l]) para dar un [l] es decir

a[l] = tanh(z_norm[l])
Aishwarya Radhakrishnan
fuente