《Machine Learning in action》,机器学习实战(笔记)之naive Bayes
使用工具:
- Python3.7
- pycharm
- anaconda
- jupyter notebook
Example-1: classifying apam email with naive Bayes 用朴素贝叶斯过滤垃圾邮件
大概的步骤
- 获取数据- 提供文本文件
- 处理数据- 将数据处理成我们所需要的词汇向量,并确保其正确性
- 训练算法- 使用我们之前建立的trainNB0()函数
- 测试算法- 使用classifyNB()函数,并且创建一个新的函数来检测其错误率
- 使用算法- 构建一个完整的程序,对一组文档进行分类,并且将错分的文档输出
1.获取数据:
- 这里的数据包括函数,都已经上传在我的GitHub上面,可以全部下载
- [数据集]https://github.com/Tupeng2019/Machine_jichu/tree/master/Machine-Learning/Machine-Learning_note/naive Bayes
- 里面有两个文件夹,ham,spam,spam就是垃圾文件
2. 处理数据
- 利用正则表达式,将邮件,划分为一个一个的单词,
- 先是显示一下我饿对数据的处理结果:
import re
# bigString就是指一个大的字符串,就是相当于邮件当中的英语文章
# 该函数的作用就是将字符串转化为字符列表
def textParse(bigString):
# 了利用正则表达式
# 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
# 在Python3中,只用W就好了
listOfTokens = re.split(r'\W', bigString)
# 只是将字符长大大于2的,全部变成小写,除了单个字符的,如I
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
# 函数createVocabList()将创建所有文档中所有唯一单词的列表
def createVocabList(dataSet):
# 创建一个空的不重复的列表
vocabSet = set([])
for document in dataSet:
# Create the union of two sets创建两个列表的联合,就是取并集
vocabSet = vocabSet | set(document)
return list(vocabSet)
if __name__ == '__main__':
docList = []; classList = []
# 遍历ham,spam中的25个TXT文件
for i in range(1, 26):
#读取每一个垃圾邮件,并将字符串装换为字符串列表
# r 既是表示的只读形式,读取文件
wordList = textParse(open('email//spam/{0}.txt'.format(i),'r').read())
# 并将其存放在docList找那个
docList.append(wordList)
# 标记每一个垃圾邮件,1就表示垃圾邮件,有一个就增加1
classList.append(1)
#读取每一个非垃圾邮件,并将字符串装换为字符串列表
wordList = textParse(open('email/ham/{0}.txt'.format(i), 'r').read())
docList.append(wordList)
# 标记非垃圾邮件,0就是表示非垃圾邮件
classList.append(0)
# 调用createVocabList(),创建不重复词汇表
vocabList = createVocabList(docList)
print(vocabList)
- 显示结果:
3.下面就是训练和测试数据
import numpy as np
import re
import random
# bigString就是指一个大的字符串,就是相当于邮件当中的英语文章
# 该函数的作用就是将字符串转化为字符列表
def textParse(bigString):
# 了利用正则表达式
# 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
# 在书中用的是W*,估计是Python3和2的区别吧,在这里使用的W
listOfTokens = re.split(r'\W', bigString)
# 只是将字符长大大于2的,全部变成小写,除了单个字符的,如I
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
# 函数createVocabList()将创建所有文档中所有唯一单词的列表
def createVocabList(dataSet):
# 创建一个空的不重复的列表
vocabSet = set([])
for document in dataSet:
# Create the union of two sets创建两个列表的联合,就是取并集
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 遍历词汇列表,输出数字向量(1代表存在在词汇列表中,0表示不在vocaSet
def setOfWords2Vec(vocabList, inputSet):
# 创建于vocabList维度一样的列表,只是全部都是0
returnVec = [0] * len(vocabList)
# 遍历每一个词汇
for word in inputSet:
# 如果词条存在于词汇表当中,就变为1
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else: print("the word: {0}is not in my Vocabulary!".format(word))
# 返回一个文档向量
return returnVec
# 朴素贝叶斯分类器-训练函数
def trainNB0(trainMatrix,trainCategory):
# trainMatrix 训练文档矩阵,就是扇面函数返回的returnVec构成的矩阵
# 计算训练的文档数目
numTrainDocs = len(trainMatrix)
# 计算每一篇文档的词条数目
numWords = len(trainMatrix[0])
# 所有文档属中,属于垃圾类的概率
pAbusive = sum(trainCategory)/float(numTrainDocs)
# 创建numpy.zero数组,表示词条出现的次数,并且初始化为0
# numerator 分子,denominator分母,都初始化为0
p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)
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])
else:#统计属于非垃圾类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
# 元素的划分
# p1Vect就是属于垃圾类的条件概率数组
p1Vect = p1Num/p1Denom
# 属于非垃圾类的条件概率数组
p0Vect = p0Num/p0Denom
# #返回属于垃圾类的条件概率数组,属于非垃圾类的条件概率数组,文档属于垃圾类的概率
return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
# 对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1)
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def spamTest():
docList = []; classList = []; fullText = []
# 遍历25个TXT文件
for i in range(1, 26): #遍历25个txt文件
# 读取每一个垃圾邮件,并字符串转换成字符串列表
wordList = textParse(open('email//spam/%d.txt' % i).read())
docList.append(wordList)
fullText.append(wordList)
# 标记垃圾邮件,1表示垃圾文件
classList.append(1)
# 读取每个非垃圾邮件,并字符串转换成字符串列表
wordList = textParse(open('email//ham/%d.txt' % i, 'r').read())
docList.append(wordList)
fullText.append(wordList)
# 标记废垃圾邮件0表示非垃圾邮件
classList.append(0)
# 创建不重复的词汇表,
vocabList = createVocabList(docList)
# 创建储存训练集的索引值的列表,和测试集的索引值的列表
trainingSet = list(range(50)); testSet = []
'''
我们将我们的数据集分一部分作为我们的训练集,一部分作为我们的测试集
在这里,选择40WieU训练集,10个为测试集
这里的数据集都是没有什么规律,所以就是随机选就行
'''
for i in range(10):
# 随机选取索引值0-39
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(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]:
# 分类错误加+1
errorCount += 1
print("分类错误的测试集:",docList[docIndex])
print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100))
if __name__ =='__main__':
spamTest()
- 结果显示
- 函数spamTest()会输出在10封随机选择的电子邮件上的分类错误概率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。如果发现错误的话,函数会输出错误的文档的此表,这样就可以了解到底是哪篇文档发生了错误。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说10次,然后求平均值。相比之下,将垃圾邮件误判为正常邮件要比将正常邮件归为垃圾邮件好。
Example-2: 朴素贝叶斯过- 新浪新闻分类(sklearn)
1.中文语句的切分
-
考虑一个问题,英文的语句可以通过非字母和非数字进行切分,但是汉语句子呢?就比如我打的这一堆字,该如何进行切分呢?我们自己写个规则?
-
幸运地是,这部分的工作不需要我们自己做了,可以直接使用第三方分词组件,即jieba,没错就是"结巴"。
-
jieba已经兼容Python2和Python3,使用如下指令直接安装即可
- conda install jieba好像是不行的
- pip install jieba
Python 中文分词组件的使用的方法
-
数据集分裂如下:
-
这是我的数据集的下载地址https://github.com/Tupeng2019/Machine_jichu/tree/master/Machine-Learning/Machine-Learning_note/naive%20Bayes
-
其中sample是数据集,ClassList,txt数据集的分类
下面就是切分中文字
- 代码如下:
import os
import jieba
def TextProcessing(folder_path):
# 查看folder_path下的文件
folder_list = os.listdir(folder_path)
# 建立储存训练集的列表
data_list = []
class_list = []
#遍历每个子文件夹
for folder in folder_list:
# 根据子文件夹,生成新的路径
new_folder_path = os.path.join(folder_path, folder)
# 根据子文件夹,生成新的路径
files = os.listdir(new_folder_path)
j = 1
#遍历每个txt文件
for file in files:
# 每类txt样本数最多100个
if j > 100:
break
# 打开txt文件
with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
raw = f.read()
# 精简模式,返回一个可迭代的generator
word_cut = jieba.cut(raw, cut_all = False)
# generator转换为list
word_list = list(word_cut)
data_list.append(word_list)
class_list.append(folder)
j += 1
print(data_list)
print(class_list)
if __name__ == '__main__':
#文本预处理
# 训练集存放地址
folder_path = './Sample'
TextProcessing(folder_path)
其结果:
2. 特征的选择
- 我们将所有文本分成训练集和测试集,并对训练集中的所有单词进行词的出现次数(词频)统计,并按降序排序。也就是将出现次数多的词语在前,出现次数少的词语在后进行排序
import os
import random
import jieba
"""
函数说明:中文文本处理
Parameters:
folder_path - 文本存放的路径
test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
all_words_list - 按词频降序排序的训练集列表
train_data_list - 训练集列表
test_data_list - 测试集列表
train_class_list - 训练集标签列表
test_class_list - 测试集标签列表
"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
# 查看folder_path下的路径的文件
folder_list = os.listdir(folder_path)
# 创建空列表放数据集数据
data_list = []
# 创建空列表放数据集类型
class_list = []
#遍历每个子文件夹
for folder in folder_list:
# 根据子文件夹,生成新的路径
new_folder_path = os.path.join(folder_path, folder)
# 存放子文件夹下的TXT文件的列表
files = os.listdir(new_folder_path)
# 定义j为每一类TXT样本的个数,并初始化为1
j = 1
#遍历每个txt文件
for file in files:
#每一类样本的个数最多为100个
if j > 100:
break
# 打开txt文件
with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
raw = f.read()
# 精简模式,返回一个可迭代的generator
word_cut = jieba.cut(raw, cut_all = False)
# 将generator装换为list
word_list = list(word_cut)
# 添加数据集数据
data_list.append(word_list)
# 向列表中添加数据集类型
class_list.append(folder)
j += 1
# #zip压缩合并,将数据与标签对应压缩
data_class_list = list(zip(data_list, class_list))
# 将data_class_list乱序
random.shuffle(data_class_list)
# 训练集和测试集切分的索引值
index = int(len(data_class_list) * test_size) + 1
# 训练集
train_list = data_class_list[index:]
# 测试集
test_list = data_class_list[:index]
# 训练集解压缩
train_data_list, train_class_list = zip(*train_list)
# 测试集解压缩
test_data_list, test_class_list = zip(*test_list)
# 统计训练集词频
all_words_dict = {}
for word_list in train_data_list:
for word in word_list:
if word in all_words_dict.keys():
all_words_dict[word] += 1
else:
all_words_dict[word] = 1
#根据键的值倒序排序
all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
# 解压缩
all_words_list, all_words_nums = zip(*all_words_tuple_list)
# 将all_words_list转换成列表
all_words_list = list(all_words_list)
return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list
if __name__ == '__main__':
#文本预处理
# 训练集的地址
folder_path = './Sample'
all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)
print(all_words_list)
-
all_words_list就是将所有训练集的切分结果通过词频降序排列构成的单词合集。观察一下打印结果,不难发现,这里包含了很多标点符号,很显然,这些标点符号是不能作为新闻分类的特征的,所以我们应该有一个处理掉这些重复太多的的词
-
一个简单的规则可以这样制定:首先去掉高频词,至于去掉多少个高频词,我们可以通过观察去掉高频词个数和最终检测准确率的关系来确定。除此之外,去除数字,不把数字作为分类特征。同时,去除一些特定的词语,比如:”的”,”一”,”在”,”不”,”当然”,”怎么”这类的对新闻分类无影响的介词、代词、连词。怎么去除这些词呢?可以使用已经整理好的stopwords_cn.txt文本。下载地址:https://github.com/Tupeng2019/Machine-learning/tree/m-learning/Machine/Machine-Learning/Machine-Learning_note/2_naive Bayes/实战- 新浪新闻分类sklearn
-
所以我们可以根据上面的文档,将这些高频单词去除,不作为分类的特征。我们先去除前100个高频词汇,然后编写代码如下:
import os
import random
import jieba
"""
函数说明:中文文本处理
Parameters:
folder_path - 文本存放的路径
test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
all_words_list - 按词频降序排序的训练集列表
train_data_list - 训练集列表
test_data_list - 测试集列表
train_class_list - 训练集标签列表
test_class_list - 测试集标签列表
"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
# 查看folder_path下的路径的文件
folder_list = os.listdir(folder_path)
# 创建空列表放数据集数据
data_list = []
# 创建空列表放数据集类型
class_list = []
#遍历每个子文件夹
for folder in folder_list:
# 根据子文件夹,生成新的路径
new_folder_path = os.path.join(folder_path, folder)
# 存放子文件夹下的TXT文件的列表
files = os.listdir(new_folder_path)
# 定义j为每一类TXT样本的个数,并初始化为1
j = 1
#遍历每个txt文件
for file in files:
#每一类样本的个数最多为100个
if j > 100:
break
# 打开txt文件
with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
raw = f.read()
# 精简模式,返回一个可迭代的generator
word_cut = jieba.cut(raw, cut_all = False)
# 将generator装换为list
word_list = list(word_cut)
# 添加数据集数据
data_list.append(word_list)
# 向列表中添加数据集类型
class_list.append(folder)
j += 1
# #zip压缩合并,将数据与标签对应压缩
data_class_list = list(zip(data_list, class_list))
# 将data_class_list乱序
random.shuffle(data_class_list)
# 训练集和测试集切分的索引值
index = int(len(data_class_list) * test_size) + 1
# 训练集
train_list = data_class_list[index:]
# 测试集
test_list = data_class_list[:index]
# 训练集解压缩
train_data_list, train_class_list = zip(*train_list)
# 测试集解压缩
test_data_list, test_class_list = zip(*test_list)
# 统计训练集词频
all_words_dict = {}
for word_list in train_data_list:
for word in word_list:
if word in all_words_dict.keys():
all_words_dict[word] += 1
else:
all_words_dict[word] = 1
#根据键的值倒序排序
all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
# 解压缩
all_words_list, all_words_nums = zip(*all_words_tuple_list)
# 将all_words_list转换成列表
all_words_list = list(all_words_list)
return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list
# 读取文件中的内容,并且去重
def MakeWordsSet(words_file):
# 创建set集合
words_set = set()
# 打开文件夹
with open(words_file, 'r', encoding = 'utf-8') as f:
# 以行为单位进行一行一行的读取
for line in f.readlines():
# 去掉回车,就是读取为一行
word = line.strip()
# 如果有文本,就添加到words_set中去
if len(word) > 0:
words_set.add(word)
return words_set
'''
# 文本的特征提取:
# all_words_list就是训练集所有的文本
# deleteN 删除词频最高的单词
stopwords 指定的结束语
'''
def words_dict(all_words_list, deleteN, stopwords_set = set()):
# 特征列表
feature_words = []
n = 1
for t in range(deleteN, len(all_words_list), 1):
# feature_words 的维度为1000
if n > 1000:
break
#如果这个词不是数字,并且不是指定的结束语,并且单词长度大于1小于5,那么这个词就可以作为特征词
if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1 < len(all_words_list[t]) < 5:
feature_words.append(all_words_list[t])
n += 1
return feature_words
if __name__ == '__main__':
#文本预处理
# 训练集存放地址
folder_path = './Sample'
all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)
#生成stopwords_set
stopwords_file = './stopwords_cn.txt'
stopwords_set = MakeWordsSet(stopwords_file)
feature_words = words_dict(all_words_list, 100, stopwords_set)
print(feature_words)
现在我们的feature_words 就是所最终处理的结果既是用于新闻分类的特征,然后要做的工作就是将其向量化,最后就是训练我们的朴素贝叶斯模型,其过程和前面的一样,这里就是不说了
3.利用sklearn构建朴素贝叶斯模型
-
下面就是官网的文档:
-
具体的详情就是看看文档
-
对于新闻分类,属于多分类问题。我们可以使用MultinamialNB()完成我们的新闻分类问题。另外两个函数的使用暂且不再进行扩展,可以自行学习。MultinomialNB假设特征的先验概率为多项式分布,即如下式:
其中,P(Xj = Xjl | Y = Ck)是第k个类别的第j维特征的第l个取值条件概率。mk是训练集中输出为第k类的样本个数。λ为一个大于0的常数,尝尝取值为1,即拉普拉斯平滑,也可以取其他值 -
下面就是来看看MultinomialNB函数,是用于多项式模型的朴素贝叶斯分分类器
-
参数说明如下:
- alpha:浮点型可选参数,默认为1.0,其实就是添加拉普拉斯平滑,即为上述 公式中的λ ,如果这个参数设置为0,就是不添加平滑;
- fit_prior:布尔型可选参数,默认为True。布尔参数fit_prior表示是否要考虑先验概率,如果是false,则所有的样本类别输出都有相同的类别先验概率。否则可以自己用第三个参数class_prior输入先验概率,或者不输入第三个参数class_prior让MultinomialNB自己从训练集样本来计算先验概率,此时的先验概率为P(Y=Ck)=mk/m。其中m为训练集样本总数量,mk为输出为第k类别的训练集样本数。
- class_prior:可选参数,默认为None。
此外还有一些方法可供我们使用:
-
MultinomialNB一个重要的功能是有partial_fit方法,这个方法的一般用在如果训练集数据量非常大,一次不能全部载入内存的时候。这时我们可以把训练集分成若干等分,重复调用partial_fit来一步步的学习训练集,非常方便。GaussianNB和BernoulliNB也有类似的功能。 在使用MultinomialNB的fit方法或者partial_fit方法拟合数据后,我们可以进行预测。此时预测有三种方法,包括predict,predict_log_proba和predict_proba。predict方法就是我们最常用的预测方法,直接给出测试集的预测类别输出。predict_proba则不同,它会给出测试集样本在各个类别上预测的概率。容易理解,predict_proba预测出的各个类别概率里的最大值对应的类别,也就是predict方法得到类别。predict_log_proba和predict_proba类似,它会给出测试集样本在各个类别上预测的概率的一个对数转化。转化后predict_log_proba预测出的各个类别对数概率里的最大值对应的类别,也就是predict方法得到类别。具体细节不再讲解,可参照官网手册。
-
通过观察可以去不同的deleteN个高频词个数与最终的分类结果的准确率的关系,来确定deleteN的值:
from sklearn.naive_bayes import MultinomialNB
import matplotlib.pyplot as plt
import os
import random
import jieba
"""
函数说明:中文文本处理
Parameters:
folder_path - 文本存放的路径
test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
all_words_list - 按词频降序排序的训练集列表
train_data_list - 训练集列表
test_data_list - 测试集列表
train_class_list - 训练集标签列表
test_class_list - 测试集标签列表
"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
# 查看folder_path下的路径的文件
folder_list = os.listdir(folder_path)
# 创建空列表放数据集数据
data_list = []
# 创建空列表放数据集类型
class_list = []
#遍历每个子文件夹
for folder in folder_list:
# 根据子文件夹,生成新的路径
new_folder_path = os.path.join(folder_path, folder)
# 存放子文件夹下的TXT文件的列表
files = os.listdir(new_folder_path)
# 定义j为每一类TXT样本的个数,并初始化为1
j = 1
#遍历每个txt文件
for file in files:
#每一类样本的个数最多为100个
if j > 100:
break
# 打开txt文件
with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
raw = f.read()
# 精简模式,返回一个可迭代的generator
word_cut = jieba.cut(raw, cut_all = False)
# 将generator装换为list
word_list = list(word_cut)
# 添加数据集数据
data_list.append(word_list)
# 向列表中添加数据集类型
class_list.append(folder)
j += 1
# #zip压缩合并,将数据与标签对应压缩
data_class_list = list(zip(data_list, class_list))
# 将data_class_list乱序
random.shuffle(data_class_list)
# 训练集和测试集切分的索引值
index = int(len(data_class_list) * test_size) + 1
# 训练集
train_list = data_class_list[index:]
# 测试集
test_list = data_class_list[:index]
# 训练集解压缩
train_data_list, train_class_list = zip(*train_list)
# 测试集解压缩
test_data_list, test_class_list = zip(*test_list)
# 统计训练集词频
all_words_dict = {}
for word_list in train_data_list:
for word in word_list:
if word in all_words_dict.keys():
all_words_dict[word] += 1
else:
all_words_dict[word] = 1
#根据键的值倒序排序
all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
# 解压缩
all_words_list, all_words_nums = zip(*all_words_tuple_list)
# 将all_words_list转换成列表
all_words_list = list(all_words_list)
return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list
# 读取文件中的内容,并且去重
def MakeWordsSet(words_file):
# 创建set集合
words_set = set()
# 打开文件夹
with open(words_file, 'r', encoding = 'utf-8') as f:
# 以行为单位进行一行一行的读取
for line in f.readlines():
# 去掉回车,就是读取为一行
word = line.strip()
# 如果有文本,就添加到words_set中去
if len(word) > 0:
words_set.add(word)
return words_set
'''
根据feature_words 将文本向量化
train_data_list 训练集
test_data_list 测试集
feature_words 特征集
-》返回参数
trian_feature_list 训练集向量化列表
test_feature_list 测试集向量化列表
'''
# 向量化:
def TextFeatures(train_data_list, test_data_list, feature_words):
#
def text_features(text, feature_words):
text_words = set(text)
# 出现在特征集中,则置1
features = [1 if word in text_words else 0 for word in feature_words]
return features
train_feature_list = [text_features(text, feature_words) for text in train_data_list]
test_feature_list = [text_features(text, feature_words) for text in test_data_list]
return train_feature_list, test_feature_list
'''
# 文本的特征提取:
# all_words_list就是训练集所有的文本
# deleteN 删除词频最高的单词
stopwords 指定的结束语
'''
def words_dict(all_words_list, deleteN, stopwords_set = set()):
# 特征列表
feature_words = []
n = 1
for t in range(deleteN, len(all_words_list), 1):
# feature_words 的维度为1000
if n > 1000:
break
#如果这个词不是数字,并且不是指定的结束语,并且单词长度大于1小于5,那么这个词就可以作为特征词
if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1 < len(all_words_list[t]) < 5:
feature_words.append(all_words_list[t])
n += 1
return feature_words
'''
构建新闻分类器TextClassifier
train_feature_list - 训练集向量化的特征文本
test_feature_list - 测试集向量化的特征文本
train_class_list - 训练集分类标签
test_class_list - 测试集分类标签
'''
def TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list):
classifier = MultinomialNB().fit(train_feature_list, train_class_list)
test_accuracy = classifier.score(test_feature_list, test_class_list)
return test_accuracy
if __name__ == '__main__':
#文本预处理
folder_path = './Sample' #训练集存放地址
all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)
#生成stopwords_set
stopwords_file = './stopwords_cn.txt'
stopwords_set = MakeWordsSet(stopwords_file)
# 建立test_accuracy_list 空列表
test_accuracy_list = []
# 建立一个列表是从1000以内,并且是间隔为20
# 0 20 40 60 ... 980
deleteNs = range(0, 1000, 20)
for deleteN in deleteNs:
feature_words = words_dict(all_words_list, deleteN, stopwords_set)
train_feature_list, test_feature_list = TextFeatures(train_data_list, test_data_list, feature_words)
test_accuracy = TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list)
test_accuracy_list.append(test_accuracy)
plt.figure()
plt.plot(deleteNs, test_accuracy_list)
plt.title('Relationship of deleteNs and test_accuracy')
plt.xlabel('deleteNs')
plt.ylabel('test_accuracy')
plt.show()
- 通过结果来观察,我们所需要的deleteN的值,最好的取值是
- 我们绘制出了deleteNs和test_accuracy的关系,这样我们就可以大致确定去掉前多少的高频词汇了。每次运行程序,绘制的图形可能不尽相同,我们可以通过多次测试,来决定这个deleteN的取值,然后确定这个参数,这样就可以顺利构建出用于新闻分类的朴素贝叶斯分类器了。
- 我这里测试了多次,感觉500的时候就不错了,所以这里的deleteN就是去500
修改我们的主函数代码,就好了
if __name__ == '__main__':
#文本预处理
# 训练集存放地址
folder_path = './Sample'
all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)
# 生成stopwords_set
stopwords_file = './stopwords_cn.txt'
stopwords_set = MakeWordsSet(stopwords_file)
test_accuracy_list = []
feature_words = words_dict(all_words_list, 500, stopwords_set)
train_feature_list, test_feature_list = TextFeatures(train_data_list, test_data_list, feature_words)
test_accuracy = TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list)
test_accuracy_list.append(test_accuracy)
ave = lambda c: sum(c) / len(c)
print(ave(test_accuracy_list))
- 看出来的结果