Perché binary_crossentropy e categorical_crossentropy offrono prestazioni diverse per lo stesso problema?


160

Sto cercando di formare una CNN per classificare il testo per argomento. Quando utilizzo l'entropia incrociata binaria ottengo un'accuratezza dell'80% circa, con un'entropia incrociata categorica un'accuratezza del 50% circa.

Non capisco perché questo sia. È un problema multiclasse, non significa che devo usare l'entropia incrociata categorica e che i risultati con l'entropia incrociata binaria sono insignificanti?

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'))

Quindi lo compilo in questo modo usando categorical_crossentropycome funzione di perdita:

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

o

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

Intuitivamente ha senso il motivo per cui vorrei usare l'entropia incrociata categorica, non capisco perché ottengo buoni risultati con i binari e scarsi risultati con i categorici.


10
Se si tratta di un problema multiclasse, è necessario utilizzare categorical_crossentropy. Anche le etichette devono essere convertite nel formato categoriale. Vedere to_categoricalper fare questo. Vedi anche le definizioni di crossentropie categoriche e binarie qui .
Autonomo

Le mie etichette sono categoriali, create usando to_categorical (un vettore caldo per ogni classe). Ciò significa che l'accuratezza dell'80% della crossentropia binaria è solo un numero falso?
Daniel Messias,

Credo di si. Se usi etichette categoriche, ad esempio un vettore caldo, allora vuoi categorical_crossentropy. Se hai due classi, saranno rappresentate come 0, 1in etichette binarie e 10, 01in formato di etichetta categoriale.
Autonomo

1
Penso che paragona solo al primo numero nel vettore e ignora il resto.
Thomas Pinetz,

2
@NilavBaranGhosh La rappresentazione sarà [[1, 0], [0, 1]] per una classificazione categorica che coinvolge due classi (non [[0, 0], [0, 1]] come dici tu). Dense(1, activation='softmax')per la classificazione binaria è semplicemente sbagliato. Ricorda che l'output di softmax è una distribuzione di probabilità che si somma a una. Se vuoi avere un solo neurone in uscita con classificazione binaria, usa sigmoid con entropia binaria binaria.
Autonomo

Risposte:


204

Il motivo di questa apparente discrepanza nelle prestazioni tra entropia incrociata categorica e binaria è ciò che l'utente xtof54 ha già segnalato nella sua risposta di seguito , ovvero:

l'accuratezza calcolata con il metodo Keras evaluateè semplicemente sbagliata quando si usa binary_crossentropy con più di 2 etichette

Vorrei approfondire ulteriormente questo aspetto, dimostrare l'effettivo problema di fondo, spiegarlo e offrire un rimedio.

Questo comportamento non è un bug; la ragione di fondo è una questione piuttosto sottile e priva di documenti su come Keras effettivamente indovini quale accuratezza usare, a seconda della funzione di perdita che hai selezionato, quando includi semplicemente metrics=['accuracy']nella tua compilazione del modello. In altre parole, mentre la tua prima opzione di compilazione

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

è valido, il tuo secondo:

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

non produrrà ciò che ti aspetti, ma il motivo non è l'uso dell'entropia binaria incrociata (che, almeno in linea di principio, è una funzione di perdita assolutamente valida).

Perché? Se controlli il codice sorgente delle metriche , Keras non definisce una singola metrica di precisione, ma diverse tra loro binary_accuracye categorical_accuracy. Quello che succede sotto il cofano è che, poiché hai selezionato l'entropia incrociata binaria come funzione di perdita e non hai specificato una particolare metrica di precisione, Keras (erroneamente ...) deduce che ti interessa binary_accuracy, e questo è ciò che restituisce - mentre in realtà sei interessato acategorical_accuracy .

Verifichiamo che sia così, usando l' esempio CNN MNIST in Keras, con la seguente modifica:

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    

Per rimediare a questo, vale a dire usare effettivamente l'entropia incrociata binaria come funzione di perdita (come ho detto, niente di sbagliato in questo, almeno in linea di principio) pur ottenendo il categorico accuratezza richiesta dal problema in questione, dovresti chiedere esplicitamente categorical_accuracynel compilazione del modello come segue:

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

Nell'esempio MNIST, dopo l'allenamento, il punteggio e la previsione del set di test, come mostrerò sopra, le due metriche ora sono le stesse, come dovrebbero essere:

# 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    

Configurazione di sistema:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

AGGIORNAMENTO : Dopo il mio post, ho scoperto che questo problema era già stato identificato in questa risposta .


1
C'è qualcosa di sbagliato nell'utilizzo loss='categorical_crossentropy', metrics=['categorical_accuracy']per la classificazione multiclasse? Questa sarebbe la mia intuizione
NeStack

2
@NeStack Non solo non c'è niente di sbagliato, ma questa è la combinazione nominale.
Desertnaut

1
Secondo quello che hai detto, finché uso loss = 'binary_crossentropy', otterrò gli stessi ritorni non più opaco uso metrics = 'binary_accuracy' o metrics = 'accurate'?
BioCoder

