Funciones de pérdida: MSE y entropía cruzada

Funciones de pérdida: MSE y entropía cruzada

30 min de lectura

Medir no es contestar, y la red sólo sabe contestar. La lección anterior, sobre el forward pass en forma matricial, cerró la mitad del recorrido que va de X\mathbf{X} a Y^\hat{\mathbf{Y}}: una cadena de productos que atraviesa las capas sin preguntarse ni una vez si lo que entrega se parece a lo que debía. La otra mitad empieza aquí, fijando una regla que compare Y^\hat{\mathbf{Y}} con Y\mathbf{Y} y resuma en un único número lo mal que va el batch. Esa regla se llama función de pérdida, y hay que elegirla.

Reglas que sirvan hay muchas, y la elección pesa. Toma divertida y la recomiendo, que habla bien de la película: su etiqueta es y=1y = 1, y la red contesta con un número entre 00 y 11 que vale más cuanto más se incline por esa clase. Supón que entrega un 0.0020.002. Falla, porque tenía que haber dado algo pegado al 11, y falla con toda la seguridad del mundo, porque más cerca del 00 no podía quedarse. Esta lección compara dos candidatas, el error cuadrático medio y la entropía cruzada, y las dos coinciden en que ahí la pérdida es alta: por ahí no se distinguen. Lo que las separa es cuánto cambia esa pérdida cuando un peso cambia, y en un ejemplo así la primera de las dos, que es la que se le ocurre a cualquiera, apenas se inmuta.

Antes de escribir ninguna fórmula, mira la forma de esa diferencia. Lo dibujado no son las dos pérdidas sino la corrección que entrega cada una: cuánto se mueve por cada unidad que se mueva la preactivación zz de la neurona de salida, que es de donde tiran los pesos. En verde la entropía cruzada; en gris el error cuadrático medio (mean squared error, MSE). Con la etiqueta valiendo 11, la red acierta hacia la derecha del dibujo y falla hacia la izquierda.

Una gráfica con la preactivación z de menos 6 a 6 en el eje horizontal y la corrección, de 0 a 1, en el vertical. La curva de la entropía cruzada, en verde, entra por la esquina superior izquierda pegada al 1, baja atravesando el centro por 0.5 y termina pegada al 0 en la derecha: decrece sin parar. La curva del MSE, en gris, entra por la esquina inferior izquierda pegada al 0, sube hasta un máximo de 0.30 poco antes del centro y vuelve a caer hasta el 0 por la derecha: es una joroba con las dos colas en el suelo.
Las dos colas del MSE valen lo mismo y significan lo contrario: la de la derecha es una red que acierta y no necesita corrección, y la de la izquierda es una red que falla con seguridad y tampoco la recibe. La entropía cruzada sólo se calla en un extremo.

De predicciones a un solo número

La red entrega Y^RB×dL\hat{\mathbf{Y}} \in \mathbb{R}^{B \times d_L}, y los ejemplos traen sus etiquetas en un array de la misma forma, YRB×dL\mathbf{Y} \in \mathbb{R}^{B \times d_L}, fila con fila. Una pérdida resume ese par en un número, y se construye en dos niveles: una función \ell que juzga un ejemplo comparando su predicción con su etiqueta, y la media de las BB que resulta,

L=1Bi=1B(y^i,yi),\mathcal{L} = \frac{1}{B} \sum_{i=1}^{B} \ell\left(\hat{\mathbf{y}}_i, \mathbf{y}_i\right),

donde y^i,yiRdL\hat{\mathbf{y}}_i, \mathbf{y}_i \in \mathbb{R}^{d_L} son la predicción y la etiqueta del ejemplo ii: las filas ii de Y^\hat{\mathbf{Y}} y de Y\mathbf{Y}, escritas en columna. Es la media y no la suma para que L\mathcal{L} no crezca con BB.

