วิธีการคำนวณฟังก์ชั่น sigmoid โลจิสติกในงูหลาม?


146

นี่คือฟังก์ชัน sigmoid โลจิสติก:

ป้อนคำอธิบายรูปภาพที่นี่

ฉันรู้ว่า x ฉันจะคำนวณ F (x) ใน Python ได้อย่างไร

สมมุติว่า x = 0.458

F (x) =?

คำตอบ:


219

สิ่งนี้ควรทำ:

import math

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

และตอนนี้คุณสามารถทดสอบได้โดยโทร:

>>> sigmoid(0.458)
0.61253961344091512

อัปเดต : โปรดทราบว่าข้อความข้างต้นมีจุดประสงค์หลักในการแปลแบบตัวต่อตัวของนิพจน์ที่กำหนดให้เป็นรหัส Python มันเป็นเรื่องที่ไม่ผ่านการทดสอบหรือที่รู้จักกันเป็นเสียงการดำเนินตัวเลข หากคุณรู้ว่าคุณต้องมีการนำไปใช้อย่างมีประสิทธิภาพฉันมั่นใจว่ามีบางคนที่คนอื่นคิดว่าปัญหานี้เกิดขึ้นจริง


7
เพียงเพราะฉันต้องการมันบ่อยครั้งเพื่อลองสิ่งเล็ก ๆ น้อย ๆ :sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
สิ่งนี้ใช้ไม่ได้กับค่าลบสุดขีดของ x ฉันใช้การติดตั้งที่โชคร้ายนี้จนกระทั่งฉันสังเกตเห็นว่ามันกำลังสร้าง NaNs
Neil G

3
หากคุณแทนที่math.expด้วยnp.expคุณจะไม่ได้รับแก่นแก้วแม้ว่าคุณจะได้รับการเตือนรันไทม์
Richard Rast

2
ใช้math.expกับอาร์เรย์ numpy TypeError: only length-1 arrays can be converted to Python scalarsสามารถให้ผลผลิตข้อผิดพลาดบางอย่างเช่น: numpy.expจะหลีกเลี่ยงได้คุณควรใช้
ViniciusArruda

ความไม่แน่นอนเชิงตัวเลขสามารถลดลงได้ง่ายๆโดยการเพิ่มx = max(-709,x)ก่อนการแสดงออก?
อีเลียส Hasle

201

มันยังมีอยู่ใน 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

ซึ่งเป็นเพียง wrapper ราคาแพง (เพราะช่วยให้คุณสามารถปรับขนาดและแปลฟังก์ชันโลจิสติก) ของฟังก์ชัน scipy อื่น:

In [3]: from scipy.special import expit

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

expitหากคุณมีความกังวลเกี่ยวกับการแสดงอ่านต่อไปมิฉะนั้นเพียงแค่ใช้

การเปรียบเทียบบางอย่าง:

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

ตามที่คาดไว้logistic.cdfช้ากว่าexpitมาก expitยังช้ากว่าsigmoidฟังก์ชั่นpython เมื่อถูกเรียกด้วยค่าเดียวเพราะมันเป็นฟังก์ชั่นสากลที่เขียนด้วยภาษา C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) ดังนั้นจึงมีค่าใช้จ่ายในการโทร โอเวอร์เฮดนี้มีค่ามากกว่าการเร่งความเร็วการคำนวณที่expitกำหนดโดยธรรมชาติของการคอมไพล์เมื่อเรียกด้วยค่าเดียว แต่มันจะไม่สำคัญเมื่อพูดถึงอาร์เรย์ขนาดใหญ่:

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

(คุณจะสังเกตเห็นการเปลี่ยนแปลงเล็กน้อยจากmath.expเป็นnp.exp(อันแรกไม่รองรับอาร์เรย์ แต่เร็วกว่ามากถ้าคุณมีเพียงค่าเดียวในการคำนวณ))

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

