贝叶斯分类器

136 阅读17分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

一、实验目的

1、理解条件概率、全概率和贝叶斯概率;

2、掌握朴素贝叶斯分类算法;

3、能熟练运用Python编写程序实现朴素贝叶斯分类。

二、实验要求

1、读代码,学python。要求能读懂所给代码,能改写代码实现其它类似算法;

2、运行结果要截图并对结果做分析;

3、所有内容汇总到本文档,无需提供.py文件;

4、文件命名方式:学号+姓名+实验二朴素贝叶斯算法.doc

三、实验内容

1. 阅读代码,为代码添加注释。

2. 改写代码实现文本分类功能

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

四、实验过程(原理,源码)

贝叶斯决策理论原理:

1. 阅读代码,为代码添加注释。

要做的是垃圾邮件过滤,首先需要将邮件进行解析为字符串列表

  1. """
  2. 函数说明:接收一个大字符串并将其解析为字符串列表
  3. """
  4. def textParse(bigString): #将字符串转换为字符列表
  5. #listOfTokens = re.split(r'W', bigString)
  6. listOfTokens = re.split(r'W+', bigString) #将特殊符号作为切分标志进行字符串切分,即非字母、非数字
  7. return [tok.lower() for tok in listOfTokens if len(tok) > 2] #除了单个字母,例如大写的I,其它单词变成小写

在进行读取邮件后,将字符串转换成字符串列表,然后需要一个函数将切分的词条整理成不重复的词条列表,即返回一个词汇表

1. """

2. createVocabList函数说明:将切分的实验样本词条整理成不重复的词条列表

3. Parameters:

4. dataSet - 整理的样本数据集

5. Returns:

6. vocabSet - 返回不重复的词条列表,也就是词汇表

7. """

8. def createVocabList(dataSet):

9. vocabSet = set([]) #创建一个空的不重复列表

10. for document in dataSet:

11. vocabSet = vocabSet | set(document) #取并集

12. return list(vocabSet)

然后需要根据返回的词汇表,进行构建词袋模型(词袋模型将一段文本看成一系列单词的集合,由于单词很多,故而这段文本就相当于一个袋子,里面装着一系列单词)

  1. """
  2. 函数说明:根据vocabList词汇表,构建词袋模型
  3. Parameters:
  4. vocabList - createVocabList返回的列表
  5. inputSet - 切分的词条列表
  6. Returns:
  7. returnVec - 文档向量,词袋模型
  8. """
  9. def bagOfWords2VecMN(vocabList, inputSet):
  10. returnVec = [0]len(vocabList) #创建一个其中所含元素都为0的向量
  11. for word in inputSet: #遍历每个词条
  12. if word in vocabList:
  13. returnVec[vocabList.index(word)] += 1 #如果词条存在于词汇表中,则计数加一
  14. return returnVec #返回词袋模型

下面是贝叶斯分类的训练函数

  1. """
  2. trainNB0函数说明:朴素贝叶斯分类器训练函数
  3. Parameters:
  4. trainMatrix - 训练文档矩阵
  5. trainCategory - 训练类别标签向量
  6. Returns:
  7. p0Vect - 正常邮件类的条件概率数组
  8. p1Vect - 垃圾邮件类的条件概率数组
  9. pAbusive - 文档属于垃圾邮件类的概率
  10. """
  11. def trainNB0(trainMatrix,trainCategory):
  12. numTrainDocs = len(trainMatrix) #计算训练的文档数目
  13. numWords = len(trainMatrix[0]) #计算每篇文档的词条数
  14. pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于垃圾邮件类的概率
  15. p0Num = np.ones(numWords)
  16. p1Num = np.ones(numWords) #创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑
  17. p0Denom = 2.0
  18. p1Denom = 2.0 #分母初始化为2 ,拉普拉斯平滑
  19. for i in range(numTrainDocs):
  20. if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
  21. p1Num += trainMatrix[i]
  22. p1Denom += sum(trainMatrix[i])
  23. else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
  24. p0Num += trainMatrix[i]
  25. p0Denom += sum(trainMatrix[i])
  26. p1Vect = np.log(p1Num/p1Denom) #取对数,防止下溢出
  27. p0Vect = np.log(p0Num/p0Denom) #取对数,防止下溢出
  28. return p0Vect,p1Vect,pAbusive #返回属于正常邮件类的条件概率数组,属于侮辱垃圾邮件类的条件概率数组,文档属于垃圾邮件类的概率

