基于Keras的15种模型:TextCNN, TextRNN, TextDPCNN, TextRCNN, TextHAN, TextBert等及其变种
支持5类特征及其组合:word-level, char-level, 结构化特征(TFIDF, LSA), Context特征(word-left, word-right, char-left, char-right), sentence-level
支持4种分类任务:单标签二分类,单标签多分类,多标签二分类,多标签多分类
任务描述:给定一个关于手机的用户提问,判断用户关注的是手机哪些Labels。
Labels: System, Function, Battery, Appearance, Network, Photo, Accessory, Purchase, Quality, Hardware, Contrast
已标注数据集共有30,000,以下为示例:
所以,任务类型:多标签二分类(Multi-label Binary Classification)任务,共有11个Labels,每个Label有2种取值(关注,不关注)。
虽然数据集是关于多标签二分类任务的,但本项目代码适用于4种分类任务中的任何1种,只取简单修改Config.py文件即可,基模型定义文件BasicModel.py会自动处理。
# Config.py
self.task = 'multilabel'
self.token_level = 'word' # word: word粒度 char: char粒度 both: word+char粒度
self.N_CLASSES = 11 # 标签/类别数量
# BasicModel.py
# 任务类型决定了类别数量、激活函数、损失函数和评估指标
if config.task == 'binary': # 单标签二分类
self.n_classes = 1
self.activation = 'sigmoid'
self.loss = 'binary_crossentropy'
self.metrics = ['accuracy']
elif config.task == 'categorical': # 单标签多分类
self.n_classes = config.N_CLASSES
self.activation = 'softmax'
self.loss = 'categorical_crossentropy'
self.metrics = ['accuracy']
elif config.task == 'multilabel': # 多标签二分类(多标签多分类需转化为多标签二分类)
self.n_classes = config.N_CLASSES
self.activation = 'sigmoid'
self.loss = 'binary_crossentropy'
self.metrics = ['accuracy']
-
a. 单标签二分类
输出为Dense(1, activation='sigmoid'),应用时1个概率值判断其与阈值大小
-
b. 单标签N分类
输出为Dense(N, activation='softmax'),应用时N个概率值取Top1
-
c. M标签二分类
- c.1 一个输出:输出为Dense(M, activation=‘sigmoid’),应用时M个概率值取TopK或与阈值判断大小
- c.2 一个输出:问题转化为M分类,类似于b,模型输出结构同b,应用时方法同c.1
-
d. M标签N分类
- d.1 一个输出:问题转化为MN标签二分类,同c.1
- d.2 一个输出:问题转化为MN分类,同c.2
- d.3 M个输出:每个输出都是b,模型输出结构、应用时方法都同b 待尝试
备注:本项目使用的处理方法是c.1
Python 3.6.5
Keras 2.2.4
Numpy 1.16.3
Pandas 0.23.0
SciPy 1.1.0
Sklearn 0.21.3
数据预处理环节流程步骤如下图所示:
内容:简单而通用的功能,如标注数据处理,分词,分字,分句子,过滤停用词,处理原始Labels
文件:Embedding.py
内容:自己训练Word Embedding,读取公开训练的Word Embedding,支持word+char两种粒度
内容:
生成词汇表,支持低频高频词过滤
基于Embedding生成<word, idx, vector>三者之间的映射字典
生成Embedding Layer初始化权重
基于映射字典的向量化编码工具(支持截断、补零、including和excluding)
以上功能支持word+char两种粒度
内容:生成TFIDF特征和LSA特征,支持word+char两种粒度,后续会增加支持LSI, LDA等其他特征
内容:基于卡方统计值等过滤词和字,项目暂未使用
文件 :ModelTrain.py
内容:使用向量化编码工具和MultiLabelBinarizer对特征和Label进行编码
内容:通过Shuffle和Random Drop进行数据增强,项目暂未使用
使用了多个Model,各Model结构关系如下图所示:
-
BasicModel: 所有模型基类
实现3种Metrics
-
BasicDeepModel: 深度学习模型基类
通用Layers创建
绘制Loss和Metrics
Embedding冻结和解冻
模型训练和评估(支持CV)
学习率Schedular
-
BasicStatModel: 传统模型基类
暂未实现
-
TextCNN:标配和基础
-
TextRNN:同上,可玩的地方更多
-
TextRCNN:结合CNN和RNN的优点
-
TextDPCNN:受ResNet启发,结合RNN+CNN
-
TextHAN:使用了层次注意力机制
-
TextBert:在TextGRU基础上把输入改为Bert编码的向量
-
此外,还有5大类待实现模型(灰色)
-
支持所有分类任务:二分类,多分类,多标签二分类,多标签多分类
-
支持各种输入组合:
[word, char, word-structure, char-structure]中任意的4选1,4选2,4选3,4选4
另外对于一些特殊模型,支持特殊输入,如TextRCNN模型的Context特征(word-left, word-right, char-left, char-right),以及TextHAN模型的Sentence-level特征
-
模型训练评估支持KFold,支持6种Finetuning方式
-
绝大多数模型支持Attention,绝大多数模型支持丰富的参数配置
Step1: 运行DataPreprocessing.py,基于已标注数据生成训练数据并保存本地
Step2: 运行Embedding.py,自己训练Embedding,读取公开训练的Embedding,支持char+word两种粒度
Step3: 运行Vocabulary.py,生成词汇表,基于Embedding生成映射字典,生成Embedding Layer初始化权重矩阵等,支持char+word两种粒度
Step4: 运行FeatureStructured.py,生成TFIDF特征和LSA特征,支持word+char两种粒度
Step5: 运行ModelTrain.py,项目全流程,包括:数据准备、Token筛选、特征和Label编码、划分Train/Test、环境配置、模型生成、模型训练和评估、模型持久化,详见脚本注释。
运行脚本:python3 ModelTrain.py
在运行脚本之前,先修改脚本里的配置项,内容如下:
# 根据实际情况修改,也可直接在Config.py里修改,推荐前者
config.n_gpus = 1
config.token_level = 'word' # 只使用word-level特征,不使用char-level
config.structured = 'none' # 不使用结构化特征
config.bert_flag = False # 不使用Bert编码的输入向量
15个模型的评估结果如下表所示:
备注:模型并未进行精细化调参,大多是默认配置和参数,效果仅供参考。
从评估结果中可得出以下结论:
-
word+char相比word,效果明显有提升
-
word+char+structured相比word+char,效果提升不明显,一些情况下反而会下降
-
TextCNN训练最快,Precision和F1值相对也较高,可作为一个强有力的Baseline
-
TextRNN训练很慢,效果不是特别好,可能是因为训练数据很多是短文本
-
各模型之间效果差不多(全是默认参数,没时间做精细化调参)
-
输入改为Bert编码向量后效果提升比较明显,简单的模型(TextGRU)就得到了最好的F1值,后续值得好好研究
-
TextHAN比较给力,取到了最高的Precision,后续值得好好研究
-
一个脚本只干一件事情,一件事情只在一个脚本里干,各脚本解耦,各功能独立,互相之间只通过持久化和Config共享信息
-
充分利用类和继承以及闭包,相同功能不要重复定义,也不要到处粘贴复制,相似的功能通过闭包来实现
-
Vocabulary及相关映射字典、Embedding权重,封装整合为一个class,统一管理
-
调试便捷化+逻辑清晰化
a. 训练和应用数据封装进字典,单输入和多输入使用无差别,字典key对应模型搭建时Input的参数name
b. 动态搭建模型,使其无缝支持多种输入及其组合
方法:通用方法位于父类BasicDeepModel,各子类模型分为模型主体和模型结尾2部分,模型核心的纯粹的结构位于模型主体,根据输入不同,进行配置和组装,然后接入模型结尾
c. 不同类模型,先选择最简单的模型如TextCNN,深入研究经验和Tricks,然后复制到别的模型
d. 同一类模型,先搭建并跑通最简单的模型,随后基于评估效果,逐渐加深加宽
-
模型组件
a. CNN+RNN是标配,CNN提取关键词,RNN适合前几层,提取依赖信息,Attention和MaxPooling可突出关键特征
b. Capsule可代替CNN,有时效果好于CNN
c. 有条件就使用Bert
-
文本分类 - Keras
https://github.com/nlpjoe/daguan-classify-2018
https://github.com/yongzhuo/Keras-TextClassification
https://github.com/ShawnyXiao/TextClassification-Keras
-
多标签分类 - PyTorch
https://github.com/chenyuntc/PyTorchText (2017知乎看山杯 多标签文本分类大赛 Rank1)
-
kashgari : NLP框架,超级傻瓜,超级Cutting Edge
-
hyperas : Keras超参数优化工具
-
sk-multilearn : Sklearn生态下的多标签分类工具