Cómo implementar la función Softmax en Python

247

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:

ingrese la descripción de la imagen aquí

¿Dónde S(y_i)está la función softmax de y_iy ees la exponencial y jes el no. de columnas en el vector de entrada Y.

He intentado 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 explícitamente toma la diferencia de cada columna y el máximo y luego se 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?

alvas
fuente
66
Tengo curiosidad por qué intentaste implementarlo de esta manera con una función máxima. ¿Qué te hizo pensar de esa manera?
BBischof
1
No sé, pensé que tratar el máximo como 0 y algo así como mover el gráfico a la izquierda y recortar a 0 ayuda. Entonces mi rango se acorta de -inf to +infa -inf to 0. Supongo que estaba pensando demasiado. jajajaaa
alvas
1
Todavía tengo una sub) preguntas que no parecen ser respondidas a continuación. ¿Cuál es el significado de axis = 0en la respuesta sugerida por Udacity?
Parva Thakkar
3
Si echa un vistazo a la documentación numpy, analiza qué suma (x, axis = 0), y de forma similar axis = 1--. En resumen, proporciona la dirección en la que sumar una matriz de matrices. En este caso, le dice que sume los vectores. En este caso, eso corresponde a los denominadores en la función softmax.
BBischof
3
Es como cada dos semanas, hay una respuesta más correcta hasta el punto en que mis matemáticas no son lo suficientemente buenas como para decidir quién está en lo correcto =) ¿Algún genio en matemáticas que no proporcionó una respuesta puede ayudar a decidir cuál es la correcta?
alvas

Respuestas:

137

Ambas son correctas, pero la suya es la preferida desde el punto de vista de la estabilidad numérica.

Empiezas con

