Dlaczego binary_crossentropy i categorical_crossentropy dają różne wyniki dla tego samego problemu?


160

Próbuję wytresować CNN do kategoryzowania tekstu według tematu. Kiedy używam binarnej entropii krzyżowej, uzyskuję ~ 80% dokładności, przy kategorycznej entropii krzyżowej uzyskuję ~ 50% dokładności.

Nie rozumiem, dlaczego tak jest. Jest to problem wieloklasowy, czy nie oznacza to, że muszę używać kategorycznej entropii krzyżowej i że wyniki z binarną entropią krzyżową są bez znaczenia?

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

Następnie kompiluję go w ten sposób, używając categorical_crossentropyjako funkcji utraty:

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

lub

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

Intuicyjnie ma sens, dlaczego chciałbym użyć kategorycznej entropii krzyżowej, nie rozumiem, dlaczego uzyskuję dobre wyniki w przypadku binarności i słabe wyniki w kategoriach.


10
Jeśli jest to problem z wieloma klasami, musisz użyć categorical_crossentropy. Etykiety również muszą zostać przekonwertowane na format kategoryczny. Zobacz, jak to_categoricalto zrobić. Zobacz także definicje jakościowych i binarnych krzyżówek tutaj .
Autonomiczny

Moje etykiety są kategoryczne, tworzone za pomocą to_categorical (jeden gorący wektor dla każdej klasy). Czy to oznacza, że ​​~ 80% dokładności z binarnej crossentropy to tylko fałszywa liczba?
Daniel Messias,

Chyba tak. Jeśli używasz etykiet kategorialnych, tj. Jednego gorącego wektora, to chcesz categorical_crossentropy. Jeśli masz dwie klasy, będą one reprezentowane jako 0, 1etykiety binarne oraz 10, 01w formacie etykiety kategorialnej.
Autonomiczny

1
Myślę, że porównuje on tylko pierwszą liczbę w wektorze i ignoruje resztę.
Thomas Pinetz,

2
@NilavBaranGhosh Reprezentacja będzie [[1, 0], [0, 1]] dla klasyfikacji kategorialnej obejmującej dwie klasy (nie [[0, 0], [0, 1]], jak wspomniałeś). Dense(1, activation='softmax')klasyfikacja binarna jest po prostu błędna. Pamiętaj, że wynik softmax to rozkład prawdopodobieństwa, który sumuje się do jednego. Jeśli chcesz mieć tylko jeden neuron wyjściowy z klasyfikacją binarną, użyj sigmoidy z binarną entropią krzyżową.
Autonomiczny

Odpowiedzi:


204

Przyczyną tej pozornej rozbieżności w wydajności między kategoryczną i binarną entropią krzyżową jest to, co użytkownik xtof54 już zgłosił w swojej odpowiedzi poniżej , tj .:

dokładność obliczona metodą Keras evaluatejest po prostu błędna, gdy używa się binary_crossentropy z więcej niż 2 etykietami

Chciałbym omówić to bardziej szczegółowo, przedstawić faktyczny problem, wyjaśnić go i zaproponować rozwiązanie.

To zachowanie nie jest błędem; Podstawową przyczyną jest raczej subtelny i nieudokumentowany problem, w jaki sposób Keras faktycznie zgaduje, której dokładności użyć, w zależności od wybranej funkcji straty, gdy po prostu metrics=['accuracy']włączasz ją do kompilacji modelu. Innymi słowy, podczas gdy twoja pierwsza opcja kompilacji

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

jest ważny, twój drugi:

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

nie wytworzy tego, czego się spodziewasz, ale powodem nie jest użycie binarnej entropii krzyżowej (która, przynajmniej w zasadzie, jest absolutnie ważną funkcją straty).

Dlaczego? Jeśli sprawdzisz kod źródłowy metryk , Keras nie definiuje jednej metryki dokładności, ale kilka różnych, między innymi binary_accuracyi categorical_accuracy. To, co dzieje się pod maską, polega na tym, że skoro wybrałeś binarną entropię krzyżową jako funkcję straty i nie określiłeś konkretnej miary dokładności, Keras (błędnie ...) wnioskuje, że jesteś zainteresowany binary_accuracy, i to właśnie zwraca - podczas gdy w rzeczywistości jesteś zainteresowany categorical_accuracy.

