1.贝叶斯定理
其中𝑃(H│𝑋)表示给定观测样本X,假设H成立时的概率;
𝑃(H│𝑋)是后验概率;(例如:包含特定特征的邮件属于垃圾邮件的先验概率)
𝑃(𝐻)是H的先验概率。(例如:垃圾邮件的先验概率)
𝑃(𝑋)是X的先验概率。
2.条件概率
如果两个事件A和B不是互相独立的,并且知道事件B中的一个事件已经发生,我们就能得到关于P(A)的信息。这反映为A在B中的条件概率,记为P(A|B):
如果A和B是独立的:
条件概率:就是已知一个事件发生的情况下,另外一个事件发生的概率。
贝叶斯定理是一个交换概率的公式;条件概率就是一个概率。
例子
“在已知石头出自B桶的条件下, 取出灰色石头的概率”?
这个概率可以记作P(gray|bucketB) ,这就是所谓的条件概率(计算从B桶取到灰色石头的概率) 。
就是已知一个事件发生的情况下,另外一个事件发生的概率
P(gray|bucketB)=1/3
3.朴素贝叶斯分类器
3.1 朴素贝叶斯分类器的由来
要得到好的分类器,就需要足够的数据样本 。由统计学知,如果每个特征需要N个样本,那么对于10个特征将需要𝑁^10个样本, 对于包含1000个特征的词汇表将需要 𝑁 ^1000个样本。可以看到,所需要的样本数会随着特征数目增大而迅速增长。如果特征之间相互独立,那么样本数就可以从𝑁 ^1000减少到1000×N。
所谓独立(independence),指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。举个例子讲,假设单词bacon出现在unhealthy后面与出现在delicious后面的概率相同。当然,我们知道这种假设并不正确, bacon常常出现delicious附近,而很少出现在unhealthy附近,这个假设正是朴素贝叶斯分类器中朴(naive)一词的含义 。
总结,就是一句话表达什么意思我们不关心,我们只关心出现那些词汇。
尽管上述假设存在一些小的瑕疵,但朴素贝叶斯的实际效果却很好。
3.2 用于解决那些问题?
新闻分类
评论分析-好评还是差评
邮件分类
3.3 理论支撑是贝叶斯定理
3.4 例子
(简单的)垃圾邮件分类问题
邮件:总体100,正常70,垃圾30.
“办证”在正常邮件中的出现10次,在垃圾邮件中出现25次。
现在有一个问题,当一个邮件包含”办证”这个词,它属于垃圾邮件的概率?
假设X为”办证”,H为垃圾邮件,则P(H|X),即为我们所要求的概率(当一个邮件包含”办证”这个词,它属于垃圾邮件的概率)?
我们就可以借助贝叶斯定理:𝑃(𝐻│𝑋)=(𝑃(𝑋|𝐻)𝑃(𝐻))/(𝑃(𝑋))
因为P(X|H)=25/30=5/6,P(H)=30/100=3/10,p(x)=35/100=7/20
𝑃(𝐻│𝑋)=(𝑃(𝑋|𝐻)𝑃(𝐻))/(𝑃(𝑋))=(5/6×3/10)/(7/20)=5/7
还是刚刚的垃圾邮件分类问题,我们现在考虑的不是当一份邮件包含“办证”的时候它属于垃圾邮件的概率(P(H|X)),现在我们考虑的是当一份邮件包含“办证”、“理财”, “投资”的时候属于垃圾邮件的概率。也就是考虑的特征不止一个的时候,朴素贝叶斯就显得非常重要;
假设:假设X1为“办证”,X2为“投资”,X3为“理财”,H为垃圾邮件,那么我们刚刚所要求的概率即为:
推广到n个特征:
注:当A和B相互独立时,𝑃(𝐴𝐵)=𝑃(𝐴)𝑃(𝐵)
3.5 朴素贝叶斯分类器的实现方式(特征模型)
3.5.1 贝(伯)努利模型(常见)
(1)适用数据类型
离散值。
(2)模型介绍
该模型在有些书中也被称为词集模型。其与多项式模型类似,不过该模型中,每个特征取值只能为1或0,表示出现与否(对于文本分类而言,1表示某个单词出现在该文本中,0则表示没有出现)
该实现方式中并不考虑词在文档中出现的次数,只考虑出不出现,因此在这个意义上相当于假设词是等权重的。
(3)例如:“代开发票,增值税发票,正规发票”
分词后的向量:
(“代开”,“发票”,“增值税”,“正规”)
重复的词我们将其视为出现一次
3.5.2 多项式模型(常见)
(1)适用数据类型
离散值。即对应的特征为离散的。比如性别(取值为男、女)、学历(小学、高中、本科、专科、硕士、博士、博士后)。
(2)模型介绍
该模型在一些书中也称为词袋模型。在词袋中,每个单词可以出现多次 ,不仅考虑它是否出现,而且考虑出现的次数(因为出现次数可能和其重要性相关)。
(3)例如:“代开发票,增值税发票,正规发票”
分词后的向量:
(“代开”,“发票”,“增值税”,“发票”,“正规”,“发票”)
重复的词我们将其视为出现多次。
3.5.3 高斯模型
(1))适用数据类型
连续型。比如身高等。
(2)模型介绍
高斯模型假设每一维特征都服从高斯分布(正态分布):
(3)举例:有些特征可能是连续性变量。比如说人的身高,物体的长度,这些特征可以转化为离散型的值。
比如身高在160cm以下的,特征值为1;在160cm和170cm之间,特征值为2;在170cm之上,特征值为3。
也可以这样转换,将身高转化为3个特征,分别为f1,f2,f3如果身高在160cm以下,这3个特征的值分别为1、0、0,在170cm之上,这3个特征的值分别为0、0、1。
4.案列 社区言论(区分善评,还是恶评)
4.1 理论基础
假设有一个数据集,由两类组成(简化问题),对于每个样本的分类,我们都已经知晓。
现在出现一个新的点new_point (x,y),其分类未知。
我们可以用p1(x,y)表示数据点(x,y)属于红色一类的概率,同时也可以用p2(x,y)表示数据点(x,y)属于蓝色一类的概率。
那要把new_point归在红、蓝哪一类呢?
我们提出这样的规则:
如果p1(x,y) > p2(x,y),则(x,y)为红色一类。
如果p1(x,y) <p2(x,y), 则(x,y)为蓝色一类。
换人类的语言来描述这一规则:选择概率高的一类作为新点的分类。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。
4.2 思路
4.2.1 找到特征属性
现在有6条留言,作为训练样本。经过处理,我们得到6条留言的词汇如下:
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‘]]
注:假设X1,X2,….Xn表示特征,H表示恶意留言,O表示善意留言。则关键是求
𝑃(H│𝑋1,𝑋2……𝑋𝑛)和𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛)
如果𝑃(H│𝑋1,𝑋2……𝑋𝑛)>𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛),则属于恶意留言。
如果𝑃(H│𝑋1,𝑋2……𝑋𝑛)<𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛),则属于善意留言。
我们找到的特征其实就是样本点包含的所有词汇,如下:
[‘please’, ‘mr’, ‘stop’, ‘love’, ‘how’, ‘park’, ‘quit’, ‘problems’, ‘to’, ‘so’, ‘I’, ‘stupid’, ‘buying’, ‘is’, ‘dalmation’, ‘cute’, ‘dog’, ‘ate’, ‘worthless’, ‘garbage’, ‘maybe’, ‘steak’, ‘has’, ‘help’, ‘him’, ‘food’, ‘not’, ‘take’, ‘flea’, ‘posting’, ‘licks’, ‘my’]
4.2.2 找到特征属性值
找到样本的特征属性之后,我们就可以确定每条留言的特征属性值(词汇向量)。
[[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], [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, 0, 0, 0, 1, 0, 0], [0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]]
4.2.3 每个词汇在每种类别出现的概率
n个特征X1,X2,…….Xn:
𝑃(𝐻𝑖│𝑋1,𝑋2……𝑋𝑛)=(𝑃(𝑋1,𝑋2…𝑋𝑛|𝐻)𝑃(𝐻))/(𝑃(𝑋1,𝑋2…𝑋𝑛))=(𝑃(𝑋1│𝐻)𝑃(𝑋2│𝐻)…𝑃(𝑋𝑛|𝐻)𝑃(𝐻𝑖))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
那么现在关键点就是求出来
𝑃(𝑋1│𝐻),𝑃(𝑋2│𝐻),…𝑃(𝑋𝑛|𝐻)
例如:
x1为”please”,H为恶意留言,则𝑃(X1│𝐻)=X1在恶意留言出现的总次数/恶意留言总的词汇数。
4.2.4 更改初始值,取对数
我们用H代表恶意留言,O代表善意留言
𝑃(𝑋1│𝐻),𝑃(𝑋2│𝐻),…𝑃(𝑋𝑛|𝐻),𝑃(𝑋1│O),𝑃(𝑋2│𝑂),…𝑃(𝑋𝑛|𝑂)
𝑃(𝐻│𝑋1,𝑋2……𝑋𝑛)=(𝑃(𝑋1│𝐻)𝑃(𝑋2│𝐻)…𝑃(𝑋𝑛|𝐻)𝑃(𝐻))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛)=(𝑃(𝑋1│𝑂)𝑃(𝑋2│𝑂)…𝑃(𝑋𝑛|𝑂)𝑃(𝑂))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
从上图可以看出,在计算的时候已经出现了概率为0的情况。如果新实例文本,包含这种概率为0的分词,那么最终的文本属于某个类别的概率也就是0了。显然,这样是不合理的,为了降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。这种做法就叫做拉普拉斯平滑(Laplace Smoothing)又被称为加1平滑,是比较常用的平滑方法,它就是为了解决0概率问题。
除此之外,另外一个遇到的问题就是下溢出,这是由于太多很小的数相乘造成的。学过数学的人都知道,两个小数相乘,越乘越小,这样就造成了下溢出。在程序中,在相应小数位置进行四舍五入,计算结果可能就变成0了。为了解决这个问题,对乘积结果取自然对数。通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
对数转化
𝑃(𝑋1│𝐻),𝑃(𝑋2│𝐻),…𝑃(𝑋𝑛|𝐻),𝑃(𝑋1│O),𝑃(𝑋2│𝑂),…𝑃(𝑋𝑛|𝑂)
𝑃(𝐻│𝑋1,𝑋2……𝑋𝑛)=(𝑃(𝑋1│𝐻)𝑃(𝑋2│𝐻)…𝑃(𝑋𝑛|𝐻)𝑃(𝐻))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛)=(𝑃(𝑋1│𝑂)𝑃(𝑋2│𝑂)…𝑃(𝑋𝑛|𝑂)𝑃(𝑂))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
比如上面的恶意留言筛选问题,如果𝑃(𝐻│𝑋1,𝑋2……𝑋𝑛)>𝑃(𝑂│𝑋1,𝑋2……𝑋𝑛),则我们判断当一个留言包含X1,x2,…Xn时,我们把它归类为恶意留言,否则就是善意留言。
经过对数转化,我们比较的结果即为:
log(𝑃(𝐻│𝑋1,𝑋2……𝑋𝑛))=𝑙𝑜𝑔{(𝑃(𝑋1│𝐻)𝑃(𝑋2│𝐻)…𝑃(𝑋𝑛│𝐻)𝑃(𝐻))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛) )}=log(𝑃(𝑋1│𝐻))+log(𝑃(𝑋2│𝐻))+…+log(𝑃(𝑋𝑛│𝐻))+log(𝑃(𝐻))−𝑀
其中 M=log(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛))
4.2.5 判断类别
log(𝑃(𝐻│𝑋1,𝑋2……𝑋𝑛))=𝑙𝑜𝑔{(𝑃(𝑋1│𝐻)𝑃(𝑋2│𝐻)…𝑃(𝑋𝑛│𝐻)𝑃(𝐻))/(𝑃(𝑋1)𝑃(𝑋2)…𝑃(𝑋𝑛) )}=log(𝑃(𝑋1│𝐻))+log(𝑃(𝑋2│𝐻))+…+log(𝑃(𝑋𝑛│𝐻))+log(𝑃(𝐻))−𝑀=𝐿−𝑀
经过上述处理,我们最终比较的是N和L的值;其中
log(𝑃(𝑋1│𝑂))+log(𝑃(𝑋2│𝑂))+…+log(𝑃(𝑋𝑛│𝑂))+log(𝑃(𝑂))=𝑁
log(𝑃(𝑋1│𝐻))+log(𝑃(𝑋2│𝐻))+…+log(𝑃(𝑋𝑛│𝐻))+log(𝑃(𝐻))=𝐿
4.3 代码实现
4.3.1 找到所有的特征(所有言论总词汇和标签)
#找到所有的特征(所有言论的总词汇)
import numpy as np
def loadDataSet():
postingList=[['my','dog','has','flea','problems','help','please'],
['maybe','not','take','him','to','dog','park','stupid'],
['stop','dalmation','is','so','cute','I','love','him'],
['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
listOPosts,listclasses=loadDataSet()
print('言论内容:',listOPosts)
def createVocaList(dataSet):
#创建空的set集合,set集合无序不重复的元素,支持交并补差集合运算
vocabSet=set([])
for document in dataSet:
vocabSet=vocabSet|set(document)#\并集,把重复的元素过滤
return list(vocabSet)#列表有索引,set集合没有
MyvocabList=crateVocalList(listOPosts)
print('特征属性:',MyvocabList)
运行结果:
4.3.2 找到每条言论对应的词汇向量,每一条词汇向量是一个列表的形式
#找到每条言论对应的词汇向量,每一条词汇向量是一个列表的形式
def setofwords2vec(feature,inputset):
returnVec=[0]*len(feature)
# print(returnVec)
for word in inputset:
#print(word)
if word in feature:
returnVec[feature.index(word)] = 1
else:
print('the word:%s is not in myvocabList'%word)
return returnVec
trainMatrix=[]
for postinDoc in listPosts:
trainMatrix.append(setofwords2vec(MyvocabList,postinDoc))
print(trainMatrix)
运行结果:
4.3.3 根据词汇向量,求出每个特征在每种标签对应的概率
#根据词汇向量,求出每个特征在每种标签对应的概率
def trainNBO(wordSetVec,labels):
numDataSet=len(wordSetVec)#样本数
pAbusive =sum(labels)/numDataSet
featureNum=len(wordSetVec[0])#特征数
#初始化零(行)向量,存放每个特征在每种标签下出现的次数,0表示善意的言论
p0Num=np.ones(featureNum);p1Num=np.ones (featureNum)
#初始化分母,分母代表每种标签里面的单词数
p0Denom=2;p1Denom=2
for i in range(numDataSet):
if labels[i]==1:
p1Num += wordSetVec[i]#对应位置上相加,累加即可求得每个特征出现的次数
p1Denom += sum(wordSetVec[i])#累加即可求得1标签下所有的单词数
else:
p0Num+= wordSetVec[i]#对应位置上相加,累加即可求得每个特征出现的次数
p0Denom+=sum(wordSetVec[i])#累加即可求得1标签下所有的单词数
p0Vec=np.log(p0Num/p0Denom) #求出每个特征在0类别下的概率
p1Vec=np.log(p1Num/p1Denom) #求出每个特征在0类别下的概率
return p0Vec,p1Vec,pAbusive
p0,p1,pA=trainNBO(trainMatrix,listclasses)
print('p0:',p0)
print('p1:',p1)
print('pA:',pA)
运行结果:
4.3.4 由上面的概率值,我们定义朴素贝叶斯分类
#由上面的概率值,我们定义朴素贝叶斯分类
def NBC(TestWordVec,p0Vec,p1Vec,pclasses):
p1=sum(TestWordVec*p1Vec)+np.log(pclasses)
p0=sum(TestWordVec*p1Vec)+np.log(1-pclasses)
if p1>p0:
return "1标签"
if p1<p0:
return "0标签"
4.3.5 测试算法
#测试算法
def testingNBC():
listOPosts,listclasses=loadDataSet()
print('词汇列表:',listOPosts)
MyvocabList=createVocaList(listOPosts)
print('特征属性:',MyvocabList)
trainMatrix=[]
for postinDoc in listOPosts:
# print(postinDoc)
trainMatrix.append(setofwords2vec(MyvocabList,postinDoc))
print('特征向量:',trainMatrix)
p0,p1,pA=trainNBO(trainMatrix,listclasses)
print('概率:',p0,p1,pA)
# testEntry=['love','my','dalmation']
# testVec=setofwords2vec(MyvocabList,testEntry)
# print('分类器分类的类别为:',NBC(testVec,p0,p1,pA))
testEntry1=['stupid','garbage']
testVec1=setofwords2vec(MyvocabList,testEntry1)
print('分类器分类的类别为:',NBC(testVec1,p0,p1,pA))
testingNBC()
运行结果:
5.使用sklearn模块的朴素贝叶斯分类器
5.1 导入相关模块、包和数据集
import numpy as np
from sklearn import datasets#训练集
from sklearn.model_selection import train_test_split#划分测试集,训练集
# from sklearn.metrics import classification_report,confusion_matrix
#性能指标(Metrics)是衡量一个模型好坏的关键
#sklearn中的classification_report函数用于显示主要分类指标的文本报告
from sklearn.naive_bayes import MultinomialNB,BernoulliNB,GaussianNB
#高斯模型主要用于处理连续性数据;前两种模型主要用于处理离散型数据
from sklearn.metrics import accuracy_score
5.2 载入数据集
#载入数据集
iris = datasets.load_iris()
print(type(iris))
print('特征值:',iris.data)
运行结果:
5.3 划分数据、建立模型、训练模型
这里采用高斯模型
x_train,x_test,y_train,y_test = train_test_split(iris.data,iris.target)#划分测试集,训练集
print(len(x_train))
print(len(x_test))
# mul_nb = MultinomialNB()
# mul_nb = BernoulliNB()
mul_nb = GaussianNB()
mul_nb.fit(x_train,y_train)
prediction = mul_nb.predict(x_test)
accuracy_score(prediction,y_test)#accuracy=分对的样本数/总的测试的样本数
运行 结果: