朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入/输出的联合概率分布;然后基于此模型,对于给定的输入X,利用贝叶斯定理后验概率最大的输出Y。
朴素贝叶斯方法 = 贝叶斯公式 + 条件独立假设(等于是说用于分类的特征在类确定的条件下都是条件独立的)。
贝叶斯公式:
其中P(Y)叫做先验概率,P(Y|X)叫做后验概率,P(Y,X)叫做联合概率;
1)P(“属于某类”|“具有某特征”)=在已知某样本“具有某特征”的条件下,该样本“属于某类”的概率。所以叫做『后验概率』;
2)P(“具有某特征”|“属于某类”)=在已知某样本“属于某类”的条件下,该样本“具有某特征”的概率;
3)P(“属于某类”)=在未知某样本具有该“具有某特征”的条件下,该样本“属于某类”的概率。所以叫做『先验概率』;
4)P(“具有某特征”)=在未知某样本“属于某类”的条件下,该样本“具有某特征”的概率。
由于计算时具有某特征而属于某类无法直接通过统计得到,比如有十个属性而求相应类概率是比较复杂的,但是反过来求对应类会有那些属性概率则可以通过统计得到,因此需要用到上面所说的概率公式将其转换。当然朴素贝叶斯算法有个更加强的假设,就是属性间相互独立,也就是概率可以进行相乘,这也是“朴素”贝叶斯的来源:
当特征属性为连续值时,通常假定其值服从高斯分布(也称正态分布)。
后验概率最大化的含义:等同于期望风险最小化;
后验概率最大化:在给定输入,通过学习到的模型计算后验概率,将后验概率最大的类作为特征的输出类;
假设是解决一个二类问题,选择的是0-1损失函数:
此时优化的目标是期望最小化
期望是对联合分布取得,由此取条件期望
为了使期望最小,只需要对逐个X=x最小,由此得到
这样根据期望最小就得到了后验概率z最大化准则:
即朴素贝叶斯采用的原理。
用python基于朴素贝叶斯实现垃圾邮件过滤,该段代码主要参考机器学习实战;
#---------------------------从文本中构建词条向量-------------------------#1 要从文本中获取特征,需要先拆分文本,这里特征是指来自文本的词条,每个词#条是字符的任意组合。词条可以理解为单词,当然也可以是非单词词条,比如URL#IP地址或者其他任意字符串 # 将文本拆分成词条向量后,将每一个文本片段表示为一个词条向量,值为1表示出现#在文档中,值为0表示词条未出现#导入numpyfrom 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'], ['my','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): #新建一个存放词条的集合 vocabSet=set([]) #遍历文档集合中的每一篇文档 for document in dataSet: #将文档列表转为集合的形式,保证每个词条的唯一性 #然后与vocabSet取并集,向vocabSet中添加没有出现 #的新的词条 vocabSet=vocabSet|set(document) #再将集合转化为列表,便于接下来的处理 return list(vocabSet)#根据词条列表中的词条是否在文档中出现(出现1,未出现0),将文档转化为词条向量 def setOfWords2Vec(vocabSet,inputSet): #新建一个长度为vocabSet的列表,并且各维度元素初始化为0 returnVec=[0]*len(vocabSet) #遍历文档中的每一个词条 for word in inputSet: #如果词条在词条列表中出现 if word in vocabSet: #通过列表获取当前word的索引(下标) #将词条向量中的对应下标的项由0改为1 returnVec[vocabSet.index(word)]=1 else: print('the word: %s is not in my vocabulary! '%'word') #返回inputet转化后的词条向量 return returnVec#训练算法,从词向量计算概率p(w0|ci)...及p(ci)#@trainMatrix:由每篇文档的词条向量组成的文档矩阵#@trainCategory:每篇文档的类标签组成的向量def trainNB0(trainMatrix,trainCategory): #获取文档矩阵中文档的数目 numTrainDocs=len(trainMatrix) #获取词条向量的长度 numWords=len(trainMatrix[0]) #所有文档中属于类1所占的比例p(c=1) pAbusive=sum(trainCategory)/float(numTrainDocs) #创建一个长度为词条向量等长的列表 p0Num=zeros(numWords);p1Num=zeros(numWords) p0Denom=0.0;p1Denom=0.0 #遍历每一篇文档的词条向量 for i in range(numTrainDocs): #如果该词条向量对应的标签为1 if trainCategory[i]==1: #统计所有类别为1的词条向量中各个词条出现的次数 p1Num+=trainMatrix[i] #统计类别为1的词条向量中出现的所有词条的总数 #即统计类1所有文档中出现单词的数目 p1Denom+=sum(trainMatrix[i]) else: #统计所有类别为0的词条向量中各个词条出现的次数 p0Num+=trainMatrix[i] #统计类别为0的词条向量中出现的所有词条的总数 #即统计类0所有文档中出现单词的数目 p0Denom+=sum(trainMatrix[i]) #利用NumPy数组计算p(wi|c1) p1Vect=p1Num/p1Denom #为避免下溢出问题,后面会改为log() #利用NumPy数组计算p(wi|c0) p0Vect=p0Num/p0Denom #为避免下溢出问题,后面会改为log() return p0Vect,p1Vect,pAbusive#p0Num=ones(numWords);p1Num=ones(numWords)#p0Denom=2.0;p1Denom=2.0#p0Vect=log(p0Num/p0Denom);p1Vect=log(p1Num/p1Denom)#朴素贝叶斯分类函数#@vec2Classify:待测试分类的词条向量#@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0)#@p0Vec:类别1所有文档中各个词条出现的频数p(wi|c1)#@pClass1:类别为1的文档占文档总数比例def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): #根据朴素贝叶斯分类函数分别计算待分类文档属于类1和类0的概率 p1=sum(vec2Classify*p1Vec)+log(pClass1) p0=sum(vec2Classify*p0Vec)+log(1.0-pClass1) if p1>p0: return 1 else: return 0#分类测试整体函数 def testingNB(): #由数据集获取文档矩阵和类标签向量 listOPosts,listClasses=loadDataSet() #统计所有文档中出现的词条,存入词条列表 myVocabList=createVocabList(listOPosts) #创建新的列表 trainMat=[] for postinDoc in listOPosts: #将每篇文档利用words2Vec函数转为词条向量,存入文档矩阵中 trainMat.append(setOfWords2Vec(myVocabList,postinDoc))\ #将文档矩阵和类标签向量转为NumPy的数组形式,方便接下来的概率计算 #调用训练函数,得到相应概率值 p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses)) #测试文档 testEntry=['love','my','dalmation'] #将测试文档转为词条向量,并转为NumPy数组的形式 thisDoc=array(setOfWords2Vec(myVocabList,testEntry)) #利用贝叶斯分类函数对测试文档进行分类并打印 print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb)) #第二个测试文档 testEntry1=['stupid','garbage'] #同样转为词条向量,并转为NumPy数组的形式 thisDoc1=array(setOfWords2Vec(myVocabList,testEntry1)) print(testEntry1,'classified as:',classifyNB(thisDoc1,p0V,p1V,pAb))def bagOfWords2VecMN(vocabList,inputSet): #词袋向量 returnVec=[0]*len(vocabList) for word in inputSet: if word in vocabList: #某词每出现一次,次数加1 returnVec[vocabList.index(word)]+=1 return returnVec#贝叶斯算法实例:过滤垃圾邮件#处理数据长字符串#1 对长字符串进行分割,分隔符为除单词和数字之外的任意符号串#2 将分割后的字符串中所有的大些字母变成小写lower(),并且只#保留单词长度大于3的单词def testParse(bigString): import re listOfTokens=re.split(r'\W*',bigString) return [tok.lower() for tok in listOfTokens if len(tok)>2]def spamTest(): #新建三个列表 docList=[];classList=[];fullTest=[] #i 由1到26 for i in range(1,26): #打开并读取指定目录下的本文中的长字符串,并进行处理返回 wordList=testParse(open('email/spam/%d.txt' %i).read()) #将得到的字符串列表添加到docList docList.append(wordList) #将字符串列表中的元素添加到fullTest fullTest.extend(wordList) #类列表添加标签1 classList.append(1) #打开并取得另外一个类别为0的文件,然后进行处理 wordList=testParse(open('email/ham/&d.txt' %i).read()) docList.append(wordList) fullTest.extend(wordList) classList.append(0) #将所有邮件中出现的字符串构建成字符串列表 vocabList=createVocabList(docList) #构建一个大小为50的整数列表和一个空列表 trainingSet=range(50);testSet=[] #随机选取1~50中的10个数,作为索引,构建测试集 for i in range(10): #随机选取1~50中的一个整型数 randIndex=int(random.uniform(0,len(trainingSet))) #将选出的数的列表索引值添加到testSet列表中 testSet.append(trainingSet[randIndex]) #从整数列表中删除选出的数,防止下次再次选出 #同时将剩下的作为训练集 del(trainingSet[randIndex]) #新建两个列表 trainMat=[];trainClasses=[] #遍历训练集中的吗每个字符串列表 for docIndex in trainingSet: #将字符串列表转为词条向量,然后添加到训练矩阵中 trainMat.append(setOfWords2Vec(vocabList,fullTest[docIndex])) #将该邮件的类标签存入训练类标签列表中 trainClasses.append(classList[docIndex]) #计算贝叶斯函数需要的概率值并返回 p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses)) errorCount=0 #遍历测试集中的字符串列表 for docIndex in testSet: #同样将测试集中的字符串列表转为词条向量 wordVector=setOfWords2Vec(vocabList,docList[docIndex]) #对测试集中字符串向量进行预测分类,分类结果不等于实际结果 if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:errorCount+=1 print('the error rate is:',float(errorCount)/len(testSet))
朴素贝叶斯算法是没有前后文信息的
在朴素贝叶斯眼里,“我司可办理正规发票”与“正规发票可办理我司”完全相同。朴素贝叶斯算法在进行文本分类时是没有前后文的。这就相当于把所有的词汇扔进到一个袋子里随便搅和,贝叶斯都认为它们一样。因此这种情况也称作词袋子模型(bag of words)多项式模型
如果我们考虑重复词语的情况,也就是说,重复的词语我们视为其出现多次,直接按条件独立假设的方式推导,这样的模型叫作多项式模型。伯努利模型
另一种更加简化的方法是将重复的词语都视为其只出现1次,这样的模型叫作伯努利模型(又称为二项独立模型)。这种方式更加简化与方便。当然它丢失了词频的信息,因此效果可能会差一些。混合模型
第三种方式是在计算句子概率时,不考虑重复词语出现的次数,但是在统计计算词语的概率P("词语"|S)时,却考虑重复词语的出现次数,这样的模型可以叫作混合模型。平滑技术
平滑技术都是给未出现在训练集中的词语一个估计的概率,而相应地调低其他已经出现的词语的概率。 使用高斯环境的贝叶斯分类器对几种数据得预测情况: 1)能够对一般数据起到很好分类作用;"""=====================高斯环境的贝叶斯分类器=====================朴素贝叶斯分类器是一种基于贝叶斯理论的简单的概率分类器,而朴素的含义是指输入变量的特征属性之间具有很强的独立性。尽管这种朴素的设计和假设过于简单,但朴素贝叶斯分类器在许多复杂的实际情况下具有很好的表现,并且在综合性能上,该分类器要优于提升树(boosted trees)和随机森林(random forests)。在许多实际应用中,对于朴素贝叶斯模型的参数估计往往使用的是极大似然法,因此我们可以这么认为,在不接受贝叶斯概率或不使用任何贝叶斯方法的前提下,我们仍然可以应用朴素贝叶斯模型对事物进行分类。"""from sklearn.naive_bayes import GaussianNBfrom sklearn.datasets import make_moons, make_circles, make_classification#引入训练数据X, y = make_moons(noise=0.6, random_state=1)#定义高斯分类器类gnb = GaussianNB()#训练过程gnb.fit(X, y)#绘图库引入import matplotlib.pyplot as pltimport matplotlib as mplimport numpy as np#调整图片风格mpl.style.use('fivethirtyeight')#定义xy网格,用于绘制等值线图x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))#预测可能性Z = gnb.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]Z = Z.reshape(xx.shape)plt.contourf(xx, yy, Z, alpha=.8)#绘制散点图plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')plt.title("GaussianNaiveBayes")plt.axis("equal")plt.show()
2)特定情况下等同于线性分类器;
"""=====================高斯环境的贝叶斯分类器=====================在训练数据协方差相同的情况下,高斯分类器等效于线性分类器"""from sklearn.naive_bayes import GaussianNBimport numpy as np#引入训练数据X1 = np.random.normal(size = [600, 2])X2 = np.random.random([600, 2])#方差均衡,使得两个类方差均为1X1 = X1/np.std(X1)X2 = X2/np.std(X2)y = np.concatenate([np.zeros_like(X1[:,0]), np.ones_like(X2[:,0])], axis=0)X = np.concatenate([X1, X2],axis=0)#定义高斯分类器类gnb = GaussianNB()#训练过程gnb.fit(X, y)#绘图库引入import matplotlib.pyplot as pltimport matplotlib as mplimport numpy as np#调整图片风格mpl.style.use('fivethirtyeight')#定义xy网格,用于绘制等值线图x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))#预测可能性Z = gnb.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]Z = Z.reshape(xx.shape)plt.contourf(xx, yy, Z, alpha=.8)#绘制散点图plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')plt.title("GaussianNaiveBayes")plt.axis("equal")plt.show()
3)鸢尾花数据划分情况;
"""=====================贝叶斯分类器用于鸢尾花数据=====================由Fisher在1936年整理,包含4个特征(Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度))特征值都为正浮点数,单位为厘米。目标值为鸢尾花的分类(Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾))。"""from sklearn import datasetsfrom sklearn.naive_bayes import GaussianNBfrom sklearn.decomposition import PCAiris = datasets.load_iris()#选择两个属性,便于绘图X = iris.datay = iris.targetpca = PCA(n_components=2)X = pca.fit(X).transform(X)#定义高斯分类器类gnb = GaussianNB()#训练过程gnb.fit(X, y)#绘图库引入import matplotlib.pyplot as pltimport matplotlib as mplimport numpy as np#调整图片风格mpl.style.use('fivethirtyeight')#定义xy网格,用于绘制等值线图x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))#预测可能性pdt = gnb.predict_proba(np.c_[xx.ravel(), yy.ravel()])Z = pdt[:, 0]Z = Z.reshape(xx.shape)plt.contourf(xx, yy, Z, alpha=.6)Z = pdt[:, 1]Z = Z.reshape(xx.shape)plt.contourf(xx, yy, Z, alpha=.6)#绘制散点图plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')plt.title("GaussianNaiveBayes")plt.axis("equal")plt.show()
本文参考《统计学习方法》《机器学习实战》