2
@BioCoder esattamente
desertnaut

54

Tutto dipende dal tipo di problema di classificazione che stai affrontando. Esistono tre categorie principali

  • classificazione binaria (due classi target),
  • classificazione multi-classe (più di due obiettivi esclusivi ),
  • classificazione multi-etichetta (più di due target non esclusivi ), in cui più classi target possono essere attive contemporaneamente.

Nel primo caso, si dovrebbe usare l'entropia binaria incrociata e i target dovrebbero essere codificati come vettori one-hot.

Nel secondo caso, si dovrebbe usare l'entropia incrociata categorica e gli obiettivi dovrebbero essere codificati come vettori one-hot.

Nell'ultimo caso, si dovrebbe usare l'entropia binaria incrociata e i target dovrebbero essere codificati come vettori one-hot. Ogni neurone (o unità) di output è considerato come una variabile binaria casuale separata e la perdita per l'intero vettore di output è il prodotto della perdita di singole variabili binarie. Pertanto è il prodotto dell'entropia binaria per ogni singola unità di output.

L'entropia incrociata binaria è definita come

inserisci qui la descrizione dell'immagine

e l'entropia incrociata categorica è definita come

inserisci qui la descrizione dell'immagine

dove si ctrova l'indice in esecuzione sul numero di classi


La tua risposta mi sembra molto vera, ma ... ho provato a seguire la risposta di @desertnaut e ho fatto questi test: con la funzione di perdita binary_crossentropy e metrcis a categorical_accurency ho una precisione migliore rispetto all'utilizzo della funzione di perdita categorical_crossentropy e metriche di accuratezza - e non posso spiegare che ...
Metal3d

@ Metal3d: qual è la formulazione del tuo problema: multi-label o single-label?
Whynote,

etichetta singola, e ora capisco perché funziona meglio :)
Metal3d

Sei sicuro che le entropropie binarie e categoriche siano definite come nelle formule di questa risposta?
nbro

