用于极值的logit和inverse logit函数

Bor*_*lik 9 python floating-point

我需要logit和inverse logit函数logit(inv_logit(n)) == n.我使用numpy,这就是我所拥有的:

import numpy as np
def logit(p):
    return np.log(p) - np.log(1 - p)

def inv_logit(p):
    return np.exp(p) / (1 + np.exp(p))
Run Code Online (Sandbox Code Playgroud)

以下是价值观:

print logit(inv_logit(2)) 
2.0 

print logit(inv_logit(10))
10.0 

print logit(inv_logit(20))
20.000000018 #well, pretty close

print logit(inv_logit(50))
Warning: divide by zero encountered in log
inf 
Run Code Online (Sandbox Code Playgroud)

现在让我们测试负数

print logit(inv_logit(-10))
-10.0 
print logit(inv_logit(-20))
-20.0 
print logit(inv_logit(-200))
-200.0 
print logit(inv_logit(-500))
-500.0 
print logit(inv_logit(-2000))
Warning: divide by zero encountered in log
-inf 
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:实现这些功能的正确方法是什么,以便要求logit(inv_logit(n)) == n适用n于尽可能宽的范围(至少[-1e4; 1e4]?

而且(我确信这与第一个相关),为什么我的函数与正值相比更稳定?

Joh*_*erg 12

要么使用

1. bigfloat包支持任意精度浮点运算.

2. SymPy 符号数学包.我将举两个例子:

首先,bigfloat:

http://packages.python.org/bigfloat/

这是一个简单的例子:

from bigfloat import *
def logit(p):
    with precision(100000):
        return log(p)- log(1 -BigFloat(p))

def inv_logit(p):
    with precision(100000):
        return exp(p) / (1 + exp(p))

int(round(logit(inv_logit(12422.0))))
# gives 12422
int(round(logit(inv_logit(-12422.0))))
# gives -12422
Run Code Online (Sandbox Code Playgroud)

这真的很慢.您可能需要考虑重构问题并分析一些部分.像这样的案例在实际问题中很少见 - 我很好奇你正在处理什么样的问题.

安装示例:

wget http://pypi.python.org/packages/source/b/bigfloat/bigfloat-0.3.0a2.tar.gz
tar xvzf bigfloat-0.3.0a2.tar.gz 
cd bigfloat-0.3.0a2
as root:
python setup.py install
Run Code Online (Sandbox Code Playgroud)

关于你的功能在负值下穿得更好的原因.考虑:

>>> float(inv_logit(-15))
3.059022269256247e-07

>>> float(inv_logit(15))
0.9999996940977731
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,浮点数很容易表示该值.移动小数点,以便不需要存储前导零:0.0000 .... 在第二种情况下,需要存储所有前导0.999,因此当稍后在logit()中执行1-p时,您需要所有额外的精度来获得精确的结果.

这是符号化的数学方式(明显更快!):

from sympy import *
def inv_logit(p):
    return exp(p) / (1 + exp(p))
def logit(p):
    return log(p)- log(1 -p)

x=Symbol('x')
expr=logit(inv_logit(x))
# expr is now:
# -log(1 - exp(x)/(1 + exp(x))) + log(exp(x)/(1 + exp(x)))
# rewrite it: (there are many other ways to do this. read the doc)
# you may want to make an expansion (of some suitable kind) instead.
expr=cancel(powsimp(expr)).expand()
# it is now 'x'

# just evaluate any expression like this:    
result=expr.subs(x,123.231)

# result is now an equation containing: 123.231
# to get the float: 
result.evalf()
Run Code Online (Sandbox Code Playgroud)

Sympy在这里找到http://docs.sympy.org/.在ubuntu中,它是通过突触发现的.


Fab*_*osa 6

有一种方法可以实现这些功能,使它们在各种值中都是稳定的,但它涉及根据参数区分的情况.

以inv_logit函数为例.你的公式"np.exp(p)/(1 + np.exp(p))"是正确的,但是对于大p会溢出.如果将分子和分母除以np.exp(p),则获得等效表达式

1. / (1. + np.exp(-p))
Run Code Online (Sandbox Code Playgroud)

不同的是,这个不会溢出大正p.然而,对于p的大负值,它会溢出.因此,稳定的实施可以如下:

def inv_logit(p):
    if p > 0:
        return 1. / (1. + np.exp(-p))
    elif p <= 0:
        np.exp(p) / (1 + np.exp(p))
    else:
        raise ValueError
Run Code Online (Sandbox Code Playgroud)

这是库LIBLINEAR(以及可能的其他库)中使用的策略.


sha*_*adi 5

现在,scipy有logitexpit(逆logit)函数,例如

>>> from scipy.special import logit, expit
>>> import numpy as np
>>> logit([0, 0.25, 0.5, 0.75, 1])
array([       -inf, -1.09861229,  0.        ,  1.09861229,         inf])
>>> expit([-np.inf, -1.5, 0, 1.5, np.inf])
array([ 0.        ,  0.18242552,  0.5       ,  0.81757448,  1.        ])

Run Code Online (Sandbox Code Playgroud)