2016-05-06 16 views
4

in scipy.special.expit wird logistische Funktion wie folgt umgesetzt:Logistik/Sigmoidfunktion Implementierung numerische Genauigkeit

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

Ich habe jedoch in anderen Sprachen/Frameworks gesehen Implementierungen, die einfach tun

1/(1 + exp(-x)) 

I Ich frage mich, wie viel Gewinn die scipy Version tatsächlich bringt.

Für sehr kleine x nähert sich das Ergebnis 0. Es funktioniert auch, wenn exp(-x) zu Inf überläuft.

Antwort

3

Es ist wirklich nur für Stabilität - das Eingeben von Werten, die sehr groß sind, kann sonst unerwartete Ergebnisse liefern.

Wenn expit umgesetzt wurde ebenso 1/(1 + exp(-x)) dann einen Wert von -710 in die Funktion setzen nan zurückkehren würde, während -709 einen Wert nahe Null geben würde, wie erwartet. Dies liegt daran, exp(710) ist zu groß, um ein Doppel zu sein.

Die Verzweigung im Code bedeutet nur, dass dieses Szenario vermieden wird.

Siehe auch this question and answer auf Stack Overflow.

+0

Ich bin nicht sicher, ich Ihren Punkt folgen ... Stabilität wird als die Motivation für den Code in [diesem GitHub-Thread] (https://github.com/scipy/scipy/pull/3386) zitiert. –

+2

Was ich zu sagen versuchte, '1/(1 + exp (-710))' gibt 'NaN' nicht zurück, es gibt im IEEE-Standard 0 als '1/Inf = 0' zurück. Und es ist wahr in numpy '1/(1 + np.exp (-710)) == 0' – colinfang

+1

Aber das Problem ist sicherlich für' expit (x) ', wobei' x' ein großer * negativer * Wert ist . Zum Beispiel muss 'expit (-710)' den Exponenten von positivem 710, i, e, berechnen. 'exp (+710)'. Das 'exp' im Quellcode I * think * bezieht sich auf die C-Math-Bibliothek' exp' Ihres Systems (nicht die ufunc 'np.exp'), die einen Fehler verursacht, wenn ein Überlauf erkannt wird, wie es hier der Fall ist. –

2

Scheint wäre es effizienter zu bedienen:

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

es sei denn, Sie benötigen Nummer mit 10^-309 Präzision (siehe unten), die scheint übertrieben!

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

Eine andere Möglichkeit, zu tun wäre es

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

Da np.exp(x)/(np.exp(x) + np.exp(0)) sein entspricht 1./(1. + np.exp(-x)) aber stabiler für negative Werte