朴素贝叶斯(naive bayes)
主要參考资料:《机器学习实战》《统计学习方法》
1.朴素贝叶斯分类原理
朴素贝叶斯法是基于贝叶斯定理和特征条件独立假设(称为朴素的原因)的分类方法。先看看维基百科中贝叶斯定理的描写叙述:
贝叶斯定理(维基百科)
通常,事件A在事件B(发生)的条件下的概率。与事件B在事件A的条件下的概率是不一样的。然而,这两者是有确定的关系,贝叶斯定理就是这样的关系的陈述。
公式描写叙述例如以下:
P(A|B)=P(B|A)P(A)P(B)
当中P(A|B) 是在B发生的情况下A发生的可能性。
在机器学习中,经常常使用事件A表示属于某个类别,事件B表示特征条件的集合。下面图作为样例解说:
图中共同拥有两类点。记为
- 假设
P(c1|x,y)>P(c2|x,y) 。则断定该点属于c1 - 假设
P(c1|x,y)<P(c2|x,y) 。则断定该点属于c2
假设用
又由于假设了特征条件独立。即
分母同样,仅仅须要比較分子就可以。朴素贝叶斯算法训练的目的就是得到训练集中
2.朴素贝叶斯实现
2.1准备数据
由于是简单的演示样例,直接创建训练集和类标签向量:
# 训练集:留言板的中的留言
def create_data_set():
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 create_vocablist(dataSet):
vocabSet = set([]) #create empty set
for document in dataSet:
vocabSet = vocabSet | set(document) #取并集
return list(vocabSet)
有了单词表就能够将语句转化为频率矢量,首先将单词表全部单词的频率初始化为0
。然后遍历每一条语句。将出现的单词频率置为1
(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
利用上面生成的数据集生成一个单词表,结果例如以下:
将语句”I buying cute dog”转化为向量例如以下:
2.2训练算法
先看看训练的目标參数:
训练函数传入的參数有两个。各自是语句集合转化的单词表向量集合和类标签向量。首先依据类标签列表能够计算出侮辱性语句和非侮辱性语句的频率。然后再计算各个特征的条件概率。
计算特征的条件概率时使用了numpy
中的矢量运算。极大的简化了程序的编写。先定义2个空的单词表矢量,用来统计侮辱性单词和非侮辱性单词的频数,再定义2个浮点型变量。用来统计侮辱性单词和非侮辱性单词的总数。遍历单词表向量集合。依据每一个向量的类别,统计侮辱性和非侮辱性单词的频数与总数。最后,将频数除以总数,就能够得到侮辱性或非侮辱性条件下某单词的出现的概率。
#朴素贝叶斯训练函数,输入为全部文档的单词频率向量集合。类标签向量
def trainNB(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) #文档数量
numWords = len(trainMatrix[0]) #单词表长度
pAbusive = sum(trainCategory)/float(numTrainDocs) #侮辱性词语的频率
p0Num = zeros(numWords); p1Num = zeros(numWords) #分子初始化为0
p0Denom = 0.0; p1Denom = 0.0 #分母初始化为0
for i in range(numTrainDocs):
if trainCategory[i] == 1: #假设是侮辱性语句
p1Num += trainMatrix[i] #矢量相加,将侮辱性语句中出现的词语频率全部加1
p1Denom += sum(trainMatrix[i]) #屈辱性词语的总量也添加
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = p1Num/p1Denom #对每一个元素做除法
p0Vect = p0Num/p0Denom
return p0Vect,p1Vect,pAbusive #返回全部词语非侮辱性词语中的频率。全部词语在侮辱性词语中的频率。侮辱性语句的频率
可是这段程序有两个问题,问题一为可能发生下溢出,问题二为某语句的某个单词在侮辱性或非侮辱性集合中的频率为0,这会导致连乘的积为0.
首先解决这个问题一:
频率都是非常小的小数,这些小数连乘的结果终于也是非常小非常小的小数,在计算机中会发生下溢出或者为0,总之都不是正确的结果。
较为常见的解决方法是使用对数。将连乘变为连加。对应的程序改动例如以下:
p1Vect = log(p1Num/p1Denom) #变为对数,防止下溢出;对每一个元素做除法
p0Vect = log(p0Num/p0Denom)
然后解决这个问题二:
能够通过分子全部初始化为1,分母初始化为2解决该问题,这样的方法在统计学中叫做贝叶斯预计。
条件概率
我们原先就是依照上面的公式计算
当中
在我们所讨论的问题中。
p0Num = ones(numWords); p1Num = ones(numWords) #分子初始化为1
p0Denom = 2.0; p1Denom = 2.0 #分母初始化为2
2.3測试算法
依据下面准则编写分类函数:
- 假设
P(c0|x,y)>P(c1|x,y) ,则断定为非侮辱性语句 - 假设
P(c0|x,y)<P(c1|x,y) ,则断定为侮辱性语句
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #矢量相乘求出概率。log相加
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
然后编写測试函数,进行測试,须要注意的是python中的列表不提供矢量运算,要想进行矢量运算,就要将列表转化为numpy
中的array
或者mat
。
def testingNB():
listOPosts,listClasses = create_data_set()
myVocabList = create_vocablist(listOPosts)
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB(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)
执行结果例如以下: