Keras binaire crossentropy vs categorical crossentropy performance?
j'essaie de former un CNN pour catégoriser le texte par sujet. Quand j'utilise binary_crossentropy j'obtiens ~80% acc, avec categoric_crossentrop j'obtiens ~50% acc.
Je ne comprends pas pourquoi. C'est un problème multiclasse, ça veut dire que je dois utiliser catégorique et les résultats binaires sont insignifiants?
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'))
puis
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
ou
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
7 réponses
la raison de cette apparente divergence de performance entre l'entropie croisée catégorique et binaire est ce que @xtof54 a déjà rapporté dans sa réponse, i.e.:
la précision calculée avec la méthode Keras "evaluate" est juste simple faux en utilisant binary_crossentropy avec plus de 2 étiquettes
je voudrais développer plus sur ce, démontrer la question sous-jacente réelle, l'expliquer, et offrir un recours.
ce comportement n'est pas un bug; la raison sous-jacente est un problème plutôt subtil et non documenté à la façon dont Keras devine quelle précision utiliser, en fonction de la fonction de perte que vous avez sélectionnée, lorsque vous incluez simplement metrics=['accuracy']
dans votre compilation de modèle. En d'autres termes, alors que votre première option de compilation
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
est valable, votre deuxième:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
sera ne pas produire ce que vous attendez, mais la raison n'est pas l'utilisation de l'entropie croisée binaire (qui, au moins en principe, est une fonction de perte absolument valide).
pourquoi? Si vous cochez le metrics source code , Keras ne définit pas une seule mesure de précision, mais plusieurs différentes, dont binary_accuracy
et categorical_accuracy
. Ce qui se passe sous le capot est que, puisque vous avez sélectionné l'entropie croisée binaire comme votre fonction de perte et n'ont pas spécifié une précision particulière métrique, Keras (à tort...) implique que vous êtes intéressé par le binary_accuracy
, et c'est ce qu'il retourne - alors qu'en fait vous êtes intéressé par le categorical_accuracy
.
vérifions que c'est le cas, en utilisant L'exemple MNIST CNN dans Keras, avec la modification suivante:
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
pour remédier à cela, c.-à-d. d'utiliser en effet l'entropie croisée binaire comme votre fonction de perte (comme je l'ai dit, rien de mal avec cela, au moins en principe) tout en obtenant toujours le catégorique précision exigée par le problème à la main, vous devriez demander explicitement pour categorical_accuracy
dans la compilation de modèle comme suit:
from keras.metrics import categorical_accuracy
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])
dans l'exemple du MNIST, après l'entraînement, la notation et la prévision du test comme je le montre ci-dessus, les deux mesures sont maintenant les mêmes, comme elles devraient l'être:
# 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
Configuration du système:
Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4
UPDATE : après mon billet, j'ai découvert que ce problème avait déjà été identifié dans this answer .
c'est un cas très intéressant. En fait, dans votre configuration, l'affirmation suivante est vraie:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
cela signifie que jusqu'à un facteur de multiplication constant vos pertes sont équivalentes. Le comportement étrange que vous observez pendant une phase de formation pourrait être un exemple d'un phénomène suivant:
- au début la classe la plus fréquente domine le réseau de perte-ainsi apprend à prédire la plupart du temps ce classe pour chaque exemple.
- après avoir appris le schéma le plus fréquent, il commence à discriminer parmi les classes moins fréquentes. Mais quand vous utilisez
adam
- le taux d'apprentissage a une valeur beaucoup plus faible qu'il avait au début de la formation (c'est en raison de la nature de cet optimiseur). Cela rend la formation plus lente et empêche votre réseau, par exemple, de laisser un mauvais minimum local moins possible.
C'est pourquoi ce facteur constant peut aider en cas de binary_crossentropy
. Après de nombreuses époques , la valeur du taux d'apprentissage est plus grande que dans le cas categorical_crossentropy
. J'ai l'habitude de reprendre la formation (et la phase d'apprentissage) à quelques reprises lorsque je remarque un tel comportement ou / et d'ajuster les poids d'une classe en utilisant le modèle suivant:
class_weight = 1 / class_frequency
Cela fait une perte d'une classe moins fréquente équilibrant l'influence d'une perte de classe dominante au début d'une formation et dans une partie ultérieure d'un processus d'optimisation.
EDIT:
en fait - j'ai vérifié que même si en cas de mathématiques:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
devrait tenir - dans le cas de keras
ce n'est pas vrai, parce que keras
normalise automatiquement toutes les sorties pour résumer à 1
. C'est la véritable raison de ce comportement étrange car, dans le cas d'une classification multiple, une telle normalisation nuit à la formation.
je suis tombé sur un problème" inversé " - j'obtenais de bons résultats avec categoric_crossentropy (avec 2 classes) et pauvre avec binary_crossentropy. Il semble que le problème était avec la mauvaise fonction d'activation. Les réglages corrects étaient:
- pour
binary_crossentropy
: activation sigmoïde, cible scalaire - pour
categorical_crossentropy
: activation softmax, cible encodée une fois
après avoir commenté la réponse de @Marcin, j'ai vérifié plus attentivement le code d'un de mes étudiants où j'ai trouvé le même comportement bizarre, même après seulement 2 époques ! (Donc l'explication de @Marcin n'était pas très probable dans mon cas).
et j'ai trouvé que la réponse est en fait très simple: l'exactitude calculée avec la méthode Keras" evaluate " est tout simplement faux en utilisant binary_crossentropy avec plus de 2 étiquettes. Vous pouvez vérifier cela en recalculant la précision vous-même (d'abord appeler la méthode de Keras "prédire", puis calculer le nombre de réponses correctes retournées par prédire): vous obtenez la vraie précision, qui est beaucoup plus faible que le Keras "évaluer" un.
tout dépend du type de problème de classification que vous traitez. Il y a trois catégories principales:
- binaire classification (deux classes cibles)
- classe multiple classification (plus de deux cibles "exclusives )
- multi-label classification (plus de deux non exclusive cibles) dans lesquelles plusieurs classes de cibles peuvent être activées en même temps
dans le premier cas, l'entropie croisée binaire doit être utilisée et les cibles doivent être encodées comme des vecteurs à une seule température.
dans le deuxième cas, il faut utiliser une entropie croisée catégorique et encoder les cibles comme des vecteurs à une chaleur.
dans le dernier cas, l'entropie croisée binaire doit être utilisée et les cibles doivent être encodées comme des vecteurs à une seule température. Chaque neurone de sortie (ou unité) est considéré comme une variable binaire aléatoire séparée, et la perte pour l'ensemble du vecteur de sorties est le produit de la perte de variables binaires simples. Par conséquent, il est le produit de binaire d'entropie croisée pour chaque unité de sortie.
entropie binaire croisée est définie comme telle: entropie binaire croisée et l'entropie croisée catégorique est définie comme telle: catégorique d'entropie croisée
comme il s'agit d'un problème multi-classes, vous devez utiliser la categorical_crossentropy, l'entropie binaire croisée produira des résultats bidons, très probablement ne évaluera que les deux premières classes seulement.
50% pour un problème multi-classes peut être assez bon, selon le nombre de classes. Si vous avez n classes, alors 100 / n est la performance minimale que vous pouvez obtenir en sortant une classe aléatoire.
lorsque vous utilisez la perte categorical_crossentropy
, vos cibles doivent être en format catégorique (par exemple, si vous avez 10 classes, la cible pour chaque échantillon doit être un vecteur en 10 dimensions qui est de tous les zéros sauf un 1 à l'index correspondant à la classe de l'échantillon).