Como calcular uma função sigmóide logística em Python?


145

Esta é uma função sigmóide logística:

insira a descrição da imagem aqui

Eu sei x. Como posso calcular F (x) em Python agora?

Digamos que x = 0,458.

F (x) =?

Respostas:


218

Isso deve servir:

import math

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

E agora você pode testá-lo chamando:

>>> sigmoid(0.458)
0.61253961344091512

Atualização : observe que o acima foi destinado principalmente como uma tradução direta direta da expressão fornecida no código Python. É não testados ou conhecido por ser uma boa execução numericamente. Se você sabe que precisa de uma implementação muito robusta, tenho certeza de que há outras em que as pessoas realmente pensaram nesse problema.


7
Só porque eu preciso muitas vezes para experimentar pequenas coisas:sigmoid = lambda x: 1 / (1 + math.exp(-x))
Martin Thoma

2
Isso não funciona para valores negativos extremos de x. Eu estava usando essa implementação infeliz até perceber que estava criando NaNs.
Neil G

3
Se você substituir math.exppor np.exp, não receberá NaNs, embora receba avisos de tempo de execução.
Richard Rast

2
Usando math.expcom matriz numpy pode produzir alguns erros, como: TypeError: only length-1 arrays can be converted to Python scalars. Para evitá-lo, você deve usar numpy.exp.
ViniciusArruda

A instabilidade numérica pode ser atenuada simplesmente adicionando x = max(-709,x)antes da expressão?
Elias Hasle

201

Também está disponível em 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

que é apenas um invólucro caro (porque permite escalar e traduzir a função logística) de outra função ciposa:

In [3]: from scipy.special import expit

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

Se você está preocupado com o desempenho, continue lendo, caso contrário, basta usar expit.

Alguns testes comparativos:

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

Como esperado, logistic.cdfé (muito) mais lento que expit. expitainda é mais lento que a sigmoidfunção python quando chamada com um único valor, porque é uma função universal escrita em C ( http://docs.scipy.org/doc/numpy/reference/ufuncs.html ) e, portanto, possui uma sobrecarga de chamada. Essa sobrecarga é maior que a aceleração computacional expitfornecida pela natureza compilada quando chamada com um único valor. Mas isso se torna insignificante quando se trata de grandes matrizes:

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

(Você notará a pequena alteração de math.exppara np.exp(a primeira não suporta matrizes, mas é muito mais rápida se você tiver apenas um valor para calcular))

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

Mas quando você realmente precisa de desempenho, uma prática comum é ter uma tabela pré-computada da função sigmoide que contém a RAM e trocar alguma precisão e memória por alguma velocidade (por exemplo: http://radimrehurek.com/2013/09 / word2vec-in-python-parte-dois-otimização / )

Além disso, observe que a expitimplementação é numericamente estável desde a versão 0.14.0: https://github.com/scipy/scipy/issues/3385


4
Usando carros alegóricos (1.) em vez de ints (1) na sua função sigmóide você iria reduzir o tempo de execução, ~ 10%
kd88

Não sei se entendi o que você quer dizer (floats são usados ​​nos exemplos), mas, de qualquer forma, raramente se calcula um sigmóide nos intergeradores.
11552 Théo T

2
O que o kd88 quis dizer foi que os literais numéricos que você usou em sua função (1) são analisados ​​como números inteiros e precisam ser convertidos em tempo de execução para flutuar. Você obteria melhor desempenho usando literais de ponto flutuante (1.0).
precisa saber é o seguinte

Você sempre pode vetorizar a função para suportar matrizes.
Agcala # 14/18

você quer falar sobre uma embalagem cara? % timeit -r 1 expit (0,458)% timeit -r 1 1 / (1 + np.exp (0,458))
Andrew Louw

42

Aqui está como você implementaria o sigmóide logístico de uma maneira numericamente estável (como descrito aqui ):

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)

Ou talvez isso seja mais preciso:

import numpy as np

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

Internamente, implementa a mesma condição acima, mas depois usa log1p.

Em geral, o sigmóide logístico 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)))

(No entanto, logaddexp.reducepode ser mais preciso.)


