当前位置: 代码迷 >> 综合 >> Albert+ TextCNN 文本二分类任务实践
  详细解决方案

Albert+ TextCNN 文本二分类任务实践

热度:99   发布时间:2024-03-07 17:43:13.0

文章目录

  • 任务
  • 数据
  • 流程
  • 模型
      • baseline
      • 自己写的
        • 输入输出
        • AlbertClassifierModel
  • 训练细节
      • 参数
      • 实验
  • 在test集输出
      • 规则
      • serch_f1
      • 线上

任务

参加了一个贝壳找房公司在DataFountain上举办的比赛

任务
本次赛题的任务是:给定app中的交流片段,片段包含一个客户问题以及随后的经纪人若干对话消息,从这些随后的经纪人消息中找出一个是对客户问题的。

这是一个二分类的问题,就是对许多的 (问题,答案)句子组合 进行 预测,如果问题和回答匹配了,就标注为1。

数据

训练集: 6000段对话, 每段对话是一句用户的问题对上多句客服的回答,这些回答里只有一部分是对客户的问题进行直接回答的,被标注为 1 , 其余的标注为0。

数据的格式可以被处理为: [query_id, reply_id,query,reply,label]

例如:

在这里插入图片描述

用户的问题是: 靠近沙川路嘛?

客服 回了三句话, 只有 "有一点靠近沙川路"这一条reply 的label是1(因为这句话直接回答了用户的问题), 其余的标注是0

流程

  • 数据预处理
    • preprocess.py
    • 找规则
  • 训练
    • train.py
  • 模型
    • model.py
    • 采用ALBERT+TextCNN
  • 在test集输出
    • test.py

模型

baseline

其实在比赛网站的排名榜上,得分第一名的人(是北航校友,已毕业)开源了他的代码

我下载下来看了一下,他是用jupyter notebook 写的,深度学习框架用的是tensorflow

在这里插入图片描述

他的模型结构是:

BERT + CNN

他的训练方法:

五折交叉验证,需要训练很久,所以我用它的notebook跑,没有跑出来结果。

自己写的

由于我只用过pytorch和python脚本训练模型,像tensorflow+notebook这样的训练方式我真的不习惯,所以就自己写了一个用pytorch和python脚本训练分类模型的代码。

  • 模型来源:

    • ALBERT:海量中文语料上预训练ALBERT模型:比Bert参数更少,效果更好。预训练小模型也能拿下13项NLP任务,ALBERT三大改造登顶GLUE基准,是适合咱们小模型使用的词向量。

    • ALBERT-base在sentence order prediction(SOP,即预测一个句子是不是另一个句子的下一个句子)上可以训练到99%的准确率,其实咱们比赛的这个问题也是一个SOP的问题:

    • 在这里插入图片描述

    • TextCNN:文本分类的关键在于准确提炼文档或者句子的中心思想,而提炼中心思想的方法是抽取文档或句子的关键词作为特征,基于这些特征去训练分类器并分类。因为CNN的卷积和池化过程就是一个抽取特征的过程,当我们可以准确抽取关键词的特征时,就能准确的提炼出文档或句子的中心思想。

    • 在这里插入图片描述

  • 自己的模型: ALBERT+TextCNN

输入输出

只看输入输出的话,模型整体是这样的:

在这里插入图片描述

输入是一条数据(query,reply),先经过Albert分词器把数据转化成albert要求的形式, 再输进我们自己设计的分类器AlbertClassifierModel,输出是对 输入做二分类,预测出的概率。

AlbertClassifierModel

具体的AlbertClassifierModel结构是这样的:

在这里插入图片描述

把 Albert作为词向量,把句子映射为词向量的列表,
然后用TextCNN提取特征,再加上Albert本身的特征向量CLS,
连接之后,就是一个[1,embedding_size + out_channels * len(kernel_size)]维度的向量
输入到 第一个全连接层fc_layer1, 并dropout一次,向量维度不变
再输入到 第二个全连接层fc_layer1,输出为1个值(二分类的概率)

训练细节

参数

参数名 默认值 作用和意义
max_epoch 5 训练多少个epoch,一个epoch会把训练集所有数据用一遍
batch_size 32 一次输入model里的数据有batch_size条
lr 1e-4 初始学习率
schedule_step 1 scheduler,在schedule_step的时候,把学习率乘0.1
weight_decay 1e-6 每个epoch学习率减少的大小,保证越靠后的训练lr越小,防止梯度消失
seed 666666 随机数种子,为了保证训练过程的可复现
dropout 0.3 模型对输入的一些维度进行随机遗忘的比例,为了防止过拟合
optim adam 优化器种类

实验

python train.py --batch_size 32 --lr 1e-4 --weight_decay 0

在这里插入图片描述

发现,已经快过拟合了。。

所以从这里面选一个ckpt就行,剩下的看运气了,如果和测试集的分布一样就分高。

在test集输出

规则

在prepocess.py的时候,我们就利用正则表达式查找了一些在query和reply里分布非常奇怪的句子

比如:

在训练集上找含有"NAME"的句子:

在这里插入图片描述

在测试上找"NAME":

在这里插入图片描述

用了规则兜底,如果不行,还可以人工check,所以命令就是2种选择了,加或者不加规则:

python test.py --ckpt xxx --thres xx  --use_rule
python test.py --ckpt xxx --thres xx

serch_f1

因为二分类概率p其实得最后转化为一个整数标记(0或者1)

所以就可以自己定义一个阈值thres,如果 p > thes ,就把句子标注为1,否则标注为0

由于训练的时候我们采用的是BCEWithLogitsLoss损失函数,它是不需要sigmoid的二分类损失函数,所以就直接把模型的输出和标注做loss计算就行,不需要在训练阶段就确定好阈值thres。

而在valid阶段,可以遍历一个范围,来找出一个最合适的thres,让f1得分最高。

线上

python test.py --ckpt model_epoch4_val0.978.pt --thres 0.15

一开始报错了:

在这里插入图片描述

发现是由于kwargs参数保留少了,之前很傻的只保存了’max_input_len’,还自以为是地把所有参数args输入到model里做初始化。

现在终于知道为什么,大家写model的__init__函数要把参数都列出来了,因为ckpt加载的时候,加载出kwargs要全部重新输入到model的__init__函数里面

现在把model的__init__函数改了,重新训练:

python train.py --batch_size 32 --lr 1e-4 --weight_decay 0 --max_epoch 10

选了一个在验证集上f1分数0.97的去test集上输出

最后提交得分是0.7,且加了规则之后得分更低了