¿Cómo calcular una función sigmoidea logística en Python?
Esta es una función sigmoidea logística:
Lo sé x. ¿Cómo puedo calcular F(x) en Python ahora?
Digamos x = 0,458.
F(x) = ?
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.
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.cdf
es (mucho) más lento que expit
. expit
sigue siendo más lento que la sigmoid
funció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 expit
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ás el pequeño cambio de math.exp
a 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 expit
la implementación es numéricamente estable desde la versión 0.14.0: https://github.com/scipy/scipy/issues/3385
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.reduce
podría ser más preciso).
Otra forma transformando la tanh
función:
sigmoid = lambda x: .5 * (math.tanh(.5 * x) + 1)