e ^ (x - max(x)) / sum(e^(x - max(x))

Al usar 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. Puede reemplazar max (x) con cualquier variable y se cancelaría.

Trevor Merrifield
fuente
44
Reformateando su respuesta @TrevorM para mayor aclaración: e ^ (x - max (x)) / sum (e ^ (x - max (x)) usando a ^ (b - c) = (a ^ b) / (a ​​^ c) tenemos, = e ^ x / {e ^ max (x) * sum (e ^ x / e ^ max (x))} = e ^ x / sum (e ^ x)
shanky_thebearer
55
@Trevor Merrifield, no creo que el primer enfoque haya recibido ningún "término innecesario". De hecho, es mejor que el segundo enfoque. He agregado este punto como una respuesta separada.
Shagun Sodhani
66
@Shagun Tienes razón. Los dos son matemáticamente equivalentes, pero no había considerado la estabilidad numérica.
Trevor Merrifield
Espero que no te importe: edité "término innecesario" en caso de que la gente no lea los comentarios (o los comentarios desaparezcan). Esta página recibe bastante tráfico de los motores de búsqueda y esta es actualmente la primera respuesta que la gente ve.
Alex Riley
Me pregunto por qué restas max (x) y no max (abs (x)) (arregla el signo después de determinar el valor). Si todos sus valores están por debajo de cero y son muy grandes en su valor absoluto, y solo el valor (el máximo) está cerca de cero, restar el máximo no cambiará nada. ¿No sería todavía numéricamente inestable?
Cerno
102

(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; que pasan a ser equivalente sólo para el caso especial de las matrices de puntuación 1-D. Lo habrías descubierto si hubieras probado también la matriz de puntuación en 2-D en el ejemplo proporcionado por Udacity quiz.

En cuanto a los resultados, la única diferencia real entre las dos soluciones es el axis=0argumento. Para ver que este es el caso, intentemos su solución ( your_softmax) y una donde la única diferencia sea el axisargumento:

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 puntaje 1-D, los resultados son de hecho 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 para la matriz de puntaje 2-D dada en el cuestionario 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 idéntico al esperado en el cuestionario Udacity, donde todas las columnas suman 1, que no es el caso con el primer resultado (incorrecto).

Entonces, todo el alboroto era en realidad un detalle de implementación: el axisargumento. De acuerdo con la documentación de numpy.sum :

El valor predeterminado, axis = None, sumará todos los elementos de la matriz de entrada

mientras que aquí queremos sumar en fila, 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énticos, por lo tanto, sus resultados idénticos en ese caso ...

El axistema de lado, su aplicación (es decir, su elección para restar el primer max) es en realidad mejor que la solución sugerida! De hecho, es la forma recomendada de implementar la función softmax: vea aquí la justificación (estabilidad numérica, también señalada por algunas otras respuestas aquí).

desertnaut
fuente
Bueno, si solo estás hablando de una matriz multidimensional. La primera solución se puede solucionar fácilmente agregando axisargumentos a ambos maxy sum. Sin embargo, la primera implementación es aún mejor ya que puede desbordarse fácilmente al tomarexp
Louis Yang
@LouisYang no estoy siguiendo; ¿Cuál es la "primera" solución? ¿Cuál no usa exp? ¿Qué más se ha modificado aquí aparte de agregar un axisargumento?
desertnaut
La primera solución se refiere a la solución de @alvas. La diferencia es que a la solución sugerida en la pregunta de alvas le falta la parte de restar el máximo. Esto puede causar un desbordamiento fácil, por ejemplo, exp (1000) / (exp (1000) + exp (1001)) vs exp (-1) / (exp (-1) + exp (0)) son los mismos en matemáticas pero el primero se desbordará.
Louis Yang
@LouisYang aún, no estoy seguro de entender la necesidad de tu comentario: todo esto ya se ha abordado explícitamente en la respuesta.
desertnaut
@LouisYang, por favor, no permita que la popularidad (posterior) del hilo lo engañe, e intente imaginar el contexto en el que se ofreció su propia respuesta: un OP desconcertado (" ambos dan el mismo resultado ") y una respuesta aceptada (¡todavía!) afirmando que " ambos son correctos " (bueno, son no ). La respuesta nunca fue " esa es la forma más correcta y eficiente de calcular softmax en general "; solo pretendía justificar por qué , en el cuestionario específico de Udacity discutido, las 2 soluciones no son equivalentes.
desertnaut
56

Entonces, este es realmente un comentario a la respuesta de desertnaut, pero todavía no puedo comentarlo debido a mi reputación. Como señaló, su versión solo es correcta si su entrada consiste en una sola muestra. Si su entrada consta de varias muestras, está mal. Sin embargo, la solución de desertnaut también está mal. El problema es que una vez que toma una entrada unidimensional y luego toma una entrada bidimensional. Déjame mostrarte esto.

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

# desertnaut solution (copied from his answer): 
def desertnaut_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

# my (correct) solution:
def softmax(z):
    assert len(z.shape) == 2
    s = np.max(z, axis=1)
    s = s[:, np.newaxis] # necessary step to do broadcasting
    e_x = np.exp(z - s)
    div = np.sum(e_x, axis=1)
    div = div[:, np.newaxis] # dito
    return e_x / div

Tomemos un ejemplo de desertnauts:

x1 = np.array([[1, 2, 3, 6]]) # notice that we put the data into 2 dimensions(!)

Esta es la salida:

your_softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

desertnaut_softmax(x1)
array([[ 1.,  1.,  1.,  1.]])

softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

Puedes ver que la versión de desernauts fallaría en esta situación. (No lo haría si la entrada fuera solo unidimensional como np.array ([1, 2, 3, 6]).

Ahora usemos 3 muestras ya que esa es la razón por la que usamos una entrada bidimensional. El siguiente x2 no es el mismo que el del ejemplo de desernauts.

x2 = np.array([[1, 2, 3, 6],  # sample 1
               [2, 4, 5, 6],  # sample 2
               [1, 2, 3, 6]]) # sample 1 again(!)

Esta entrada consiste en un lote con 3 muestras. Pero la muestra uno y tres son esencialmente lo mismo. ¡Ahora esperamos 3 filas de activaciones de softmax donde la primera debería ser la misma que la tercera y también la misma que nuestra activación de x1!

your_softmax(x2)
array([[ 0.00183535,  0.00498899,  0.01356148,  0.27238963],
       [ 0.00498899,  0.03686393,  0.10020655,  0.27238963],
       [ 0.00183535,  0.00498899,  0.01356148,  0.27238963]])


desertnaut_softmax(x2)
array([[ 0.21194156,  0.10650698,  0.10650698,  0.33333333],
       [ 0.57611688,  0.78698604,  0.78698604,  0.33333333],
       [ 0.21194156,  0.10650698,  0.10650698,  0.33333333]])

softmax(x2)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047],
       [ 0.01203764,  0.08894682,  0.24178252,  0.65723302],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

Espero que puedan ver que este es solo el caso con mi solución.

softmax(x1) == softmax(x2)[0]
array([[ True,  True,  True,  True]], dtype=bool)

softmax(x1) == softmax(x2)[2]
array([[ True,  True,  True,  True]], dtype=bool)

Además, aquí están los resultados de la implementación de TensorFlows softmax:

import tensorflow as tf
import numpy as np
batch = np.asarray([[1,2,3,6],[2,4,5,6],[1,2,3,6]])
x = tf.placeholder(tf.float32, shape=[None, 4])
y = tf.nn.softmax(x)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(y, feed_dict={x: batch})

Y el resultado:

array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037045],
       [ 0.01203764,  0.08894681,  0.24178252,  0.657233  ],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037045]], dtype=float32)
