朴素贝叶斯是有监督机器学习的一个经典算法。在了解了贝叶斯公式和朴素贝叶斯算法之后,我决定用一个简单的例子进行计算,并与已编写好的NLTK中的朴素贝叶斯算法对比,求证结果。这一对比,竟然发现了不少问题。于是开始了这段探究过程...
问题举例
我的例子很简单,一个语料中有两类文档(简单到每个文档我只用了一个单词。。。)
pos | neg |
good | terrible |
great | bad |
yeah | horrible |
nice |
目的是使用朴素贝叶斯算法,学习语料,并预测一个新的文档"nice good terrible" (毫无疑义,只是为了测试)的类别。
结果1
手算的结果一开始当然是pos和neg的概率都是0, 因为下面式子当中红色部分的值为0。
P(pos | nice good terrible)=P(pos)*P(nice | pos)*P(good | pos)*P(terrible | pos) / P(nice good terrible)=0
P(neg | nice good terrible)=P(neg)*P(nice | neg)*P(good | neg)*P(terrible | neg) / P(nice good terrible)=0
于是发现,smoothing对于Naive Bayes来说简直是太重要了。
结果2
网上搜索朴素贝叶斯的smoothing方法,找到了最常用的“Laplace加一”方法。
斯坦福公开课中 Dan Jurafsky & Chris Manning讲的也是这种方法:
https://www.youtube.com/watch?v=0hxaqDbdIeE
另一个视频中给出了很好的例子:
https://www.youtube.com/watch?v=evtCdmjcZ4I
但二者有一点区别,就是关于类的概率P(pos)和P(neg)的计算:
第一个视频中没有对类别的概率使用laplace smoothing,而后者对类别的计算也使用了smoothing,虽然结果都是1/2。
我先是按照第一个视频中的方法计算我的问题:
P(pos | nice good terrible)
=P(pos)*P(nice | pos)*P(good | pos)*P(terrible | pos) / P(nice good terrible)
=(4/7) * (1+1)/(4+7) * (1+1)/(4+7) * (0+1)/(4+7) / P(nice good terrible)
≈0.00171729 / P(nice good terrible)
P(neg | nice good terrible)
=P(neg)*P(nice | neg)*P(good | neg)*P(terrible | neg) / P(nice good terrible)
=(3/7) * (0+1)/(3+7) * (0+1)/(3+7) * (1+1)/(3+7) / P(nice good terrible)
≈0.00257443 / P(nice good terrible)
按照nltk的算法,把这两个概率normalize一下,就是最终输出的各类别的概率了:
P(pos) = P(pos | nice good terrible) / ( P(pos | nice good terrible) + P(neg | nice good terrible) ) ≈0.6670564
P(neg)= P(neg | nice good terrible) / ( P(pos | nice good terrible) + P(neg | nice good terrible) )≈0.332944
如果按照第二个视频的方法,对P(pos)和P(neg)也使用smoothing,结果是这样的:
P(pos) ≈0.61524
P(neg) ≈0.38475
结果3
由此可以看到,仅仅说使用了朴素贝叶斯算法也不能保证计算结果都是一样的,因为有不同的smoothing算法,而且同一种smoothing算法也要看用在了哪一部分。那么我更加好奇nltk作为常用的自然语言处理包,其中编写的NaiveBayesClassifier 用的究竟是哪种计算方法呢?
import nltk
featuresets=[({'nice':True},'pos'),({'good':True},'pos'),({'great':True},'pos'),({'yeah':True},'pos'),({'bad':True},'neg'),({'horrible':True},'neg'),({'terrible':True},'neg')]