参考:
http://blog.csdn.net/marvin521/article/details/9262445
http://blog.csdn.net/rk2900/article/details/8984276
naive bayes 还是很简单的,网上的很多细节都没有说清楚,我直接看了原版的《Machine learning in action》
把细节都扒了出来,流程是这样的
1.根据所有邮件构建一个特征向量,向量的维度就是邮件所有单词的最小集合,[0,0,0,0,0]
2.训练Naive Bayes classfier,生成特征向量对垃圾/非垃圾邮件判断的权重,纬度和特征向量一致[1,2.1,4,2,5]/[1,-1,0.2,4,5]
(我一开始以为NB不需要训练,直接计算的,所以很多位置弄错了)
3.开始测试,将测试的邮件转为特征向量,这里有两点,一定要注意,网上都没有提到,我看代码才搞清楚,不知到为什么一直没人提到过
a) 转换时,如果特征向量里没有这个纬度的话,就不考虑,比如我输入的是"我是谁呢",特征向量里没有“呢”这个纬度,不考虑,生成(0,1,0,1,1)
b)转换时,如果一个纬度出现两次,只用考虑一次比如“我是谁谁”,还是生成(0,1,0,1,1)
4.将生成的向量与垃圾/非垃圾权重向量乘积(相当于屏蔽某些特征)再求和
5.比较大小,判断类型
Bayes公式在这里的应用
Bayes就是通过先验概率求后验概率
P(A|B) = P(B|A) * P(A) / P(B)
符号说明:
R:邮件中垃圾邮件的概率
r:邮件中正常邮件的概率
Xi:出现纬度i的概率
则判断是否是垃圾邮件可以通过:
P(R|X1,X2,X3.....Xn) <> P(r|X1,X2,X3.....Xn) 大小关系确定测试邮件的归属
由bayes公式
=> P(X1,X2,...Xn|R) * P(R) / P(X1,X2,...Xn) <> P( P(X1,X2,...Xn|r) * P(r) / P(X1,X2,...Xn)
因为是邮件中的文本,可以简单认为是文本间是相互独立的,所以
=>P(X1|R) *P(X2|R)...P(Xn|R) * P(R) <> P(X1|r)*P(X2|R),...*P(Xn|r) * P(r)
测试邮件中并不能包含所有的特征文本,可能P(Xn|R) = 0,造成两边无法比较的情况 0 <> 0
可以取对数来化解,Ln(X)的单调性和 X是一致的
=>Ln(P(X1|R) *P(X2|R)...P(Xn|R) * P(R) ) <> Ln(P(X1|r)*P(X2|R),...*P(Xn|r) * P(r))
=>P(X1|R) + P(X2|R) +....+ P(Xn|R) + P(R) <> P(X1|r) + P(X2|r)....+P(Xn|r) * P(r)
我也在这里实现一下
#-*- coding:utf-8 -*-
'''
Created on Aug 24, 2013
@author: blacklaw
'''
from numpy import *
postingList = [
['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
]
classVec = [0,1,0,1,0,1]
def docs2set(data):
retSet = set()
for li in data:
retSet = retSet | set(li)
return retSet
def list2vect(li, wordsSet):
retVect = zeros(len(wordsSet))
for word in li:
if word in wordsSet:
retVect[list(wordsSet).index(word)] = 1
else:
print "%s not in wordsSet" % word
return retVect
def trainNB(martix, wordsSet, category):
'''
pRvect = zeros(len(wordsSet))
prvect = zeros(len(wordsSet))
'''
# use ones not zeros: caution, log(0) will be error
pRvect = ones(len(wordsSet))
prvect = ones(len(wordsSet))
RSum = rSum = 0
for i,vect in enumerate(martix):
if category[i] == 1: # 1 means Rubbish
pRvect += vect
RSum += sum(vect)
else:
prvect += vect
rSum += sum(vect)
return log(pRvect/RSum), log(prvect/rSum)
def testNB(pRvect, prvect, testDoc, wordsSet, category):
testVect = list2vect(testDoc, wordsSet)
R = float(sum(category)) / len(category)
r = 1 - R
'''
Caution:
we have trained NB pR/rvect use log()
so there use "+ R" "sum", not * R
'''
pFromR = R + sum(pRvect * testVect)
pFromr = r + sum(prvect * testVect)
print pFromR, pFromr
return pFromR > pFromr
if __name__ == "__main__":
wordsSet = docs2set(postingList)
martix = []
for posting in postingList:
martix.append(list2vect(posting, wordsSet))
pRvect, prvect = trainNB(martix, wordsSet, classVec)
print testNB(pRvect, prvect, ['love', 'my', 'dalmation'], wordsSet, classVec)
print testNB(pRvect, prvect, ['stupid', 'garbage'], wordsSet, classVec)
结果:
-8.3333169375 -6.2615727688
False
-3.30943641665 -5.8561076607
True