一. 相关介绍
(一) 朴素贝叶斯
朴素贝叶斯分类最适合的场景是文本分类、情感分析和垃圾邮件识别,python 中的 sklearn 机器学习包提供了3种朴素贝叶斯分类算法:
1. 高斯朴素贝叶斯(GaussianNB)
适用场景:特征变量是连续变量,符合正态分布。
2. 多项式朴素贝叶斯(MultinomialNB)
适用场景:特征变量是离散变量,符合多项分布。
3. 伯努利朴素贝叶斯(BernoulliNB)
适用场景:特征变量是布尔变量,符合0/1分布。
(二) TF-IDF
TF-IDF 是一个统计方法,用来评估某个词语对于一个文件集或文档库中的其中一份文件的重要程度。它由Term Frequency 和 Inverse Document Frequency 的组成,两者缩写为 TF 和 IDF,分别代表了词频和逆向文档频率。
词频计算了一个单词在文档中出现的次数,它认为一个单词的重要性和它在文档中出现的次数呈正比。
逆向文档频率是指一个单词在文档中的区分度。它认为一个单词出现在的文档数越少,就越能通过这个单词把该文档和其他文档区分开。IDF 越大就代表该单词的区分度越大。
计算方法:
TF-IDF为TF与IDF乘积: TF-IDF = TF * IDF
在sklearn中可以直接使用 TfidfVectorizer 类计算单词TF-IDF向量的值。
二. 数据分析
(一) 加载停用词表
停用词是指在信息检索中,为节省存储空间和提高搜索效率,在处理自然语言数据(或文本)之前或之后会自动过滤掉某些字或词,这些字或词即被称为Stop Words(停用词)。
stop_words_path = './data/stop/stopword.txt'
with open(stop_words_path, 'r', encoding='utf-8') as sw_msg:
stop_words = [line.strip() for line in sw_msg.readlines()]
stop_words[0] = "," # 修改'\ufeff,' 为 ‘,’
print(stop_words)
print(len(stop_words))
通过检视stop_words列表,发现','读取后变为了'\ufeff,'(疑为编码格式错误造成), 所以在代码中专门进行了修改。
(二) 加载数据并进行分词
利用中文分词工具包 jieba 对原始数据进行分词,并分别存入训练集和测试集列表中。
代码中使用了try方式逐一读取文件,遇到读取错误的文件捕获异常并进行提示,之后对异常文件进行编码修改或删除操作,直到全部文件能够正确读取为止。
# 训练集
train_contents = []
data_list1 = ['体育', '女性', '文学', '校园'] # 文件夹(标签)名字
for j in data_list1:
errortimes = 0 # 初始化读取文档错误次数
path_lst = [name for name in os.listdir('./data/train/' + j)
if os.path.isfile(os.path.join('./data/train/' + j, name))] # 获取对应文件名列表
for path in path_lst:
path = './data/train/' + j + '/' + path # 补全为相对路径
try: # 使用jieba工具包进行分词
with open(path, 'r') as train_msg0:
train_msg1 = train_msg0.read()
train_cut_msg0 = jieba.cut(train_msg1)
train_cut_msg1 = " ".join(train_cut_msg0)
train_contents.append(train_cut_msg1)
except Exception: # 读取文件错误时, 打印读取错误的文档名
errortimes += 1
print(path, ' 文件读取错误')
print(j, "训练集中,文件读取错误数:", errortimes)
print("训练集长度:", len(train_contents))
# 测试集
test_contents = []
data_list2 = ['体育', '女性', '文学', '校园']
for j in data_list2:
errortimes = 0
path_lst = [name for name in os.listdir('./data/test/' + j)
if os.path.isfile(os.path.join('./data/test/' + j, name))]
for path in path_lst:
path = './data/test/' + j + '/' + path
try:
with open(path, 'r') as test_msg0:
test_msg1 = test_msg0.read()
test_cut_msg0 = jieba.cut(test_msg1)
test_cut_msg1 = " ".join(test_cut_msg0)
test_contents.append(test_cut_msg1)
except Exception:
errortimes += 1
print(path, '.txt 文件读取错误')
print(j, "测试集中,文件读取错误数:", errortimes)
print("测试集长度:", len(test_contents))
最终输出结果:
(三) 计算单词权重
使用TfidfVectorizer类得到TF-IDF 特征空间数据,其中max_df 参数为单词在文档中的最高出现率,假设 max_df=0.5,代表一个单词在 50% 的文档中都出现过了,那么它只携带了非常少的信息,因此就不作为分词统计。
值得注意的是,为保证训练集与测试集样本特征量一致性,在训练集数据进行拟合后,必须再次使用 TfidfVectorizer初始化向量空间模型,并且使用训练集词袋向量vocabulary=tf.vocabulary_。
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
train_features = tf.fit_transform(train_contents)
print('训练集特征shape:', train_features.get_shape())
# 使用 TfidfVectorizer初始化向量空间模型 使用训练集词袋向量
tf_test = TfidfVectorizer(stop_words=stop_words, sublinear_tf=True, max_df=0.5, vocabulary=tf.vocabulary_)
test_features = tf_test.fit_transform(test_contents)
print('测试集特征shape:', test_features.get_shape())
打印结果: 训练集特征shape: (3306, 22893),测试集特征shape: (200, 22893)。
(四) 生成分类器
首先构造与特征对应的标签列表,样本对应的标签为其所在的文件夹名,代码中的数字为测试集对应分类文件夹中文件的个数,可以使用len(【文件名列表】)方法获得,在此为简化代码,所有数字为手动进入文件夹查询的值。
由于TF-IDF特征数据为离散型数据,故使用多项式朴素贝叶斯类分类器,其中alpha为平滑参数,在本例中取值为0.01。
train_labels = ['体育'] * 1337 + ['女性'] * 954 + ['文学'] * 766 + ['校园'] * 249
test_labels = ['体育'] * 115 + ['女性'] * 38 + ['文学'] * 31 + ['校园'] * 16
model = MultinomialNB(alpha=0.01)
model.fit(train_features, train_labels)
predict_labels = model.predict(test_features)
# print(predict_labels)
score = accuracy_score(test_labels, predict_labels)
print('准确率:', score)
使用accuracy_score对模型进行评估,测试集分类准确率为0.92,判定此模型较为理想。
详细代码及数据请点击:GitHub