Implementación de funciones logísticas / sigmoideas de precisión numérica.

En scipy.special.expit , la función logística se implementa como la siguiente:

 if x < 0 a = exp(x) a / (1 + a) else 1 / (1 + exp(-x)) 

Sin embargo, he visto implementaciones en otros lenguajes / marcos que simplemente hacen

 1 / (1 + exp(-x)) 

Me pregunto cuánto beneficio realmente trae la versión scipy.

Para x muy pequeño, el resultado se aproxima a 0. Funciona incluso si exp(-x) desborda a Inf .

En realidad, es solo para la estabilidad: la introducción de valores de una magnitud muy grande puede dar resultados inesperados de lo contrario.

Si expit se implementó solo como 1 / (1 + exp(-x)) , poner un valor de -710 en la función devolvería nan , mientras que -709 daría un valor cercano a cero como se esperaba. Esto se debe a que exp(710) es demasiado grande para ser doble.

La bifurcación en el código solo significa que se evita este escenario.

Vea también esta pregunta y respuesta en Desbordamiento de stack.

Parece que sería más eficiente usar:

 if x < -709 sigmoid = 0.0 else sigmoid = 1.0 / (1.0 + exp(-x)) 

a menos que necesite un número con una precisión de 10 ^ -309 (ver más abajo) que parece excesivo!

 >>> 1 / (1 + math.exp(709.78)) 5.5777796105262746e-309 

Otra forma de hacerlo sería

python np.where(x > 0, 1. / (1. + np.exp(-x)), np.exp(x) / (np.exp(x) + np.exp(0)))

Dado que np.exp(x) / (np.exp(x) + np.exp(0)) es equivalente a 1. / (1. + np.exp(-x)) pero más estable para valores negativos