朴素贝叶斯(Naive Bayes)是一种简单的分类算法,它的经典应用案例为人所熟知:文本分类(如垃圾邮件过滤)。
优点:在数据较少的情况下仍然有效,可以处理多类别问题
缺点:对于输入数据的准备方式较为敏感。
适用数据类型:标称型数据(即离散型数据,变量的结果只在有限目标集中取值)
贝叶斯决策理论核心思想:选择高概率对应的类别。
例:p1(x,y)表示(x,y)属于类别1的概率, p2(x,y)表示(x,y)属于类别2的概率。如果p1>p2,则类别为1。如果p2>p1,则类别为2
假设y为类别,x为测试数据,那么贝叶斯分类器就是一个求p(y|x)的过程。
P(y)就是在不知道数据是类别为y的概率,这个数值可以通过计算训练集中属于特定类别的样本比例来得到。
假设F1,F2为训练集数据的特征,C为一种类别,P(x)就是每个特征的概率P(F1,F2)。这可以通过计算训练集中含有特定特征值的样本比例来得到。
比较微妙的部分是计算P(x|y),即P(F1,F2|C)。这个值的意义为:如果知道样本的类别为C,那么有多大的可能性可以看到特征值F1,F2。
在概率论中我们还知道:
P(F1,F2|C)=P(F1|C)P(F2|C,F1)
在这里我们假设F1,F2相互独立,那么就可以把P(F2|C,F1)简化成P(F2|C)
最终我们得到的公式为:
对于每个类别的结果,其分母的P(F1,F2)都是一样的值,因此可以简单忽略,这并不会改变最终胜出的类别。也就是说我们只要求到p(C)和p(F1|C) p(F2|C)就可以得到p(C|F1,F2)
接下来我们使用朴素贝叶斯进行文档分类。
准备数据:
def loadDataSet():#创建训练集
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]#训练集每个数据对应的标签的列表。1为侮辱性文字,0为正常言论
return postingList,classVec
从训练集中创建词汇表:
def createVocabList(dataSet):
vocabList=set([])#set方法用于创建集合
for document in dataSet:
vocabList=vocabList|set(document)
# |用于求两个集合的并集,最终得到不重复词表。
return list(vocabList)
将文档向量化
#vocabList为词汇表 inputSet为输入文本
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]+=1
return returnVec
训练函数:
#trainMatrix为向量化后的文档矩阵
#trainCategory为每篇文档类别标签构成的列表,即第一个函数中的classVec
def trainNB0(trainMatrix,trainCategory): #训练函数
numTrainDocs=len(trainMatrix) #文档矩阵所含文档数量,即训练集数据数量
numWords=len(trainMatrix[0]) #数据的特征值数量
pAbusive=sum(trainCategory)/float(numTrainDocs)
#计算任意文档属于侮辱性文档(class=1)的概率。即p(1)。p(0)=1-p(1),也就是公式中的p(y)
p0Num=np.ones(numWords)
p1Num=np.ones(numWords)
p0Denom=2.0
p1Denom=2.0
for i in range(numTrainDocs):
if trainCategory[i]==1:
p1Num+=trainMatrix[i]#所有侮辱性文档向量相加
p1Denom+=sum(trainMatrix[i])#侮辱性文档的总词数。
else:
p0Num+=trainMatrix[i]#所有正常文档向量相加
p0Denom+=sum(trainMatrix[i])#正常文档的总词数。
p1Vect=sp.log(p1Num/p1Denom) #对每个元素做除法
#p0Vect,p1Vect为给定文档类别条件下词汇表中单词的出现概率
p0Vect=sp.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
朴素贝叶斯分类函数:
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#测试函数
p1=sum(vec2Classify*p1Vec)+sp.log(pClass1)
p0=sum(vec2Classify*p0Vec)+sp.log(1.0-pClass1)
if p1>p0:
return 1
else:
return 0
测试函数:
def testingNB():
listOPosts,listClasses=loadDataSet()#加载训练数据和标签
myVocabList=createVocabList(listOPosts)#创建词汇表
trainMat=[]#初始化训练向量矩阵
for postinDoc in listOPosts:#将训练数据转化为向量
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
p0V,p1V,pAb=trainNB0(trainMat,listClasses)
testEntry=['love','my','dalmation']
thisDoc=setOfWords2Vec(myVocabList,testEntry)
print testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb)
testEntry=['stupid','garbage']
thisDoc=np.array(setOfWords2Vec(myVocabList,testEntry))
print testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb)
现在我们分类器已经构建好。下面我们使用该分类器来过滤垃圾邮件。
切分文章单词函数:
def textParse(bigString):
import re
listOfTokens=re.split(r'\W*',bigString)
#以任意非字母的字符作为分隔符。
return [tok.lower()for tok in listOfTokens if len(tok)>2]
#将单词小写化,并过滤掉小于两个字符的单词。
垃圾邮件分类函数:
import random
def spamTest():
docList=[]; classList=[];fullText=[]
for i in range(1,26):
wordList=textParse(open('C:\Users\Lenovo\Desktop\Data\machinelearninginaction\Ch04\email\spam\%d.txt' %i).read())
docList.append(wordList)
fullText.extend(wordList)#在fullText末尾扩展添加wordList
classList.append(1)
wordList=textParse(open('C:\Users\Lenovo\Desktop\Data\machinelearninginaction\Ch04\email\ham\%d.txt' %i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList=createVocabList(docList)
trainingSet=range(50);testSet=[]
for i in range(10):
randIndex=int(random.uniform(0,len(trainingSet)))
#uniform函数生成0到trainingSet长度之间的一个浮点数。
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat=[];trainClasses=[]
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam=trainNB0(np.array(trainMat),np.array(trainClasses))
errorCount=0
for docIndex in testSet:
wordVector=setOfWords2Vec(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
errorCount+=1
print 'the error rate is: ',float(errorCount)/len(testSet)
spamTest()
随机选择数据的一部分作为训练集,
而剩余部分作为测试集的过程称为留存交叉验证
函数spamTest()会输出在10封随机选择的电子邮件上的分类错误率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。