然后是贝叶斯分类的测试函数

  1. """
  2. classifyNB函数说明:朴素贝叶斯分类器分类函数
  3. Parameters:
  4. vec2Classify - 待分类的词条数组
  5. p0Vec - 正常邮件类的条件概率数组
  6. p1Vec - 垃圾邮件类的条件概率数组
  7. pClass1 - 文档属于垃圾邮件的概率
  8. Returns:
  9. 0 - 属于正常邮件类
  10. 1 - 属于垃圾邮件类
  11. """
  12. def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
  13. p1 = sum(vec2Classify p1Vec) + np.log(pClass1) #element-wise mult
  14. p0 = sum(vec2Classify p0Vec) + np.log(1.0 - pClass1)
  15. if p1 > p0:
  16. return 1
  17. else:
  18. return 0

最后是测试朴素贝叶斯分类的效果,使用朴素贝叶斯进行交叉验证。使用40个数据为训练集,10个数据为测试集进行测试。

  1. """
  2. 函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
  3. """
  4. def spamTest():
  5. docList=[]; classList = []; fullText =[]
  6. for i in list(range(1,26)): #遍历25个txt文件
  7. wordList = textParse(open('email/spam/%d.txt' % i,encoding='ISO-8859-1').read()) #读取每个垃圾邮件,并字符串转换成字符串列表
  8. docList.append(wordList)
  9. fullText.extend(wordList)
  10. classList.append(1) #标记垃圾邮件,1表示垃圾文件
  11. wordList = textParse(open('email/ham/%d.txt' % i,encoding='ISO-8859-1').read()) #读取每个非垃圾邮件,并字符串转换成字符串列表
  12. docList.append(wordList)
  13. fullText.extend(wordList)
  14. classList.append(0) #标记正常邮件,0表示正常文件
  15. vocabList = createVocabList(docList) #创建词汇表,不重复
  16. trainingSet = list(range(50)); testSet=[] #创建存储训练集的索引值的列表和测试集的索引值的列表
  17. for i in list(range(10)): #从50个邮件中,随机挑选出40个作为训练集,10个做测试集
  18. randIndex = int(np.random.uniform(0,len(trainingSet))) #随机选取索索引值
  19. testSet.append(trainingSet[randIndex]) #添加测试集的索引值
  20. del(trainingSet[randIndex]) #在训练集列表中删除添加到测试集的索引值
  21. trainMat=[]; trainClasses = [] #创建训练集矩阵和训练集类别标签系向量
  22. for docIndex in trainingSet: #遍历训练集
  23. trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) #将生成的词集模型添加到训练矩阵中
  24. trainClasses.append(classList[docIndex]) #将类别添加到训练集类别标签系向量中
  25. p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses)) #训练朴素贝叶斯模型
  26. errorCount = 0 #错误分类计数
  27. for docIndex in testSet: #遍历测试集
  28. wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) #测试集的词集模型
  29. if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: #如果分类错误
  30. errorCount += 1 # 错误计数加1
  31. print ("分类错误的测试集:",docList[docIndex])
  32. print ('错误率: ',float(errorCount)/len(testSet))

下图为运行结果

2. 改写代码实现文本分类功能

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

大体思路跟邮件拦截是一样的

首先创建一些数据样本作为训练集,并给出标签0/1

  1. """
  2. loadDataSet函数说明: 创建一些实验样本
  3. Returns:
  4. postingList - 原始的词条列表
  5. classVec - 类别标签向量
  6. """
  7. def loadDataSet():
  8. postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
  9. ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
  10. ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
  11. ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
  12. ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
  13. ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
  14. classVec = [0,1,0,1,0,1] #1 代表侮辱性文字, 0 代表正常言论
  15. return postingList,classVec

第二个函数是将样本整理承不重复的词条列表,与第一个例子中的函数一样

  1. """
  2. createVocabList函数说明:将切分的实验样本词条整理成不重复的词条列表
  3. Parameters:
  4. dataSet - 整理的样本数据集
  5. Returns:
  6. vocabSet - 返回不重复的词条列表,也就是词汇表
  7. """
  8. def createVocabList(dataSet): #创建一个包含在所有文档中的不重复词的列表
  9. vocabSet = set([]) #创建一个空集
  10. for document in dataSet:
  11. vocabSet = vocabSet | set(document) # “ |”代表创建两个集合的并集
  12. return list(vocabSet)

