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

Resuelto Richard Knop asked hace 14 años • 16 respuestas

Esta es una función sigmoidea logística:

ingrese la descripción de la imagen aquí

Lo sé x. ¿Cómo puedo calcular F(x) en Python ahora?

Digamos x = 0,458.

F(x) = ?

Richard Knop avatar Oct 21 '10 15:10 Richard Knop
Aceptado

Esto debería hacerlo:

import math

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

Y ahora puedes probarlo llamando a:

>>> 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 otras personas en las que la gente ha pensado un poco en este problema.

unwind avatar Oct 21 '2010 08:10 unwind

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 envoltorio 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 preocupa el rendimiento, continúe leyendo; de lo contrario, utilice expit.

Algunas evaluaciones comparativas:

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 lento que la sigmoidfunción de 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 llamada. Esta sobrecarga es mayor que la velocidad de cálculo expitdada 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ás el pequeño cambio de math.expa np.exp(el primero no admite matrices, pero es mucho más rápido si solo tienes 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 necesitas rendimiento, una práctica común es tener una tabla precalculada de la función sigmoidea que contiene RAM y cambiar algo de precisión y memoria por algo de velocidad (por ejemplo: http://radimrehurek.com/2013/09 /word2vec-en-python-segunda-optimización/ )

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

Théo T avatar Aug 06 '2014 15:08 Théo T

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 quizás esto sea más exacto:

import numpy as np

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

Internamente, implementa la misma condición 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).

Neil G avatar Apr 25 '2015 10:04 Neil G

Otra forma transformando la tanhfunción:

sigmoid = lambda x: .5 * (math.tanh(.5 * x) + 1)
dontloo avatar Apr 06 '2016 02:04 dontloo