统计学习方法--朴素贝叶斯 python实现

朴素贝叶斯

朴素贝叶斯方法是一套基于贝叶斯定理的监督学习算法,在每一对特征之间采用独立的 “naive” 假设。给定一个类变量 y 和一个从属特征向量 x_1 到 x_n ,贝叶斯定理表示以下关系:

使用 naive ( 天真 ) 独立假设:

对于所有的 i ,这种关系被简化为:

由于  给定输入是常数,所以我们可以使用以下分类规则:

我们可以使用 Maximum A Posteriori ( 最大后验 ) ( MAP ) 估计来估计P(y) 和  ; 那么前者是训练集中的类 ÿ 的相对频率。

不同的朴素贝叶斯分类器主要取决于它们对  的分布的假设。

尽管他们显然过于简化的假设,但朴素贝叶斯分类器在许多现实世界的情况下都有很好的效果,有著名的文档分类和垃圾邮件过滤。他们需要少量的训练数据来估计必要的参数。( 为了了解到朴素贝叶斯运作良好,并且在哪类数据上运行良好的假想的原因,请看下文。 )

扫描二维码关注公众号,回复: 1506620 查看本文章

与更复杂的方法相比,朴素贝叶斯 learners ( 学习器 )和 classifiers ( 分类器 ) 可以非常快。类条件特征分布的解耦意味着每个分布可以独立地估计为一维分布。这又有助于缓解 the curse of dimensionality ( 维度惩罚 ) 所引起的问题。

另一方面,虽然朴素贝叶斯被认为是一个还不错的分类器,但它被认为是一个不好的 estimator ( 估计器 ) ,所以来自 predict_proba 的概率输出不被过于重视。


1,创建BYS模型

[python]  view plain  copy
  1. # -*- coding: utf-8 -*-  
  2. """ 
  3. Created on Fri Oct 20 11:44:48 2017 
  4.  
  5. @author: lizheng 
  6. """  
  7. # 项目案例1: 屏蔽社区留言板的侮辱性言论  
  8. import numpy as np  
  9. def loadDataSet():  
  10.     """ 
  11.     创建数据集 
  12.     :return: 单词列表postingList, 所属类别classVec 
  13.     """  
  14.     postingList = [['my''dog''has''flea''problems''help''please'], #[0,0,1,1,1......]  
  15.                    ['maybe''not''take''him''to''dog''park''stupid'],  
  16.                    ['my''dalmation''is''so''cute''I''love''him'],  
  17.                    ['stop''posting''stupid''worthless''gar e'],  
  18.                    ['mr''licks''ate''my''steak''how''to''stop''him'],  
  19.                    ['quit''buying''worthless''dog''food''stupid']]  
  20.     classVec = [010101]  # 1 is abusive, 0 not  
  21.     return postingList, classVec  
  22.   
  23.   
  24. def createVocabList(dataSet):  
  25.     """ 
  26.     获取所有单词的集合 
  27.     :param dataSet: 数据集 
  28.     :return: 所有单词的集合(即不含重复元素的单词列表) 
  29.     """  
  30.     vocabSet = set([])  # create empty set  
  31.     for document in dataSet:  
  32.         # 操作符 | 用于求两个集合的并集  
  33.         vocabSet = vocabSet | set(document)  # union of the two sets  
  34.     return list(vocabSet)  
  35.   
  36.   
  37. def setOfWords2Vec(vocabList, inputSet):  
  38.     """ 
  39.     遍历查看该单词是否出现,出现该单词则将该单词置1 
  40.     :param vocabList: 所有单词集合列表 
  41.     :param inputSet: 输入数据集 
  42.     :return: 匹配列表[0,1,0,1...],其中 1与0 表示词汇表中的单词是否出现在输入的数据集中 
  43.     """  
  44.     # 创建一个和词汇表等长的向量,并将其元素都设置为0  
  45.     returnVec = [0] * len(vocabList)# [0,0......]  
  46.     # 遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1  
  47.     for word in inputSet:  
  48.         if word in vocabList:  
  49.             returnVec[vocabList.index(word)] = 1  
  50.         else:  
  51.             print ("the word: %s is not in my Vocabulary!" % word)  
  52.     return returnVec  
  53. """ 
  54. 现在已经知道了一个词是否出现在一篇文档中,也知道该文档所属的类别。 
  55. 接下来我们重写贝叶斯准则,将之前的 x, y 替换为 w. 粗体的 w 表示这是一个向量,即它由多个值组成。 
  56. 在这个例子中,数值个数与词汇表中的词个数相同。 
  57.  
  58. P(ci|w)=p(w|ci)p(ci)/p(w) 
  59.  
  60. 我们使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。 
  61. #首先可以通过类别 i (侮辱性留言或者非侮辱性留言)中的文档数除以总的文档数来计算概率 p(ci) 。 
  62. 接下来计算 p(w | ci) ,这里就要用到朴素贝叶斯假设。如果将 w 展开为一个个独立特征, 
  63. 那么就可以将上述概率写作 p(w0, w1, w2...wn | ci) 。 
  64. 这里假设所有词都互相独立,该假设也称作条件独立性假设(例如 A 和 B 两个人抛骰子,概率是互不影响的, 
  65. 也就是相互独立的,A 抛 2点的同时 B 抛 3 点的概率就是 1/6 * 1/6), 
  66. 它意味着可以使用 p(w0 | ci)p(w1 | ci)p(w2 | ci)...p(wn | ci) 来计算上述概率,这样就极大地简化了计算的过程。 
  67. """  
  68.   
  69.   
  70. def trainNB0(trainMatrix, trainCategory):  
  71.     """ 
  72.     训练数据 
  73.     :param trainMatrix: 文件单词矩阵 
  74.     :param trainCategory: 文件对应的类别 
  75.     :return: 
  76.     """  
  77.     # 总文件数  
  78.     numTrainDocs = len(trainMatrix)  
  79.     # 总单词数  
  80.     numWords = len(trainMatrix[0])  
  81.     # 侮辱性文件的出现概率  
  82.     pAbusive = sum(trainCategory) / float(numTrainDocs)  
  83.     # 构造单词出现次数列表  
  84.     # p0Num 正常的统计  
  85.     # p1Num 侮辱的统计   
  86.     # 避免单词列表中的任何一个单词为0,而导致最后的乘积为0,所以将每个单词的出现次数初始化为 1  
  87.     p0Num = np.ones(numWords)#[0,0......]->[1,1,1,1,1.....]  
  88.     p1Num = np.ones(numWords)  
  89.   
  90.     # 整个数据集单词出现总数,2.0根据样本/实际调查结果调整分母的值(2主要是避免分母为0,当然值可以调整)  
  91.     # p0Denom 正常的统计  
  92.     # p1Denom 侮辱的统计  
  93.     p0Denom = 2.0  
  94.     p1Denom = 2.0  
  95.     for i in range(numTrainDocs):  
  96.         if trainCategory[i] == 1:  
  97.             # 累加辱骂词的频次  
  98.             p1Num += trainMatrix[i]  
  99.             # 对每篇文章的辱骂的频次 进行统计汇总  
  100.             p1Denom += sum(trainMatrix[i])  
  101.         else:  
  102.             p0Num += trainMatrix[i]  
  103.             p0Denom += sum(trainMatrix[i])  
  104.     # 类别1,即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表  
  105.     p1Vect = np.log(p1Num / p1Denom)  
  106.     # 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表  
  107.     p0Vect = np.log(p0Num / p0Denom)  
  108.     return p0Vect, p1Vect, pAbusive  
  109.   
  110.   
  111. def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):  
  112.     """ 
  113.     使用算法: 
  114.         # 将乘法转换为加法 
  115.         乘法:P(C|F1F2...Fn) = P(F1F2...Fn|C)P(C)/P(F1F2...Fn) 
  116.         加法:P(F1|C)*P(F2|C)....P(Fn|C)P(C) -> log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C)) 
  117.     :param vec2Classify: 待测数据[0,1,1,1,1...],即要分类的向量 
  118.     :param p0Vec: 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表 
  119.     :param p1Vec: 类别1,即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表 
  120.     :param pClass1: 类别1,侮辱性文件的出现概率 
  121.     :return: 类别1 or 0 
  122.     """  
  123.     # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))  
  124.     # 使用 NumPy 数组来计算两个向量相乘的结果,这里的相乘是指对应元素相乘,即先将两个向量中的第一个元素相乘,然后将第2个元素相乘,以此类推。  
  125.     # 我的理解是:这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来  
  126.     # 可以理解为 1.单词在词汇表中的条件下,文件是good 类别的概率 也可以理解为 2.在整个空间下,文件既在词汇表中又是good类别的概率  
  127.     p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  
  128.     p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)  
  129.     if p1 > p0:  
  130.         return 1  
  131.     else:  
  132.         return 0  
  133.   
  134.   
  135. def bagOfWords2VecMN(vocabList, inputSet):  
  136.     returnVec = [0] * len(vocabList)  
  137.     for word in inputSet:  
  138.         if word in vocabList:  
  139.             returnVec[vocabList.index(word)] += 1  
  140.     return returnVec  


