代码:https://download.csdn.net/download/qq_38735017/87425780
实验主要来自天池的一个新人赛,赛题以自然语言处理为背景,要求选手根据新闻文本字符对新闻的类别进行分类,这是一个经典文本分类问题。通过这道赛题可以引导大家走入自然语言处理的世界,带大家接触 NLP 的预处理、模型构建和模型训练等知识点。
赛题以匿名处理后的新闻数据为赛题数据,数据集报名后可见并可下载。赛题数据为新闻文本,并按照字符级别进行匿名处理。整合划分出 14 个候选分类类别:财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐****的文本数据。
题数据由以下几个部分构成:训练集 20w 条样本,测试集 A 包括 5w 条样本,测试集 B 包括 5w 条样本。为了预防选手人工标注测试集的情况,我们将比赛数据的文本按照字符级别进行了匿名处理。处理后的赛题训练数据如下:
在数据集中标签的对应的关系如下:
测指标如下:评价标准为类别 f1_score 的均值,选手提交结果与实际测试集的类别进行对比,结果越大越好。
实验数据分析:在读取完成数据集后,我们还可以对数据集进行数据分析的操作。虽然对于非结构数据并不需要做很多的数据分析,但通过数据分析还是可以找出一些规律的。此步骤我们读取了所有的训练集数据,在此我们通过数据分析希望得出以下结论:赛题数据中,新闻文本的长度;赛题数据的类别分布是怎么样的,哪些类别比较多;赛题数据中,字符分布是怎么样的。
在赛题数据中每行句子的字符使用空格进行隔开,所以可以直接统计单词的个数来得到每个句子的长度。统计并如下
对新闻句子的统计可以得出,本次赛题给定的文本比较长,每个句子平均由 907 个字符构成,最短的句子长度为 2,最长的句子长度为 57921。下图将句子长度绘制了直方图,可见大部分句子的长度都几种在 2000 以内。
接下来可以对数据集的类别进行分布统计,具体统计每类新闻的样本个数。
从统计结果可以看出,赛题的数据集类别分布存在较为不均匀的情况。在训练集中科技类新闻最多,其次是股票类新闻,最少的新闻是星座新闻。接下来可以统计每个字符出现的次数,首先可以将训练集中所有的句子进行拼接进而划分为字符,并统计每个字符的个数。从统计结果中可以看出,在训练集中总共包括 6869 个字,其中编号 3750 的字出现的次数最多,编号 3133 的字出现的次数最少。通过上述分析我们可以得出以下结论:赛题中每个新闻包含的字符个数平均为 1000 个,还有一些新闻字符较长;赛题中新闻类别分布不均匀,科技类新闻样本量接近 4w,星座类新闻样本量不到 1k;赛题总共包括 7000-8000 个字符;通过数据分析,我们还可以得出以下结论:每个新闻平均字符个数较多,可能需要截断;由于类别不均衡,会严重影响模型的精度;
二、实验方法和流程
1 文本分词
词是最小的能够独立活动的有意义的语言成分,英文单词之间是以空格作为自然分界符的,而汉语是以字为基本的书写单位,词语之间没有明显的区分标记,在常规的中文文本分类任务中,开始一般需要对数据进行中文分词和去除停用词的操作,但是由于本次数据是匿名处理的,所以用字作为最小的处理单元,无需分词操作。
2word2vec
word2vec,即词向量,将一个词用一个向量来表示。是 2013 年 Google 提出的。word2vec 工具主要包含两个模型:跳字模型(skip-gram)和连续词袋模型(continuousbagofwords,简称 CBOW),以及两种高效训练的方法:负采样(negativesampling)和层序 softmax(hierarchicalsoftmax)。word2vec 词向量可以较好地表达不同词之间的相似和类比关系。word2vec 是一个 NLP 工具,它可以将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。
在本次实验中,由于训练集数据量比较大,所以为了提高模型的性能,用训练集的数据预训练了一个维度为 100 的 Word2vec 词向量
3Bi-LSTM
LSTM 的全称是 LongShort-TermMemory,它是 RNN(RecurrentNeuralNetwork)的一种。LSTM 由于其设计的特点,非常适合用于对时序数据的建模,如文本数据。Bi-LSTM 是 Bi-directionalLongShort-TermMemory 的缩写,是由前向 LSTM 与后向 LSTM 组合而成。
4TextRNN
TextRNN 的结构分为:1.embedddinglayer,2.Bi-LSTMlayer,3.concatoutput,4.FClayer,5.softmax,下图是一个传统的 TextRNN 模型结构图:
一般取前向/反向 LSTM 在最后一个时间步长上隐藏状态,然后进行拼接,在经过一个 softmax 层(输出层使用 softmax 激活函数)进行一个多分类;或者取前向/反向 LSTM 在每一个时间步长上的隐藏状态,对每一个时间步长上的两个隐藏状态进行拼接,然后对所有时间步长上拼接后的隐藏状态取均值,再经过一个 softmax 层(输出层使用 softmax 激活函数)进行一个多分类(2 分类的话使用 sigmoid 激活函数)。但是在本次实验中使用的是对 LSTM 的每个时间步的输出进行 Attention 之后的结果作为句子或者文本的向量表示,再进行后续的处理。
5 实验流程
5.1 数据处理
数据首先会经过 all_data2fold 函数,这个函数的作用是把原始的 DataFrame 数据,转换为一个 list,有 10 个元素,表示交叉验证里的 10 份,每个元素是 dict,每个 dict 包括 label 和 text。首先根据 label 来划分数据行 index,生成 label2id。label2id 是一个 dict,key 为 label,value 是一个 list,存储的是该类对应的 index。然后根据 label2id,把每一类别的数据,划分到 10 份数据中.最终得到的数据 fold_data 是一个 list,有 10 个元素,每个元素是 dict,[{labels:textx},{labels:textx}...]。最后,把前 9 份数据作为训练集 train_data,最后一份数据作为验证集 dev_data,并读取测试集 test_data
5.2 词表 Vocab
创建词和 index 对应的字典,这里包括 2 份字典,分别是:_id2word 和_id2extword。其中 id2word 是从新闻得到的,把词频小于 5 的词替换为了 UNK。对应到模型输入的 batch_inputs1。_id2extword 是从 word2vec.txt 中得到的,有 5976 个词。对应到模型输入的 batch_inputs2。后面会有两个 embedding 层,其中_id2word 对应的 embedding 是可学习的,_id2extword 对应的 embedding 是从文件中加载的,是固定的。创建 label 和 index 对应的字典。上面这些字典,都是基于 train_data 创建的。
5.3 模型
5.3.1 数据准备:
上上一步得到的 3 个数据,都是一个 list,list 里的每个元素是 dict,每个 dict 包括 label 和 text。这 3 个数据会经过 get_examples 函数。get_examples 函数里,会调用 sentence_split 函数,把每一篇文章分割成为句子。 然后,根据 vocab,把 word 转换为对应的索引,这里使用了 2 个字典,转换为 2 份索引,分别是:word_ids 和 extword_ids。最后返回的数据是一个 list,每个元素是一个 tuple:(label,句子数量,doc)。其中 doc 又是一个 list,每个元素是一个 tuple:(句子长度,word_ids,extword_ids),在迭代训练时,调用 data_iter 函数,生成每一批的 batch_data。在 data_iter 函数里,会调用 batch_slice 函数生成每一个 batch。拿到 batch_data 后,每个数据的格式仍然是上图中所示的格式,下面,调用 batch2tensor 函数,batch2tensor 函数最后返回的数据是:(batch_inputs1,batch_inputs2,batch_masks),batch_labels。形状都是(batch_size,doc_len,sent_len)。doc_len 表示每篇新闻有几句话,sent_len 表示每句话有多少个单词。
batch_masks 在有单词的位置,值为 1,其他地方为 0,用于后面计算 Attention,把那些没有单词的位置的 attention 改为 0。
batch_inputs1,batch_inputs2,batch_masks,形状是(batch_size,doc_len,sent_len),转换为(batch_size*doc_len,sent_len)。
5.3.2 模型网络:
Embedding 层:
batch_inputs1,batch_inputs2 都输入到 WordLSTMEncoder。WordLSTMEncoder 包括两个 embedding 层,分别对应 batch_inputs1,embedding 层是可学习的,得到 word_embed;batch_inputs2,读取的是外部训练好的词向量,因此是不可学习的,得到 extword_embed。所以会分别得到两个词向量,将 2 个词向量相加,得到最终的词向量 batch_embed,形状是(batch_size*doc_len,sent_len,100).
WordLSTMEncoder 层:
使用一个双层的 LSTM 对于单词进行编码,这里采取的是 BiLSTM+Attention 对句子进行编码。
SentEncoder 层
用 2 层的 BiLSTM 对 WordLSTMEncoder+Attention 层编码后的句子向量进行处理,隐藏层大小默认为 256,目的是最终得到一个新闻 doc 的向量表示,但是由于加了 attention 机制,所以这个网络的输出为 bilstm 的 output 而非最后的 hn,常规的做法是把最后一层的 bilstm 的 hn 进行拼接得到整个句子的向量表示,但是会造成新闻中部分信息的丢失,预测效果降低,所以这里为了后续的 attention 层处理方便,返回的是整个 output。
Attention 层:
Attention 的输入是 sent_hiddens 和 sent_masks,首先 sent_hiddens 经过线性变换得到 key 值,维度不变,所以 key 的 shape 是:(batch_size,doc_len,num_directions*hidden_size),然后 key 和 query 相乘得到最后 outputs,query 的维度在这里是 512,所以 outputs 的 shape 为(batch_size,doc_len)
FC 层:
最后经过 FC 层,得到分类概率的向量。
6 实验结果