Warum bieten binäre Kreuzentropie und kategoriale Kreuzentropie unterschiedliche Leistungen für dasselbe Problem?

159

Ich versuche, ein CNN zu trainieren, um Text nach Themen zu kategorisieren. Wenn ich binäre Kreuzentropie verwende, erhalte ich eine Genauigkeit von ~ 80%, bei kategorialer Kreuzentropie eine Genauigkeit von ~ 50%.

Ich verstehe nicht, warum das so ist. Es ist ein Problem mit mehreren Klassen. Bedeutet das nicht, dass ich eine kategoriale Kreuzentropie verwenden muss und dass die Ergebnisse mit binärer Kreuzentropie 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 kompiliere ich es entweder so oder benutze es categorical_crossentropyals Verlustfunktion:

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

oder

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

Intuitiv macht es Sinn, warum ich kategoriale Kreuzentropie verwenden möchte. Ich verstehe nicht, warum ich mit binären Ergebnissen gute und mit kategorialen schlechte Ergebnisse erhalte.

Daniel Messias
quelle
10
Wenn es sich um ein Problem mit mehreren Klassen handelt, müssen Sie es verwenden categorical_crossentropy. Auch Beschriftungen müssen in das kategoriale Format konvertiert werden. Sehen Sie to_categorical, um dies zu tun. Siehe auch Definitionen von kategorialen und binären Crossentropien hier .
Autonome
Meine Beschriftungen sind kategorisch und werden mit to_categorical erstellt (ein heißer Vektor für jede Klasse). Bedeutet das, dass die Genauigkeit von ~ 80% aus der binären Kreuzentropie nur eine falsche Zahl ist?
Daniel Messias
Ich glaube schon. Wenn Sie kategoriale Beschriftungen verwenden, dh einen heißen Vektor, dann möchten Sie categorical_crossentropy. Wenn Sie zwei Klassen haben, werden diese wie 0, 1in binären Beschriftungen und 10, 01im kategorialen Beschriftungsformat dargestellt.
Autonome
1
Ich denke, er vergleicht nur mit der ersten Zahl im Vektor und ignoriert den Rest.
Thomas Pinetz
2
@NilavBaranGhosh Die Darstellung ist [[1, 0], [0, 1]] für eine kategoriale Klassifizierung mit zwei Klassen (nicht [[0, 0], [0, 1]], wie Sie erwähnen). Dense(1, activation='softmax')für die binäre Klassifizierung ist einfach falsch. Denken Sie daran, dass die Softmax-Ausgabe eine Wahrscheinlichkeitsverteilung ist, die sich zu eins summiert. Wenn Sie nur ein Ausgangsneuron mit binärer Klassifizierung haben möchten, verwenden Sie Sigmoid mit binärer Kreuzentropie.
Autonome

Antworten:

204

Der Grund für diese offensichtliche Leistungsdiskrepanz zwischen kategorialer und binärer Kreuzentropie ist, was Benutzer xtof54 bereits in seiner Antwort unten angegeben hat , dh:

Die mit der Keras-Methode berechnete Genauigkeit evaluateist bei Verwendung von binary_crossentropy mit mehr als 2 Beschriftungen einfach falsch

Ich möchte näher darauf eingehen, das eigentliche zugrunde liegende Problem aufzeigen, es erklären und Abhilfe schaffen.

Dieses Verhalten ist kein Fehler. Der zugrunde liegende Grund ist eine ziemlich subtile und nicht dokumentierte Frage, wie Keras tatsächlich errät, welche Genauigkeit abhängig von der von Ihnen ausgewählten Verlustfunktion verwendet werden soll, wenn Sie sie einfach metrics=['accuracy']in Ihre Modellzusammenstellung einbeziehen. Mit anderen Worten, während Ihrer ersten Kompilierungsoption

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 ist das so? Wenn Sie den Quellcode der Metriken überprüfen , definiert Keras keine einzelne Genauigkeitsmetrik, sondern mehrere verschiedene, darunter binary_accuracyund categorical_accuracy. Was unter der Haube passiert, ist, dass Keras (fälschlicherweise ...), da Sie die binäre Kreuzentropie als Verlustfunktion ausgewählt und keine bestimmte Genauigkeitsmetrik angegeben haben, darauf schließen lässt, dass Sie daran interessiert sind binary_accuracy, und dies ist, was es zurückgibt - während in der Tat interessieren Sie sich für die categorical_accuracy.

