Hi,大家好,这里是丹成学长的毕设系列文章!
对毕设有任何疑问都可以问学长哦!
这两年开始,各个学校对毕设的要求越来越高,难度也越来越大… 毕业设计耗费时间,耗费精力,甚至有些题目即使是专业的老师或者硕士生也需要很长时间,所以一旦发现问题,一定要提前准备,避免到后面措手不及,草草了事。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的新项目是
中文文本分类 ( 机器学习 和 深度学习 ) - 新闻分类 情感分类 垃圾邮件分类
学长这里给一个题目综合评分(每项满分5分)
选题指导, 项目分享:
https://blog.csdn.net/Mr_DC_IT/article/details/126460477
学长今天帮助同学开发项目,正好需要到文本分类,今天就带大家梳理一下中文文本分类的主要方法和流程
学长本片博客的目的主要记录学长自己构建文本分类系统的过程,分别构建基于传统机器学习的文本分类和基于深度学习的文本分类系统,并在同一数据集上进行测试。
作为NLP领域最经典的场景之一,文本分类积累了大量的技术实现方法,如果将是否使用深度学习技术作为标准来衡量,实现方法大致可以分成两类:
facebook之前开源的fastText属于简化版的第二类,词向量取平均直接进softmax层,还有业界研究上使用比较多的TextCNN模型属于第二类。
学长本片博客的目的主要记录学长自己构建文本分类系统的过程,分别构建基于传统机器学习的文本分类和基于深度学习的文本分类系统,并在同一数据集上进行测试。
经典的机器学习方法采用获取tf-idf文本特征,分别喂入logistic regression分类器和随机森林分类器的思路,并对两种方法做性能对比。
基于深度学习的文本分类,这里主要采用CNN对文本分类,考虑到RNN模型相较CNN模型性能差异不大并且耗时还比较久,这里就不多做实验了。
实验过程有些比较有用的small trick分享,包括多进程分词、训练全量tf-idf、python2对中文编码的处理技巧等等,在下文都会仔细介绍。
本文采用的数据集是很流行的搜狗新闻数据集,get到的时候已经是经过预处理的了,所以省去了很多数据预处理的麻烦,数据集内容如下
数据集一共包括10类新闻,每类新闻65000条文本数据,训练集50000条,测试集10000条,验证集5000条。
使用短文本分类博文中提到的分词工具类,对训练集、测试集、验证集进行多进程分词,以节省时间:
import multiprocessing
tmp_catalog = '/home/zhouchengyu/haiNan/textClassifier/data/cnews/'
file_list = [tmp_catalog+'cnews.train.txt', tmp_catalog+'cnews.test.txt']
write_list = [tmp_catalog+'train_token.txt', tmp_catalog+'test_token.txt']
def tokenFile(file_path, write_path):
word_divider = WordCut()
with open(write_path, 'w') as w:
with open(file_path, 'r') as f:
for line in f.readlines():
line = line.decode('utf-8').strip()
token_sen = word_divider.seg_sentence(line.split('\t')[1])
w.write(line.split('\t')[0].encode('utf-8') + '\t' + token_sen.encode('utf-8') + '\n')
print file_path + ' has been token and token_file_name is ' + write_path
pool = multiprocessing.Pool(processes=4)
for file_path, write_path in zip(file_list, write_list):
pool.apply_async(tokenFile, (file_path, write_path, ))
pool.close()
pool.join() # 调用join()之前必须先调用close()
print "Sub-process(es) done."
这里有几点需要注意的,一是计算tf-idf是全量计算,所以需要将train+test+val的所有corpus都相加,再进行计算,二是为了防止文本特征过大,需要去低频词,因为是在jupyter上写的,所以测试代码的时候,先是选择最小的val数据集,成功后,再对test,train数据集迭代操作,希望不要给大家留下代码冗余的影响…[悲伤脸]。实现代码如下:
def constructDataset(path):
"""
path: file path
rtype: lable_list and corpus_list
"""
label_list = []
corpus_list = []
with open(path, 'r') as p:
for line in p.readlines():
label_list.append(line.split('\t')[0])
corpus_list.append(line.split('\t')[1])
return label_list, corpus_list
tmp_catalog = '/home/zhouchengyu/haiNan/textClassifier/data/cnews/'
file_path = 'val_token.txt'
val_label, val_set = constructDataset(tmp_catalog+file_path)
print len(val_set)
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
tmp_catalog = '/home/zhouchengyu/haiNan/textClassifier/data/cnews/'
write_list = [tmp_catalog+'train_token.txt', tmp_catalog+'test_token.txt']
tarin_label, train_set = constructDataset(write_list[0]) # 50000
test_label, test_set = constructDataset(write_list[1]) # 10000
# 计算tf-idf
corpus_set = train_set + val_set + test_set # 全量计算tf-idf
print "length of corpus is: " + str(len(corpus_set))
vectorizer = CountVectorizer(min_df=1e-5) # drop df < 1e-5,去低频词
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus_set))
words = vectorizer.get_feature_names()
print "how many words: {0}".format(len(words))
print "tf-idf shape: ({0},{1})".format(tfidf.shape[0], tfidf.shape[1])
"""
length of corpus is: 65000
how many words: 379000
tf-idf shape: (65000,379000)
"""
因为本来文本就是以一定随机性抽取成3份数据集的,所以,这里就不shuffle啦,偷懒一下下。
from sklearn import preprocessing
# encode label
corpus_label = tarin_label + val_label + test_label
encoder = preprocessing.LabelEncoder()
corpus_encode_label = encoder.fit_transform(corpus_label)
train_label = corpus_encode_label[:50000]
val_label = corpus_encode_label[50000:55000]
test_label = corpus_encode_label[55000:]
# get tf-idf dataset
train_set = tfidf[:50000]
val_set = tfidf[50000:55000]
test_set = tfidf[55000:]
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
# from sklearn.metrics import confusion_matrix
# LogisticRegression classiy model
lr_model = LogisticRegression()
lr_model.fit(train_set, train_label)
print "val mean accuracy: {0}".format(lr_model.score(val_set, val_label))
y_pred = lr_model.predict(test_set)
print classification_report(test_label, y_pred)
分类结果如下(包括准确率、召回率、F1值):
# 随机森林分类器
from sklearn.ensemble import RandomForestClassifier
rf_model = RandomForestClassifier(n_estimators=200, random_state=1080)
rf_model.fit(train_set, train_label)
print "val mean accuracy: {0}".format(rf_model.score(val_set, val_label))
y_pred = rf_model.predict(test_set)
print classification_report(test_label, y_pred)
分类结果(包括准确率、召回率、F1值):
这里和前文差异比较大的地方,主要是提取文本特征这一块,这里的CNN模型采用的是字符级特征提取,比如data目录下cnews_loader.py中:
def read_file(filename):
"""读取文件数据"""
contents, labels = [], []
with open_file(filename) as f:
for line in f:
try:
label, content = line.strip().split('\t')
contents.append(list(content)) # 字符级特征
labels.append(label)
except:
pass
return contents, labels
def build_vocab(train_dir, vocab_dir, vocab_size=5000):
"""根据训练集构建词汇表,存储"""
data_train, _ = read_file(train_dir)
all_data = []
for content in data_train:
all_data.extend(content)
counter = Counter(all_data)
count_pairs = counter.most_common(vocab_size - 1)
words, _ = list(zip(*count_pairs))
# 添加一个 来将所有文本pad为同一长度
words = ['' ] + list(words)
学长这里做了一下测试:
#! /bin/env python
# -*- coding: utf-8 -*-
from collections import Counter
"""
字符级别处理,
对于中文来说,基本不是原意的字,但是也能作为一种统计特征来表征文本
"""
content1 = "你好呀大家"
content2 = "你真的好吗?"
# content = "abcdefg"
all_data = []
all_data.extend(list(content1))
all_data.extend(list(content2))
# print list(content) # 字符级别处理
# print "length: " + str(len(list(content)))
counter = Counter(all_data)
count_pairs = counter.most_common(5)
words, _ = list(zip(*count_pairs))
words = ['' ] + list(words) #['', '\xe5', '\xbd', '\xa0', '\xe4', '\xe7']
这种基本不是原意的字符级别的特征,也能从统计意义上表征文本,从而作为特征,这一点需要清楚。
最终,在同一数据集上,得到的分类结果如下:
选题指导, 项目分享:
https://blog.csdn.net/Mr_DC_IT/article/details/126460477