referindo-se ao sigmóide logística multinomial (softmax), se eu também queria um parâmetro de temperatura para a aprendizagem Reforço , não é suficiente para dividir max_qe rebased_qpor tau? porque eu tentei isso e não tenho probabilidades que somam 1
Ciprian Tomoiagă

@CiprianTomoiaga Se você quiser ter uma temperatura, basta dividir sua evidência ( q) pela sua temperatura. rebased_q pode ser qualquer coisa: não altera a resposta; melhora a estabilidade numérica.
Neil G

você nat_to_exptem certeza que é equivalente ao softmax (como você mencionou na sua outra resposta)? Copiar e colar dele retorna probabilidades que não somam 1
Ciprian Tomoiagă

@CiprianTomoiaga A resposta curta é que eu omito o componente final da entrada e da saída, então você terá que calculá-lo se quiser como um menos a soma do resto. A explicação mais estatística é que a distribuição categórica tem parâmetros naturais n-1 ou parâmetros de expectativa n-1.
Neil G

faz sentido, mais ou menos. Gostaria de elaborar a minha pergunta ?
Ciprian Tomoiagă

7

outra maneira

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

1
Qual é a diferença entre this e unbind's function? O math.e ** - x é melhor que math.exp (-x)?
Richard Knop

Não há diferença em termos de resultado da saída. Se você quiser saber a diferença em termos de velocidade, use timeit para cronometrar sua execução. Mas isso realmente não é importante.
ghostdog74

9
powé frequentemente implementado em termos de expe log, portanto, usar expdiretamente é quase certamente melhor.
japreiss

2
Isso sofre estouros quando xé muito negativo.
31415 Neil G

7

Outra maneira, transformando a tanhfunção:

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

@ NeilG Matematicamente, sigmóide (x) == (1 + tanh (x / 2)) / 2. Portanto, esta é uma solução válida, embora os métodos numericamente estabilizados sejam superiores.
22418 scottclowe

6

Sinto que muitos podem estar interessados ​​em parâmetros livres para alterar a forma da função sigmóide. Segundo, para muitos aplicativos que você deseja usar uma função sigmóide espelhada. Terceiro, você pode querer fazer uma normalização simples, por exemplo, os valores de saída estão entre 0 e 1.

Experimentar:

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

E para desenhar e comparar:

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

Finalmente:

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

Gráfico de funções sigmóides


6

Use o pacote numpy para permitir que sua função sigmoide analise vetores.

Em conformidade com o Deeplearning, eu uso o seguinte código:

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

2

Boa resposta de @unwind. No entanto, ele não pode lidar com números negativos extremos (lançando OverflowError).

Minha melhoria:

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

Isso é melhor, mas você ainda está com problemas de percussão numérica com valores negativos.
Neil G


2

Uma versão numericamente estável da função sigmóide logística.

    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
se x é positivo, estamos simplesmente usando 1 / (1 + np.exp (-x)), mas quando x é negativo, estamos usando a função np.exp (x) / (1 + np.exp (x)) em vez de usando 1 / (1 + np.exp (-x)) porque quando x é negativo -x será positivo, então np.exp (-x) pode explodir devido ao grande valor de -x.
Yash Khare

2

Um forro ...

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

Método vetorizado ao usar pandas DataFrame/Seriesou numpy array:

As respostas principais são métodos otimizados para o cálculo de ponto único, mas quando você deseja aplicar esses métodos a uma série de pandas ou matriz numpy, é necessário apply, que é basicamente o loop em segundo plano e iterará em todas as linhas e aplicará o método. Isso é bastante ineficiente.

Para acelerar nosso código, podemos fazer uso de vetorização e transmissão 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

Ou com um pandas Series:

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

1

você pode calculá-lo como:

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

ou conceitual, mais profundo e sem importações:

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

ou você pode usar numpy para matrizes:

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 código acima é a função sigmóide logística em python. Se eu sei disso x = 0.467, a função sigmóide F(x) = 0.385,. Você pode tentar substituir qualquer valor de x que você conhece no código acima e obterá um valor diferente de F(x).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.