我正在研究概率模型,在对这些模型进行推理时,估计的概率可能变得非常小。为了避免下溢,我目前在日志域工作(我存储概率的日志)。乘法概率等于加法,并使用以下公式进行求和:
log(exp(a) + exp(b)) = log(exp(a - m) + exp(b - m)) + m
其中m = max(a, b)
。
我使用了一些非常大的矩阵,我必须采用这些矩阵的元素指数来计算矩阵向量乘法。这一步非常昂贵,我想知道在使用概率时是否存在其他方法来处理下溢。
编辑:出于效率原因,我正在寻找使用基本类型的解决方案,而不是存储实数的任意精度表示的对象。
编辑2:我正在寻找比日志域技巧更快的解决方案,而不是更准确的解决方案。我对目前的准确性感到满意,但我需要一种更快的方法。特别是,在矩阵向量乘法期间进行求和,我希望能够使用有效的BLAS方法。
解决方案:在与Jonathan Dursi讨论后,我决定按其最大元素对每个矩阵和向量进行分解,并将该因子存储在日志域中。乘法很简单。在添加之前,我必须通过两个因子的比率将一个添加的矩阵/向量分解。我每十次操作更新一次因子。
答案 0 :(得分:9)
最近在computational science stack exchange site上出现了这个问题,虽然立即担心会出现溢出,但问题或多或少都是一样的。
转换为日志空间当然是一种合理的方法。无论你处于什么样的空间,要正确地完成大量的总和,你可以使用几种方法来提高求和的准确性。补偿求和方法,最着名的是Kahan summation,保持一个总和,实际上是“余数”;它为您提供了使用更高精度算术的一些优点,而无需所有成本(并且仅使用原始类型)。余下的术语也可以说明你的表现如何。
除了改进添加的实际机制外,更改添加条款的顺序可能会产生很大的不同。排序你的术语,使你从最小到最大的总和可以帮助,因为你不再经常添加非常不同的术语(这可能导致显着的舍入问题);在某些情况下,做log 2 N重复的成对总和也可以比直线性求和更好,这取决于你的术语是什么样的。
所有这些方法的实用性很大程度上取决于数据的属性。任意精度数学库虽然在计算时间(以及可能的内存)中使用非常昂贵,但它具有相当普遍的解决方案的优势。
答案 1 :(得分:4)
选项1: Commons Math - The Apache Commons Mathematics Library
Commons Math是一个轻量级,自包含的数学和统计组件库,可解决最常见的问题 可以使用Java编程语言或Commons Lang。
注意:API在命名工厂DfpField(而不是更直观的DfpFac或DfpFactory)时保护构造函数强制工厂模式。所以你必须使用
new DfpField(numberOfDigits).newDfp(myNormalNumber)
实例化一个Dfp,然后你可以调用.multiply
或其他任何东西。我以为我会提到这个,因为它有点令人困惑。
选项2: GNU Scientific Library或Boost C++ Libraries。 在这些情况下,您应该使用JNI来调用这些本机库。
选项3 :如果您可以自由使用其他程序和/或语言,则可以考虑使用程序/语言进行数值计算,例如Octave,Scilab,和类似的。
选项4: BigDecimal Java。
答案 2 :(得分:4)
在您的情况下,看起来您需要计算log(1 + exp(-x1)+ exp(-x2)+ ...)。抛弃那些大的负值。例如,假设a,b和c是三个对数概率,其中0> a> b> c。如果a-c> 38,则可以忽略c。它根本不会对你的联合记录概率有所贡献,至少在你使用双打时不会有所贡献。
答案 3 :(得分:1)
我认为你可能最好使用与double
相同的概念,即浮点表示,而不是以对数形式存储值。例如,您可以将每个值存储为两个long
s,一个用于符号和尾数,一个用于指数。 ( Real 浮点有一个经过精心调整的设计,可以支持大量的边缘情况并避免浪费一点;但你可能不需要担心任何这些,并且可以专注于以一种易于实现的方式进行设计。)
答案 4 :(得分:0)
我不明白为什么会这样,但这个公式似乎有效并且更简单:
c = a + log(1 + exp(b - a))
c = log(exp(a)+exp(b))