Où appeler la fonction BatchNormalization dans Keras?

168

Si je souhaite utiliser la fonction BatchNormalization dans Keras, dois-je l'appeler une seule fois au début?

J'ai lu cette documentation pour cela: http://keras.io/layers/normalization/

Je ne vois pas où je suis censé l'appeler. Voici mon code essayant de l'utiliser:

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)

Je demande parce que si j'exécute le code avec la deuxième ligne, y compris la normalisation par lots et si j'exécute le code sans la deuxième ligne, j'obtiens des sorties similaires. Donc, soit je n'appelle pas la fonction au bon endroit, soit je suppose que cela ne fait pas beaucoup de différence.

pr338
la source

Réponses:

225

Juste pour répondre à cette question un peu plus en détail, et comme l'a dit Pavel, la normalisation par lots n'est qu'une autre couche, vous pouvez donc l'utiliser en tant que telle pour créer l'architecture réseau souhaitée.

Le cas d'utilisation général est d'utiliser BN entre les couches linéaires et non linéaires de votre réseau, car il normalise l'entrée de votre fonction d'activation, de sorte que vous soyez centré dans la section linéaire de la fonction d'activation (telle que Sigmoid). Il y a une petite discussion à ce sujet ici

Dans votre cas ci-dessus, cela pourrait ressembler à:


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

J'espère que cela clarifie un peu plus les choses.

Lucas Ramadan
la source
25
FYI apparemment la normalisation par lots fonctionne mieux en pratique après la fonction d'activation
Claudiu
10
Salut @Claudiu, cela vous dérangerait-il de développer cet article? Cela semble contredire directement la réponse ci-dessus.
Ben Ogorek
7
@benogorek: chose sûre, en gros, je me suis entièrement basé sur les résultats ici où placer la norme de lot après le relu a mieux fonctionné. FWIW Je n'ai pas réussi à l'appliquer d'une manière ou d'une autre sur le seul filet que j'ai essayé
Claudiu
32
Intéressant. Juste pour continuer, si vous continuez à lire dans ce résumé, il dit que leur meilleur modèle [GoogLeNet128_BN_lim0606] a en fait la couche BN AVANT le ReLU. Ainsi, alors que BN après activation peut améliorer la précision dans un cas isolé, lorsque l'ensemble du modèle est construit, avant d'être le mieux exécuté. Il est probablement possible que le placement de BN après l'activation améliore la précision, mais dépend probablement du problème.
Lucas Ramadan
7
@ En quelque sorte CarlThomé. Voir ce commentaire reddit de ReginaldIII par exemple. Ils déclarent: "BN normalise la distribution des caractéristiques issues d'une convolution, certaines [de] ces caractéristiques peuvent être négatives [et] tronquées par une non-linéarité comme ReLU. Si vous normalisez avant l'activation, vous incluez ces valeurs négatives dans la normalisation immédiatement avant de les extraire de l'espace des caractéristiques. BN après activation normalisera les caractéristiques positives sans les polariser statistiquement avec des caractéristiques qui ne parviennent pas à la couche convolutive suivante. "
mab
60

Ce fil est trompeur. J'ai essayé de commenter la réponse de Lucas Ramadan, mais je n'ai pas encore les bons privilèges, alors je vais simplement mettre ceci ici.

La normalisation par lots fonctionne mieux après la fonction d'activation, et voici ou voici pourquoi: elle a été développée pour empêcher le décalage de covariable interne. Le décalage de covariable interne se produit lorsque la distribution des activationsd'une couche change considérablement tout au long de la formation. La normalisation par lots est utilisée pour que la distribution des entrées (et ces entrées sont littéralement le résultat d'une fonction d'activation) vers une couche spécifique ne change pas au fil du temps en raison des mises à jour des paramètres de chaque lot (ou du moins, lui permet de changer de manière avantageuse). Il utilise des statistiques par lots pour faire la normalisation, puis utilise les paramètres de normalisation par lots (gamma et bêta dans le papier d'origine) "pour s'assurer que la transformation insérée dans le réseau peut représenter la transformation d'identité" (citation du papier original). Mais le fait est que nous essayons de normaliser les entrées sur une couche, donc cela devrait toujours aller immédiatement avant la couche suivante du réseau. Que ce soit ou non

jmancuso
la source
27
Je viens de voir dans la classe deeplearning.ai qu'Andrew Ng dit qu'il y a un débat à ce sujet dans la communauté Deep Learning. Il préfère appliquer la normalisation par lots avant la non-linéarité.
shahensha
3
@kRazzyR Je voulais dire que le professeur Andrew Ng a parlé de ce sujet dans ses cours d'apprentissage en profondeur sur deeplearning.ai Il a dit que la communauté était divisée sur la bonne façon de faire les choses et qu'il préférait appliquer la normalisation par lots avant d'appliquer la non-linéarité.
shahensha
3
@jmancuso, BN est appliqué avant l'activation. D'après le papier lui-même, l'équation est g(BN(Wx + b)), où gest la fonction d'activation.
yashgarg1232
43

Ce fil a un débat considérable sur la question de savoir si BN doit être appliqué avant la non-linéarité de la couche actuelle ou aux activations de la couche précédente.

Bien qu'il n'y ait pas de réponse correcte, les auteurs de la normalisation par lots disent qu'elle doit être appliquée immédiatement avant la non-linéarité du calque actuel. La raison (citée du papier original) -

"Nous ajoutons la transformée BN immédiatement avant la non-linéarité, en normalisant x = Wu + b. Nous aurions pu aussi normaliser les entrées de couche u, mais comme u est probablement la sortie d'une autre non-linéarité, la forme de sa distribution est susceptible de changer pendant formation, et contraindre ses premier et deuxième moments n'éliminerait pas le changement de covariable. En revanche, Wu + b est plus susceptible d'avoir une distribution symétrique et non clairsemée, c'est-à-dire «plus gaussienne» (Hyv¨arinen & Oja, 2000) ; sa normalisation est susceptible de produire des activations avec une distribution stable. "

user12340
la source
3
Dans ma propre expérience personnelle, cela ne fait pas une énorme différence, mais toutes choses étant égales par ailleurs, j'ai toujours vu BN fonctionner légèrement mieux lorsque la normalisation par lots est appliquée avant la non-linéarité (avant la fonction d'activation).
Brad Hesse
31

