¿Por qué binary_crossentropy y categorical_crossentropy dan diferentes desempeños para el mismo problema?

160

Estoy tratando de entrenar a una CNN para clasificar el texto por tema. Cuando uso entropía cruzada binaria obtengo ~ 80% de precisión, con entropía cruzada categórica obtengo ~ 50% de precisión.

No entiendo por qué es esto. Es un problema multiclase, ¿eso no significa que tengo que usar entropía cruzada categórica y que los resultados con entropía cruzada binaria no tienen sentido?

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

Luego lo compilo o bien así categorical_crossentropycomo la función de pérdida:

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

o

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

Intuitivamente tiene sentido por qué me gustaría utilizar la entropía cruzada categórica, no entiendo por qué obtengo buenos resultados con binario y malos resultados con categórico.

Daniel Messias
fuente
10
Si se trata de un problema multiclase, debe usarlo categorical_crossentropy. También las etiquetas deben convertirse al formato categórico. Mira to_categoricalpara hacer esto. También vea las definiciones de las crossentropías categóricas y binarias aquí .
Autónomo
Mis etiquetas son categóricas, creadas usando to_categorical (un vector caliente para cada clase). ¿Eso significa que la precisión de ~ 80% de la crossentropía binaria es solo un número falso?
Daniel Messias
Creo que sí. Si utiliza etiquetas categóricas, es decir, un vector caliente, entonces lo desea categorical_crossentropy. Si tiene dos clases, se representarán como 0, 1en etiquetas binarias y 10, 01en formato de etiqueta categórica.
Autónomo
1
Creo que solo se compara con el primer número en el vector e ignora el resto.
Thomas Pinetz
2
@NilavBaranGhosh La representación será [[1, 0], [0, 1]] para una clasificación categórica que involucra dos clases (no [[0, 0], [0, 1]] como usted menciona). Dense(1, activation='softmax')porque la clasificación binaria es simplemente incorrecta. Recuerde que la salida de softmax es una distribución de probabilidad que se suma a uno. Si desea tener solo una neurona de salida con clasificación binaria, use sigmoide con entropía cruzada binaria.
Autónomo el

Respuestas:

204

La razón de esta aparente discrepancia de rendimiento entre la entropía cruzada categórica y binaria es lo que el usuario xtof54 ya ha informado en su respuesta a continuación , es decir:

la precisión calculada con el método Keras evaluatees simplemente incorrecta cuando se usa binary_crossentropy con más de 2 etiquetas

Me gustaría dar más detalles sobre esto, demostrar el problema subyacente real, explicarlo y ofrecer un remedio.

Este comportamiento no es un error; La razón subyacente es un problema bastante sutil e indocumentado sobre cómo Keras realmente adivina qué precisión usar, dependiendo de la función de pérdida que haya seleccionado, cuando se incluye simplemente metrics=['accuracy']en la compilación de su modelo. En otras palabras, mientras tu primera opción de compilación

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

es válido, tu segundo:

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

no producirá lo que espera, pero la razón no es el uso de entropía cruzada binaria (que, al menos en principio, es una función de pérdida absolutamente válida).

¿Porqué es eso? Si marca el código fuente de las métricas , Keras no define una sola métrica de precisión, sino varias, entre ellas binary_accuracyy categorical_accuracy. Lo que sucede debajo del capó es que, dado que seleccionó la entropía cruzada binaria como su función de pérdida y no especificó una métrica de precisión particular, Keras (erróneamente ...) infiere que le interesa binary_accuracy, y esto es lo que devuelve: mientras que de hecho estás interesado en el categorical_accuracy.

Verifiquemos que este sea el caso, utilizando el ejemplo MNIST CNN en Keras, con la siguiente modificación:

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    

Para remediar esto, es decir, para utilizar la entropía cruzada binaria como su función de pérdida (como dije, no hay nada de malo en esto, al menos en principio) mientras obtiene la precisión categórica requerida por el problema en cuestión, debe solicitarlo explícitamente categorical_accuracyen el compilación del modelo de la siguiente manera:

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

En el ejemplo MNIST, después de entrenar, puntuar y predecir el conjunto de pruebas como muestro arriba, las dos métricas ahora son las mismas, como deberían ser:

# 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    

Configuración del sistema:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

ACTUALIZACIÓN : Después de mi publicación, descubrí que este problema ya se había identificado en esta respuesta .

desertnaut
fuente
1
¿Hay algo de malo en usar loss='categorical_crossentropy', metrics=['categorical_accuracy']para la clasificación multiclase? Esta sería mi intuición
NeStack el
2
@NeStack No solo no hay nada malo, sino que esta es la combinación nominal.
desertnaut
1
De acuerdo con lo que dijo, siempre que use loss = 'binary_crossentropy', ¿obtendré los mismos retornos sin importar si uso metrics = 'binary_accuracy' o metrics = 'precision'?
BioCoder
2
@BioCoder exactamente
desertnaut
54

