实现 | 朴素贝叶斯模型算法研究与实例分析
(白宁超  2018年9月4日10:28:49)

导读:朴素贝叶斯模型是机器学习常用的模型算法之一,其在文本分类方面简单易行,且取得不错的分类效果。所以很受欢迎,对于朴素贝叶斯的学习,本文首先介绍理论知识即朴素贝叶斯相关概念和公式推导,为了加深理解,采用一个维基百科上面性别分类例子进行形式化描述。然后通过编程实现朴素贝叶斯分类算法,并在屏蔽社区言论、垃圾邮件、个人广告中获取区域倾向等几个方面进行应用,包括创建数据集、数据预处理、词集模型和词袋模型、朴素贝叶斯模型训练和优化等。然后结合复旦大学新闻语料进行朴素贝叶斯的应用。最后,大家熟悉其原理和实现之后,采用机器学习sklearn包进行实现和优化。由于篇幅较长,采用理论理解、案例实现、sklearn优化三个部分进行学习。(本文原创,转载必须注明出处:朴素贝叶斯模型算法研究与实例分析)

案例场景1: 屏蔽社区留言板的侮辱性言论

项目概述

构建一个快速过滤器来屏蔽在线社区留言板上的侮辱性言论。如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标识为内容不当。对此问题建立两个类别: 侮辱类和非侮辱类,使用 1 和 0 分别表示。

本案例开发流程如下:

  1. 收集数据: 可以是文本数据、数据库数据、网络爬取的数据、自定义数据等等
  2. 数据预处理: 对采集数据进行格式化处理,文本数据的格式一致化,网络数据的分析抽取等,包括中文分词、停用词处理、词袋模型、构建词向量等。
  3. 分析数据: 检查词条确保解析的正确性,根据特征进行模型选择、特征抽取等。
  4. 训练算法: 从词向量计算概率
  5. 测试算法: 根据现实情况修改分类器
  6. 使用算法: 对社区留言板言论进行分类

收集数据

本案例我们采用自定义的数据集,我们选择6条社区评论,然后进行数据处理后以list形式存储在文档列表postingList中。其中每个词代表一个特征。将每条评论进行分类(即1代表侮辱性文字,0代表非侮辱文字)存在在类别列表classVec中。最后返回数据集和类标签。代码实现如下:

