Я пытаюсь обучить CNN классифицировать текст по темам. Когда я использую двоичную кросс-энтропию, я получаю точность ~ 80%, с категориальной кросс-энтропией я получаю точность ~ 50%.

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

model.add (слой_встраивания)
model.add (Выпадение (0,25))
# сверточный слой
model.add (Conv1D (nb_filter = 32,
                    filter_length = 4,
                    border_mode = 'действительный',
                    активация = 'relu'))
model.add (MaxPooling1D (pool_length = 2))
# плотные слои
model.add (Сглаживание ())
model.add (Плотный (256))
model.add (Выпадение (0,25))
model.add (Активация ('relu'))
# выходной слой
model.add (Плотный (len (class_id_index)))
model.add (Активация ('softmax'))

Затем я компилирую это либо так, используя categoryorical_crossentropy в качестве функции потерь:

model.compile (loss = 'category_crossentropy', optimizer = 'adam', metrics = ['precision'])

или

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

Интуитивно понятно, почему я хочу использовать категориальную кросс-энтропию, я не понимаю, почему я получаю хорошие результаты с двоичными и плохие результаты с категориальными.

Ответы (12)

Причина этого очевидного несоответствия производительности между категориальной и двоичной перекрестной энтропией заключается в том, что пользователь xtof54 уже сообщил в его ответе ниже, то есть: * 100004*

точность, вычисленная методом Кераса оценка, просто неверно при использовании binary_crossentropy с более чем 2 метками

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

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

model.compile (loss = 'category_crossentropy', optimizer = 'adam', metrics = ['precision'])

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

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

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

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

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

model.compile (loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['precision']) # НЕПРАВИЛЬНЫЙ способ

model.fit (x_train, y_train,
          batch_size = размер_пакета,
          epochs = 2, # только 2 эпохи, для демонстрационных целей
          verbose = 1,
          validation_data = (x_test, y_test))

# Керас сообщил о точности:
score = model.evaluate (x_test, y_test, verbose = 0)
оценка [1]
# 0.9975801164627075

# Фактическая точность рассчитывается вручную:
импортировать numpy как np
y_pred = model.predict (x_test)
acc = sum ([np.argmax (y_test [i]) == np.argmax (y_pred [i]) для i в диапазоне (10000)]) / 10000
соотв
# 0.98780000000000001

оценка [1] == acc
# Ложь

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

из keras.metrics import category_accuracy
model.compile (loss = 'binary_crossentropy', optimizer = 'adam', metrics = [категориальная_точность])

В примере MNIST после обучения, оценки и прогнозирования набора тестов, как я показал выше, две метрики теперь такие же, как и должны быть:

# Керас сообщил о точности:
score = model.evaluate (x_test, y_test, verbose = 0)
оценка [1]
# 0.98580000000000001

# Фактическая точность рассчитывается вручную:
y_pred = model.predict (x_test)
acc = sum ([np.argmax (y_test [i]) == np.argmax (y_pred [i]) для i в диапазоне (10000)]) / 10000
соотв
# 0.98580000000000001

оценка [1] == acc
# Истинный

Настройка системы:

Python версии 3.5.3
Tensorflow версии 1.2.1
Керас версии 2.0.4

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

Это действительно интересный случай. На самом деле в вашей настройке верно следующее утверждение:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Это означает, что с точностью до постоянного коэффициента умножения ваши потери эквивалентны. Странное поведение, которое вы наблюдаете во время фазы обучения, может быть примером следующего явления:

  1. Вначале наиболее частый класс преобладает в убытках - поэтому сеть учится предсказывать в основном этот класс для каждого примера.
  2. После того, как он выучил наиболее частый образец, он начинает различать менее часто встречающиеся классы. Но когда вы используете adam - скорость обучения имеет гораздо меньшее значение, чем в начале обучения (это из-за природы этого оптимизатора). Это замедляет обучение и предотвращает появление вашей сети, например, оставляя плохой местный минимум менее возможным.

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

class_weight = 1 / class_frequency

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

РЕДАКТИРОВАТЬ:

На самом деле - я это проверил, хотя в случае с математикой:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

должно сохраняться - в случае keras это неверно, потому что keras автоматически нормализует все выходные данные, чтобы в сумме получить 1. Это настоящая причина такого странного поведения, поскольку в случае мультиклассификации такая нормализация вредит обучению.

простой пример с настройкой нескольких классов для иллюстрации

предположим, что у вас есть 4 класса (с кодировкой onehot), а ниже - только одно предсказание

true_label = [0,1,0,0] предсказанная_ метка = [0,0,1,0]

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

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

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

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

In the source code of binary_crossentropy(), the nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output) of tensorflow was actually used. And, in the documentation, it says that "Measures the probability error in discrete classification tasks in which each class is independent and not mutually exclusive. For instance, one could perform multilabel classification where a picture can contain both an elephant and a dog at the same time."

На главный вопрос удовлетворительно отвечает блестящая розыскная работа Дезернаута. Однако бывают случаи, когда BCE (двоичная перекрестная энтропия) может давать результаты, отличные от CCE (категориальная перекрестная энтропия), и может быть предпочтительным выбором. Хотя приведенные выше правила для большого пальца (, какие потери выбрать) работают нормально в 99% случаев, я хотел бы добавить в это обсуждение несколько новых аспектов.

