La couche de normalisation par lots de Keras est brisée par PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

La couche de normalisation par lots de Keras est rompue

MISE À JOUR : Malheureusement, ma Pull-Request vers Keras qui a modifié le comportement de la couche de normalisation par lots n'a pas été acceptée. Vous pouvez lire les détails ici. Pour ceux d'entre vous qui sont assez courageux pour jouer avec des implémentations personnalisées, vous pouvez trouver le code dans ma succursale. Je pourrais le maintenir et le fusionner avec la dernière version stable de Keras (2.1.6, 2.2.2 ainsi que 2.2.4) aussi longtemps que je l'utilise mais aucune promesse.

La plupart des personnes qui travaillent dans le Deep Learning ont utilisé ou entendu parler de Keras. Pour ceux d'entre vous qui ne l'ont pas encore fait, il s'agit d'une excellente bibliothèque qui résume les frameworks de Deep Learning sous-jacents tels que TensorFlow, Theano et CNTK et fournit un API de haut niveau pour la formation des ANN. Il est facile à utiliser, permet un prototypage rapide et dispose d’une communauté active et conviviale. Je l'utilise beaucoup et je contribue périodiquement au projet depuis un certain temps et je le recommande vivement à tous ceux qui souhaitent travailler sur le Deep Learning.

Même si Keras m'a rendu la vie plus facile, j'ai souvent été mordu par le comportement étrange de la couche de normalisation par lots. Son comportement par défaut a changé au fil du temps, néanmoins il pose toujours des problèmes à de nombreux utilisateurs et par conséquent il existe plusieurs problèmes liés. questions ouvertes sur Github. Dans cet article de blog, je vais essayer d'expliquer pourquoi la couche BatchNormalization de Keras ne fonctionne pas bien avec Transfer Learning, je fournirai le code qui résout le problème et je donnerai des exemples avec les résultats de l'étude. pièce.

Dans les sous-sections ci-dessous, je fournis une introduction sur la façon dont l'apprentissage par transfert est utilisé dans le Deep Learning, qu'est-ce que la couche de normalisation par lots, comment fonctionne learning_phase et comment Keras a modifié le comportement du BN au fil du temps. Si vous les connaissez déjà, vous pouvez passer directement à la section 2 en toute sécurité.

1.1 L'utilisation de l'apprentissage par transfert est cruciale pour le Deep Learning

L’une des raisons pour lesquelles le Deep Learning a été critiqué dans le passé est qu’il nécessite trop de données. Ce n'est pas toujours vrai; Il existe plusieurs techniques pour remédier à cette limitation, dont l'une est l'apprentissage par transfert.

Supposons que vous travaillez sur une application de vision par ordinateur et que vous souhaitiez créer un classificateur qui distingue les chats des chiens. Vous n’avez pas réellement besoin de millions d’images de chat/chien pour entraîner le modèle. Au lieu de cela, vous pouvez utiliser un classificateur pré-entraîné et affiner les convolutions supérieures avec moins de données. L'idée derrière cela est que puisque le modèle pré-entraîné a été adapté aux images, les convolutions inférieures peuvent reconnaître des caractéristiques telles que des lignes, des bords et d'autres modèles utiles, ce qui signifie que vous pouvez utiliser ses poids soit comme bonnes valeurs d'initialisation, soit recycler partiellement le réseau avec vos données. .
La couche de normalisation par lots de Keras est brisée par PlatoBlockchain Data Intelligence. Recherche verticale. Aï.
Keras est livré avec plusieurs modèles pré-entraînés et des exemples faciles à utiliser sur la façon d'affiner les modèles. Vous pouvez en savoir plus sur le Documentation.

1.2 Qu'est-ce que la couche de normalisation par lots ?