De \ell se exigen tres cosas y no pesan igual: que devuelva un número, que baje al acercarse la predicción a la etiqueta, y que tenga derivada respecto de cada parámetro, porque de esas derivadas está hecho el método que corrige los pesos —la siguiente lección sobre el descenso de gradiente—. Las dos primeras las cumple casi cualquier cosa; la tercera es la que descarta candidatas, y es el mismo criterio con el que la lección sobre funciones de activación y no linealidad eligió φ\varphi: allí el escalón se cayó por tener derivada nula, no por medir mal.

La primera de las dos mide la distancia al cuadrado. El error cuadrático medio es

LMSE=1Bi=1By^iyi2,MSE(y^,y)=(y^y)2,\mathcal{L}_{\text{MSE}} = \frac{1}{B} \sum_{i=1}^{B} \left\lVert \hat{\mathbf{y}}_i - \mathbf{y}_i \right\rVert^{2}, \qquad \ell_{\text{MSE}}(\hat{y}, y) = (\hat{y} - y)^{2},

con el subíndice nombrando cuál de las candidatas es, y con la segunda forma escrita para dL=1d_L = 1 —la red de reseñas—, donde y^\hat{y} e yy son escalares y los escribo sin negrita. El cuadrado hace dos cosas: quita el signo, de modo que pasarse y quedarse corto cuestan lo mismo, y castiga los errores grandes más que proporcionalmente, porque fallar por 0.80.8 cuesta dieciséis veces lo que fallar por 0.20.2, no cuatro.

Ese es el criterio correcto cuando la red predice una cantidad: si hay que adivinar cuántas estrellas le puso su autor, la diferencia y^y\hat{y} - y son estrellas y su cuadrado mide lo que se busca. Al derivar sale MSE/y^=2(y^y)\partial \ell_{\text{MSE}} / \partial \hat{y} = 2(\hat{y} - y), la primera propiedad buena de esta pérdida: la corrección es proporcional al error. Sobre ese factor 22 hay una convención que no adopto —mucha gente pone un 12\tfrac{1}{2} delante para que se cancele al derivar—, y no cambia nada medible: multiplicar todas las correcciones por una constante se absorbe en el tamaño del paso, que es lo que ajusta la lección siguiente.

Por qué se apaga justo cuando más falta hace

Clasificar no es predecir una cantidad, y ahí la cuenta cambia. La etiqueta vale 00 o 11, la salida sale de una sigmoide, y^=σ(z)\hat{y} = \sigma(z), y la derivada que hace falta no es la de y^\hat{y} sino la de zz, que es lo que los pesos controlan. Encadenando, y con σ(z)=σ(z)(1σ(z))\sigma'(z) = \sigma(z)\left(1 - \sigma(z)\right) de la lección sobre funciones de activación:

MSEz=MSEy^dy^dz=2(y^y)y^(1y^).\frac{\partial \ell_{\text{MSE}}}{\partial z} = \frac{\partial \ell_{\text{MSE}}}{\partial \hat{y}} \cdot \frac{d\hat{y}}{dz} = 2\left(\hat{y} - y\right)\hat{y}\left(1 - \hat{y}\right).

Mira el producto de los dos últimos factores: es la derivada de la sigmoide, y la lección sobre funciones de activación ya dijo dónde se apaga: cuando y^\hat{y} se acerca a 00 o a 11, o sea, cuando la red está segura. La saturación que allí era un defecto de la activación reaparece aquí multiplicando a la corrección. Con y=1y = 1 y z=6z = -6 —la red dice que no, con convicción, y se equivoca— el primer factor vale casi 2-2, pero y^(1y^)\hat{y}\left(1 - \hat{y}\right) vale 0.00250.0025, y el producto se queda en 0.00490.0049. El peor error del batch pide la corrección más pequeña del batch.

No es un caso raro elegido a mano: es la mitad izquierda entera de la curva gris de arriba. Y ningún ajuste del tamaño del paso arregla una pérdida que confunde sus dos colas, porque las escala a las dos por igual.

La entropía cruzada, leyendo la salida como probabilidad

El arreglo no es parchear la fórmula, sino cambiar lo que se supone que la red está diciendo. Hasta aquí y^\hat{y} era un número entre 00 y 11; tómalo ahora como la probabilidad que la red le da a la clase positiva, y^=P(y=1x)\hat{y} = P(y = 1 \mid \mathbf{x}). Con eso, la probabilidad que le asigna a la etiqueta que traía el ejemplo se escribe de una vez para los dos casos,

P(yx)=y^y(1y^)1y,P(y \mid \mathbf{x}) = \hat{y}^{\,y}\left(1 - \hat{y}\right)^{1-y},

porque con y=1y = 1 el segundo factor vale 11 y queda y^\hat{y}, y con y=0y = 0 es el primero el que desaparece. Suponiendo los BB ejemplos independientes, la probabilidad de observar el conjunto entero es el producto de las BB, y a esa cantidad, como función de los parámetros, se la llama la verosimilitud. Hay que hacerla grande. Multiplicar BB números menores que uno lleva el producto a cero muy deprisa, así que tomamos logaritmos —que conservan el orden, luego el máximo no se mueve—, le damos la vuelta al signo para tener algo que minimizar y dividimos entre BB. Sale la entropía cruzada:

LEC=1Bi=1B[yilogy^i+(1yi)log(1y^i)].\mathcal{L}_{\text{EC}} = -\frac{1}{B} \sum_{i=1}^{B} \left[\, y_i \log \hat{y}_i + \left(1 - y_i\right)\log\left(1 - \hat{y}_i\right) \right].

Léela por ejemplos. Uno con yi=1y_i = 1 aporta logy^i-\log \hat{y}_i: vale 00 si la red le da probabilidad 11 y crece sin techo según esa probabilidad baja hacia cero, y uno con yi=0y_i = 0 mira a 1y^i1 - \hat{y}_i. Que no haya techo es la diferencia de fondo con el MSE, que nunca pasaba de 11.

Ahora la derivada, que es lo que decide. Derivamos EC=ylogy^(1y)log(1y^)\ell_{\text{EC}} = -y\log\hat{y} - (1-y)\log(1-\hat{y}) respecto de y^\hat{y}, ponemos los dos términos sobre denominador común y encadenamos con la sigmoide:

ECy^=yy^+1y1y^=y(1y^)+y^(1y)y^(1y^)=y^yy^(1y^),ECz=y^yy^(1y^)y^(1y^)=y^y.\begin{aligned} \frac{\partial \ell_{\text{EC}}}{\partial \hat{y}} &= -\frac{y}{\hat{y}} + \frac{1 - y}{1 - \hat{y}} = \frac{-y\left(1 - \hat{y}\right) + \hat{y}\left(1 - y\right)}{\hat{y}\left(1 - \hat{y}\right)} = \frac{\hat{y} - y}{\hat{y}\left(1 - \hat{y}\right)}, \\[4pt] \frac{\partial \ell_{\text{EC}}}{\partial z} &= \frac{\hat{y} - y}{\hat{y}\left(1 - \hat{y}\right)} \cdot \hat{y}\left(1 - \hat{y}\right) = \hat{y} - y \end{aligned}.

En la primera línea el numerador se simplifica porque los dos productos yy^y\hat{y} se cancelan. En la segunda, el denominador es exactamente el y^(1y^)\hat{y}(1 - \hat{y}) que la sigmoide aporta al encadenar, y los dos se van.

La corrección es la diferencia entre lo que la red dijo y lo que debía decir. Nada más. En el ejemplo de antes vale 0.9975-0.9975 en lugar de los 0.00490.0049 del MSE. La saturación no ha desaparecido, la sigue teniendo la sigmoide; lo que pasa es que la pérdida trae en su derivada el factor inverso exacto que la deshace, y por eso la fórmula final no menciona a φ\varphi.

Más de dos clases: softmax

Con tres respuestas en lugar de dos —positiva, negativa y neutra— la capa de salida tiene dL=3d_L = 3 neuronas y la sigmoide ya no sirve: aplasta cada coordenada por su cuenta, y tres números en (0,1)(0,1) no forman una distribución. La función que sí lo hace es el softmax, el mismo de la lección sobre Word2Vec, que repartía probabilidad entre las entradas del vocabulario:

y^k=ezkm=1dLezm,k=1,,dL,\hat{y}_k = \frac{e^{z_k}}{\sum_{m=1}^{d_L} e^{z_m}}, \qquad k = 1, \dots, d_L,

positivo por ser exponencial y sumando 11 por el denominador. La etiqueta se escribe one-hot, y{0,1}dL\mathbf{y} \in \{0,1\}^{d_L} con un único 11 en la posición de la clase correcta cc, tal como la lección sobre one-hot codificaba una entrada del vocabulario. La pérdida es la de antes, escrita para dLd_L clases:

=k=1dLyklogy^k=logy^c,\ell = -\sum_{k=1}^{d_L} y_k \log \hat{y}_k = -\log \hat{y}_c,

donde la segunda igualdad sale de que la one-hot anula todos los sumandos menos uno. Sólo cuenta la probabilidad que la red le dio a la clase verdadera; lo que reparta entre las otras importa porque el denominador las ata a todas.

La derivada, con las dos clases de índice que hay que distinguir

Derivamos =logy^c\ell = -\log \hat{y}_c respecto de cada zjz_j. El softmax mete todas las coordenadas en cada una —mover zjz_j mueve el denominador, y con él las dLd_L probabilidades—, así que hacen falta dos cuentas, según jj sea o no la clase correcta. Llamemos S=mezmS = \sum_{m} e^{z_m}, con S/zj=ezj\partial S / \partial z_j = e^{z_j}.

Cuando j=cj = c, el numerador también depende de zjz_j, y es una derivada de un cociente:

y^czc=ezcSezcezcS2=ezcS(1ezcS)=y^c(1y^c).\frac{\partial \hat{y}_c}{\partial z_c} = \frac{e^{z_c} S - e^{z_c} e^{z_c}}{S^{2}} = \frac{e^{z_c}}{S}\left(1 - \frac{e^{z_c}}{S}\right) = \hat{y}_c\left(1 - \hat{y}_c\right).

Cuando jcj \neq c, el numerador ezce^{z_c} no depende de zjz_j y sólo se mueve el denominador:

y^czj=ezc(1S2)ezj=ezcSezjS=y^cy^j.\frac{\partial \hat{y}_c}{\partial z_j} = e^{z_c} \cdot \left(-\frac{1}{S^{2}}\right) e^{z_j} = -\frac{e^{z_c}}{S}\cdot\frac{e^{z_j}}{S} = -\hat{y}_c\,\hat{y}_j.

Con /y^c=1/y^c\partial \ell / \partial \hat{y}_c = -1/\hat{y}_c, la regla de la cadena da los dos casos:

zc=1y^cy^c(1y^c)=y^c1,zj=1y^c(y^cy^j)=y^j(jc),\frac{\partial \ell}{\partial z_c} = -\frac{1}{\hat{y}_c}\,\hat{y}_c\left(1 - \hat{y}_c\right) = \hat{y}_c - 1, \qquad \frac{\partial \ell}{\partial z_j} = -\frac{1}{\hat{y}_c}\left(-\hat{y}_c\,\hat{y}_j\right) = \hat{y}_j \quad (j \neq c),

y las dos son la misma expresión, porque yc=1y_c = 1 y yj=0y_j = 0. Coordenada a coordenada:

z=y^y.\nabla_{\mathbf{z}}\,\ell = \hat{\mathbf{y}} - \mathbf{y}.

Con dL=2d_L = 2 esto reproduce el caso binario: la fórmula corta de la sección anterior no era un accidente de tener una sola salida.

Las dos derivaciones llegan al mismo sitio, y por eso merece la pena hacerlas una vez: de aquí a la lección sobre backpropagation, la capa de salida no vuelve a derivarse, entra con y^y\hat{\mathbf{y}} - \mathbf{y} y ya está. El softmax está aquí porque el resto del curso predice sobre vocabularios enteros, donde las clases son decenas de miles.