Sprawdźmy, czy tak jest, korzystając z przykładu MNIST CNN w Keras, z następującą modyfikacją:

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    

Aby temu zaradzić, czyli użycie rzeczywiście binarny przekrój entropię jako swojej funkcji straty (jak powiedziałem, nic złego w tym, przynajmniej w zasadzie), a jednocześnie coraz kategoryczne dokładności wymaganej przez problem pod ręką, należy zwrócić się wprost do categorical_accuracyw kompilacja modeli w następujący sposób:

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

W przykładzie MNIST, po wytrenowaniu, ocenie i przewidywaniu zestawu testowego, jak pokazałem powyżej, te dwie metryki są teraz takie same, jak powinny:

# 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    

Ustawienia systemu:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

AKTUALIZACJA : Po moim poście odkryłem, że ten problem został już zidentyfikowany w tej odpowiedzi .


1
Czy jest coś złego w używaniu loss='categorical_crossentropy', metrics=['categorical_accuracy']klasyfikacji wieloklasowej? To byłaby moja intuicja
NeStack

2
@NeStack Nie tylko nie ma nic złego, ale jest to nominalna kombinacja.
desertnaut

1
Zgodnie z tym, co powiedziałeś, jeśli używam loss = 'binary_crossentropy', otrzymam te same zwroty, bez względu na to, czy używam metrics = 'binary_accuracy' lub metrics = 'dokładność'?
BioCoder

2
@BioCoder dokładnie
desertnaut

54

Wszystko zależy od rodzaju problemu klasyfikacyjnego, z jakim masz do czynienia. Istnieją trzy główne kategorie

  • klasyfikacja binarna (dwie klasy docelowe),
  • klasyfikacja wieloklasowa (więcej niż dwa wyłączne cele),
  • klasyfikacja z wieloma etykietami (więcej niż dwa niewyłączne cele), w której jednocześnie może być włączonych wiele klas docelowych.

W pierwszym przypadku należy zastosować binarną entropię krzyżową, a cele należy zakodować jako jeden gorący wektory.

W drugim przypadku należy zastosować kategoryczną entropię krzyżową, a cele powinny być zakodowane jako wektory o jednym punkcie.

W ostatnim przypadku należy zastosować binarną entropię krzyżową, a cele powinny być zakodowane jako jedno gorące wektory. Każdy neuron wyjściowy (lub jednostka) jest uważany za oddzielną losową zmienną binarną, a strata dla całego wektora wyjść jest iloczynem utraty pojedynczych zmiennych binarnych. Dlatego jest to iloczyn binarnej entropii krzyżowej dla każdej pojedynczej jednostki wyjściowej.

Binarna entropia krzyżowa jest zdefiniowana jako

wprowadź opis obrazu tutaj

a kategoryczna entropia krzyżowa jest zdefiniowana jako

wprowadź opis obrazu tutaj

gdzie cjest indeks biegnący przez liczbę klas


Twoja odpowiedź wydaje mi się bardzo prawdziwa, ale ... Próbowałem postępować zgodnie z odpowiedzią @desertnaut i wykonałem to testy: z funkcją straty binary_crossentropy i metrcis to categorical_accurency mam lepszą precyzję niż przy użyciu funkcji straty categorical_crossentropy i metryk dokładności - i nie potrafię tego wyjaśnić że ...
Metal3d

@ Metal3d: jakie jest sformułowanie twojego problemu: z wieloma etykietami czy z jedną etykietą?
Whynote

single-label, a teraz zdaję sobie sprawę, dlaczego to działa lepiej :)
Metal3d

Czy na pewno binarne i jakościowe cross-entropie są zdefiniowane tak, jak we wzorach w tej odpowiedzi?
nbro

