Die Batch-Normalisierungsschicht von Keras ist eine kaputte PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.

Die Batch-Normalisierungsschicht von Keras ist unterbrochen

UPDATE: Leider wurde meine Pull-Anfrage an Keras, die das Verhalten der Batch-Normalisierungsschicht geändert hat, nicht akzeptiert. Sie können die Details lesen hier. Für diejenigen unter Ihnen, die mutig genug sind, sich mit benutzerdefinierten Implementierungen herumzuschlagen, finden Sie den Code in mein Zweig. Ich könnte es pflegen und mit der neuesten stabilen Version von Keras zusammenführen (2.1.6, 2.2.2 und 2.2.4) solange ich es benutze aber keine versprechen.

Die meisten Leute, die in Deep Learning arbeiten, haben entweder benutzt oder davon gehört Keras. Für diejenigen unter Ihnen, die dies nicht getan haben, ist es eine großartige Bibliothek, die die zugrunde liegenden Deep Learning-Frameworks wie TensorFlow, Theano und CNTK abstrahiert und eine bereitstellt High-Level-API für das Training von ANNs. Es ist einfach zu bedienen, ermöglicht schnelles Prototyping und hat eine freundliche, aktive Community. Ich benutze es seit einiger Zeit intensiv und trage regelmäßig zum Projekt bei. Ich kann es definitiv jedem empfehlen, der an Deep Learning arbeiten möchte.

Obwohl Keras mir das Leben leichter gemacht hat, wurde ich ziemlich oft von dem merkwürdigen Verhalten der Batch-Normalisierungsschicht gebissen. Das Standardverhalten hat sich im Laufe der Zeit geändert, dennoch verursacht es vielen Benutzern immer noch Probleme, und infolgedessen gibt es mehrere verwandte offene Punkte auf Github. In diesem Blog-Beitrag werde ich versuchen, einen Fall zu erstellen, warum die BatchNormalization-Ebene von Keras mit Transfer Learning nicht gut funktioniert. Ich werde den Code bereitstellen, der das Problem behebt, und ich werde Beispiele mit den Ergebnissen der geben Flicken.

In den folgenden Unterabschnitten werde ich eine Einführung geben, wie Transfer Learning in Deep Learning verwendet wird, was die Batch-Normalisierungsebene ist, wie learnining_phase funktioniert und wie Keras das BN-Verhalten im Laufe der Zeit geändert hat. Wenn Sie diese bereits kennen, können Sie sicher direkt zu Abschnitt 2 springen.

1.1 Die Verwendung von Transfer Learning ist für Deep Learning von entscheidender Bedeutung

Einer der Gründe, warum Deep Learning in der Vergangenheit kritisiert wurde, ist, dass es zu viele Daten benötigt. Dies ist nicht immer wahr; Es gibt verschiedene Techniken, um diese Einschränkung zu beheben. Eine davon ist das Transferlernen.

Angenommen, Sie arbeiten an einer Computer Vision-Anwendung und möchten einen Klassifikator erstellen, der Katzen von Hunden unterscheidet. Sie brauchen nicht wirklich Millionen von Katzen- / Hundebildern, um das Modell zu trainieren. Stattdessen können Sie einen vorab trainierten Klassifikator verwenden und die Top-Windungen mit weniger Daten optimieren. Die Idee dahinter ist, dass die unteren Windungen Merkmale wie Linien, Kanten und andere nützliche Muster erkennen können, da das vorab trainierte Modell auf Bilder angepasst wurde. Dies bedeutet, dass Sie seine Gewichte entweder als gute Initialisierungswerte verwenden oder das Netzwerk teilweise mit Ihren Daten neu trainieren können .
Die Batch-Normalisierungsschicht von Keras ist eine kaputte PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.
Keras enthält mehrere vorgefertigte Modelle und benutzerfreundliche Beispiele für die Feinabstimmung von Modellen. Sie können mehr auf der lesen Dokumentation.

1.2 Was ist die Batch-Normalisierungsschicht?