@nbro, in realtà, l' cindice è ridondante nella formula binaria dell'entropia incrociata, non ha bisogno di essere lì (poiché ci sono solo 2 classi e la probabilità di ciascuna classe è incorporata y(x). Altrimenti quelle formule dovrebbero essere corrette, ma nota che queste non sono perdite, queste sono probabilità. Se vuoi la perdita, devi prenderne una log.
Whynote

40

Mi sono imbattuto in un problema "invertito": stavo ottenendo buoni risultati con categorical_crossentropy (con 2 classi) e scarso con binary_crossentropy. Sembra che il problema riguardasse una funzione di attivazione errata. Le impostazioni corrette erano:

  • per binary_crossentropy: attivazione sigmoide, bersaglio scalare
  • per categorical_crossentropy: attivazione softmax, target con codifica a caldo singolo

4
Sei sicuro del target scalare per binary_crossentropy. Sembra che dovresti usare un target codificato "molti-caldo" (ad es. [0 1 0 0 1 1]).
Dmitry,

5
Sicuro. Vedi keras.io/losses/#usage-of-loss-functions , dice: "quando si utilizza la perdita categorical_crossentropy, i target dovrebbero essere in formato categoriale (ad esempio se si hanno 10 classi, l'obiettivo per ciascun campione dovrebbe essere un 10 tridimensionale che è tutto zero aspettano un 1 nell'indice corrispondente alla classe del campione) "
Alexander Svetkin,

1
Ma stiamo parlando di binary_crossentropy, non di categorical_crossentropy.
Dmitry,

Questa risposta sembra incoerente con stackoverflow.com/a/49175655/3924118 , in cui l'autore afferma che gli obiettivi dovrebbero essere codificati in un'unica soluzione, mentre, nella tua risposta, suggerisci che dovrebbero essere scalari. Dovresti chiarire questo.
nbro

@AlexanderSvetkin, il target dovrebbe essere codificato a caldo ovunque, non solo quando si utilizza l'entropia incrociata categorica
Whynote,

28

È un caso davvero interessante. In realtà nella tua configurazione è vera la seguente affermazione:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Ciò significa che fino a un fattore di moltiplicazione costante le perdite sono equivalenti. Lo strano comportamento che stai osservando durante una fase di allenamento potrebbe essere un esempio di un fenomeno seguente:

  1. All'inizio la classe più frequente sta dominando la perdita, quindi la rete sta imparando a prevedere principalmente questa classe per ogni esempio.
  2. Dopo aver appreso il modello più frequente, inizia a discriminare tra le classi meno frequenti. Ma quando si utilizza adam, il tasso di apprendimento ha un valore molto inferiore rispetto a quello all'inizio dell'allenamento (è a causa della natura di questo ottimizzatore). Rende la formazione più lenta e impedisce alla tua rete di lasciare un minimo locale scarso meno possibile.

Ecco perché questo fattore costante potrebbe aiutare in caso di binary_crossentropy. Dopo molte epoche - il valore del tasso di apprendimento è maggiore che nel categorical_crossentropycaso. Di solito riavvio l'allenamento (e la fase di apprendimento) alcune volte quando noto tale comportamento o / e adeguo i pesi di una classe utilizzando il seguente schema:

class_weight = 1 / class_frequency

Questo fa perdere da classi meno frequenti bilanciando l'influenza di una perdita di classe dominante all'inizio di una formazione e in un'ulteriore parte di un processo di ottimizzazione.

MODIFICARE:

In realtà - l'ho verificato anche se in caso di matematica:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

dovrebbe essere valido, nel caso in cui kerasnon fosse vero, perché kerassta normalizzando automaticamente tutti gli output per riassumere 1. Questo è il vero motivo alla base di questo strano comportamento, poiché in caso di multiclassificazione tale normalizzazione danneggia un allenamento.


La mia risposta ti ha aiutato?
Marcin Możejko,

1
Questa è una spiegazione molto plausibile. Ma non sono sicuro che sia davvero il motivo principale. Perché ho anche osservato in molti dei miei studenti lavorare a questo strano comportamento quando si applica X-ent binaria anziché cat-X-ent (che è un errore). E questo è vero anche quando ci si allena per solo 2 epoche! L'uso di class_weight con classi precedenti inverse non ha aiutato. Potrebbe essere una messa a punto rigorosa della velocità di apprendimento, ma i valori predefiniti sembrano favorire bin-X-ent. Penso che questa domanda meriti ulteriori indagini ...
xtof54

1
Aspetta, non mi dispiace, non ricevo il tuo aggiornamento: il softmax fa sempre sommare gli output a 1, quindi non ci interessa? E perché questo danneggerebbe l'allenamento, purché ci sia una sola classe d'oro corretta per esempio?
xtof54,

20

Dopo aver commentato la risposta di @Marcin, ho controllato più attentamente uno dei miei codici degli studenti in cui ho trovato lo stesso strano comportamento, anche dopo solo 2 epoche! (Quindi la spiegazione di @ Marcin non era molto probabile nel mio caso).

E ho scoperto che la risposta è in realtà molto semplice: l'accuratezza calcolata con il metodo Keras evaluateè semplicemente sbagliata quando si utilizza binary_crossentropy con più di 2 etichette. Puoi verificarlo ricompilando tu stesso l'accuratezza (prima chiama il metodo di Keras "predire" e poi calcola il numero di risposte corrette restituite da predict): ottieni la precisione esatta, che è molto più bassa di quella di "valutare" di Keras.


1
Ho visto un comportamento simile anche sulla prima iterazione.
dolbi,

10

un semplice esempio in un'impostazione multi-classe per illustrare

supponiamo di avere 4 classi (codificato in un colpo) e di seguito è solo una previsione

true_label = [0,1,0,0] predicted_label = [0,0,1,0]

quando si utilizza categorical_crossentropy, la precisione è solo 0, importa solo se si ottiene la classe interessata giusta.

tuttavia quando si utilizza binary_crossentropy, l'accuratezza viene calcolata per tutte le classi, per questa previsione sarebbe del 50%. e il risultato finale sarà la media delle accuratezze individuali per entrambi i casi.

si consiglia di utilizzare categorical_crossentropy per problemi multi-classe (le classi si escludono a vicenda) ma binary_crossentropy per problemi multi-etichetta.


8

Poiché si tratta di un problema multi-classe, devi usare la categorical_crossentropy, l'entropia binaria incrociata produrrà risultati fasulli, molto probabilmente valuterà solo le prime due classi.

Il 50% per un problema multi-classe può essere abbastanza buono, a seconda del numero di classi. Se hai n classi, allora 100 / n è la prestazione minima che puoi ottenere emettendo una classe casuale.


2

quando si utilizza la categorical_crossentropyperdita, i target devono essere in formato categoriale (ad esempio se si dispone di 10 classi, il target per ciascun campione dovrebbe essere un vettore 10-dimensionale che è tutto-zeri tranne un 1 nell'indice corrispondente alla classe del campione).


3
Come risponde esattamente alla domanda?
Desertnaut,


1

Stai passando una matrice target di forma (x-dim, y-dim) mentre usi come perdita categorical_crossentropy. categorical_crossentropysi aspetta che gli obiettivi siano matrici binarie (1 e 0) di forma (campioni, classi). Se i tuoi target sono classi intere, puoi convertirli nel formato previsto tramite:

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

In alternativa, è possibile utilizzare sparse_categorical_crossentropyinvece la funzione di perdita , che prevede obiettivi interi.

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

0

Binary_crossentropy (y_target, y_predict) non deve essere applicato nel problema di classificazione binaria. .

Nel codice sorgente di binary_crossentropy () , è nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)stata effettivamente utilizzata la funzione TensorFlow. E, nella documentazione , si dice che:

Misura l'errore di probabilità in compiti di classificazione discreti in cui ogni classe è indipendente e non si escludono a vicenda. Ad esempio, si potrebbe eseguire la classificazione multilabel in cui un'immagine può contenere contemporaneamente un elefante e un cane.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.