2,模型测试

[python]  view plain  copy
  1. import BYS  
  2. import numpy as np  
  3.      # 1. 加载数据集  
  4. listOPosts, listClasses = BYS.loadDataSet()  
  5.     # 2. 创建单词集合  
  6. myVocabList = BYS.createVocabList(listOPosts)  
  7.     # 3. 计算单词是否出现并创建数据矩阵  
  8. trainMat = []  
  9. for postinDoc in listOPosts:  
  10.         # 返回m*len(myVocabList)的矩阵, 记录的都是0,1信息  
  11.         trainMat.append(BYS.setOfWords2Vec(myVocabList, postinDoc))  
  12.     # 4. 训练数据  
  13. p0V, p1V, pAb = BYS.trainNB0(np.array(trainMat), np.array(listClasses))  
  14.     # 5. 测试数据  
  15. testEntry = ['love''my''dalmation']  
  16. thisDoc = np.array(BYS.setOfWords2Vec(myVocabList, testEntry))  
  17. print (testEntry, 'classified as: ', BYS.classifyNB(thisDoc, p0V, p1V, pAb))  
  18. testEntry = ['stupid''garbage']  
  19. thisDoc = np.array(BYS.setOfWords2Vec(myVocabList, testEntry))  
  20. print (testEntry, 'classified as: ', BYS.classifyNB(thisDoc, p0V, p1V, pAb))  

  • 朴素贝叶斯的准确率,其实是比较依赖于训练语料的,机器学习算法就和纯洁的小孩一样,取决于其成长(训练)条件,”吃的是草挤的是奶”,但”不是所有的牛奶,都叫特仑苏”。



代码来自https://github.com/apachecn

猜你喜欢

转载自blog.csdn.net/qq_36603091/article/details/78338198