然后是根据上面返回的词汇表构建词袋模型

  1. """
  2. 函数说明:根据vocabList词汇表,构建词袋模型
  3. Parameters:
  4. vocabList - createVocabList返回的列表
  5. inputSet - 切分的词条列表
  6. Returns:
  7. returnVec - 文档向量,词袋模型
  8. """
  9. def bagOfWords2Vec(vocabList, inputSet): #创建一个其中所含元素为0的向量
  10. returnVec = [0]len(vocabList)
  11. for word in inputSet:
  12. if word in vocabList:
  13. returnVec[vocabList.index(word)] += 1
  14. return returnVec

下面是贝叶斯分类的训练算法

  1. """
  2. trainNB0函数说明:朴素贝叶斯分类器训练函数
  3. Parameters:
  4. trainMatrix - 训练文档矩阵,即bagOfWords2Vec返回的returnVec构成的矩阵
  5. trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
  6. Returns:
  7. p0Vect - 正常邮件类的条件概率数组
  8. p1Vect - 垃圾邮件类的条件概率数组
  9. pAbusive - 文档属于垃圾邮件类的概率
  10. """
  11. def trainNB0(trainMatrix,trainCategory):#输入参数为文档矩阵trainMatrix,和每篇文档类别标签所构成的向量trainCategory
  12. numTrainDocs = len(trainMatrix)
  13. numWords = len(trainMatrix[0])
  14. pAbusive = sum(trainCategory)/float(numTrainDocs)
  15. p0Num = zeros(numWords); p1Num = zeros(numWords) #初始化概率
  16. p0Denom = 0.0; p1Denom = 0.0
  17. for i in range(numTrainDocs):
  18. if trainCategory[i] == 1:
  19. p1Num += trainMatrix[i]
  20. p1Denom += sum(trainMatrix[i])
  21. else:
  22. p0Num += trainMatrix[i]
  23. p0Denom += sum(trainMatrix[i])
  24. p1Vect = p1Num/p1Denom #change to log()
  25. p0Vect = p0Num/p0Denom #change to log()
  26. return p0Vect,p1Vect,pAbusive

然后是贝叶斯分类的测试算法

  1. """
  2. classifyNB函数说明:朴素贝叶斯分类器分类函数
  3. Parameters:
  4. vec2Classify - 待分类的词条数组
  5. p0Vec - 正常邮件类的条件概率数组
  6. p1Vec - 垃圾邮件类的条件概率数组
  7. pClass1 - 文档属于垃圾邮件的概率
  8. Returns:
  9. 0 - 属于正常邮件类
  10. 1 - 属于垃圾邮件类
  11. """
  12. def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
  13. p1 = sum(vec2Classify p1Vec) + log(pClass1)
  14. p0 = sum(vec2Classify p0Vec) + log(1.0 - pClass1) #2分类问题可以用1.0-pClass1,多分类需要修改
  15. if p1 > p0:
  16. return 1
  17. else:
  18. return 0

最后是贝叶斯分类器,对文本进行分类预测

  1. """

  2. 函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证

  3. 0 - 属于正常邮件类

  4. 1 - 属于垃圾邮件类

  5. """

  6. def testingNB():

  7. listOPosts, listClasses = loadDataSet()

  8. myVocabList = createVocabList(listOPosts)

  9. trainMat = []

  10. for postinDoc in listOPosts:

  11. trainMat.append(bagOfWords2Vec(myVocabList, postinDoc))

  12. p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))

  13. testEntry = ['love', 'my', 'dalmation']

  14. thisDoc = array(bagOfWords2Vec(myVocabList, testEntry))

  15. print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))

  16. testEntry = ['stupid', 'garbage']

  17. thisDoc = array(bagOfWords2Vec(myVocabList, testEntry))

  18. print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))

对两组词汇进行分类预测,判断其为正常词汇或侮辱词汇

0 - 属于正常邮件类

1 - 属于垃圾邮件类

下图为运行结果:

五、实验总结(困难,收获)

本次实验完成了两个任务,一是阅读老师给的代码并注释,二是理解朴素贝叶斯原理并改写代码完成测试。

首先,在阅读老师给的实验代码时,setOfWords2Vec函数的代码并没有找到在全局中的调用,并将该段代码进行注释后运行源代码,仍然得到正常的实验结果,猜测为不必要函数,将其进行了删除,后面发现该函数与bagOfWords2VecMN函数作用类似,采用的是bagOfWords2VecMN函数。在阅读贝叶斯训练函数时,觉得有一点绕,读起来很吃力,通过百度相关知识,并结合手动测试,解决了该问题。