@nbro, w rzeczywistości cindeks jest zbędny w binarnej formule krzyżowej entropii, nie musi tam być (ponieważ są tylko 2 klasy, a prawdopodobieństwo każdej klasy jest osadzone w y(x). W przeciwnym razie te formuły powinny być poprawne, ale zwróć uwagę, że to nie są straty, to są prawdopodobieństwa. Jeśli chcesz straty, musisz ją wziąć log.
Dlaczego

40

Natrafiłem na „odwrócony” problem - uzyskiwałem dobre wyniki z categorical_crossentropy (z 2 klasami) i słabe z binary_crossentropy. Wygląda na to, że problem dotyczył niewłaściwej funkcji aktywacji. Prawidłowe ustawienia to:

  • dla binary_crossentropy: aktywacji esicy, celu skalarnego
  • dla categorical_crossentropy: aktywacji softmax, jeden-gorący zakodowany cel

4
Czy jesteś pewien co do celu skalarnego dla binary_crossentropy. Wygląda na to, że powinieneś używać celu zakodowanego w systemie „wiele gorących” (np. [0 1 0 0 1 1]).
Dmitry

5
Pewnie. Zobacz keras.io/losses/#usage-of-loss-functions , gdzie jest napisane: "kiedy używasz straty categorical_crossentropy, twoje cele powinny być w formacie kategorycznym (np. Jeśli masz 10 klas, celem dla każdej próbki powinno być 10 -wymiarowy wektor składający się z samych zer oczekujących na 1 w indeksie odpowiadającym klasie próbki) ”
Alexander Svetkin

1
Ale mówimy o binary_crossentropy - a nie categorical_crossentropy.
Dmitry

Ta odpowiedź wydaje się być niespójna ze stackoverflow.com/a/49175655/3924118 , gdzie autor mówi, że cele powinny być zakodowane na gorąco, podczas gdy w swojej odpowiedzi sugerujesz, że powinny to być skalary. Powinieneś to wyjaśnić.
nbro

@AlexanderSvetkin, cel powinien być zakodowany na gorąco wszędzie, nie tylko przy użyciu kategorycznej entropii krzyżowej
Whynote

28

To naprawdę interesujący przypadek. Właściwie w twojej konfiguracji prawdziwe jest następujące stwierdzenie:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Oznacza to, że aż do stałego mnożnika Twoje straty są równoważne. Dziwne zachowanie, które obserwujesz podczas fazy treningu, może być przykładem następującego zjawiska:

  1. Na początku najczęstsza klasa dominuje nad stratą - więc sieć uczy się przewidywać głównie tę klasę dla każdego przykładu.
  2. Gdy nauczy się najczęstszego schematu, zaczyna rozróżniać wśród rzadziej uczęszczanych zajęć. Ale kiedy używasz adam- współczynnik uczenia ma znacznie mniejszą wartość niż na początku treningu (wynika to z natury tego optymalizatora). Powoduje to, że szkolenie jest wolniejsze i utrudnia np. Pozostawienie słabego lokalnego minimum w sieci.

Dlatego ten stały czynnik może pomóc w przypadku binary_crossentropy. Po wielu epokach - wartość wskaźnika uczenia się jest większa niż w categorical_crossentropyprzypadku. Zwykle wznawiam trening (i fazę uczenia się) kilka razy, gdy zauważam takie zachowanie lub / i dostosowuję wagi klasy według następującego wzoru:

class_weight = 1 / class_frequency

To sprawia, że ​​straty z rzadszych zajęć równoważą wpływ przegranej klasy dominującej na początku treningu oraz w dalszej części procesu optymalizacji.

EDYTOWAĆ:

Właściwie - sprawdziłem, chociaż w przypadku matematyki:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

powinien trzymać - w przypadku, kerasgdy to nieprawda, ponieważ kerasautomatycznie normalizuje wszystkie wyniki do sumowania 1. To jest prawdziwy powód tego dziwnego zachowania, ponieważ w przypadku wieloklasyfikacji taka normalizacja szkodzi treningowi.


Czy moja odpowiedź ci pomogła?
Marcin Możejko

1
To jest bardzo prawdopodobne wyjaśnienie. Ale nie jestem pewien, czy to naprawdę główny powód. Ponieważ zauważyłem również, że kilku moich uczniów działa dziwnie, gdy stosuję binary-X-ent zamiast cat-X-ent (co jest błędem). I to jest prawdą nawet podczas treningu tylko przez 2 epoki! Używanie class_weight z odwrotnymi poprzednimi klasami nie pomogło. Może pomóc rygorystyczne dostrojenie współczynnika uczenia się, ale wartości domyślne wydają się faworyzować bin-X-ent. Myślę, że to pytanie zasługuje na więcej badań ...
xtof54

1
Czekaj, nie, przepraszam, nie dostaję twojej aktualizacji: softmax zawsze sumuje wyjścia do 1, więc nas to nie obchodzi? I dlaczego miałoby to szkodzić treningowi, skoro mamy tylko jedną złotą klasę, która jest poprawna na przykład?
xtof54

20

Po skomentowaniu odpowiedzi @Marcin, dokładniej sprawdziłem kod jednego z moich uczniów, w którym znalazłem to samo dziwne zachowanie, nawet po zaledwie 2 epokach! (Więc wytłumaczenie @ Marcina nie było bardzo prawdopodobne w moim przypadku).

I odkryłem, że odpowiedź jest w rzeczywistości bardzo prosta: dokładność obliczona metodą Keras evaluatejest po prostu błędna, gdy używa się binary_crossentropy z więcej niż 2 etykietami. Możesz to sprawdzić, przeliczając dokładność samodzielnie (najpierw wywołaj metodę Keras „przewiduj”, a następnie oblicz liczbę poprawnych odpowiedzi zwróconych przez funkcję prognozy): otrzymujesz prawdziwą dokładność, która jest znacznie niższa niż metoda „oceniania” Keras.


1
Widziałem podobne zachowanie również w pierwszej iteracji.
dolbi

10

prosty przykład w środowisku wieloklasowym do zilustrowania

załóżmy, że masz 4 klasy (zakodowane w jednym ujęciu), a poniżej jest tylko jedna prognoza

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

kiedy używasz categorical_crossentropy, dokładność wynosi tylko 0, przejmuje się tylko tym, czy poprawnie wykonasz odpowiednią klasę.

jednak gdy używasz binary_crossentropy, dokładność jest obliczana dla wszystkich klas, dla tej prognozy będzie to 50%. a ostateczny wynik będzie średnią z poszczególnych dokładności w obu przypadkach.

zaleca się użycie categorical_crossentropy dla problemu z wieloma klasami (klasy wykluczają się wzajemnie), ale binary_crossentropy dla problemu z wieloma etykietami.


8

Ponieważ jest to problem wieloklasowy, musisz użyć categorical_crossentropy, binarna entropia krzyżowa da fałszywe wyniki, najprawdopodobniej oceni tylko dwie pierwsze klasy.

50% dla problemu wieloklasowego może być całkiem dobre, w zależności od liczby klas. Jeśli masz n klas, to 100 / n jest minimalną wydajnością, jaką można uzyskać, wyświetlając losową klasę.


2

w przypadku categorical_crossentropyutraty wartości docelowe powinny być w formacie kategorialnym (np. jeśli masz 10 klas, cel dla każdej próbki powinien być 10-wymiarowym wektorem składającym się z samych zer, z wyjątkiem 1 w indeksie odpowiadającym klasie próba).


3
Jak dokładnie to odpowiada na pytanie?
desertnaut


1

Przekazujesz docelową tablicę kształtu (x-dim, y-dim), używając jako straty categorical_crossentropy. categorical_crossentropyoczekuje, że cele będą binarnymi macierzami (1 i 0) kształtu (próbki, klasy). Jeśli celami są klasy całkowite, możesz przekonwertować je na oczekiwany format za pomocą:

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

Alternatywnie można sparse_categorical_crossentropyzamiast tego użyć funkcji utraty , która nie oczekuje wartości docelowych w postaci liczb całkowitych.

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

0

Binary_crossentropy (y_target, y_predict) nie musi mieć zastosowania w problemie z klasyfikacją binarną. .

W kodzie źródłowym binary_crossentropy () The nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)funkcja TensorFlow był rzeczywiście używany. W dokumentacji jest napisane, że:

Mierzy błąd prawdopodobieństwa w dyskretnych zadaniach klasyfikacji, w których każda klasa jest niezależna i nie wyklucza się wzajemnie. Na przykład można przeprowadzić klasyfikację z wieloma etykietami, w której obraz może jednocześnie przedstawiać słonia i psa.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.