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'])
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 .
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:
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.
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:
binary_crossentropy
: activation sigmoïde, cible scalaire categorical_crossentropy
: activation softmax, cible encodée à chaudAprè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".
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.
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).
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.
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'])
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.