The OP had a softmax activation and this throws a probability distribution as the predicted value. It is a multi-class problem. The preferred loss is categorical CE. Essentially this boils down to -ln(p) where 'p' is the predicted probability of the lone positive class in the sample. This means that the negative predictions dont have a role to play in calculating CE. This is by intention.

В редких случаях может потребоваться сделать подсчет голосов -ve. Это можно сделать, рассматривая приведенный выше образец как серию двоичных предсказаний. Таким образом, если ожидается [1 0 0 0 0], а прогноз - [0,1 0,5 0,1 0,1 0,2], это далее разбивается на:

expected = [1,0], [0,1], [0,1], [0,1], [0,1]
predicted = [0.1, 0.9], [.5, .5], [.1, .9], [.1, .9], [.2, .8]

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

CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.8)]

CE имеет другую шкалу, но продолжает служить мерой разницы между ожидаемыми и прогнозируемыми значениями. Единственное отличие состоит в том, что в этой схеме значения -ve также штрафуются / вознаграждаются вместе со значениями + ve. Если ваша проблема такова, что вы собираетесь использовать вероятности вывода (как + ve, так и -ves) вместо использования max () для прогнозирования только метки 1 + ve, тогда вы можете рассмотреть эту версию CE.

Как насчет ситуации с несколькими метками, в которой ожидается = [1 0 0 0 1]? Традиционный подход заключается в использовании одного сигмоида на выходной нейрон вместо общего softmax. Это гарантирует, что вероятности выхода не зависят друг от друга. Получается что-то вроде:

expected = [1 0 0 0 1]
predicted is = [0.1 0.5 0.1 0.1 0.9]

По определению CE измеряет разницу между двумя распределениями вероятностей. Но приведенные выше два списка не являются распределениями вероятностей. Распределения вероятностей всегда должны составлять в сумме 1. Таким образом, обычное решение состоит в том, чтобы использовать тот же подход потерь, что и раньше - разбить ожидаемые и прогнозируемые значения на 5 отдельных распределений вероятностей, приступить к вычислению 5 перекрестных энтропий и суммировать их. Тогда:

CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.9)] = 3.3

Проблема возникает, когда количество классов может быть очень большим - скажем, 1000, и в каждом образце может присутствовать только пара из них. Итак, ожидаемый выглядит примерно так: [1,0,0,0,0,0,1,0,0,0 .....990 нулей]. Прогнозируемое может быть примерно таким: [.8, .1, .1, .1, .1, .1, .8, .1, .1, .1 .....990 0,1]

В данном случае CE =

- [ ln(.8) + ln(.8) for the 2 +ve classes and 998 * ln(0.9) for the 998 -ve classes]

= 0.44 (for the +ve classes) +  105 (for the negative classes)

Вы можете видеть, как классы -ve начинают создавать неприятное значение при вычислении убытков. Голос положительных сэмплов (которые могут быть всем, что нас волнует) заглушается. Что мы делаем? Мы не можем использовать категориальный CE (версия, в которой в расчетах учитываются только + ve выборок). Это потому, что мы вынуждены разбивать распределения вероятностей на несколько бинарных распределений вероятностей, потому что в противном случае это не было бы распределением вероятностей в первую очередь. Как только мы разбиваем его на несколько бинарных распределений вероятностей, у нас нет другого выбора, кроме как использовать бинарный CE, и это, конечно, дает вес классам -ve.

Один из вариантов - заглушить голос классов -ve множителем. Поэтому мы умножаем все потери на значение гаммы, где гамма <1. Скажем, в приведенном выше случае гамма может быть 0,0001. Теперь убыток составляет:

= 0.44 (for the +ve classes) +  0.105 (for the negative classes)

Снизилось значение неудобства. 2 года назад Facebook сделал это и многое другое в своей статье, в которой они также умножили потери -ve на p в степень x. «p» - это вероятность того, что на выходе будет a + ve, а x - константа> 1. Это еще больше уменьшило потери -ve , особенно те, в которых модель довольно уверена (где 1-p близко к 1). Этот комбинированный эффект наказания отрицательных классовых потерь в сочетании с более суровым наказанием за легко классифицируемые дела (на которые приходилось большинство случаев -ve) прекрасно сработал для Facebook, и они назвали это очаговой потерей.

Итак, в ответ на вопрос OP о том, имеет ли бинарный CE вообще какой-либо смысл в его случае, ответ - это зависит от обстоятельств. В 99% случаев обычные правила для большого пальца работают, но могут быть случаи, когда эти правила могут быть отклонены или даже нарушены в зависимости от решаемой задачи.

Для более подробной информации вы можете обратиться к: https://towardsdatascience.com/cross-entropy-classification-losses-no-math-few-stories-lots-of-intuition-d56f8c7f06b0

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

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

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

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

Однако категориальная перекрестная энтропия наказывает только тех label = 1, но предсказанных = 1. Вот почему мы делаем предположение, что есть только ОДИН положительный ярлык.

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

  • для binary_crossentropy: активация сигмоида, скалярная цель
  • для category_crossentropy: активация softmax, цель с горячим кодированием

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

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

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

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

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

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

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

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

enter image description here

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

enter image description here

, где c - это индекс, работающий по количеству классов C.

As it is a multi-class problem, you have to use the categorical_crossentropy, the binary cross entropy will produce bogus results, most likely will only evaluate the first two classes only.

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

2022 WebDevInsider