我正在寻找以下函数的数值稳定实现。由于我的应用涉及 t 分布,所以我这里以 t 分布为例。
Log CDF
# Naive Python implementation of the function I need
import scipy
import numpy as np
def t_log_cdf(x, df):
p = scipy.stats.t.cdf(x, df=df)
return np.log(p)
在 SciPy 中,有一个 logcdf ,但它是数值不够稳定。将 scipy 函数的行为:
>>> import scipy
>>> import numpy as np
>>> scipy.stats.t.logcdf(np.linspace(0, 60, 5), df=15)
array([-6.93147181e-01, -9.70638014e-11, -4.21884749e-15, 0.00000000e+00,
0.00000000e+00])
与 R 中的等效函数进行比较:
# R code
> pt(seq(0, 60, length.out = 5), df = 15, log.p = TRUE)
[1] -6.931472e-01 -9.706380e-11 -4.157404e-15 -1.012752e-17 -1.384523e-19
Tensorflow Probability 提供了一个实现,但其结果类似于 scipy 结果。
我无法在 中找到实现PyTorch (根本不提供 t 分布的 CDF)。
基于对数概率的分位数函数:
# Naive Python implementation of the function I need
import scipy
import numpy as np
def t_log_ppf(log_p, loc, scale):
p = np.exp(log_p)
return scipy.stats.t.ppf(p, df=df))
我在 SciPy 中找不到对对数概率进行操作的分位数函数, PyTorch ,或 张量流概率
是对的,在对数概率的极端尾部计算 log-CDF 和分位数函数可能会在数值上变得不稳定。观察到 SciPy 和 TensorFlow Probability 中的默认方法在此类情况下难以提供准确结果。
以下是解决这些问题的策略以及提供更稳定实现的方法:
1. 用于 Log-CDF 的任意精度算术
- 挑战: 主要问题在于浮点数的精度有限。当 CDF 非常接近 1(对于 log-CDF 接近 0)时,直接计算会损失大量有效数字,从而导致不准确或甚至下溢。
-
解决方案: 使用支持任意精度算术的库。Python 中的一个流行选择是
mpmath
。mpmath
允许以比标准浮点数高得多的精度执行计算,从而减轻了这些问题。```python import mpmath
def t_log_cdf_stable(x, df): with mpmath.workdps(30): # 将工作精度设置为 30 位数(或根据需要进行调整) return mpmath.log(mpmath.betainc(df / 2, 0.5, df / (df + mpmath.mpf(x)**2)))
# 示例用法 x_values = np.linspace(0, 60, 5) log_cdf_values = [t_log_cdf_stable(x, 15) for x in x_values] print(log_cdf_values) ```
2. 用于 Log-CDF 的尾部逼近
- 洞察力: 对于许多分布(包括 t 分布),对于极端尾部值,存在提供准确近似值的专用数学公式(通常涉及渐近展开)。
- 解决方案: 为 |x| 的大值实现这些尾部逼近值。这需要查阅 t 分布的统计文献以找到适当的公式。
3. 用于基于对数概率的分位数函数(逆 CDF)的反向数值方法
-
挑战:
即使我们使用
log_cdf
的稳定实现,直接反转它(使用类似scipy.optimize.fsolve
的方法)也可能在数值上很具有挑战性。 -
解决方案: 应用专门设计用于反转函数的数值方法,即使在对数概率空间中也是如此。一种这样的方法是使用根查找算法(例如 Brent 方法或 Ridders 方法)结合稳定的
log_cdf
函数。```python from scipy.optimize import brentq
def t_log_ppf_stable(log_p, df): def objective(x): return t_log_cdf_stable(x, df) - log_p # 为根查找定义适当的界限 a = -1e3
b = 1e3
return brentq(objective, a, b)# 示例用法 log_p_values = [-6.931472, -9.706380, -4.157404, -1.012752, -1.384523]
quantiles = [t_log_ppf_stable(lp, 15) for lp in log_p_values] print(quantiles) ```
重要注意事项:
- 选择适当的方法: 最佳方法取决于的特定需求,例如所需的精度水平、计算速度以及正在处理的尾部值的极端程度。任意精度算术非常准确,但可能会比较慢,而尾部逼近值提供了良好的折衷方案。
- 测试和验证: 始终使用已知结果或替代实现彻底测试选择的实现,以确保其在预期的输入范围内准确且稳定。
如果需要在速度和数值稳定性方面都具有最佳性能的解决方案,可以考虑使用 C/C++ 等编译语言实现这些专门函数,并使用 Python 包装它们。这样,就可以利用优化的数学库并更直接地控制数值计算。
标签:python,scipy From: 78822823