将个体概率相结合在朴素贝叶斯垃圾邮件过滤中

18

我目前正在尝试通过分析我收集的语料库来生成垃圾邮件过滤器。

我正在使用维基百科条目http://en.wikipedia.org/wiki/Bayesian_spam_filtering来开发我的分类代码。

我已经实现了计算一个消息包含特定单词时它是垃圾邮件的概率的代码,通过实现维基上的以下公式:

pr(S|W) = (pr(W|S)*pr(S))/(pr(W|S)*pr(S) + pr(W|H)*pr(H))

我的PHP代码:

public function pSpaminess($word)
{
    $ps = $this->pContentIsSpam();
    $ph = $this->pContentIsHam();
    $pws = $this->pWordInSpam($word);
    $pwh = $this->pWordInHam($word);
    $psw = ($pws * $ps) / ($pws * $ps + $pwh * $ph);
    return $psw;
}

根据"合并个体概率"一节,我已经实现了代码来合并测试消息中所有唯一单词的概率以确定垃圾邮件的可能性。
从维基公式中可以看出:

p=(p1*pn)/((p1*pn)+(1-p)(1-pn))

我的PHP代码:

public function predict($content)
{
    $words = $this->tokenize($content);
    $pProducts = 1;
    $pSums = 1;
    foreach($words as $word)
    {
        $p = $this->pSpaminess($word);
        echo "$word: $p\n";
        $pProducts *= $p;
        $pSums *= (1 - $p);
    }
    return $pProducts / ($pProducts + $pSums);
}

在测试字符串"This isn't very bad at all."上,将产生以下输出:

C:\projects\bayes>php test.php
this: 0.19907407407407
isn't: 0.23
very: 0.2
bad: 0.2906976744186
at: 0.17427385892116
all: 0.16098484848485
probability message is spam: float(0.00030795502523944)

这是我的问题:我是否正确实现了组合个体概率?假设我正在生成有效的单词概率,那么组合方法是否正确?
我的担忧是计算结果非常小的概率。我已经在更大的测试消息上进行了测试,并得到了一个科学计数法表示的结果概率,其中有超过10个零的位数。我原本期望的值应该在10或100之间。
我希望问题出在我的PHP实现上--但是当我查看维基百科的组合函数时,公式的被除数是分数的乘积。我不明白多个概率的组合怎么会变成超过0.1%的概率。
如果情况确实如此,即消息越长,概率得分就越低,那么我如何补偿垃圾邮件配额以正确预测小型和大型测试案例的垃圾邮件/非垃圾邮件?

附加信息

我的语料库实际上是由大约40,000个Reddit评论组成的集合。我正在使用我的"垃圾邮件过滤器"来对这些评论进行评分。我根据支持票数与反对票数的比例将单个评论评为垃圾邮件/正常邮件:如果支持票数少于反对票数,则被视为正常邮件,否则为垃圾邮件。

现在,由于语料库的类型,事实证明有一些词汇在垃圾邮件中使用得更多,而不是在正常邮件中。也就是说,以下是出现在垃圾邮件中比正常邮件更频繁的前十个单词列表。

+-----------+------------+-----------+
| word      | spam_count | ham_count |
+-----------+------------+-----------+
| krugman   |         30 |        27 |
| fetus     |       12.5 |       7.5 |
| boehner   |         12 |        10 |
| hatred    |       11.5 |       5.5 |
| scum      |         11 |        10 |
| reserve   |         11 |        10 |
| incapable |        8.5 |       6.5 |
| socalled  |        8.5 |       5.5 |
| jones     |        8.5 |       7.5 |
| orgasms   |        8.5 |       7.5 |
+-----------+------------+-----------+

相反,大多数单词在垃圾邮件中的使用比在正常邮件中更为频繁。例如,以下是我筛选出来的前十个垃圾邮件中出现次数最高的单词。

+------+------------+-----------+
| word | spam_count | ham_count |
+------+------------+-----------+
| the  |       4884 |     17982 |
| to   |     4006.5 |   14658.5 |
| a    |     3770.5 |   14057.5 |
| of   |     3250.5 |   12102.5 |
| and  |       3130 |     11709 |
| is   |     3102.5 |   11032.5 |
| i    |     2987.5 |   10565.5 |
| that |     2953.5 |   10725.5 |
| it   |       2633 |      9639 |
| in   |     2593.5 |    9780.5 |
+------+------------+-----------+

正如您所看到的,垃圾邮件的使用频率显著低于普通邮件的使用频率。在我的40k评论语料库中,有2100条评论被认为是垃圾邮件。

如下所建议,对一篇被认为是垃圾邮件的帖子进行测试,结果如下:

短语

Cops are losers in general. That's why they're cops.

