Python 极小值函数积分对数的数值稳定计算

Python 极小值函数积分对数的数值稳定计算,python,scipy,statistics,numerical-integration,numerical-stability,Python,Scipy,Statistics,Numerical Integration,Numerical Stability,如果我有一个随机数Z,它被定义为,X和Y,那么Z的概率分布是X和Y的概率分布的卷积。卷积基本上是分布函数乘积的积分。卷积中的积分通常没有解析解,因此必须使用基本的求积算法进行计算。在伪代码中: prob_z(z) = integrate(lambda t: prob_x(t) * prob_y(z-t), -inf, inf) 对于一个具体示例,正态分布变量X和对数正态分布变量Y的和Z可以使用以下Python/Scipy代码计算: from scipy.integrate import qua

如果我有一个随机数
Z
,它被定义为,
X
Y
,那么
Z
的概率分布是
X
Y
的概率分布的卷积。卷积基本上是分布函数乘积的积分。卷积中的积分通常没有解析解,因此必须使用基本的求积算法进行计算。在伪代码中:

prob_z(z) = integrate(lambda t: prob_x(t) * prob_y(z-t), -inf, inf)
对于一个具体示例,正态分布变量
X
和对数正态分布变量
Y
的和
Z
可以使用以下Python/Scipy代码计算:

from scipy.integrate import quad
from scipy.stats import norm, lognorm
from scipy import log

prob_x = lambda x: norm.pdf(x, 0, 1)  # N(mu=0, sigma=1)
prob_y = lambda y: lognorm.pdf(y, 0.1, scale=10)  # LogN(mu=log(10), sigma=0.1)
def prob_z(z):
    return quad(lambda t: prob_x(t)*prob_y(z-t), -inf, inf)
现在我想计算对数概率。简单的解决方案是:

def log_prob_z(z):
    return log(prob_z(z))
然而,这在数值上是不稳定的。在大约39个标准差之后,概率分布在数值上为0.0,因此即使对数概率有一些有限值,也不能通过简单地取概率的对数来计算。比较
norm.pdf(39,1,0)
的0.0和
norm.logpdf(39,1,0)
的-761。显然,Scipy不会将
logpdf
计算为
log(pdf)
——它会找到其他方法,因为否则它将返回
-inf
,这是一个较差的响应。同样,我想找到另一种解决问题的方法

(你可能想知道为什么我这么关心远离平均值的值的对数似然性。答案是参数拟合。当对数似然性是一个巨大的负数时,拟合算法可以更接近,但当它是
-inf
nan
时,什么也做不到)


问题是:有人知道我如何重新排列
log(quad(…)
,这样我就不会计算
quad(…)
,从而避免在日志中创建一个0.0吗?

问题是,要集成的函数值太小,无法以双精度表示,这只有在1e-308左右时才有效

我来营救你 当双精度不足以进行数值计算时,需要一个用于任意精度浮点运算的库。它有自己的
quad
例程,但您需要实现pdf函数,以便它们在mpmath级别工作(否则就没有任何东西需要集成)。有,包括,所以我要用它来说明

这里我用SciPy卷积两个相距70的正常PDF:

z = 70
p = quad(lambda t: norm.pdf(t, 0, 1)*norm.pdf(z-t, 0, 1), -np.inf, np.inf)[0]
遗憾的是,p正好是0.0

在这里,我对mpmath做了同样的操作,在
将mpmath导入为mp
之后:

z = 70
p = mp.quad(lambda t: mp.npdf(t, 0, 1)*mp.npdf(z-t, 0, 1), [-mp.inf, mp.inf])
现在p是一个mpmath对象,打印为2.95304756048889e-543,远远超过双精度刻度。它的对数,mp.log(p),是-1249.22086778731

基于SciPy的备选方案:对数偏移 如果出于某种原因不能使用mpmath,至少可以尝试通过将其值移动到双精度范围来“规范化”函数。以下是一个例子:

z = 70
offset = 2*norm.logpdf(z/2, 0, 1)
logp = offset + np.log(quad(lambda t: np.exp(norm.logpdf(t, 0, 1) + norm.logpdf(z-t, 0, 1) - offset), -np.inf, np.inf)[0])
这里logp打印-1264.66566393,这不如mpmath结果好(因此我们丢失了一些函数),但它是合理的。我所做的是:

  • 计算函数对数的最大值的对数(这是变量偏移量)
  • 从pdf的对数中减去该偏移量;这是部分
    norm.logpdf(t,0,1)+norm.logpdf(z-t,0,1)-偏移量
  • 求结果的指数,因为我们不能把对数放在积分中。在代数上,这与PDF乘以exp(-offset)的乘积相同。但从数字上看,这是一个不太可能溢出的数字;实际上,在t=z/2时,它是exp(0)=1
  • 正常整合;取对数,在对数上加上偏移量。代数上,结果就是我们想要的积分的对数
这可能更适合作为一个普通的数学问题。如果我认为解析求解积分是可行的,我会同意。但我真的只关心从数值角度的解决方案。根据我的经验,数字问题最好在这里提出。虽然我同意要弄明白这一点需要一点数学知识。正态分布永远不是0.0——它延伸到无穷远,并且总是有一个有限的值。在机翼上,概率变得太小,无法用简单的方式在计算机上表示。即使您想出了一种计算这些值的方法,您仍然无法用标准浮点数来表示它们。我想你要找的不是这个特定数学问题的解决方案,而是一种提高精度的浮点数表示方法。我可能会看看SymPy,因为它支持任意精度。@PaulCornelius在可能性太小而无法用浮点数表示的领域,对数似然可以准确地表示。我正在寻找一种计算对数可能性的方法,而不是简单地获取不可表示可能性的对数。mpmath版本易于实现,速度惊人。我只是同意了。它的工作范围确实很大。对于我的问题,它在大约1e20标准偏差下崩溃,这是默认的mp精度,同时依赖于Scipy获得对数-正常-对数pdf。