本文已参加「新人创造礼」活动,一起开启创造之路。
一、准备数据
创立一个bayes.py程序,从文本中构建词向量,完成词表向向量转换函数。
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)] = 1
else:
print("the word:%s is not in my Vocabulary!"" % word")
return returnVec
第一个函数创立了一些试验样本,第二个函数创立一个包括在一切文档中呈现的不重复的列表,第三个函数输入参数为词汇表及某个文档,输出的是文档向量,向量的每一个元素为1或0,分别表明词汇表中的单词在输入文档中是否呈现。
可查验函数是否正常工作:
myVocabList=createVocabList(listOPosts)
print(myVocabList)
print(setOfWords2Vec(myVocabList,listOPosts[0]))
print(setOfWords2Vec(myVocabList,listOPosts[3]))
二、练习算法:从词向量核算概率
该函数伪代码如下:
根据前篇基础理论先求得P(w|ci),再核算P(ci)。
朴素贝叶斯分类器练习函数:
numTrainDocs = len(trainMatrix)#文本矩阵
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numWords)
p0Num = zeros(numWords);p1Num = zeros(numWords)#创立两个长度为词条向量等长的列表,滑润处理:初始值设为1
p0Denom = 0.000001;p1Denom = 0.000001
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 # 运用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)得到。
查验:
运用贝叶斯分类器对文档进行分类时,要进行多个概率的乘积可取得文档归于某个类别额的概率,即核算p(w0|1)p(w1|1)p(w2|1)。其间一个概率值为0,那么最终的乘积也为0.咱们能够将一切呈现的词初始值初始化为1,并将分母初始化为2. 修正:
p0Denom = 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), 所以通过求对数能够防止下溢出或许浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何丢失。
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 1
else:
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))
测验:
"""
测验朴素贝叶斯算法
"""
# 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稍加修正:
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.split()
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python.']
可是最终一个词有标点符号,这个咱们通过正则表达式解决,正则表达式在文本分类中是有很大效果的。
regEx = re.compile('\\W*')
listOfTokens = regEx.split(myStr)
listOfTokens
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', '']
这儿会有空字符串产生。咱们能够核算字符串的长度,只回来字符串长度大于0的字符串。
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python']
另外咱们考虑构建词库,并不必考虑单词的大小写,悉数改为小写
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python']
这么一来咱们就完成了简单文本的切分。当然一些文本也有十分复杂的处理办法,具体看文本的内容和性质。
3.测验算法:运用朴素贝叶斯进行穿插验证
import re
listOfTokens = 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 pickle
file=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=0
for docIndex in testSet:
wordVector=bayes.setOfWords2Vec(vocabList,docList[docIndex])
if bayes.classifyNB(wordVector,p0v,p1v,pAb)!=classList[docIndex]:
errorCount+=1
return float(errorCount)/len(testSet)
参加序列化永久性保存目标,保存目标的字节序列到本地文件中。本例中共有50封电子邮件,其间10封电子邮件被随机挑选为测验集合。挑选出的数字所对应的文档被添加到测验集,同时也将其从练习会集除掉。这种随机挑选数据的一部分作为练习集,而剩余部分作为测验集的过程称为留存穿插验证。现在咱们只作出一次迭代,为了更精确的估计分类器的错误率,咱们应该屡次迭代后求出平均错误率。
当然你也能够用:
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2,
random_state=0)
办法许多很简单,这儿不重复叙述。
(对了上面的代码setOfWords2Vec其实是bagOfWords2VecMN,我仅仅没有换姓名而已,内容是bagOfWords2VecMN)
开始结构分类器:
import numpy as np
import tkinter as tk
from tkinter import filedialog
def fileClassify(filepath):
import pickle
fileWordList=textParse(open(filepath,mode='r').read())
file=open('trashclass/vocabList.txt',mode='rb')
vocabList=pickle.load(file)
vocabList=vocabList
fileWordVec=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
点重视,防走丢,如有纰漏之处,请留言指教,十分感谢
以上便是本期悉数内容。我是fanstuck ,有问题咱们随时留言评论 ,咱们下期见。