La couche de normalisation par lots a été introduite en 2014 par Ioffe et Szegedy. Il résout le problème du gradient de disparition en standardisant la sortie de la couche précédente, accélère la formation en réduisant le nombre d'itérations requises et permet la formation de réseaux neuronaux plus profonds. Expliquer exactement comment cela fonctionne dépasse le cadre de cet article, mais je vous encourage fortement à lire le papier original. Une explication trop simpliste est qu'il redimensionne l'entrée en soustrayant sa moyenne et en divisant par son écart type ; il peut également apprendre à annuler la transformation si nécessaire.
La couche de normalisation par lots de Keras est brisée par PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

1.3 Qu'est-ce que la phase d'apprentissage dans Keras ?

Certaines couches fonctionnent différemment en mode formation et inférence. Les exemples les plus notables sont les couches de normalisation par lots et d'abandon. Dans le cas de BN, lors de la formation, nous utilisons la moyenne et la variance du mini-lot pour redimensionner l'entrée. D'un autre côté, lors de l'inférence, nous utilisons la moyenne mobile et la variance estimées lors de la formation.

Keras sait dans quel mode s'exécuter car il dispose d'un mécanisme intégré appelé phase_d'apprentissage. La phase d'apprentissage contrôle si le réseau est en mode train ou test. S'il n'est pas défini manuellement par l'utilisateur, pendant fit() le réseau fonctionne avec learning_phase=1 (mode train). Lors de la production de prédictions (par exemple lorsque nous appelons les méthodes prédire() & évaluer() ou à l'étape de validation de fit()) le réseau fonctionne avec learning_phase=0 (mode test). Même si cela n'est pas recommandé, l'utilisateur peut également modifier statiquement la phase d'apprentissage en une valeur spécifique, mais cela doit se produire avant qu'un modèle ou un tenseur ne soit ajouté au graphique. Si la phase d'apprentissage est définie de manière statique, Keras sera verrouillé quel que soit le mode sélectionné par l'utilisateur.

1.4 Comment Keras a-t-il mis en œuvre la normalisation par lots au fil du temps ?

Keras a modifié le comportement de la normalisation par lots à plusieurs reprises, mais la mise à jour importante la plus récente a eu lieu dans Keras 2.1.3. Avant la version 2.1.3, lorsque la couche BN était gelée (entraînable = False), elle continuait à mettre à jour ses statistiques de lots, ce qui causait des maux de tête épiques à ses utilisateurs.

Ce n’était pas seulement une politique étrange, c’était en fait une mauvaise politique. Imaginez qu'une couche BN existe entre les convolutions ; si le calque est gelé, aucun changement ne devrait lui arriver. Si nous mettons à jour partiellement ses poids et que les couches suivantes sont également gelées, elles n'auront jamais la possibilité de s'adapter aux mises à jour des statistiques des mini-lots, ce qui entraînera une erreur plus élevée. Heureusement, à partir de la version 2.1.3, lorsqu'une couche BN est gelée, elle ne met plus à jour ses statistiques. Mais est-ce suffisant ? Pas si vous utilisez Transfer Learning.

Ci-dessous, je décris exactement quel est le problème et j'esquisse la mise en œuvre technique pour le résoudre. Je fournis également quelques exemples pour montrer les effets sur la précision du modèle avant et après le pièce est appliqué.

2.1 Description technique du problème

Le problème avec l'implémentation actuelle de Keras est que lorsqu'une couche BN est gelée, elle continue d'utiliser les statistiques de mini-batch pendant l'entraînement. Je pense qu'une meilleure approche lorsque le BN est gelé consiste à utiliser la moyenne mobile et la variance qu'il a apprises au cours de la formation. Pourquoi? Pour les mêmes raisons pour lesquelles les statistiques du mini-batch ne doivent pas être mises à jour lorsque la couche est gelée : cela peut conduire à de mauvais résultats car les couches suivantes ne sont pas correctement entraînées.

