朴素贝叶斯(Naive Bayesian)是最为普遍使用的分类方法,它以几率论为基础,是基于贝叶斯定理和特征条件独立假设的分类方法。
git
朴素贝叶斯(Naive Bayesian)是基于贝叶斯定理和特征条件独立假设原则的分类方法。经过给出的特征计算分类的几率,选取几率大的状况进行分类。也是基于几率论的一种机器学习分类方法。分类目标肯定,属于监督学习。github
经过几率来衡量事件发生的可能性。几率论和统计学刚好是两个相反的概念,统计学是抽取部分样本进行统计来估算整体的状况,而几率论是经过整体状况来估计单个事件或者部分事情的发生状况。所以,几率论须要已知的数据去预测未知的事件。
例如,咱们看到天气乌云密布,电闪雷鸣并阵阵狂风,在这样的天气特征(F)下,咱们推断下雨的几率比不下雨的几率大,也就是p(下雨)>p(不下雨),因此认为待会儿会下雨。这个从经验上看对几率进行判断。
而气象局经过多年长期积累的数据,通过计算,今天下雨的几率p(下雨)=85%,p(不下雨)=15%,一样的,p(下雨)>p(不下雨),所以今天的天气预报确定预报下雨。这是经过必定的方法计算几率从而对下雨事件进行判断。
复制代码
为何叫朴素贝叶斯:简单,易于操做,基于特征独立性假设,也即各个特征彼此独立,互相不影响发生。算法
某个事件已发生的状况下另一个事件发生的几率。计算公式以下:P(A|B)=P(A∩B) / P(B) 简单理解:画维恩图,两个圆圈相交的部分就是A发生B也发生了,由于求的是B发生下A发生的几率。B至关于一个新的样本空间。AB/B便可。bash
几率相乘法则:P(A∩B)=P(A)P(B|A) or P(A∩B)=P(B)P(A|B) 独立事件的几率:P(A∩B)=P(A)P(B)app
若是有穷k个互斥事件,B1, B2,,,Bk 而且 P(B1)+P(B2)+⋅⋅⋅+P(Bk)=1和一个能够观测到的事件A,那么有: less
基于几率论,二分类问题以下: 若是p1 > p2, 分入类别1; 不然分入类别2。机器学习
其次,贝叶斯定理,有 p(ci|x,y) = p(x,y|ci) * p(ci) / p(x,y)
x, y 表示特征变量,以下例子中的单词。Ci表示类别。p(ci | x, y) 即表示在特征x, y出现的状况下,分入类别Ci的几率。结合如上: p(ci | x, y) > p(cj | x, y), 分入类别i, 不然分入类别j。ide
贝叶斯定理最大的好处是能够用已知的三个几率去计算未知的几率,而若是仅仅是为了比较p(ci|x,y)和p(cj|x,y)的大小,只须要已知两个几率便可,分母相同,比较p(x,y|ci)p(ci)和p(x,y|cj)p(cj)便可。
函数
朴素贝叶斯经常使用与对文档分类。根据文档中出现的词汇,判断文章属于什么类别。将词汇出现的特征条件用词向量W表示,由多个值组成,值的个数和训练集中的词汇表个数相同。 上面的贝叶斯公式能够表示为: p(ci|ω)=p(ω|ci) * p(ci) / p(ω)
各个单词的出现不会相互影响,则p(ω|ci) = p(ω0|ci)*p(ω1|ci)*...* p(ωk|ci)
post
import numpy as np
np.seterr(divide='ignore', invalid='ignore') #消除向量中除以0的警告
# 获取数据
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):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 对输入的词汇表构建词向量
def setOfWords2Vec(vocabList, inputSet):
returnVec = np.zeros(len(vocabList)) #生成零向量的array
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #有单词,该位置填充1
else:
print("the word: %s is not in my Vocabulary" % word)
# pass
return returnVec #返回0,1的向量
if __name__ == '__main__':
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
print(myVocabList)
复制代码
输出结果以下: ['flea', 'ate', 'how', 'licks', 'quit', 'problems', 'dog', 'I', 'garbage', 'help', 'is', 'cute', 'steak', 'to', 'worthless', 'please', 'has', 'posting', 'buying', 'love', 'food', 'so', 'my', 'take', 'dalmation', 'stop', 'park', 'not', 'stupid', 'him', 'mr', 'maybe']
, 表示不一样类别言论去重后获得的词向量。 [ 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
: 表示词聚集1中的单词是否在词向量中出现。
如上,这个方法只记录了每一个词是否出现,并无记录出现次数,成为词集模型。若是记录词出现的次数,这样的词向量构建方法称为词袋模型,以下。本文只使用词集模型。
# 词袋模型
def bagofWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return vocabList #返回非负整数的词向量
复制代码
运用词向量计算几率:
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #文档数目
numWord = len(trainMatrix[0]) #词汇表数目
print(numTrainDocs, numWord)
pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出现侮辱性评论的几率 [0, 1, 0, 1, 0, 1]
p0Num = np.zeros(numWord)
p1Num = np.zeros(numWord)
p0Demon = 0
p1Demon = 0
for i in range(numTrainDocs):
if trainCategory[i] == 0:
p0Num += trainMatrix[i] #向量相加
p0Demon += sum(trainMatrix[i]) #向量中1累加其和
else:
p1Num += trainMatrix[i]
p1Demon += sum(trainMatrix[i])
p0Vec = p0Num / p0Demon
p1Vec = p1Num / p1Demon
return p0Vec, p1Vec, pAbusive
if __name__ == '__main__':
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
trainMat = []
trainMat = []
for postinDoc in listPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print(trainMat)
p0Vec, p1Vec, pAbusive = trainNB0(trainMat, listClasses)
print(p0Vec, p1Vec, pAbusive)
复制代码
输出结果稍微有点多,慢慢来看: trainMat
:表示数据中六个给定的特征在词集模型中的出现状况。
array([ 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 1., 0.,
0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 1., 1.]), array([ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1.,
0., 1., 0., 1., 1., 1., 0., 0., 0., 1., 0., 0., 0.,
0., 0., 1., 0., 0., 0.]), array([ 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
1., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
1., 1., 0., 0., 0., 1.]), array([ 0., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
0., 0., 0., 0., 0., 0.]), array([ 0., 1., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0.,
0., 0., 1., 1., 0., 1.]), array([ 0., 0., 1., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0.,
0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 1.,
0., 0., 0., 0., 0., 0.])]
复制代码
print(numTrainDocs, numWord)
: 6 32 (6个文档,一共32个词汇) print(p0Vec, p1Vec, pAbusive)
:pAbusive是文档中是侮辱性言论的几率,为0.5。 而p0Vec
表示类别0(非侮辱言论)中的词在词向量中出现的几率:
[ 0. 0.04166667 0.04166667 0.04166667 0.04166667 0.
0.08333333 0.04166667 0. 0.04166667 0. 0.04166667
0. 0.04166667 0. 0. 0.04166667 0.04166667
0.04166667 0.04166667 0.04166667 0. 0. 0.04166667
0.04166667 0.04166667 0. 0.125 0. 0.04166667
0.04166667 0.04166667]
复制代码
def trainNB1(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #文档数目
numWord = len(trainMatrix[0]) #词汇表数目
pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出现侮辱性评论的几率
p0Num = np.ones(numWord) #修改成1
p1Num = np.ones(numWord)
p0Demon = 2 #修改成2
p1Demon = 2
for i in range(numTrainDocs):
if trainCategory[i] == 0:
p0Num += trainMatrix[i] #向量相加
p0Demon += sum(trainMatrix[i]) #向量中1累加其和
else:
p1Num += trainMatrix[i]
p1Demon += sum(trainMatrix[i])
p0Vec = np.log(p0Num / p0Demon) #求对数
p1Vec = np.log(p1Num / p1Demon)
return p0Vec, p1Vec, pAbusive
复制代码
注意:这里获得p0Vec多是没有规律的,但其对最后的几率比较没有影响。
def classifyNB(vec2Classify, p0Vc, p1Vc, pClass1):
p1 = sum(vec2Classify * p1Vc) * pClass1
p0 = sum(vec2Classify * p0Vc) * (1-pClass1)
# p1 = sum(vec2Classify * p1Vc) + np.log(pClass1) #取对数,防止结果溢出
# p0 = sum(vec2Classify * p0Vc) + np.log(1 - pClass1)
if p1 > p0:
return 1
else:
return 0
复制代码
解释一下:vec2Classify是所需分类文档的词量。根据公式 p(ci|ω)=p(ω|ci)p(ci) / p(ω)
, 已知特征向量求分类的几率等于 p(ω|ci)p(ci)
。忽略分母:
p(ci)好求,用样本集中,ci的数量/总样本数便可
p(ω|ci)因为各个条件特征相互独立且地位相同,`p(ω|ci)=p(w0|ci)p(w1|ci)p(w2|ci)......p(wN|ci)`,能够分别求p(w0|ci),p(w1|ci),p(w2|ci),......,p(wN|ci),从而获得p(ω|ci)。
而求p(ωk|ci)也就变成了求在分类类别为ci的文档词汇表集合中,单个词项ωk出现的几率。
复制代码
使用两个不一样的样原本测试分类函数:
# 构造样本测试
def testingNB():
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
trainMat = []
for postinDoc in listPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0v, p1v, pAb = trainNB0(trainMat, listClasses)
# print(p0v, p1v, pAb)
testEntry = ['love']
thisDoc = setOfWords2Vec(myVocabList, testEntry)
print(testEntry, 'classified as', classifyNB(thisDoc, p0v, p1v, pAb))
testEntry = ['stupid', 'garbage']
thisDoc = (setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))
if __name__ == '__main__':
testingNB()
复制代码
观察结果,能够看到将两个文档正确的分类。 完整代码请查看: