在NLTK Python的朴素贝叶斯分类器中使用文档长度
我正在用Python的NLTK库构建一个垃圾邮件过滤器。现在我检查单词出现的次数,并使用朴素贝叶斯分类器,结果准确率达到了0.98,垃圾邮件的F值为0.92,而非垃圾邮件的F值为0.98。不过,当我查看程序出错的文档时,发现很多被错误分类为非垃圾邮件的垃圾邮件都是非常短的消息。
所以我想把文档的长度作为朴素贝叶斯分类器的一个特征。问题是,现在它只处理二进制值。有没有其他方法可以做到这一点,比如说:长度<100 =真/假?
(顺便提一下,我构建的垃圾邮件检测器是参考了这个http://nltk.googlecode.com/svn/trunk/doc/book/ch06.html的例子)
2 个回答
有一些多项式朴素贝叶斯算法可以处理范围值,但在NLTK这个库里没有实现。对于NLTK的朴素贝叶斯分类器,你可以尝试用几个不同的长度阈值作为二进制特征。还有,我建议你试试最大熵分类器,看看它对较小文本的处理效果如何。
NLTK的朴素贝叶斯实现并没有直接做到这一点,但你可以把朴素贝叶斯分类器的预测结果和文档长度的分布结合起来。NLTK的prob_classify方法会根据文档中的单词给你一个条件概率分布,也就是P(cl|doc),表示在给定文档内容的情况下,各个类别的概率。你想要的是P(cl|doc,len)——在知道文档内容和长度的情况下,某个类别的概率。如果我们再做一些独立性假设,就可以得到:
P(cl|doc,len) = (P(doc,len|cl) * P(cl)) / P(doc,len)
= (P(doc|cl) * P(len|cl) * P(cl)) / (P(doc) * P(len))
= (P(doc|cl) * P(cl)) / P(doc) * P(len|cl) / P(len)
= P(cl|doc) * P(len|cl) / P(len)
你已经从prob_classify得到了第一个部分,剩下的就是估计P(len|cl)和P(len)。
在建模文档长度时,你可以尽情发挥,但刚开始时可以简单假设文档长度的对数是正态分布的。如果你知道每个类别和整体的文档长度对数的均值和标准差,那么计算P(len|cl)和P(len)就变得简单了。
这里有一种估计P(len)的方法:
from nltk.corpus import movie_reviews
from math import sqrt,log
import scipy
loglens = [log(len(movie_reviews.words(f))) for f in movie_reviews.fileids()]
sd = sqrt(scipy.var(loglens))
mu = scipy.mean(loglens)
p = scipy.stats.norm(mu,sd)
需要记住的唯一棘手的地方是,这里是对数长度的分布,而不是直接的长度分布,而且这是一个连续分布。所以,长度为L的文档的概率将是:
p.cdf(log(L+1)) - p.cdf(log(L))
条件长度分布也可以用同样的方法估计,使用每个类别中文档的对数长度。这应该能给你计算P(cl|doc,len)所需的信息。