Bagaimana cara menghitung fungsi sigmoid logistik dengan Python?


146

Ini adalah fungsi sigmoid logistik:

masukkan deskripsi gambar di sini

Saya tahu x. Bagaimana saya bisa menghitung F (x) dengan Python sekarang?

Katakanlah x = 0,458.

F (x) =?

Jawaban:


219

Ini harus dilakukan:

import math

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

Dan sekarang Anda dapat mengujinya dengan menelepon:

>>> sigmoid(0.458)
0.61253961344091512

Pembaruan : Perhatikan bahwa di atas terutama dimaksudkan sebagai terjemahan langsung satu-ke-satu dari ekspresi yang diberikan ke dalam kode Python. Ini tidak diuji atau dikenal sebagai implementasi yang baik secara numerik. Jika Anda tahu Anda membutuhkan implementasi yang sangat kuat, saya yakin ada orang lain di mana orang benar-benar memikirkan masalah ini.


7
Hanya karena saya sangat sering membutuhkannya untuk mencoba hal-hal kecil:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
Ini tidak berfungsi untuk nilai negatif ekstrim x. Saya menggunakan implementasi yang tidak menguntungkan ini sampai saya perhatikan itu membuat NaNs.
Neil G

3
Jika Anda mengganti math.expdengan np.expAnda tidak akan mendapatkan NaN, meskipun Anda akan mendapatkan peringatan runtime.
Richard Rast

2
Menggunakan math.expdengan berbagai numpy dapat menghasilkan beberapa kesalahan, seperti: TypeError: only length-1 arrays can be converted to Python scalars. Untuk menghindarinya sebaiknya Anda gunakan numpy.exp.
ViniciusArruda

Bisakah ketidakstabilan numerik dikurangi hanya dengan menambahkan x = max(-709,x)sebelum ekspresi?
Elias Hasle

201

Ini juga tersedia di 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

yang hanya merupakan pembungkus yang mahal (karena ini memungkinkan Anda untuk mengukur dan menerjemahkan fungsi logistik) dari fungsi scipy lainnya:

In [3]: from scipy.special import expit

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

Jika Anda khawatir tentang pertunjukan, teruskan membaca, jika tidak gunakan saja expit.

Beberapa pembandingan:

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

Seperti yang diharapkan logistic.cdf(lebih) lebih lambat dari expit. expitmasih lebih lambat daripada sigmoidfungsi python ketika dipanggil dengan nilai tunggal karena itu adalah fungsi universal yang ditulis dalam C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) dan dengan demikian memiliki panggilan overhead. Overhead ini lebih besar dari percepatan perhitungan yang expitdiberikan oleh sifat kompilasi ketika dipanggil dengan nilai tunggal. Tetapi menjadi diabaikan ketika datang ke array besar:

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

(Anda akan melihat perubahan kecil dari math.expmenjadi np.exp(yang pertama tidak mendukung array, tetapi jauh lebih cepat jika Anda hanya memiliki satu nilai untuk dihitung))

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

Tetapi ketika Anda benar-benar membutuhkan kinerja, praktik umum adalah memiliki tabel yang sudah dikomputasi dari fungsi sigmoid yang menampung RAM, dan menukar beberapa presisi dan memori untuk beberapa kecepatan (misalnya: http://radimrehurek.com/2013/09 / word2vec-in-python-bagian-dua-mengoptimalkan / )

Juga, perhatikan bahwa expitimplementasinya stabil secara numerik sejak versi 0.14.0: https://github.com/scipy/scipy/issues/3385


4
Dengan menggunakan floats (1.) alih-alih ints (1) dalam fungsi sigmoid Anda, Anda akan mengurangi waktu berlari hingga ~ 10%
kd88

Saya tidak yakin saya mengerti apa yang Anda maksud (mengapung digunakan dalam contoh), tetapi dalam hal apapun jarang menghitung sigmoid pada intergers.
Théo T

2
Apa yang ingin dikatakan kd88 adalah bahwa literal numerik yang Anda gunakan dalam fungsi Anda (1) diuraikan sebagai bilangan bulat, dan harus dilemparkan pada saat runtime untuk mengapung. Anda akan mendapatkan kinerja yang lebih baik menggunakan literal floating point (1.0).
krs013

Anda selalu dapat membuat vektorisasi fungsi sehingga akan mendukung array.
agcala

Anda ingin berbicara tentang bungkus mahal? % timeit -r 1 expit (0.458)% timeit -r 1 1 / (1 + np.exp (0.458))
Andrew Louw

42

Inilah cara Anda menerapkan sigmoid logistik dengan cara yang stabil secara numerik (seperti dijelaskan di sini ):

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)

Atau mungkin ini lebih akurat:

import numpy as np

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

Secara internal, ini mengimplementasikan kondisi yang sama seperti di atas, tetapi kemudian menggunakan log1p.

Secara umum, sigmoid logistik multinomial adalah:

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