Las dos pérdidas sobre las diez reseñas

La celda evalúa las dos candidatas sobre las diez reseñas de siempre, con las preactivaciones de la neurona de pesos puestos a mano de la lección sobre la neurona artificial. Nada va al azar. Ejecútala y mira tres cosas: qué ordena cada columna, dónde se apaga cada corrección, y qué pasa con un logaritmo de cero.

import numpy as np

# --- Las diez reseñas de siempre, con sus etiquetas y sus preactivaciones.
y = np.array([1., 0., 1., 0., 1., 0., 0., 1., 0., 1.])
z = np.array([2., -2., 2., -2., 2., -1., -2., 1., 0., 2.])
sigmoide = lambda z: 1.0 / (1.0 + np.exp(-z))


def mse(y_hat, y):
return float(np.mean((y_hat - y) ** 2))


def entropia_cruzada(y_hat, y):
p = np.clip(y_hat, 1e-12, 1.0 - 1e-12) # sin esto, un 0 se lleva la media entera
return float(-np.mean(y * np.log(p) + (1.0 - y) * np.log(1.0 - p)))


# 1. Tres redes: la de pesos a mano, la misma sin convicción, y una que contesta 0.5 a todo.
convencida = sigmoide(z)
tibia = 0.5 + (convencida - 0.5) * 0.1

print("%-25s %8s %8s %8s" % ("", "aciertos", "MSE", "EC"))
for nombre, p in [("neurona de pesos a mano", convencida),
("la misma, sin convicción", tibia),
("contesta 0.5 a todo", np.full(10, 0.5))]:
aciertos = int(((p >= 0.5) == (y == 1.0)).sum())
print("%-25s %8d %8.4f %8.4f" % (nombre, aciertos, mse(p, y), entropia_cruzada(p, y)))
print()

# 2. La corrección de cada pérdida, para un ejemplo cuya etiqueta vale 1.
print("%6s %6s %11s %11s %9s" % ("z", "ŷ", "|dMSE/dz|", "|dEC/dz|", "cociente"))
for zz in [-6.0, -3.0, 0.0, 3.0, 6.0]:
s = sigmoide(zz)
d_mse = abs(2.0 * (s - 1.0) * s * (1.0 - s))
d_ec = abs(s - 1.0)
print("%6.1f %6.3f %11.6f %11.6f %9.1f" % (zz, s, d_mse, d_ec, d_ec / d_mse))
print()

# 3. El logaritmo de cero, y lo que cuesta taparlo.
p = np.array([0.9, 0.0, 0.5])
recortada = np.clip(p, 1e-12, 1.0)
with np.errstate(divide="ignore"):
print("sin recortar:", -np.log(p), " media:", float(-np.mean(np.log(p))))
print("recortando: ", np.round(-np.log(recortada), 2),
" media:", round(float(-np.mean(np.log(recortada))), 2))
numpy

La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador.

La primera tabla justifica la lección. El recuento de aciertos empata las dos primeras en nueve de diez: no distingue entre decir 0.8810.881 y decir 0.5380.538, porque las dos caen del mismo lado. Las dos pérdidas las separan —0.0490.049 contra 0.2200.220 la convencida, 0.2200.220 contra 0.6330.633 la tibia—, y dejan en último lugar a la que contesta 0.50.5 a todo, con 0.2500.250 y 0.6930.693, que es log2\log 2. Ordenan igual. Lo que no hacen igual es corregir.

Eso es la segunda tabla. En z=6z = -6, con la etiqueta valiendo 11, la entropía cruzada entrega 0.99750.9975 y el MSE 0.00490.0049: un cociente de 202.7202.7. En z=0z = 0, con la red sin opinión, es 22. La ventaja crece hacia los extremos, y uno de ellos es donde la red se equivoca. La última fila repite el 202.7202.7 con la red acertando, y ahí las dos correcciones son minúsculas porque deben serlo.

