一、算法概述贝叶斯算法是一种基于统计的概率分类方法,朴素贝叶斯是最简单的一种;朴素贝叶斯属于监督学习算法之一,一般用于解决分类问题。之所以称之为“朴素”,是因为整个形式化过程只做了最原始、最简单的假设,即假设所有的数据集都是独立存在的,互不影响。使用条件概率公式可以更好地理解此假设。假设一个样本(a1,a2,a3,...an)中有n个样本,若有P(a1,a2,a3,...,an)=P(a1)P(a2)P(a3)...P(an),则称数据集中的每个样本都是独立存在的。假设我们有一个数据集,有三角形和圆形两个特征,如下图所示:如果用P1(x,y)表示数据点(x,y)属于圆形的概率,则P2(x,y)表示一个数据点(x,y)属于三角形的概率,那么对于一个新的数据点(x,y),可以使用以下规则来确定它的类别:如果P1(x,y))>P2(x,y),则点类别为1。如果P1(x,y)0.105,出现三次—>0.157,出现一次—>0.052。4.3测试算法defclassifyNB(vec2Classify,p0V,P1V,PAb):#将对应元素相乘p1=reduce(lambdax,y:x*y,vec2Classify*p1V)*pAbp0=reduce(lambdax,y:x*y,vec2Classify*p0V)*(1.0-PAb)print('p0:',p0)print('p1:',p1)ifp1>p0:return1else:return0classifyNB函数传入的四个参数是测试文本的向量和训练函数trainNB返回的三个参数,其作用是将文本的向量对应p1V和p2V相乘,并乘以pAb及其对应的两类概率(1.0-pAb),然后比较p1和p2的大小,判断测试文本属于哪一类。这里给reduce方法做个小例子,方便理解。reduce(lambdax,y:x+y,[1,2,3,4])'''10'''reduce方法是根据一定的运算符将两个元素合并为一个结果,它是一个迭代的过程,每次调用这个方法直到得到最后一个结果,比如上面的数组[1,2,3,4]使用加法作为运算运算,实现了1+2的运算;3+3;6+4=10个过程。接下来,通过调用前面的函数对测试数据进行分类。代码如下:deftestingNB(testVec):#创建实验样本postingList,classVec=loadDataSet()#创建词汇vocabSet=createVocabList(postingList)#汇总实验样本向量trainMat=get_Mat(postingList)#训练算法p0V,P1V,PAb=trainNB(trainMat,classVec)#向量化测试文本The_test=setOfWords2Vec(vocabSet,testVec)#判断类别ifclassifyNB(The_test,p0V,P1V,PAb):print(testVec,"Insult")else:print(testVec,"Non-Insult")传入测试数据testVec,返回分类结果如下图:哦,这个笨蛋怎么判断不是侮辱呢?难道是程序变傻了?该程序是正常的,但我们需要对程序进行一些改进。我们都知道0是一个特殊的数,因为无论什么数乘以0,结果都是0,所以只要p1V向量和test向量同时有一个对应的位置为0,那么最后的result必须为0。为了减少上述影响,可以将所有词的出现次数初始化为1,分母初始化为2,这种方法称为拉普拉斯平滑。这部分对trainNB函数做了如下修改:p0Num=np.ones(numWords)p1Num=np.ones(numWords)p0Denom=2.0p1Denom=2.0另外,还有一个问题就是underflow,什么是underflow?当很多小数相乘时,在计算乘积P(W0|Ci)P(W1|Ci)P(W2|Ci)...P(Wn|Ci)时,由于大部分因数很小,所以程序会下溢或得不到正确答案。例如,程序会将乘积的结果四舍五入到很小,得到0。一个经典的解决方案是对乘积取自然对数。在代数中,有ln(a*b)=ln(a)+ln(b)。乘法转换为加法后,可以避免下溢或浮点数舍入引起的错误。可能有人会担心,两者的计算结果确实不同,但是对我们需要的分类结果没有影响。f(x)和ln(x)的曲线如下图所示:通过观察两条曲线,会发现它们在同一区域同时增大或减小,取极值处同样的一点,虽然两者的极值不同,但是并不影响最终的结果,因为我们只需要通过比较两个值的大小来判断测试数据的类别即可。这部分对trainNB函数做了如下改动:p1Vect=np.log(p1Num/p1Denom)p0Vect=np.log(p0Num/p0Denom)之前计算概率的时候做了对数运算,因为log(a*b)=log(a)+log(b),所以classifyNB函数可以用sum方法代替reduce方法进行改进。具体代码如下:defclassifyNB(ClassifyVec,p0V,p1V,pAb):#p1=reduce(lambdax,y:x*y,ClassifyVec*p1V)*pAb#p0=reduce(lambdax,y:x*y,ClassifyVec*p0V)*(1.0-PAb)#将对应元素相乘p1=sum(ClassifyVec*p1V)+np.log(pAb)p0=sum(ClassifyVec*p0V)+np.log(1.0-pAb)print('p0:',p0)print('p1:',p1)ifp1>p0:return1else:return0最终测试整体代码运行截图如下:通过比较p0和p1,测试文字能正确分类,笨到最后被判定为侮辱,看来程序不会变笨,变笨的是我。4.4Bag-of-wordsmodelextension在前面的程序中,我们将每个词的出现作为特征,可以描述为词集模型。如果一个词在文本中出现不止一次,则不能仅仅因为它涉及不同的权重就将其视为特征。这种方法称为词袋模型。在词袋中,每个词可以出现多次,而在词集中,每个词只能出现一次。可以在词集模型的基础上进行修改,转化为词袋模型。代码如下:defsetOfWords2Vec(vocabList,inputSet):#创建一个向量,其元素全为0returnVec=[0]*len(vocabList)forwordininputSet:ifwordinvocabList:#如果是词汇表中的词文中出现的单个单词,在returnVec[vocabList.index(word)]位置的数字加1+=1returnreturnVec5.文末总结朴素贝叶斯对应的优点如下:可以处理样本较少的数据集,可以处理多类别问题,对缺失数据不是很敏感。适用于文本分类。朴素贝叶斯相应的缺点如下:对输入数据的表达敏感,需要假设数据中的每个特征都需要相互独立。先验模型建立不当可能导致预测结果不佳。简单介绍了朴素贝叶斯算法的原理,下一篇将介绍朴素贝叶斯的应用实例。关注公众号【奶糖猫】后台回复“贝叶斯”获取源码供参考,感谢阅读。