知乎“看山杯”夺冠记

本文原做者陈云,原载于知乎专栏。雷锋网已得到做者受权。python

七月,酷暑难耐,认识的几位同窗参加知乎看山杯,均取得不错的排名。当时天池 AI 医疗大赛初赛结束,官方正在为复赛进行平台调试,复赛时间一拖再拖。看着几位同窗在比赛中排名都还很不错,因而决定抽空试一试。结果一发不可收拾,又找了两个同窗一块儿组队(队伍 init)以致于整个暑假都投入到这个比赛之中,并最终以必定的优点夺得第一名(参见最终排名 )linux

比赛介绍git

这是一个文本多分类的问题:目标是 “参赛者根据知乎给出的问题及话题标签的绑定关系的训练数据,训练出对未标注数据自动标注的模型”。通俗点讲就是:当用户在知乎上提问题时,程序要可以根据问题的内容自动为其添加话题标签。一个问题可能对应着多个话题标签,以下图所示。github

这是一个文本多分类,多 label 的分类问题(一个样本可能属于多个类别)。总共有 300 万条问题 - 话题对,超过 2 亿词,4 亿字,共 1999 个类别。架构

比赛源码(PyTorch 实现)GitHub 地址 https://github.com/chenyuntc/PyTorchText机器学习

比赛官网: https://biendata.com/competition/zhihu/函数

比赛结果官方通告: https://zhuanlan.zhihu.com/p/28912353工具

1. 数据介绍学习

参考 https://biendata.com/competition/zhihu/data/测试

总的来讲就是:

  • 数据通过脱敏处理,看到的不是 “如何评价 2017 知乎看山杯机器学习比赛”,而是 “w2w34w234w54w909w2343w1"这种通过映射的词的形式,或者是”c13c44c4c5642c782c934c02c2309c42c13c234c97c8425c98c4c340" 这种通过映射的字的形式。

  • 由于词和字通过脱敏处理,因此没法使用第三方的词向量,官方特意提供了预训练好的词向量,即 char_embedding.txt 和 word_embedding.txt ,都是 256 维。

  • 主办方提供了 1999 个类别的描述和类别之间的父子关系(好比机器学习的父话题是人工智能,统计学和计算机科学),但这个知识没有用上。

  • 训练集包含 300 万条问题的标题(title),问题的描述(deion)和问题的话题(topic)

  • 测试集包含 21 万条问题的标题(title), 问题的描述 (deion),须要给出最有可能的 5 个话题(topic)

2. 数据处理

数据处理主要包括两部分:

  • char_embedding.txt 和 word_embedding.txt 转为 numpy 格式,这个很简单,直接使用 word2vec 的 python 工具便可

  • 对于不一样长度的问题文本,pad 和截断成同样长度的(利用 pad_sequence 函数,也能够本身写代码 pad)。过短的就补空格,太长的就截断。操做图示以下:

3. 数据加强

文本中数据加强不太常见,这里咱们使用了 shuffle 和 drop 两种数据加强,前者打乱词顺序,后者随机的删除掉某些词。效果举例如图:

4. 评价指标

每一个预测样本,提供最有可能的五个话题标签,计算加权后的准确率和召回率,再计算 F1 值。注意准确率是加权累加的,意味着越靠前的正确预测对分数贡献越大,同时也意味着准确率可能高于 1,可是 F1 值计算的时候分子没有乘以 2,因此 0.5 是很难达到的。

具体评价指标说明请参照

https://biendata.com/competition/zhihu/evaluation/

模型介绍

