Wie berechnet man eine logistische Sigmoidfunktion in Python?


145

Dies ist eine logistische Sigmoidfunktion:

Geben Sie hier die Bildbeschreibung ein

Ich weiß x. Wie kann ich jetzt F (x) in Python berechnen?

Sagen wir x = 0,458.

F (x) =?

Antworten:


218

Dies sollte es tun:

import math

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

Und jetzt können Sie es testen, indem Sie anrufen:

>>> sigmoid(0.458)
0.61253961344091512

Update : Beachten Sie, dass das oben Gesagte hauptsächlich als direkte Eins-zu-Eins-Übersetzung des angegebenen Ausdrucks in Python-Code gedacht war. Es ist nicht getestet oder bekannt, dass es sich um eine numerisch einwandfreie Implementierung handelt. Wenn Sie wissen, dass Sie eine sehr robuste Implementierung benötigen, gibt es sicher andere, bei denen die Leute tatsächlich über dieses Problem nachgedacht haben.


7
Nur weil ich es so oft brauche, um kleine Dinge auszuprobieren:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
Dies funktioniert nicht für extrem negative Werte von x. Ich habe diese unglückliche Implementierung verwendet, bis ich bemerkte, dass NaNs erstellt wurden.
Neil G

3
Wenn Sie ersetzen math.expmit np.expIhnen nicht NaNs bekommen, obwohl Sie Laufzeit - Warnungen erhalten.
Richard Rast

2
Die Verwendung math.expmit einem numpy-Array kann zu Fehlern führen, z TypeError: only length-1 arrays can be converted to Python scalars. Um dies zu vermeiden, sollten Sie verwenden numpy.exp.
ViniciusArruda

Kann die numerische Instabilität einfach durch Hinzufügen x = max(-709,x)vor dem Ausdruck verringert werden ?
Elias Hasle

201

Es ist auch in scipy verfügbar: 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

Dies ist nur ein kostspieliger Wrapper (da Sie damit die logistische Funktion skalieren und übersetzen können) einer anderen Scipy-Funktion:

In [3]: from scipy.special import expit

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

Wenn Sie über Aufführungen besorgt sind, lesen Sie weiter, andernfalls verwenden Sie einfach expit.

Einige Benchmarking:

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

Wie erwartet logistic.cdfist (viel) langsamer als expit. expitist immer noch langsamer als die Python- sigmoidFunktion, wenn sie mit einem einzelnen Wert aufgerufen wird, da es sich um eine universelle Funktion handelt, die in C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) geschrieben ist und daher einen Aufrufaufwand hat. Dieser Overhead ist größer als die Rechengeschwindigkeit, expitdie durch seine kompilierte Natur gegeben ist, wenn er mit einem einzelnen Wert aufgerufen wird. Aber es wird vernachlässigbar, wenn es um große Arrays geht:

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))
   ....: 

(Sie werden die winzige Änderung von math.expnach bemerken np.exp(die erste unterstützt keine Arrays, ist jedoch viel schneller, wenn Sie nur einen Wert berechnen müssen))

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

Wenn Sie jedoch wirklich Leistung benötigen, ist es üblich, eine vorberechnete Tabelle der Sigmoid-Funktion im RAM zu haben und Präzision und Speicher gegen eine gewisse Geschwindigkeit einzutauschen (zum Beispiel: http://radimrehurek.com/2013/09) / word2vec-in-python-part-two-optimizing / )

Beachten Sie außerdem, dass die expitImplementierung seit Version 0.14.0 numerisch stabil ist: https://github.com/scipy/scipy/issues/3385


4
Durch die Verwendung von Floats (1.) anstelle von Ints (1) in Ihrer Sigmoid-Funktion würden Sie die Laufzeit um ~ 10% reduzieren
kd88

Ich bin mir nicht sicher, ob ich verstehe, was Sie meinen (in den Beispielen werden Floats verwendet), aber auf jeden Fall berechnet man selten ein Sigmoid für Interger.
Théo T

2
Was kd88 bedeuten wollte, war, dass die numerischen Literale, die Sie in Ihrer Funktion (1) verwendet haben, als Ganzzahlen analysiert werden und zur Laufzeit in Floats umgewandelt werden müssen. Mit Gleitkomma-Literalen (1.0) erzielen Sie eine bessere Leistung.
krs013

Sie können die Funktion jederzeit vektorisieren, damit sie Arrays unterstützt.
Agcala

Sie möchten über eine teure Verpackung sprechen? % timeit -r 1 expit (0,458)% timeit -r 1 1 / (1 + np.exp (0,458))
Andrew Louw

42

So würden Sie das logistische Sigmoid numerisch stabil implementieren (wie hier beschrieben ):

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)

Oder vielleicht ist das genauer:

import numpy as np

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

Intern implementiert es die gleiche Bedingung wie oben, verwendet dann aber log1p.

Im Allgemeinen lautet das multinomiale logistische Sigmoid:

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)))

(Könnte logaddexp.reducejedoch genauer sein.)