'''创建数据集:单词列表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

代码分析:postingList列表存储6条评论信息,classVec列表存储每条信息类别(1代表侮辱性文字,0代表非侮辱文字)。最后返回文档列表和类别列表。

数据预处理

数据预处理包括对样本进行分词、词性筛选、停用词处理等,最后形成规范化干净的数据样本。由于本案例收集数据时默认进行了数据预处理,所以本节不在介绍(复旦新闻语料文本分类案例会详细介绍)。目前,我们采集的数据还是文本类型,计算机还不能直接处理,需要将文本数据转化成词向量进行处理。这里面需要获取特征的词汇集合(如果暂时不理解,先看看代码实现,下面会进行形式化描述)。其实现过程如下:

'''获取所有单词的集合:返回不含重复元素的单词列表'''
def createVocabList(dataSet):vocabSet = set([])for document in dataSet:vocabSet = vocabSet | set(document)  # 操作符 | 用于求两个集合的并集# print(vocabSet)return list(vocabSet)

代码分析:方法参数dataSet即加载数据集返回的文档列表。vocabSet是定义的不重复的数据集合。然后for循环对文档列表每条数据进行遍历处理,将不重复的词汇添加到vocabSet中,最终形成整个文档的词汇集,然后以list形式返回。

上面的方法已经获取了整个文档词汇集合,接着构建数据矩阵,代码实现如下:

'''词集模型构建数据矩阵'''
def setOfWords2Vec(vocabList, inputSet):# 创建一个和词汇表等长的向量,并将其元素都设置为0returnVec = [0] * len(vocabList)# 遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1for word in inputSet:if word in vocabList:returnVec[vocabList.index(word)] = 1else:print("单词: %s 不在词汇表之中!" % word)# print(returnVec)return returnVec

代码分析:本方法提供两个参数分别是整个训练文档词汇集(即全部训练文档6条评论不重复的单词集合),输入的数据列表。以整个词汇集等长的0向量。我们遍历输入数据列表,如果词特征在词汇集则标记1,不在词汇集保持为0.最后返回词向量矩阵。

与词集模型对应的,有个词袋模型。两者都是构建词向量,只是方式不一样,词袋模型也是推荐使用的词向量化方法,其实现如下:

'''文档词袋模型构建数据矩阵'''
def bagOfWords2VecMN(vocabList, inputSet):returnVec = [0] * len(vocabList)for word in inputSet:if word in vocabList:returnVec[vocabList.index(word)] += 1# print(returnVec)return returnVec

分析数据

运行词集模型setOfWords2Vec(vocabList, dataSet[0])运行结果如下:

['dog', 'to', 'take', 'park', 'licks', 'has', 'help', 'stupid', 'him', 'so', 'not', 'love', 'buying', 'problems', 'cute', 'stop', 'steak', 'how', 'flea', 'maybe', 'food', 'I', 'please', 'dalmation', 'mr', 'posting', 'ate', 'garbage', 'worthless', 'my', 'is', 'quit']
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0]

结果分析:我们将dataSet[0]即第一条信息['my', 'dog', 'has', 'flea', 'problems', 'help', 'please']构建词集模型,词特征集为['dog', 'to', 'take', 'park', 'licks', 'has', 'help', 'stupid', 'him', 'so', 'not', 'love', 'buying', 'problems', 'cute', 'stop', 'steak', 'how', 'flea', 'maybe', 'food', 'I', 'please', 'dalmation', 'mr', 'posting', 'ate', 'garbage', 'worthless', 'my', 'is', 'quit']。结果显示[1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0]。即词特征集dog在dataSet[0]中,标记为1,to不在则保留原始的0.以此类推。我们也可以查看dataSet[1]等数据结果。

数据样本分析仅仅如上所述?当然不是,本例子中数据量比较小,容易分析。当数据量比较大,特征数以万计之时,人工分析就显得捉襟见肘了。我们可以采用图形化分析方法,根据具体业务需求,可以选择基于python自带的matplotlib可视化分析、或者其他图形可视化工具进行平面或多维数据分析,然后便于特征的选择。

如果是中文分词,我们还可以对词性进行分析,然后选择相应的词性特征,比如名词、动词、地名、人名、机构名等等,对虚词、助词等进行过滤,一方面达到数据降维另一方面防止模型训练拟合化等问题。

训练模型

现在已经知道了一个词是否出现在一篇文档中,也知道该文档所属的类别。接下来我们重写贝叶斯准则,将之前的 x, y 替换为 w. 粗体的 w 表示这是一个向量,即它由多个值组成。在这个例子中,数值个数与词汇表中的词个数相同。

我们使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。根据上述公式可知,我们右边的式子等同于左边的式子,由于对于每个ci,P(w)是固定的。并且我们只需要比较左边式子值的大小来决策分类,那么我们就可以简化为通过比较右边分子值得大小来做决策分类。

首先可以通过类别 i (侮辱性留言或者非侮辱性留言)中的文档数除以总的文档数来计算概率 。接下来计算 ,这里就要用到朴素贝叶斯假设。如果将 w 展开为一个个独立特征,那么就可以将上述概率写作。这里假设所有词都互相独立,该假设也称作条件独立性假设(例如 A 和 B 两个人抛骰子,概率是互不影响的,也就是相互独立的,A 抛 2点的同时 B 抛 3 点的概率就是 1/6 * 1/6),它意味着可以使用来计算上述概率,这样就极大地简化了计算的过程。具体代码实现如下:

'''朴素贝叶斯分类器训练函数'''
def _trainNB0(trainMatrix, trainCategory):numTrainDocs = len(trainMatrix) # 文件数numWords = len(trainMatrix[0]) # 单词数# 侮辱性文件的出现概率,即trainCategory中所有的1的个数,# 代表的就是多少个侮辱性文件,与文件的总数相除就得到了侮辱性文件的出现概率pAbusive = sum(trainCategory) / float(numTrainDocs)# 构造单词出现次数列表p0Num = zeros(numWords) # [0,0,0,.....]p1Num = zeros(numWords) # [0,0,0,.....]p0Denom = 0.0;p1Denom = 0.0 # 整个数据集单词出现总数for i in range(numTrainDocs):# 遍历所有的文件,如果是侮辱性文件,就计算此侮辱性文件中出现的侮辱性单词的个数if trainCategory[i] == 1:p1Num += trainMatrix[i] #[0,1,1,....]->[0,1,1,...]p1Denom += sum(trainMatrix[i])else:# 如果不是侮辱性文件,则计算非侮辱性文件中出现的侮辱性单词的个数p0Num += trainMatrix[i]p0Denom += sum(trainMatrix[i])# 类别1,即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表# 即 在1类别下,每个单词出现次数的占比p1Vect = p1Num / p1Denom# [1,2,3,5]/90->[1/90,...]# 类别0,即正常文档的[P(F1|C0),P(F2|C0),P(F3|C0),P(F4|C0),P(F5|C0)....]列表# 即 在0类别下,每个单词出现次数的占比p0Vect = p0Num / p0Denomreturn p0Vect, p1Vect, pAbusive

代码分析:本方法参数分别是文档特征向量矩阵和文档类别向量矩阵。首先计算侮辱性文档占总文档的概率,然后计算正常文档下特征词的概率向量和侮辱性特征词的向量,为了更好理解上面的代码我们看下运行p0V,p1V,pAb=_trainNB0(trainMatrix,Classlabels)结果:

词汇表集
['I', 'cute', 'help', 'dalmation', 'please', 'has', 'my', 'him', 'worthless', 'problems', 'so', 'mr', 'flea', 'love', 'take', 'stupid', 'dog', 'park', 'how', 'quit', 'buying', 'posting', 'steak', 'maybe', 'to', 'is', 'ate', 'not', 'garbage', 'food', 'stop', 'licks']
各条评论特征向量,其中1,3,5条为类别0;2,4,6条为类别1
[0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0]
[1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
类别0下特征词条件概率
[0.04166667 0.04166667 0.04166667 0.04166667 0.04166667 0.041666670.125      0.08333333 0.         0.04166667 0.04166667 0.041666670.04166667 0.04166667 0.         0.         0.04166667 0.0.04166667 0.         0.         0.         0.04166667 0.0.04166667 0.04166667 0.04166667 0.         0.         0.0.04166667 0.04166667] 类别1下特征词条件概率
[0.         0.         0.         0.         0.         0.0.         0.05263158 0.10526316 0.         0.         0.0.         0.         0.05263158 0.15789474 0.10526316 0.052631580.         0.05263158 0.05263158 0.05263158 0.         0.052631580.05263158 0.         0.         0.05263158 0.05263158 0.052631580.05263158 0.        ] 0.5

结果分析:结合结果我们去理解上面的训练模型代码。首先最后一个是0.5代表侮辱性文档占全部文档的50%即一半,实际上我们标记3个正常评论词条,3个非正常的,这个显然正确。其次,第一次词I在类别1中出现0次,在类别0中出现1次。对应的条件概率分别是0.04166667和0

测试算法

在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 。如果其中一个概率值为 0,那么最后的乘积也为 0。为降低这种影响,可以将所有词的出现数初始化为 1,并将分母初始化为 2 (取1 或 2 的目的主要是为了保证分子和分母不为0,大家可以根据业务需求进行更改)。

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

下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。

根据朴素贝叶斯公式,我们观察分子进行条件概率连乘时候,由于有条件概率极小或者为0,最后导致结果为0 ,显然不符合我们预期结果,因此对训练模型进行优化,其优化代码如下:

'''训练数据优化版本'''
def trainNB0(trainMatrix, trainCategory):numTrainDocs = len(trainMatrix) # 总文件数numWords = len(trainMatrix[0]) # 总单词数pAbusive = sum(trainCategory) / float(numTrainDocs) # 侮辱性文件的出现概率# 构造单词出现次数列表,p0Num 正常的统计,p1Num 侮辱的统计# 避免单词列表中的任何一个单词为0,而导致最后的乘积为0,所以将每个单词的出现次数初始化为 1p0Num = ones(numWords)#[0,0......]->[1,1,1,1,1.....],ones初始化1的矩阵p1Num = ones(numWords)# 整个数据集单词出现总数,2.0根据样本实际调查结果调整分母的值(2主要是避免分母为0,当然值可以调整)# p0Denom 正常的统计# p1Denom 侮辱的统计p0Denom = 2.0p1Denom = 2.0for i in range(numTrainDocs):if trainCategory[i] == 1:p1Num += trainMatrix[i]  # 累加辱骂词的频次p1Denom += sum(trainMatrix[i]) # 对每篇文章的辱骂的频次 进行统计汇总else:p0Num += trainMatrix[i]p0Denom += sum(trainMatrix[i])# 类别1,即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表,取对数避免下溢出或浮点舍入出错p1Vect = log(p1Num / p1Denom)# 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表p0Vect = log(p0Num / p0Denom)return p0Vect, p1Vect, pAbusive

我们再看生成条件概率结果如下:

[-2.56494936 -2.15948425 -3.25809654 -2.56494936 -3.25809654 -3.25809654-2.56494936 -2.56494936 -3.25809654 -2.56494936 -3.25809654 -3.25809654
 -2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936
 -2.56494936 -2.56494936 -1.87180218 -2.56494936 -3.25809654 -2.56494936
 -2.56494936 -2.56494936 -2.56494936 -3.25809654 -3.25809654 -2.56494936
 -3.25809654 -2.56494936]
 [-3.04452244 -2.35137526 -2.35137526 -3.04452244 -2.35137526 -2.35137526-3.04452244 -3.04452244 -1.94591015 -2.35137526 -2.35137526 -2.35137526
 -3.04452244 -2.35137526 -3.04452244 -1.65822808 -1.94591015 -3.04452244
 -3.04452244 -3.04452244 -3.04452244 -3.04452244 -2.35137526 -3.04452244
 -3.04452244 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -3.04452244
 -2.35137526 -3.04452244]
 0.5

使用算法对社区留言板言论进行分类

构建朴素贝叶斯分类函数

将乘法转换为加法
乘法:

加法:

'''
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    # 使用 NumPy 数组来计算两个向量相乘的结果,这里的相乘是指对应元素相乘,即先将两个向量中的第一个元素相乘,然后将第2个元素相乘,以此类推。这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