建议你们先阅读这篇文章,了解文本多分类问题几个经常使用模型:用深度学习(CNN RNN Attention)解决大规模文本分类问题 ( https://zhuanlan.zhihu.com/p/25928551)

1. 通用模型结构

文本分类的模型不少,此次比赛中用到的模型基本上都遵循如下的架构:

基本思路就是,词(或者字)通过 embedding 层以后,利用 CNN/RNN 等结构,提取局部信息、全局信息或上下文信息,利用分类器进行分类,分类器的是由两层全链接层组成的。

在开始介绍每一个模型以前,这里先下个结论:

当模型复杂到必定程度的时候,不一样模型的分数差距很小!

2. TextCNN

这是最经典的文本分类模型,这里就不细说了,模型架构以下图:

和原始的论文的区别就在于:

  • 使用两层卷积

  • 使用更多的卷积核,更多尺度的卷积核

  • 使用了 BatchNorm

  • 分类的时候使用了两层的全链接

总之就是更深,更复杂。不过卷积核的尺寸设计的不够合理,致使感觉野差距过大。

3. TextRNN

没找到论文,我就凭感受实现了一下:

相比于其余人的作法,这里的不一样点在于:

  • 使用了两层的双向 LSTM。

  • 分类的时候不是只使用最后一个隐藏元的输出,而是把全部隐藏元的输出作 K-MaxPooling 再分类。

4. TextRCNN

参考原论文的实现,和 RNN 相似,也是两层双向 LSTM,可是须要和 Embedding 层的输出 Concat(相似于 resnet 的 shortcut 直连)。

5. TextInception

这个是我本身提出来的,参照 TextCNN 的思想(多尺度卷积核),模仿 Inception 的结构设计出来的,一层的 Inception 结构以下图所示,比赛中用了两层的 Inception 结构,最深有 4 层卷积,比 TextCNN 更深。

6. 各个模型分数计算

训练的时候,每一个模型要么只训练基于词(word)的模型,要么只训练基于字(char)的模型。各个模型的分数都差很少,这里再也不单独列出来了,只区分训练的模型的类型和数据加强与否。

能够看出来

  • 基于词的模型效果远远好于基于字的(说明中文分词颇有必要)。

  • 数据加强对基于词(word)的模型有必定的提高,可是对于基于字(char)的模型主要是起到反作用。

  • 各个模型之间的分数差距不大。

7. 模型融合

像这种模型比较简单,数据量相对比较小的比赛,模型融合是比赛获胜的关键。

在这里,我只使用到了最简单的模型融合方法 ----- 几率等权重融合。对于每一个样本,单模型会给出一个 1999 维的向量,表明着这个模型属于 1999 个话题的几率。融合的方式就是把每个模型输出的向量直接相加,而后选择几率最大的 5 个话题提交。结构如图所示:

下面咱们再来看看两个模型融合的分数:

第一列的对比模型采用的是 RNN(不采用数据加强,使用 word 做为训练数据),第二列是四个不一样的模型(不一样的结构,或者是不一样的数据)。

咱们能够得出如下几个结论:

  • 从第一行和第二行的对比之中咱们能够看出,模型差别越大提高越多(RNN 和 RCNN 比较类似,由于他们底层都采用了双向 LSTM 提取特征),虽然 RCNN 的分数比 Inception 要高,Inception 对模型融合的提高更大。

  • 从第一行和第四行的对比之中咱们能够看出,数据的差别越大,融合的提高越多,虽然基于字(char)训练的模型分数比较低,可是和基于词训练的模型进行融合,仍是能有极大的提高。

  • 采用数据加强,有助于提高数据的差别性,对模型融合的提高帮助也很大。

总结: 差别性越大,模型融合效果越好。没有差别性,创造条件也要制造差别性。

8. MultiModel

其实模型融合的方式,咱们换一种角度考虑,其实就是一个很大的模型,每个分支就像多通道的 TextCNN 同样。那么咱们能不能训练一个超级大的模型?答案是能够的,可是效果每每不好。由于模型过于复杂,太难以训练。这里我尝试了两种改进的方法。

第一种方法,利用预训练好的单模型初始化复杂模型的某一部分参数,模型架构如图所示:

可是这种作法会带来一个问题: 模型过拟合很严重,难以学习到新的东西。由于单模型在训练集上的分数都接近 0.5,已经逼近理论上的极限分数,这时候很难接着学习到新的内容。这里采起的应对策略是采用较高的初始学习率,强行把模型从过拟合点拉出来,使得模型在训练集上的分数迅速下降到 0.4 左右,而后再下降学习率,缓慢学习,提高模型的分数。

第二种作法是修改预训练模型的 embedding 矩阵为官方给的 embedding 权重。这样共享 embedding 的作法,可以必定程度上抑制模型过拟合,减小参数量。虽然 CNN/RNN 等模型的参数过拟合,可是因为相对应的 embedding 没有过拟合,因此模型一开始分数就会降低许多,而后再缓慢提高。这种作法更优。在最后提交模型复现成绩的时候,我只提交了七个这种模型,里面包含着不一样子模型的组合,通常包含 3-4 个子模型。这种方式生成的权重文件也比较小(600M-700M 左右),上传到网盘相对来讲更方便。

9. 失败的模型和方法

MultiMode 只是我诸多尝试的方法中比较成功的一个,其它方法大多以失败了结(或者效果不明显)

  • 数据多折训练:由于过拟合严重,想着先拿一半数据训,容许它充分过拟合,而后再拿另一半数据训。效果不如以前的模型。

  • Attention Stack,参考了这篇文章,其实本质上至关于调权重,可是效果有限,还麻烦,因此最后直接用等权重融合(权重全设为 1)。

  • Stack,太费时费力,浪费了很多时间,也有多是实现有误,提高有限,没有继续研究下去。

  • Boost,和第二名 Koala 的方法很像,先训一个模型,而后再训第二个模型和第一个模型的输出相加,可是固定第一个模型的参数。至关于不停的修正上一个模型误判的 (能够尝试计算一下梯度,你会发现第一个模型已经判对的样本,即便第二个模型判别错了,第二个模型的梯度也不会很大,即第二个模型不会花费太多时间学习这个样本)。可是效果很差,缘由:过拟合很严重,第一个模型在训练集上的分数直接就逼近 0.5,致使第二个模型什么都没学到。Koala 队伍最终就是凭借着这个 Boost 模型拿到了第二名,我过早放弃,没能在这个方法上有所突破十分遗憾。

  • TTA(测试时数据加强),至关于在测试的时候人为的制造差别性,对单模型的效果通常,对融合几乎没有帮助。

  • Hyperopt 进行超参数查询,主要用来查询模型融合的权重,效果通常,最后就也没有使用了,就手动稍微调了一下。

  • label 设权重,对于正样本给予更高的权重,训练模型,而后和正常权重的模型进行融合,在单模型上可以提高 2-3 个千分点(十分巨大),可是在最后的模型融合是效果颇有限(0.0002),并且须要调整权重比较麻烦,遂舍弃。

结束语

我以前虽然学过 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!

相关文章
相关标签/搜索