《Machine Learning in Action》| 第4章 基于概率论的分类方法:朴素贝叶斯

使用Python进行文本分类

准备数据:从文本中构建词向量

程序清单 4-1 词表到向量的转换函数
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] # (6篇文档)类别标签向量,1 代表侮辱性文字,0 代表正常言论
    return postingList, classVec # 返回词条列表,所属类别

"""
@函数说明:创建一个包含在所有文档中出现的不重复词的列表
是用python的 Set 数据类型,不重复
将词条列表输给 Set 构造函数,set 就会返回一个不重复词表
"""
def createVocabList(dataSet):
    vocabSet = set([]) # 创建一个空的不重复列表
    for document in dataSet: # 将每篇文档返回的新词集合添加进去,即取两个集合的并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet) # 获得词汇表(32条),用来将词条向量化

"""
@函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
遍历遍历查看该单词是否出现,出现该单词则将该单词置1
Parameters:
    vocabList - createVocabList返回的不重复词条列表
    inputSet - 输入数据集,切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList) # 创建一个和词汇表等长的向量,将其元素都设置为 0
    for word in inputSet: # 遍历文档中所有单词
        if word in inputSet: # 如果出现词汇表中的单词,将输出的文档向量中的对应值设为 1
            returnVec[vocabList.index(word)] = 1
        else:
            print('the word: %s is not in my vocabulary!' % word)
    return returnVec # 返回文档向量,如[0, 0, 1, 1,...,]32条

执行代码,运行结果:
在这里插入图片描述

训练算法:从词向量计算概率

  • 函数的伪代码如下:
    计算每个类别中的文档数目
    对每篇训练文档:
    ——对每个类别:
    ————如果词条出现在文档中—>增加该词条的计数值
    ————增加所有词条的计数值
    对每个类别:
    ——对每个词条:
    ————将该词条对数目除以总词条数目得到条件概率
    返回每个类别对条件概率
程序清单 4-2 朴素贝叶斯分类器训练函数
"""
@函数说明:朴素贝叶斯分类器训练函数
Parameters:
    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec文档向量构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 非侮辱类的条件概率数组
    p1Vect - 侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
Author:
"""
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix) # 训练文档的数目(6篇)
    numWords = len(trainMatrix[0]) # 每篇文档的词条数(32条)
    # 计算先验概率:p(1)=0.5,p(0)=1-0.5=0.5
    pAbusive = sum(trainCategory) / float(numTrainDocs) # 文档属于侮辱类的概率(p(1)=0.5),侮辱类文档数(类别为1的个个数)除以总文档数
#    print('pAbusive',pAbusive) #p(1)=0.5,p(0)=1-0.5=0.5
    #若其中一个概率为0,导致乘积为0,分子初始值修改为1,分母修改为2
#    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 类别中单词出现的个数初始化为0
#    p0Denom = 0.0; p1Denom = 0.0 # 分母初始化为0,整个数据集单词出现的个数(原来是0,后面改成2了)
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)
    p0Denom = 2.0; p1Denom = 2.0 
    for i in range(numTrainDocs): # 遍历所有的文件,如果是侮辱性文件,就计算此侮辱性文件中出现的侮辱性单词的个数
#        print('trainMatrix[%d]:\n'%i,trainMatrix[i])
        if trainCategory[i] == 1: # 侮辱性单词在某个文档中出现
            p1Num += trainMatrix[i] # 该词对应个数加一,即分子把所有的文档向量按位置累加,trainMatrix[1]、[3]、[5]
            p1Denom += sum(trainMatrix[i]) # 侮辱性单词总数目(19个)
        else: # 非侮辱性的单词在对应位置加1
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i]) # 非侮辱性单词总数目(24个)
#    print('p1Num:',p1Num) #p1Num:[1. 0. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 1. 1. 3. 2. 1. 0. 1. 0. 0. 0. 1. 1. 1. 1. 1. 0. 0. 0. 2. 0.]
#    print('p1Denom:',p1Denom) #p1Denom: 19.0
#    print('p0Num:',p0Num) #p0Num: [1. 1. 1. 1. 1. 3. 2. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 1. 1. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1.]
#    print('p0Denom:',p0Denom) #p0Denom: 24.0
    # 计算后验概率
    #P(w1|1)=1/19,P(w2|1)=0,P(w3|1)=0,···,p(w31|1)=2/19,p(w32|1)=0
    #P(w1|0)=1/24,P(w2|0)=1/24,P(w3|0)=1/24,···,p(w32|0)=1/24
    # 防止太多的很小的数相乘造成下溢。对乘积取对数
#    p1Vect = p1Num/p1Denom #P(w1|1),P(w2|1),P(w3|1),···,p(w32|1)
#    p0Vect = p0Num/p0Denom #P(w1|0),P(w2|0),P(w3|0),···,p(w32|0)
    p1Vect = np.log(p1Num/p1Denom) # 取对数
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect, p1Vect, pAbusive # 返回属于侮辱类的条件概率数组p(1|w),属于非侮辱类的条件概率数组p(0|w),文档属于侮辱类的概率p(ci=1)

执行代码,运行结果:
在这里插入图片描述

测试算法:根据现实情况修改分类器

程序清单 4-3 朴素贝叶斯分类函数
"""
@函数说明:朴素贝叶斯分类函数
# 将乘法转换为加法
        乘法:P(C|w1w2...w32) = P(w1w2...w32n|C)P(C)/P(w1w2...w32)
  取对数,加法:P(w1|C)*P(w2|C)....P(wn|C)P(C) -> log(P(w1|C))+log(P(w2|C))+....+log(P(wn|C))+log(P(C))
