C语言中浮点运算精度问题

在我的课程项目中,我开始在C语言中实现“朴素贝叶斯分类器”。我的项目是使用大量训练数据实现一个文档分类应用程序(特别是垃圾邮件)。

现在我在实现算法时遇到了问题,因为C语言的数据类型有限制。

(我使用的算法在这里给出,http://en.wikipedia.org/wiki/Bayesian_spam_filtering

问题陈述:该算法涉及到对文档中的每个单词进行处理,并计算其为垃圾邮件单词的概率。如果p1, p2, p3 …. pn分别是单词1, 2, 3 … n的概率。文档被判定为垃圾邮件或非垃圾邮件的概率是通过以下方式计算的:

alt text

这里,概率值很容易达到0.01左右。因此,即使我使用“double”数据类型,我的计算也会出现问题。为了确认这一点,我编写了以下示例代码。

#define PROBABILITY_OF_UNLIKELY_SPAM_WORD     (0.01)#define PROBABILITY_OF_MOSTLY_SPAM_WORD     (0.99)int main(){    int index;    long double numerator = 1.0;    long double denom1 = 1.0, denom2 = 1.0;    long double doc_spam_prob;    /* 模拟一些不太可能是垃圾邮件的单词 */    for(index = 0; index < 162; index++)    {        numerator = numerator*(long double)PROBABILITY_OF_UNLIKELY_SPAM_WORD;        denom2    = denom2*(long double)PROBABILITY_OF_UNLIKELY_SPAM_WORD;        denom1    = denom1*(long double)(1 - PROBABILITY_OF_UNLIKELY_SPAM_WORD);    }    /* 模拟大量明显是垃圾邮件的单词 */    for (index = 0; index < 1000; index++)    {        numerator = numerator*(long double)PROBABILITY_OF_MOSTLY_SPAM_WORD;        denom2    = denom2*(long double)PROBABILITY_OF_MOSTLY_SPAM_WORD;        denom1    = denom1*(long double)(1- PROBABILITY_OF_MOSTLY_SPAM_WORD);    }    doc_spam_prob= (numerator/(denom1+denom2));    return 0;}

我尝试了Float, double甚至long double数据类型,但仍然存在相同的问题。

因此,假设我在分析一个包含10万个单词的文档,如果其中只有162个单词的垃圾邮件概率为1%,而剩下的99838个单词明显是垃圾邮件,那么由于精度错误(因为分子很容易变为零),我的应用程序仍然会将其判定为非垃圾邮件文档!!!

这是我第一次遇到这样的问题。那么,究竟应该如何解决这个问题呢?


回答:

你的问题是因为你收集了太多不考虑其大小的项。一个解决方案是取对数。另一个是排序你的各个项。首先,让我们将方程重写为1/p = 1 + ∏((1-p_i)/p_i)。现在你的问题是有些项很小,而有些项很大。如果你连续有太多小的项,你会遇到下溢,而有太多大的项会导致中间结果上溢。

所以,不要连续放置太多相同数量级的项。排序项(1-p_i)/p_i。结果,第一个将是最小的项,最后一个是最大的项。现在,如果你直接相乘,你仍然会遇到下溢。但是计算的顺序并不重要。使用两个迭代器进入你的临时集合。一个从开始处开始(即(1-p_0)/p_0),另一个从末尾开始(即(1-p_n)/p_n),你的中间结果从1.0开始。现在,当你的中间结果大于或等于1.0时,你从前面取一个项,当你的中间结果小于1.0时,你从后面取一个结果。

结果是,当你取项时,中间结果会在1.0附近波动。只有当你用完小项或大项时,它才会上升或下降。但这没关系。此时,你已经消耗了两端的极端值,因此中间结果将慢慢接近最终结果。

当然,溢出的可能性是存在的。如果输入完全不可能是垃圾邮件(p=1E-1000),那么1/p会溢出,因为∏((1-p_i)/p_i)溢出。但由于项是排序的,我们知道中间结果只会在∏((1-p_i)/p_i)溢出时溢出。所以,如果中间结果溢出,就不会有后续的精度损失。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注