text_classification
文本分类-深度学习课程大作业
程序运行说明
硬件
GPU: Tesla V100, 32GB显存
内存:32GB
系统:Linux(训练),Windows(展示)
软件
CUDA版本: 9.2
Pytorch:1.5
其他库:gensim,sklearn,tqdm,flask,numpy等
运行方法
如果直接使用,步骤为:
进入"Flask"文件夹,在cmd中执行以下命令:python NLP_flask.py,便可启动 flask 后台,然后在浏览器地址栏输入127.0.0.1:5000,即可看到分类系统界面。
如果需要训练,步骤为:
如果想要训练非bert的模型,需要先训练词向量:进入"src"目录下,在terminal中执行以下命令:python train_w2v.py,修改该文件的代码可以设置word2vec的窗口大小、词向量维度等。
(./src目录)在terminal中输入python run.py --model model_name --word True/False,即可启动相应模型的训练。model_name是选择的模型,word为True(默认)则进行词级别的训练,否则进行字级别的训练。word参数只针对非bert模型,因为bert是分字的。可选的模型会在后面介绍。
**注意:**训练之前请先下载数据集和bert的相关文件,在相应的文件夹内有README说明文档,内附有百度云盘下载链接。
代码文件说明
下面解释./src目录下的代码:
run.py 训练主程序
train_eval.py 具体的训练逻辑
utils.py 工具类函数
train_w2v.py 训练词向量
global_config.py 全局参数设置,如batch_size等
./model 下是不同模型的实现
./temp 下存放了预训练的词向量
运行截图
背景及任务定义
随着上世纪 90 年代互联网飞速发展,到现在我们已经进入了大数据时代。在当今的互联网中,容纳了大量的数据信息,其中包含了文本,声音,图片等。文本数据相较于图片和声音,占用网络资源少,使得在网络资源中,文本占据了相当大的一部分。为了能够将这些信息有效的组织和管理,使用户能够快速、准确、全面地获取需要的信息,就成了当今信息科学面临地一个大挑战。文本分类从知识工程到九十年代基于机器学习地分类系统,再到近
十年快速发展地基于深度学习地分类系统,现在地文本分类系统已经形成了一个较为完善的体系。
对于文本分类任务,可将其定义为一组输入到输出的映射。输入一般是包含一些原始文本的数据集,数据集由每个文档中的文本序列组成,如 $D = {X_1 ,X_2 ,…,X_n }$ ,其中 $X_i $为一个文本(每个文本包含 w 个单词),每个文本都用一个离散的数值来标注,代表该文本所在的类别。
我们做的是新闻分类任务,因此输入是一组新闻文本,输出则应该是该新闻对应的类别,如“军事”、“政治”等。我们的目的,就是训练一个模型,使其能完成自动分类的任务。
提出方法
我们把整个训练过程分为数据预处理和训练两个阶段。下面将一一介绍。
数据预处理
本系统采用了清华NLP组提供的THUCNews新闻文本分类数据集的子集。THUCNews是根据新浪新闻RSS订阅频道2005~2011年间的历史数据筛选过滤生成,包含74万篇新闻文档(2.19 GB),均为UTF-8纯文本格式。并在的原始新浪新闻分类体系的基础上,重新整合划分出14个候选分类类别:财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐。
由于本地电脑计算资源有限我们在上述14个类中,每一类随机抽取了2万条数据,有三个类别数据量不足两万,我们全部使用。每个类别的使用数据量如图1,其中一条新闻样例如图2所示。
图1 每个类别新闻数:
图2 新闻样例(时政类):
分词
由于汉语不像英语一样有空格作为分隔符,所以我们在训练之前需要把句子分成一个个的单词。目前常用的是算法是HMM、CRF、SVM、深度学习等算法,比如Stanford、Hanlp分词工具是基于CRF算法。以CRF为例,基本思路是对汉字进行标注训练,不仅考虑了词语出现的频率,还考虑上下文,具备较好的学习能力,因此其对歧义词和未登录词的识别都具有良好的效果。
本系统中采用jieba分词对数据预处理。首先使用jieba分词将所有的文章中的单词分开,单词与单词之间用空格分隔,并在最后一个单词后面加上退格符:\t,上述内容构成数据集中的一行数据。我们的对所有的文章进行遍历,按照上述内容构建我们的数据集,并划分出测试集和验证集,它们的比例为:0.9: 0.1: 0.1。
词嵌入
本文使用的是近几年在深度领域非常火的技术:Word2Vec。Word2Vec方法使用具有两个隐藏层的浅神经网络:连续词袋(CBOW)和跳跃图(Skip-gram)模型,将文本中的每个单词表示为一个相对较小的稠密向量。输出向量的大小通常用 k 表示,Word2vec 将语义相似的词聚集在这个 k 维空间中比较接近的区域。在CBOW 中,根据一个词的上下文来预测该词;Skip-gram 则只看一个词来预测它的上下文。Word2Vec 最大的特点之一是它可以实现字向量之间的算术运算,即向量反映了词的相似性。
本系统采用gensim来实现Word2Vec,并将训练好的词向量放进分类深度学习模型中进行fine-tune,以根据具体任务进一步调整。
对于bert模型,不需要预训练的词向量。
填充/截断
我们需要将每一个文本数据转换成一个等长的数据,以便神经网络处理。但新闻文本一般长度不一,所以我们需要对长的文本进行截断,对长度不够的文本进行填充。我们设定的长度值为500,即每篇文章都被处理成500个词的表示。
对于bert模型,由于参数量过大,我们将长度值设为256。
训练
在该系统中,我们使用了9个模型进行实验,它们分别是:bert、bert-LSTM、LSTM、LSTM-Attention、LSTM-GRU、FastText、TextCNN、TextRCNN、DPCNN,由于篇幅限制,下面仅对其进行简单的介绍。值得注意的是,我们在某些模型中加入了自己的想法,虽然只是少量的改动,但我们认为也是一种微创新。在FastText模型中,由于中文没有像英文这种字符级的n-gram,而1-gram就相当于中文字级别的建模了,所以我们同时使用了1-gram,2-gram和3-gram,将其拼在一起进行分类,效果比只用1-gram要提升不少。另外,我们也自己实现了一个模型:LSTM-GRU,它是将两种变种RNN堆叠起来实现的一个模型,实际效果显示也取得不错的精度。
bert & bert-LSTM
谷歌团队在2018年提出了预训练模型Bert用于词向量表示,在当时NLP的11项任务中基本取得了最好的效果。Bert模型的全名是Bidirectional Encoder Representations from Transformer,是由2017年的Transformer模型的Encoder部分在大规模语料库训练而来的。Transformer舍弃了传统的CNN和RNN操作,整个模型都是由attention机制组成的,这样的好处是更好地实现了并行化,在长文本序列下依然可以有效地获取文本的依赖关系,而且模型是双向地学习上下文的关系,使得词向量表示的语义信息更加丰富。Bert学习到普通文本的语义表示后,可以在特定的NLP任务下进行微调,使得任务的结果有更大的提升。
Bert的训练采用了33亿单词以及25亿维基百科和8亿文本语料,使用的TPU和GPU进行训练,这个规模的训练数据和算力是非常庞大的,这也是Bert模型在众多任务中脱颖而出的重要原因。BERT实际是一个语言模型,通过大规模预料让模型理解和掌握语言的本质规律,便于后续对具体NLP任务进行微调。由此,作者提出了两个预训练任务:Masked LM和Next Sentence Prediction。Bert-LSTM模型则是在Bert的基础上加入了LSTM模型。
前面有说到,Bert是Transformer Encoder训练而来的,下面展示了该模型的架构:
图3 Bert模型架构
解释一下其中的几个关键模块:
Multi-head Self-Attention
作者用多个Self-Attention模块来表示不同的语义空间,从而获取文本中每个字在不同语义空间的词向量表示,然后将每个字的多个空间的词向量表示进行线性组合得到增强的语义表示向量。
残差连接
这个步骤是将模型的输入向量与Multi-head Self-Attention的输出向量直接相加,作为最后的输出,这种残差连接有利解决梯度消失的问题,而且由于修改输入比重构整个输出更容易,所以模型也变得更加容易训练。
Layer Normalization :对残差连接后的输出向量作均值为,方差为1的标准化。
线性转换:对Layer Normalization后的增强语义向量进行两次线性变换,来增强整个模型的表达能力。
LSTM
LSTM是一种特殊的RNN结构,是Jürgen Schmidhuber等人在1997年提出的。它通过引入三个门来让输入信息有选择性地影响网络的隐藏状态,这三个门分别被称为遗忘门、输入门和输出门。遗忘门的作用是让网络“忘记”之前学到的不重要的信息,它会根据当前的输入$x_t$和上一时刻的隐藏状态$h_{t-1}$来决定哪一部分需要被遗忘。输入门的作用是根据当前的输入为网络补充新的记忆,它同样是利用当前的输入$x_t$和上一时刻的隐藏状态$h_{t-1}$来计算得出。输出门的作用是把输入门和遗忘门的信息整合到一起,生成LSTM网络的最终隐藏状态。为了完成这一计算,LSTM添加了额外的隐含状态$c_t$来记忆序列的信息。可以这么理解,$h_{t-1}$是上一时刻的隐藏状态,$c_{t}$是经过遗忘门和输入门计算得出的当前时刻新状态,它通过输出门后生成当前时刻的最终隐藏状态$h_t$。图3展示了一个LSTM的单元结构。
图4 LSTM结构图:
LSTM -Attention
注意力机制(Attention Mechanism, AM)近几年被频繁用于 Deep Learning 的各个领域,特别是在自然语言处理上,并在文本分类任务取得了很好的效果。注意力机制可以看做一个加权方案,它的目的是计算出每一条输入数据对分类结果的注意力权重,该权重值越大,说明对结果的影响越大,即该条数据越重要。通常计算注意力权重分为以下两个步骤:第一步先计算每一条输入数据与输出结果的相关性,一般可用两者向量点积、向量相似性计算或者直接利用神经网络求解;由于不同方法求得的数值范围不同,所以第二步需要对第一步求出的相关性进行 softmax归一化,即可计算出注意力权重。
LSTM-Attention模型在LSTM模型的基础上,加入了注意力机制。在LSTM的输出层接一个小的神经网络,该神经网络接受LSTM的输出并计算出每一个timestep的权重作为输出,得到的权重值与每一个timestep作加权平均,便得到了加入注意力机制的LSTM输出。最后将这个输出拿去进行softmax分类,即可得到分类结果。
LSTM-GRU
门控递归单元 (GRU) 是 J. Chung 等人提出来的带门控机制的 RNN,是 LSTM的一个简化变体,但有如下区别:GRU 包含两个门(LSTM 有三个门),不具有任何内部内存(即 LSTM 中的 C 状态)。
GRU比LSTM晚了将近20年被提出,相比LSTM,GRU结合了遗忘门和输入门,合并了LSTM中的c和h,使得模型的参数更少,相比于LSTM更容易训练,需要更少的数据来泛化,能够取得不错的效果。但是LSTM作为一种经典的模型,在本文提供足够数据量的情况下,LSTM的强大表达能力可能会产生更好的结果。因此,本模型选择将两者结合在一起,先设计LSTM,得到的输出再输入到GRU中,比较这种结合的方式是否比单独使用LSTM更有效果。
本案例中,LSTM和GRU都是双向的。
FastText
FastText是FAIR(FacebookAIResearch)于2016年发布的一个文本分类模型,正如其名称所要表达的那样,“Fast”,处理速度很快,因其简单高效的优秀特性,在文本分类任务中广受大家的喜爱。FastText的模型结构如下图所示,非常简单,主要由三部分构成:输入层、隐藏层以及输出层。输入层输入的是文本的embedding表示,隐藏层负责获取文本的整体语义表示,而输出层主要输出分类各类别的概率分布,完成分类目标。
图5: FastText模型结构图
FastText在输入层、隐藏层以及输出层都进行了一定的创新,总体上构成了这个优美的模型。在输入层上,区别于一般的文本表示方法使用单词的词向量表示,FastText则使用了更细粒度的字符级n-gram向量表示,可以捕捉单词内部更细微的语义,来消除形态方面的一些影响,在处理低频词以及未登陆词上效果很好。在隐藏层上,FastText没有使用复杂的神经网络结构,而是直接用所有字符级n-gram向量表示的平均结果来作为整个文本的向量表示,这被证明是一个十分简单有效的方法。在输出层上,不是使用传统的softmax分类层,而是使用改进后的层次softmax分类层,它根据训练样本中每一类出现的频率来构造哈夫曼树,频率越高的类别在树中的路径越短,频率越低的类别在树中的路径越长,这样在类别越多时性能提升越明显。
FastText在本质上是一个线性分类器,但其在每一层独特的处理方法使得其在文本分类任务上展现出了优越的性能,尤其是在处理超大规模、类别非常多的文本分类任务上,更是分类模型的不二选择,深得大家青睐。
TextCNN
TextCNN是Yoon Kim在2014年提出的文本分类模型,相比较于传统的CNN而言,结构整体上更加简化。因为自然语言数据往往是一维的,对词向量进行从左到右的滑动卷积是没有意义的,因此输入层变成了一维卷积层。TextCNN在预先训练好的词向量基础上取得了不错的效果,在多个数据集上都达到了超越benchmark 的表现。另一方面,其网络结构简单,参数少,也就意味着计算量少,训练速度很快,适合在实际应用中部署。
图7:TextCNN结构图
如上图所示,TextCNN首先需要通过word-embedding方式将每个词构建成词向量作为网络的输入,然后进行convolution卷积操作,其中有不同的kernel尺寸以便获得更宽的感受野,每个kernel size输出两个feature maps,经过1-max pooling层之后就变成了定长的表示,最后是全连接的softmax层,输出每个类别的概率,从而实现文本的分类。
TextRCNN
RNN擅长处理序列结构,它可以考虑到句子上下文信息,是文本分类任务中的常用模型,但RNN属于有偏(biased)模型,句子中越往后的词重要性越高,因此可能影响分类结果;而CNN属于无偏模型,能够通过最大池化获得最重要的特征。TextRCNN是一种结合了两者优点的文本分类模型,它使用双向循环结构来获取上下文信息,并使用最大池化层来得到文本的重要部分,自动判断出哪些特征在文本分类过程中起更重要的作用。
图7:TextRCNN结构图
上图为TextRCNN的结构图,从左往右看,第一步对词进行词向量编码得到e(w),由word embedding层实现;第二步将词向量输入到双向的 RNN得到Cl和Cr,这里可以看成两个 RNN分别从左往右,从右往左扫描,这样能获得词汇更多的上下文信息,图中Cl(wi)和Cr(wi)分别代表词wi的左上下文和右上下文;第三步将前两步得出的结果拼接到一起得到单词wi的表示为[Cl(wi); e(wi); Cr(wi)],再通过一个线性变化并输入到tanh激活函数得到wi的潜在语义向量yi(2),通过这前三步即完成了文本的表示,该模型的作者表示,从卷积神经网络的角度来看,这三步完成的循环结构就是卷积层;第四步使用一维max pooling对yi(2)进行池化,得到y(3),最大池化可以帮助我们找到句子中最重要的潜在语义信息;最后一步经过全连接层(线性变化),再接上softmax层得到分类结果。
DPCNN
DPCNN是腾讯AI-lab提出的,发表在自然语言处理领域顶级会议ACL2017。这篇paper提出了一个低复杂度词级别的卷积神经网络架构,该网络可以用于文本分类,有效地表示文本中的长范围关联。实验证明在不增加太多计算成本的情况下,增加网络深度就可以获得最佳的准确率。下图是论文中给出的模型结构及其比较。
图左边(a)中作者将TextCNN的包含多尺寸卷积滤波器的卷积层的卷积结果称之为Region embedding,意思就是对一个文本区域/片段(比如3gram)进行一组卷积操作后生成的embedding;紧接着是叠加的卷积块,这里选择了适当的两层等长卷积来提高词位embedding的表示丰富性;然后就开始池化,在一个卷积块(两层的等长卷积)后,使用一个size=3和stride=2进行最大池化,序列的长度就被压缩成了原来的一半,其能够感知到的文本片段就比之前长了一倍,从而捕获到长距离依赖;再重复一次两个等长的卷积;最后的池化层把每个文档的数据聚合为一个向量。另外作者为了恒等映射,使用加法进行残差连接,这样就可以极大的缓解了梯度消失问题,使深度网络的训练成为可能。其整体架构十分精美。
图8:DPCNN结构图
实验
我们在前面章节提出了九个模型,因此在实验部分我们主要比较各个模型之间的分类效果,包括F1值和训练所消耗的时间。由于时间问题,我们没有做更多的超参数调节工作,每个模型的基本超参数,比如batch的大小,词向量的维度,dropout等都是一样的。我们随机生成了两批数据,对每个模型进行了两次实验,然后取其平均值,得到了以下的实验结果:
表1:不同深度学习模型训练结果对比
方法
loss
准确率
F1
时间(1 Epoch)
TextCNN
0.17
95.67%
95.67%
2:48
TextRNN
0.13
96.43%
96.43%
13:24
TextRCNN
0.12
96.42%
96.43%
07:24
TextRNN_Att
0.13
96.40%
96.40%
13:33
LSTMGRU
0.13
96.45%
96.45%
27:29
DPCNN
0.14
95.82%
95.83%
04:07
FastText
0.17
95.11%
95.11%
03:11
Bert
0.12
96.49%
96.49%
115:43
Bert-RNN
0.13
96.25%
96.25%
157:43
根据上述的结果,我们对这几个模型进行F1值从高到低的排序,结果是:Bert(96.49%) > LSTMGRU(96.45%) > TextRCNN(96.43%) > TextRNN(96.43%) > TextRNN_Att(96.40%) > Bert_RNN(96.25%) > DPCNN(95.83%) > TextCNN (96.67%) > FastText(95.11%)
首先看到,每个模型的分类效果都不差,F1值都在95%以上。其中得分最高的模型是Bert,但领先优势并不是很大,只比第二名的LSTM-GRU模型高出0.04%。但是,使用了RNN结构的Bert效果反而降低了,仅为96.25%,这说明Bert模型本来就很强大了,在应付文本分类这种任务时,接上复杂的模型反而会使效果变差。同时,使用了注意力机制的LSTM比原始的LSTM效果更差,这也许跟上面谈论的原因相同。另外,可以看到,第二、第三、第四名的模型全部都是使用了RNN结构,而倒数第二、第三的模型则只使用了CNN结构,这说明在处理序列数据的时候,RNN的确会比CNN更有优势。效果最差的模型是FastText,但依然达到了95.11%的准确率,这对于它相对简单的架构来讲,这个分数也不低了。
在训练一个epoch的时间消耗上,对这几个模型进行从低到高的排序,结果是:TextCNN(02:48) < FastText(03:11) < DPCNN(04:07) < TextRCNN(07:24) < TextRNN(13:24) < TextRNN_Att(13:33) < LSTMGRU(27:29) < Bert(115:43) < Bert_RNN(157:43)
可以看到,训练一个epoch所使用的时间最长的是Bert_RNN模型,其次是Bert,分别达到了两个半小时和两个小时左右,比其他模型高出非常多。这是因为Bert参数量巨大的原因,计算非常消耗时间。参考上面的F1值对比结果,我们认为Bert模型在这个任务上并不是特别有效率,它消耗了非常多的时间,却只带来一点点精度的领先。在排名前四的模型中,消耗最少时间的是CNN,然后是FastText,往后看紧接着的两个模型也是使用了CNN的结构,之后才是RNN。这是因为CNN可以并行运算,而RNN只能一个接一个地递归计算,因此在GPU的加速上CNN的优势体现得非常明显。
结论
在本项目中,我们利用深度学习方法进行了新闻文本的分类。我们先对数据进行预处理,包括清洗和训练词向量等,接着提出了9个模型进行了对比实验。在这九个模型中,Bert模型分类效果最好,但训练消耗时间太长,因此并不是很有性价比。CNN架构的方法在训练时间上有很大的优势,比RNN架构的方法要快很多,但是分类效果普遍比RNN方法要差。在该任务中,使用了Bert_RNN模型和RNN_Attention方法都比其对应的原始方法效果要差,说明对于这个简单的任务,过于复杂的模型反而会带来反效果。综合F1值和训练时间来看,TextRCNN在分类效果上和消耗时间上都表现不错,是一个很有实际运用意义的模型。
方法
loss
准确率
F1
时间(1 Epoch)
TextCNN
0.21
92.97%
92.97%
00:31
TextRNN
0.19
93.50%
93.50%
00:45
TextRCNN
0.18
93.87%
93.87%
00:51
TextRNN_Att
0.18
93.45%
93.45%
01:50
DPCNN
0.19
93.00%
93.00%
00:38
FastText
0.21
93.10%
93.10%
00:40
Bert
0.16
94.71%
94.71%
24:02
Bert-RNN
0.17
94.51%
94.51%
30:41
方法
loss
准确率
F1
时间(1 Epoch)
TextCNN
0.63
74.76%
68.48%
00:19
TextRNN
0.62
75.71%
70.84%
00:36
TextRCNN
0.57
76.99%
72.07%
00:22
TextRNN_Att
0.57
76.98%
72.32%
00:48
DPCNN
0.58
76.64%
71.78%
01:16
FastText
0.68
72.96%
66.96%
00:58
Bert
0.50
80.89%
77.30%
13:52
Bert-RNN
0.50
80.49%
76.68%
17:12