Lassen Sie uns anhand des MNIST CNN-Beispiels in Keras mit der folgenden Änderung überprüfen, ob dies der Fall ist :

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 dies zu beheben, dh um tatsächlich binäre Kreuzentropie als Ihre Verlustfunktion zu verwenden (wie gesagt, zumindest im Prinzip nichts Falsches daran), während Sie dennoch die für das jeweilige Problem erforderliche kategoriale Genauigkeit erhalten, sollten Sie categorical_accuracyin der Modellzusammenstellung wie folgt:

from keras.metrics import categorical_accuracy
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])

Im MNIST-Beispiel sind nach dem Training, Bewerten und Vorhersagen des Testsatzes, wie oben gezeigt, die beiden Metriken jetzt dieselben, 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 stellte ich fest, dass dieses Problem bereits in dieser Antwort identifiziert wurde .

desertnaut
quelle
1
Stimmt etwas nicht mit der Verwendung loss='categorical_crossentropy', metrics=['categorical_accuracy']für die Klassifizierung mehrerer Klassen? Dies wäre meine Intuition
NeStack
2
@NeStack Es ist nicht nur nichts falsch, sondern dies ist die nominelle Kombination.
Desertnaut
1
Laut dem, was Sie gesagt haben, erhalte ich, solange ich loss = 'binary_crossentropy' verwende, die gleichen Renditen, egal ob ich Metrics = 'binary_accuracy' oder Metrics = 'Genauigkeit' verwende.
BioCoder
2
@ BioCoder genau
desertnaut
54

Es hängt alles von der Art des Klassifizierungsproblems ab, mit dem Sie sich befassen. Es gibt drei Hauptkategorien

  • binäre Klassifikation (zwei Zielklassen),
  • Mehrklassenklassifizierung (mehr als zwei exklusive Ziele),
  • Multi-Label- Klassifizierung (mehr als zwei nicht exklusive Ziele), bei der mehrere Zielklassen gleichzeitig aktiviert sein können.

Im ersten Fall sollte eine binäre Kreuzentropie verwendet werden und Ziele sollten als One-Hot-Vektoren codiert werden.

Im zweiten Fall sollte eine kategoriale Kreuzentropie verwendet werden und Ziele sollten als One-Hot-Vektoren codiert werden.

Im letzten Fall sollte eine binäre Kreuzentropie verwendet werden und Ziele sollten als One-Hot-Vektoren codiert werden. Jedes Ausgangsneuron (oder jede Ausgangseinheit) wird als separate zufällige binäre Variable betrachtet, und der Verlust für den gesamten Ausgangsvektor ist das Produkt des Verlusts einzelner binärer Variablen. Daher ist es das Produkt der binären Kreuzentropie für jede einzelne Ausgabeeinheit.

Die binäre Kreuzentropie ist definiert als

Geben Sie hier die Bildbeschreibung ein

und kategoriale Kreuzentropie ist definiert als

Geben Sie hier die Bildbeschreibung ein

Wo cläuft der Index über die Anzahl der Klassen?

