web-dev-qa-db-fra.com

Keras binary_crossentropy vs categorical_crossentropy performance?

J'essaie de former un CNN pour catégoriser le texte par sujet. Lorsque j'utilise binary_crossentropy, j'obtiens environ 80% de CA.

Je ne comprends pas pourquoi c'est. C'est un problème de multi-classes, cela signifie-t-il que je dois utiliser catégorique et que les résultats binaires n'ont pas de 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 

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

ou 

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

La raison de cet écart de performance apparent entre entropie croisée catégorique et binaire est ce que @ xtof54 a déjà signalé dans sa réponse, à savoir:

la précision calculée avec la méthode de Keras evaluate est tout à fait simple faux lors de l'utilisation de binary_crossentropy avec plus de 2 étiquettes

J'aimerais approfondir ce sujet, exposer le problème sous-jacent, l'expliquer et proposer un remède.

Ce comportement n'est pas un bug. la raison sous-jacente est une question assez subtile et non documentée concernant la façon dont Keras devine quelle précision utiliser, en fonction de la fonction de perte 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 valide, votre deuxième:

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

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

Pourquoi donc? Si vous vérifiez le code source des métriques }, Keras ne définit pas une métrique de précision unique, mais plusieurs différentes, parmi lesquelles binary_accuracy et categorical_accuracy. Qu'est-ce qui se passe (sous le capot } _ _ c'est que, puisque vous avez sélectionné 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 (à tort ...) en déduit que le code binary_accuracy vous intéresse, et c’est ce qu’il retourne - alors que vous êtes intéressé par le categorical_accuracy.

Vérifions que c'est le cas, en utilisant le exemple de 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’est-à-dire pour utiliser effectivement l’entropie croisée binaire comme fonction de perte (comme je l’ai dit, rien de mal à cela, du moins en principe) tout en obtenant la précision catégorique requise par le problème, vous devez demander explicitement pour 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 MNIST, après l'entraînement, la notation et la prédiction de l'ensemble de tests, comme indiqué ci-dessus, les deux métriques sont maintenant identiques, 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    

Installation du système:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

UPDATE: Après ma publication, j'ai découvert que ce problème avait déjà été identifié dans cette réponse .

106
desertnaut

C'est un cas vraiment intéressant. En fait, dans votre configuration, la déclaration suivante est vraie:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Cela signifie que vos pertes sont équivalentes jusqu'à un facteur de multiplication constant. Le comportement étrange que vous observez pendant une phase de formation peut ê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évoir principalement cette classe pour chaque exemple.
  2. Après avoir appris le schéma le plus fréquent, il commence à discriminer les classes les moins fréquentes. Mais lorsque vous utilisez adam - le taux d’apprentissage a une valeur bien inférieure à celle qu’il avait au début de la formation (c’est en raison de la nature de cet optimiseur). Cela ralentit la formation et empêche votre réseau, par exemple, de laissant un minimum local faible moins possible.

C'est pourquoi ce facteur constant pourrait aider en cas de binary_crossentropy. Après de nombreuses époques - la valeur du taux d’apprentissage est supérieure à celle de categorical_crossentropy. Je reprends généralement l'entraînement (et la phase d'apprentissage) plusieurs fois lorsque je remarque un tel comportement et/ou que je modifie les poids d'une classe en utilisant le schéma suivant:

class_weight = 1 / class_frequency

Cela rend les pertes de classes moins fréquentes équilibrant l’influence d’une perte de classe dominante au début d’une formation et dans un processus supplémentaire d’optimisation.

MODIFIER:

En fait - j'ai vérifié cela même si en cas de maths:

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 totaliser 1. C’est la raison réelle de ce comportement étrange, car en cas de multiclassification, cette normalisation nuit à la formation.

24
Marcin Możejko

Je suis tombé sur un problème "inversé" - je obtenais de bons résultats avec categorical_crossentropy (avec 2 classes) et médiocre avec binary_crossentropy. Il semble que ce problème était lié à une fonction d’activation incorrecte. Les paramètres corrects étaient:

  • pour binary_crossentropy: activation sigmoïde, cible scalaire 
  • pour categorical_crossentropy: activation softmax, cible encodée à chaud
18
Alexander Svetkin

Après avoir commenté la réponse de @Marcin, j'ai plus soigneusement vérifié le code de l'un de mes étudiants où j'ai trouvé le même comportement étrange, 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 réalité très simple: l’exactitude calculée avec la méthode Keras evaluate est tout simplement fausse lorsqu’on utilise binary_crossentropy avec plus de 2 étiquettes. Vous pouvez vérifier cela en recalculant vous-même la précision (appelez d’abord la méthode de Keras "prédire" puis calculez le nombre de réponses correctes renvoyées par predict): vous obtenez la précision réelle, qui est bien inférieure à celle de Keras "évaluer".

12
xtof54

Comme il s’agit d’un problème multi-classes, vous devez utiliser categorical_crossentropy, l’entropie croisée binaire produira des résultats erronés, et évaluera probablement uniquement les deux premières classes.

50% pour un problème multi-classes peut être assez bon, selon le nombre de classes. Si vous avez n classes, 100/n est la performance minimale que vous pouvez obtenir en affichant une classe aléatoire.

4
Matias Valdenegro

lorsque vous utilisez la perte categorical_crossentropy, vos cibles doivent être au format catégorique (par exemple, si vous avez 10 classes, la cible de chaque échantillon doit être un vecteur à 10 dimensions entièrement en zéros, à l'échantillon).

1
Priyansh

un exemple simple dans un contexte multi-classes pour illustrer

supposons que vous ayez 4 classes (onehot encodé) et que ci-dessous ne représente qu'une prédiction

true_label = [0,1,0,0] étiquette prédite = [0,0,1,0]

lors de l'utilisation de categorical_crossentropy, la précision est de 0, il importe uniquement si la classe concernée est correcte.

cependant, lorsque vous utilisez binary_crossentropy, la précision est calculée pour toutes les classes, elle serait de 50% pour cette prédiction. et le résultat final sera la moyenne des précisions individuelles pour les deux cas.

il est recommandé d'utiliser catégorical_crossentropy pour les problèmes multi-classes (les classes s'excluent mutuellement) mais binary_crossentropy pour les problèmes multi-étiquettes.

1
bazinga

Vous passez un tableau cible de forme (x-dim, y-dim) en utilisant comme perte categorical_crossentropy. categorical_crossentropy s'attend à ce que les cibles soient des matrices binaires (1 et 0) de forme (échantillons, classes). Si vos cibles sont des classes entières, vous pouvez les convertir au format attendu via:

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

Vous pouvez également utiliser la fonction de perte sparse_categorical_crossentropy, qui attend des cibles entières.

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

Jetez un coup d'œil à l'équation: vous pouvez constater que l'entropie croisée binaire punit non seulement les libellés = 1, prédits = 0, mais également les libellés = 0, prédits = 1.

Cependant une entropie croisée catégorique ne punit que ceux qui ont l'étiquette = 1 mais qui ont été prédits = 1. C'est pourquoi nous supposons qu'il n'y a qu'une seule étiquette positive.

0
Kuang Yan