以下内容来源于《Machine Learning in Action》
朴素贝叶斯
优点:在数据较少的情况下仍然有效,可以处理多类别问题。
缺点:对于输入数据的准备方式较为敏感。
适用数据类型:标称型数据
朴素贝叶斯的一般过程
(1) 收集数据:可以使用任何方法。本章使用RSS源。
(2) 准备数据:需要数值型或者布尔型数据。
(3) 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。
(4) 训练算法:计算不同的独立特征的条件概率。
(5) 测试算法:计算错误率。
(6) 使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴
素贝叶斯分类器,不一定非要是文本。
准备数据:从文本中构建词向量
from numpy import *
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
#createVocabList()会创建一个包含在所有文档中出现的不重复词的列表
def createVocabList(dataSet):
vacabSet=set([])
for document in dataSet:
#操作符|用于求两个集合的并集,这也是一个按位或( OR)操作符
vacabSet=vacabSet | set(document)
return list(vacabSet)
#统计inputSet的单词是否出现在vocabList中,返回向量(值只为0或1)
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1
else:
print("the word :%s is not in my Vocabulary!"%word)
return returnVec
训练算法:从词向量计算概率
朴素贝叶斯分类器训练函数
#文档矩阵trainMatrix,以及由每篇文档类别标签所构成的向量trainCategory
def trainNB0(trainMatrix,trainCategory):
numTrainDocs=len(trainMatrix)#获取文档矩阵中有几篇文档
numWords=len(trainMatrix[0])#获取第一篇文档的单词长度
pAbusive=sum(trainCategory)/float(numTrainDocs)#sum() 方法对序列进行求和计算。 类别为1的样本数量/样本总数
#numpy.zeros()的作用:通常是把数组转换成想要的矩阵
# p0Num=zeros(numWords)
# p1Num=zeros(numWords)
# p0Denom = 0.0
# p1Denom = 0.0
# 利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概
# 率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也为0。为降低
# 这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2
p0Num = ones(numWords)
p1Num = 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])
# 当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案
# 一种解决办法是对乘积取自然对数。在代数中有ln(a * b) = ln(a) + ln(b),于是通过求对数可以
# 避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
# p1Vect=p1Num/p1Denom
# p0Vect=p0Num/p0Denom
p1Vect = log(p1Num / p1Denom)
p0Vect=log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
关于 # p1Vect=p1Num/p1Denom
# p0Vect=p0Num/p0Denom
p1Vect = log(p1Num / p1Denom)
p0Vect=log(p0Num/p0Denom)
测试算法:根据现实情况修改分类器
def classifierNB(vec2Classify,p0Vec,p1Vec,pClass1):
#这里的相乘是指对应元素相乘,即先将两个向量中的第1个元素相乘,然后将第2个元素相乘,以此类推
p1=sum(vec2Classify*p1Vec)+log(pClass1)
p0=sum(vec2Classify*p0Vec)+log((1-pClass1))
if p1>p0:
return 1
else:
return 0
def testingNB():
listOPosts,listClasses=loadDataSet()
myVocabList=createVocabList(listOPosts)
trainMat=[]
#生成值只为0或者1的矩阵
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
p0V,p1V,pAb=trainNB0(trainMat,listClasses)
testEntry=['love','my','dalmation']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
print(testEntry,'classified as :',classifierNB(thisDoc,p0V,p1V,pAb))
testEntry=['stupid','garbage']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
print(testEntry, 'classified as :', classifierNB(thisDoc, p0V, p1V, pAb))
testingNB()
测试结果
准备数据:文档词袋模型
目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型( set-of-words
model)。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表
达的某种信息,这种方法被称为词袋模型( bag-of-words model)。在词袋中,每个单词可以出现
多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数setOfWords2Vec()
稍加修改,修改后的函数称为bagOfWords2Vec()。
下面的程序清单给出了基于词袋模型的朴素贝叶斯代码。它与函数setOfWords2Vec()几乎
完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的
数值设为1。
朴素贝叶斯词袋模型
#朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList,inpustSet):
returnVec=[0]*len(vocabList)
for word in len(vocabList):
if word in vocabList:
returnVec[vocabList.index(word)]+=1
return returnVec