La tercera prueba enseña el detalle que rompe una implementación. log0-\log 0 es infinito, y basta un ejemplo al que la red le dé probabilidad cero para que la media del batch entero valga infinito y deje de decir nada. El recorte a 101210^{-12} lo evita a un precio que digo en voz alta: ese 27.6327.63 no lo ha medido nadie.

Comprueba tu intuición

Cinco preguntas —una entropía cruzada a mano, el MSE al clasificar, la corrección limpia, el recorte del logaritmo y el máximo del softmax— y un desafío con la capa de salida.

Una red con sigmoide en la salida le asigna y^=0.2\hat{y} = 0.2 a una reseña cuya etiqueta es y=1y = 1. ¿Cuánto vale la entropía cruzada de ese ejemplo?

Se acepta un margen de ±0.01.

¿Qué le pasa al error cuadrático medio cuando la salida de la red es una sigmoide y la etiqueta vale 00 o 11?

Marca todo lo que sea cierto sobre el resultado z=y^y\dfrac{\partial \ell}{\partial z} = \hat{y} - y de esta lección.

Marca todas las opciones correctas. Se corrige todo o nada: no hay puntuación parcial.

Así recorta la celda las probabilidades antes de tomar el logaritmo. ¿Qué imprime?

import numpy as np
 
p = np.array([0.9, 1e-15, 0.5])
print(np.round(-np.log(np.clip(p, 1e-12, 1.0)), 2))
 

softmax(z) y softmax(z + 100) devuelven exactamente lo mismo. ¿Por qué, y qué se gana restando el máximo de cada fila antes de exponenciar?

Escribe la capa de salida de una red de varias clases, sobre un batch entero y sin bucles de Python:

  • softmax(Z) recibe Z de forma (B,dL)(B,\, d_L) y devuelve las probabilidades fila a fila. Cada fila tiene que sumar 11, y la función tiene que sobrevivir a preactivaciones grandes: np.exp(1000) desborda.
  • entropia_cruzada(Y_hat, Y) devuelve la media sobre el batch de kyklogy^k-\sum_k y_k \log \hat{y}_k, con Y codificada one-hot. Un y^k\hat{y}_k que valga cero no puede devolver inf.

Ninguna de las dos puede escribir en los arrays que recibe.

La primera comprobación descarga el intérprete de Python (~15 MB); después queda en la caché del navegador. Este desafío se resuelve mejor con un teclado físico: en el móvil puedes leerlo y volver luego.


Ya hay un número que dice lo mal que va la red, y en toda la lección no se ha movido ni un peso. Las tres redes llegaron con sus números puestos, la pérdida las ha ordenado, que es lo que se le pedía, y ninguna ha mejorado por haber sido medida.

Entrenar es hacer pequeño ese número, y lo único que se puede tocar para conseguirlo son los pesos: cambiarlos cambia Y^\hat{\mathbf{Y}}, y con ella L\mathcal{L}. De ese movimiento, la derivada da el sentido —si y^y\hat{y} - y es positivo, subir zz sube la pérdida, luego hay que bajarlo— y no da la distancia: describe la pendiente en el punto donde estás y deja de valer en cuanto te mueves. Así que los pesos no se corrigen de una vez, sino a pasos, y el tamaño de cada paso es una decisión: demasiado corto no llega nunca, demasiado largo se pasa. Cómo se elige, qué ocurre cuando se elige mal y por qué repetirlo acaba dando con el mínimo sin verlo, es la siguiente lección, sobre descenso de gradiente.

Para profundizar1 fuente · 1 libro

De dónde sale lo de esta lección, y dónde seguir si quieres más. Nada de aquí hace falta para continuar el curso.

  • Deep Learning, cap. 6: Deep Feedforward Networks
    libroGoodfellow, Bengio y Courville, 2016deeplearningbook.orgEN

    Su §6.2 saca la pérdida de la verosimilitud —que es la entropía cruzada— y explica por qué una salida que satura no va con el error cuadrático medio, y cómo el logaritmo lo arregla. El argumento de la lección, con menos álgebra.