Parameters:
	vec2Classify - 待测数据[0,1,1,1,1...],即待分类词条数组
	p0Vec - 侮辱类的条件概率数组
	p1Vec -非侮辱类的条件概率数组
	pClass1 - 文档属于侮辱类的概率
Returns:
	0 - 属于非侮辱类
	1 - 属于侮辱类
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 计算公式  log(P(w1|C))+log(P(w2|C))+....+log(P(wn|C))+log(P(C))
    # 使用 NumPy 数组来计算两个向量相乘的结果,这里的相乘是指对应元素相乘,即先将两个向量中的第一个元素相乘,然后将第2个元素相乘,以此类推。
    # 这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
    # 可以理解为 1.单词在词汇表中的条件下,文件是good 类别的概率 也可以理解为 2.在整个空间下,文件既在词汇表中又是good类别的概率
    p1 = np.sum(vec2Classify * p1Vec) + np.log(pClass1)
    p0 = np.sum(vec2Classify * p0Vec) + np.log(1 - pClass1)
    if p1 > p0: # 比较类别的概率,返回大概率对应的类别标签
        return 1 
    else:
        return 0
    
"""
@函数说明:测试朴素贝叶斯分类器
"""
def testingNB():
    listOPosts, listClasses = loadDataSet() # 创建实验样本
    myVocabList = createVocabList(listOPosts) # 创建不重复词汇表
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) # 将词汇表向量化
    p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses)) # 训练朴素贝叶斯分类器,ndarray数组点乘
    testEntry = ['love','my','dalmation'] # 测试样本1
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
    if classifyNB(thisDoc, p0V, p1V, pAb): # 执行分类并打印分类结果
        print(testEntry,'属于侮辱类文档')
    else:
        print(testEntry,'属于非侮辱类文档')
    testEntry = ['stupid', 'garbage'] # 测试样本2
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
    if classifyNB(thisDoc, p0V, p1V, pAb): # 执行分类并打印分类结果
        print(testEntry,'属于侮辱类文档')
    else:
        print(testEntry,'属于非侮辱类文档')

执行代码,运行结果:
在这里插入图片描述

准备数据:文档词袋模型

词集模型:将每个词的出现与否作为一个特征。即我们上面所用到的。
词袋模型:将每个词出现次数作为一个特征。每遇到一个单词,其词向量对应值 +1,而不是全设置为 1。
对函数setOfWords2Vec()进行修改,修改后的函数为bagOfWords2VecMN。