Supposons que vous construisez un modèle de vision par ordinateur mais que vous ne disposez pas de suffisamment de données, vous décidez donc d'utiliser l'un des CNN pré-entraînés de Keras et de l'affiner. Malheureusement, ce faisant, vous n'obtenez aucune garantie que la moyenne et la variance de votre nouvel ensemble de données à l'intérieur des couches BN seront similaires à celles de l'ensemble de données d'origine. N'oubliez pas qu'à l'heure actuelle, lors de la formation, votre réseau utilisera toujours les statistiques des mini-batchs, que la couche BN soit gelée ou non ; également lors de l'inférence, vous utiliserez les statistiques précédemment apprises des couches BN gelées. En conséquence, si vous affinez les couches supérieures, leurs poids seront ajustés à la moyenne/variance de la neufs base de données. Néanmoins, lors de l'inférence, ils recevront des données mises à l'échelle différemment parce que la moyenne/variance du original l’ensemble de données sera utilisé.
La couche de normalisation par lots de Keras est brisée par PlatoBlockchain Data Intelligence. Recherche verticale. Aï.
Ci-dessus, je propose une architecture simpliste (et irréaliste) à des fins de démonstration. Supposons que nous affinions le modèle depuis la convolution k+1 jusqu'au haut du réseau (côté droit) et que nous gardions gelé le bas (côté gauche). Pendant l'entraînement, toutes les couches BN de 1 à k utiliseront la moyenne/variance de vos données d'entraînement. Cela aura des effets négatifs sur les ReLU gelés si la moyenne et la variance de chaque BN ne sont pas proches de celles apprises lors de la pré-formation. Cela entraînera également que le reste du réseau (à partir de CONV k+1 et versions ultérieures) soit formé avec des entrées qui ont des échelles différentes par rapport à ce qui recevra lors de l'inférence. Pendant la formation, votre réseau peut s'adapter à ces changements, néanmoins dès que vous passez en mode prédiction, Keras utilisera différentes statistiques de standardisation, ce qui accélérera la distribution des entrées des couches suivantes conduisant à de mauvais résultats.

2.2 Comment détecter si vous êtes concerné ?

Une façon de le détecter est de définir statiquement la phase d'apprentissage de Keras sur 1 (mode train) et sur 0 (mode test) et d'évaluer votre modèle dans chaque cas. S'il existe une différence significative de précision sur le même ensemble de données, vous êtes concerné par le problème. Il convient de souligner qu’en raison de la manière dont le mécanisme learning_phase est implémenté dans Keras, il n’est généralement pas conseillé de s’en mêler. Les modifications apportées à la phase d'apprentissage n'auront aucun effet sur les modèles déjà compilés et utilisés ; comme vous pouvez le voir sur les exemples des sous-sections suivantes, la meilleure façon de procéder est de commencer par une session propre et de modifier la phase d'apprentissage avant qu'un tenseur ne soit défini dans le graphique.

Une autre façon de détecter le problème lorsque vous travaillez avec des classificateurs binaires consiste à vérifier la précision et l'AUC. Si la précision est proche de 50 % mais que l'AUC est proche de 1 (et que vous observez également des différences entre le mode train/test sur le même ensemble de données), il se peut que les probabilités soient hors d'échelle en raison des statistiques BN. De même, pour la régression, vous pouvez utiliser la corrélation MSE et Spearman pour la détecter.

2.3 Comment pouvons-nous y remédier ?

Je pense que le problème peut être résolu si les couches BN gelées ne sont en réalité que cela : verrouillées en permanence en mode test. Du point de vue de la mise en œuvre, l'indicateur pouvant être entraîné doit faire partie du graphe de calcul et le comportement du BN doit dépendre non seulement de la phase d'apprentissage mais également de la valeur de la propriété pouvant être entraînée. Vous pouvez retrouver les détails de ma mise en œuvre sur Github.

En appliquant le correctif ci-dessus, lorsqu'une couche BN est gelée, elle n'utilisera plus les statistiques de mini-batch mais utilisera plutôt celles apprises lors de la formation. En conséquence, il n’y aura aucune différence entre les modes d’entraînement et de test, ce qui entraînera une précision accrue. Évidemment, lorsque la couche BN n'est pas gelée, elle continuera à utiliser les statistiques du mini-batch pendant l'entraînement.