再修改原有代码时,不知道还可以做什么,通过百度找到的相关实验为直接使用封装好的贝叶斯函数,由于不清楚已经封装好的函数内部结构,未采用找到的几个贝叶斯实验。然后找到一个文本分类的题目,感觉跟邮件识别原理相似,遂决定改善代码完善该实验。

六、附录(源代码)

邮件识别代码及注释

_# from numpy import *
_import numpy as np
import re
"""
createVocabList函数说明:将切分的实验样本词条整理成不重复的词条列表Parameters:
dataSet -
整理的样本数据集****Returns:
vocabSet -
返回不重复的词条列表,也就是词汇表**
"""
def createVocabList(dataSet):
vocabSet = set([]) _#__创建一个空的不重复列表
_
for** document in dataSet:
vocabSet = vocabSet | set(document) _#__取并集
_return list(vocabSet)

"""
trainNB0函数说明:朴素贝叶斯分类器训练函数Parameters:
trainMatrix -
训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵****trainCategory - 训练类别标签向量,即loadDataSet返回的****classVec
Returns:
p0Vect -
正常邮件类的条件概率数组****p1Vect - 垃圾邮件类的条件概率数组****pAbusive - 文档属于垃圾邮件类的概率**
"""
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) _#__计算训练的文档数目
_numWords = len(trainMatrix[0]) _#__计算每篇文档的词条数
_pAbusive = sum(trainCategory)/float(numTrainDocs) _#__文档属于垃圾邮件类的概率
_p0Num = np.ones(numWords)
p1Num = np.ones(numWords) _#创建__numpy.ones__数组,__词条出现数初始化为__1,__拉普拉斯平滑
_p0Denom = 2.0
p1Denom = 2.0 _#__分母初始化为__2 ,__拉普拉斯平滑
_
for** i in range(numTrainDocs):
if trainCategory[i] == 1: _#__统计属于侮辱类的条件概率所需的数据,即__P(w0|1),P(w1|1),P(w2|1)···
_p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: _#__统计属于非侮辱类的条件概率所需的数据,即__P(w0|0),P(w1|0),P(w2|0)···
_p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = np.log(p1Num/p1Denom) _#__取对数,防止下溢出
_p0Vect = np.log(p0Num/p0Denom) _#__取对数,防止下溢出
_return p0Vect,p1Vect,pAbusive _#__返回属于正常邮件类的条件概率数组,属于侮辱垃圾邮件类的条件概率数组,文档属于垃圾邮件类的概率

_"""
classifyNB函数说明:朴素贝叶斯分类器分类函数Parameters:
vec2Classify -
待分类的词条数组****p0Vec - 正常邮件类的条件概率数组****p1Vec - 垃圾邮件类的条件概率数组****pClass1 - 文档属于垃圾邮件的概率****Returns:
0 -
属于正常邮件类****1 - 属于垃圾邮件类**
"""
**def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) _#element-wise mult
_p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0

"""
函数说明:根据vocabList词汇表,构建词袋模型
Parameters:
vocabList - createVocabList返回的列表inputSet -
切分的词条列表****Returns:
returnVec -
文档向量**,词袋模型
"""
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0]*len(vocabList) _#__创建一个其中所含元素都为__0__的向量
_
for** word in inputSet: _#__遍历每个词条
_if word in vocabList:
returnVec[vocabList.index(word)] += 1 _#__如果词条存在于词汇表中,则计数加一
_return returnVec _#__返回词袋模型

_"""
函数说明:接收一个大字符串并将其解析为字符串列表
"""
def textParse(bigString): _#__将字符串转换为字符列表
__#listOfTokens = re.split(r'\W*', bigString)
_listOfTokens = re.split(r'\W+', bigString) _#__将特殊符号作为切分标志进行字符串切分,即非字母、非数字
_
return
[tok.lower() for tok in listOfTokens if len(tok) > 2] _#除了单个字母,例如大写的__I,其它单词变成小写