分析:
C:\projects\bayes>php test.php
cops: 0.15833333333333
are: 0.2218958611482
losers: 0.44444444444444
in: 0.20959269435914
general: 0.19565217391304
that's: 0.22080730418068
why: 0.24539170506912
they're: 0.19264544456641
float(6.0865969793861E-5)

根据此,这很可能不是垃圾邮件。但是,如果我现在分析一条正常的评论:
短语
Bill and TED's excellent venture?

分析
C:\projects\bayes>php test.php
bill: 0.19534050179211
and: 0.21093065570456
ted's: 1
excellent: 0.16091954022989
venture: 0.30434782608696
float(1)

好的,这很有趣。我在撰写此更新时正在进行这些示例,所以这是我第一次看到这个特定测试案例的结果。我认为我的预测是相反的。它实际上挑选出了“垃圾邮件”而不是“正常邮件”的概率。这值得验证。

对已知的正常邮件进行新的测试。

短语

Complain about $174,000 salary being too little for self.  Complain about $50,000 a year too much for teachers.
Scumbag congressman.

分析
C:\projects\bayes>php test.php
complain: 0.19736842105263
about: 0.21896031561847
174: 0.044117647058824
000: 0.19665809768638
salary: 0.20786516853933
being: 0.22011494252874
too: 0.21003236245955
little: 0.21134020618557
for: 0.20980452359022
self: 0.21052631578947
50: 0.19245283018868
a: 0.21149315683195
year: 0.21035386631717
much: 0.20139771283355
teachers: 0.21969696969697
scumbag: 0.22727272727273
congressman: 0.27678571428571
float(3.9604152477223E-11)

很遗憾,不行。结果是巧合。我开始怀疑评论是否可以这么容易地量化。也许恶意评论的本质与垃圾邮件的本质相差太大了。也许只有在具有特定单词类别的垃圾邮件时,垃圾邮件过滤才能起作用?

最终更新

正如回复中所指出的那样,奇怪的结果是由于语料库的性质所致。在评论语料库中,没有明确定义垃圾邮件的情况下,贝叶斯分类无法执行。由于任何一个评论可能会被各种用户评为垃圾邮件和正常邮件,因此无法对垃圾邮件进行硬分类。

最终,我希望生成一个评论分类器,可以根据针对评论内容调整的贝叶斯分类来确定评论发布是否会获得声望。我可能仍然会研究将分类器调整为电子邮件垃圾邮件,并查看这样的分类器是否能够猜测评论系统的声望响应。但目前问题已得到解答。感谢大家的意见。


2
+1 为使用数学表达式和代码,并提供完整、清晰的解释!我希望我能给予 +10 的赞同。 - wallyk
嗨,Jeremy。你最终是否使用了这个算法进行垃圾邮件过滤?我也想做类似的事情,但结果不一致。 - Paul Atkins
嘿,保罗。我做这个只是为了练习,它从未被用于任何实际应用。不过,就它的价值而言,我发现如下所述,当我提供了一个相等数量的正常邮件/垃圾邮件样本语料库时,结果更符合我的预期。 - Jeremy Giberson
3个回答

1

仅使用计算器进行验证,你发布的非垃圾短语似乎没问题。在这种情况下,你的$pProducts比$pSums小几个数量级。

尝试从垃圾邮件文件夹中运行一些真正的垃圾邮件,其中可能会遇到0.8这样的概率。猜猜为什么垃圾邮件发送者有时会尝试在消息中隐藏一个报纸框架 :)


不幸的是,如上所述的附加信息,即使评估垃圾邮件也会导致不良的小概率。 - Jeremy Giberson
2
你的问题是(假设有40000个帖子,其中约2000个是垃圾邮件)... 垃圾邮件不足。通常在电子邮件通信中,垃圾邮件占95-98%左右,而你则相反。这就是为什么贝叶斯过滤器可能会看起来像检测正常邮件的原因。我看到的另一个问题是最垃圾邮件般的单词大概只有0.625的概率被认为是垃圾邮件 - 那还不够。我给你的建议是获取真正的垃圾邮件数据库,并用它来训练你的过滤器 - 毕竟,无论是电子邮件还是论坛帖子,垃圾邮件都没有那么不同。 - meteor

1
如果您的过滤器没有偏见(Pr(S)=Pr(H) = 0.5),那么:“同样建议学习到的消息集符合垃圾邮件和正常邮件之间50%的假设分配,即垃圾邮件和正常邮件数据集大小相同。”
这意味着您应该在类似数量的垃圾邮件和正常邮件消息上教授您的贝叶斯过滤器。例如1000封垃圾邮件和1000封正常邮件。
我认为(未经检查)如果您的过滤器有偏见,则学习集应符合任何消息都是垃圾邮件的假设。

0
基于消息长度的补偿思想,您可以估计每个集合中消息单词成为特定单词的概率,然后使用泊松分布来估计包含该特定单词的N个单词的消息的概率。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接