2.4 Évaluation des effets du patch

Même si j'ai récemment écrit l'implémentation ci-dessus, l'idée qui la sous-tend est fortement testée sur des problèmes du monde réel en utilisant diverses solutions de contournement qui ont le même effet. Par exemple, l'écart entre les modes de formation et de test peut être évité en divisant le réseau en deux parties (gelée et non gelée) et en effectuant une formation en cache (en transmettant les données une fois via le modèle gelé, puis en les utilisant pour former le réseau non gelé). Néanmoins, comme le « croyez-moi, j’ai déjà fait cela » n’a généralement aucun poids, je donne ci-dessous quelques exemples qui montrent les effets de la nouvelle mise en œuvre dans la pratique.

Voici quelques points importants concernant l’expérience :

  1. J'utiliserai une infime quantité de données pour surajuster intentionnellement le modèle et j'entraînerai et validerai le modèle sur le même ensemble de données. Ce faisant, je m'attends à une précision presque parfaite et à des performances identiques sur l'ensemble de données d'entraînement/validation.
  2. Si lors de la validation, j'obtiens une précision nettement inférieure sur le même ensemble de données, j'aurai une indication claire que la politique actuelle du BN affecte négativement les performances du modèle lors de l'inférence.
  3. Tout prétraitement aura lieu en dehors des générateurs. Ceci est fait pour contourner un bogue introduit dans la v2.1.5 (actuellement corrigé dans la prochaine v2.1.6 et le dernier master).
  4. Nous obligerons Keras à utiliser différentes phases d'apprentissage lors de l'évaluation. Si nous repérons des différences entre l’exactitude déclarée, nous saurons que nous sommes affectés par la politique actuelle du NE.

Le code de l'expérience est présenté ci-dessous :

import numpy as np
from keras.datasets import cifar10
from scipy.misc import imresize

from keras.preprocessing.image import ImageDataGenerator
from keras.applications.resnet50 import ResNet50, preprocess_input
from keras.models import Model, load_model
from keras.layers import Dense, Flatten
from keras import backend as K


seed = 42
epochs = 10
records_per_class = 100

# We take only 2 classes from CIFAR10 and a very small sample to intentionally overfit the model.
# We will also use the same data for train/test and expect that Keras will give the same accuracy.
(x, y), _ = cifar10.load_data()

def filter_resize(category):
   # We do the preprocessing here instead in the Generator to get around a bug on Keras 2.1.5.
   return [preprocess_input(imresize(img, (224,224)).astype('float')) for img in x[y.flatten()==category][:records_per_class]]

x = np.stack(filter_resize(3)+filter_resize(5))
records_per_class = x.shape[0] // 2
y = np.array([[1,0]]*records_per_class + [[0,1]]*records_per_class)


# We will use a pre-trained model and finetune the top layers.
np.random.seed(seed)
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
l = Flatten()(base_model.output)
predictions = Dense(2, activation='softmax')(l)
model = Model(inputs=base_model.input, outputs=predictions)

for layer in model.layers[:140]:
   layer.trainable = False

for layer in model.layers[140:]:
   layer.trainable = True

model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit_generator(ImageDataGenerator().flow(x, y, seed=42), epochs=epochs, validation_data=ImageDataGenerator().flow(x, y, seed=42))

# Store the model on disk
model.save('tmp.h5')


# In every test we will clear the session and reload the model to force Learning_Phase values to change.
print('DYNAMIC LEARNING_PHASE')
K.clear_session()
model = load_model('tmp.h5')
# This accuracy should match exactly the one of the validation set on the last iteration.
print(model.evaluate_generator(ImageDataGenerator().flow(x, y, seed=42)))


print('STATIC LEARNING_PHASE = 0')
K.clear_session()
K.set_learning_phase(0)
model = load_model('tmp.h5')
# Again the accuracy should match the above.
print(model.evaluate_generator(ImageDataGenerator().flow(x, y, seed=42)))


print('STATIC LEARNING_PHASE = 1')
K.clear_session()
K.set_learning_phase(1)
model = load_model('tmp.h5')
# The accuracy will be close to the one of the training set on the last iteration.
print(model.evaluate_generator(ImageDataGenerator().flow(x, y, seed=42)))

Vérifions les résultats sur Keras v2.1.5 :

Epoch 1/10
1/7 [===>..........................] - ETA: 25s - loss: 0.8751 - acc: 0.5312
2/7 [=======>......................] - ETA: 11s - loss: 0.8594 - acc: 0.4531
3/7 [===========>..................] - ETA: 7s - loss: 0.8398 - acc: 0.4688 
4/7 [================>.............] - ETA: 4s - loss: 0.8467 - acc: 0.4844
5/7 [====================>.........] - ETA: 2s - loss: 0.7904 - acc: 0.5437
6/7 [========================>.....] - ETA: 1s - loss: 0.7593 - acc: 0.5625
7/7 [==============================] - 12s 2s/step - loss: 0.7536 - acc: 0.5744 - val_loss: 0.6526 - val_acc: 0.6650

Epoch 2/10
1/7 [===>..........................] - ETA: 4s - loss: 0.3881 - acc: 0.8125
2/7 [=======>......................] - ETA: 3s - loss: 0.3945 - acc: 0.7812
3/7 [===========>..................] - ETA: 2s - loss: 0.3956 - acc: 0.8229
4/7 [================>.............] - ETA: 1s - loss: 0.4223 - acc: 0.8047
5/7 [====================>.........] - ETA: 1s - loss: 0.4483 - acc: 0.7812
6/7 [========================>.....] - ETA: 0s - loss: 0.4325 - acc: 0.7917
7/7 [==============================] - 8s 1s/step - loss: 0.4095 - acc: 0.8089 - val_loss: 0.4722 - val_acc: 0.7700

Epoch 3/10
1/7 [===>..........................] - ETA: 4s - loss: 0.2246 - acc: 0.9375
2/7 [=======>......................] - ETA: 3s - loss: 0.2167 - acc: 0.9375
3/7 [===========>..................] - ETA: 2s - loss: 0.2260 - acc: 0.9479
4/7 [================>.............] - ETA: 2s - loss: 0.2179 - acc: 0.9375
5/7 [====================>.........] - ETA: 1s - loss: 0.2356 - acc: 0.9313
6/7 [========================>.....] - ETA: 0s - loss: 0.2392 - acc: 0.9427
7/7 [==============================] - 8s 1s/step - loss: 0.2288 - acc: 0.9456 - val_loss: 0.4282 - val_acc: 0.7800

Epoch 4/10
1/7 [===>..........................] - ETA: 4s - loss: 0.2183 - acc: 0.9688
2/7 [=======>......................] - ETA: 3s - loss: 0.1899 - acc: 0.9844
3/7 [===========>..................] - ETA: 2s - loss: 0.1887 - acc: 0.9792
4/7 [================>.............] - ETA: 1s - loss: 0.1995 - acc: 0.9531
5/7 [====================>.........] - ETA: 1s - loss: 0.1932 - acc: 0.9625
6/7 [========================>.....] - ETA: 0s - loss: 0.1819 - acc: 0.9688
7/7 [==============================] - 8s 1s/step - loss: 0.1743 - acc: 0.9747 - val_loss: 0.3778 - val_acc: 0.8400

Epoch 5/10
1/7 [===>..........................] - ETA: 3s - loss: 0.0973 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0828 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0851 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0897 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0928 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0936 - acc: 1.0000
7/7 [==============================] - 8s 1s/step - loss: 0.1337 - acc: 0.9838 - val_loss: 0.3916 - val_acc: 0.8100