แต่เมื่อคุณต้องการประสิทธิภาพจริงๆการปฏิบัติทั่วไปคือการมีตาราง precomputed ของฟังก์ชัน sigmoid ที่เก็บไว้ใน RAM และแลกเปลี่ยนความแม่นยำและหน่วยความจำสำหรับความเร็วบางอย่าง (ตัวอย่าง: http://radimrehurek.com/2013/09 / word2vec-in-python-part-two-optimizing / )

นอกจากนี้โปรดทราบว่าexpitการใช้งานมีความเสถียรเป็นตัวเลขตั้งแต่รุ่น 0.14.0: https://github.com/scipy/scipy/issues/3385


4
โดยการใช้ floats (1. ) แทน ints (1) ในฟังก์ชั่น sigmoid คุณจะลดเวลาในการทำงานลง ~ 10%
kd88

ฉันไม่แน่ใจว่าฉันเข้าใจสิ่งที่คุณหมายถึง (ลอยถูกนำมาใช้ในตัวอย่าง) แต่ในกรณีใด ๆ ที่ไม่ค่อยคำนวณ sigmoid ใน intergers
Théo T

2
สิ่งที่ kd88 หมายถึงพูดก็คือตัวอักษรตัวเลขที่คุณใช้ในการทำงาน (1) ถูกแยกวิเคราะห์เป็นจำนวนเต็มและจะต้องทำการร่ายที่รันไทม์เพื่อทำการลอย คุณจะได้ประสิทธิภาพที่ดีขึ้นโดยใช้ตัวอักษรจุดลอยตัว (1.0)
krs013

คุณสามารถ vectorize ฟังก์ชั่นเพื่อสนับสนุนอาร์เรย์
agcala

คุณต้องการที่จะพูดคุยเกี่ยวกับเสื้อคลุมราคาแพง? % timeit -r 1 expit (0.458)% timeit -r 1 1 / (1 + np.exp (0.458))
Andrew Louw

42

นี่คือวิธีที่คุณจะใช้ sigmoid โลจิสติกในวิธีที่มั่นคงตัวเลข (ดังอธิบายไว้ที่นี่ ):

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)

หรือบางทีนี่อาจแม่นยำกว่า:

import numpy as np

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

ภายในก็ใช้เงื่อนไขเดียวกับข้างต้น log1pแต่การใช้งานแล้ว

โดยทั่วไป sigmoid โลจิสติก multinomial คือ:

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

(อย่างไรก็ตามlogaddexp.reduceอาจแม่นยำยิ่งขึ้น)


หมายถึง sigmoid โลจิสติกพหุนาม (softmax) ถ้าผมยังอยากพารามิเตอร์อุณหภูมิสำหรับการเรียนรู้เสริมสร้างมันไม่พอเพียงที่จะแบ่งmax_qและrebased_qโดยtau? เพราะฉันลองแล้วไม่ได้ความน่าจะเป็นที่รวม 1
Ciprian Tomoiagă

@CiprianTomoiaga หากคุณต้องการมีอุณหภูมิเพียงแค่แบ่งหลักฐานของคุณ ( q) ตามอุณหภูมิของคุณ rebased_q สามารถเป็นอะไรก็ได้: มันไม่เปลี่ยนคำตอบ มันช่วยปรับปรุงเสถียรภาพเชิงตัวเลข
Neil G

คุณแน่ใจหรือว่าnat_to_expเทียบเท่ากับ softmax (ตามที่คุณกล่าวถึงในคำตอบอื่น ๆ )? Copy-paste ของมันจะส่งคืนความน่าจะเป็นซึ่งไม่ได้รวมเป็น 1
Ciprian Tomoiagă

@CiprianTomoiaga คำตอบสั้น ๆ คือฉันไม่ใส่องค์ประกอบสุดท้ายของอินพุตและเอาต์พุตดังนั้นคุณจะต้องคำนวณถ้าคุณต้องการให้มันเป็นหนึ่งลบผลรวมของส่วนที่เหลือ คำอธิบายทางสถิติที่มากขึ้นคือการแจกแจงหมวดหมู่มีพารามิเตอร์ธรรมชาติ n-1 หรือพารามิเตอร์การคาดการณ์ n-1
Neil G

ทำให้รู้สึกชนิดของ สนใจคำถามของฉันไหม?
Ciprian Tomoiagă

7

อีกวิธีหนึ่ง

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

1
อะไรคือความแตกต่างระหว่างฟังก์ชั่นนี้กับการคลายตัว? math.e ** - x ดีกว่า math.exp (-x) หรือไม่
Richard Knop