Die Batch-Normalisierungsschicht wurde 2014 von Ioffe und Szegedy eingeführt. Es behebt das Problem des verschwindenden Gradienten durch Standardisierung der Ausgabe der vorherigen Schicht, beschleunigt das Training durch Reduzierung der Anzahl der erforderlichen Iterationen und ermöglicht das Training tieferer neuronaler Netze. Genau zu erklären, wie es funktioniert, würde den Rahmen dieses Beitrags sprengen, aber ich empfehle Ihnen dringend, das zu lesen Original Papier. Eine stark vereinfachte Erklärung ist, dass die Eingabe neu skaliert wird, indem ihr Mittelwert subtrahiert und durch ihre Standardabweichung dividiert wird. Es kann auch lernen, die Transformation bei Bedarf rückgängig zu machen.
Die Batch-Normalisierungsschicht von Keras ist eine kaputte PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.

1.3 Was ist die Lernphase in Keras?

Einige Ebenen arbeiten während des Trainings- und Inferenzmodus unterschiedlich. Die bemerkenswertesten Beispiele sind die Ebenen "Stapelnormalisierung" und "Dropout". Im Fall von BN verwenden wir während des Trainings den Mittelwert und die Varianz des Mini-Batch, um die Eingabe neu zu skalieren. Andererseits verwenden wir während der Inferenz den gleitenden Durchschnitt und die Varianz, die während des Trainings geschätzt wurden.

Keras weiß, in welchem ​​Modus er ausgeführt werden soll, da ein integrierter Mechanismus aufgerufen wird Lernphase. Die Lernphase steuert, ob sich das Netzwerk im Zug- oder Testmodus befindet. Wenn es nicht manuell vom Benutzer festgelegt wird, wird das Netzwerk während fit () mit learning_phase = 1 (Zugmodus) ausgeführt. Während der Erstellung von Vorhersagen (z. B. wenn wir die Predict () & Evaluate () -Methoden aufrufen oder beim Validierungsschritt von fit ()) wird das Netzwerk mit learning_phase = 0 (Testmodus) ausgeführt. Obwohl dies nicht empfohlen wird, kann der Benutzer die Lernphase auch statisch auf einen bestimmten Wert ändern. Dies muss jedoch geschehen, bevor ein Modell oder ein Tensor in das Diagramm eingefügt wird. Wenn die Lernphase statisch festgelegt ist, wird Keras für den vom Benutzer ausgewählten Modus gesperrt.

1.4 Wie hat Keras die Chargennormalisierung im Laufe der Zeit implementiert?

Keras hat das Verhalten der Stapelnormalisierung mehrmals geändert, aber das letzte wichtige Update wurde in Keras 2.1.3 durchgeführt. Vor Version 2.1.3, als die BN-Ebene eingefroren war (trainierbar = falsch), wurden die Stapelstatistiken ständig aktualisiert, was den Benutzern epische Kopfschmerzen bereitete.

Dies war nicht nur eine seltsame Politik, es war tatsächlich falsch. Stellen Sie sich vor, dass zwischen den Windungen eine BN-Schicht existiert. Wenn die Schicht eingefroren ist, sollten keine Änderungen daran vorgenommen werden. Wenn wir die Gewichte teilweise aktualisieren und die nächsten Ebenen ebenfalls eingefroren sind, haben sie nie die Möglichkeit, sich an die Aktualisierungen der Mini-Batch-Statistiken anzupassen, was zu höheren Fehlern führt. Zum Glück aktualisiert eine BN-Schicht ab Version 2.1.3 ihre Statistiken nicht mehr, wenn sie eingefroren ist. Aber ist das genug? Nicht, wenn Sie Transfer Learning verwenden.

Im Folgenden beschreibe ich genau das Problem und skizziere die technische Implementierung zur Lösung. Ich gebe auch einige Beispiele, um die Auswirkungen auf die Genauigkeit des Modells vor und nach dem Modell zu zeigen Flicken wird angewandt.

2.1 Technische Beschreibung des Problems