程序清单 4-4 朴素贝叶斯词袋模型
"""
@函数说明:根据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 # 返回词袋模型

示例:使用朴素贝叶斯过滤垃圾邮件

测试算法:使用朴素贝叶斯进行交叉验证

程序清单 4-5 文件解析及完整的垃圾邮件测试函数
# ------示例2: 使用朴素贝叶斯过滤垃圾邮件 -------------------------------------
"""
@函数说明:文本解析
#接收一个大字符串并将其解析为字符串列表
"""
def textParse(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 range(1,26): # 遍历25个txt文件
        wordList = textParse(open(r'D:\dataset\inaction\Naive Bayes\email\spam\%d.txt' % i, 'r').read()) # 读取每个垃圾邮件,并将文本转换成字符串列表
        docList.append(wordList) # 单词列表
        fullText.append(wordList) # 整个文档
        classList.append(1) # 标记垃圾文件,1为垃圾文件
        wordList = textParse(open(r'D:\dataset\inaction\Naive Bayes\email\ham\%d.txt' % i, 'r').read()) # 读取每个垃圾邮件,并将文本转换成字符串列表
        docList.append(wordList) # 单词列表
        fullText.append(wordList) # 整个文档
        classList.append(0) # 标记非垃圾文件,0为非垃圾文件
    vocabList = createVocabList(docList) # 创建不重复列表
    # 创建训练集和测试集
    trainingSet = list(range(50)); testSet = [] # 训练集和测试集列表
    for i in range(10): # 从50个文件中随机选取10个作为测试集,剩下的40个作为训练集
        randIndex = int(random.uniform(0, len(trainingSet))) # 随机选取索引
        testSet.append(trainingSet[randIndex]) # 添加到测试集
        del(trainingSet[randIndex]) # 在训练集中删除随机选取的10个测试集,留下40个为训练集
    # 创建训练集矩阵和训练集类别标签向量
    trainMat = []; trainClasses = [] # 训练集矩阵和训练集类别标签向量
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))  # 将生成的词集向量添加到训练矩阵中
        trainClasses.append(classList[docIndex]) # 将邮件分类的类别添加到训练集类别标签向量中
    # 训练模型
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses)) # 训练朴素贝叶斯模型
    # 计算错误率并且打印分类错误的测试集
    errorCount = 0 # 错误分类计数
    for docIndex in testSet: # 遍历测试集
        wordVector = setOfWords2Vec(vocabList, docList[docIndex]) # 测试集的词集向量
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: 
            errorCount += 1 # 邮件分类错误,错误分类计数加1
            print("分类错误的测试集:",docList[docIndex]) # 打印分类错误的测试集
    print('错误率:%.2f%%' % (float(errorCount) / len(testSet) *100)) # 打印错误率

执行代码,运行结果:
在这里插入图片描述
附所有代码:

# -*- coding: utf-8 -*-
import numpy as np
import re # 使用内置re模块,来使用正则表达式
import random
"""
贝叶斯公式
p(xy)=p(x|y)p(y)=p(y|x)p(x)
p(x|y)=p(y|x)p(x)/p(y)
"""
# ------示例1: 使用python进行文本分类(过滤侮辱类言论) -------------------------------------
"""
@函数说明:创建实验样本
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] # (6篇文档)类别标签向量,1 代表侮辱性文字,0 代表正常言论
    return postingList, classVec # 返回词条列表,所属类别

"""
@函数说明:创建一个包含在所有文档中出现的不重复词的列表
是用python的 Set 数据类型,不重复
将词条列表输给 Set 构造函数,set 就会返回一个不重复词表
"""
def createVocabList(dataSet):
    vocabSet = set([]) # 创建一个空的不重复列表
    for document in dataSet: # 将每篇文档返回的新词集合添加进去,即取两个集合的并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet) # 获得词汇表(32条),用来将词条向量化

