朴素贝叶斯对文档进行分类
主要使用sklearn这个机器学习包完成对文档的分类
sklearn机器学习包
sklearn提供了三个朴素贝叶斯分类算法:
高斯朴素贝叶斯:特征变量是连续变量,符合高斯分布,比如人的身高和体重等多项式朴素贝叶斯:特征变量是离线变量,符合多项分布,在文档分类中特征向量体现在一个单词出现的次数或者是单词的TF-IDF值等
伯努利朴素贝叶斯:特征变量是布尔变量,符合0/1分布,在文档分类中特征是单词是否出现
TF-IDF
多项式朴素贝叶斯中提到了TF-IDF(Term Frequency-Inverse Document Frequence 的缩写,其中TF代表词频,IDF代表逆向文档频率),它是一个统计方法,用以评估某一个词语对于一个文档集或一个语料库中的其中一份文档的重要程度。词语的重要性随着它在文档中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降
词频TF:指的是某一个给定的词语在该文档中出现的频率,词语的重要性和它在文档中出现的次数正相关
逆向文档频率IDF:指的是某一个给定的词语在文档中的区分度,词语出现在的文档数越少,这个词语的区分度就越高,越容易用这个词语把该文档和其他文档区分开。IDF越大,这个词语的区分度就越高 所以,我们倾向于找到TF和IDF都高的单词作为区分,即这个单词在一个文档中出现的次数多,但在其他文档中出现的次数少,所以很适合用于分类
TF-IDF=TF*IDF
其中计算IDF公式的分母中,单词出现的次数+1是为了避免分子为0
介绍完了sklearn机器学习包中的三个朴素贝叶斯算法,下面就开始用这个包进行对文档的分类
文档分类的步骤
对文档分类,分两个阶段,看下面的图:
基于分词的准备:分词、单词权重计算、去掉停用词
应用朴素贝叶斯进行分类:先通过训练集得到朴素贝叶斯分类器,然后将分类器用于测试集,将预测结果和实际结果做对比并计算准确率
整个过程分为六个步骤
1.对文档进行分词
对文档分词的时候,英文文档和中文文档使用的分词工具不同。 英文文档常用NTLK包,其中包含了英文的停用词表、分词和标注方法 中文文档常用jieba包,其中包含了中文的停用词表和分词方法
def get_data(base_path, labels): # 获取数据集
contents = []
# 数据在文件夹下,需要遍历各个文档类别下的文件
for label in labels:
files = {fileName for fileName in os.listdir(base_path + label)}
try:
for fileName in files:
file = open(base_path + label + '/' + fileName, encoding='gb18030')
word = jieba.cut(file.read()) # 对文档进行分词 中文文档多用 jieba 包
contents.append(' '.join(word)) # 因为切词,所以用 ' ' 分隔
except Exception:
print(fileName + '文件读取错误')
return contents
# 获取训练集
train_contents = get_data(train_base_path, train_labels)
# 获取测试集
test_contents = get_data(test_base_path, test_labels)
2.加载停用词表
需要我们先下载常用的停用词表,然后放到stop_word.txt中,读取里面的数据放到数组中
stop_words = [line.strip() for line in open(stop_words_path, encoding='utf-8-sig').readlines()]
3.计算单词权重
用sklearn里的TfidfVectorizer类,使用fit_transform方法进行拟合,得到TF-IDF特征空间features,可以理解为选出来的分词就是特征,计算这些特征在文档上的特征向量,得到特征空间features
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents) # 拟合
这里max_df参数表示单词在文档中最高出现频率。这里设置0.5代表一个单词在50%的文档中都出现过了,携带的信息比较少,就不作为分词统计,这个参数可以调。min_df很少设置
4.生成朴素贝叶斯分类器
把训练集训练出来的特征空间features和训练集对应的分类labels传给贝叶斯分类器,会自动生成一个符合特征空间和对应分类的分类器 上面介绍了sklearn包提供的三个朴素贝叶斯分类器,根据特性这里采用多项式朴素贝叶斯分类器即MultinomialNB,其中alpha为平滑参数。当一个单词在训练集中样本中没有出现的时候,该单词的概率被置为0,但是训练集一般是抽样的,所以单词在样本中不存在就置为0不太合理,需要平滑处理
alpha | 平滑类别 | 特点 |
---|---|---|
0~1 | Laplace平滑 | 采用+1的方式,当样本很大的时候,+1得到的概率的影响可以忽略不计,同时也避免了分母为0的情况 |
1 | Lidstone平滑 | alpha越小,迭代次数越多,精度越高 |
代码
train_labels = ['体育'] * 1337 + ['女性'] * 954 + ['文学'] * 766 + ['校园'] * 249 # 数字是自己算的,也可以用程序判断
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels) # MultinomialNB.fit(x,y) 其中 y的数量要等于训练x的数量
5.使用生成的分类器做预测
主要是得到测试集的特征矩阵,先创建FfidfVectorizer,使是和训练集一样的stop_words和max_df,然后用FfidfVectorizer类对测试集内容进行拟合,得到测试集特征矩阵,再用训练好的分类器对测试特征矩阵进行预测,predict这个函数求解所有的后验概率并找到最大的那个
test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=tf.vocabulary_)
test_features = test_tf.fit_transform(test_contents)
# 预测
predicted_labels = clf.predict(test_features)
6.计算准确率
计算正确率主要是对模型预测的结果和实际的结果进行对比,给出模型的准确率,用到sklearn中的metrics包中的accuracy_score函数
test_labels = ['体育'] * 115 + ['女性'] * 38 + ['文学'] * 31 + ['校园'] * 16
print('准确率', metrics.accuracy_score(test_labels, predicted_labels))
实战
实战用到的数据从专栏老师的github上下载,点击这里下载数据介绍: 提供了三份数据,一个训练集文档(train文件夹下)、一个测试集文档(test文件夹下)和一个停用词表,其中训练集和测试集的文档分为四种类型:女性、体育、文学、校园 用朴素贝叶斯分类对训练集训练,并对测试集进行验证,并给出真确率
我的完整代码
import os
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
# 停用词表地址
stop_words_path = 'D:/workspace/study/python/test_data/text_classification/stop/stop_word.txt'
# 训练集根目录
train_base_path = 'D:/workspace/study/python/test_data/text_classification/train/'
# 测试集根目录
test_base_path = 'D:/workspace/study/python/test_data/text_classification/test/'
# 文档类别
train_labels = ['女性', '体育', '文学', '校园']
test_labels = ['女性', '体育', '文学', '校园']
def get_data(base_path, labels): # 获取数据集
contents = []
# 数据在文件夹下,需要遍历各个文档类别下的文件
for label in labels:
files = {fileName for fileName in os.listdir(base_path + label)}
try:
for fileName in files:
file = open(base_path + label + '/' + fileName, encoding='gb18030')
word = jieba.cut(file.read()) # 对文档进行分词 中文文档多用 jieba 包
contents.append(' '.join(word)) # 因为切词,所以用 ' ' 分隔
except Exception:
print(fileName + '文件读取错误')
return contents
# 1.对文档进行分词
# 获取训练集
train_contents = get_data(train_base_path, train_labels)
# print(train_contents)
# print('训练集的长度:', len(train_contents))
# 获取测试集
test_contents = get_data(test_base_path, test_labels)
# print(test_contents)
# print('测试集的长度:', len(test_contents))
# 2.加载停用词表
stop_words = [line.strip() for line in open(stop_words_path, encoding='utf-8-sig').readlines()]
# print(stop_words)
# 3.计算单词权重
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents) # 拟合
# 4.生成朴素贝叶斯分类器 这里使用多项式贝叶斯分类器
train_labels = ['体育'] * 1337 + ['女性'] * 954 + ['文学'] * 766 + ['校园'] * 249
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels) # MultinomialNB.fit(x,y) 其中 y的数量要等于训练x的数量
# 5.使用生成的分类器做预测
test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=tf.vocabulary_)
test_features = test_tf.fit_transform(test_contents)
# 预测
predicted_labels = clf.predict(test_features)
# 6.计算准确率
test_labels = ['体育'] * 115 + ['女性'] * 38 + ['文学'] * 31 + ['校园'] * 16
print('准确率', metrics.accuracy_score(test_labels, predicted_labels))
结果
0.62 这个结果的准确率我觉得还是挺低的,刚过60%,需要优化
声明:文中的图片和知识点内容主要来自极客时间的专栏《数据分析实战45讲》,这是专栏的一道实战作业题