当前位置:   article > 正文

机器学习实战3(朴素贝叶斯篇)_朴素贝叶斯分类器例题

朴素贝叶斯分类器例题

目录

1、朴素贝叶斯

2、朴素贝叶斯算法实例1--文档分类

3、朴素贝叶斯算法实例2--过滤垃圾邮件


1、朴素贝叶斯

        朴素贝叶斯算法是有监督的学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立(条件特征独立)性和连续变量的正态性假设为前提,就会导致算法精度在某种程度上受影响。        

        朴素贝叶斯是贝叶斯决策理论的一部分,所以讲述朴素贝叶斯之前有必要快速了解一下贝叶斯决策理论。

        假设现在我们有一个数据集,它由两类数据组成,数据分布如图4-1所示。

         假设有位读者找到了描述图中两类数据的统计参数。我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中用圆点表示的类别)的概率,用p2(x, y)表示数据点(x,y)属于类别2(图中用三角形表示的类别)的概率,那么对于一个新数据点(x,y),可以用下面的规则来判断它的类别:

        ①如果p1 (x,y) > p2(x,y),那么类别为1。

        ②如果p2(x,y) > p1(x,y),那么类别为2。

        也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。回到图4-1,如果该图中的整个数据使用6个浮点数"来表示,并且计算类别概率的Python代码只有两行,那么你会更倾向于使用下面哪种方法来对该数据点进行分类?

        ①使用第1章的kNN,进行1000次距离计算;

        ②使用第2章的决策树,分别沿x轴、y轴划分数据;

        ③计算数据点属于每个类别的概率,并进行比较。

        使用决策树不会非常成功,而和简单的概率计算相比,kNN的计算量太大。因此,对于上述问题,最佳选择是使用刚才提到的概率比较方法。

        提到贝叶斯决策理论要求计算两个概率p1(x, y)和p2(x,y)。如果p1(x,y) > p2(x, y),那么属于类别1;如果p2(x,y) > p1(x,y),那么属于类别2。

        但这两个准则并不是贝叶斯决策理论的所有内容。使用p1( )和p2( )只是为了尽可能简化描述,而真正需要计算和比较的是p(c_{1}|x,y)p(c_{2}|x,y)。这些符号所代表的具体意义是:给定某个由x、y表示的数据点,那么该数据点来自类别c_{1}的概率是多少?数据点来自类别c_{2}的概率又是多少?注意这些概率与刚才给出的概率p(x,y|c)并不一样,不过可以使用贝叶斯准则来交换概率中条件与结果。具体地,应用贝叶斯准则得到:

         使用这些定义,可以定义贝叶斯分类准则为:

       ①如果p(c1|x,y)>p(c2|x,y),那么属于类别c_{1}

        ②如果p(c2|x,y)>p(c1|x,y),那么属于类别c_{2}

        使用贝叶斯准则,可以通过已知的三个概率值来计算未知的概率值。后面就会给出利用贝叶斯准则来计算概率并对数据进行分类的代码。现在介绍了一些概率理论,你也了解了基于这些理论构建分类器的方法,接下来就要将它们付诸实践。

2、朴素贝叶斯算法实例1--文档分类

        机器学习的一个重要应用就是文档的自动分类。在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。虽然电子邮件是一种会不断增加的文本,但我们同样也可以对新闻报道、用户留言、政府公文等其他任意类型的文本进行分类。我们可以观察文档中出现的词,并把每个词的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的词目一样多。朴素贝叶斯是上节介绍的贝叶斯分类器的一个扩展,是用于文档分类的常用算法。

        便用每个词作为特征并观察它们是否出现,这样得到的特征数目会有多少呢?针对的是哪一种人类语言呢?当然不止一种语言。据估计,仅在英语中,单词的总数就有500 000之多。为了能进行英文阅读,估计需要掌握数千单词。

        假设词汇表中有1000个单词。要得到好的概率分布,就需要足够的数据样本,假定样本数为N。前面讲到的约会网站示例中有1000个实例,手写识别示例中每个数字有200个样本,而决策树示例中有24个样本。其中,24个样本有点少,200个样本好一些,而1000个样本就非常好了。约会网站例子中有三个特征。由统计学知,如果每个特征需要N个样本,那么对于10个特征将需要N^{10}个样本,对于包含1000个特征的词汇表将需要N^{1000}个样本。可以看到,所需要的样本数会随着特征数目增大而迅速增长。

        如果特征之间相互独立,那么样本数就可以从N^{1000}减少到1000×N。所谓独立指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。举个例子讲,假设单词bacon出现在unhealthy后面与出现在delicious后面的概率相同。当然,我们知道这种假设并不正确,bacon常常出现在delicious附近,而很少出现在unhealthy附近,这个假设正是朴素贝叶斯分类器中朴素( naive)一词的含义。朴素贝叶斯分类器中的另一个假设是,每个特征相同重要。

        以在线社区的留言板为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标识为内容不当。过滤这类内容是一个很常见的需求。对此问题建立两个类别:侮辱类和非侮辱类,使用1和0分别表示。