"""
@函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
遍历遍历查看该单词是否出现,出现该单词则将该单词置1
Parameters:
    vocabList - createVocabList返回的不重复词条列表
    inputSet - 输入数据集,切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList) # 创建一个和词汇表等长的向量,将其元素都设置为 0
    for word in inputSet: # 遍历文档中所有单词
        if word in inputSet: # 如果出现词汇表中的单词,将输出的文档向量中的对应值设为 1
            returnVec[vocabList.index(word)] = 1
        else:
            print('the word: %s is not in my vocabulary!' % word)
    return returnVec # 返回文档向量,如[0, 0, 1, 1,...,]32条

"""
@函数说明:根据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 # 返回词袋模型

"""
@函数说明:朴素贝叶斯分类器训练函数
Parameters:
    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec文档向量构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 非侮辱类的条件概率数组
    p1Vect - 侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
Author:
"""
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix) # 训练文档的数目(6篇)
    numWords = len(trainMatrix[0]) # 每篇文档的词条数(32条)
    # 计算先验概率:p(1)=0.5,p(0)=1-0.5=0.5
    pAbusive = sum(trainCategory) / float(numTrainDocs) # 文档属于侮辱类的概率(p(1)=0.5),侮辱类文档数(类别为1的个个数)除以总文档数
#    print('pAbusive',pAbusive) #p(1)=0.5,p(0)=1-0.5=0.5
    #若其中一个概率为0,导致乘积为0,分子初始值修改为1,分母修改为2
#    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 类别中单词出现的个数初始化为0
#    p0Denom = 0.0; p1Denom = 0.0 # 分母初始化为0,整个数据集单词出现的个数(原来是0,后面改成2了)
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)
    p0Denom = 2.0; p1Denom = 2.0 
    for i in range(numTrainDocs): # 遍历所有的文件,如果是侮辱性文件,就计算此侮辱性文件中出现的侮辱性单词的个数
#        print('trainMatrix[%d]:\n'%i,trainMatrix[i])
        if trainCategory[i] == 1: # 侮辱性单词在某个文档中出现
            p1Num += trainMatrix[i] # 该词对应个数加一,即分子把所有的文档向量按位置累加,trainMatrix[1]、[3]、[5]
            p1Denom += sum(trainMatrix[i]) # 侮辱性单词总数目(19个)
        else: # 非侮辱性的单词在对应位置加1
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i]) # 非侮辱性单词总数目(24个)
#    print('p1Num:',p1Num) #p1Num:[1. 0. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 1. 1. 3. 2. 1. 0. 1. 0. 0. 0. 1. 1. 1. 1. 1. 0. 0. 0. 2. 0.]
#    print('p1Denom:',p1Denom) #p1Denom: 19.0
#    print('p0Num:',p0Num) #p0Num: [1. 1. 1. 1. 1. 3. 2. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 1. 1. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1.]
#    print('p0Denom:',p0Denom) #p0Denom: 24.0
    # 计算后验概率
    #P(w1|1)=1/19,P(w2|1)=0,P(w3|1)=0,···,p(w31|1)=2/19,p(w32|1)=0
    #P(w1|0)=1/24,P(w2|0)=1/24,P(w3|0)=1/24,···,p(w32|0)=1/24
    # 防止太多的很小的数相乘造成下溢。对乘积取对数
#    p1Vect = p1Num/p1Denom #P(w1|1),P(w2|1),P(w3|1),···,p(w32|1)
#    p0Vect = p0Num/p0Denom #P(w1|0),P(w2|0),P(w3|0),···,p(w32|0)
    p1Vect = np.log(p1Num/p1Denom) # 取对数
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect, p1Vect, pAbusive # 返回属于侮辱类的条件概率数组p(1|w),属于非侮辱类的条件概率数组p(0|w),文档属于侮辱类的概率p(ci=1)

