Keras binary_crossentropy vs performance categorical_crossentropy?

Sto cercando di addestrare una CNN per classificare il testo per argomento. Quando uso binary_crossentropy ottengo ~ 80% acc, con categorical_crossentrop ottengo ~ 50% acc.

Non capisco perché sia ​​così. È un problema multiclass, vuol dire che devo usare categorico ei risultati binari sono privi di significato?

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

poi

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

o

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

Il motivo di questa apparente discrepanza di prestazioni tra l’entropia categoriale e quella binaria è ciò che @ xtof54 ha già segnalato nella sua risposta, ovvero:

l’accuratezza calcasting con il metodo Keras “valutare” è semplicemente errata quando si utilizza binary_crossentropy con più di 2 etichette

Mi piacerebbe approfondire questo argomento, dimostrare l’effettiva questione di fondo, spiegarlo e offrire un rimedio.

Questo comportamento non è un bug; la ragione di fondo è un problema piuttosto sottile e non documentato su come Keras indovina effettivamente quale accuratezza usare, a seconda della funzione di perdita che hai selezionato, quando includi semplicemente metrics=['accuracy'] nella 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 la ragione non è l’uso dell’entropia binario (che, almeno in linea di principio, è una funzione di perdita assolutamente valida).

Perché? Se si controlla il codice sorgente delle metriche , Keras non definisce una singola metrica di accuratezza, ma diverse, tra cui binary_accuracy e categorical_accuracy . Quello che succede sotto il cofano è che, dal momento che hai selezionato l’entropia incrociata binaria come funzione di perdita e non hai specificato una particolare metrica di accuratezza, Keras (erroneamente …) deduce che sei interessato binary_accuracy , e questo è ciò che ritorna – mentre in realtà sei interessato alla categorical_accuracy .

Verifichiamo che questo è il caso, usando l’ esempio CNN di 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 ovviare a questo, vale a dire utilizzare effettivamente l’entropia binaria come funzione di perdita (come ho detto, niente di sbagliato in questo, almeno in linea di principio) pur ottenendo l’accuratezza categoriale richiesta dal problema in questione, dovresti chiedere esplicitamente categorical_accuracy in la 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 mostrato 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 del 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 .

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

 binary_crossentropy = len(class_id_index) * categorical_crossentropy 

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

  1. All’inizio la class più frequente sta dominando la perdita, quindi la rete sta imparando a predire principalmente questa class per ogni esempio.
  2. Dopo aver appreso il modello più frequente inizia a discriminare tra classi meno frequenti. Ma quando si utilizza adam , il tasso di apprendimento ha un valore molto inferiore a quello che aveva all’inizio dell’allenamento (è a causa della natura di questo ottimizzatore). Rende la formazione più lenta e impedisce alla rete di es. Di lasciare un minimo locale povero meno ansible.

Ecco perché questo fattore costante potrebbe essere d’aiuto in caso di binary_crossentropy . Dopo molte epoche, il valore del tasso di apprendimento è maggiore rispetto al caso categorical_crossentropy . Solitamente riavvio l’allenamento (e la fase di apprendimento) alcune volte quando noto un tale comportamento o / e aggiustando un peso di class usando il seguente schema:

 class_weight = 1 / class_frequency 

Ciò rende la perdita da classi meno frequenti che bilancia l’influenza di una perdita di class dominante all’inizio di una formazione e in una parte ulteriore di un processo di ottimizzazione.

MODIFICARE:

In realtà, l’ho controllato anche se in matematica:

 binary_crossentropy = len(class_id_index) * categorical_crossentropy 

dovrebbe contenere – in caso di keras non è vero, perché keras normalizza automaticamente tutte le uscite per riassumere fino a 1 . Questa è la vera ragione alla base di questo strano comportamento, in quanto in caso di multiclassificazione tale normalizzazione danneggia un allenamento.

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 fosse con la funzione di triggerszione sbagliata. Le impostazioni corrette erano:

  • per binary_crossentropy : triggerszione binary_crossentropy , target scalare
  • per categorical_crossentropy : triggerszione softmax, target codificato a una temperatura elevata

Dopo aver commentato la risposta @Marcin, ho controllato più attentamente uno dei miei codici studenti in cui ho trovato lo stesso comportamento strano, 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 calcasting con il metodo Keras “valutare” è semplicemente sbagliata quando si utilizza binary_crossentropy con più di 2 etichette. Puoi verificarlo ricalcolandone la precisione da solo (prima chiama il metodo Keras “prevedi” e poi calcola il numero di risposte corrette restituite da predire): ottieni la precisione esatta, che è molto inferiore a quella di Keras “valuta”.

Tutto dipende dal tipo di problema di classificazione che stai affrontando. Ci sono tre categorie principali;

  • classificazione binaria (due classi objective)
  • classificazione multi-class (più di due obiettivi esclusivi )
  • classificazione multi-label (più di due target non esclusivi ) in cui più classi target possono essere attive contemporaneamente

Nel primo caso, dovrebbe essere utilizzata l’entropia incrociata binaria e gli obiettivi dovrebbero essere codificati come vettori ad uno caldo.

Nel secondo caso, dovrebbe essere utilizzata l’entropia incrociata categoriale e gli obiettivi dovrebbero essere codificati come vettori one-hot.

Nell’ultimo caso, dovrebbe essere utilizzata l’entropia incrociata binaria e gli obiettivi dovrebbero essere codificati come vettori ad uno caldo. Ogni neurone (o unità) di uscita è 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 di cross-entropy binario per ogni singola unità di uscita.

l’entropia incrociata binaria è definita come tale: l’ entropia incrociata binaria e l’entropia incrociata categoriale è definita come tale: cross-entropia categoriale

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

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

quando si utilizza la perdita categorical_crossentropy , i target devono essere in formato categoriale (ad es. se si hanno 10 classi, l’objective per ogni campione deve essere un vettore 10-dimensionale che è tutto-zero eccetto un 1 nell’indice corrispondente alla class di il campione).