ไม่มีความแตกต่างในแง่ของผลลัพธ์ผลลัพธ์ หากคุณต้องการทราบความแตกต่างในแง่ของความเร็วคุณสามารถใช้ timeit เพื่อดำเนินการตามเวลา แต่นั่นไม่สำคัญจริงๆ
ghostdog74

9
powมักจะถูกนำมาใช้ในแง่ของexpและlogดังนั้นการใช้expโดยตรงจะดีกว่าแน่นอน
japreiss

2
สิ่งนี้ทนทุกข์ทรมานจากการล้นเมื่อxเป็นลบมาก
Neil G

7

อีกวิธีหนึ่งโดยการเปลี่ยนtanhฟังก์ชั่น:

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

@NeilG ทางคณิตศาสตร์ sigmoid (x) == (1 + tanh (x / 2)) / 2 ดังนั้นนี่จึงเป็นคำตอบที่ถูกต้องแม้ว่าวิธีที่มีความเสถียรเชิงตัวเลขนั้นดีกว่า
scottclowe

6

ฉันรู้สึกว่าหลายคนอาจสนใจพารามิเตอร์อิสระเพื่อเปลี่ยนรูปร่างของฟังก์ชัน sigmoid ข้อสองสำหรับหลาย ๆ แอพพลิเคชั่นที่คุณต้องการใช้ฟังก์ชั่น sigmoid ประการที่สามคุณอาจต้องการทำการปรับสภาพอย่างง่ายตัวอย่างเช่นค่าเอาต์พุตอยู่ระหว่าง 0 ถึง 1

ลอง:

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

และเพื่อวาดและเปรียบเทียบ:

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

สุดท้าย:

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

กราฟฟังก์ชัน Sigmoid


6

ใช้แพ็คเกจ numpy เพื่อให้ฟังก์ชัน sigmoid แยกวิเคราะห์เวกเตอร์

ตาม Deeplearning ฉันใช้รหัสต่อไปนี้:

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

2

คำตอบที่ดีจาก @unwind อย่างไรก็ตามไม่สามารถจัดการกับจำนวนลบมาก (การขว้าง OverflowError)

การปรับปรุงของฉัน:

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

นี้จะดีกว่า แต่คุณยังคงทุกข์ทรมานจากปัญหากระทบเชิงตัวเลขที่มีค่าลบ
Neil G


2

เวอร์ชันที่มีเสถียรภาพเชิงตัวเลขของฟังก์ชัน 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
ถ้า x เป็นบวกเราแค่ใช้ 1 / (1 + np.exp (-x)) แต่เมื่อ x เป็นลบเราใช้ฟังก์ชัน np.exp (x) / (1 + np.exp (x)) แทน ใช้ 1 / (1 + np.exp (-x)) เพราะเมื่อ x เป็นลบ -x จะเป็นค่าบวกดังนั้น np.exp (-x) สามารถระเบิดได้เนื่องจากมีค่ามาก -x
Yash Khare


1

วิธีการ Vectorized เมื่อใช้pandas DataFrame/Seriesหรือnumpy array:

คำตอบยอดนิยมคือวิธีการที่ได้รับการปรับให้เหมาะสมสำหรับการคำนวณแบบจุดเดียว แต่เมื่อคุณต้องการใช้วิธีการเหล่านี้กับชุดแพนด้าหรืออาร์เรย์แบบ numpy มันต้องการapplyซึ่งโดยทั่วไปแล้วจะวนซ้ำในพื้นหลังและจะวนซ้ำทุกแถว มันค่อนข้างไร้ประสิทธิภาพ

เพื่อเพิ่มความเร็วโค้ดของเราเราสามารถใช้ประโยชน์จาก vectorization และ 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

หรือด้วยpandas Series:

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

1

คุณสามารถคำนวณมันเป็น:

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

หรือแนวคิดลึกและไม่มีการนำเข้าใด ๆ :

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

หรือคุณสามารถใช้ numpy สำหรับเมทริกซ์:

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)

รหัสข้างต้นเป็นฟังก์ชัน sigmoid โลจิสติกในหลาม ถ้าผมรู้ว่าx = 0.467ฟังก์ชั่น F(x) = 0.385sigmoid, คุณสามารถลองแทนค่าใด ๆ ของ x F(x)คุณรู้ว่าในรหัสข้างต้นและคุณจะได้รับความคุ้มค่าที่แตกต่างกันของ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.