Làm thế nào để tính toán một hàm sigmoid logistic trong Python?


145

Đây là một hàm sigmoid logistic:

nhập mô tả hình ảnh ở đây

Tôi biết x. Làm cách nào để tính F (x) trong Python bây giờ?

Giả sử x = 0,458.

F (x) =?

Câu trả lời:


218

Điều này nên làm điều đó:

import math

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

Và bây giờ bạn có thể kiểm tra nó bằng cách gọi:

>>> sigmoid(0.458)
0.61253961344091512

Cập nhật : Lưu ý rằng phần trên chủ yếu nhằm mục đích dịch trực tiếp một thành một của biểu thức đã cho thành mã Python. Nó không được thử nghiệm hoặc được biết là một triển khai âm thanh số. Nếu bạn biết bạn cần một triển khai rất mạnh mẽ, tôi chắc chắn có những người khác mà mọi người thực sự đã đưa ra vấn đề này.


7
Chỉ vì tôi cần nó thường xuyên để thử những điều nhỏ nhặt:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
Điều này không hoạt động cho các giá trị cực âm của x. Tôi đã sử dụng triển khai không may này cho đến khi tôi nhận thấy nó đang tạo NaN.
Neil G

3
Nếu bạn thay thế math.expbằng np.expbạn sẽ không nhận được NaN, mặc dù bạn sẽ nhận được cảnh báo thời gian chạy.
Richard Rast

2
Sử dụng math.expvới mảng numpy có thể mang lại một số lỗi, như : TypeError: only length-1 arrays can be converted to Python scalars. Để tránh nó, bạn nên sử dụng numpy.exp.
ViniciusArruda

Sự mất ổn định số có thể được giảm thiểu chỉ bằng cách thêm vào x = max(-709,x)trước biểu thức?
Elias Hasle

201

Nó cũng có sẵn trong 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

vốn chỉ là một trình bao bọc tốn kém (vì nó cho phép bạn chia tỷ lệ và dịch hàm logistic) của một hàm scipy khác:

In [3]: from scipy.special import expit

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

Nếu bạn quan tâm về màn trình diễn tiếp tục đọc, nếu không chỉ cần sử dụng expit.

Một số điểm chuẩn:

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

Như mong đợi logistic.cdflà (nhiều) chậm hơn expit. expitvẫn chậm hơn sigmoidhàm python khi được gọi với một giá trị duy nhất vì đây là hàm phổ quát được viết bằng C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) và do đó có một cuộc gọi trên đầu. Chi phí này lớn hơn tốc độ tính toán expitđược đưa ra bởi bản chất được biên dịch của nó khi được gọi với một giá trị duy nhất. Nhưng nó trở nên không đáng kể khi nói đến mảng lớn:

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

(Bạn sẽ nhận thấy sự thay đổi nhỏ từ math.expsang np.exp(cái đầu tiên không hỗ trợ mảng, nhưng nhanh hơn nhiều nếu bạn chỉ có một giá trị để tính toán))

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

Nhưng khi bạn thực sự cần hiệu năng, một thực tế phổ biến là có một bảng được tính toán trước của hàm sigmoid giữ RAM, và trao đổi một số độ chính xác và bộ nhớ cho một số tốc độ (ví dụ: http://radimrehurek.com/2013/09 / word2vec-in-python-part-hai-tối ưu hóa / )

Ngoài ra, lưu ý rằng expitviệc triển khai ổn định về số lượng kể từ phiên bản 0.14.0: https://github.com/scipy/scipy/issues/3385


4
Bằng cách sử dụng float (1.) thay vì ints (1) trong chức năng sigmoid của bạn, bạn sẽ giảm thời gian chạy ~ 10%
kd88 11/03/2016

Tôi không chắc là tôi hiểu ý của bạn (phao được sử dụng trong các ví dụ), nhưng trong mọi trường hợp, người ta hiếm khi tính toán một sigmoid trên intergers.
Théo T

2
Điều kd88 muốn nói là các chữ số mà bạn đã sử dụng trong hàm (1) được phân tích cú pháp dưới dạng số nguyên và phải được truyền trong thời gian chạy thành float. Bạn sẽ có hiệu suất tốt hơn bằng cách sử dụng các ký tự dấu phẩy động (1.0).
krs013

Bạn luôn có thể vector hóa hàm để nó hỗ trợ các mảng.
agcala

Bạn muốn nói về một bọc đắt tiền? % timeit -r 1 expit (0.458)% timeit -r 1 1 / (1 + np.bao (0.458))
Andrew Louw

42

Dưới đây là cách bạn sẽ triển khai sigmoid logistic theo cách ổn định về số lượng (như được mô tả ở đây ):

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)

Hoặc có lẽ điều này chính xác hơn:

import numpy as np

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

Trong nội bộ, nó thực hiện các điều kiện tương tự như trên, nhưng sau đó sử dụng log1p.

Nói chung, sigmoid logistic đa thức là:

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

(Tuy nhiên, logaddexp.reducecó thể chính xác hơn.)