测试朴素贝叶斯算法

结合上面分析流程和实现方法,我们综合测试朴素贝叶斯对评论信息分类如下:

'''朴素贝叶斯算法屏蔽社区留言板的侮辱性言论的应用'''
def testingNB():# 1. 加载数据集dataSet, Classlabels = loadDataSet()# 2. 创建单词集合myVocabList = createVocabList(dataSet)# 3. 计算单词是否出现并创建数据矩阵trainMat = []for postinDoc in dataSet:# 返回m*len(myVocabList)的矩阵, 记录的都是0,1信息trainMat.append(setOfWords2Vec(myVocabList, postinDoc))# print('test',len(array(trainMat)[0]))# 4. 训练数据p0V, p1V, pAb = trainNB0(array(trainMat), array(Classlabels))# 5. 测试数据testEntry = ['love', 'my', 'dalmation']thisDoc = array(setOfWords2Vec(myVocabList, testEntry))print(testEntry, '分类结果是: ', classifyNB(thisDoc, p0V, p1V, pAb))testEntry = ['stupid', 'garbage']thisDoc = array(setOfWords2Vec(myVocabList, testEntry))print(testEntry, '分类结果是: ', classifyNB(thisDoc, p0V, p1V, pAb))

运行结果如下:

['love', 'my', 'dalmation'] 分类结果是:  0
['stupid', 'garbage'] 分类结果是:  1

案例场景2: 对社区留言板言论进行分类

项目概述

我们运行朴素贝叶斯分类进行电子邮件垃圾过滤。在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。我们可以观察文档中出现的词,并把每个词作为一个特征,而每个词的出现或者不出现作为该特征的值,这样得到的特征数目就会跟词汇表中的词的数目一样多。

本项目开发流程如下:

收集数据: 提供文本文件
准备数据: 将文本文件解析成词条向量
分析数据: 检查词条确保解析的正确性
训练算法: 使用我们之前建立的 trainNB() 函数
测试算法: 使用朴素贝叶斯进行交叉验证
使用算法: 构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上

收集数据并预处理

邮件格式内容如下:

对邮件进行读取实现如下:

'''读取文本'''
def testParseTest():print(textParse(open('./email/ham/1.txt').read()))

准备数据

对读取的文本进行词条向量化,其实现如下:

'''接收一个大字符串并将其解析为字符串列表'''
def textParse(bigString):import re# 使用正则表达式来切分句子,其中分隔符是除单词、数字外的任意字符串listOfTokens = re.split(r'\W*', bigString)return [tok.lower() for tok in listOfTokens if len(tok) > 2]

分析数据

这个部分在案例场景1进行了详细描述,此处不在赘述。

训练算法

此处,我们去调用在场景1优化过的朴素贝叶斯训练模型trainNB0() 函数,这里也是一劳永逸的方法。还可以对数据预处理等进行封装。

测试算法

本测试方法中使用的数据集,即文档可以参见下文代码下载。采用方法跟场景1基本类似,这里不作代码解析。具体实现代码如下:

'''对贝叶斯垃圾邮件分类器进行自动化处理。'''
def spamTest():docList = [];classList = [];fullText = [] # 文档列表、类别列表、文本特征for i in range(1, 26): # 总共25个文档# 切分,解析数据,并归类为 1 类别wordList = textParse(open('./email/spam/%d.txt' % i).read())docList.append(wordList)classList.append(1)# 切分,解析数据,并归类为 0 类别wordList = textParse(open('./email/ham/%d.txt' % i,encoding='UTF-8').read())docList.append(wordList)classList.append(0)fullText.extend(wordList)# 创建词汇表vocabList = createVocabList(docList)trainingSet = list(range(50)) # 词汇表文档索引testSet = []# 随机取 10 个邮件用来测试for i in range(10):# random.uniform(x, y) 随机生成一个范围为 x - y 的实数randIndex = int(random.uniform(0, len(trainingSet)))testSet.append(trainingSet[randIndex]) # 随机抽取测试样本del(trainingSet[randIndex]) # 训练集中删除选择为测试集的文档trainMat = [];trainClasses = [] # 训练集合训练标签for docIndex in trainingSet:trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))errorCount = 0for docIndex in testSet:wordVector = setOfWords2Vec(vocabList, docList[docIndex])if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:errorCount += 1print('the errorCount is: ', errorCount)print('the testSet length is :', len(testSet))print('the error rate is :', float(errorCount)/len(testSet))

运行结果如下:

the errorCount is:  2
the testSet length is : 10
the error rate is : 0.2

案例场景3: 使用朴素贝叶斯分类器从个人广告中获取区域倾向

项目概述

广告商往往想知道关于一个人的一些特定人口统计信息,以便能更好地定向推销广告。我们将分别从美国的两个城市中选取一些人,通过分析这些人发布的信息,来比较这两个城市的人们在广告用词上是否不同。如果结论确实不同,那么他们各自常用的词是哪些,从人们的用词当中,我们能否对不同城市的人所关心的内容有所了解。

开发流程如下:

收集数据: 从 RSS 源收集内容,这里需要对 RSS 源构建一个接口
准备数据: 将文本文件解析成词条向量
分析数据: 检查词条确保解析的正确性
训练算法: 使用我们之前建立的 trainNB0() 函数
测试算法: 观察错误率,确保分类器可用。可以修改切分程序,以降低错误率,提高分类结果
使用算法: 构建一个完整的程序,封装所有内容。给定两个 RSS 源,改程序会显示最常用的公共词

收集数据

从 RSS 源收集内容,这里需要对 RSS 源构建一个接口,也就是导入 RSS 源,我们使用 python 下载文本,在http://code.google.com/p/feedparser/ 下浏览相关文档,安装 feedparse,首先解压下载的包,并将当前目录切换到解压文件所在的文件夹,然后在 python 提示符下输入:python setup.py install

准备数据

文档词袋模型

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

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

这部分在场景1中已经构建完成,并进行了阐述。

分析数据

这个部分在案例场景1进行了详细描述,此处不在赘述。

训练算法

此处,我们去调用在场景1优化过的朴素贝叶斯训练模型trainNB0() 函数,这里也是一劳永逸的方法。还可以对数据预处理等进行封装。

