215 votes

Pourquoi binary_crossentropy et categorical_crossentropy donnent des performances différentes pour le même problème ?

J'essaie d'entraîner un CNN à catégoriser un texte par sujet. Lorsque j'utilise l'entropie croisée binaire, j'obtiens une précision de ~80%, avec l'entropie croisée catégorielle, j'obtiens une précision de ~50%.

Je ne comprends pas pourquoi. Il s'agit d'un problème multiclasse, cela ne signifie-t-il pas que je dois utiliser l'entropie croisée catégorielle et que les résultats obtenus avec l'entropie croisée binaire n'ont aucun sens ?

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 je le compile soit comme ceci en utilisant categorical_crossentropy comme fonction de perte :

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

o

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

Intuitivement, il est logique que je veuille utiliser l'entropie croisée catégorielle, mais je ne comprends pas pourquoi j'obtiens de bons résultats avec le binaire et de mauvais résultats avec le catégorique.

24 votes

Si c'est un problème multi-classes, vous devez utiliser categorical_crossentropy . Les étiquettes doivent également être converties au format catégoriel. Voir to_categorical pour ce faire. Voir également les définitions des entropies croisées catégorielles et binaires. aquí .

0 votes

Mes étiquettes sont catégoriques, créées à l'aide de to_categorical (un vecteur chaud pour chaque classe). Cela signifie-t-il que la précision de ~80% de la crossentropie binaire est juste un chiffre bidon ?

0 votes

Je pense que oui. Si vous utilisez des étiquettes catégoriques, c'est-à-dire des vecteurs à une chaleur, alors vous voulez categorical_crossentropy . Si vous avez deux classes, elles seront représentées comme suit 0, 1 en étiquettes binaires et 10, 01 sous forme d'étiquettes catégoriques.

261voto

desertnaut Points 15220

La raison de cette apparente divergence de performance entre l'entropie croisée catégorielle et binaire est ce que l'utilisateur xtof54 a déjà signalé dans sa réponse ci-dessous c'est-à-dire :

la précision calculée avec la méthode Keras evaluate est tout simplement erronée lorsqu'on utilise l'algorithme binary_crossentropy avec plus de 2 étiquettes.

J'aimerais m'étendre davantage sur ce sujet, montrer le véritable problème sous-jacent, l'expliquer et proposer un remède.

Ce comportement n'est pas un bogue ; la raison sous-jacente est un problème plutôt subtil et non documenté dans la manière dont Keras utilise les techniques d'analyse des données. suppositions quelle précision utiliser, en fonction de la fonction de perte que vous avez choisie, lorsque vous incluez simplement metrics=['accuracy'] dans la compilation de votre modèle. En d'autres termes, si 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'])

ne produira pas ce que vous attendez, mais la raison n'en est pas l'utilisation de l'entropie croisée binaire (qui, au moins en principe, est une fonction de perte absolument valide).

Pourquoi cela ? Si vous vérifiez le code source métrique Keras ne définit pas une seule mesure de précision, mais plusieurs, parmi lesquelles binary_accuracy y categorical_accuracy . Ce qui se passe sous le capot est que, puisque vous avez choisi l'entropie croisée binaire comme fonction de perte et que vous n'avez pas spécifié de mesure de précision particulière, Keras en déduit (à tort...) que vous vous intéressez à l'entropie croisée binaire. binary_accuracy et c'est ce qu'elle renvoie - alors qu'en fait, vous êtes intéressé par l'option categorical_accuracy .

Vérifions que c'est bien le cas, en utilisant la fonction MNIST exemple 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'est-à-dire pour utiliser effectivement l'entropie croisée binaire comme fonction de perte (comme je l'ai dit, il n'y a rien de mal à cela, du moins en principe) tout en obtenant la catégorique précision requise par le problème en question, vous devez demander explicitement categorical_accuracy dans la compilation du modèle comme suit :

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

Dans l'exemple de MNIST, après l'entraînement, la notation et la prédiction de l'ensemble de test comme je l'ai montré ci-dessus, les deux métriques sont maintenant identiques, comme il se doit :

# 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 post, j'ai découvert que cette question avait déjà été identifiée dans cette réponse .

85voto

P. Laffitte Points 194

Tout dépend du type de problème de classification auquel vous êtes confronté. Il existe trois catégories principales

  • binaire classification (deux classes cibles),
  • multi-classes classification (plus de deux exclusif cibles),
  • multi-étiquettes classification (plus de deux non exclusif ), dans lequel plusieurs classes de cibles peuvent être activées en même temps.

Dans le premier cas, il faut utiliser l'entropie croisée binaire et coder les cibles sous forme de vecteurs à un coup.

Dans le second cas, l'entropie croisée catégorielle doit être utilisée et les cibles doivent être codées comme des vecteurs à un coup.

Dans le dernier cas, l'entropie croisée binaire doit être utilisée et les cibles doivent être codées comme des vecteurs à un coup. Chaque neurone (ou unité) de sortie est considéré comme une variable binaire aléatoire distincte, et la perte pour le vecteur entier de sorties est le produit de la perte des variables binaires individuelles. Il s'agit donc du produit de l'entropie croisée binaire pour chaque unité de sortie.

L'entropie croisée binaire est définie comme suit

enter image description here

et l'entropie croisée catégorielle est définie comme suit

enter image description here

c est l'indice courant sur le nombre de classes C .

1 votes

Êtes-vous sûr que les entropies croisées binaires et catégorielles sont définies comme dans les formules de cette réponse ?