ChuckFive
fuente
66
Habría sido un gran comentario ;-)
Michael Benjamin
27
np.exp (z) / np.sum (np.exp (z), axis = 1, keepdims = True) alcanza el mismo resultado que su función softmax. Los pasos con s son innecesarios.
PabTorre
En lugar de` s = s[:, np.newaxis], s = s.reshape(z.shape[0],1)también debería funcionar.
Debashish
2
tantas soluciones incorrectas / ineficientes en esta página. Hágase un favor y use PabTorre's
Miss Palmer
@PabTorre, ¿quiso decir axis = -1? axis = 1 no funcionará para la entrada unidimensional
DiehardThe Tryhard
36

Diría que si bien ambos son matemáticamente correctos, en cuanto a la implementación, el primero es mejor. Al calcular softmax, los valores intermedios pueden llegar a ser muy grandes. Dividir dos números grandes puede ser numéricamente inestable. Estas notas (de Stanford) mencionan un truco de normalización que es esencialmente lo que estás haciendo.

Shagun Sodhani
fuente
3
Los efectos de la cancelación catastrófica no pueden subestimarse.
Cesar
24

sklearn también ofrece implementación de softmax

from sklearn.utils.extmath import softmax
import numpy as np

x = np.array([[ 0.50839931,  0.49767588,  0.51260159]])
softmax(x)

# output
array([[ 0.3340521 ,  0.33048906,  0.33545884]]) 
Orac romano
fuente
3
¿Cómo responde exactamente esto a la pregunta específica, que se refiere a la implementación en sí misma y no a la disponibilidad en alguna biblioteca de terceros?
desertnaut
8
Estaba buscando una implementación de terceros para verificar los resultados de ambos enfoques. Esta es la forma en que este comentario ayuda.
Eugenio F. Martinez Pacheco
13

Desde el punto de vista matemático, ambos lados son iguales.

Y puedes probar esto fácilmente. Vamos m=max(x). Ahora su función softmaxdevuelve un vector, cuya i-ésima coordenada es igual a

ingrese la descripción de la imagen aquí

tenga en cuenta que esto funciona para cualquiera m, porque para todos los números (incluso complejos)e^m != 0

  • desde el punto de vista de la complejidad computacional, también son equivalentes y ambos se ejecutan en el O(n)tiempo, donde nes el tamaño de un vector.

  • desde el punto de vista de la estabilidad numérica , se prefiere la primera solución, porque e^xcrece muy rápido e incluso xse desbordarán valores muy pequeños . Restar el valor máximo permite deshacerse de este desbordamiento. Para experimentar prácticamente las cosas de las que estaba hablando, intente alimentar x = np.array([1000, 5])sus dos funciones. Uno devolverá la probabilidad correcta, el segundo se desbordará connan

  • su solución solo funciona para vectores (el cuestionario Udacity quiere que también lo calcule para matrices). Para arreglarlo necesitas usarsum(axis=0)

Salvador Dalí
fuente
1
¿Cuándo es útil poder calcular softmax en matriz en lugar de vector? es decir, ¿qué modelos de matriz de salida? ¿Puede ser aún más dimensional?
mrgloom
2
¿te refieres a la primera solución en "desde el punto de vista de la estabilidad numérica, se prefiere la segunda solución ..."?
Dataman
10

EDITAR . A partir de la versión 1.2.0, scipy incluye softmax como una función especial:

https://scipy.github.io/devdocs/generated/scipy.special.softmax.html

Escribí una función aplicando el softmax sobre cualquier eje:

def softmax(X, theta = 1.0, axis = None):
    """
    Compute the softmax of each element along an axis of X.

    Parameters
    ----------
    X: ND-Array. Probably should be floats. 
    theta (optional): float parameter, used as a multiplier
        prior to exponentiation. Default = 1.0
    axis (optional): axis to compute values along. Default is the 
        first non-singleton axis.

    Returns an array the same size as X. The result will sum to 1
    along the specified axis.
    """

    # make X at least 2d
    y = np.atleast_2d(X)

    # find axis
    if axis is None:
        axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)

    # multiply y against the theta parameter, 
    y = y * float(theta)

    # subtract the max for numerical stability
    y = y - np.expand_dims(np.max(y, axis = axis), axis)

    # exponentiate y
    y = np.exp(y)

    # take the sum along the specified axis
    ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)

    # finally: divide elementwise
    p = y / ax_sum

    # flatten if X was 1D
    if len(X.shape) == 1: p = p.flatten()

    return p

Restar el máximo, como lo describieron otros usuarios, es una buena práctica. Escribí una publicación detallada al respecto aquí .

Nolan Conaway
fuente
9

Aquí puedes averiguar por qué lo usaron - max.

Desde allí:

"Cuando escribes código para calcular la función Softmax en la práctica, los términos intermedios pueden ser muy grandes debido a los exponenciales. Dividir números grandes puede ser numéricamente inestable, por lo que es importante usar un truco de normalización".

Sadegh Salehi
fuente
4

Una versión más concisa es:

def softmax(x):
    return np.exp(x) / np.exp(x).sum(axis=0)
Pimin Konstantin Kefaloukos
fuente
99
esto puede encontrarse con desbordamiento aritmético
minhle_r7
4

Para ofrecer una solución alternativa, considere los casos en que sus argumentos son de una magnitud extremadamente grande, de modo que exp(x)se desbordaría (en el caso negativo) o se desbordaría (en el caso positivo). Aquí desea permanecer en el espacio logarítmico el mayor tiempo posible, exponiendo solo al final donde puede confiar en que el resultado se comportará bien.

import scipy.special as sc
import numpy as np

def softmax(x: np.ndarray) -> np.ndarray:
    return np.exp(x - sc.logsumexp(x))
PikalaxALT
fuente
Para que sea igual al código de los carteles, debe agregar axis=0como argumento a logsumexp.
Björn Lindqvist
Alternativamente, uno podría desempaquetar args adicionales para pasar a logsumexp.
PikalaxALT
3

Necesitaba algo compatible con la salida de una capa densa de Tensorflow .

La solución de @desertnaut no funciona en este caso porque tengo lotes de datos. Por lo tanto, vine con otra solución que debería funcionar en ambos casos:

def softmax(x, axis=-1):
    e_x = np.exp(x - np.max(x)) # same code
    return e_x / e_x.sum(axis=axis, keepdims=True)

Resultados:

logits = np.asarray([
    [-0.0052024,  -0.00770216,  0.01360943, -0.008921], # 1
    [-0.0052024,  -0.00770216,  0.01360943, -0.008921]  # 2
])

print(softmax(logits))

#[[0.2492037  0.24858153 0.25393605 0.24827873]
# [0.2492037  0.24858153 0.25393605 0.24827873]]

Ref: Tensorflow softmax

Lucas Casagrande
fuente
Solo tenga en cuenta que la respuesta se refiere a un entorno muy específico descrito en la pregunta; nunca fue destinado a ser 'cómo calcular el softmax, en general, bajo ninguna circunstancia, o en el formato de datos de su gusto' ...
desertnaut
Ya veo, he puesto esto aquí porque la pregunta se refiere a la "clase de aprendizaje profundo de Udacity" y no funcionaría si está utilizando Tensorflow para construir su modelo. Su solución es fresca y limpia, pero solo funciona en un escenario muy específico. Gracias de cualquier manera.
Lucas Casagrande
1

Para mantener la estabilidad numérica, se debe restar max (x). El siguiente es el código para la función softmax;

def softmax (x):

if len(x.shape) > 1:
    tmp = np.max(x, axis = 1)
    x -= tmp.reshape((x.shape[0], 1))
    x = np.exp(x)
    tmp = np.sum(x, axis = 1)
    x /= tmp.reshape((x.shape[0], 1))
else:
    tmp = np.max(x)
    x -= tmp
    x = np.exp(x)
    tmp = np.sum(x)
    x /= tmp


return x
Rahul Ahuja
fuente
1

Ya respondí con mucho detalle en las respuestas anteriores. maxse resta para evitar el desbordamiento. Estoy agregando aquí una implementación más en python3.

import numpy as np
def softmax(x):
    mx = np.amax(x,axis=1,keepdims = True)
    x_exp = np.exp(x - mx)
    x_sum = np.sum(x_exp, axis = 1, keepdims = True)
    res = x_exp / x_sum
    return res

x = np.array([[3,2,4],[4,5,6]])
print(softmax(x))
Debashish
fuente
1

Todos parecen publicar su solución, así que yo publicaré la mía:

def softmax(x):
    e_x = np.exp(x.T - np.max(x, axis = -1))
    return (e_x / e_x.sum(axis=0)).T

Obtengo exactamente los mismos resultados que los importados de sklearn:

from sklearn.utils.extmath import softmax
Julian
fuente
1
import tensorflow as tf
import numpy as np

def softmax(x):
    return (np.exp(x).T / np.exp(x).sum(axis=-1)).T

logits = np.array([[1, 2, 3], [3, 10, 1], [1, 2, 5], [4, 6.5, 1.2], [3, 6, 1]])

sess = tf.Session()
print(softmax(logits))
print(sess.run(tf.nn.softmax(logits)))
sess.close()
Rey
fuente
Bienvenido a SO. Una explicación de cómo su código responde a la pregunta siempre es útil.
Nick
1

Basado en todas las respuestas y las notas de CS231n , permítanme resumir:

def softmax(x, axis):
    x -= np.max(x, axis=axis, keepdims=True)
    return np.exp(x) / np.exp(x).sum(axis=axis, keepdims=True)

Uso:

x = np.array([[1, 0, 2,-1],
              [2, 4, 6, 8], 
              [3, 2, 1, 0]])
softmax(x, axis=1).round(2)

Salida:

array([[0.24, 0.09, 0.64, 0.03],
       [0.  , 0.02, 0.12, 0.86],
       [0.64, 0.24, 0.09, 0.03]])
remykarem
fuente
0

Me gustaría complementar un poco más de comprensión del problema. Aquí es correcto restar max de la matriz. Pero si ejecuta el código en la otra publicación, descubrirá que no le da la respuesta correcta cuando la matriz tiene dimensiones 2D o superiores.

Aquí te doy algunas sugerencias:

  1. Para obtener el máximo, intente hacerlo a lo largo del eje x, obtendrá una matriz 1D.
  2. Cambie la forma de su matriz máxima a la forma original.
  3. ¿Np.exp obtiene un valor exponencial?
  4. Hacer np.sum a lo largo del eje.
  5. Obtén los resultados finales.

Siga el resultado y obtendrá la respuesta correcta haciendo la vectorización. Como está relacionado con la tarea de la universidad, no puedo publicar el código exacto aquí, pero me gustaría darle más sugerencias si no lo comprende.

Hao Xu
fuente
1
No está relacionado con ninguna tarea de la universidad, sólo para una prueba práctica, sin clasificar de una manera no acreditada por supuesto, donde se proporciona la respuesta correcta en el siguiente paso ...
desertnaut
0

El propósito de la función softmax es preservar la relación de los vectores en lugar de aplastar los puntos finales con un sigmoide a medida que los valores se saturan (es decir, tienden a +/- 1 (tanh) o de 0 a 1 (logístico)). Esto se debe a que conserva más información sobre la tasa de cambio en los puntos finales y, por lo tanto, es más aplicable a las redes neuronales con codificación de salida 1-de-N (es decir, si aplastamos los puntos finales sería más difícil diferenciar el 1 -of-N clase de salida porque no podemos decir cuál es el "más grande" o el "más pequeño" porque fueron aplastados); también hace que la producción total sume a 1, y el ganador claro estará más cerca de 1, mientras que otros números que están cerca uno del otro sumarán 1 / p, donde p es el número de neuronas de salida con valores similares.

El propósito de restar el valor máximo del vector es que cuando haces e ^ y exponentes puedes obtener un valor muy alto que recorta el flotador en el valor máximo que conduce a un empate, lo cual no es el caso en este ejemplo. Esto se convierte en un GRAN problema si resta el valor máximo para hacer un número negativo, entonces tiene un exponente negativo que reduce rápidamente los valores que alteran la relación, que es lo que ocurrió en la pregunta del póster y arrojó la respuesta incorrecta.

La respuesta proporcionada por Udacity es HORRIBLEMENTE ineficiente. Lo primero que debemos hacer es calcular e ^ y_j para todos los componentes del vector, MANTENER ESOS VALORES, luego sumarlos y dividirlos. Donde Udacity está en mal estado, calculan e ^ y_j ¡DOS VECES! Aquí está la respuesta correcta:

def softmax(y):
    e_to_the_y_j = np.exp(y)
    return e_to_the_y_j / np.sum(e_to_the_y_j, axis=0)

fuente
0

El objetivo era lograr resultados similares usando Numpy y Tensorflow. El único cambio de la respuesta original es el axisparámetro para np.sumapi.

Enfoque inicial : axis=0sin embargo, esto no proporciona los resultados previstos cuando las dimensiones son N.

Enfoque modificado : axis=len(e_x.shape)-1- Suma siempre en la última dimensión. Esto proporciona resultados similares a la función softmax de tensorflow.

def softmax_fn(input_array):
    """
    | **@author**: Prathyush SP
    |
    | Calculate Softmax for a given array
    :param input_array: Input Array
    :return: Softmax Score
    """
    e_x = np.exp(input_array - np.max(input_array))
    return e_x / e_x.sum(axis=len(e_x.shape)-1)
kingspp
fuente
0

Aquí hay una solución generalizada que utiliza numpy y comparación para la corrección con tensorflow y scipy:

Preparación de datos:

import numpy as np

np.random.seed(2019)

batch_size = 1
n_items = 3
n_classes = 2
logits_np = np.random.rand(batch_size,n_items,n_classes).astype(np.float32)
print('logits_np.shape', logits_np.shape)
print('logits_np:')
print(logits_np)

Salida:

logits_np.shape (1, 3, 2)
logits_np:
[[[0.9034822  0.3930805 ]
  [0.62397    0.6378774 ]
  [0.88049906 0.299172  ]]]

Softmax usando tensorflow:

import tensorflow as tf

logits_tf = tf.convert_to_tensor(logits_np, np.float32)
scores_tf = tf.nn.softmax(logits_np, axis=-1)

print('logits_tf.shape', logits_tf.shape)
print('scores_tf.shape', scores_tf.shape)

with tf.Session() as sess:
    scores_np = sess.run(scores_tf)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np,axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Salida:

logits_tf.shape (1, 3, 2)
scores_tf.shape (1, 3, 2)
scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.4965232  0.5034768 ]
  [0.64137274 0.3586273 ]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]

Softmax usando scipy:

from scipy.special import softmax

scores_np = softmax(logits_np, axis=-1)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np, axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Salida:

scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.4965232  0.5034768 ]
  [0.6413727  0.35862732]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]

Softmax usando numpy ( https://nolanbconaway.github.io/blog/2017/softmax-numpy ):

def softmax(X, theta = 1.0, axis = None):
    """
    Compute the softmax of each element along an axis of X.

    Parameters
    ----------
    X: ND-Array. Probably should be floats.
    theta (optional): float parameter, used as a multiplier
        prior to exponentiation. Default = 1.0
    axis (optional): axis to compute values along. Default is the
        first non-singleton axis.

    Returns an array the same size as X. The result will sum to 1
    along the specified axis.
    """

    # make X at least 2d
    y = np.atleast_2d(X)

    # find axis
    if axis is None:
        axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)

    # multiply y against the theta parameter,
    y = y * float(theta)

    # subtract the max for numerical stability
    y = y - np.expand_dims(np.max(y, axis = axis), axis)

    # exponentiate y
    y = np.exp(y)

    # take the sum along the specified axis
    ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)

    # finally: divide elementwise
    p = y / ax_sum

    # flatten if X was 1D
    if len(X.shape) == 1: p = p.flatten()

    return p


scores_np = softmax(logits_np, axis=-1)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np, axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Salida:

scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.49652317 0.5034768 ]
  [0.64137274 0.3586273 ]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]
mrgloom
fuente
0

La función softmax es una función de activación que convierte los números en probabilidades que suman uno. La función softmax genera un vector que representa las distribuciones de probabilidad de una lista de resultados. También es un elemento central utilizado en las tareas de clasificación de aprendizaje profundo.

La función Softmax se usa cuando tenemos múltiples clases.

Es útil para descubrir la clase que tiene el máximo. Probabilidad.

La función Softmax se usa idealmente en la capa de salida, donde en realidad estamos tratando de alcanzar las probabilidades para definir la clase de cada entrada.

Varía de 0 a 1.

La función Softmax convierte los logits [2.0, 1.0, 0.1] en probabilidades [0.7, 0.2, 0.1], y las probabilidades suman 1. Los logits son los puntajes brutos producidos por la última capa de una red neuronal. Antes de que tenga lugar la activación. Para comprender la función softmax, debemos mirar la salida de la capa (n-1) th.

La función softmax es, de hecho, una función arg max. Eso significa que no devuelve el valor más grande de la entrada, sino la posición de los valores más grandes.

Por ejemplo:

Antes de softmax

X = [13, 31, 5]

Después de softmax

array([1.52299795e-08, 9.99999985e-01, 5.10908895e-12]

Código:

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
Krishna veer
fuente