Чому binary_crossentropy та categorical_crossentropy дають різні вистави для однієї проблеми?


160

Я намагаюся навчити CNN класифікувати текст за темами. Коли я використовую бінарну перехресну ентропію, я отримую ~ 80% точності, при категоричній перехресній ентропії я отримую ~ 50% точності.

Я не розумію, чому це. Це багатокласова проблема, чи це не означає, що я повинен використовувати категоричну перехресну ентропію і що результати з бінарною перехресною ентропією безглузді?

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

Потім я компілюю це або так, використовуючи categorical_crossentropyяк функцію втрати:

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

або

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

Інтуїтивно має сенс, чому я хотів би використовувати категоричну перехресну ентропію, я не розумію, чому я отримую хороші результати з бінарними, а погані результати - категоричними.


10
Якщо це багатокласова проблема, вам доведеться скористатися categorical_crossentropy. Також мітки потрібно перетворити на категоричний формат. Дивіться, to_categoricalщоб зробити це. Дивіться також визначення категоріального і бінарних crossentropies тут .
— Автономний

Мої мітки категоричні, створені за допомогою to_categorical (по одному гарячому вектору для кожного класу). Чи означає це, що ~ 80% точність від двійкової кросцентропії - це лише хибне число?
— Даніель Мессія

Я думаю так. Якщо ви використовуєте категоричні етикетки, тобто один гарячий вектор, то вам потрібно categorical_crossentropy. Якщо у вас є два класи, вони будуть представлені як 0, 1у бінарних мітках, так і 10, 01у категоричному форматі міток.
— Автономний

1
Я думаю, що він просто порівнює перше число у векторі, а решта ігнорує.
— Томас Пінець

2
@NilavBaranGhosh Представлення буде [[1, 0], [0, 1]] для категоричної класифікації, що включає два класи (не [[0, 0], [0, 1]], як ви згадуєте). Dense(1, activation='softmax')для двійкової класифікації просто неправильно. Пам'ятайте, що вихід softmax - це розподіл ймовірностей, який дорівнює одиниці. Якщо ви хочете мати лише один вихідний нейрон з бінарною класифікацією, використовуйте сигмоїд з бінарною перехресною ентропією.
— Автономне

Відповіді:


204

Причиною цього очевидного розбіжності між категорійною та бінарною крос-ентропією є те, що користувач xtof54 вже повідомив у своїй відповіді нижче , тобто:

точність, обчислена методом evaluateКераса, є просто неправильною при використанні binary_crossentropy з більш ніж 2 мітками

Я хотів би детальніше розібратися з цим, продемонструвати фактичне основне питання, пояснити його та запропонувати правовий засіб.

Така поведінка не є помилкою; Основна причина - досить тонка та незадокументована проблема в тому, як Керас насправді здогадується, яку точність використовувати, залежно від вибраної функції втрат, коли ви просто включаєте metrics=['accuracy']в свою компіляцію моделі. Іншими словами, тоді як ваш перший варіант компіляції

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

діє, ваш другий:

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

не призведе до того, що ви очікуєте, але причина не у використанні бінарної перехресної ентропії (що, принаймні в принципі, є абсолютно дійсною функцією втрати).

Чому так? Якщо ви перевіряєте вихідний код метрик , Керас визначає не одну метрику точності, а кілька різних, серед них binary_accuracyі categorical_accuracy. Що відбувається під кришкою, це те, що ви вибрали бінарну перехресну ентропію як функцію втрати і не вказали конкретної метрики точності, Керас (неправильно ...) підказує, що вас цікавить binary_accuracy, і це те, що воно повертає - а насправді вас цікавить categorical_accuracy.

Перевіримо, що це так, використовуючи приклад MNIST CNN в Керасі, з наступною модифікацією:

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    

Щоб виправити цю ситуацію , тобто використовувати дійсно бінарний перехресний ентропію як ваші функції втрат (як я сказав, нічого поганого в цьому, по крайней мере , в принципі) , а по- , як і раніше отримувати категоричне точність , необхідну розв'язуваної задачі, ви повинні задати в явному вигляді для categorical_accuracyв складання моделі наступним чином:

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

У прикладі MNIST після тренувань, підрахунку та прогнозування тестового набору, як я показав вище, дві показники зараз однакові, як і повинні бути:

# 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    

Налаштування системи:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

ОНОВЛЕННЯ : Після своєї публікації я виявив, що це питання вже було визначено в цій відповіді .


1
Щось не так у використанні loss='categorical_crossentropy', metrics=['categorical_accuracy']для багатокласової класифікації? Це була б моя інтуїція
— NeStack

2
@NeStack Не тільки немає нічого поганого, але це іменна комбінація.
— пустеля

1
Відповідно до того, що ви сказали, доки я використовую loss = 'binary_crossentropy', я отримаю те саме, що не повертає ніякої матерії, я використовую metrics = 'binary_accuracy' або metrics = 'точність'
— BioCoder

2
@BioCoder саме
— пустеля

