2

在朴素贝叶斯中乘以大量概率会导致浮点下溢。

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)
4

2 回答 2

2

假设所有的概率都在一个合理的范围内,比如 [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。

如果您的平台支持捕获浮点算术并且您的输入已知位于合理但未知的范围内,您可以n通过添加用于溢出和下溢的陷阱处理程序来自适应地选择步幅,而对较大的性能影响最小。如果您同时使用平台的汇编语言编写产品例程和陷阱处理程序,这样做可能是最容易的。

如果您改为添加对数,则会损失相当多的精度,首先是取对数,其次是求和,您可能关心也可能不关心。更糟糕的是,计算如此多的对数也会导致相当大的速度损失。

于 2016-01-30T17:33:54.220 回答
2

在发生下溢或上溢之前,浮点乘法是浮点运算的最佳表现。此外,在您的公式中,一旦达到下溢,就知道最终值很小,因为未处理的因子小于 1.0,只能有助于使最终结果更小。

使用对数似乎只会降低总体准确性,首先是因为对数本身,其次是因为浮点数相加的不同数量级表现不佳

除非您出于某种原因而关心 2 -1024和零概率之间的差异,而您的问题并未说明,否则我不明白您为什么要将第一个公式中表现良好的乘法更改为危险-在第二个中添加了令人担忧的内容。

注意:您必须有大约 20 个因子,每个因子大约为 2 -50,才能使 IEEE 754 的 binary64 格式下溢。如果这是您期望并希望准确处理的那种数据,您可能会考虑使用 80 位双扩展格式,前提是您的编译器使这种类型可用(例如,long double就像您使用 C 一样),或者MPFR,我相信它使用一个完整的词来表示指数。

于 2016-01-30T12:21:36.873 回答