Почему двоичные_кросентропии и категориальные_кросентропии дают разные результаты для одной и той же проблемы?

160

Я пытаюсь обучить Си-Эн-Эн классифицировать текст по темам. Когда я использую двоичную кросс-энтропию, я получаю точность ~ 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чтобы сделать это. Также см. Определения категориальных и бинарных кроссентропий здесь .
автономный
Мои ярлыки являются категориальными, созданными с использованием 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Кераса, просто неверна при использовании двоичной_кросентропии с более чем 2 метками

Я хотел бы подробнее остановиться на этом, продемонстрировать реальную проблему, объяснить ее и предложить решение проблемы.

Такое поведение не ошибка; основная причина является довольно тонким и недокументированным вопросом, как на самом деле Keras догадок , которые точность для использования, в зависимости от функции потерь вы выбрали, когда вы просто включить metrics=['accuracy']в вашей модели компиляции. Другими словами, пока ваш первый вариант компиляции

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

действительно, ваш второй:

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

не даст того, что вы ожидаете, но причина не в использовании двоичной кросс-энтропии (которая, по крайней мере в принципе, является абсолютно допустимой функцией потерь).

Это почему? Если вы проверите исходный код метрики , Keras определит не одну метрику точности, а несколько разных, среди них binary_accuracyи categorical_accuracy. Что происходит под капотом, так это то, что, поскольку вы выбрали двоичную перекрестную энтропию в качестве функции потерь и не указали конкретную метрику точности, Керас (ошибочно ...) делает вывод, что вы заинтересованы в этом binary_accuracy, и это то, что он возвращает - в то время как на самом деле вы заинтересованы в categorical_accuracy.

Давайте проверим, что это так, используя пример MNIST CNN в Keras со следующей модификацией:

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

ОБНОВЛЕНИЕ : После моего сообщения я обнаружил, что эта проблема уже была определена в этом ответе .

desertnaut
источник
1
Что-то не так с использованием loss='categorical_crossentropy', metrics=['categorical_accuracy']мультиклассовой классификации? Это была бы моя интуиция
NeStack
2
@ NeStack Мало того, что в этом нет ничего плохого, это номинальная комбинация.
пустынен
1
Согласно тому, что вы сказали, до тех пор, пока я использую loss = 'binary_crossentropy', я получу одинаковые возвраты, независимо от того, использую я метрики = 'binary_accuracy' или метрики = 'точность'?
BioCoder
2
@BioCoder точно
пустынаут
54

Все зависит от типа проблемы классификации, с которой вы имеете дело. Есть три основных категории

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

В первом случае следует использовать двоичную кросс-энтропию и цели должны быть закодированы как векторы с одним горячим током.

Во втором случае следует использовать категорическую кросс-энтропию и цели должны быть закодированы как векторы с одним горячим током.

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

Двоичная кросс-энтропия определяется как

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

и категориальная кросс-энтропия определяется как

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

где cиндекс работает над количеством классов

Whynote
источник
Ваш ответ кажется мне очень верным, но ... Я попытался следовать ответу @desertnaut и выполнил следующие тесты: с функцией потери binary_crossentropy и metrcis to categoryorical_accurency я получаю более высокую точность, чем при использовании функции потери categoryor__crossentropy и метрик точности - и я не могу объяснить что ...
Metal3d
@ Metal3d: какова формулировка вашей проблемы: мульти-лейбл или одно-лейбл?
Whynote
один лейбл, и теперь я понимаю, почему это работает лучше :)
Metal3d
Вы уверены, что бинарные и категориальные кросс-энтропии определены как в формулах в этом ответе?
nbro
@nbro, фактически, cиндекс является избыточным в двоичной формуле кросс-энтропии, он не должен быть там (так как есть только 2 класса, и вероятность каждого класса встроена y(x). В противном случае эти формулы должны быть правильными, но замечать те не потери, те Вероятности Если вы хотите потери , вы должны принять. logиз них.
Whynote
40

Я столкнулся с «перевернутой» проблемой - я получал хорошие результаты с категориальной_кросентропией (с 2 классами) и плохой с двоичной_кросцентропией. Похоже, проблема была в неправильной активации. Правильные настройки были:

  • для binary_crossentropy: сигмовидная активация, скалярная цель
  • для categorical_crossentropy: softmax активация, горячая закодированная цель
Александр Светкин
источник
4
Вы уверены в скалярной цели для binary_crossentropy. Похоже, вы должны использовать закодированную цель «много горячих» (например, [0 1 0 0 1 1]).
Дмитрий
5
Конечно. Смотрите keras.io/losses/#usage-of-loss-functions , там написано: «при использовании потери categoryor_crossentropy ваши цели должны быть в категориальном формате (например, если у вас есть 10 классов, цель для каждой выборки должна быть 10 вектор, в котором все нули ожидают для 1 по индексу, соответствующему классу выборки) »
Александр Светкин
1
Но мы говорим о двоичной_кросентропии, а не категориальной_кросентропии.
Дмитрий
Этот ответ кажется несовместимым со 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
Это очень правдоподобное объяснение. Но я не уверен, что это действительно главная причина. Потому что я также наблюдал, как некоторые из моих учеников работают странно, когда применяют двоичный X-ent вместо cat-X-ent (что является ошибкой). И это верно даже при тренировках всего 2 эпохи! Использование class_weight с обратными априорными классами не помогло. Может быть, строгая настройка скорости обучения поможет, но значения по умолчанию, кажется, предпочитают bin-X-ent. Я думаю, что этот вопрос заслуживает дальнейшего изучения ...
xtof54
1
Подождите, нет, извините, я не получаю ваше обновление: softmax всегда делает выходные суммы равными 1, так что нас это не волнует? И зачем это вредить тренировке, если у нас только один золотой класс, который является правильным для каждого примера?
xtof54
20

Прокомментировав ответ @Marcin, я более тщательно проверил код одного из моих учеников, где обнаружил такое же странное поведение, даже после всего 2 эпох! (Так что объяснение @ Marcin было не очень вероятно в моем случае).

И я обнаружил, что ответ на самом деле очень прост: точность, вычисленная с помощью метода evaluateКераса, просто неверна при использовании двоичной_кросентропии с более чем 2 метками. Вы можете проверить это, пересчитав точность самостоятельно (сначала вызовите метод Keras «предсказать», а затем вычислите количество правильных ответов, возвращаемых предикатом): вы получите истинную точность, которая намного ниже, чем у Keras «оценивает» один.

xtof54
источник
1
Я видел подобное поведение и на первой итерации.
Долби
10

простой пример под настройкой мультикласса, чтобы проиллюстрировать

Предположим, у вас есть 4 класса (закодированные одним выстрелом) и ниже только один прогноз

true_label = [0,1,0,0] Forecast_label = [0,0,1,0]

при использовании categoryorical_crossentropy точность равна 0, это важно только в том случае, если вы правильно поняли интересующий вас класс.

однако при использовании binary_crossentropy точность рассчитывается для всех классов, для этого прогноза она будет равна 50%. и окончательный результат будет означать индивидуальную точность для обоих случаев.

Рекомендуется использовать categoryorc_crossentropy для задачи с несколькими классами (классы являются взаимоисключающими), но binary_crossentropy для задачи с несколькими метками.

Bazinga
источник
8

Поскольку это проблема нескольких классов, вы должны использовать categoryorical_crossentropy, двоичная кросс-энтропия даст ложные результаты, скорее всего, будут оцениваться только первые два класса.

50% для многоклассовой задачи может быть достаточно хорошим, в зависимости от количества классов. Если у вас есть n классов, то 100 / n - это минимальная производительность, которую вы можете получить, выдав случайный класс.

Доктор Снупи
источник
2

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

Priyansh
источник
3
Как именно это отвечает на вопрос?
пустынен
2

Взгляните на уравнение, которое вы можете найти, что двоичная кросс-энтропия не только наказывает эти метки = 1, прогнозируемые = 0, но и метку = 0, прогнозируемые = 1.

Однако категорическая перекрестная энтропия только наказывает эти метки = 1, но предсказывает = 1. Поэтому мы предполагаем, что существует только ОДНА метка, положительная.

Куанг Ян
источник
1

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

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'])
susan097
источник
0

Двоичный_кроссцентроп (y_target, y_predict) не требуется применять в задаче двоичной классификации. ,

В исходном коде binary_crossentropy () , то на nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)самом деле была использована функция TensorFlow. И в документации сказано, что:

Измеряет вероятностную ошибку в задачах дискретной классификации, в которых каждый класс независим и не является взаимоисключающим. Например, можно выполнить многослойную классификацию, где изображение может содержать как слона, так и собаку одновременно.

翟志伟
источник