机器学习实战(三):朴素贝叶斯
一、概述
1、什么是朴素贝叶斯算法
朴素贝叶斯算法是有监督的学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。
2、优点
简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。
3、概率公式
贝叶斯推断
P(A):先验概率(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B):后验概率(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B):可能性函数(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。
因此,条件概率可以理解成:后验概率 = 先验概率 * 调整因子
这就是贝叶斯推断的含义。我们先预估一个"先验概率",然后加入实验结果,看这个实验到底是增强还是削弱了"先验概率",由此得到更接近事实的"后验概率"。
如果"可能性函数"P(B|A)/P(B)>1,意味着"先验概率"被增强,事件A的发生的可能性变大;如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。
4、朴素贝叶斯
朴素贝叶斯对条件个概率分布做了条件独立性的假设。 比如下面的公式,假设有n个特征:
由于每个特征都是独立的,我们可以进一步拆分公式 :
二、实战实例
1、使用朴素贝叶斯进行文档分类
以在线社区留言为例。我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示。
把文本看成单词向量或者词条向量,即将句子转换为向量。考虑出现文档中的所有单词,再决定将哪些单词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。简单起见,我们先假设已经将本文切分完毕,存放到列表中,并对词汇向量进行分类标注。
# 1.编写代码得到词条向量
postingList:存放词条列表
classVec:存放每个词条的所属类别,1代表侮辱类 ,0代表非侮辱类。
trainMat:所有的词条向量组成的列表
# 2.通过词条向量训练朴素贝叶斯分类器
p0V:每个单词属于类别0,也就是非侮辱类词汇的概率。
p1V:每个单词属于类别1,也就是侮辱类词汇的概率。
pAb:先验概率
# 3.通过已经训练好的朴素贝叶斯分类器进行分类
结果错误,这样写的算法无法分类,需要改进,即使用拉普拉斯平滑
- 问题1:利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中有一个概率值为0,那么最后的成绩也为0;
解决:所有词的出现数初始化为1,并将分母初始化为2。这种做法就叫做拉普拉斯平滑(Laplace Smoothing)又被称为加1平滑,是比较常用的平滑方法,它就是为了解决0概率问题。 - 问题2:下溢出,由于太多很小的数相乘,计算结果可能就变成0了,
解决:对乘积结果取自然对数。通过求对数可以避免下溢出或者浮点数舍入导致的错误。
见下面曲线图:
发现它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果
改进后结果图:
代码汇总
import numpy as np
from functools import reduce
"""
函数说明:创建实验样本
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']]
# 类别标签向量,1代表侮辱性词汇,0代表不是
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec
"""
函数说明:将切分的实验样本词条整理成不重复的词条列表
Parameters:
dataSet - 整理的样本数据集
Returns:
vocabSet - 返回不重复的词条列表
"""
def createVocabList(dataSet):
# 创建一个空的不重复列表
vocabSet = set([])
# 添加新词集合
for document in dataSet:
vocabSet = vocabSet | set(document) # 取并集
return list(vocabSet)
"""
函数说明:根据vocabList词汇表,将inputSet向量化,响亮的每个元素为0或1
Parameters:
vocabList - createVocabList返回的列表
inputSet - 切分的词条列表
Returns:
returnVec - 文档向量,词集模型
"""
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的向量
returnVec = [0] * len(vocabList)
# 遍历每个词条
for word in inputSet:
if word in vocabList:
# 如果词条存在,则置1
returnVec[vocabList.index(word)] = 1
else:
print("the word:%s not in my vocabulary" % word)
# 返回文档向量
return returnVec
"""
函数说明:朴素贝叶斯分类器训练函数
Parameters:
trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
p0Vect - 非侮辱的条件概率数组
p1Vect - 侮辱的条件概率数组
pAbuseive - 文档属于侮辱类的概率
"""
def trainNB0(trainMatrix, trainCategory):
# 训练的文档数目
numTrainDocs = len(trainMatrix)
# 每篇文档的词条数
numWords = len(trainMatrix[0])
# 文档属于侮辱类的概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 创建数组
p0Num = np.zeros(numWords)
p1Num = np.zeros(numWords)
# 分母初始化为0.0
p0Denom = 0.0
p1Denom = 0.0
for i in range(numTrainDocs):
# 统计属于侮辱类的条件概率所需数据,P(w0|1),P(w1|1),P(w2|1)···
if trainCategory[i] == 1:
p1Num += trainMatrix[i] # 该文档次数
p1Denom += sum(trainMatrix[i]) # 文档总词数
# 统计属于非侮辱类的条件概率所需的数据,P(w0|0),P(w1|0),P(w2|0)···
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0Vect = p0Num / p0Denom
p1Vect = p1Num / p1Denom
return p0Vect, p1Vect, pAbusive
"""
函数说明:朴素贝叶斯分类器训练函数(改进:拉普拉斯平滑)
Parameters:
trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
p0Vect - 非侮辱的条件概率数组
p1Vect - 侮辱的条件概率数组
pAbuseive - 文档属于侮辱类的概率
"""
def trainNB1(trainMatrix, trainCategory):
# 训练的文档数目
numTrainDocs = len(trainMatrix)
# 每篇文档的词条数
numWords = len(trainMatrix[0])
# 文档属于侮辱类的概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 创建数组
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
# 分母初始化为2.0
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
# 统计属于侮辱类的条件概率所需数据,P(w0|1),P(w1|1),P(w2|1)···
if trainCategory[i] == 1:
p1Num += trainMatrix[i] # 该文档次数
p1Denom += sum(trainMatrix[i]) # 文档总词数
# 统计属于非侮辱类的条件概率所需的数据,P(w0|0),P(w1|0),P(w2|0)···
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0Vect = np.log(p0Num / p0Denom)
p1Vect = np.log(p1Num / p1Denom)
return p0Vect, p1Vect, pAbusive
"""
函数说明:朴素贝叶斯分类器分类函数
Parameters:
vec2Classify - 待分类的词条数目
p0Vec - 非侮辱类的条件概率数组
p1Vec - 侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率
Returns:
0 - 属于非侮辱类
1 - 属于侮辱类
"""
def classifyNB0(vec2Classify, p0Vec, p1Vec, pClass1):
# 对应元素相乘
p0 = reduce(lambda x, y: x * y, vec2Classify * p0Vec) * (1.0 - pClass1)
p1 = reduce(lambda x, y: x * y, vec2Classify * p1Vec) * pClass1
print('p0:', p0)
print('p1:', p1)
if p1 > p0:
return 1
else:
return 0
"""
函数说明:朴素贝叶斯分类器分类函数(改进)
Parameters:
vec2Classify - 待分类的词条数目
p0Vec - 非侮辱类的条件概率数组
p1Vec - 侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率
Returns:
0 - 属于非侮辱类
1 - 属于侮辱类
"""
def classifyNB1(vec2Classify, p0Vec, p1Vec, pClass1):
# 对应元素相乘
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
print('p0:', p0)
print('p1:', p1)
if p1 > p0:
return 1
else:
return 0
"""
函数说明:测试朴素贝叶斯分类器
"""
def testingNB0():
# 创建实验样本
listOPosts, listClasses = loadDataSet()
# 创建词汇表
myVocabList = createVocabList(listOPosts)
trainMat = []
for postingDoc in listOPosts:
# 将实验样本向量化
trainMat.append(setOfWords2Vec(myVocabList, postingDoc))
# 训练朴素贝叶斯分类器
p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))
# 测试样本1
testEntry = ['love', 'my', 'dalmation']
# 测试样本向量化
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB0(thisDoc, p0V, p1V, pAb):
print(testEntry, '属于侮辱类') # 执行分类并打印分类结果
else:
print(testEntry, '属于非侮辱类') # 执行分类并打印分类结果
# 测试样本2
testEntry = ['stupid', 'garbage']
# 测试样本向量化
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB0(thisDoc, p0V, p1V, pAb):
print(testEntry, '属于侮辱类') # 执行分类并打印分类结果
else:
print(testEntry, '属于非侮辱类')
"""
函数说明:测试朴素贝叶斯分类器(改进)
"""
def testingNB1():
# 创建实验样本
listOPosts, listClasses = loadDataSet()
# 创建词汇表
myVocabList = createVocabList(listOPosts)
trainMat = []
for postingDoc in listOPosts:
# 将实验样本向量化
trainMat.append(setOfWords2Vec(myVocabList, postingDoc))
# 训练朴素贝叶斯分类器
p0V, p1V, pAb = trainNB1(np.array(trainMat), np.array(listClasses))
# 测试样本1
testEntry = ['love', 'my', 'dalmation']
# 测试样本向量化
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB1(thisDoc, p0V, p1V, pAb):
print(testEntry, '属于侮辱类') # 执行分类并打印分类结果
else:
print(testEntry, '属于非侮辱类') # 执行分类并打印分类结果
# 测试样本2
testEntry = ['stupid', 'garbage']
# 测试样本向量化
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB1(thisDoc, p0V, p1V, pAb):
print(testEntry, '属于侮辱类') # 执行分类并打印分类结果
else:
print(testEntry, '属于非侮辱类')
if __name__ == '__main__':
# 1.得到词条向量
# 样本词条
# postingList, classVec = loadDataSet()
# print('postingList:\n', postingList)
# # 不重复的词条列表
# myVocabList = createVocabList(postingList)
# print('myVocabList:\n', myVocabList)
# # 所有词条向量组成的列表
# trainMat = []
# for postingDoc in postingList:
# trainMat.append(setOfWords2Vec(myVocabList, postingDoc))
# print('trainMat:\n', trainMat)
# 2.通过词条向量训练朴素贝叶斯分类器
# postingList, classVec = loadDataSet()
# myVocabList = createVocabList(postingList)
# print('myVocabList:\n', myVocabList)
# trainMat = []
# for postingDoc in postingList:
# trainMat.append(setOfWords2Vec(myVocabList, postingDoc))
# p0V, p1V, pAb = trainNB1(trainMat, classVec)
# print('p0V:\n', p0V)
# print('p1V:\n', p1V)
# print('classVec:\n', classVec)
# print('pAb:\n', pAb)
# 3.通过已经训练好的朴素贝叶斯分类器进行分类
testingNB1()
2、朴素贝叶斯之过滤垃圾邮件
电子邮件垃圾过滤使用朴素贝叶斯对电子邮件进行分类的步骤:
收集数据:提供文本文件。
准备数据:将文本文件解析成词条向量。
分析数据:检查词条确保解析的正确性。
训练算法:使用我们之前建立的trainNB0()函数。
测试算法:使用classifyNB(),并构建一个新的测试函数来计算文档集的错误率。
使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
数据以非字母、非数字作为符号进行切分,使用split函数即可,得到词汇表,再根据词汇表将每个文本向量化,将数据集分为训练集和测试集,使用交叉验证的方式测试朴素贝叶斯分类器的准确性
下图是词汇表以及测试结果:
代码汇总
import re
import numpy as np
import random
"""
函数说明:接收一个大字符串并将其解析为字符串列表
"""
def textParse(bigString):
# 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
listOfTokens = re.split(r'\W+', bigString)
# 除了单个字母,例如大写的I,其它单词变成小写
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,即词汇表
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 setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的向量
returnVec = [0] * len(vocabList)
# 遍历每个词条
for word in inputSet:
if word in vocabList:
# 如果词条存在,则置1
returnVec[vocabList.index(word)] += 1
# 返回文档向量
return returnVec
"""
函数说明:朴素贝叶斯分类器训练函数(改进:拉普拉斯平滑)
Parameters:
trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
p0Vect - 非侮辱的条件概率数组
p1Vect - 侮辱的条件概率数组
pAbuseive - 文档属于侮辱类的概率
"""
def trainNB1(trainMatrix, trainCategory):
# 训练的文档数目
numTrainDocs = len(trainMatrix)
# 每篇文档的词条数
numWords = len(trainMatrix[0])
# 文档属于侮辱类的概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 创建数组
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
# 分母初始化为2.0
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
# 统计属于侮辱类的条件概率所需数据,P(w0|1),P(w1|1),P(w2|1)···
if trainCategory[i] == 1:
p1Num += trainMatrix[i] # 该文档次数
p1Denom += sum(trainMatrix[i]) # 文档总词数
# 统计属于非侮辱类的条件概率所需的数据,P(w0|0),P(w1|0),P(w2|0)···
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0Vect = np.log(p0Num / p0Denom)
p1Vect = np.log(p1Num / p1Denom)
return p0Vect, p1Vect, pAbusive
"""
函数说明:朴素贝叶斯分类器分类函数(改进)
Parameters:
vec2Classify - 待分类的词条数目
p0Vec - 非侮辱类的条件概率数组
p1Vec - 侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率
Returns:
0 - 属于非侮辱类
1 - 属于侮辱类
"""
def classifyNB1(vec2Classify, p0Vec, p1Vec, pClass1):
# 对应元素相乘
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
if p1 > p0:
return 1
else:
return 0
"""
函数说明:测试朴素贝叶斯分类器
"""
def spamTest():
docList = []
classList = []
fullText = []
# 遍历25个txt文件
for i in range(1, 26):
# 读取每个垃圾邮件,并字符串转换成字符串列表
wordList = textParse(open('email/spam/%d.txt' % i, 'r').read())
docList.append(wordList)
fullText.append(wordList)
classList.append(1) # 标记垃圾邮件,1表示垃圾文件
# 读取每个非垃圾邮件,并字符串转换成字符串列表
wordList = textParse(open('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 = []
# 从50个邮件中,随机挑选出40个作为训练集,10个做测试集
for i in range(10):
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 = trainNB1(np.array(trainMat), np.array(trainClasses))
# 错误分类计数
errorCount = 0
# 遍历测试集
for docIndex in testSet:
# 测试集的词集模型
wordVector = setOfWords2Vec(vocabList, docList[docIndex])
# 如果分类错误
if classifyNB1(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1 # 错误计数加1
print("分类错误的测试集:", docList[docIndex])
print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100))
if __name__ == '__main__':
# 准备数据,得到词汇表
# docList = []
# classList = []
# # 遍历25个txt文件
# for i in range(1, 26):
# # 读取每个垃圾邮件,并字符串转换成字符串列表
# wordList = textParse(open('email/spam/%d.txt' % i, 'r').read())
# docList.append(wordList)
# classList.append(1) # 标记垃圾邮件,1表示垃圾文件
# # 读取每个非垃圾邮件,并字符串转换成字符串列表
# wordList = textParse(open('email/ham/%d.txt' % i, 'r').read())
# docList.append(wordList)
# classList.append(0) # 标记非垃圾邮件,0表示非垃圾文件
# # 创建词汇表,不重复
# vocabList = createVocabList(docList)
# print(vocabList)
# 测试
spamTest()
函数spamTest()会输出在10封随机选择的电子邮件上的分类错误概率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说10次,然后求平均值
3、朴素贝叶斯之新浪新闻分类(Sklearn)
1.中文语句切分
直接使用第三方分词组件,即jieba pip3 install jieba
新闻分类数据集:
2.文本特征选择
将所有文本分成训练集和测试集,并对训练集中的所有单词进行词频统计,并按降序排序。
包含了很多标点符号,这些标点符号是不能作为新闻分类的特征的,为了降低这些高频的符号对分类结果的影响,规则可以这样制定:
1)去掉高频词,至于去掉多少个高频词,我们可以通过观察去掉高频词个数和最终检测准确率的关系来确定。
2)去除数字,不把数字作为分类特征。
3)去除一些特定的词语,比如:“的”,“一”,“在”,“不”,“当然”,"怎么"这类的对新闻分类无影响的介词、代词、连词。
滤除了那些没有用的词组,这个featureWords就是我们最终选出的用于新闻分类的特征。随后,我们就可以根据featureWords,将文本向量化,然后用于训练朴素贝叶斯分类器。
使用Sklearn构建朴素贝叶斯分类器
MultinomialNB一个重要的功能是有partial_fit方法,这个方法的一般用在如果训练集数据量非常大,一次不能全部载入内存的时候。这时我们可以把训练集分成若干等分,重复调用partial_fit来一步步的学习训练集,非常方便。
编写代码,通过观察取不同的去掉前deleteN个高频词的个数与最终检测准确率的关系,确定deleteN的取值
制出了deleteNs和test_accuracy的关系,这样我们就可以大致确定去掉前多少的高频词汇了。每次运行程序,绘制的图形可能不尽相同,我们可以通过多次测试,来决定这个deleteN的取值,然后确定这个参数,这样就可以顺利构建出用于新闻分类的朴素贝叶斯分类器了。
代码汇总
import os
import random
from sklearn.naive_bayes import MultinomialNB
import matplotlib.pyplot as plt
import jieba
"""
函数说明:中文文本处理
Parameters:
folderPath - 文本存放路径
testSize - 测试集占比,默认占所有数据集的20%
Returns:
allWordsList - 按词频降序排列的训练集列表
trainDataList - 训练集列表
testDataList - 测试集列表
trainClassList - 训练集标签列表
testClassList - 测试集标签列表
"""
def TextProcessing(folderPath, testSize=0.2):
# 查看folderPath下的文件
folderList = os.listdir(folderPath)
dataList = [] # 数据集数据
classList = [] # 数据集类别
# 遍历每个子文件夹
for folder in folderList:
# 根据子文件夹,生成新的文件夹
newFolderPath = os.path.join(folderPath, folder)
# 存放子文件夹下的txt文件的列表
files = os.listdir(newFolderPath)
j = 1
# 遍历每个txt列表
for file in files:
if j > 100:
break
# 打开txt文件
with open(os.path.join(newFolderPath, file), 'r', encoding='utf-8') as f:
raw = f.read()
# 精简模式,返回一个可迭代的generator
wordCut = jieba.cut(raw, cut_all=False)
# generator转换为list
wordList = list(wordCut)
# 添加数据集
dataList.append(wordList)
# 添加数据集类别
classList.append(folder)
j += 1
# print(dataList)
# print(classList)
# zip压缩合并,将数据与标签对应压缩
dataClassList = list(zip(dataList, classList))
# 将data_class_list乱序
random.shuffle(dataClassList)
# 训练集和测试集切分的索引值
index = int(len(dataClassList) * testSize) + 1
# 训练集
trainList = dataClassList[index:]
# 测试集
testList = dataClassList[:index]
# 训练集解压缩
trainDataList, trainClassList = zip(*trainList)
# 测试集解压缩
testDataList, testClassList = zip(*testList)
# 统计训练集词频
allWordsDict = {}
for wordList in trainDataList:
for word in wordList:
if word in allWordsDict.keys():
allWordsDict[word] += 1
else:
allWordsDict[word] = 1
# 按键值倒序排列
allWordsTupleList = sorted(allWordsDict.items(), key=lambda f: f[1], reverse=True)
# 解压缩
allWordsList, allWordsNums = zip(*allWordsTupleList)
# 转换成列表
allWordsList = list(allWordsList)
return allWordsList, trainDataList, testDataList, trainClassList, testClassList
"""
函数说明:读取文件里的内容,并去重
Parameters:
wordsFile - 文件路径
Returns:
wordsSet - 读取的内容的set集合
"""
def makeWordsSet(wordsFile):
# 创建set集合
wordsSet = set()
# 打开文件
with open(wordsFile, 'r', encoding='utf-8') as f:
for line in f.readlines():
word = line.strip() # 去回车
# 有文本,则添加到wordsSet中
if len(word) > 0:
wordsSet.add(word)
# 返回处理结果
return wordsSet
"""
函数说明:根据featureWords将文本向量化
Parameters:
trainDataList - 训练集
testDataList - 测试集
featureWords - 特征集
Returns:
trainFeatureList - 训练集向量化列表
testFeatureList - 测试集向量化列表
"""
def TestFeatures(trainDataList, testDataList, featureWords):
# 出现在特征集中,则置1
def textFeatures(text, featureWords):
textWords = set(text)
features = [1 if word in textWords else 0 for word in featureWords]
return features
trainFeatureList = [textFeatures(text, featureWords) for text in trainDataList]
testFeatureList = [textFeatures(text, featureWords) for text in testDataList]
# 返回结果集
return trainFeatureList, testFeatureList
"""
函数说明:文本特征选取
Parameters:
allWordsList - 训练集所有文本列表
deleteN - 删除词频最高德词数
stopWordsSet -指定的结束语
Returns:
featureWords - 特征集
"""
def wordsDict(allWordsList, deleteN, stopWordsSet=set()):
# 特征列表
featureWords = []
n = 1
for t in range(deleteN, len(allWordsList), 1):
# feature_words的维度为1000
if n > 1000:
break
# 如果这个词不是数字,不是指定的结束语,单词长度大于1小于5,那么这个词就可以作为特征词
if not allWordsList[t].isdigit() and allWordsList[t] not in stopWordsSet \
and 1 < len(allWordsList[t]) < 5:
featureWords.append(allWordsList[t])
n += 1
return featureWords
"""
函数说明:新闻分类器
Parameters:
trainFeatureList - 训练集向量化的特征文本
testFeatureList - 测试集向量化的特征文本
trainClassList - 训练集分类标签
testClassList - 测试集分类标签
Returns:
testAccuracy - 分类器精度
"""
def TextClassifier(trainFeatureList, testFeatureList, trainClassList, testClassList):
classifier = MultinomialNB().fit(trainFeatureList, trainClassList)
testAccuracy = classifier.score(testFeatureList, testClassList)
return testAccuracy
if __name__ == '__main__':
# 1.数据处理,去掉高频词,数字以及特定词语
# 文本预处理,切分文本并进行类别标记
# folderPath = 'email/SogouC/Sample' # 训练集存放地址
# allWordsList, trainDataList, testDataList, trainClassList, testClassList = TextProcessing(folderPath)
#
# # 生成stopWordsSet
# stopWordFile = 'email/stopwords_cn.txt'
# stopWordSet = makeWordsSet(stopWordFile)
#
# featureWords = wordsDict(allWordsList, 100, stopWordSet)
# print(featureWords)
# 2.使用Sklearn构建朴素贝叶斯分类器
folderPath = 'email/SogouC/Sample' # 训练集存放地址
allWordsList, trainDataList, testDataList, trainClassList, testClassList = TextProcessing(folderPath)
# 生成stopWordsSet
stopWordFile = 'email/stopwords_cn.txt'
stopWordSet = makeWordsSet(stopWordFile)
testAccuracyList = []
deleteNs = range(0, 1000, 20)
for deleteN in deleteNs:
featureWords = wordsDict(allWordsList, deleteN, stopWordSet)
trainFeatureList, testFeatureList = TestFeatures(trainDataList, testDataList, featureWords)
testAccuracy = TextClassifier(trainFeatureList, testFeatureList, trainClassList, testClassList)
testAccuracyList.append(testAccuracy)
ave = lambda c: sum(c) / len(c)
print(ave(testAccuracyList))
plt.figure()
plt.plot(deleteNs, testAccuracyList)
plt.title('Relationship of deleteN and testAccuracy')
plt.xlabel('deleteNs')
plt.ylabel('testAccuracy')
plt.show()
总结
1.在训练朴素贝叶斯分类器之前,要处理好训练集,文本的清洗还是有很多需要学习的东西。
2.根据提取的分类特征将文本向量化,然后训练朴素贝叶斯分类器。
3.去高频词汇数量的不同,对结果也是有影响的的。
4.拉普拉斯平滑对于改善朴素贝叶斯分类器的分类效果有着积极的作用。