Epoch 6/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0747 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0852 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0812 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0831 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0779 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0766 - acc: 1.0000
7/7 [==============================] - 8s 1s/step - loss: 0.0813 - acc: 1.0000 - val_loss: 0.3637 - val_acc: 0.8550

Epoch 7/10
1/7 [===>..........................] - ETA: 1s - loss: 0.2478 - acc: 0.8750
2/7 [=======>......................] - ETA: 2s - loss: 0.1966 - acc: 0.9375
3/7 [===========>..................] - ETA: 2s - loss: 0.1528 - acc: 0.9583
4/7 [================>.............] - ETA: 1s - loss: 0.1300 - acc: 0.9688
5/7 [====================>.........] - ETA: 1s - loss: 0.1193 - acc: 0.9750
6/7 [========================>.....] - ETA: 0s - loss: 0.1196 - acc: 0.9792
7/7 [==============================] - 8s 1s/step - loss: 0.1084 - acc: 0.9838 - val_loss: 0.3546 - val_acc: 0.8600

Epoch 8/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0539 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.0900 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0815 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0740 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0700 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0701 - acc: 1.0000
7/7 [==============================] - 8s 1s/step - loss: 0.0695 - acc: 1.0000 - val_loss: 0.3269 - val_acc: 0.8600

Epoch 9/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0306 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0377 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0898 - acc: 0.9583
4/7 [================>.............] - ETA: 1s - loss: 0.0773 - acc: 0.9688
5/7 [====================>.........] - ETA: 1s - loss: 0.0742 - acc: 0.9750
6/7 [========================>.....] - ETA: 0s - loss: 0.0708 - acc: 0.9792
7/7 [==============================] - 8s 1s/step - loss: 0.0659 - acc: 0.9838 - val_loss: 0.3604 - val_acc: 0.8600

Epoch 10/10
1/7 [===>..........................] - ETA: 3s - loss: 0.0354 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0381 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0354 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0828 - acc: 0.9688
5/7 [====================>.........] - ETA: 1s - loss: 0.0791 - acc: 0.9750
6/7 [========================>.....] - ETA: 0s - loss: 0.0794 - acc: 0.9792
7/7 [==============================] - 8s 1s/step - loss: 0.0704 - acc: 0.9838 - val_loss: 0.3615 - val_acc: 0.8600

DYNAMIC LEARNING_PHASE
[0.3614931714534759, 0.86]

STATIC LEARNING_PHASE = 0
[0.3614931714534759, 0.86]

STATIC LEARNING_PHASE = 1
[0.025861846953630446, 1.0]

Comme nous pouvons le voir ci-dessus, pendant la formation, le modèle apprend très bien les données et atteint une précision presque parfaite sur l'ensemble de formation. Toujours à la fin de chaque itération, lors de l'évaluation du modèle sur le même ensemble de données, nous obtenons des différences significatives en termes de perte et de précision. Notez que nous ne devrions pas obtenir cela ; nous avons intentionnellement surajusté le modèle sur l'ensemble de données spécifique et les ensembles de données de formation/validation sont identiques.

Une fois la formation terminée, nous évaluons le modèle en utilisant 3 configurations de phase d'apprentissage différentes : Dynamique, Statique = 0 (mode test) et Statique = 1 (mode formation). Comme nous pouvons le voir, les deux premières configurations fourniront des résultats identiques en termes de perte et de précision et leur valeur correspond à la précision rapportée du modèle sur l'ensemble de validation lors de la dernière itération. Néanmoins, une fois que l’on passe en mode entraînement, on observe un écart (amélioration) massif. Pourquoi ça ? Comme nous l'avons dit précédemment, les poids du réseau sont ajustés en s'attendant à recevoir des données mises à l'échelle avec la moyenne/variance des données d'entraînement. Malheureusement, ces statistiques sont différentes de celles stockées dans les couches BN. Les couches BN étant gelées, ces statistiques n’ont jamais été mises à jour. Cet écart entre les valeurs des statistiques BN entraîne une détérioration de la précision lors de l'inférence.