Das Problem bei der aktuellen Implementierung von Keras besteht darin, dass eine eingefrorene BN-Schicht während des Trainings weiterhin die Mini-Batch-Statistiken verwendet. Ich glaube, ein besserer Ansatz, wenn der BN eingefroren ist, besteht darin, den sich bewegenden Mittelwert und die Varianz zu verwenden, die er während des Trainings gelernt hat. Warum? Aus den gleichen Gründen, warum die Mini-Batch-Statistiken beim Einfrieren der Ebene nicht aktualisiert werden sollten: Dies kann zu schlechten Ergebnissen führen, da die nächsten Ebenen nicht richtig trainiert werden.

Angenommen, Sie erstellen ein Computer Vision-Modell, verfügen jedoch nicht über genügend Daten. Daher entscheiden Sie sich, eines der vorab geschulten CNNs von Keras zu verwenden und es zu optimieren. Leider erhalten Sie auf diese Weise keine Garantie dafür, dass der Mittelwert und die Varianz Ihres neuen Datensatzes innerhalb der BN-Ebenen denen des ursprünglichen Datensatzes ähnlich sind. Denken Sie daran, dass Ihr Netzwerk während des Trainings im Moment immer die Mini-Batch-Statistiken verwendet, entweder wenn die BN-Schicht eingefroren ist oder nicht. Auch während der Inferenz verwenden Sie die zuvor gelernten Statistiken der eingefrorenen BN-Schichten. Wenn Sie die obersten Ebenen fein abstimmen, werden ihre Gewichte daher an den Mittelwert / die Varianz der angepasst neu Datensatz. Während der Inferenz erhalten sie jedoch skalierte Daten anders weil der Mittelwert / die Varianz der Original Datensatz wird verwendet.
Die Batch-Normalisierungsschicht von Keras ist eine kaputte PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.
Oben stelle ich zu Demonstrationszwecken eine vereinfachte (und unrealistische) Architektur zur Verfügung. Nehmen wir an, wir optimieren das Modell von Convolution k + 1 bis zur Oberseite des Netzwerks (rechte Seite) und halten die Unterseite (linke Seite) eingefroren. Während des Trainings verwenden alle BN-Schichten von 1 bis k den Mittelwert / die Varianz Ihrer Trainingsdaten. Dies hat negative Auswirkungen auf die eingefrorenen ReLUs, wenn der Mittelwert und die Varianz der einzelnen BN nicht nahe an denen liegen, die während des Vortrainings gelernt wurden. Es wird auch dazu führen, dass der Rest des Netzwerks (ab CONV k + 1) mit Eingaben trainiert wird, die andere Skalen haben als die, die während der Inferenz empfangen werden. Während des Trainings kann sich Ihr Netzwerk an diese Änderungen anpassen. Sobald Sie jedoch in den Vorhersagemodus wechseln, verwendet Keras unterschiedliche Standardisierungsstatistiken, wodurch die Verteilung der Eingaben der nächsten Ebenen schnell erfolgt und schlechte Ergebnisse erzielt werden.

2.2 Wie können Sie feststellen, ob Sie betroffen sind?

Eine Möglichkeit, dies zu erkennen, besteht darin, die Lernphase von Keras statisch auf 1 (Zugmodus) und 0 (Testmodus) zu setzen und Ihr Modell jeweils zu bewerten. Wenn derselbe Datensatz einen signifikanten Unterschied in der Genauigkeit aufweist, sind Sie von dem Problem betroffen. Es ist erwähnenswert, dass es aufgrund der Art und Weise, wie der Mechanismus "learning_phase" in Keras implementiert ist, normalerweise nicht ratsam ist, sich damit zu beschäftigen. Änderungen an der Lernphase haben keine Auswirkungen auf Modelle, die bereits kompiliert und verwendet wurden. Wie Sie in den Beispielen in den nächsten Unterabschnitten sehen können, beginnen Sie am besten mit einer sauberen Sitzung und ändern Sie die Lernphase, bevor ein Tensor in der Grafik definiert wird.