Wenn ich mich auf das multinomiale logistische Sigmoid (Softmax) beziehe, wenn ich auch einen Temperaturparameter für das Reinforcement-Lernen haben wollte , reicht es dann aus, zu teilen max_qund rebased_qdurch tau? weil ich das versucht habe und keine Wahrscheinlichkeiten bekomme, die sich zu 1 summieren
Ciprian Tomoiagă

@CiprianTomoiaga Wenn Sie eine Temperatur haben möchten, teilen Sie einfach Ihre Beweise ( q) durch Ihre Temperatur. rebased_q kann alles sein: Es ändert nichts an der Antwort. es verbessert die numerische Stabilität.
Neil G

nat_to_expSind Sie sicher , dass dies Softmax entspricht (wie Sie in Ihrer anderen Antwort erwähnt haben)? Durch Kopieren und Einfügen werden Wahrscheinlichkeiten zurückgegeben, die nicht 1 ergeben
Ciprian Tomoiagă

@CiprianTomoiaga Die kurze Antwort lautet, dass ich die letzte Komponente der Eingabe und der Ausgabe weglasse, sodass Sie sie berechnen müssen, wenn Sie möchten, dass sie eins ist, abzüglich der Summe der übrigen. Die statistischere Erklärung ist, dass die kategoriale Verteilung n-1 natürliche Parameter oder n-1 Erwartungsparameter aufweist.
Neil G

macht irgendwie Sinn. Möchtest du meine Frage bearbeiten ?
Ciprian Tomoiagă

7

ein anderer Weg

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

1
Was ist der Unterschied zwischen dieser und der Abwicklungsfunktion? Ist math.e ** - x besser als math.exp (-x)?
Richard Knop

Es gibt keinen Unterschied in Bezug auf das Ausgabeergebnis. Wenn Sie den Unterschied in Bezug auf die Geschwindigkeit kennen möchten, können Sie timeit verwenden, um deren Ausführung zeitlich festzulegen. Aber das ist wirklich nicht wichtig.
Ghostdog74

9
powwird oft in Bezug auf expund implementiert log, daher ist die expdirekte Verwendung mit ziemlicher Sicherheit besser.
Japreiss

2
Dies leidet unter Überläufen, wenn xes sehr negativ ist.
Neil G

7

Ein anderer Weg durch Transformation der tanhFunktion:

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

@NeilG Mathematisch gesehen ist Sigmoid (x) == (1 + tanh (x / 2)) / 2. Dies ist also eine gültige Lösung, obwohl die numerisch stabilisierten Methoden überlegen sind.
Scottclowe

6

Ich glaube, viele könnten an freien Parametern interessiert sein, um die Form der Sigmoidfunktion zu verändern. Zweitens möchten Sie für viele Anwendungen eine gespiegelte Sigmoid-Funktion verwenden. Drittens möchten Sie vielleicht eine einfache Normalisierung durchführen, zum Beispiel liegen die Ausgabewerte zwischen 0 und 1.

Versuchen:

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

Und zum Zeichnen und Vergleichen:

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

Schließlich:

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

Sigmoid-Funktionsgraph


6

Verwenden Sie das numpy-Paket, damit Ihre Sigmoid-Funktion Vektoren analysieren kann.

In Übereinstimmung mit Deeplearning verwende ich den folgenden Code:

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

2

Gute Antwort von @unwind. Es kann jedoch keine extrem negativen Zahlen verarbeiten (OverflowError auslösen).

Meine Verbesserung:

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

Dies ist besser, aber Sie leiden immer noch unter numerischen Percussion-Problemen mit negativen Werten.
Neil G


2

Eine numerisch stabile Version der logistischen Sigmoidfunktion.

    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
Wenn x positiv ist, verwenden wir einfach 1 / (1 + np.exp (-x)), aber wenn x negativ ist, verwenden wir stattdessen die Funktion np.exp (x) / (1 + np.exp (x)) Verwenden von 1 / (1 + np.exp (-x)), da bei negativem x -x positiv ist, sodass np.exp (-x) aufgrund des großen Werts von -x explodieren kann.
Yash Khare

2

Ein Einzeiler ...

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

Vektorisierte Methode bei Verwendung von pandas DataFrame/Seriesoder numpy array:

Die besten Antworten sind optimierte Methoden für die Einzelpunktberechnung. Wenn Sie diese Methoden jedoch auf eine Pandas-Reihe oder ein Numpy-Array anwenden möchten, ist dies erforderlich apply ist im Grunde eine Schleife im Hintergrund und wird über jede Zeile iteriert und die Methode . Das ist ziemlich ineffizient.

Um unseren Code zu beschleunigen, können wir Vektorisierung und Numpy Broadcasting verwenden:

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

Oder mit einem pandas Series:

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

1

Sie können es berechnen als:

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

oder konzeptionell, tiefer und ohne Importe:

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

oder Sie können numpy für Matrizen verwenden:

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)

Der obige Code ist die logistische Sigmoid-Funktion in Python. Wenn ich das weiß x = 0.467, Die Sigmoidfunktion , F(x) = 0.385. Sie können versuchen, einen beliebigen Wert von x zu ersetzen, den Sie im obigen Code kennen, und Sie erhalten einen anderen Wert von F(x).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.