• 【毕业设计】深度学习中文文本分类(新闻分类 情感分类 垃圾邮件分类)


    0 简介

    🔥 Hi,大家好,这里是丹成学长的毕设系列文章!

    🔥 对毕设有任何疑问都可以问学长哦!

    这两年开始,各个学校对毕设的要求越来越高,难度也越来越大… 毕业设计耗费时间,耗费精力,甚至有些题目即使是专业的老师或者硕士生也需要很长时间,所以一旦发现问题,一定要提前准备,避免到后面措手不及,草草了事。

    为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的新项目是

    🚩 中文文本分类 ( 机器学习 和 深度学习 ) - 新闻分类 情感分类 垃圾邮件分类

    🥇学长这里给一个题目综合评分(每项满分5分)

    • 难度系数:4分
    • 工作量:4分
    • 创新点:3分

    🧿 选题指导, 项目分享:

    https://gitee.com/yaa-dc/BJH/blob/master/gg/cc/README.md

    1 前言

    学长今天帮助同学开发项目,正好需要到文本分类,今天就带大家梳理一下中文文本分类的主要方法和流程

    学长本片博客的目的主要记录学长自己构建文本分类系统的过程,分别构建基于传统机器学习的文本分类和基于深度学习的文本分类系统,并在同一数据集上进行测试。

    2 中文文本分类

    作为NLP领域最经典的场景之一,文本分类积累了大量的技术实现方法,如果将是否使用深度学习技术作为标准来衡量,实现方法大致可以分成两类:

    • 基于传统机器学习的文本分类
    • 基于深度学习的文本分类

    facebook之前开源的fastText属于简化版的第二类,词向量取平均直接进softmax层,还有业界研究上使用比较多的TextCNN模型属于第二类。

    学长本片博客的目的主要记录学长自己构建文本分类系统的过程,分别构建基于传统机器学习的文本分类和基于深度学习的文本分类系统,并在同一数据集上进行测试。

    经典的机器学习方法采用获取tf-idf文本特征,分别喂入logistic regression分类器和随机森林分类器的思路,并对两种方法做性能对比。

    基于深度学习的文本分类,这里主要采用CNN对文本分类,考虑到RNN模型相较CNN模型性能差异不大并且耗时还比较久,这里就不多做实验了。

    实验过程有些比较有用的small trick分享,包括多进程分词、训练全量tf-idf、python2对中文编码的处理技巧等等,在下文都会仔细介绍。

    3 数据集准备

    本文采用的数据集是很流行的搜狗新闻数据集,get到的时候已经是经过预处理的了,所以省去了很多数据预处理的麻烦,数据集内容如下

    在这里插入图片描述

    数据集一共包括10类新闻,每类新闻65000条文本数据,训练集50000条,测试集10000条,验证集5000条。

    4 经典机器学习方法

    4.1 分词、去停用词

    使用短文本分类博文中提到的分词工具类,对训练集、测试集、验证集进行多进程分词,以节省时间:

    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."
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.2 文本向量化 tf-idf

    这里有几点需要注意的,一是计算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)
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    4.3 构建训练和测试数据

    因为本来文本就是以一定随机性抽取成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:]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.4 训练分类器

    4.4.1 logistic regression分类器

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    分类结果如下(包括准确率、召回率、F1值):

    在这里插入图片描述

    4.5 Random Forest 分类器

    # 随机森林分类器
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    分类结果(包括准确率、召回率、F1值):

    在这里插入图片描述

    4.6 结论

    • 1 上面采用逻辑回归分类器和随机森林分类器做对比:
    • 2 可以发现,除了个别分类随机森林方法有较大进步,大部分都差于逻辑回归分类器
    • 3 并且200棵树的随机森林耗时过长,比起逻辑回归分类器来说,运算效率太低

    5 深度学习分类器 - CNN文本分类

    5.1 字符级特征提取

    这里和前文差异比较大的地方,主要是提取文本特征这一块,这里的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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    学长这里做了一下测试:

    #! /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']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这种基本不是原意的字符级别的特征,也能从统计意义上表征文本,从而作为特征,这一点需要清楚。

    最终,在同一数据集上,得到的分类结果如下:

    在这里插入图片描述

    6 最后

  • 相关阅读:
    rgb转hsl,红绿蓝转色调饱和度亮度(颜色转换,c#)
    【华为OD机试python】斗地主之顺子【2023 B卷|100分】
    iMazing兼容Win和Mac2023免费版iOS设备管理器
    黑盒测试-场景法
    【无标题】
    单例模式(饿汉式和懒汉式 )(简洁版)(Java)
    Python基于php+MySQL的英语学习交流平台
    09 Ubuntu安装FreeCAD
    Docker Cgroups资源控制管理
    DDIM代码详细解读(3):核心采样代码、超分辨率重建
  • 原文地址:https://blog.csdn.net/caxiou/article/details/127803131