1、准备数据

        我们将把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。接下来我们正式开始。打开文本编辑器,创建一个叫bayes.py的新文件,然后将下面的程序清单添加到文件中。

  1. #数据集
  2. def loadDataSet():
  3. postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
  4. ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
  5. ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
  6. ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
  7. ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
  8. ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
  9. classVec = [0,1,0,1,0,1] #1 是侮辱性文字 0 是正常言论
  10. return postingList,classVec
'
运行
  1. #创建集合的并集
  2. def createVocabList(dataSet):
  3. vocabSet = set([])
  4. for document in dataSet:
  5. vocabSet = vocabSet | set(document)
  6. return list(vocabSet)
'
运行
  1. #根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
  2. def setOfWordVex(vocabList,inputSet):
  3. returnVec = [0]*len(vocabList)
  4. for word in inputSet:
  5. if word in vocabList:
  6. returnVec[vocabList.index(word)] = 1
  7. else:
  8. print("%s is not in vocalList",word)
  9. return returnVec
'
运行

        我们来测试一下代码:

  1. DataSet,classVec = loadDataSet()
  2. vocabSet = createVocabList(DataSet)
  3. ans = setOfWordVex(vocabSet,DataSet[0])

        得到的输出为:

 2、朴素贝叶斯分类训练函数

        前面介绍了如何将一组单词转换为一组数字,接下来看看如何使用这些数字计算概率。

  1. #朴素贝叶斯分类器训练函数
  2. def trainNBO(trainMatrix,trainCategroy):
  3. numTrainDocs = len(trainMatrix) #计算训练的文档数目
  4. numWords = len(trainMatrix[0]) #计算每篇文档的词条数
  5. pAbusive = sum(trainCategroy)/float(numTrainDocs) #文档属于侮辱类的概率
  6. #初始化概率
  7. p0num = np.ones(numWords) #拉普拉斯平滑
  8. p1num = np.ones(numWords)
  9. p0Denom = 2.0
  10. p1Denom = 2.0
  11. for i in range(numTrainDocs):
  12. if trainCategroy[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
  13. p1num += trainMatrix[i]
  14. p1Denom += sum(trainMatrix[i])
  15. else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
  16. p0num += trainMatrix[i]
  17. p0Denom += sum(trainMatrix[i])
  18. p1Vect = log(p1num/p1Denom) #取对数,取值不同,但是不影响最终结果。
  19. p0Vect = log(p0num/p0Denom) #可以防止下溢出,这是由于太多太小的数相乘造成的。
  20. return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
'
运行

        利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w_{0}|1)p(w_{2}|1)p(w_{3}|1)。如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。

        另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积p(w_{0}|1)p(w_{2}|1)p(w_{3}|1)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(读者可以用Python尝试相乘许多很小的数,最后四舍五入后会得到0。)一种解决办法是对乘积取自然对数。在代数中有1n(a*b) = ln (a)+1n(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

3、朴素贝叶斯分类函数

        使用NumPy的数组来计算两个向量相乘的结果。这里的相乘是指对应元素相乘,即先将两个向量中的第1个元素相乘,然后将第2个元素相乘,以此类推。接下来将词汇表中所有词的对应值相加,然后将该值加到类别的对数概率上。最后,比较类别的概率返回大概率对应的类别标签。这一切不是很难,对吧?

  1. def classifyNB(vecClassify,p0Vec,p1Vec,pClass1):
  2. p1 = sum(vecClassify*p1Vec)+log(pClass1)
  3. p0 = sum(vecClassify*p0Vec)+log(1-pClass1)
  4. if p1>p0:
  5. return 1
  6. else :
  7. return 0
'
运行
  1. def testingNB():
  2. DataSet,classVec = loadDataSet()
  3. vocabSet = createVocabList(DataSet)
  4. trainMat = []
  5. for postinDoc in DataSet:
  6. trainMat.append(setOfWordVex(vocabSet,postinDoc))
  7. p0V,p1V,pAb = trainNBO(trainMat,classVec)
  8. testEntry = ['love','my','dalmation']
  9. thisDoc = array(setOfWordVex(vocabSet,testEntry))
  10. print(classifyNB(thisDoc,p0V,p1V,pAb))
'
运行

        我们测试一下代码,得到:

 4、词袋模型

        目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数setofWordsvec()稍加修改,修改后的函数称为bagofwordsvec ()。

        下面的函数给出了基于词袋模型的朴素贝叶斯代码。它与函数setofwordsvec()几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为1。

  1. #词带模型
  2. def bagOfWordVecMN(vocabList,inputSet):
  3. returnVec = [0]*len(vocabList)
  4. for word in inputSet:
  5. if word in vocabList:
  6. returnVec[vocabList.index(word)] += 1 #与词集模型不同之处
  7. return returnVec
'
运行

        现在分类器已经构建好了,下面我们将利用该分类器来过滤垃圾邮件

5、完整代码

        见下个实例吧。哈。

3、朴素贝叶斯算法实例2--过滤垃圾邮件

        在前面那个简单的例子中,我们引人了字符串列表。使用朴素贝叶斯解决一些现实生活中的问题时,需要先从文本内容得到字符串列表,然后生成词向量。下面这个例子中,.我们将了解朴素贝叶斯的一个最著名的应用:电子邮件垃圾过滤。首先看一下如何使用通用框架来解决该问题。

1、准备数据,切分文本

        前一节介绍了如何创建词向量,并基于这些词向量进行朴素贝叶斯分类的过程。前一节中的词向量是预先给定的,下面介绍如何从文本文档中构建自己的词列表。

        数据:数据在这!         提取码:kfpg

  1. #切分文本
  2. def textParse(bigString):
  3. listOfTokens = re.split(r'\W+', bigString)
  4. return [tok.lower() for tok in listOfTokens if len(tok)>2]
'
运行
  1. def spamTest():
  2. docList = []
  3. classList = []
  4. fullText = []
  5. for i in range(1,26):#遍历25个txt文件
  6. wordList = textParse(open('email/spam/%d.txt' % i, 'r').read()) #读取每个垃圾邮件,并字符串转换成字符串列表
  7. docList.append(wordList)
  8. fullText.extend(wordList)
  9. classList.append(1) #标记垃圾邮件,1表示垃圾文件
  10. wordList = textParse(open('email/ham/%d.txt' % i).read())
  11. docList.append(wordList)
  12. fullText.extend(wordList)
  13. classList.append(0)#标记非垃圾邮件
  14. # print(docList)
  15. vocabList = createVocabList(docList) #创建词汇表,不重复
  16. trainingSet = list(range(50)) #训练集
  17. print(trainingSet)
  18. testSet = [] # 测试集
  19. for i in range (10): #随机构建训练集、测试集
  20. randIndex = int(random.uniform(0,len(trainingSet)))
  21. testSet.append(trainingSet[randIndex])
  22. del(trainingSet[randIndex])
  23. trainMat = []
  24. trainClasses = []
  25. for docIndex in trainingSet:
  26. trainMat.append(setOfWordVex(vocabList,docList[docIndex]))
  27. trainClasses.append(classList[docIndex])
  28. p0V,p1V,pSpam = trainNBO(array(trainMat),array(trainClasses))
  29. errorCount = 0
  30. for docIndex in testSet:
  31. wordVector = setOfWordVex(vocabList,docList[docIndex])
  32. if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
  33. errorCount += 1
  34. print('the error is %f',errorCount/float(len(testSet)))
'
运行

        测试一下得到:

        函数spamTest ()会输出在10封随机选择的电子邮件上的分类错误率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。如果发现错误的话,函数会输出错分文档的词表,这样就可以了解到底是哪篇文档发生了错误。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说10次,然后求平均值。我这么做了一下,获得的平均错误率为6%。

        这里一直出现的错误是将垃圾邮件误判为正常邮件。相比之下,将垃圾邮件误判为正常邮件要比将正常邮件归到垃圾邮件好。为避免错误,有多种方式可以用来修正分类器,这些将在第7章中进行讨论。

 2、完整代码

  1. from numpy import *
  2. import numpy as np
  3. import random
  4. import re
  5. #数据集
  6. def loadDataSet():
  7. postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
  8. ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
  9. ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
  10. ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
  11. ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
  12. ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
  13. classVec = [0,1,0,1,0,1] #1 是侮辱性文字 0 是正常言论
  14. return postingList,classVec
  15. #创建集合的并集
  16. def createVocabList(dataSet):
  17. vocabSet = set([])
  18. for document in dataSet:
  19. vocabSet = vocabSet | set(document)
  20. return list(vocabSet)
  21. #根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
  22. def setOfWordVex(vocabList,inputSet):
  23. returnVec = [0]*len(vocabList)
  24. for word in inputSet:
  25. if word in vocabList:
  26. returnVec[vocabList.index(word)] = 1
  27. else:
  28. print("%s is not in vocalList",word)
  29. return returnVec
  30. #词带模型
  31. def bagOfWordVecMN(vocabList,inputSet):
  32. returnVec = [0]*len(vocabList)
  33. for word in inputSet:
  34. if word in vocabList:
  35. returnVec[vocabList.index(word)] += 1 #与词集模型不同之处
  36. return returnVec
  37. #朴素贝叶斯分类器训练函数
  38. def trainNBO(trainMatrix,trainCategroy):
  39. numTrainDocs = len(trainMatrix) #计算训练的文档数目
  40. numWords = len(trainMatrix[0]) #计算每篇文档的词条数
  41. pAbusive = sum(trainCategroy)/float(numTrainDocs) #文档属于侮辱类的概率
  42. #初始化概率
  43. p0num = np.ones(numWords) #拉普拉斯平滑
  44. p1num = np.ones(numWords)
  45. p0Denom = 2.0
  46. p1Denom = 2.0
  47. for i in range(numTrainDocs):
  48. if trainCategroy[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
  49. p1num += trainMatrix[i]
  50. p1Denom += sum(trainMatrix[i])
  51. else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
  52. p0num += trainMatrix[i]
  53. p0Denom += sum(trainMatrix[i])
  54. p1Vect = log(p1num/p1Denom) #取对数,取值不同,但是不影响最终结果。
  55. p0Vect = log(p0num/p0Denom) #可以防止下溢出,这是由于太多太小的数相乘造成的。
  56. return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
  57. def classifyNB(vecClassify,p0Vec,p1Vec,pClass1):
  58. p1 = sum(vecClassify*p1Vec)+log(pClass1)
  59. p0 = sum(vecClassify*p0Vec)+log(1-pClass1)
  60. if p1>p0:
  61. return 1
  62. else :
  63. return 0
  64. def testingNB():
  65. DataSet,classVec = loadDataSet()
  66. vocabSet = createVocabList(DataSet)
  67. trainMat = []
  68. for postinDoc in DataSet:
  69. trainMat.append(setOfWordVex(vocabSet,postinDoc))
  70. p0V,p1V,pAb = trainNBO(trainMat,classVec)
  71. testEntry = ['love','my','dalmation']
  72. thisDoc = array(setOfWordVex(vocabSet,testEntry))
  73. print(classifyNB(thisDoc,p0V,p1V,pAb))
  74. #切分文本
  75. def textParse(bigString):
  76. listOfTokens = re.split(r'\W+', bigString)
  77. return [tok.lower() for tok in listOfTokens if len(tok)>2]
  78. #
  79. def spamTest():
  80. docList = []
  81. classList = []
  82. fullText = []
  83. for i in range(1,26):#遍历25个txt文件
  84. wordList = textParse(open('email/spam/%d.txt' % i, 'r').read()) #读取每个垃圾邮件,并字符串转换成字符串列表
  85. docList.append(wordList)
  86. fullText.extend(wordList)
  87. classList.append(1) #标记垃圾邮件,1表示垃圾文件
  88. wordList = textParse(open('email/ham/%d.txt' % i).read())
  89. docList.append(wordList)
  90. fullText.extend(wordList)
  91. classList.append(0)#标记非垃圾邮件
  92. # print(docList)
  93. vocabList = createVocabList(docList) #创建词汇表,不重复
  94. trainingSet = list(range(50)) #训练集
  95. print(trainingSet)
  96. testSet = [] # 测试集
  97. for i in range (10): #随机构建训练集、测试集
  98. randIndex = int(random.uniform(0,len(trainingSet)))
  99. testSet.append(trainingSet[randIndex])
  100. del(trainingSet[randIndex])
  101. trainMat = []
  102. trainClasses = []
  103. for docIndex in trainingSet:
  104. trainMat.append(setOfWordVex(vocabList,docList[docIndex]))
  105. trainClasses.append(classList[docIndex])
  106. p0V,p1V,pSpam = trainNBO(array(trainMat),array(trainClasses))
  107. errorCount = 0
  108. for docIndex in testSet:
  109. wordVector = setOfWordVex(vocabList,docList[docIndex])
  110. if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
  111. errorCount += 1
  112. print('the error is %f',errorCount/float(len(testSet)))
  113. # DataSet,classVec = loadDataSet()
  114. # vocabSet = createVocabList(DataSet)
  115. # ans = setOfWordVex(vocabSet,DataSet[0])
  116. # print(ans)
  117. # trainMat = []
  118. # for postinDoc in DataSet:
  119. # trainMat.append(setOfWordVex(vocabSet,postinDoc))
  120. #
  121. # p0V,p1V,pAb = trainNBO(trainMat,classVec)
  122. # testingNB()
  123. spamTest()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/875519
推荐阅读
相关标签
  

闽ICP备14008679号