¿Cómo implementar la función Softmax en Python?
De la clase de aprendizaje profundo de Udacity , el softmax de y_i es simplemente el exponencial dividido por la suma del exponencial de todo el vector Y:
¿Dónde S(y_i)
está la función softmax de y_i
y e
es la exponencial y j
es el no. de columnas en el vector de entrada Y.
Intenté lo siguiente:
import numpy as np
def softmax(x):
"""Compute softmax values for each sets of scores in x."""
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum()
scores = [3.0, 1.0, 0.2]
print(softmax(scores))
que devuelve:
[ 0.8360188 0.11314284 0.05083836]
Pero la solución sugerida fue:
def softmax(x):
"""Compute softmax values for each sets of scores in x."""
return np.exp(x) / np.sum(np.exp(x), axis=0)
que produce el mismo resultado que la primera implementación , aunque la primera implementación toma explícitamente la diferencia de cada columna y el máximo y luego la divide por la suma.
¿Alguien puede mostrar matemáticamente por qué? ¿Es uno correcto y el otro incorrecto?
¿La implementación es similar en términos de código y complejidad de tiempo? ¿Cuál es más eficiente?
Ambos son correctos, pero se prefiere el suyo desde el punto de vista de la estabilidad numérica.
empiezas con
e ^ (x - max(x)) / sum(e^(x - max(x))
Usando el hecho de que a^(b - c) = (a^b)/(a^c) tenemos
= e ^ x / (e ^ max(x) * sum(e ^ x / e ^ max(x)))
= e ^ x / sum(e ^ x)
Que es lo que dice la otra respuesta. Podrías reemplazar max(x) con cualquier variable y se cancelaría.
(Bueno... mucha confusión aquí, tanto en la pregunta como en las respuestas...)
Para empezar, las dos soluciones (es decir, la suya y la sugerida) no son equivalentes; resultan ser equivalentes sólo para el caso especial de matrices de puntuación 1-D. Lo habría descubierto si hubiera probado también la matriz de puntuación 2-D en el ejemplo proporcionado por el cuestionario de Udacity.
En cuanto a los resultados, la única diferencia real entre las dos soluciones es el axis=0
argumento. Para ver que este es el caso, probemos su solución ( your_softmax
) y una donde la única diferencia es el axis
argumento:
import numpy as np
# your solution:
def your_softmax(x):
"""Compute softmax values for each sets of scores in x."""
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum()
# correct solution:
def softmax(x):
"""Compute softmax values for each sets of scores in x."""
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum(axis=0) # only difference
Como dije, para una matriz de puntuación 1-D, los resultados son idénticos:
scores = [3.0, 1.0, 0.2]
print(your_softmax(scores))
# [ 0.8360188 0.11314284 0.05083836]
print(softmax(scores))
# [ 0.8360188 0.11314284 0.05083836]
your_softmax(scores) == softmax(scores)
# array([ True, True, True], dtype=bool)
Sin embargo, aquí están los resultados de la matriz de puntuación 2-D proporcionada en el cuestionario de Udacity como ejemplo de prueba:
scores2D = np.array([[1, 2, 3, 6],
[2, 4, 5, 6],
[3, 8, 7, 6]])
print(your_softmax(scores2D))
# [[ 4.89907947e-04 1.33170787e-03 3.61995731e-03 7.27087861e-02]
# [ 1.33170787e-03 9.84006416e-03 2.67480676e-02 7.27087861e-02]
# [ 3.61995731e-03 5.37249300e-01 1.97642972e-01 7.27087861e-02]]
print(softmax(scores2D))
# [[ 0.09003057 0.00242826 0.01587624 0.33333333]
# [ 0.24472847 0.01794253 0.11731043 0.33333333]
# [ 0.66524096 0.97962921 0.86681333 0.33333333]]
Los resultados son diferentes: el segundo es, de hecho, idéntico al esperado en el cuestionario de Udacity, donde todas las columnas suman 1, lo que no es el caso con el primer resultado (incorrecto).
Entonces, todo el alboroto se debió en realidad a un detalle de implementación: el axis
argumento. Según la documentación de numpy.sum :
El valor predeterminado, eje = Ninguno, sumará todos los elementos de la matriz de entrada.
mientras que aquí queremos sumar por filas, por lo tanto axis=0
. Para una matriz 1-D, la suma de la (única) fila y la suma de todos los elementos resultan ser idénticas, de ahí sus resultados idénticos en ese caso...
Dejando a un lado el axis
problema, su implementación (es decir, su elección de restar el máximo primero) es en realidad mejor que la solución sugerida. De hecho, es la forma recomendada de implementar la función softmax; consulte aquí la justificación (estabilidad numérica, también señalada por algunas otras respuestas aquí).