测试算法

观察错误率,确保分类器可用。可以修改切分程序,以降低错误率,提高分类结果。其具体实现如下:

'''RSS源分类器及高频词去除函数'''
def calcMostFreq(vocabList,fullText):import operatorfreqDict={}for token in vocabList:  #遍历词汇表中的每个词freqDict[token]=fullText.count(token)  #统计每个词在文本中出现的次数sortedFreq=sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)  #根据每个词出现的次数从高到底对字典进行排序return sortedFreq[:30]   #返回出现次数最高的30个单词def localWords(feed1,feed0):# import feedparser # feedparser是python中最常用的RSS程序库docList=[];classList=[];fullText=[]minLen=min(len(feed1['entries']),len(feed0['entries'])) # entries内容无法抓取,网站涉及反爬虫技术print(len(feed1['entries']),len(feed0['entries']))for i in range(minLen):wordList=textParse(feed1['entries'][i]['summary'])   #每次访问一条RSS源docList.append(wordList)fullText.extend(wordList)classList.append(1)wordList=textParse(feed0['entries'][i]['summary'])docList.append(wordList)fullText.extend(wordList)classList.append(0)vocabList=createVocabList(docList)top30Words=calcMostFreq(vocabList,fullText)for pairW in top30Words:if pairW[0] in vocabList:vocabList.remove(pairW[0])    #去掉出现次数最高的那些词trainingSet=range(2*minLen);testSet=[]for i in range(20):randIndex=int(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(array(trainMat),array(trainClasses))errorCount=0for docIndex in testSet:wordVector=bagOfWords2VecMN(vocabList,docList[docIndex])if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:errorCount+=1print('the error rate is:',float(errorCount)/len(testSet))return vocabList,p0V,p1V

运行结果:

ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
# print(ny)
vocabList,pSF,pNY=localWords(ny,sf)

由于如上两个地址抓取,得到feed0['entries']为空,所以没有进行结果分析,读者可以试用其他rss地址进行处理。如下是采用之前网站反爬虫抓取前的分析结果:

vocabList,pSF,pNY=bayes.localWords(ny,sf)
the error rate is: 0.2
vocabList,pSF,pNY=bayes.localWords(ny,sf)
the error rate is: 0.3
vocabList,pSF,pNY=bayes.localWords(ny,sf)
the error rate is: 0.55

参考文献

  1. scikit中文社区:http://sklearn.apachecn.org/cn/0.19.0/
  2. 中文维基百科:https://zh.wikipedia.org/wiki/
  3. 文本分类特征选择:https://www.cnblogs.com/june0507/p/7601001.html
  4. GitHub:https://github.com/BaiNingchao/MachineLearning-1
  5. 图书:《机器学习实战》
  6. 图书:《自然语言处理理论与实战》

完整代码下载

源码请进【机器学习和自然语言QQ群:436303759】文件下载:

作者声明

本文版权归作者白宁超所有,本文原创,旨在学术和科研使用。文章同步如下:

  • 博客园 http://www.cnblogs.com/baiboy/
  • 我爱自然语言处理 http://www.52nlp.cn/author/baiboy
  • 阿里云栖 https://yq.aliyun.com/u/baiboy
  • 腾讯云社区https://cloud.tencent.com/developer/user/2991686