Voyons ce qui se passe une fois que nous appliquons le pièce:

Epoch 1/10
1/7 [===>..........................] - ETA: 26s - loss: 0.9992 - acc: 0.4375
2/7 [=======>......................] - ETA: 12s - loss: 1.0534 - acc: 0.4375
3/7 [===========>..................] - ETA: 7s - loss: 1.0592 - acc: 0.4479 
4/7 [================>.............] - ETA: 4s - loss: 0.9618 - acc: 0.5000
5/7 [====================>.........] - ETA: 2s - loss: 0.8933 - acc: 0.5250
6/7 [========================>.....] - ETA: 1s - loss: 0.8638 - acc: 0.5417
7/7 [==============================] - 13s 2s/step - loss: 0.8357 - acc: 0.5570 - val_loss: 0.2414 - val_acc: 0.9450

Epoch 2/10
1/7 [===>..........................] - ETA: 4s - loss: 0.2331 - acc: 0.9688
2/7 [=======>......................] - ETA: 2s - loss: 0.3308 - acc: 0.8594
3/7 [===========>..................] - ETA: 2s - loss: 0.3986 - acc: 0.8125
4/7 [================>.............] - ETA: 1s - loss: 0.3721 - acc: 0.8281
5/7 [====================>.........] - ETA: 1s - loss: 0.3449 - acc: 0.8438
6/7 [========================>.....] - ETA: 0s - loss: 0.3168 - acc: 0.8646
7/7 [==============================] - 9s 1s/step - loss: 0.3165 - acc: 0.8633 - val_loss: 0.1167 - val_acc: 0.9950

Epoch 3/10
1/7 [===>..........................] - ETA: 1s - loss: 0.2457 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.2592 - acc: 0.9688
3/7 [===========>..................] - ETA: 2s - loss: 0.2173 - acc: 0.9688
4/7 [================>.............] - ETA: 1s - loss: 0.2122 - acc: 0.9688
5/7 [====================>.........] - ETA: 1s - loss: 0.2003 - acc: 0.9688
6/7 [========================>.....] - ETA: 0s - loss: 0.1896 - acc: 0.9740
7/7 [==============================] - 9s 1s/step - loss: 0.1835 - acc: 0.9773 - val_loss: 0.0678 - val_acc: 1.0000

Epoch 4/10
1/7 [===>..........................] - ETA: 1s - loss: 0.2051 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.1652 - acc: 0.9844
3/7 [===========>..................] - ETA: 2s - loss: 0.1423 - acc: 0.9896
4/7 [================>.............] - ETA: 1s - loss: 0.1289 - acc: 0.9922
5/7 [====================>.........] - ETA: 1s - loss: 0.1225 - acc: 0.9938
6/7 [========================>.....] - ETA: 0s - loss: 0.1149 - acc: 0.9948
7/7 [==============================] - 9s 1s/step - loss: 0.1060 - acc: 0.9955 - val_loss: 0.0455 - val_acc: 1.0000

Epoch 5/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0769 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.0846 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0797 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0736 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0914 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0858 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0808 - acc: 1.0000 - val_loss: 0.0346 - val_acc: 1.0000

Epoch 6/10
1/7 [===>..........................] - ETA: 1s - loss: 0.1267 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.1039 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0893 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0780 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0758 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0789 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0738 - acc: 1.0000 - val_loss: 0.0248 - val_acc: 1.0000

Epoch 7/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0344 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0385 - acc: 1.0000
3/7 [===========>..................] - ETA: 3s - loss: 0.0467 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0445 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0446 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0429 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0421 - acc: 1.0000 - val_loss: 0.0202 - val_acc: 1.0000