_"""
函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
"""
def spamTest():
docList=[]; classList = []; fullText =[]
for i in list(range(1,26)): _#__遍历__25__个__txt__文件
_wordList = textParse(open(
'email/spam/%d.txt'
% i,encoding='ISO-8859-1').read()) _#__读取每个垃圾邮件,并字符串转换成字符串列表
_docList.append(wordList)
fullText.extend(wordList)
classList.append(1) _#__标记垃圾邮件,__1__表示垃圾文件
_wordList = textParse(open('email/ham/%d.txt' % i,encoding='ISO-8859-1').read()) _#__读取每个非垃圾邮件,并字符串转换成字符串列表
_docList.append(wordList)
fullText.extend(wordList)
classList.append(0) _#__标记正常邮件,__0__表示正常文件
_vocabList = createVocabList(docList) _#__创建词汇表,不重复
__# print(len(vocabList))
_trainingSet = list(range(50)); testSet=[] _#__创建存储训练集的索引值的列表和测试集的索引值的列表
_for i in list(range(10)): _#从__50__个邮件中,随机挑选出__40__个作为训练集,10__个做测试集
_randIndex = int(np.random.uniform(0,len(trainingSet))) _#__随机选取索索引值
_testSet.append(trainingSet[randIndex]) _#__添加测试集的索引值
_del(trainingSet[randIndex]) _#__在训练集列表中删除添加到测试集的索引值
_trainMat=[]; trainClasses = [] _#__创建训练集矩阵和训练集类别标签系向量
_for docIndex in trainingSet: _#__遍历训练集
_trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) _#__将生成的词集模型添加到训练矩阵中
_trainClasses.append(classList[docIndex]) _#__将类别添加到训练集类别标签系向量中
_p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses)) _#__训练朴素贝叶斯模型
_errorCount = 0 _#__错误分类计数
_for docIndex in testSet: _#__遍历测试集
_wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) _#__测试集的词集模型
_if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: _#__如果分类错误
_errorCount += 1 # _错误计数加__1
_print ("分类错误的测试集:",docList[docIndex])
print ('错误率: ',float(errorCount)/len(testSet))
_#return vocabList,fullText

_if __name__ == '__main__':
spamTest()

文本分类实验代码及注释:

from numpy import *
  
  """
loadDataSet函数说明: 创建一些实验样本
Returns:
    postingList - 原始的词条列表
    classVec - 类别标签向量
"""
  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'],
                 ['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
  
  """
createVocabList函数说明:将切分的实验样本词条整理成不重复的词条列表
Parameters:
    dataSet - 整理的样本数据集
Returns:
    vocabSet - 返回不重复的词条列表,也就是词汇表
"""
  def createVocabList(dataSet): #创建一个包含在所有文档中的不重复词的列表
    vocabSet = set([])        #创建一个空集
    for document in dataSet:
        vocabSet = vocabSet | set(document) # “ |”代表创建两个集合的并集
    return list(vocabSet)
  
  """
  函数说明:根据vocabList词汇表,构建词袋模型
Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量,词袋模型
"""
  def bagOfWords2Vec(vocabList, inputSet):  #创建一个其中所含元素为0的向量
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec
  
  """
trainNB0函数说明:朴素贝叶斯分类器训练函数
Parameters:
    trainMatrix - 训练文档矩阵,即bagOfWords2Vec返回的returnVec构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 正常邮件类的条件概率数组
    p1Vect - 垃圾邮件类的条件概率数组
    pAbusive - 文档属于垃圾邮件类的概率
"""
  def trainNB0(trainMatrix,trainCategory):#输入参数为文档矩阵trainMatrix,和每篇文档类别标签所构成的向量trainCategory
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    p0Num = zeros(numWords); p1Num = zeros(numWords)     #初始化概率
    p0Denom = 0.0; p1Denom = 0.0
   #p0Num = ones(numWords); p1Num = ones(numWords)
   #p0Denom = 2.0; p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = p1Num/p1Denom         #change to log()
    p0Vect = p0Num/p0Denom         #change to log()
    #p1Vect = log(p1Num/p1Denom)
    #p0Vect = log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive
  """
classifyNB函数说明:朴素贝叶斯分类器分类函数
Parameters:
    vec2Classify - 待分类的词条数组
    p0Vec - 正常邮件类的条件概率数组
    p1Vec - 垃圾邮件类的条件概率数组
    pClass1 - 文档属于垃圾邮件的概率
Returns:
    0 - 属于正常邮件类
    1 - 属于垃圾邮件类
"""
  def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)   #2分类问题可以用1.0-pClass1,多分类需要修改
    if p1 > p0:
        return 1
    else:
        return 0
  
  """
  函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
    0 - 属于正常邮件类
    1 - 属于垃圾邮件类
"""
  def testingNB():
    listOPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(bagOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))
  
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = array(bagOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
  
    testEntry = ['stupid', 'garbage']
    thisDoc = array(bagOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
  
  if __name__ == '__main__':
    testingNB()