Eine andere Möglichkeit, das Problem bei der Arbeit mit binären Klassifizierern zu erkennen, besteht darin, die Genauigkeit und die AUC zu überprüfen. Wenn die Genauigkeit nahe bei 50% liegt, die AUC jedoch nahe bei 1 liegt (und Sie auch Unterschiede zwischen Zug- / Testmodus im selben Datensatz beobachten), kann es sein, dass die Wahrscheinlichkeiten aufgrund der BN-Statistik nicht skalierbar sind. In ähnlicher Weise können Sie für die Regression die Korrelation von MSE und Spearman verwenden, um sie zu erkennen.

2.3 Wie können wir das beheben?

Ich glaube, dass das Problem behoben werden kann, wenn die gefrorenen BN-Schichten tatsächlich genau das sind: permanent im Testmodus gesperrt. In Bezug auf die Implementierung muss das trainierbare Flag Teil des Berechnungsgraphen sein und das Verhalten des BN muss nicht nur von der Lernphase, sondern auch vom Wert der trainierbaren Eigenschaft abhängen. Details zu meiner Implementierung finden Sie unter Github.

Wenn Sie die obige Korrektur anwenden, werden beim Einfrieren einer BN-Schicht nicht mehr die Mini-Batch-Statistiken verwendet, sondern die während des Trainings erlernten. Infolgedessen gibt es keine Diskrepanz zwischen Trainings- und Testmodus, was zu einer erhöhten Genauigkeit führt. Wenn die BN-Schicht nicht eingefroren ist, werden die Mini-Batch-Statistiken natürlich während des Trainings weiter verwendet.

2.4 Bewertung der Auswirkungen des Patches

Obwohl ich die obige Implementierung kürzlich geschrieben habe, wird die Idee dahinter anhand verschiedener Problemumgehungen, die den gleichen Effekt haben, stark auf reale Probleme getestet. Zum Beispiel kann die Diskrepanz zwischen Trainings- und Testmodus vermieden werden, indem das Netzwerk in zwei Teile aufgeteilt wird (eingefroren und nicht eingefroren) und ein zwischengespeichertes Training durchgeführt wird (Daten einmal durch das eingefrorene Modell geleitet und dann zum Trainieren des nicht eingefrorenen Netzwerks verwendet werden). Da das „Vertrauen Sie mir, dass ich das schon einmal gemacht habe“ normalerweise kein Gewicht hat, möchte ich im Folgenden einige Beispiele nennen, die die Auswirkungen der neuen Implementierung in der Praxis zeigen.

Hier sind einige wichtige Punkte zum Experiment:

  1. Ich werde eine winzige Datenmenge verwenden, um das Modell absichtlich zu überpassen, und ich werde das Modell auf demselben Datensatz trainieren und validieren. Auf diese Weise erwarte ich eine nahezu perfekte Genauigkeit und identische Leistung des Zug- / Validierungsdatensatzes.
  2. Wenn ich während der Validierung eine signifikant geringere Genauigkeit für denselben Datensatz erhalte, habe ich einen klaren Hinweis darauf, dass die aktuelle BN-Richtlinie die Leistung des Modells während der Inferenz negativ beeinflusst.
  3. Jede Vorverarbeitung findet außerhalb der Generatoren statt. Dies geschieht, um einen Fehler zu umgehen, der in Version 2.1.5 eingeführt wurde (derzeit behoben in Version 2.1.6 und dem neuesten Master).
  4. Wir werden Keras zwingen, während der Evaluierung verschiedene Lernphasen zu verwenden. Wenn wir Unterschiede zwischen der gemeldeten Genauigkeit feststellen, wissen wir, dass wir von der aktuellen BN-Richtlinie betroffen sind.

Der Code für das Experiment ist unten gezeigt:

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

Lassen Sie uns die Ergebnisse unter Keras v2.1.5 überprüfen:

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]

Wie wir oben sehen können, lernt das Modell während des Trainings die Daten sehr gut und erreicht auf dem Trainingssatz eine nahezu perfekte Genauigkeit. Noch am Ende jeder Iteration erhalten wir bei der Bewertung des Modells auf demselben Datensatz signifikante Unterschiede in Bezug auf Verlust und Genauigkeit. Beachten Sie, dass wir dies nicht bekommen sollten; Wir haben das Modell absichtlich auf den spezifischen Datensatz angepasst und die Trainings- / Validierungsdatensätze sind identisch.