Epoch 8/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0319 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0300 - acc: 1.0000
3/7 [===========>..................] - ETA: 3s - loss: 0.0320 - acc: 1.0000
4/7 [================>.............] - ETA: 2s - loss: 0.0307 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0303 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0291 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0358 - acc: 1.0000 - val_loss: 0.0167 - val_acc: 1.0000

Epoch 9/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0246 - acc: 1.0000
2/7 [=======>......................] - ETA: 3s - loss: 0.0255 - acc: 1.0000
3/7 [===========>..................] - ETA: 3s - loss: 0.0258 - acc: 1.0000
4/7 [================>.............] - ETA: 2s - loss: 0.0250 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0252 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0260 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0327 - acc: 1.0000 - val_loss: 0.0143 - val_acc: 1.0000

Epoch 10/10
1/7 [===>..........................] - ETA: 4s - loss: 0.0251 - acc: 1.0000
2/7 [=======>......................] - ETA: 2s - loss: 0.0228 - acc: 1.0000
3/7 [===========>..................] - ETA: 2s - loss: 0.0217 - acc: 1.0000
4/7 [================>.............] - ETA: 1s - loss: 0.0249 - acc: 1.0000
5/7 [====================>.........] - ETA: 1s - loss: 0.0244 - acc: 1.0000
6/7 [========================>.....] - ETA: 0s - loss: 0.0239 - acc: 1.0000
7/7 [==============================] - 9s 1s/step - loss: 0.0290 - acc: 1.0000 - val_loss: 0.0127 - val_acc: 1.0000

DYNAMIC LEARNING_PHASE
[0.012697912137955427, 1.0]

STATIC LEARNING_PHASE = 0
[0.012697912137955427, 1.0]

STATIC LEARNING_PHASE = 1
[0.01744014158844948, 1.0]

Tout d’abord, on observe que le réseau converge beaucoup plus rapidement et atteint une précision parfaite. Nous constatons également qu'il n'y a plus d'écart en termes de précision lorsque nous basculons entre différentes valeurs de phase d'apprentissage.

2.5 Comment le correctif fonctionne-t-il sur un ensemble de données réel ?

Alors, comment le patch fonctionne-t-il sur une expérience plus réaliste ? Utilisons ResNet50 pré-entraîné de Keras (adapté à l'origine sur imagenet), supprimons la couche de classification supérieure et affinons-la avec et sans le patch et comparons les résultats. Pour les données, nous utiliserons CIFAR10 (la répartition standard train/test fournie par Keras) et nous redimensionnerons les images à 224×224 pour les rendre compatibles avec la taille d'entrée du ResNet50.

Nous ferons 10 époques pour entraîner la couche de classification supérieure à l'aide de RSMprop, puis nous en ferons 5 autres pour tout affiner après la 139ème couche en utilisant SGD (lr = 1e-4, momentum = 0.9). Sans le patch, notre modèle atteint une précision de 87.44 %. En utilisant le patch, nous obtenons une précision de 92.36%, soit près de 5 points de plus.

2.6 Devrions-nous appliquer le même correctif à d'autres calques tels que Dropout ?

La normalisation par lots n'est pas la seule couche qui fonctionne différemment entre les modes train et test. Le dropout et ses variantes ont également le même effet. Devons-nous appliquer la même politique à toutes ces couches ? Je ne crois pas (même si j'aimerais entendre votre avis à ce sujet). La raison en est que Dropout est utilisé pour éviter le surapprentissage, donc le verrouiller de manière permanente en mode prédiction pendant l'entraînement irait à l'encontre de son objectif. Qu'en penses-tu?

Je crois fermement que cette divergence doit être résolue à Keras. J'ai constaté des effets encore plus profonds (de 100 % à 50 % de précision) dans des applications réelles provoqués par ce problème. je prévoyez d'envoyer déjà envoyé un PR à Keras avec le correctif et j'espère qu'il sera accepté.

Si vous avez aimé cet article de blog, prenez un moment pour le partager sur Facebook ou Twitter. 🙂

Horodatage:

Plus de Boîte de données