这是一个文本多分类的问题:目标是 “参赛者根据知乎给出的问题及话题标签的绑定关系的训练数据,训练出对未标注数据自动标注的模型”。通俗点讲就是:当用户在知乎上提问题时,程序要能够根据问题的内容自动为其添加话题标签。一个问题可能对应着多个话题标签,如下图所示。
这是一个文本多分类,多 label 的分类问题(一个样本可能属于多个类别)。总共有 300 万条问题 - 话题对,超过 2 亿词,4 亿字,共 1999 个类别。
比赛源码(PyTorch 实现)GitHub 地址 https://github.com/chenyuntc/PyTorchText 比赛官网: https://biendata.com/competition/zhihu/ 比赛结果官方通告: https://zhuanlan.zhihu.com/p/28912353
参考 https://biendata.com/competition/zhihu/data/
总的来说就是:
数据处理主要包括两部分:
文本中数据增强不太常见,这里我们使用了 shuffle 和 drop 两种数据增强,前者打乱词顺序,后者随机的删除掉某些词。效果举例如图:
每个预测样本,提供最有可能的五个话题标签,计算加权后的准确率和召回率,再计算 F1 值。注意准确率是加权累加的,意味着越靠前的正确预测对分数贡献越大,同时也意味着准确率可能高于 1,但是 F1 值计算的时候分子没有乘以 2,所以 0.5 是很难达到的。
具体评价指标说明请参照
https://biendata.com/competition/zhihu/evaluation/
建议大家先阅读这篇文章,了解文本多分类问题几个常用模型:用深度学习(CNN RNN Attention)解决大规模文本分类问题 ( https://zhuanlan.zhihu.com/p/25928551 )
文本分类的模型很多,这次比赛中用到的模型基本上都遵循以下的架构:
基本思路就是,词(或者字)经过 embedding 层之后,利用 CNN/RNN 等结构,提取局部信息、全局信息或上下文信息,利用分类器进行分类,分类器的是由两层全连接层组成的。
在开始介绍每个模型之前,这里先下个结论:
当模型复杂到一定程度的时候,不同模型的分数差距很小!
这是最经典的文本分类模型,这里就不细说了,模型架构如下图:
和原始的论文的区别就在于:
总之就是更深,更复杂。不过卷积核的尺寸设计的不够合理,导致感受野差距过大。
没找到论文,我就凭感觉实现了一下:
相比于其他人的做法,这里的不同点在于:
参考原论文的实现,和 RNN 类似,也是两层双向 LSTM,但是需要和 Embedding 层的输出 Concat(类似于 resnet 的 shortcut 直连)。
这个是我自己提出来的,参照 TextCNN 的思想(多尺度卷积核),模仿 Inception 的结构设计出来的,一层的 Inception 结构如下图所示,比赛中用了两层的 Inception 结构,最深有 4 层卷积,比 TextCNN 更深。
训练的时候,每个模型要么只训练基于词(word)的模型,要么只训练基于字(char)的模型。各个模型的分数都差不多,这里不再单独列出来了,只区分训练的模型的类型和数据增强与否。
可以看出来
像这种模型比较简单,数据量相对比较小的比赛,模型融合是比赛获胜的关键。
在这里,我只使用到了最简单的模型融合方法 ----- 概率等权重融合。对于每个样本,单模型会给出一个 1999 维的向量,代表着这个模型属于 1999 个话题的概率。融合的方式就是把每一个模型输出的向量直接相加,然后选择概率最大的 5 个话题提交。结构如图所示:
下面我们再来看看两个模型融合的分数:
第一列的对比模型采用的是 RNN(不采用数据增强,使用 word 作为训练数据),第二列是四个不同的模型(不同的结构,或者是不同的数据)。
我们可以得出以下几个结论:
总结: 差异性越大,模型融合效果越好。没有差异性,创造条件也要制造差异性。
其实模型融合的方式,我们换一种角度考虑,其实就是一个很大的模型,每一个分支就像多通道的 TextCNN 一样。那么我们能不能训练一个超级大的模型?答案是可以的,但是效果往往很差。因为模型过于复杂,太难以训练。这里我尝试了两种改进的方法。
第一种方法,利用预训练好的单模型初始化复杂模型的某一部分参数,模型架构如图所示:
但是这种做法会带来一个问题: 模型过拟合很严重,难以学习到新的东西。因为单模型在训练集上的分数都接近 0.5,已经逼近理论上的极限分数,这时候很难接着学习到新的内容。这里采取的应对策略是采用较高的初始学习率,强行把模型从过拟合点拉出来,使得模型在训练集上的分数迅速降低到 0.4 左右,然后再降低学习率,缓慢学习,提升模型的分数。
第二种做法是修改预训练模型的 embedding 矩阵为官方给的 embedding 权重。这样共享 embedding 的做法,能够一定程度上抑制模型过拟合,减少参数量。虽然 CNN/RNN 等模型的参数过拟合,但是由于相对应的 embedding 没有过拟合,所以模型一开始分数就会下降许多,然后再缓慢提升。这种做法更优。在最后提交模型复现成绩的时候,我只提交了七个这种模型,里面包含着不同子模型的组合,一般包含 3-4 个子模型。这种方式生成的权重文件也比较小(600M-700M 左右),上传到网盘相对来说更方便。
MultiMode 只是我诸多尝试的方法中比较成功的一个,其它方法大多以失败告终(或者效果不明显)
我之前虽然学过 CS224D 的课程,也做了前两次的作业,但是除此之外几乎从来没写过自然语言处理相关的代码,能拿第一离不开队友的支持,和同学们不断的激励。
这次比赛入门对我帮助最大的两篇文章是用深度学习(CNN RNN Attention)解决大规模文本分类问题 ( https://zhuanlan.zhihu.com/p/25928551) 和 deep-learning-nlp-best-practices ( http://t.cn/RpvjG9K)
第一篇是北邮某学长(但我并不认识~)写的,介绍了许多文本分类的模型(CNN/RNN/RCNN),对我入门帮助很大。
第二篇是国外某博士写的,当时我已经把分数刷到前三,在家看到了这篇文章,叹为观止,解释了我很多的疑惑,提到的很多经验总结和我的情况也确实相符。
P.S. 为什么队伍名叫 init? 因为 git init,linux init,python __init__ 。我最喜欢的三个工具。而且 pidof init is 1.
P.S. 欢迎报考北邮模式识别实验室( http://t.cn/RpvjaKo)
最后的最后:人生苦短,快用 PyTorch!