引言:
朴素贝叶斯(naïve Bayes)法是基于贝叶斯定理与特征条件独立假设的分类方法。在统计学中,相互独立的含义是它们中一个已发生,不影响另一个发生的概率,即特征条件独立指一个特征出现的可能性与它的相邻没有关系。
基本思想如下:
假设训练集标记类别集合为{c1,c2},P(c1|x1,x2)为给定数据点(x1,x2)来自类别c1的概率;P(c2|x1,x2)为给定数据点(x1,x2)来自类别c2的概率。若P(c1|x1,x2) > P(c2|x1,x2),数据点属于c1;若P(c1|x1,x2) < P(c2|x1,x2),数据点属于c2。
具体地可利用贝叶斯定理计算概率值。运算中,利用了特征条件独立的假设,由于这是一个较强的假设,使整个形式化过程变得原始简单,“朴素”由此得名。
一、数学预备知识
1.条件概率
设A,B是两个事件,且P(A)>0,称
2.乘法公式
P(A)>0,则有
3.全概率公式
设试验E的样本空间为S,A为E的事件,B1,B2,…,Bn为S的一个划分(即BiBj=∅,i≠j,i,j=1,2,…,n,且P{B1∪B2∪…∪Bn}=1),且P(Bi)>0(i=1,2,…,n),则
4.贝叶斯公式
设试验E的样本空间为S,A为E的事件,B1,B2,…,Bn为S的一个划分(即BiBj=∅,i≠j,i,j=1,2,…,n,且P{B1∪B2∪…∪Bn}=1),且P(A)>0,P(Bi)>0(i=1,2,…,n),则
5.独立的定义
设A,B是两事件,如果满足等式
二、朴素贝叶斯分类器
设输入空间X是n维实数向量空间
,输出空间为标记类别集合
。训练集为:
设测试实例为 ,其中 表示测试实例的第i个特征。计算 ,k=1,2,…,K,即得测试实例x属于 的概率,在K个概率中选择概率最大的,所对应的 则为测试实例x所属类别。
将上式条件代入贝叶斯公式可得:
1.条件概率
根据定义计算条件概率
:
2.朴素贝叶斯公式
将(2)式代入(1),可得朴素贝叶斯公式:
3.朴素贝叶斯分类器
x所属类别为能使(3)使取得最大值的
,于是,朴素贝叶斯分类器可表示为:
三、朴素贝叶斯的参数估计(未完,待续)
四、代码实现(python)
以下代码来自Peter Harrington《Machine Learing in Action》
本例利用朴素贝叶斯对文档进行分类。
以下文档数据来自斑点犬爱好者留言板。我们将文档看成单词向量,也就是说将句子转换为向量,每个单词对应一个特征。每条文档对应一个类别,本例类别为0和1,分别代表正常言论和侮辱性文字。
根据贝叶斯公式,可得测试实例类别为
的概率如下:
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 ,如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。
另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。一种解决办法是对乘积取自然对数,在代数中有 ,于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。则最终需对比的数值为log(P(0))与log(P(1)),求法如下:
# -- coding: utf-8 --
from numpy import *
def loadDataSet():
# 创建单词向量及对应的分类,1代表侮辱性文字,0代表正常言论
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]
return postingList,classVec
def createVocabList(dataSet): # 创建一个过滤dataSet重复数据的表
vocabSet = set([]) # 创建一个空集
for document in dataSet:
vocabSet = vocabSet | set(document) # 创建两个集合的并集
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet): # 将文档转换成特征向量
returnVec = [0]*len(vocabList) # 创建一个长度与不重复词表一样的一维数组,元素默认为0
for word in inputSet:
if word in vocabList: # 若词表单词在文档中出现过,则将元素改为1
returnVec[vocabList.index(word)] = 1
else: print "the word: %s is not in my Vocabulary!" % word
return returnVec
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) # 计算训练样本数量
numWords = len(trainMatrix[0]) # 计算不重复词表中单词数量
pAbusive = sum(trainCategory)/float(numTrainDocs) # 类别为1的训练样本的概率,即P(Y=c1)
# 创建一个长度与不重复词表一样的一维数组,计算各单词出现次数,初始化为1
p0Num = ones(numWords); p1Num = ones(numWords)
p0Denom = 2.0; p1Denom = 2.0 # 将分母(所有单词量)初始化为2
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1Num += trainMatrix[i] # 若类别为1,将相应样本列相加,得该单词在全部文档中出现次数
p1Denom += sum(trainMatrix[i]) # 计算类别为1的样本的所有单词量
else:
p0Num += trainMatrix[i] # 若类别为0,将相应样本列相加,得该单词在全部文档中出现次数
p0Denom += sum(trainMatrix[i]) # 计算类别为0的样本的所有单词量
# 在类别为1的条件下,各单词在文档中出现的概率,并求其对数,即log(P(x=xi|Y=c1))
p1Vect = log(p1Num/p1Denom)
# 在类别为0的条件下,各单词在文档中出现的概率,并求其对数,即log(P(x=xi|Y=c0))
p0Vect = log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
# 假设传入的测试样本特征为第1,3,4个
# 则vec2Classify * p0Vec表示为log(P(x=x1|Y=c0))+log(P(x=x3|Y=c0))+log(P(x=x4|Y=c0))
# 则vec2Classify * p1Vec表示为log(P(x=x1|Y=c1))+log(P(x=x3|Y=c1))+log(P(x=x4|Y=c1))
# p1=log(P(x=x1|Y=c1))+...+log(P(x=xn|Y=c1))+log(P(Y=c1))
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
# p0=log(P(x=x1|Y=c0))+...+log(P(x=xn|Y=c0))+log(P(Y=c0))
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
# 对比p1和p0的大小,大的对应的值及为最终的分类结果
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts,listClasses = loadDataSet() # 获取单词向量及对应分类
myVocabList = createVocabList(listOPosts) # 获取不重复的词表(此时假设每个特征同等重要)
trainMat=[]
for postinDoc in listOPosts:
# 为每个单词构建一个特征
# 输入某文档,输出文档向量,向量为1或0,分别表示词表myVocabList中的单词在输入文档是否出现
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)
运行命令如下:
上述命令行为实际效果,下面通过执行部分函数深入了解:
listOPosts为单词集合,listClasses为所属类别。
通过createVocabList()函数,可获得不重复词表myVocabList。
再利用setOfWords2Vec()函数,得到最终构造成的特征向量trainMat。具体可看trainMat[0],对应的是listOPosts[0],以listOPosts[0]中的单词help为例,help出现在myVocabList的第3个位置,则trainMat[0][2]=1。
以上全部内容参考书籍如下:
李航《统计学习方法》
Peter Harrington《Machine Learing in Action》
《概率论与数理统计》高等教育出版社