đề cập đến sigmoid logistic đa thức (softmax), nếu tôi cũng muốn có một tham số nhiệt độ cho việc học Củng cố , liệu nó có đủ để phân chia max_qrebased_qbởi tau? bởi vì tôi đã thử điều đó và tôi không nhận được xác suất nào bằng 1
Ciprian Tomoiagă

@CiprianTomoiaga Nếu bạn muốn có nhiệt độ, chỉ cần chia bằng chứng của bạn ( q) cho nhiệt độ của bạn. rebasing_q có thể là bất cứ điều gì: nó không thay đổi câu trả lời; nó cải thiện sự ổn định số.
Neil G

bạn có chắc nat_to_explà tương đương với softmax (như bạn đã đề cập trong câu trả lời khác của bạn) không? Sao chép-dán của nó trả về xác suất không tổng bằng 1
Ciprian Tomoiagă

@CiprianTomoiaga Câu trả lời ngắn gọn là tôi bỏ qua thành phần cuối cùng của đầu vào và đầu ra, vì vậy bạn sẽ phải tính toán nó nếu bạn muốn nó trừ đi tổng của phần còn lại. Giải thích thống kê hơn là phân phối phân loại có tham số tự nhiên n-1 hoặc tham số kỳ vọng n-1.
Neil G

có ý nghĩa, loại. Muốn quan tâm đến câu hỏi của tôi ?
Ciprian Tomoiagă

7

cách khác

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

1
Sự khác biệt giữa chức năng này và thư giãn là gì? Là math.e ** - x tốt hơn math.bao (-x)?
Richard Knop

Không có sự khác biệt về kết quả đầu ra. Nếu bạn muốn biết sự khác biệt về tốc độ, bạn có thể sử dụng thời gian để thực hiện chúng. Nhưng điều đó thực sự không quan trọng.
ghostdog74

9
powthường được thực hiện dưới dạng explog, vì vậy sử dụng exptrực tiếp gần như chắc chắn tốt hơn.
japreiss

2
Điều này bị tràn khi xrất tiêu cực.
Neil G

7

Một cách khác bằng cách chuyển đổi tanhchức năng:

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

@NeilG Về mặt toán học, sigmoid (x) == (1 + tanh (x / 2)) / 2. Vì vậy, đây là một giải pháp hợp lệ, mặc dù các phương pháp ổn định số là ưu việt.
scottclowe

6

Tôi cảm thấy nhiều người có thể quan tâm đến các tham số miễn phí để thay đổi hình dạng của hàm sigmoid. Thứ hai cho nhiều ứng dụng bạn muốn sử dụng chức năng sigmoid được nhân đôi. Thứ ba, bạn có thể muốn thực hiện chuẩn hóa đơn giản, ví dụ các giá trị đầu ra nằm trong khoảng từ 0 đến 1.

Thử:

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

Và để vẽ và so sánh:

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

Cuối cùng:

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

Đồ thị hàm Sigmoid


6

Sử dụng gói numpy để cho phép hàm sigmoid của bạn phân tích các vectơ.

Để phù hợp với Deeplearning, tôi sử dụng mã sau:

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

2

Câu trả lời hay từ @unwind. Tuy nhiên, nó không thể xử lý số cực âm (ném OverflowError).

Cải thiện của tôi:

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

Điều này tốt hơn, nhưng bạn vẫn đang chịu đựng các vấn đề về bộ gõ với các giá trị âm.
Neil G


2

Một phiên bản ổn định về số của hàm sigmoid logistic.

    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
x sử dụng 1 / (1 + np.bao (-x)) vì khi x âm -x sẽ dương nên np.bao (-x) có thể phát nổ do giá trị lớn của -x.
Yash Khare

2

Một lót ...

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

Phương pháp véc tơ khi sử dụng pandas DataFrame/Serieshoặc numpy array:

Các câu trả lời hàng đầu là các phương thức được tối ưu hóa để tính điểm đơn lẻ, nhưng khi bạn muốn áp dụng các phương thức này cho một chuỗi gấu trúc hoặc mảng numpy, nó đòi hỏi apply , về cơ bản là vòng lặp trong nền và sẽ lặp lại trên mỗi hàng và áp dụng phương thức. Điều này khá không hiệu quả.

Để tăng tốc mã của chúng tôi, chúng tôi có thể sử dụng vector hóa và phát sóng 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

Hoặc với một pandas Series:

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

1

bạn có thể tính toán nó như sau:

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

hoặc khái niệm, sâu hơn và không có bất kỳ nhập khẩu:

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

hoặc bạn có thể sử dụng numpy cho ma trận:

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)

Đoạn mã trên là hàm sigmoid logistic trong python. Nếu tôi biết điều đó x = 0.467, Hàm sigmoid , F(x) = 0.385. Bạn có thể thử thay thế bất kỳ giá trị nào của x bạn biết trong đoạn mã trên và bạn sẽ nhận được một giá trị khác F(x).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.