Как рассчитать логистическую сигмовидную функцию в Python?

157

Это логистическая сигмовидная функция:

введите описание изображения здесь

Я знаю х. Как теперь я могу вычислить F (x) в Python?

Скажем, x = 0,458.

F (x) =?

Ричард Кноп
источник

Ответы:

232

Это должно сделать это:

import math

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

И теперь вы можете проверить это, позвонив:

>>> sigmoid(0.458)
0.61253961344091512

Обновление : обратите внимание, что приведенное выше было в основном предназначено как прямой однозначный перевод данного выражения в код Python. Это не тестировалось и не известно, что это численно правильная реализация. Если вы знаете, что вам нужна очень надежная реализация, я уверен, что есть другие, где люди действительно задумывались над этой проблемой.

размотать
источник
8
Просто потому, что мне это нужно так часто, чтобы пробовать мелочи:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Мартин Тома
2
Это не работает для экстремально отрицательных значений x. Я использовал эту неудачную реализацию, пока не заметил, что она создает NaN.
Neil G
3
Если вы замените math.expна, np.expвы не получите NaN, хотя вы получите предупреждения во время выполнения.
Ричард Раст,
2
Использование math.expс Numpy массива может дать некоторые ошибки, например: TypeError: only length-1 arrays can be converted to Python scalars. Чтобы избежать этого, вы должны использовать numpy.exp.
ViniciusArruda
Можно ли уменьшить числовую нестабильность, просто добавив x = max(-709,x)перед выражением?
Элиас
206

Он также доступен в 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

это всего лишь дорогостоящая оболочка (потому что она позволяет масштабировать и транслировать логистическую функцию) другой 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

Но когда вам действительно нужна производительность, обычной практикой является наличие предварительно вычисленной таблицы сигмоидной функции, которая хранится в ОЗУ, и обмен некоторой точности и памяти на некоторую скорость (например: http://radimrehurek.com/2013/09 / word2vec-in-python-part-two-optimizing / )

Также обратите внимание, что expitреализация численно стабильна с версии 0.14.0: https://github.com/scipy/scipy/issues/3385

Тео Т
источник
4
Используя float (1.) вместо ints (1) в вашей сигмоидной функции, вы сократите время выполнения на ~ 10%
kd88
Я не уверен, что понимаю, что вы имеете в виду (в примерах используются числа с плавающей запятой), но в любом случае редко вычисляют сигмоид на промежуточных числах.
Théo T,
2
Kd88 имел в виду, что числовые литералы, которые вы использовали в своей функции (1), анализируются как целые числа и должны быть преобразованы во время выполнения в числа с плавающей запятой. Вы получите лучшую производительность, используя литералы с плавающей запятой (1.0).
krs013 03
Вы всегда можете векторизовать функцию, чтобы она поддерживала массивы.
agcala
вы хотите рассказать о дорогой обертке? % timeit -r 1 expit (0,458)% timeit -r 1 1 / (1 + np.exp (0,458))
Эндрю Лоу
43

Вот как бы вы реализовали логистический сигмоид численно стабильным способом (как описано здесь ):

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.

В общем, полиномиальная логистическая сигмоида:

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можно было бы точнее.)

Нил Джи
источник
Что касается полиномиальной логистической сигмоиды (softmax), если мне также нужен температурный параметр для обучения с подкреплением , достаточно ли его разделить max_qи rebased_qна tau? потому что я пробовал это, и я не получаю вероятностей, которые в сумме составляют 1
Ciprian Tomoiagă
@CiprianTomoiaga Если вы хотите измерить температуру, просто разделите показания ( q) на вашу температуру. rebased_q может быть любым: он не меняет ответ; это улучшает численную стабильность.
Neil G
вы уверены, что nat_to_expэто эквивалент softmax (как вы упомянули в другом ответе)? Копирование-паста возвращает вероятности, которые не суммируются с 1
Ciprian Tomoiagă
1
Вы не имели в виду np.exp(-np.logaddexp(0, -x))? (примечание npвместо math)
Юваль Ацмон
1
Понял. Меня беспокоит то, mathчто не работает с массивами
Юваль Ацмон
8

по-другому

>>> def sigmoid(x):
...     return 1 /(1+(math.e**-x))
...
>>> sigmoid(0.458)
призрачная собака74
источник
1
В чем разница между этой функцией и функцией размотки? Math.e ** - x лучше, чем math.exp (-x)?
Ричард Кноп,
По выходному результату разницы нет. Если вы хотите узнать разницу в скорости, вы можете использовать timeit для измерения времени их выполнения. Но это действительно не важно.
ghostdog74
10
powчасто реализуется в терминах expи log, поэтому использование expнапрямую почти наверняка лучше.
japreiss
2
Это страдает от переполнения, когда xочень отрицательно.
Neil G
8

Другой способ, преобразовав tanh функцию:

sigmoid = lambda x: .5 * (math.tanh(.5 * x) + 1)
Dontloo
источник
@NeilG Математически сигмоид (x) == (1 + tanh (x / 2)) / 2. Так что это правильное решение, хотя численно стабилизированные методы лучше.
scottclowe
7

Я чувствую, что многие могут быть заинтересованы в свободных параметрах для изменения формы сигмовидной функции. Во-вторых, для многих приложений вы хотите использовать зеркальную сигмоидальную функцию. В-третьих, вы можете выполнить простую нормализацию, например, выходные значения находятся в диапазоне от 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)

График сигмовидной функции

Филипп Шварц
источник
7

Используйте пакет numpy, чтобы ваша сигмоидальная функция могла анализировать векторы.

В соответствии с Deeplearning я использую следующий код:

import numpy as np
def sigmoid(x):
    s = 1/(1+np.exp(-x))
    return s
Диатче
источник
3

Хороший ответ от @unwind. Однако он не может обрабатывать крайне отрицательное число (бросая OverflowError).

Мое улучшение:

def sigmoid(x):
    try:
        res = 1 / (1 + math.exp(-x))
    except OverflowError:
        res = 0.0
    return res
czxttkl
источник
Это лучше, но у вас все еще есть проблемы с числовой перкуссией с отрицательными значениями.
Neil G
3

Численно стабильная версия логистической сигмовидной функции.

    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.
Яш Кхаре
3

Один лайнер ...

In[1]: import numpy as np

In[2]: sigmoid=lambda x: 1 / (1 + np.exp(-x))

In[3]: sigmoid(3)
Out[3]: 0.9525741268224334
cristiandatum
источник
2

Векторизованный метод при использовании pandas DataFrame/Seriesили numpy array:

Лучшие ответы - это оптимизированные методы для вычисления одной точки, но если вы хотите применить эти методы к серии pandas или массиву numpy, это требует apply , что в основном для цикла в фоновом режиме и будет перебирать каждую строку и применять метод. Это довольно неэффективно.

Чтобы ускорить наш код, мы можем использовать векторизацию и широковещательную рассылку:

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))
Эрфан
источник
2

вы можете рассчитать это как:

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))
Али
источник
1
import numpy as np

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

result = sigmoid(0.467)
print(result)

Приведенный выше код является логистической сигмовидной функцией в Python. Если я это знаю x = 0.467, сигмовидная функция F(x) = 0.385. Вы можете попробовать заменить любое известное вам значение x в приведенном выше коде, и вы получите другое значение F(x).

Д-р Азаил Браун
источник
0

Ниже приведена функция Python, которая делает то же самое.

def sigmoid(x) :
    return 1.0/(1+np.exp(-x))
Хардик Вагадиа
источник