Keras prend désormais en charge l' use_bias=Falseoption, nous pouvons donc enregistrer des calculs en écrivant comme

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

ou

model.add(Convolution2D(64, 3, 3, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('relu'))
dontloo
la source
hows model.add(BatchNormalization())different frommodel.add(BatchNormalization(axis=bn_axis))
kRazzy R
@kRazzR cela ne diffère pas si vous utilisez tensorflowcomme backend. Il est écrit ici car il l'a copié à partir du keras.applicationsmodule, où bn_axisdoit être spécifié afin de prendre en charge les formats channels_firstet channels_last.
ldavid
9
Quelqu'un peut-il expliquer en quoi cela se rapporte à la question du PO? (Je suis plutôt débutant avec les NN alors peut-être que quelque chose me manque.)
Pepacz
30

C'est presque devenu une tendance maintenant d'avoir un Conv2Dsuivi d'un ReLusuivi d'un BatchNormalizationcalque. J'ai donc créé une petite fonction pour les appeler tous à la fois. Rend la définition du modèle beaucoup plus propre et plus facile à lire.

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
la source
7
peut-être pousser ça à Keras?
sachinruk
6

C'est un autre type de calque, vous devez donc l'ajouter en tant que calque à un endroit approprié de votre modèle

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

Voir un exemple ici: https://github.com/fchollet/keras/blob/master/examples/kaggle_otto_nn.py

Pavel Surmenok
la source
1
Après avoir ajouté BatchNormalization, val_acc a cessé d'augmenter à chaque époque. Le val_acc est resté stagnant au même nombre après chaque époque après avoir ajouté BatchNormalization. Je pensais que la normalisation par lots était censée augmenter le val_acc. Comment savoir si cela fonctionne correctement? Savez-vous ce qui a pu causer cela?
pr338 le
malheureusement le lien n'est plus valide :(
user2324712
Il existe des copies de cet exemple dans les fourchettes de Keras (par exemple github.com/WenchenLi/kaggle/blob/master/otto/keras/… ), mais je ne sais pas pourquoi il a été supprimé du dépôt Keras d'origine, et si le le code est compatible avec les dernières versions de Keras.
Pavel Surmenok
4

La normalisation par lots est utilisée pour normaliser la couche d'entrée ainsi que les couches cachées en ajustant la moyenne et la mise à l'échelle des activations. En raison de cet effet de normalisation avec une couche supplémentaire dans les réseaux de neurones profonds, le réseau peut utiliser un taux d'apprentissage plus élevé sans disparaître ou exploser de gradients. En outre, la normalisation par lots régularise le réseau de sorte qu'il est plus facile à généraliser, et il n'est donc pas nécessaire d'utiliser le décrochage pour atténuer le surajustement.

Juste après avoir calculé la fonction linéaire en utilisant, par exemple, Dense () ou Conv2D () dans Keras, nous utilisons BatchNormalization () qui calcule la fonction linéaire dans un calque, puis nous ajoutons la non-linéarité au calque en utilisant Activation ().

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)

Comment la normalisation des lots est-elle appliquée?

Supposons que nous ayons entré a [l-1] dans une couche l. Nous avons également les poids W [l] et l'unité de biais b [l] pour la couche l. Soit a [l] le vecteur d'activation calculé (c'est-à-dire après addition de la non-linéarité) pour la couche l et z [l] le vecteur avant d'ajouter la non-linéarité

  1. En utilisant a [l-1] et W [l] nous pouvons calculer z [l] pour la couche l
  2. Habituellement, dans la propagation par anticipation, nous ajouterons l'unité de biais au z [l] à ce stade comme ceci z [l] + b [l], mais dans la normalisation par lots, cette étape d'ajout de b [l] n'est pas requise et non Le paramètre b [l] est utilisé.
  3. Calculer les moyennes de z [l] et les soustraire de chaque élément
  4. Divisez (z [l] - moyenne) en utilisant l'écart type. Appelez-le Z_temp [l]
  5. Définissez maintenant de nouveaux paramètres γ et β qui changeront l'échelle du calque caché comme suit:

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

Dans cet extrait de code, le Dense () prend le a [l-1], utilise W [l] et calcule z [l]. Ensuite, le BatchNormalization () immédiat exécutera les étapes ci-dessus pour donner z_norm [l]. Et puis l'activation immédiate () calculera tanh (z_norm [l]) pour donner un [l] ie

a[l] = tanh(z_norm[l])
Aishwarya Radhakrishnan
la source