54

Все залежить від типу проблеми класифікації, з якою ви маєте справу. Існує три основні категорії

  • двійкова класифікація (два цільові класи),
  • класифікація на багато класів (більше двох ексклюзивних цілей),
  • класифікація на багато міток (більше двох неексклюзивних цілей), в якій одночасно можуть бути включені кілька цільових класів.

У першому випадку слід застосовувати двійкову перехресну ентропію, а цілі - кодувати як вектори з гарячою швидкістю.

У другому випадку слід застосовувати категоричну перехресну ентропію, а цілі - кодувати як вектори з гарячою швидкістю.

В останньому випадку слід застосовувати двійкову перехресну ентропію, а цілі - кодувати як вектори з гарячою швидкістю. Кожен вихідний нейрон (або одиниця) розглядають як окрему випадкову бінарну змінну, а втрата для всього вектора виходів - добуток втрати одиничних бінарних змінних. Тому це добуток двійкової перехресної ентропії для кожної окремої вихідної одиниці.

Двійкова перехресна ентропія визначається як

введіть тут опис зображення

а категорична перехресна ентропія визначається як

введіть тут опис зображення

де cіндекс, що працює над кількістю класів


Ваша відповідь здається мені дуже правдивою, але ... Я спробував дотримуватися відповіді @desertnaut і зробив це тести: З функцією втрати binary_crossentropy і metrcis до категорійного_страхування я маю кращу точність, що за допомогою функції втрати категоричної_кросцентропії та показників страхування - і я не можу пояснити що ...
— Metal3d

@ Metal3d: яка формулювання вашої проблеми: багатозначна або одномітка?
— Whynote

сингл-лейбл, і тепер я розумію, чому це працює краще :)
— Metal3d

Ви впевнені, що бінарні та категоричні перехресні ентропії визначені як у формулах цієї відповіді?
— nbro

