机器学习:基于概率的朴素贝叶斯分类器详解--Python实现以及项目实战
前言
前篇基础理论知识:机器学习:贝叶斯分类器详解(一)-贝叶斯决策理论与朴素贝叶斯
这篇主要使用代码实现贝叶斯分类。
一、准备数据
创建一个bayes.py程序,从文本中构建词向量,实现词表向向量转换函数。
from numpy import *
def loadDataSet():postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], # 分词可用wordcloud['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 #返回的第二个变量为人工标注用于区别侮辱性和非侮辱性的标签。#创建一个空集
def createVocabList(dataSet):vocabSet = set([])for document in dataSet:vocabSet = vocabSet | set(document) #创建两个集合的并集 划掉重复出现的单词return list(vocabSet)#处理样本输出为向量形式
def setOfWords2Vec(vocaList , inputSet):returnVec = [0]*len(vocaList)#创建一个其中所含元素全为0的向量代替文本for word in inputSet:if word in vocaList:returnVec[vocaList.index(word)] = 1else:print("the word:%s is not in my Vocabulary!"" % word")return returnVec
第一个函数创建了一些实验样本,第二个函数创建一个包含在所有文档中出现的不重复的列表,第三个函数输入参数为词汇表及某个文档,输出的是文档向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。
可检验函数是否正常工作:
listOPosts,listClasses=loadDataSet()
myVocabList=createVocabList(listOPosts)
print(myVocabList)
print(setOfWords2Vec(myVocabList,listOPosts[0]))
print(setOfWords2Vec(myVocabList,listOPosts[3]))
二、训练算法:从词向量计算概率
该函数伪代码如下:
根据前篇基础理论先求得P(w|ci),再计算P(ci)。
朴素贝叶斯分类器训练函数:
def trainNB0(trainMatrix,trainCategory):#朴素贝叶斯分类器训练函数。参数:1:向量化文档2:词条向量numTrainDocs = len(trainMatrix)#文本矩阵numWords = len(trainMatrix[0])pAbusive = sum(trainCategory)/float(numWords)p0Num = zeros(numWords);p1Num = zeros(numWords)#创建两个长度为词条向量等长的列表,平滑处理:初始值设为1p0Denom = 0.000001;p1Denom = 0.000001for 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 # 利用Numpy数组计算p(wi/c1),即类1条件下各词条出现的概率p0Vect = p0Num/p0Denom # 利用Numpy数组计算p(wi/c0),为避免下溢,后面会改为log()return p0Vect, p1Vect, pAbusive # 返回
由于当p0Num时会报RuntimeWarning: invalid value encountered in true_divide,这是由0/0导致,因此在设置p0Denom时不能设置为0.
首先,计算文档属于侮辱性文档(class=1)的概率,即P(1)。P(0)可由1-P(1)得到。
检验:
print(p1v)
print(p0v)
print(pAb)
结果:
利用贝叶斯分类器对文档进行分类时,要进行多个概率的乘积可获得文档属于某个类别额的概率,即计算p(w0|1)p(w1|1)p(w2|1)。其中一个概率值为0,那么最后的乘积也为0.我们可以将所有出现的词初始值初始化为1,并将分母初始化为2.
修改:
p0Num = ones(numWords); p1Num = ones(numWords)#创建两个长度为词条向量等长的列表,平滑处理:初始值设为1p0Denom = 2.0;p1Denom = 2.0#平滑处理,初始值设为2
另一个问题为下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci) * p(w1|ci) * p(w2|ci)... p(wn|ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
修改:
p1Vect = log(p1Num/p1Denom)#利用Numpy数组计算p(wi/c1),即类1条件下各词条出现的概率p0Vect = log(p0Num/p0Denom)#利用Numpy数组计算p(wi/c0),为避免下溢,后面会改为log()
三、分类函数
朴素贝叶斯分类函数:
#朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#注意参数2,3均已log化p1 = sum(vec2Classify * p1Vec) + log(pClass1)# P(w|c1) * P(c1) ,即贝叶斯准则的分子p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) # P(w|c0) * P(c0) ,即贝叶斯准则的分子if p1 > p0:return 1else:return 0"""使用算法:# 将乘法转换为加法乘法:P(C|F1F2...Fn) = P(F1F2...Fn|C)P(C)/P(F1F2...Fn)加法: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)):param vec2Classify: 待测数据[0,1,1,1,1...],即要分类的向量:param p0Vec: 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表:param p1Vec: 类别1,即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表:param pClass1: 类别1,侮辱性文件的出现概率:return: 类别1 or 0"""# 计算公式 log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
测试:
def testingNB():"""测试朴素贝叶斯算法"""# 1. 加载数据集listPosts,listClasses =loadDataSet()#2. 创建单词集合myVocabList = createVocabList(listPosts)#3.计算单词是否出现并创建数据矩阵trainMat = []for postinDoc in listPosts:trainMat.append(setOfWords2Vec(myVocabList,postinDoc))#4.训练数据p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))#5.测试数据testEntry = ['love','my','dalmation']thisDoc = array(setOfWords2Vec(myVocabList,testEntry))print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))testEntry = ['stupid', 'garbage']thisDoc = array(setOfWords2Vec(myVocabList, testEntry))print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
四、文档词袋模型
由于我们将每个词的出现作为一个特征,这可以被描述为词集模型。但单词往往有多义性,意味着一个单词在文档出现可能代表有不同的含义。这种方法被称为词袋模型。
在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数setOfWords2Vec稍加修改:
def bagOfWords2VecMN(vocaList , inputSet):returnVec = [0]*len(vocaList)#创建一个其中所含元素全为0的向量代替文本for word in inputSet:if word in vocaList:returnVec[vocaList.index(word)] += 1 #每遇到一个单词,相应加一else:print("the word:%s is not in my Vocabulary!"" % word")return returnVec
五、使用朴素贝叶斯过滤垃圾邮件
1.收集数据
使用朴素贝叶斯过滤垃圾邮件数据集
数据集说明: 数据集下包含两个文件夹,其中spam文件夹下为垃圾邮件,ham文件夹下为非垃圾邮件。
数据集格式: txt文件
2.准备数据(处理数据)
英文由于单词之间有空格,方便切分。中文有jieba库,有兴趣的可以了解一下。
myStr = 'This book is the best book on Python.'
myStr.split()['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python.']
但是最后一个词有标点符号,这个我们通过正则表达式解决,正则表达式在文本分类中是有很大作用的。
import re
regEx = re.compile('\\W*')
listOfTokens = regEx.split(myStr)
listOfTokens
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', '']
这里会有空字符串产生。我们可以计算字符串的长度,只返回字符串长度大于0的字符串。
[tok for tok in listOfTokens if len(tok)>0]
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python']
另外我们考虑构建词库,并不用考虑单词的大小写,全部改为小写
[tok.lower() for tok in listOfTokens if len(tok)>0]
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python']
这么一来我们就完成了简单文本的切分。当然一些文本也有非常复杂的处理方法,具体看文本的内容和性质。
3.测试算法:使用朴素贝叶斯进行交叉验证
直接贴上代码
def textParse(bigString):import relistOfTokens = re.split(r'\W*', bigString)return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():docList = [] # 文档(邮件)矩阵classList = [] # 类标签列表for i in range(1, 26):wordlist = textParse(open('trashclass/spam/{}.txt'.format(str(i))).read())docList.append(wordlist)classList.append(1)wordlist = textParse(open('trashclass/ham/{}.txt'.format(str(i))).read())docList.append(wordlist)classList.append(0)vocabList = bayes.createVocabList(docList) # 所有邮件内容的词汇表import picklefile=open('trashclass/vocabList.txt',mode='wb') #存储词汇表 二进制方式写入pickle.dump(vocabList,file)file.close()# 对需要测试的邮件,根据其词表fileWordList构造向量# 随机构建40训练集与10测试集trainingSet = list(range(50))testSet = []for i in range(10):randIndex = int(np.random.uniform(0, len(trainingSet)))testSet.append(trainingSet[randIndex])del (trainingSet[randIndex])trainMat = [] # 训练集trainClasses = [] # 训练集中向量的类标签列表for docIndex in trainingSet:# 使用词袋模式构造的向量组成训练集trainMat.append(bayes.setOfWords2Vec(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])p0v,p1v,pAb=bayes.trainNB0(trainMat,trainClasses)file=open('trashclass/threeRate.txt',mode='wb') #用以存储分类器的三个概率 二进制方式写入pickle.dump([p0v,p1v,pAb],file)file.close()errorCount=0for docIndex in testSet:wordVector=bayes.setOfWords2Vec(vocabList,docList[docIndex])if bayes.classifyNB(wordVector,p0v,p1v,pAb)!=classList[docIndex]:errorCount+=1return float(errorCount)/len(testSet)
加入序列化永久性保存对象,保存对象的字节序列到本地文件中。本例中共有50封电子邮件,其中10封电子邮件被随机选择为测试集合。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证。现在我们只作出一次迭代,为了更精确的估计分类器的错误率,我们应该多次迭代后求出平均错误率。
当然你也可以用:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.2,random_state=0)
方法很多很简单,这里不重复叙述。
(对了上面的代码setOfWords2Vec其实是bagOfWords2VecMN,我只是没有换名字而已,内容是bagOfWords2VecMN)
开始构造分类器:
import bayes
import numpy as np
import tkinter as tk
from tkinter import filedialog
def fileClassify(filepath):import picklefileWordList=textParse(open(filepath,mode='r').read())file=open('trashclass/vocabList.txt',mode='rb')vocabList=pickle.load(file)vocabList=vocabListfileWordVec=bayes.setOfWords2Vec(vocabList,fileWordList) #被判断文档的向量file=open('trashclass/threeRate.txt',mode='rb')rate=pickle.load(file)p0v=rate[0];p1v=rate[1];pAb=rate[2]return bayes.classifyNB(fileWordVec,p0v,p1v,pAb)if __name__=='__main__':print('朴素贝叶斯分类的错误率为:{}'.format(spamTest())) #测试算法的错误率# filepath=input('输入需判断的邮件路径')root = tk.Tk()root.withdraw()Filepath = filedialog.askopenfilename() # 获得选择好的文件print(Filepath)#判断某一路径下的邮件是否为垃圾邮件if fileClassify(Filepath)==1:print('垃圾邮件')else:print('非垃圾邮件')
这里我直接用Tk直接选路径懒得打了QWQ
完成!~~~~感觉不错就点个赞吧~
总结
实践才是硬道理。
参阅:
朴素贝叶斯算法 python 实现 - RamboBai - 博客园
《机器学习实战》源码解析(三):朴素贝叶斯_qq_45393426的博客-CSDN博客
机器学习3朴素贝叶斯 - 越影&逐日而行 - 博客园
使用朴素贝叶斯过滤垃圾邮件_会飞的哼哧的博客-CSDN博客
机器学习:基于概率的朴素贝叶斯分类器详解--Python实现以及项目实战相关推荐
- python数据挖掘课程】二十一.朴素贝叶斯分类器详解及中文文本舆情分析
#2018-04-06 13:52:30 April Friday the 14 week, the 096 day SZ SSMR python数据挖掘课程]二十一.朴素贝叶斯分类器详解及中文文本舆 ...
- 朴素贝叶斯分类器详解及中文文本舆情分析(附代码实践)
参加 2018 AI开发者大会,请点击 ↑↑↑ 作者 | 杨秀璋(笔名:Eastmount),贵州财经大学信息学院老师,硕士毕业于北京理工大学,主要研究方向是Web数据挖掘.知识图谱.Python数据 ...
- 【python数据挖掘课程】二十一.朴素贝叶斯分类器详解及中文文本舆情分析
这是<Python数据挖掘课程>系列文章,也是我上课内容及书籍中的一个案例.本文主要讲述朴素贝叶斯分类算法并实现中文数据集的舆情分析案例,希望这篇文章对大家有所帮助,提供些思路.内容包括: ...
- 朴素贝叶斯分类器详解+例子
b站贝叶斯讲解 来自b站的正月点灯笼大佬,讲解的很详细,很早以前看过,当时记得很清楚但是过段时间又忘了,所以打算写下来做个记录. 公式有点抽象,下面我们举一个例子来加深一下理解. 如下图,有8个样例. ...
- 贝叶斯分类器详解 从零开始 从理论到实践
贝叶斯分类器详解 从零开始 从理论到实践 大纲总览 一.贝叶斯相关概念 1.1.频率学派和贝叶斯学派 1.1.1.频率学派 1.1.2.贝叶斯学派 1.2.概率论基础知识 1.3.贝叶斯定理 二.概率 ...
- 用Python开始机器学习(6:朴素贝叶斯分类器)
朴素贝叶斯分类器是一个以贝叶斯定理为基础,广泛应用于情感分类领域的优美分类器.本文我们尝试使用该分类器来解决上一篇文章中影评态度分类. 1.贝叶斯定理 假设对于某个数据集,随机变量C表示样本为C类的概 ...
- 机器学习:伯努利朴素贝叶斯分类器(原理+python实现)
伯努利朴素贝叶斯分类器主要用于文本分类,下面我们以一个具体的例子,来讲述下伯努利朴素贝叶斯的原理和实现逻辑. 具体例子: 已知我们有八个句子以及每个句子对应的类别,即中性或侮辱性.那么再给出一个句子, ...
- python贝叶斯分类器_朴素贝叶斯分类器的简单Python实现
本文介绍如何使用Python实现一个简易的朴素贝叶斯分类器(Naive Baves classifier). 贝叶斯公式 我们先简单回顾一下贝叶斯公式: 其中,我们称P(A)和P(B)为先验概率,P( ...
- python装饰器详解-Python装饰器基础概念与用法详解
本文实例讲述了Python装饰器基础概念与用法.分享给大家供大家参考,具体如下: 装饰器基础 前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自 ...
最新文章
- centos7 安装 openssl
- 手写简版spring --6--应用上下文(BeanPostProcessor 和 BeanFactoryPostProcessor)
- 腾讯游戏主美:二次元卡通渲染有哪些黑科技?
- SAP Spartacus打印Occ endpoint请求的url
- android json.out,Android 之 json数据的解析(jsonReader)
- hadoop的作业提交过程之yarn
- ANTLR实现的SQL解析器 - OQL
- 16位顶尖对冲基金大佬:畅谈量化投资的下个10年!
- XILINX FPGA数字信号处理——4、CORDIC算法原理及实现
- [系统安装]_VirtualBox + slackware64-15.0-install-dvd + fiuxbox 窗口管理器 + 终端界面鼠标支持
- HTML 合并单元格(学生成绩管理表格)
- 计算机无法复制大文件格式,U盘复制文件电脑提示文件过大无法复制怎么解决?...
- ubuntu20.04不能切换输入法
- 三小时学会css(菜鸟教程精华版)【中】
- RFC 822 中文版 MIME解析基础(4)(第5-6也)
- 微信支付和支付宝支付整合(含设计模式1)
- AutoSar之微控制器抽象层MCAL
- 2022.04.21【日常维护】|服务器存储清理浅谈
- 前端js数组元素的筛选,修改,新增属性小技巧一---前端数据筛选filter()函数,更新数组map()函数;
- 那么,我是不工作会死啦?
热门文章
- vue项目中使用到的 git 指令 (github)
- Android Studio 4.1以上报错adb不是内部命令
- 免堆期由谁申请_Alan Logistics摘录:知识贴| 关于免箱期和免堆期
- blender节点实例 凸壳的应用
- Spring注解@Primary的意思
- 万人在线机房服务器配置,如何搭建小型服务器机房?
- Flutter 实现高仿开眼 APP 的页面开发 03
- 围棋——金字塔上的游戏
- win7下framework3.5 安装不成功的教训
- 天天基金估值数据接口http://j4.dfcfw.com/charts/pic6/基金代码.png