Whynote
quelle
Ihre Antwort scheint mir sehr wahr zu sein, aber ... Ich habe versucht, der Antwort von @desertnaut zu folgen, und diese Tests durchgeführt: Mit der binären Kreuzkreuzungsverlustfunktion und der Metrik für die kategoriale Genauigkeit habe ich eine bessere Genauigkeit als mit der kategorialen Kreuzungsverlustfunktion und den Genauigkeitsmetriken - und ich kann es nicht erklären das ...
Metal3d
@ Metal3d: Wie lautet die Formulierung Ihres Problems: Multi-Label oder Single-Label?
Whynote
Single-Label, und jetzt
merke
Sind Sie sicher, dass die binären und kategorialen Kreuzentropien wie in den Formeln in dieser Antwort definiert sind?
6.
@nbro, tatsächlich ist der cIndex in der binären Kreuzentropieformel redundant, er muss nicht vorhanden sein (da es nur 2 Klassen gibt und die Wahrscheinlichkeit jeder Klasse eingebettet ist y(x). Andernfalls sollten diese Formeln korrekt sein, aber log
Beachten
40

Ich bin auf ein "invertiertes" Problem gestoßen - ich habe gute Ergebnisse mit kategorialer Kreuzentropie (mit 2 Klassen) und schlechte mit binärer Kreuzentropie erzielt. Es scheint, dass das Problem mit der falschen Aktivierungsfunktion war. Die richtigen Einstellungen waren:

  • für binary_crossentropy: Sigmoidaktivierung, skalares Ziel
  • für categorical_crossentropy: Softmax-Aktivierung, One-Hot-codiertes Ziel
Alexander Svetkin
quelle
4
Sind Sie sich über das skalare Ziel für binary_crossentropy sicher? Es sieht so aus, als ob Sie ein "many-hot" -codiertes Ziel verwenden sollten (z. B. [0 1 0 0 1 1]).
Dmitry
5
Sicher. Unter keras.io/losses/#usage-of-loss-functions heißt es: "Wenn Sie den Verlust von categoryical_crossentropy verwenden , sollten Ihre Ziele in einem kategorialen Format vorliegen (z. B. wenn Sie 10 Klassen haben, sollte das Ziel für jede Stichprobe eine 10 sein -dimensionaler Vektor, der aus Nullen besteht, erwartet eine 1 am Index, der der Klasse der Stichprobe entspricht. "
Alexander Svetkin
1
Aber wir sprechen von binärer Kreuzentropie - nicht von kategorialer Kreuzentropie.
Dmitry
Diese Antwort scheint nicht mit stackoverflow.com/a/49175655/3924118 übereinzustimmen , wo der Autor sagt, dass die Ziele One-Hot-codiert sein sollten, während Sie in Ihrer Antwort vorschlagen, dass sie Skalare sein sollten. Sie sollten dies klarstellen.
6.
@AlexanderSvetkin, das Ziel sollte überall One-Hot-codiert sein, nicht nur bei Verwendung einer kategorialen Kreuzentropie
Whynote
28

Es ist wirklich ein interessanter Fall. Tatsächlich ist in Ihrem Setup die folgende Aussage wahr:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Dies bedeutet, dass Ihre Verluste bis zu einem konstanten Multiplikationsfaktor gleichwertig sind. Das seltsame Verhalten, das Sie während einer Trainingsphase beobachten, könnte ein Beispiel für ein folgendes Phänomen sein:

  1. Zu Beginn dominiert die häufigste Klasse den Verlust - daher lernt das Netzwerk, meistens diese Klasse für jedes Beispiel vorherzusagen.
  2. Nachdem es das häufigste Muster gelernt hat, beginnt es, zwischen weniger häufigen Klassen zu unterscheiden. Aber wenn Sie verwenden, hat adamdie Lernrate einen viel geringeren Wert als zu Beginn des Trainings (dies liegt an der Natur dieses Optimierers). Dies verlangsamt das Training und verhindert, dass Ihr Netzwerk z. B. ein schlechtes lokales Minimum weniger möglich macht.

Deshalb könnte dieser konstante Faktor bei helfen binary_crossentropy. Nach vielen Epochen ist der Wert der Lernrate größer als im categorical_crossentropyFall. Normalerweise starte ich das Training (und die Lernphase) einige Male neu, wenn ich ein solches Verhalten bemerke oder / und ein Klassengewicht nach folgendem Muster anpasse:

class_weight = 1 / class_frequency

Dies führt zu einem Verlust aus weniger häufigen Klassen, der den Einfluss eines dominanten Klassenverlusts zu Beginn eines Trainings und in einem weiteren Teil eines Optimierungsprozesses ausgleicht.

BEARBEITEN:

Eigentlich - ich habe das überprüft, obwohl im Falle von Mathematik:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

sollte gelten - falls kerasdies nicht der Fall ist, da kerasautomatisch alle Ausgänge normalisiert werden, um eine Summe zu erhalten 1. Dies ist der eigentliche Grund für dieses seltsame Verhalten, da im Falle einer Mehrfachklassifizierung eine solche Normalisierung einem Training schadet.

Marcin Możejko
quelle
Hat meine Antwort dir geholfen?
Marcin Możejko
1
Dies ist eine sehr plausible Erklärung. Aber ich bin nicht sicher, ob es wirklich der Hauptgrund ist. Weil ich auch bei einigen meiner Schüler beobachtet habe, dass sie dieses seltsame Verhalten anwenden, wenn sie Binär-X-ent anstelle von cat-X-ent anwenden (was ein Fehler ist). Und das gilt auch beim Training für nur 2 Epochen! Die Verwendung von class_weight mit inversen Klassenprioren hat nicht geholfen. Möglicherweise hilft eine strenge Anpassung der Lernrate, aber die Standardwerte scheinen bin-X-ent zu bevorzugen. Ich denke, diese Frage verdient weitere Untersuchungen ...
xtof54
1
Warten Sie, nein, tut mir leid, ich bekomme Ihr Update nicht: Der Softmax bringt die Ausgaben immer auf 1, also ist uns das egal? Und warum würde dies dem Training schaden, solange wir nur eine einzige Goldklasse haben, die pro Beispiel korrekt ist?
xtof54
20

Nachdem ich die Antwort von @Marcin kommentiert habe, habe ich einen meiner Schülercodes genauer überprüft, bei dem ich das gleiche seltsame Verhalten festgestellt habe, selbst nach nur 2 Epochen! (Also @ Marcins Erklärung war in meinem Fall nicht sehr wahrscheinlich).

Und ich fand, dass die Antwort eigentlich sehr einfach ist: Die mit der Keras-Methode berechnete Genauigkeit evaluateist einfach falsch, wenn binäre_kreuzentropie mit mehr als 2 Beschriftungen verwendet wird. Sie können dies überprüfen, indem Sie die Genauigkeit selbst neu berechnen (rufen Sie zuerst die Keras-Methode "Vorhersagen" auf und berechnen Sie dann die Anzahl der korrekten Antworten, die von Vorhersagen zurückgegeben werden): Sie erhalten die wahre Genauigkeit, die viel niedriger ist als die von Keras "Auswerten".

xtof54
quelle
1
Ich habe ein ähnliches Verhalten auch bei der ersten Iteration gesehen.
Dolbi
10

Ein einfaches Beispiel unter einer Einstellung für mehrere Klassen zur Veranschaulichung

Angenommen, Sie haben 4 Klassen (onehot codiert) und unten ist nur eine Vorhersage

true_label = [0,1,0,0] prognostiziertes_Label = [0,0,1,0]

Wenn Sie categoryical_crossentropy verwenden, ist die Genauigkeit nur 0, es ist nur wichtig, ob Sie die betreffende Klasse richtig machen.

Bei Verwendung von binary_crossentropy wird die Genauigkeit jedoch für alle Klassen berechnet. Für diese Vorhersage beträgt sie 50%. und das Endergebnis ist der Mittelwert der individuellen Genauigkeiten für beide Fälle.

Es wird empfohlen, kategoriale_Crossentropie für Probleme mit mehreren Klassen (Klassen schließen sich gegenseitig aus), binäre_Crossentropie für Probleme mit mehreren Bezeichnungen zu verwenden.

Bazinga
quelle
8

Da es sich um ein Problem mit mehreren Klassen handelt, müssen Sie die kategoriale Kreuzentropie verwenden. Die binäre Kreuzentropie führt zu falschen Ergebnissen und bewertet höchstwahrscheinlich nur die ersten beiden Klassen.

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 Mindestleistung, die Sie durch Ausgabe einer zufälligen Klasse erzielen können.

Dr. Snoopy
quelle
2

Wenn Sie den categorical_crossentropyVerlust verwenden, sollten Ihre Ziele in einem kategorialen Format vorliegen (z. B. wenn Sie 10 Klassen haben, sollte das Ziel für jede Stichprobe ein 10-dimensionaler Vektor sein, der aus Nullen besteht, mit Ausnahme einer 1 im Index, der der Klasse der Klasse entspricht Stichprobe).

Priyansh
quelle
3
Wie genau beantwortet dies die Frage?
Desertnaut
2

Schauen Sie sich die Gleichung an, mit der Sie diese binäre Kreuzentropie finden können nicht nur die Bezeichnung = 1, vorhergesagt = 0, sondern auch die Bezeichnung = 0, vorhergesagt = 1 bestraft.

Eine kategoriale Kreuzentropie bestraft jedoch nur das Label = 1, aber das vorhergesagte = 1. Deshalb gehen wir davon aus, dass es nur EIN Label gibt, das positiv ist.

Kuang Yan
quelle
1

Sie übergeben ein Zielarray mit Form (x-dim, y-dim), während Sie es als Verlust verwenden categorical_crossentropy. categorical_crossentropyerwartet, dass Ziele binäre Matrizen (1s und 0s) mit Form (Samples, Klassen) sind. Wenn Ihre Ziele ganzzahlige Klassen sind, können Sie sie über Folgendes in das erwartete Format konvertieren.

from keras.utils import to_categorical
y_binary = to_categorical(y_int)

Alternativ können Sie sparse_categorical_crossentropystattdessen die Verlustfunktion verwenden, die ganzzahlige Ziele erwartet.

model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
susan097
quelle
0

Die binary_crossentropy (y_target, y_predict) muss bei binären Klassifizierungsproblemen nicht angewendet werden. .

Im Quellcode von binary_crossentropy ()nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output) wurde tatsächlich die TensorFlow-Funktion verwendet. In der Dokumentation heißt es:

Misst den Wahrscheinlichkeitsfehler bei diskreten Klassifizierungsaufgaben, bei denen jede Klasse unabhängig ist und sich nicht gegenseitig ausschließt. Zum Beispiel könnte man eine Multilabel-Klassifizierung durchführen, bei der ein Bild gleichzeitig einen Elefanten und einen Hund enthalten kann.

翟志伟
quelle