【发布时间】:2016-05-08 00:26:01
【问题描述】:
在朴素贝叶斯中乘以大量概率会导致浮点下溢。
P(x_1,….,x_n│c) = P(x_1│c).P(x_2│c).P(x_3│c)… … P(x_n |c)
而不是使用上面的公式(导致浮点下溢),使用下面给出的公式是否更可行/更好?还是会截断信息?
log(xy) = log(x) + log(y)
【问题讨论】:
标签: floating-point naivebayes underflow
在朴素贝叶斯中乘以大量概率会导致浮点下溢。
P(x_1,….,x_n│c) = P(x_1│c).P(x_2│c).P(x_3│c)… … P(x_n |c)
而不是使用上面的公式(导致浮点下溢),使用下面给出的公式是否更可行/更好?还是会截断信息?
log(xy) = log(x) + log(y)
【问题讨论】:
标签: floating-point naivebayes underflow
在发生下溢或上溢之前,浮点乘法是浮点运算的最佳表现。此外,在您的公式中,一旦达到下溢,就知道最终值很小,因为未处理的因素小于 1.0,只能有助于使最终结果更小。
使用对数似乎只会降低总体准确度,首先是因为对数本身,其次是因为浮点数相加不同幅度的数字不表现良好。
除非你关心 2-1024 的概率和 0 的概率之间的差异,因为你的问题没有说明,我不明白你为什么要换井- 第一个公式中的行为乘法到第二个公式中的危险加法。
注意:您必须有大约 20 个因子,每个因子为 2-50 才能使 IEEE 754 的 binary64 格式下溢。如果这是您期望并希望准确处理的那种数据,您可能会考虑使用 80 位双扩展格式,前提是您的编译器使这种类型可用(例如,如果您使用 C,则为 long double) ,或MPFR,我相信它使用一个完整的单词来表示指数。
【讨论】:
假设所有的概率都在一个合理的范围内,比如 [2^{-63}, 2^{63}],你可以这样累加乘积:
double prod(double *d, int n, int64_t *expo) {
*expo = 0;
double ans = 1;
for (int i = 0; i < n; i++) {
ans *= d[i];
if (!(i % 16)) {
int foo = 0;
ans = frexp(ans, &foo);
expo += foo;
}
}
}
然后乘积在返回值乘以 2^{*expo} 的 n/2 ulp 内。这段代码很容易向量化,也很容易为这种特殊情况编写一个更快的替代方案frexp,它只是进行位摆弄并忽略 NaNs/infinities/zeroes/subnormals。
如果您的平台支持捕获浮点运算并且已知您的输入位于合理但未知的范围内,您可以通过添加用于溢出和下溢。如果您同时使用平台的汇编语言编写产品例程和陷阱处理程序,这样做可能最容易。
如果您改为添加对数,则会损失相当多的精度,首先是取对数,其次是求和,您可能关心也可能不关心。更糟糕的是,您还通过计算这么多对数损失了相当多的速度。
【讨论】: