CCF BDCI新闻情感分类初赛A榜4/2735,复赛1%题解报告
写在前面
本文将带来CCF BDCI新闻情感分类的题解报告,该方案在初赛A榜获得了4/2735,复赛成绩1%。希望可以给大家提供一些思路,互相交流学习 。
比赛代码已经开源在
https://github.com/linhaow/TextClassify
赛题说明
比赛的链接在这里:
https://www.datafountain.cn/competitions/350

比赛的内容是互联网的新闻情感分析。给定新闻标题和新闻的内容,然后需要我们设计一个方案对新闻的情感进行分类,判断新闻是消极的,积极的还是中立的。
训练数据集的字段如下:
id:新闻的唯一标识。
title:新闻的题目。
content:新闻的内容。
label:情感分类的标签。
数据分析
一个好的数据分析可以给比赛带来很大的提升。所以数据分析的过程不能忽视。对训练数据集分析后,可以发现训练集有如下一些特征。
(1)训练数据集中 0标签
的数据量比较少,只有几百。
(2)训练集中 1和2的标签
比较平衡,都是几千,相差不大。
(3)此外新闻的 文章内容
很长
,很多有上千个字。
(4)相比于新闻内容, 新闻标题较短
,很少有上百字的标题。
针对上述特征,可以看出标签中存在一定的数据不均衡,此外如何处理过长的文章内容也是一个核心任务。对于常用的bert模型,只能接收512个token,所以需要一个能处理过千字的文章内容的方法。
baseline
我刚参加比赛的时候初赛已经过去了一段时间了,当时有一个郭大开源的baseline,我的baseline是基于这个开源baseline修改的。baseline的结构图如下:

整个模型由两个部分组成。
(1)第一部分是最下方的split_num个bert模型(这里可以使用bert全家桶中的任意一个),现在基本文本分类比赛的前排都是bert模型。毕竟bert模型在预训练的时候就加入了很多比赛外的数据。所以相对来说效果也不会太差。
为了同时利用标题和文章内容信息,
在bert模型的
输入端。
我选用了
两个sentence加[SEP]做为模型的输入,其中sentence1是该新闻的标题,而sentence2则是该新闻的内容的一部分。为了让新闻内容可以覆盖到整篇文章,我首先将文章分成split_num段,然后在每一段选择maxlen的长度,分别做为split_num个bert模型的sentence2的输入。
举个例子,如果下面的长方形代表的是一个文章的内容,而split_num是3,则三颗五角星的地方是三个bert的sentence2的输入。sentence1均是文章标题。

(2)第二部分是上方的biGRU模块。该模块将bert对文章理解的不同部分串起来,最后给出综合考虑的分类输出。在这种RNN结构中, 双向
的效果往往比单向更好,所以使用了双向的GRU。
上述结构有如下的优点:
(1)减少了显存的使用,经过split后你可以在同样显存下处理更长的长度。
(2)另一个就是解决了长度上千的句子塞进bert的问题。上文bert模型处我使用的是中文的roberta-large模型。
提升模型
使用完上述baseline,并且调参后成绩就可以达到100名左右了。接下来就是如何提升模型的效果了。
(1)首先我发现郭大最初的代码好像有bug,当gru的层数大于1的时候维度不对。于是我查看源代码后把它fix了,然后在gru中加了几层layers,调整参数后,名次就到了前50了,大概线上是81.6左右。
具体的代码在pytorch_transformers文件夹中的modeling_bert.py第976行,修改后的代码如下:
self.gru.append(nn.GRU(config.hidden_sizeif i==0else config.lstm_hidden_size*2, config.lstm_hidden_size,num_layers=1,bidirectional=True,batch_first=True).cuda() )
这里增加layers的层数是为了让BiGRU更好地学习一篇长文章的不同部分。一层layer明显是 欠拟合
的,实际上实验结果也证明这里layers增大后成绩上去了不少。
(2)我尝试去清洗数据,除去一些url,img之类的数据,发现成绩不但没有提升,还有下降(其实这一点早就有心理准备了)。
因为在bert的使用中去除停用词等经常不能带来性能的提升。毕竟bert在预训练时使用的就是完成的带有噪声的文本信息,所以去停用词,清洗数据等不一定能带来效果的提升。
(3)我尝试了不同的split_num和maxlen,发现在roberta模型中,split_num越大的时候效果越好(我在7,8左右时效果最好),这是因为更大的split_num可以让模型更好地获取长句子的信息,更多的split_num可以让模型 捕捉的
密度
更高
,从而对长篇章的把握更加准群。给一个极端的例子,如果你的 split_num
比较小,maxlen也比较小,刚好把文章先抑后扬的抑的部分全捕捉了,那准确性肯定会下降。
与此同时也需要增加
weigt_dec
ay去防
止过拟合。通过这个方法线上就可以到81.7了

(4)使用多折交叉验证,多折交叉验证的效果会明显好于单折。 多折交叉和融合往往都能带来一定的稳定性和性能的提升。不过多折也
会增加 训练的时长
,所以往往在先调参确定好模型后再进行多折交叉。通过这个方法,线上就可以到达81.8了,
当时已经在a榜前15了。
(5)使用roberta-wwm-ext模型,
roberta-wwm-ext是一个基于全词遮罩(Whole Word Masking)技术的预训练模型。
简单来说,原有基于WordPiece的分词方式会把一个完整的词切分成若干个子词,在生成训练样本时,这些被分开的子词会随机被mask。
在全词Mask中,如果一个完整的词的部分WordPiece子词被mask,则同属该词的其他部分也会被mask,即全词Mask。
这个模型在复赛的a榜上效果比较好。这个应该是初赛和复赛训练数据集分布情况不同导致的。不过多样化的模型对于模型融合是很有帮助的。repo的链接如下:
https://github.com/ymcui/Chinese-BERT-wwm
(6)All data训练,在之前的训练中会使用一部分作为验证集,但是当你确定了所有超参数后你就可以尝试把所以数据拿过来盲跑,这样训练数据会多一点,不过缺点是拿不到本地的开发集测试结果。
模型融合
在得到两个模型的多个不同split_num,maxlen值后,就可以对模型的结果进行融合了,在融合中我使用的是分类问题中常用 投票法
。
总的来说就是用不同的模型输出结果进行投票,哪个结果的票多就选择哪个结果,如果票数一样,有0 label就选0,没有的话就选单模最好的模型。这里选0是因为之前的0样本在训练的时候比较少。
对于候选融合结果的选择我们采用的方法如下:

首先我们计算不同输出文件之间的相关性,然后选择相关性最小的几个输出结果,在相关性和得分上需要对不同的输出结果进行一个trade off。即最好的情况就是几个得分较高的模型相关性比较低。相关系数可以使用pandas中的 corr()计算。
接着对选出的几个模型进行融合即可得到最后的输出,投票融合结果的代码框架如下:
import pandas as pd import numpy as np submits =['0.818.csv','0.816.csv'] #需要融合的文件放submits中 files = [] data = [] for f in submits: if 'csv' in f: files.append(f) data.append(pd.read_csv(f).values) print(len(files))
weight = [2,1] #融合的权重 result = np.zeros([len(data[0]), 3])
for i in range(len(data)): for j in range(len(data[0])): if data[i][j][1] == 0: result[j][0] += weight[i] elif data[i][j][1] == 1: result[j][1] += weight[i] elif data[i][j][1] == 2: result[j][2] += weight[i]
np.argmax(result, axis = 1) submit = pd.read_csv('submit_example.csv') submit['label'] = np.argmax(result, axis = 1) submit.to_csv('result.csv',index=None)
总结
其实比赛中还有很多方法可以尝试,不过中间有点事耽误了很多时间所以就尝试了两个模型,有点可惜,文章中有很多地方都还是可以调整的,比如融合方法等,大家有空可以继续探究。
本文转载在公众号:
纸鱼AI,作者:
linhw
推荐阅读
Transformer详解《attention is all your need》论文笔记
关于AINLP
AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP君微信(id:AINLP2),备注工作/研究方向+加群目的。