(Namun, logaddexp.reducebisa lebih akurat.)


mengacu pada sigmoid logistik multinomial (Softmax), jika saya juga ingin parameter suhu untuk belajar Penguatan , apakah itu cukup untuk membagi max_qdan rebased_qoleh tau? karena saya mencobanya dan saya tidak mendapatkan probabilitas yang berjumlah 1
Ciprian Tomoiagă

@CiprianTomoiaga Jika Anda ingin memiliki suhu, cukup bagi bukti Anda ( q) dengan suhu Anda. rebased_q bisa berupa apa saja: tidak mengubah jawaban; itu meningkatkan stabilitas numerik.
Neil G

apakah Anda yakin nat_to_expsetara dengan softmax (seperti yang Anda sebutkan dalam jawaban Anda yang lain)? Salin-tempel dari itu mengembalikan probabilitas yang tidak berjumlah 1
Ciprian Tomoiagă

@ CiprianTomoiaga Jawaban singkatnya adalah saya menghilangkan komponen akhir dari input dan output, jadi Anda harus menghitungnya jika Anda menginginkannya sebagai satu dikurangi jumlah sisanya. Penjelasan yang lebih statistik adalah bahwa distribusi kategorikal memiliki parameter alami n-1 atau parameter ekspektasi n-1.
Neil G

masuk akal, semacam. Ingin menjelaskan pertanyaan saya ?
Ciprian Tomoiagă

7

cara lain

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

1
Apa perbedaan antara ini dan fungsi bersantai? Apakah math.e ** - x lebih baik dari math.exp (-x)?
Richard Knop

Tidak ada perbedaan dalam hal hasil keluaran. Jika Anda ingin mengetahui perbedaan dalam hal kecepatan, Anda dapat menggunakan timeit ke waktu eksekusi mereka. Tapi itu benar-benar tidak penting.
ghostdog74

9
powsering diimplementasikan dalam hal expdan log, jadi menggunakan expsecara langsung hampir pasti lebih baik.
japreiss

2
Ini menderita dari luapan ketika xsangat negatif.
Neil G

7

Cara lain dengan mengubah tanhfungsi:

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

@NeilG Secara matematis, sigmoid (x) == (1 + tanh (x / 2)) / 2. Jadi ini adalah solusi yang valid, meskipun metode yang distabilkan secara numerik lebih unggul.
scottclowe

6

Saya merasa banyak yang mungkin tertarik pada parameter gratis untuk mengubah bentuk fungsi sigmoid. Kedua untuk banyak aplikasi Anda ingin menggunakan fungsi sigmoid cermin. Ketiga, Anda mungkin ingin melakukan normalisasi sederhana misalnya nilai output antara 0 dan 1.

Mencoba:

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

Dan untuk menggambar dan membandingkan:

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

Akhirnya:

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

Grafik fungsi Sigmoid


6

Gunakan paket numpy untuk memungkinkan fungsi sigmoid Anda mengurai vektor.

Sesuai dengan Deeplearning, saya menggunakan kode berikut:

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

2

Jawaban bagus dari @unwind. Namun itu tidak dapat menangani angka negatif ekstrim (melempar OverflowError).

Peningkatan saya:

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

Ini lebih baik, tetapi Anda masih menderita masalah perkusi numerik dengan nilai negatif.
Neil G


2

Versi stabil fungsi logistic sigmoid.

    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
jika x positif kita hanya menggunakan 1 / (1 + np.exp (-x)) tetapi ketika x negatif kita menggunakan fungsi np.exp (x) / (1 + np.exp (x)) alih-alih menggunakan 1 / (1 + np.exp (-x)) karena ketika x negatif -x akan positif sehingga np.exp (-x) dapat meledak karena nilai -x yang besar.
Yash Khare

2

Satu liner ...

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

Metode vektor saat menggunakan pandas DataFrame/Seriesatau numpy array:

Jawaban teratas adalah metode yang dioptimalkan untuk perhitungan titik tunggal, tetapi ketika Anda ingin menerapkan metode ini ke seri panda atau array numpy, itu membutuhkan apply, yang pada dasarnya adalah untuk loop di latar belakang dan akan beralih ke setiap baris dan menerapkan metode. Ini cukup tidak efisien.

Untuk mempercepat kode kita, kita dapat menggunakan vektorisasi dan siaran numpy:

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

Atau dengan pandas Series:

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

1

Anda dapat menghitungnya sebagai:

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

atau konseptual, lebih dalam dan tanpa impor:

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

atau Anda dapat menggunakan numpy untuk matriks:

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)

Kode di atas adalah fungsi sigmoid logistik di python. Jika saya tahu itu x = 0.467, Fungsi sigmoid F(x) = 0.385,. Anda dapat mencoba mengganti nilai x yang Anda tahu dalam kode di atas, dan Anda akan mendapatkan nilai yang berbeda F(x).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.