"""
@函数说明:测试朴素贝叶斯训练函数
"""
def test():
    postingList, classVec = loadDataSet() # 加载构造的数据集
    print('postingList:\n',postingList) # 原始的词条列表
    myVocabList = createVocabList(postingList) # 不重复词条列表
    print('myVocbaList:\n',myVocabList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    print('trainMat:\n',trainMat) # 整个数据集词条列表向量
    print('classVec:\n',classVec) #  6篇文档所属类别[0, 1, 0, 1, 0, 1]
    p0V, p1V, pAb = trainNB0(trainMat, classVec)
    print('p0V:\n', p0V)
    print('p1V:\n', p1V)
    print('pAb:\n', pAb)
'''
 原始的朴素贝叶斯分类器执行结果
 p0V:   [p(第i个单词|非侮辱类) i=1,2,...,32]
 [0.04166667 0.04166667 0.04166667 0.04166667 0.04166667 0.125
 0.08333333 0.04166667 0.04166667 0.04166667 0.04166667 0.04166667
 0.         0.         0.         0.         0.         0.04166667
 0.         0.04166667 0.04166667 0.04166667 0.         0.
 0.         0.         0.         0.04166667 0.04166667 0.04166667
 0.04166667 0.04166667]
p1V:   [说明:p(第i个单词|侮辱类) i=1,2,...,32 其中,概率最大的为p1V[14]=3/19,对应到myVocbaList[14]='stupid',则stupid是最能代表侮辱类的单词]
 [0.05263158 0.         0.         0.         0.05263158 0.
 0.05263158 0.         0.         0.         0.         0.
 0.05263158 0.05263158 0.15789474 0.10526316 0.05263158 0.
 0.05263158 0.         0.         0.         0.05263158 0.05263158
 0.05263158 0.05263158 0.05263158 0.         0.         0.
 0.10526316 0.        ]
pAb:   [p(侮辱类)=0.5,p(非侮辱类)=0.5]
 0.5
 
 根据现实情况对分类器修改后:【取对数后取值为负数,但不影响最终结果】
 p0V:
 [-2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -1.87180218
 -2.15948425 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936
 -3.25809654 -3.25809654 -3.25809654 -3.25809654 -3.25809654 -2.56494936
 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654 -3.25809654
 -3.25809654 -3.25809654 -3.25809654 -2.56494936 -2.56494936 -2.56494936
 -2.56494936 -2.56494936]
p1V:
 [-2.35137526 -3.04452244 -3.04452244 -3.04452244 -2.35137526 -3.04452244
 -2.35137526 -3.04452244 -3.04452244 -3.04452244 -3.04452244 -3.04452244
 -2.35137526 -2.35137526 -1.65822808 -1.94591015 -2.35137526 -3.04452244
 -2.35137526 -3.04452244 -3.04452244 -3.04452244 -2.35137526 -2.35137526
 -2.35137526 -2.35137526 -2.35137526 -3.04452244 -3.04452244 -3.04452244
 -1.94591015 -3.04452244]
pAb:
 0.5
'''
     
"""
@函数说明:朴素贝叶斯分类函数
# 将乘法转换为加法
        乘法:P(C|w1w2...w32) = P(w1w2...w32n|C)P(C)/P(w1w2...w32)
  取对数,加法:P(w1|C)*P(w2|C)....P(wn|C)P(C) -> log(P(w1|C))+log(P(w2|C))+....+log(P(wn|C))+log(P(C))
Parameters:
	vec2Classify - 待测数据[0,1,1,1,1...],即待分类词条数组
	p0Vec - 侮辱类的条件概率数组
	p1Vec -非侮辱类的条件概率数组
	pClass1 - 文档属于侮辱类的概率
Returns:
	0 - 属于非侮辱类
	1 - 属于侮辱类
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 计算公式  log(P(w1|C))+log(P(w2|C))+....+log(P(wn|C))+log(P(C))
    # 使用 NumPy 数组来计算两个向量相乘的结果,这里的相乘是指对应元素相乘,即先将两个向量中的第一个元素相乘,然后将第2个元素相乘,以此类推。
    # 这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
    # 可以理解为 1.单词在词汇表中的条件下,文件是good 类别的概率 也可以理解为 2.在整个空间下,文件既在词汇表中又是good类别的概率
    p1 = np.sum(vec2Classify * p1Vec) + np.log(pClass1)
    p0 = np.sum(vec2Classify * p0Vec) + np.log(1 - pClass1)
    if p1 > p0: # 比较类别的概率,返回大概率对应的类别标签
        return 1 
    else:
        return 0
    
"""
@函数说明:测试朴素贝叶斯分类器
"""
def testingNB():
    listOPosts, listClasses = loadDataSet() # 创建实验样本
    myVocabList = createVocabList(listOPosts) # 创建不重复词汇表
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) # 将词汇表向量化
    p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses)) # 训练朴素贝叶斯分类器,ndarray数组点乘
    testEntry = ['love','my','dalmation'] # 测试样本1
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
    if classifyNB(thisDoc, p0V, p1V, pAb): # 执行分类并打印分类结果
        print(testEntry,'属于侮辱类文档')
    else:
        print(testEntry,'属于非侮辱类文档')
    testEntry = ['stupid', 'garbage'] # 测试样本2
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
    if classifyNB(thisDoc, p0V, p1V, pAb): # 执行分类并打印分类结果
        print(testEntry,'属于侮辱类文档')
    else:
        print(testEntry,'属于非侮辱类文档')
        
# ------示例2: 使用朴素贝叶斯过滤垃圾邮件 -------------------------------------
"""
@函数说明:文本解析
#接收一个大字符串并将其解析为字符串列表
"""
def textParse(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 range(1,26): # 遍历25个txt文件
        wordList = textParse(open(r'D:\dataset\inaction\Naive Bayes\email\spam\%d.txt' % i, 'r').read()) # 读取每个垃圾邮件,并将文本转换成字符串列表
        docList.append(wordList) # 单词列表
        fullText.append(wordList) # 整个文档
        classList.append(1) # 标记垃圾文件,1为垃圾文件
        wordList = textParse(open(r'D:\dataset\inaction\Naive Bayes\email\ham\%d.txt' % i, 'r').read()) # 读取每个垃圾邮件,并将文本转换成字符串列表
        docList.append(wordList) # 单词列表
        fullText.append(wordList) # 整个文档
        classList.append(0) # 标记非垃圾文件,0为非垃圾文件
    vocabList = createVocabList(docList) # 创建不重复列表
    # 创建训练集和测试集
    trainingSet = list(range(50)); testSet = [] # 训练集和测试集列表
    for i in range(10): # 从50个文件中随机选取10个作为测试集,剩下的40个作为训练集
        randIndex = int(random.uniform(0, len(trainingSet))) # 随机选取索引
        testSet.append(trainingSet[randIndex]) # 添加到测试集
        del(trainingSet[randIndex]) # 在训练集中删除随机选取的10个测试集,留下40个为训练集
    # 创建训练集矩阵和训练集类别标签向量
    trainMat = []; trainClasses = [] # 训练集矩阵和训练集类别标签向量
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))  # 将生成的词集向量添加到训练矩阵中
        trainClasses.append(classList[docIndex]) # 将邮件分类的类别添加到训练集类别标签向量中
    # 训练模型
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses)) # 训练朴素贝叶斯模型
    # 计算错误率并且打印分类错误的测试集
    errorCount = 0 # 错误分类计数
    for docIndex in testSet: # 遍历测试集
        wordVector = setOfWords2Vec(vocabList, docList[docIndex]) # 测试集的词集向量
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: 
            errorCount += 1 # 邮件分类错误,错误分类计数加1
            print("分类错误的测试集:",docList[docIndex]) # 打印分类错误的测试集
    print('错误率:%.2f%%' % (float(errorCount) / len(testSet) *100)) # 打印错误率
        
if __name__ == '__main__':
    
    test() # 测试训练函数
    testingNB() # 测试朴素贝叶斯分类器
    spamTest() # 测试垃圾邮件分类    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值