朴素贝叶斯是一种简单的分类算法,称它“朴素”是因为,整个形式化过程只做最原始 最简单的假设。朴素贝叶斯的核心思想是:对于待分类项,求解此待分类项在各个类别中出现的概率,哪个类别概率最大,则认为此待分类项就属于那个类别。
朴素贝叶斯是贝叶斯决策理论的一部分
1 朴素贝叶斯原理
1.1 概率论知识
既然朴素贝叶斯是求概率,首先介绍概率论的知识。假设\(X\)和\(Y\)相互独立,则有条件独立公式:
\[P(X,Y)=P(X)P(Y) \]
条件概率公式:
\[P(Y|X)=\frac{P(X,Y)}{P(X)} \]
\[P(X|Y)=\frac{P(X,Y)}{P(Y)} \]
全概率公式:
\[P(X)=\sum_{k}P(X|Y=Y_{k})P(Y_{k}) \]
其中,\(\sum_{k}P(Y_{k})=1\)。
1.2 贝叶斯定理
朴素贝叶斯是贝叶斯决策理论的一部分,讲朴素贝叶斯前,有必要了解一下贝叶斯决策理论。贝叶斯理论解决的是这样的问题:已知某条件概率,如何得到两个事件交换后的概率呢?也就是,已知\(P(X|Y)\)的情况下如何求得\(P(Y|X)\)?于是,提出贝叶斯定理:
\[P(Y|X)=\frac{P(X|Y)P(Y)}{P(X)} \]
\[P(Y_{k}|X)=\frac{P(X|Y_{k})P(Y_{k})}{\sum_{k}P(X|Y=Y_{k})P(Y_{k})} \]
2 朴素贝叶斯算法
输入:训练集有\(m\)个样本,每个样本有\(n\)个特征,共有\(K\)个特征输出类别,训练集表示为:\(T=\left \{ \left ( x_{1},y_{1} \right ),\left ( x_{2},y_{2} \right ),\cdots,\left ( x_{n},y_{n} \right ) \right \},其中\)x_{i}=\left ( x_{i}{(1)},x_{i},\cdots,x_{i}^{n} \right ){T}$,$x_{i}\(是第\)i\(个样本的第\)j\(个特征,\)y\epsilon \left { c_{1},c_{2},\cdots,c_{K} \right }\( **输出:**待测试实例\)x_{(test)}$的分类
算法流程:
step1 计算\(Y\)的\(K\)个先验概率
\[P(Y=c_{k}) \]
step2 计算条件概率
\[P(X=x|Y=c_{k})=P(X^{(1)}=x^{(1)},\cdots, X^{(n)}=x^{(n)}|Y=c_{k}) \]
上式的参数是指数级别,无法计算。所以根据特征条件独立假设,可以化简为下式
\[P(X=x|Y=c_{k})=\prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k}) \]
step3 根据贝叶斯定理,计算后验概率
\[P(Y=c_{k}|X=x)=\frac{P(X=x|Y=c_{k})P(Y=c_{k})}{\sum_{k}P(X=x|Y=c_{k})P(Y=c_{k})} \]
把\(P(X=x|Y=c_{k})=\prod _{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k})\)得到
\[P(Y=c_{k}|X=x)=\frac{\prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k})P(Y=c_{k})}{\sum_{k}\prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k})P(Y=c_{k})} \]
由于分母相同,上式简化为
\[P(Y=c_{k}|X=x)=\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_{k})P(Y=c_{k}) \]
step4 计算\(X_{(test)}\)的类别
\[y_{(test)}=arg max_{c_{k}}\prod_{j=1}^{n} P(X^{(j)}=x^{(j)}_{(test)}|Y=c_{k})P(Y=c_{k}) \]
3 朴素贝叶斯代码实现
#bayes.py
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 #postingList表示进行词条切分后的文档集合 classVec表示类别标签集合
def createVocabList(dataSet): #创建一个包含在所有文档中出现的不重复词的列表(获取词表)
vocabSet = set([]) #创建一个空集 set返回一个不重复词表
for document in dataSet: #将每篇文档返回的新词集合添加到该集合vocabSet中
vocabSet = vocabSet | set(document) #创建两个集合的并集
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet): #参数vocabList代表词汇表 inputSet代表输入文档 函数最后输出文档向量 向量每个元素为1或0 表示词汇表中的单词在输入文档中是否出现
returnVec = [0]*len(vocabList) #创建一个其中所含元素都为0的向量(该向量与词汇表等长)
for word in inputSet: #遍历文档中的所有单词 如果出现词汇表中的单词则将输出的文档向量中的对应值设为1
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec
#训练算法:朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory): #trainMatrix代表文档矩阵 trainCategory代表每篇文档类别标签所构成的向量
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs) #计算文档属于侮辱性文档的概率
#p0Num = zeros(numWords) #初始化概率
#p1Num = zeros(numWords)
#p0Denom = 0.0
#p1Denom = 0.0
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs): #遍历训练集trainMatrix中的所有文档
if trainCategory[i] == 1: #向量相加 某个词语在文档中出现 则该词对应个数p1Num或p0Num加1 同时在所有文档中 该文档的总词数也对应加1
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
#p1Vect = p1Num/p1Denom #对每个元素除以该该类别的总词数 得出在给定文档类别条件下词汇表中单词的出现概率
#p0Vect = p0Num/p0Denom
p1Vect = log(p1Num / p1Denom)
p0Vect = log(p0Num / p0Denom)
return p0Vect,p1Vect,pAbusive #返回两个向量 一个概率
#测试算法:朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): #参数:要分类的向量 三个概率(两个概率向量 一个概率)
p1 = sum(vec2Classify*p1Vec) + log(pClass1)
p0 = sum(vec2Classify*p0Vec) + log(1.0 - pClass1)
if p1>p0:
return 1
else:
return 0
#朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
#封装函数所有操作
def testingNB():
listOPosts,listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
testEntry = ['stupid', 'garbage']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
#文件解析及垃圾邮件测试函数
def textParse(bigSting): #接受一个大字符串并将其解析为字符串列表
import re
listOfTokens = re.split(r'\W*', bigSting)
return [tok.lower() for tok in listOfTokens if len(tok) > 2] #去掉少于两个字符的字符串 并将所有字符串转换为小写
def spamTest(): #对贝叶斯垃圾邮件分类器进行自动化处理
docList=[]; classList = []; fullText =[]
for i in range(1,26): #导入文本文件并将其解析为词列表
wordList = textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i).read())
#print(i)
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)#create vocabulary
trainingSet = list(range(50)); testSet=[] #create test set
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat=[]; trainClasses = []
for docIndex in trainingSet:#train the classifier (get probs) trainNB0
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
errorCount = 0
for docIndex in testSet: #classify the remaining items
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print("classification error",docList[docIndex])
print('the error rate is: ',float(errorCount)/len(testSet))
#运行效果
>>> import bayes
>>> listOPosts, listClasses = bayes.loadDataSet()
>>> myVocaList = bayes.createVocabList(listOPosts)
>>> myVocaList
['garbage', 'ate', 'park', 'stupid', 'dalmation', 'him', 'not', 'how', 'cute', 'posting', 'food', 'has', 'so', 'please', 'love', 'my', 'to', 'steak', 'dog', 'quit', 'licks', 'buying', 'stop', 'take', 'mr', 'I', 'worthless', 'maybe', 'is', 'flea', 'problems', 'help']
>>>bayes.setOfWords2Vec(myVocaList, listOPosts[0])
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
>>>from numpy import *
>>>import imp
>>>import bayes
>>>imp.reload(bayes)
>>> myVocabList = bayes.createVocabList(listOPosts)
>>> trainMat = []
for postinDoc in listOPosts:
... trainMat.append(bayes.setOfWords2Vec(myVocabList, postinDoc))
...
>>> p0V,p1V,pAb = bayes.trainNB0(trainMat, listClasses)
>>> pAb
0.5
>>> p0V
array([ 0. , 0.04166667, 0. , 0. , 0.04166667,
0.08333333, 0. , 0.04166667, 0.04166667, 0. ,
0. , 0.04166667, 0.04166667, 0.04166667, 0.04166667,
0.125 , 0.04166667, 0.04166667, 0.04166667, 0. ,
0.04166667, 0. , 0.04166667, 0. , 0.04166667,
0.04166667, 0. , 0. , 0.04166667, 0.04166667,
0.04166667, 0.04166667])
>>> p1V
array([ 0.05263158, 0. , 0.05263158, 0.15789474, 0. ,
0.05263158, 0.05263158, 0. , 0. , 0.05263158,
0.05263158, 0. , 0. , 0. , 0. ,
0. , 0.05263158, 0. , 0.10526316, 0.05263158,
0. , 0.05263158, 0.05263158, 0.05263158, 0. ,
0. , 0.10526316, 0.05263158, 0. , 0. ,
0. , 0. ])
myVocabList
['garbage', 'ate', 'park', 'stupid', 'dalmation', 'him', 'not', 'how', 'cute', 'posting', 'food', 'has', 'so', 'please', 'love', 'my', 'to', 'steak', 'dog', 'quit', 'licks', 'buying', 'stop', 'take', 'mr', 'I', 'worthless', 'maybe', 'is', 'flea', 'problems', 'help']
imp.reload(bayes)
<module 'bayes' from 'D:\\Python\\Mechine_learning\\Bayes\\bayes.py'>
>>> bayes.testingNB()
['love', 'my', 'dalmation'] classified as: 0
['stupid', 'garbage'] classified as: 1
4 朴素贝叶斯小结
贝叶斯优缺点:
优点
朴素贝叶斯模型有稳定的分类效率。
对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,可以一批批的去增量训练。
对缺失数据不太敏感,算也比较简单,常用于文本分类。
缺点
朴素贝叶斯模型的特征条件独立假设在实际应用中往往是不成立的。
如果样本数据分布不能很好的代表样本空间分布,那先验概率容易测不准。对输入数据的表达形式很敏感。