因为想要学习一下深度学习+NLP,所以最近就抽空参加了一下“细粒度用户评论情感分析”这个比赛。但是平时还要工作、带娃,没有太多精力打比赛,最终只有两周多的时间参加了A榜的比赛,成绩也马马虎虎,只能排到A榜top13%左右。
比赛链接为:https://challenger.ai/competition/fsauor2018。
该比赛所处理的"细粒度用户评论情感分析“属于多标签-多分类问题。
针对“多标签-多分类”问题,分别尝试了“单模型”和“多模型”两种不同类型的架构。由于没有太多的时间尝试,只采用了textcnn和单/双层的rnn作为基础网络。
所谓单模型,即单输入-多输出模型, 也可称之为多任务模型, 只接收一个输入,每个标签都对应一个输出。在这种架构下,由于不同的输出之间需要共享低层的网络层,因此不同的共享方式,会对模型的效率和分数产生不同的影响。
在本次比赛中,我尝试了两种不同的共享策略:1. 所有的输出共享相同的低层网络层,2. 根据每个细粒度所属于的大类分组,同一组的输出共享相同的低层网络层, 不同组的输出之间不共享低层网络层。试验对比下来,在训练和预测效率上,第一种策略优于第二种,但是在模型分数上,第二种策略往往要好于第一种。具体实现可以参考 models/own.py
所谓多模型,即单输入-单输出模型, 为每一个粒度各构建一个模型。为了方便,每个粒度都使用相同的模型架构,但分开训练。相较于单模型架构,这种方式建模相对简单,不涉及低层网络层的共享问题,调参难度相对小一些。在执行效率上,当模型比较简单时,单模型的效率要远好于多模型,但当模型比较复杂,参数量较多时, 单模型和多模型在执行效率上差距缩小的比较大。在模型分数上,当用相同的基础模型时, 多模型上的分数基本上都好于单模型,猜测可能的原因是没有对单模型的参数进行细致的调整,而且每个粒度可能需要不同的迭代次数才能达到最优的分数。多模型的实现可以参考 models/textrnn.py
本次比赛使用的深度学习框架是keras,后端tensorflow,python环境3.5, 下面是代码的组织结构(本来想好好整理的,但是有些懒散,望见谅):
ai_challenger_sentiment/
|-- README.md
|-- __pycache__
| `-- env.cpython-35.pyc
|-- data # 用于保存各种数据处理结果
| |-- caches
| |-- dataset
| |-- features
| |-- interresults
| |-- results
| |-- saved_models
| `-- stopwords
|-- env.py # 项目相关的路径信息
|-- models # 模型文件
| |-- __init__.py
| |-- __pycache__
| |-- base.py # 模型基类, 定义了最基本的接口
| |-- fasttext.py # FastText模型
| |-- linxi.py # 前期尝试,并未继承基类
| |-- own.py # 单模型框架类, 利用粗粒度对细粒度进行了分组,继承该类,重写相应接口,可以 1)实现不同的网络层共享方式,2)采用不同的基础网络架构,比如textcnn,textrnn等, 比赛时为了方便,虽然写了这个框架,但是直接在框架里改了,没派生子类。。。 但是通过继承的方式更有利于后期维护和复用。
| |-- sentiment_base.py # (单/多)模型基类,继承自base.py,封装实现了单模型和多模型框架的训练和预测逻辑等,派生的子类可以直接复用相应方法。
| |-- textcnnpy # TextCNN模型
| `-- textrnn.py # TextRNN模型, 继承BaseRNN模型,改写_rnn接口可以实现不同的rnn结构。
|-- notebooks
| |-- eda
| |-- test
| |-- train
| `-- transform
|-- requirements.txt
|-- scripts # 数据处理相关的脚本
| |-- segment_chars.py # 字符级,由于时间原因,最终并没有尝试字符级的特征。
| `-- segment_words.py # 词语级, 去除特殊字符,繁简体转换,停用词,分词等等。
`-- utils
|-- __init__.py
|-- __pycache__
|-- augment.py # 数据增强,时间原因,比赛中并未应用。
|-- callbacks.py # 自定义的keras callback,其中CyclicLR, 引自他处,可用于实现周期性学习率;CustomModelCheckPoint, 自己编写的callback,根据macro-f1来保存最优的模型。
|-- dataset.py # 数据集类,用来管理数据集
|-- metrics.py # 自己实现的batch-wise的micro,macro f1
`-- transform.py # 数据处理用到的api
- 合适的数据预处理对模型提升有很大的增益,比如句子最大长度、停用词等参数的设置等。
- 词向量使用方式(静态,动态,随机初始化等)会对结果有一定影响。
- 词向量的维数不能太小,适当取大一些会有比较好的效果。
- 当模型效果难以提升,或者是收敛缓慢,可以尝试使用更深更宽的模型。
- 当出现过拟合时,更深更宽的模型+dropout等,比浅层模型或小学习率更好。
- 模型深度两层基本就可以了,更深也不一定带来更好的效果。
- 随着模型复杂度的增加,模型之间效果的差异不在明显。
- 集成大法往往可以带来不错的提升。(比赛中,我只用4个模型的输出做了简单的平均)
如有新体会,后续再补充。