Ich versuche, ein CNN zu trainieren, um Text nach Themen zu kategorisieren. Wenn ich binary_crossentropy verwende, bekomme ich ~ 80% acc, mit categorical_crossentrop bekomme ich ~ 50% acc.
Ich verstehe nicht, warum das so ist. Es ist ein Problem mit mehreren Klassen, bedeutet das, dass ich kategoriale verwenden muss und die binären Ergebnisse bedeutungslos sind?
model.add(embedding_layer)
model.add(Dropout(0.25))
# convolution layers
model.add(Conv1D(nb_filter=32,
filter_length=4,
border_mode='valid',
activation='relu'))
model.add(MaxPooling1D(pool_length=2))
# dense layers
model.add(Flatten())
model.add(Dense(256))
model.add(Dropout(0.25))
model.add(Activation('relu'))
# output layer
model.add(Dense(len(class_id_index)))
model.add(Activation('softmax'))
dann
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
oder
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
Der Grund für diese scheinbare Leistungsdifferenz zwischen kategorialer und binärer Kreuzentropie ist das, was @ xtof54 bereits in seiner Antwort berichtet hat, d.h.
die mit der Keras-Methode
evaluate
berechnete Genauigkeit ist einfach falsch bei Verwendung von binary_crossentropy mit mehr als 2 Labels
Ich möchte darauf näher eingehen, das eigentliche zugrunde liegende Problem aufzeigen, erläutern und Abhilfe schaffen.
Dieses Verhalten ist kein Fehler. Der zugrunde liegende Grund ist ein eher subtiler und undokumentierter Grund, wie Keras tatsächlich die {Vermutungen _ Genauigkeit verwendet, abhängig von der von Ihnen gewählten Verlustfunktion, wenn Sie einfach metrics=['accuracy']
in Ihre Modellkompilierung aufnehmen. Mit anderen Worten, während Ihre erste Übersetzungsoption
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
ist gültig, dein zweiter:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
wird nicht das produzieren, was Sie erwarten, aber der Grund ist nicht die Verwendung der binären Kreuzentropie (die zumindest im Prinzip eine absolut gültige Verlustfunktion ist).
Warum das? Wenn Sie den metrics-Quellcode überprüfen, definiert Keras keine einzelne Genauigkeitsmetrik, sondern mehrere verschiedene, darunter binary_accuracy
und categorical_accuracy
. Was passiert unter der Haube ist, dass Keras (falsch ...), da Sie als Verlustfunktion die binäre Kreuzentropie als Verlustfunktion ausgewählt und keine bestimmte Genauigkeitsmetrik angegeben haben, an Sie und Ihr Interesse an dem binary_accuracy
erinnert ist das, was er zurückgibt - während Sie tatsächlich am categorical_accuracy
interessiert sind.
Stellen Sie sicher, dass dies der Fall ist, und verwenden Sie dazu das MNIST CNN-Beispiel in Keras mit der folgenden Änderung:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) # WRONG way
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=2, # only 2 epochs, for demonstration purposes
verbose=1,
validation_data=(x_test, y_test))
# Keras reported accuracy:
score = model.evaluate(x_test, y_test, verbose=0)
score[1]
# 0.9975801164627075
# Actual accuracy calculated manually:
import numpy as np
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000
acc
# 0.98780000000000001
score[1]==acc
# False
Um Abhilfe zu schaffen, dh um die binäre Kreuzentropie als Verlustfunktion zu verwenden (wie gesagt, dies ist zumindest im Prinzip nichts Falsches), während gleichzeitig die durch das anstehende Problem geforderte Genauigkeit von kategorical erreicht wird, sollten Sie fragen explizit für categorical_accuracy
in der Modellzusammenstellung wie folgt:
from keras.metrics import categorical_accuracy
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])
Im MNIST-Beispiel sind die beiden Metriken nach dem Training, Bewerten und Vorhersagen des Test-Sets, wie ich es oben gezeigt habe, nun die gleichen, wie sie sein sollten:
# Keras reported accuracy:
score = model.evaluate(x_test, y_test, verbose=0)
score[1]
# 0.98580000000000001
# Actual accuracy calculated manually:
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000
acc
# 0.98580000000000001
score[1]==acc
# True
Systemkonfiguration:
Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4
UPDATE: Nach meinem Beitrag habe ich festgestellt, dass dieses Problem bereits in dieser Antwort gefunden wurde.
Es ist wirklich ein interessanter Fall. Eigentlich gilt in Ihrem Setup die folgende Aussage:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
Dies bedeutet, dass Ihre Verluste bis zu einem konstanten Multiplikationsfaktor gleich sind. Das merkwürdige Verhalten, das Sie während einer Trainingsphase beobachten, kann ein Beispiel für ein folgendes Phänomen sein:
adam
verwenden, hat die Lernrate einen viel geringeren Wert als zu Beginn des Trainings (dies liegt an der Natur dieses Optimierers). Dies macht das Training langsamer und verhindert, dass Ihr Netzwerk z. Hinterlassen eines schlechten lokalen Minimums ist weniger möglich.Deshalb kann dieser konstante Faktor im Fall von binary_crossentropy
helfen. Nach vielen Epochen - der Lernratenwert ist größer als in categorical_crossentropy
. Normalerweise starte ich das Training (und die Lernphase) einige Male neu, wenn ich ein solches Verhalten bemerke oder/und die Klassengewichtung anhand des folgenden Musters anpasse:
class_weight = 1 / class_frequency
Dies führt dazu, dass der Verlust von weniger häufig auftretenden Klassen den Einfluss eines dominanten Klassenverlusts zu Beginn eines Trainings und in einem weiteren Teil eines Optimierungsprozesses ausbalanciert.
BEARBEITEN:
Eigentlich - ich habe das überprüft, auch wenn es sich um Mathematik handelt:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
sollte halten - im Fall von keras
ist dies nicht wahr, da keras
automatisch alle Ausgaben normalisiert, um 1
zu summieren. Dies ist der eigentliche Grund für dieses seltsame Verhalten, da bei einer Klassifizierung eine solche Normalisierung einem Training schadet.
Ich bin auf ein "umgekehrtes" Problem gestoßen - ich habe gute Ergebnisse mit categorical_crossentropy (mit 2 Klassen) und schlecht mit binary_crossentropy erzielt. Es scheint, dass das Problem mit einer falschen Aktivierungsfunktion war. Die korrekten Einstellungen waren:
binary_crossentropy
: sigmoidale Aktivierung, skalares Ziel categorical_crossentropy
: Softmax-Aktivierung, One-Hot-codiertes ZielEs hängt alles von der Art des Klassifizierungsproblems ab, mit dem Sie sich befassen. Es gibt drei Hauptkategorien:
Im ersten Fall sollte eine binäre Kreuzentropie verwendet werden, und Ziele sollten als One-Hot-Vektoren codiert werden.
Im zweiten Fall sollte kategoriale Kreuzentropie verwendet werden, und Ziele sollten als One-Hot-Vektoren kodiert werden.
Im letzten Fall sollte eine binäre Kreuzentropie verwendet werden, und Ziele sollten als One-Hot-Vektoren codiert werden. Jedes Ausgabeneuron (oder jede Einheit) wird als separate zufällige binäre Variable betrachtet, und der Verlust für den gesamten Vektor der Ausgänge ist das Produkt des Verlusts einzelner binärer Variablen. Daher ist es das Produkt der binären Kreuzentropie für jede einzelne Ausgabeeinheit.
binäre Kreuzentropie ist als solche definiert: Binäre Kreuzentropie und kategoriale Kreuzentropie ist als solche definiert: Kategoriale Kreuzentropie
Nachdem ich die Antwort von @Marcin kommentiert habe, habe ich einen Code meiner Schüler genauer überprüft, in dem ich selbst nach nur zwei Epochen das seltsame Verhalten fand. (Also war @ Marcins Erklärung in meinem Fall nicht sehr wahrscheinlich).
Und ich fand, dass die Antwort eigentlich sehr einfach ist: Die mit der Keras-Methode evaluate
berechnete Genauigkeit ist einfach falsch, wenn Sie binary_crossentropy mit mehr als 2 Labels verwenden. Sie können dies überprüfen, indem Sie die Genauigkeit selbst neu berechnen (rufen Sie zuerst die Keras-Methode "Prediction" an und berechnen Sie dann die Anzahl der korrekten Antworten, die von Predict zurückgegeben werden).
Da es sich um ein Problem mit mehreren Klassen handelt, müssen Sie categorical_crossentropy verwenden. Die binäre Kreuzentropie führt zu falschen Ergebnissen. Wahrscheinlich werden nur die ersten beiden Klassen ausgewertet.
50% für ein Problem mit mehreren Klassen können je nach Anzahl der Klassen recht gut sein. Wenn Sie n Klassen haben, ist 100/n die minimale Leistung, die Sie durch Ausgabe einer zufälligen Klasse erzielen können.
wenn Sie categorical_crossentropy
loss verwenden, sollten Ihre Ziele in kategorialem Format sein (z. B. wenn Sie 10 Klassen haben, sollte das Ziel für jede Probe ein 10-dimensionaler Vektor sein, der aus Nullen besteht, mit Ausnahme einer 1 am Index, die der Klasse von entspricht die Probe).
ein einfaches Beispiel unter einer Multi-Class-Einstellung
angenommen, Sie haben 4 Klassen (onehot-codiert) und unten ist nur eine Vorhersage
true_label = [0,1,0,0] predicted_label = [0,0,1,0]
bei der Verwendung von categorical_crossentropy ist die Genauigkeit nur 0, es ist nur wichtig, wenn Sie die betreffende Klasse richtig einstellen.
bei der Verwendung von binary_crossentropy wird jedoch die Genauigkeit für alle Klassen berechnet. Diese Vorhersage würde 50% betragen. und das Endergebnis ist das Mittel der Einzelgenauigkeiten für beide Fälle.
es wird empfohlen, categorical_crossentropy für Probleme mit mehreren Klassen (Klassen sind sich gegenseitig ausschließend) zu verwenden, aber binary_crossentropy für Probleme mit mehreren Labels.
Sie übergeben ein Ziel-Array mit einer Form (x-dim, y-dim), während Sie als Verlust categorical_crossentropy
verwenden. categorical_crossentropy
erwartet, dass Ziele binäre Matrizen (1s und 0s) der Form (Samples, Klassen) sind. Wenn Ihre Ziele Ganzzahlklassen sind, können Sie sie in das erwartete Format konvertieren:
from keras.utils import to_categorical
y_binary = to_categorical(y_int)
Alternativ können Sie stattdessen die Verlustfunktion sparse_categorical_crossentropy
verwenden, die ganzzahlige Ziele erwartet.
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
Schauen Sie sich die Gleichung an, Sie können feststellen, dass binäre Kreuzentropie nicht nur die Label = 1, Predicted = 0, sondern auch Label = 0, Predicted = 1 bestraft.
Jedoch kategorische Kreuzentropie bestraft nur das Label = 1, aber vorhergesagt = 1. Deshalb gehen wir davon aus, dass nur EIN Label positiv ist.