Todo depende del tipo de problema de clasificación con el que esté lidiando. Hay tres categorías principales.

  • clasificación binaria (dos clases objetivo),
  • clasificación de varias clases (más de dos objetivos exclusivos ),
  • clasificación de etiquetas múltiples (más de dos objetivos no exclusivos ), en la que pueden estar activadas varias clases de objetivos al mismo tiempo.

En el primer caso, se debe utilizar la entropía cruzada binaria y los objetivos deben codificarse como vectores de un solo calor.

En el segundo caso, se debe utilizar la entropía cruzada categórica y los objetivos deben codificarse como vectores de un solo calor.

En el último caso, se debe utilizar la entropía cruzada binaria y los objetivos deben codificarse como vectores de un solo calor. Cada neurona de salida (o unidad) se considera como una variable binaria aleatoria separada, y la pérdida para todo el vector de salidas es el producto de la pérdida de variables binarias individuales. Por lo tanto, es el producto de la entropía cruzada binaria para cada unidad de salida individual.

La entropía cruzada binaria se define como

ingrese la descripción de la imagen aquí

y la entropía cruzada categórica se define como

ingrese la descripción de la imagen aquí

donde cse ejecuta el índice sobre el número de clases

Whynote
fuente
Su respuesta me parece muy cierta, pero ... Traté de seguir la respuesta de @desertnaut e hice esas pruebas: con la función de pérdida de cruz cruzada binaria y la métrica para la precisión categórica tengo una mejor precisión que el uso de la función de pérdida de cruz cruzada categórica y las métricas de precisión, y no puedo explicar que ...
Metal3d
@ Metal3d: ¿cuál es la formulación de su problema: etiqueta múltiple o etiqueta única?
Whynote
etiqueta única, y ahora me doy cuenta de por qué eso funciona mejor :)
Metal3d
¿Está seguro de que las entropías cruzadas binarias y categóricas se definen como en las fórmulas de esta respuesta?
nbro
@nbro, en realidad, el cíndice es redundante en la fórmula binaria de entropía cruzada, no necesita estar allí (ya que solo hay 2 clases y la probabilidad de que cada clase esté incorporada y(x). De lo contrario, esas fórmulas deberían ser correctas, pero cuenta esas no son las pérdidas, esas son las probabilidades Si desea que la pérdida que tiene que tomar el. logde éstos.
Whynote
40

Encontré un problema "invertido": estaba obteniendo buenos resultados con categorical_crossentropy (con 2 clases) y pobre con binary_crossentropy. Parece que el problema fue con la función de activación incorrecta. La configuración correcta fue:

  • para binary_crossentropy: activación sigmoidea, objetivo escalar
  • para categorical_crossentropy: activación softmax, objetivo codificado en caliente
Alexander Svetkin
fuente
44
¿Está seguro acerca del objetivo escalar para binary_crossentropy? Parece que debería usar un objetivo codificado "muchos calientes" (por ejemplo, [0 1 0 0 1 1]).
Dmitry
55
Por supuesto. Ver keras.io/losses/#usage-of-loss-functions , dice: "cuando se utiliza la pérdida categorical_crossentropy, sus objetivos deben estar en formato categórico (por ejemplo, si tiene 10 clases, el objetivo para cada muestra debe ser un 10 vectorial tridimensional que es todo ceros, espera un 1 en el índice correspondiente a la clase de la muestra) "
Alexander Svetkin
1
Pero estamos hablando de binary_crossentropy - no categorical_crossentropy.
Dmitry
Esta respuesta parece ser inconsistente con stackoverflow.com/a/49175655/3924118 , donde el autor dice que los objetivos deben estar codificados en caliente, mientras que, en su respuesta, sugiere que deberían ser escalares. Deberías aclarar esto.
nbro
@AlexanderSvetkin, el objetivo debe estar codificado en todas partes, no solo cuando se usa entropía cruzada categórica
Whynote
28

Es un caso realmente interesante. En realidad, en su configuración, la siguiente afirmación es verdadera:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

Esto significa que hasta un factor de multiplicación constante sus pérdidas son equivalentes. El comportamiento extraño que estás observando durante una fase de entrenamiento podría ser un ejemplo de un fenómeno siguiente:

  1. Al principio, la clase más frecuente está dominando la pérdida, por lo que la red está aprendiendo a predecir principalmente esta clase para cada ejemplo.
  2. Después de aprender el patrón más frecuente, comienza a discriminar entre las clases menos frecuentes. Pero cuando está usando adam, la tasa de aprendizaje tiene un valor mucho menor que al principio del entrenamiento (es debido a la naturaleza de este optimizador). Hace que el entrenamiento sea más lento y evita que su red, por ejemplo, deje un mínimo local pobre menos posible.

Es por eso que este factor constante podría ayudar en caso de binary_crossentropy. Después de muchas épocas, el valor de la tasa de aprendizaje es mayor que en el categorical_crossentropycaso. Por lo general, reinicio el entrenamiento (y la fase de aprendizaje) algunas veces cuando noto tal comportamiento y / y ajusto los pesos de una clase usando el siguiente patrón:

class_weight = 1 / class_frequency

Esto hace que la pérdida de clases menos frecuentes equilibre la influencia de una pérdida de clase dominante al comienzo de un entrenamiento y en una parte adicional de un proceso de optimización.

EDITAR:

En realidad, lo comprobé aunque en el caso de las matemáticas:

binary_crossentropy = len(class_id_index) * categorical_crossentropy

debe mantenerse, en caso de kerasque no sea cierto, porque kerasestá normalizando automáticamente todas las salidas para resumir 1. Esta es la razón real detrás de este comportamiento extraño, ya que en caso de multiclasificación, tal normalización daña un entrenamiento.

Marcin Możejko
fuente
¿Te ha ayudado mi respuesta?
Marcin Możejko
1
Esta es una explicación muy plausible. Pero no estoy seguro de que sea realmente la razón principal. Porque también he observado en varios de mis alumnos trabajar este comportamiento extraño al aplicar binary-X-ent en lugar de cat-X-ent (lo cual es un error). ¡Y esto es cierto incluso cuando se entrena por solo 2 épocas! Usar class_weight con clases inversas anteriores no ayudó. Puede ser útil un ajuste riguroso de la tasa de aprendizaje, pero los valores predeterminados parecen favorecer bin-X-ent. Creo que esta cuestión merece más investigaciones ...
xtof54
1
Espera, no lo siento, no recibo tu actualización: el softmax siempre hace que las salidas sumen 1, ¿entonces no nos importa eso? ¿Y por qué esto dañaría el entrenamiento, siempre y cuando solo tengamos una sola clase de oro que sea correcta por ejemplo?
xtof54
20

Después de comentar la respuesta de @Marcin, he revisado con más cuidado el código de uno de mis estudiantes donde encontré el mismo comportamiento extraño, ¡incluso después de solo 2 épocas! (Entonces, la explicación de @ Marcin no era muy probable en mi caso).

Y descubrí que la respuesta es realmente muy simple: la precisión calculada con el método Keras evaluatees simplemente incorrecta cuando se usa binary_crossentropy con más de 2 etiquetas. Puede verificarlo volviendo a calcular la precisión usted mismo (primero llame al método de Keras "predecir" y luego calcule el número de respuestas correctas devueltas por predecir): obtiene la verdadera precisión, que es mucho menor que la de "evaluar" Keras.

xtof54
fuente
1
También vi un comportamiento similar en la primera iteración.
dolbi
10

Un ejemplo simple en un entorno de varias clases para ilustrar

supongamos que tiene 4 clases (codificadas con un solo disparo) y a continuación hay solo una predicción

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

cuando se utiliza categorical_crossentropy, la precisión es solo 0, solo le importa si obtiene la clase correcta.

sin embargo, cuando se usa binary_crossentropy, la precisión se calcula para todas las clases, sería del 50% para esta predicción. y el resultado final será la media de las precisiones individuales para ambos casos.

se recomienda utilizar categorical_crossentropy para el problema de varias clases (las clases se excluyen mutuamente) pero binary_crossentropy para el problema de múltiples etiquetas.

bazinga
fuente
8

Como es un problema de varias clases, debe usar la categorical_crossentropy, la entropía cruzada binaria producirá resultados falsos, lo más probable es que solo evalúe las dos primeras clases.

El 50% para un problema de varias clases puede ser bastante bueno, dependiendo del número de clases. Si tiene n clases, entonces 100 / n es el rendimiento mínimo que puede obtener al generar una clase aleatoria.

Dr. Snoopy
fuente
2

al usar la categorical_crossentropypérdida, sus objetivos deben estar en formato categórico (por ejemplo, si tiene 10 clases, el objetivo para cada muestra debe ser un vector de 10 dimensiones que es todo ceros, excepto un 1 en el índice correspondiente a la clase de muestra).

Priyansh
fuente
3
¿Cómo exactamente esto responde la pregunta?
desertnaut
2

Eche un vistazo a la ecuación y descubra que la entropía cruzada binaria no solo castiga a esas etiquetas = 1, predichas = 0, sino también a etiquetas = 0, predichas = 1.

Sin embargo, la entropía cruzada categórica solo castiga a esas etiquetas = 1 pero predice = 1. Es por eso que asumimos que solo hay UNA etiqueta positiva.

Kuang Yan
fuente
1

Está pasando una matriz de formas objetivo (x-dim, y-dim) mientras usa como pérdida categorical_crossentropy. categorical_crossentropyespera que los objetivos sean matrices binarias (1s y 0s) de forma (muestras, clases). Si sus objetivos son clases enteras, puede convertirlas al formato esperado a través de:

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

Alternativamente, puede usar la función de pérdida sparse_categorical_crossentropy, que espera objetivos enteros.

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

La binary_crossentropy (y_target, y_predict) no necesita aplicarse en un problema de clasificación binaria. .

En el código fuente de binary_crossentropy () , en nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)realidad se utilizó la función TensorFlow. Y, en la documentación , dice que:

Mide el error de probabilidad en tareas de clasificación discretas en las que cada clase es independiente y no se excluye mutuamente. Por ejemplo, uno podría realizar una clasificación de múltiples etiquetas donde una imagen puede contener un elefante y un perro al mismo tiempo.

翟志伟
fuente