实现 | 朴素贝叶斯模型算法研究与实例分析相关推荐

  1. 一步步教你轻松学朴素贝叶斯模型算法理论篇1

    一步步教你轻松学朴素贝叶斯模型理论篇1 (白宁超2018年9月3日17:51:32) 导读:朴素贝叶斯模型是机器学习常用的模型算法之一,其在文本分类方面简单易行,且取得不错的分类效果.所以很受欢迎,对 ...

  2. 机器学习:朴素贝叶斯模型算法原理(含实战案例)

    机器学习:朴素贝叶斯模型算法原理 作者:i阿极 作者简介:Python领域新星作者.多项比赛获奖者:博主个人首页

  3. 机器学习第六章之朴素贝叶斯模型

    朴素贝叶斯模型(了解) 6.1 朴素贝叶斯模型算法原理 6.1.1 一维特征向量下的贝叶斯模型 6.1.2 二维特征向量下的贝叶斯模型 6.1.3 n维特征向量下的贝叶斯模型 6.1.4 朴素贝叶斯模 ...

  4. EM算法--应用到三个模型: 高斯混合模型 ,混合朴素贝叶斯模型,因子分析模型...

    主要是对Ng教授的machinelearning视频学习和参考jerryLead讲义整理(特别鸣谢~): 由"判别模型.生成模型与朴素贝叶斯方法 "一节得知: 判别模型求的是条件概 ...

  5. 机器学习算法之朴素贝叶斯模型

    基本原理 从统计学知识回到我们的数据分析.假如我们的分类模型样本是: 即我们有m个样本,每个样本有n个特征,特征输出有k个类别,定义为C1,C2,-,Ck,.从样本我们可以学习得到朴素贝叶斯的先验分布 ...

  6. 人工智能算法一朴素贝叶斯模型

    简介 朴素贝叶斯模型主要是解决逻辑回归多维不能解耦的情况. 前提描述 在讲解朴素贝叶斯模型的时候先回归下逻辑回归的函数. 如果计算多维的时候比如在婚恋网的预测的时候,根据x1身高,x2体重,x3收入, ...

  7. 从零开始学Python【38】--朴素贝叶斯模型(实战部分)

    [前言] 在<从零开始学Python[37]--朴素贝叶斯模型(理论部分)>中我们详细介绍了朴素贝叶斯算法的基本概念和理论知识,在这一期我们继续介绍该算法的实战案例.将会对高斯贝叶斯.多项 ...

  8. Spark MLlib 源码学习---朴素贝叶斯模型(Naive Bayes)

    朴素贝叶斯是机器学习中比较常用的一种模型,尤其在文本分类的问题上是比较常用的baseline.朴素贝叶斯本身训练速度快,具有可并行化程度高,可解释性好的优点,但由于其对特征之间的独立性假设不是很符合某 ...

  9. 用朴素贝叶斯模型预测柯南中被害人和凶手!

    本文来自公众号:超级数学建模 微信号:supermodeling 作者:周铂 本文长度为3000字,建议阅读5分钟 本文介绍朴素贝叶斯模型通过角色特征(性格.行为.与他人关系等)预测其身份(凶手/被害 ...

最新文章

  1. 西瓜书第二章 模型评估与选择
  2. CodeForces - 17E Palisection(回文自动机/Palindrome Series优化dp)
  3. 每周总结(第十一周)
  4. Android设计模式之——责任链模式
  5. 程序员分析了 50 万条拼多多商品数据,告诉你到底是消费升级还是降级!
  6. 三星明星机又悲剧了!万元折叠屏,玩不过2天,组团黑屏,蜜汁凸起
  7. python基础笔记_python基础笔记
  8. 图算法——欧拉回路问题的解答
  9. ipmitool介绍_ipmitool管理工具
  10. 【路径规划】基于matlab GUI蚁群算法求解电动汽车充电站与换电站协调路径规划【含Matlab源码 796期】
  11. 《21天学通C语言》总结(1)
  12. J2EE学习篇之--Struts1详解
  13. ucfirst() 把字符串中的首字符转换为大写
  14. 线性代数 【23】 概念的深入01 - Points坐标点和Vectors向量
  15. java JPG等图片格式转成PGM
  16. Win11 的这 19 个新功能,你都用上了吗?
  17. 生信自学笔记(五)计分矩阵的实例
  18. 常见的HTTP状态码以及代表的意义
  19. 用力和应变片计算弹性模量_材料弹性模量及泊松比测试实验教案.
  20. 国内DRGs发展之路

热门文章

  1. Matlab按照二进制读写txt文件
  2. 简述igp和egp_路由协议的常见分类——GGP、EGP和IGP介绍
  3. 深度学习框架zf_深度学习十大框架比较
  4. Java实现谷歌验证器
  5. mysql笔记整理2(聚合函数).md
  6. 科技企业家的自恋行为启示录
  7. 基于Arduino的超声波悬浮
  8. MySQL语句练习---由简入繁
  9. 安卓测试二(Espresso)
  10. Python爬虫:给我一个链接,虎牙视频随便下载