0 votes

@nbro, en fait, le c est redondant dans la formule d'entropie croisée binaire, il n'a pas besoin d'y figurer (puisqu'il n'y a que deux classes et que la probabilité de chaque classe est intégrée dans l'indice y(x) . Sinon, ces formules devraient être correctes, mais remarquez que ce ne sont pas des pertes, mais des probabilités. Si vous voulez la perte, vous devez prendre les log de ces derniers.

0 votes

@Whynote Vous devriez expliquer pourquoi la formule de l'entropie croisée catégorielle semble apparemment plus simple que la formule de l'entropie croisée binaire. Vous devriez également expliquer ce que C , c et tous les autres symboles qu'il y a. (Oui, je suis familier avec le log-trick). De plus, dans tous les cas, vous dites que les cibles doivent être codées à chaud, mais vous le dites pour chaque cas, plutôt que de dire "pour tous les cas, les cibles doivent être codées à chaud". Vous devriez peut-être consacrer du temps à expliquer votre explication.

45voto

Alexander Svetkin Points 649

Je suis tombé sur un problème "inversé" - j'obtenais de bons résultats avec categorical_crossentropy (avec 2 classes) et de mauvais résultats avec binary_crossentropy. Il semble que le problème vienne de la mauvaise fonction d'activation. Les paramètres corrects étaient les suivants :

  • pour binary_crossentropy : activation sigmoïde, cible scalaire
  • pour categorical_crossentropy : activation softmax, cible codée à un coup

5 votes

Etes-vous sûr de la cible scalaire pour binary_crossentropy. Il semble que vous devriez utiliser une cible codée "many-hot" (par exemple [0 1 0 0 1 1]).

6 votes

Bien sûr. keras.io/losses/#usage-of-loss-functions Il est dit : "lorsque vous utilisez la perte categorical_crossentropy, vos cibles doivent être dans un format catégorique (par exemple, si vous avez 10 classes, la cible pour chaque échantillon doit être un vecteur à 10 dimensions qui est tout-zéro sauf pour un 1 à l'indice correspondant à la classe de l'échantillon)".

1 votes

Mais nous parlons de l'entropie croisée binaire - et non de l'entropie croisée catégorielle.

31voto

Marcin Możejko Points 19602

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 d'entraînement pourrait être un exemple du phénomène suivant :

  1. Au début, la classe la plus fréquente domine la perte - le réseau apprend donc à prédire principalement cette classe pour chaque exemple.
  2. Après avoir appris le modèle le plus fréquent, il commence à discriminer les classes moins fréquentes. Mais lorsque vous utilisez adam - le taux d'apprentissage a une valeur beaucoup plus petite que celle qu'il avait au début de la formation (c'est à cause de la nature de cet optimiseur). Cela rend l'apprentissage plus lent et empêche votre réseau de quitter, par exemple, un pauvre minimum local moins possible.

C'est pourquoi ce facteur constant pourrait être utile en cas de binary_crossentropy . Après de nombreuses époques, la valeur du taux d'apprentissage est supérieure à celle de l'époque. categorical_crossentropy cas. J'ai l'habitude de recommencer la formation (et la phase d'apprentissage) plusieurs fois lorsque je remarque un tel comportement ou/et d'ajuster le poids d'une classe en utilisant le schéma suivant :

class_weight = 1 / class_frequency

Ainsi, la perte d'une classe moins fréquente compense l'influence de la perte d'une classe dominante au début d'une formation et dans la suite d'un processus d'optimisation.

EDIT :

En fait - j'ai vérifié que même si dans le cas des maths :

binary_crossentropy = len(class_id_index) * categorical_crossentropy

devrait tenir - en cas de keras ce n'est pas vrai, parce que keras normalise automatiquement toutes les sorties pour que leur somme soit égale à 1 . C'est la raison réelle derrière ce comportement étrange, car en cas de multiclassification, une telle normalisation nuit à la formation.

2 votes

C'est une explication très plausible. Mais je ne suis pas sûr que ce soit vraiment la raison principale. Parce que j'ai aussi observé dans plusieurs de mes étudiants travaillent ce comportement bizarre en appliquant binary-X-ent au lieu de cat-X-ent (ce qui est une erreur). Et ceci est vrai même lorsque l'entraînement ne dure que 2 époques ! L'utilisation de class_weight avec des prieurs de classe inverses n'a pas aidé. Peut-être qu'un réglage rigoureux du taux d'apprentissage serait utile, mais les valeurs par défaut semblent favoriser bin-X-ent. Je pense que cette question mérite d'être approfondie...

2 votes

Attendez, non désolé, je ne comprends pas votre mise à jour : le softmax fait toujours la somme des sorties à 1, donc nous ne nous en soucions pas ? Et pourquoi cela nuirait-il à la formation, tant que nous n'avons qu'une seule classe d'or qui est correcte par exemple ?

24voto

xtof54 Points 435

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 découvert que la réponse est en fait très simple : la précision calculée avec la méthode Keras evaluate est tout simplement erroné lorsque l'on utilise binary_crossentropy avec plus de 2 étiquettes. Vous pouvez le vérifier en recalculant vous-même la précision (appelez d'abord la méthode Keras "predict", puis calculez le nombre de réponses correctes renvoyées par predict) : vous obtenez la véritable précision, qui est bien inférieure à celle de la méthode Keras "evaluate".

2 votes

J'ai également constaté un comportement similaire lors de la première itération.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X