@nbro, насправді, cіндекс є надмірним у бінарній формулі крос-ентропії, він не повинен бути там (оскільки існує лише 2 класи і вбудована ймовірність кожного класу y(x). Інакше ці формули повинні бути правильними, але зауважте, що це не втрати, це ймовірність. Якщо ви хочете втрати, ви повинні взяти їх logіз себе
— Чому зауважте

40

Я натрапив на "перевернуту" проблему - я отримував хороші результати з категорією_кросцентропією (з 2 класами) і поганою з бінарною_кросцентропією. Здається, що проблема була з неправильною функцією активації. Правильними налаштуваннями були:

  • для binary_crossentropy: сигмоїдна активація, скалярна мішень
  • для categorical_crossentropy: активації програмного забезпечення, кодована ціна з гарячим кодом

4
Ви впевнені у скалярній цілі для binary_crossentropy. Схоже, ви повинні використовувати кодовану ціль "гарячої" (наприклад, [0 1 0 0 1 1]).
— Дмитро

5
Звичайно. Дивіться на keras.io/losses/#usage-of-loss-functions , там сказано: "при використанні втрати categorical_crossentropy ваші цілі повинні бути у категоричному форматі (наприклад, якщо у вас є 10 класів, ціль для кожного зразка має бути 10 -вимірний вектор, який очікується на всі нулі для 1 при індексі, що відповідає класу вибірки) "
— Олександр Світкін

1
Але ми говоримо про binary_crossentropy - а не категоричну_crossentropy.
— Дмитро

Ця відповідь, здається, суперечить stackoverflow.com/a/49175655/3924118 , де автор каже, що цілі повинні бути закодованими, тоді як, у своїй відповіді, ви припускаєте, що вони повинні бути скалярами. Ви повинні уточнити це.
— nbro

@AlexanderSvetkin, ціль має бути скрізь закодована скрізь, а не лише при використанні категоричної перехресної ентропії
— Whynote

28

Це справді цікавий випадок. Насправді у вашому налаштуванні вірно наступне твердження:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Це означає, що до постійного коефіцієнта множення ваші втрати еквівалентні. Дивна поведінка, яку ви спостерігаєте під час тренувального етапу, може бути прикладом наступного явища:

  1. На початку найпоширеніший клас домінує у збитках - тому мережа вчиться передбачати здебільшого цей клас для кожного прикладу.
  2. Після того, як вона засвоїла найчастішу закономірність, вона починає розрізняти менш рідкісні заняття. Але коли ви використовуєте adam- рівень навчання має набагато менше значення, ніж він мав на початку навчання (це пов’язано з характером цього оптимізатора). Це робить навчання повільнішим і не дозволяє вашій мережі, наприклад, залишати менш можливим локальний мінімум.

Ось чому цей постійний фактор може допомогти у випадку binary_crossentropy. Після багатьох епох - значення швидкості навчання більше, ніж у categorical_crossentropyвипадку. Я, як правило, кілька разів перезавантажую навчання (і етап навчання), коли помічаю таку поведінку або / і коригую вагу класу за такою схемою:

class_weight = 1 / class_frequency

Це робить втрати від менш частих занять, що врівноважує вплив домінуючої втрати класу на початку навчання та в подальшій частині процесу оптимізації.

Редагувати:

Насправді - я перевірив це, хоча у випадку з математикою:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

має утримуватись - якщо kerasце неправда, оскільки kerasавтоматично нормалізується всі результати, які підводяться підсумки 1. Це фактична причина цієї дивної поведінки, оскільки у випадку багатокласифікації така нормалізація шкодить навчанню.


Чи допомогла вам моя відповідь?
— Marcin Możejko

1
Це дуже правдоподібне пояснення. Але я не впевнений, що це дійсно головна причина. Оскільки я також спостерігав, як у кількох моїх студентів ця дивна поведінка застосовується при застосуванні binary-X-ent замість cat-X-ent (що є помилкою). І це справедливо навіть при тренуванні всього 2 епохи! Використання class_weight з оберненими пріорами класу не допомогло. Можливо, сувора настройка рівня навчання допоможе, але значення за замовчуванням здаються на користь bin-X-ent. Я думаю, що це питання заслуговує на більшу кількість досліджень ...
— xtof54

1
Зачекайте, не вибачте, я не отримую ваше оновлення: софтмакс завжди робить результати на рівні 1, тому нам це не цікаво? І навіщо це шкодити шкоді, якщо ми маємо лише єдиний клас золота, що відповідає правильному прикладу?
— xtof54

20

Прокоментувавши відповідь @Marcin, я ретельніше перевірив код одного зі своїх учнів, де я виявив таку ж дивну поведінку, навіть після лише двох епох! (Тож пояснення @ Марціна в моєму випадку було не дуже вірогідним).

І я виявив, що відповідь насправді дуже проста: точність, обчислена методом evaluateКераса, явно неправильна при використанні binary_crossentropy з більш ніж 2 мітками. Ви можете переконатись, що самостійно перерахувавши точність (спочатку зателефонуйте методу Кераса "прогнозуйте", а потім обчисліть кількість правильних відповідей, повернених прогнозом): ви отримуєте справжню точність, що набагато нижча, ніж Кераса "оцінити".


1
Я бачив подібну поведінку і на першій ітерації.
— dolbi

10

простий приклад під настановою для багатьох класів для ілюстрації

припустимо, у вас є 4 класи (onehot закодовані), а нижче - лише одне передбачення

true_label = [0,1,0,0] передбачуваний_шарок = [0,0,1,0]

при використанні categorical_crossentropy точність становить лише 0, вона хвилює лише те, чи правильно ви отримаєте відповідний клас.

однак при використанні binary_crossentropy точність обчислюється для всіх класів, це прогноз буде 50%. і кінцевим результатом буде середня індивідуальна точність в обох випадках.

рекомендується використовувати categorical_crossentropy для проблеми класу (класи взаємно виключають), але binary_crossentropy для задачі з декількома мітками.


8

Оскільки це багатокласова проблема, вам доведеться використовувати категоричну_кросцентропію, двійкові перехресні ентропії дадуть хибні результати, швидше за все, оцінюватимуть лише перші два класи.

50% для багатокласної проблеми може бути досить непогано, залежно від кількості занять. Якщо у вас n класів, то 100 / n - це мінімальна продуктивність, яку ви можете отримати, вивівши випадковий клас.


2

при використанні categorical_crossentropyвтрати цілі повинні бути у категоричному форматі (наприклад, якщо у вас є 10 класів, ціль для кожного зразка має бути 10-мірним вектором, який є загальним нулем, за винятком 1 в індексі, що відповідає класу зразок).


3
Як саме це відповідає на питання?
— пустеля

2

Погляньте на рівняння, ви можете виявити, що бінарна перехресна ентропія не тільки карає тих міток = 1, прогнозованих = 0, але і мітки = 0, передбачуваних = 1.

Однак категорична перехресна ентропія карає лише ті позначки = 1, але прогнозовані = 1. Тому ми робимо припущення, що існує лише ОДНА мітка позитивна.


1

Ви передаєте цільовий масив фігур (x-dim, y-dim), використовуючи в якості втрати categorical_crossentropy. categorical_crossentropyочікує, що цілями будуть двійкові матриці (1s і 0s) форми (зразки, класи). Якщо цілі - цілі класи, ви можете перетворити їх у очікуваний формат за допомогою:

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

Крім того, ви можете скористатися функцією втрат sparse_categorical_crossentropy, яка очікує цілі цілі.

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

0

Бінарну_кросцентропію (y_target, y_predict) не потрібно застосовувати в задачі бінарної класифікації. .

У вихідному коді binary_crossentropy () , то на nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)справді була використана функція TensorFlow. А в документації написано, що:

Вимірює помилку ймовірності в дискретних задачах класифікації, в яких кожен клас незалежний і не виключає взаємовиключення. Наприклад, можна виконати багатозначну класифікацію, де зображення може містити одночасно і слона, і собаку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.