Nach Abschluss des Trainings bewerten wir das Modell anhand von 3 verschiedenen Konfigurationen für die Lernphase: Dynamisch, Statisch = 0 (Testmodus) und Statisch = 1 (Trainingsmodus). Wie wir sehen können, liefern die ersten beiden Konfigurationen identische Ergebnisse in Bezug auf Verlust und Genauigkeit, und ihr Wert stimmt mit der angegebenen Genauigkeit des Modells für den in der letzten Iteration festgelegten Validierungssatz überein. Sobald wir jedoch in den Trainingsmodus wechseln, stellen wir eine massive Diskrepanz (Verbesserung) fest. Warum das? Wie bereits erwähnt, werden die Gewichte des Netzwerks so abgestimmt, dass Daten empfangen werden, die mit dem Mittelwert / der Varianz der Trainingsdaten skaliert sind. Leider unterscheiden sich diese Statistiken von denen, die in den BN-Schichten gespeichert sind. Da die BN-Schichten eingefroren waren, wurden diese Statistiken nie aktualisiert. Diese Diskrepanz zwischen den Werten der BN-Statistik führt zu einer Verschlechterung der Genauigkeit während der Inferenz.

Mal sehen, was passiert, wenn wir das anwenden Flicken:

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]

Zunächst stellen wir fest, dass das Netzwerk deutlich schneller konvergiert und eine perfekte Genauigkeit erzielt. Wir sehen auch, dass es keine Diskrepanz mehr in Bezug auf die Genauigkeit gibt, wenn wir zwischen verschiedenen Lernphasenwerten wechseln.

2.5 Wie funktioniert der Patch für einen realen Datensatz?

Wie funktioniert der Patch bei einem realistischeren Experiment? Verwenden wir Keras 'vorab trainiertes ResNet50 (ursprünglich für Imagenet geeignet), entfernen Sie die oberste Klassifizierungsebene und optimieren Sie sie mit und ohne Patch und vergleichen Sie die Ergebnisse. Für Daten verwenden wir CIFAR10 (die von Keras bereitgestellte Standard-Zug- / Testaufteilung) und ändern die Größe der Bilder auf 224 × 224, um sie mit der Eingabegröße des ResNet50 kompatibel zu machen.

Wir werden 10 Epochen machen, um die oberste Klassifizierungsschicht mit RSMprop zu trainieren, und dann werden wir weitere 5 Epochen machen, um alles nach der 139. Schicht mit SGD zu optimieren (lr = 1e-4, Impuls = 0.9). Ohne den Patch erreicht unser Modell eine Genauigkeit von 87.44%. Mit dem Patch erhalten wir eine Genauigkeit von 92.36%, fast 5 Punkte höher.

2.6 Sollten wir den gleichen Fix auf andere Ebenen wie Dropout anwenden?

Die Chargennormalisierung ist nicht die einzige Schicht, die zwischen Zug- und Testmodus unterschiedlich arbeitet. Dropout und seine Varianten haben ebenfalls den gleichen Effekt. Sollten wir auf alle diese Ebenen dieselbe Richtlinie anwenden? Ich glaube nicht (obwohl ich gerne Ihre Gedanken dazu hören würde). Der Grund dafür ist, dass Dropout verwendet wird, um eine Überanpassung zu vermeiden. Wenn Sie es also während des Trainings dauerhaft im Vorhersagemodus sperren, wird sein Zweck zunichte gemacht. Was denken Sie?

Ich bin der festen Überzeugung, dass diese Diskrepanz in Keras gelöst werden muss. Ich habe noch tiefgreifendere Effekte (von 100% bis zu 50% Genauigkeit) in realen Anwendungen gesehen, die durch dieses Problem verursacht wurden. ich planen zu senden schon gesendet a PR an Keras mit dem Fix und hoffentlich wird es akzeptiert.

Wenn Ihnen dieser Blogpost gefallen hat, nehmen Sie sich bitte einen Moment Zeit, um ihn auf Facebook oder Twitter zu teilen. 🙂

Zeitstempel:

Mehr von Bezugsbox