¿Cómo calcular una función sigmoidea logística en Python?


146

Esta es una función logística sigmoidea:

ingrese la descripción de la imagen aquí

Yo se x. ¿Cómo puedo calcular F (x) en Python ahora?

Digamos x = 0.458.

F (x) =?

Respuestas:


219

Esto debería hacerlo:

import math

def sigmoid(x):
  return 1 / (1 + math.exp(-x))

Y ahora puedes probarlo llamando:

>>> sigmoid(0.458)
0.61253961344091512

Actualización : Tenga en cuenta que lo anterior fue pensado principalmente como una traducción directa uno a uno de la expresión dada al código Python. No se ha probado ni se sabe que sea una implementación numéricamente sólida. Si sabe que necesita una implementación muy sólida, estoy seguro de que hay otros en los que la gente realmente ha pensado en este problema.


77
Solo porque lo necesito con tanta frecuencia para probar pequeñas cosas:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
Esto no funciona para valores negativos extremos de x. Estaba usando esta desafortunada implementación hasta que noté que estaba creando NaNs.
Neil G

3
Si reemplaza math.expcon np.expno obtendrá NaNs, aunque recibirá advertencias de tiempo de ejecución.
Richard Rast

2
Usando math.expcon matriz numpy puede producir algunos errores, como: TypeError: only length-1 arrays can be converted to Python scalars. Para evitarlo debes usarlo numpy.exp.
ViniciusArruda

¿Se puede mitigar la inestabilidad numérica simplemente agregando x = max(-709,x)antes de la expresión?
Elias Hasle

201

También está disponible en scipy: http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.logistic.html

In [1]: from scipy.stats import logistic

In [2]: logistic.cdf(0.458)
Out[2]: 0.61253961344091512

que es solo un contenedor costoso (porque le permite escalar y traducir la función logística) de otra función scipy:

In [3]: from scipy.special import expit

In [4]: expit(0.458)
Out[4]: 0.61253961344091512

Si le preocupan las actuaciones, continúe leyendo, de lo contrario, solo use expit.

Algunos puntos de referencia:

In [5]: def sigmoid(x):
  ....:     return 1 / (1 + math.exp(-x))
  ....: 

In [6]: %timeit -r 1 sigmoid(0.458)
1000000 loops, best of 1: 371 ns per loop


In [7]: %timeit -r 1 logistic.cdf(0.458)
10000 loops, best of 1: 72.2 µs per loop

In [8]: %timeit -r 1 expit(0.458)
100000 loops, best of 1: 2.98 µs per loop

Como se esperaba logistic.cdfes (mucho) más lento que expit. expitsigue siendo más lenta que la sigmoidfunción python cuando se llama con un solo valor porque es una función universal escrita en C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) y, por lo tanto, tiene una sobrecarga de llamadas. Esta sobrecarga es mayor que la velocidad de cálculo deexpit dada por su naturaleza compilada cuando se llama con un solo valor. Pero se vuelve insignificante cuando se trata de grandes matrices:

In [9]: import numpy as np

In [10]: x = np.random.random(1000000)

In [11]: def sigmoid_array(x):                                        
   ....:    return 1 / (1 + np.exp(-x))
   ....: 

(Notará el pequeño cambio de math.expa np.exp(el primero no admite matrices, pero es mucho más rápido si solo tiene un valor para calcular))

In [12]: %timeit -r 1 -n 100 sigmoid_array(x)
100 loops, best of 1: 34.3 ms per loop

In [13]: %timeit -r 1 -n 100 expit(x)
100 loops, best of 1: 31 ms per loop

Pero cuando realmente necesita rendimiento, una práctica común es tener una tabla precalculada de la función sigmoidea que retiene la RAM y cambiar cierta precisión y memoria por cierta velocidad (por ejemplo: http://radimrehurek.com/2013/09 / word2vec-in-python-part-two-optimizing / )

Además, tenga en cuenta que la expitimplementación es numéricamente estable desde la versión 0.14.0: https://github.com/scipy/scipy/issues/3385


44
Al usar flotadores (1.) en lugar de ints (1) en su función sigmoide, reduciría el tiempo de ejecución en ~ 10%
kd88

No estoy seguro de entender lo que quieres decir (se usan flotadores en los ejemplos), pero en cualquier caso, rara vez se calcula un sigmoide en los números enteros.
Théo T

2
Lo que kd88 quiso decir fue que los literales numéricos que usó en su función (1) se analizan como enteros y deben emitirse en tiempo de ejecución para flotar. Obtendrá un mejor rendimiento utilizando literales de coma flotante (1.0).
krs013

Siempre puede vectorizar la función para que admita matrices.
agcala

quieres hablar de un envoltorio costoso? % timeit -r 1 expit (0.458)% timeit -r 1 1 / (1 + np.exp (0.458))
Andrew Louw

42

Así es como implementaría el sigmoide logístico de una manera numéricamente estable (como se describe aquí ):

def sigmoid(x):
    "Numerically-stable sigmoid function."
    if x >= 0:
        z = exp(-x)
        return 1 / (1 + z)
    else:
        z = exp(x)
        return z / (1 + z)

O tal vez esto es más preciso:

import numpy as np

def sigmoid(x):  
    return math.exp(-np.logaddexp(0, -x))

Internamente, implementa la misma condición que la anterior, pero luego usa log1p .

En general, el sigmoide logístico multinomial es:

def nat_to_exp(q):
    max_q = max(0.0, np.max(q))
    rebased_q = q - max_q
    return np.exp(rebased_q - np.logaddexp(-max_q, np.logaddexp.reduce(rebased_q)))

(Sin embargo, logaddexp.reducepodría ser más preciso).


refiriéndome al sigmoide logístico multinomial (softmax), si también quisiera un parámetro de temperatura para el aprendizaje por refuerzo , ¿es suficiente dividir max_qy rebased_qpor tau? porque lo intenté y no obtengo probabilidades que sumen 1
Ciprian Tomoiagă

@CiprianTomoiaga Si desea tener una temperatura, simplemente divida su evidencia ( q) por su temperatura. rebased_q puede ser cualquier cosa: no cambia la respuesta; Mejora la estabilidad numérica.
Neil G

¿estás seguro de que nat_to_expes equivalente a softmax (como mencionaste en tu otra respuesta)? Copiar y pegar devuelve probabilidades que no suman 1
Ciprian Tomoiagă

@CiprianTomoiaga La respuesta corta es que omito el componente final de la entrada y la salida, por lo que tendrá que calcularlo si lo desea como uno menos la suma del resto. La explicación más estadística es que la distribución categórica tiene parámetros naturales n-1 o parámetros de expectativa n-1.
Neil G

tiene sentido, más o menos. ¿Te importaría trabajar en mi pregunta ?
Ciprian Tomoiagă

7

de otra manera

>>> def sigmoid(x):
...     return 1 /(1+(math.e**-x))
...
>>> sigmoid(0.458)

1
¿Cuál es la diferencia entre esto y la función de desconexión? ¿Es math.e ** - x mejor que math.exp (-x)?
Richard Knop

No hay diferencia en términos de resultado de salida. Si desea saber la diferencia en términos de velocidad, puede usar timeit para cronometrar su ejecución. Pero eso realmente no es importante.
ghostdog74

9
powa menudo se implementa en términos de expy log, por lo que usarlo expdirectamente es casi seguro que sea mejor.
japreiss

2
Esto sufre desbordamientos cuando xes muy negativo.
Neil G

7

Otra forma al transformar la tanhfunción:

sigmoid = lambda x: .5 * (math.tanh(.5 * x) + 1)

@NeilG Matemáticamente, sigmoide (x) == (1 + tanh (x / 2)) / 2. Entonces, esta es una solución válida, aunque los métodos numéricamente estabilizados son superiores.
scottclowe

6

Creo que muchos podrían estar interesados ​​en parámetros libres para alterar la forma de la función sigmoidea. En segundo lugar, para muchas aplicaciones, desea utilizar una función sigmoidea reflejada. En tercer lugar, es posible que desee hacer una normalización simple, por ejemplo, los valores de salida están entre 0 y 1.

Tratar:

def normalized_sigmoid_fkt(a, b, x):
   '''
   Returns array of a horizontal mirrored normalized sigmoid function
   output between 0 and 1
   Function parameters a = center; b = width
   '''
   s= 1/(1+np.exp(b*(x-a)))
   return 1*(s-min(s))/(max(s)-min(s)) # normalize function to 0-1

Y para dibujar y comparar:

def draw_function_on_2x2_grid(x): 
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
    plt.subplots_adjust(wspace=.5)
    plt.subplots_adjust(hspace=.5)

    ax1.plot(x, normalized_sigmoid_fkt( .5, 18, x))
    ax1.set_title('1')

    ax2.plot(x, normalized_sigmoid_fkt(0.518, 10.549, x))
    ax2.set_title('2')

    ax3.plot(x, normalized_sigmoid_fkt( .7, 11, x))
    ax3.set_title('3')

    ax4.plot(x, normalized_sigmoid_fkt( .2, 14, x))
    ax4.set_title('4')
    plt.suptitle('Different normalized (sigmoid) function',size=10 )

    return fig

Finalmente:

x = np.linspace(0,1,100)
Travel_function = draw_function_on_2x2_grid(x)

Gráfico de funciones sigmoideas


6

Use el paquete numpy para permitir que su función sigmoide analice los vectores.

De conformidad con Deeplearning, utilizo el siguiente código:

import numpy as np
def sigmoid(x):
    s = 1/(1+np.exp(-x))
    return s

2

Buena respuesta de @unwind. Sin embargo, no puede manejar un número negativo extremo (arrojando OverflowError).

Mi mejora:

def sigmoid(x):
    try:
        res = 1 / (1 + math.exp(-x))
    except OverflowError:
        res = 0.0
    return res

Esto es mejor, pero aún tiene problemas de percusión numérica con valores negativos.
Neil G


2

Una versión numéricamente estable de la función sigmoidea logística.

    def sigmoid(x):
        pos_mask = (x >= 0)
        neg_mask = (x < 0)
        z = np.zeros_like(x,dtype=float)
        z[pos_mask] = np.exp(-x[pos_mask])
        z[neg_mask] = np.exp(x[neg_mask])
        top = np.ones_like(x,dtype=float)
        top[neg_mask] = z[neg_mask]
        return top / (1 + z)

1
si x es positivo, simplemente estamos usando 1 / (1 + np.exp (-x)) pero cuando x es negativo, estamos usando la función np.exp (x) / (1 + np.exp (x)) en lugar de usando 1 / (1 + np.exp (-x)) porque cuando x es negativo -x será positivo, entonces np.exp (-x) puede explotar debido al gran valor de -x.
Yash Khare

2

Un trazador de líneas ...

In[1]: import numpy as np

In[2]: sigmoid=lambda x: 1 / (1 + np.exp(-x))

In[3]: sigmoid(3)
Out[3]: 0.9525741268224334

1

Método vectorizado cuando se usa pandas DataFrame/Serieso numpy array:

Las principales respuestas son métodos optimizados para el cálculo de un solo punto, pero cuando desea aplicar estos métodos a una serie de pandas o matriz numpy, requiere apply , que es básicamente un bucle en el fondo e iterará sobre cada fila y aplicará el método. Esto es bastante ineficiente.

Para acelerar nuestro código, podemos hacer uso de la vectorización y la transmisión entumecida:

x = np.arange(-5,5)
np.divide(1, 1+np.exp(-x))

0    0.006693
1    0.017986
2    0.047426
3    0.119203
4    0.268941
5    0.500000
6    0.731059
7    0.880797
8    0.952574
9    0.982014
dtype: float64

O con un pandas Series:

x = pd.Series(np.arange(-5,5))
np.divide(1, 1+np.exp(-x))

1

puedes calcularlo como:

import math
def sigmoid(x):
  return 1 / (1 + math.exp(-x))

o conceptual, más profundo y sin ninguna importación:

def sigmoid(x):
  return 1 / (1 + 2.718281828 ** -x)

o puedes usar numpy para matrices:

import numpy as np #make sure numpy is already installed
def sigmoid(x):
  return 1 / (1 + np.exp(-x))

0
import numpy as np

def sigmoid(x):
    s = 1 / (1 + np.exp(-x))
    return s

result = sigmoid(0.467)
print(result)

El código anterior es la función sigmoidea logística en python. Si sé que x = 0.467, la función sigmoide, F(x) = 0.385. Puede intentar sustituir cualquier valor de x que conozca en el código anterior, y obtendrá un valor diferente de F(x).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.