AI 夏令营第三期 - 基于论文摘要的文本分类与关键词抽取挑战赛学习笔记2

背景

       Datawhale联合科大讯飞、阿里云天池平台开设了机器学习、深度学习、AI for Science 三个方向的夏令营学习。其中,深度学习实践-NLP方向以讯飞平台“基于论文摘要的文本分类与关键词抽取挑战赛” (2023 iFLYTEK A.I.开发者大赛-讯飞开放平台)为学习命题,并提供了一些解题代码供夏令营的初学者、学习者学习研究。本笔记在此学习过程中产出。

       赛题背景:医学领域的文献库中蕴含了丰富的疾病诊断和治疗信息,如何高效地从海量文献中提取关键信息,进行疾病诊断和治疗推荐,对于临床医生和研究人员具有重要意义。赛题目的是根据包含标题、作者、摘要、关键词的数据集变量构建模型判断文献是否为医学领域文献,为二分类问题。该笔记为学习笔记2。

 

学习任务

任务二:

1. 阅读深度学习Baseline章节

        a. 跑通深度学习Baseline,获得自己的成绩

2. 提交任务二打卡,查看个人成绩排行榜

 

解题思路

本次学习的Baseline代码是基于bert模型的。关于bert模型的解题思路是:

1. 数据预处理:Baseline 中主要是进行 a.fillna() 缺失值处理,以及用 “+” 拼接内容,构建训练集和测试集,这部分与机器学习的教程一样。

2. 构建训练所需的dataset与dataloader:其中构建 Dataset 类包含了三个方法__init__、__getitem__、__len__。__init__完成类的初始化,Pytorch 中自定义dataset需要继承torch.utils.data.Dataset,继承 Dataset 这个类时,它要求你必须重写 __getitem__(self, index)、 __len__(self) 两个方法。__getitem__(self, index)通过提供索引返回数据,也就是提供 DataLoader 获取数据的方式,在里面指返回 text 和 label 。 __len__(self) 返回数据集的长度,DataLoader 依据 len 确定自身索引采样器的长度。而 Dataloader 用来加载训练数据和训练目标。加载完的数据需要为 tensor 形式,所以 Baseline 中定义了 collate_fn 函数来帮助完成batch组装以及将文本内容向量化,而对label的值,直接用 torch.LongTensor() 方法完成。

3. Bert模型预训练:关于 Bert 模型后面一节会做简单介绍,从中可以理解预训练。

4. 模型训练与验证:加载 Bert 模型后,根据数据集对模型进行训练,并在每次epoch后,用验证集进行验证,最后选取最优的模型。不过值得注意的是模型训练的一套流程,这在深度学习中是通用的,即

(1)建立模型

(2)定义损失函数和优化器

(3) 损失反馈、计算梯度

(4)更新参数

(5)重复步骤,观察是否收敛。

5. 模型预测与结果输出:根据选取的最优模型对测试集进行预测,并输出需要的结果文件。

6. 调参优化:修改 batch_size、epochs 等。这个会在后面会主要说明。

 

模型介绍

这节对 Bert 模型进行简单介绍,由学习了Ai夏令营的资料和B站视频https://www.bilibili.com/video/BV14V4y1o7ff?vd_source=6274aa3cff8cf99ea78aa3f04caaae34后产出。

Bert 全称为 Bidirectional Encoder Representation from Transformers,从中可以看出Bert来自Transformer。Transformer 由 google 在2017年提出,由 Encoders 和 Decoders 两部分组成。2018 年 6 月,OpenAI 根据 Decoders 部分发表了 GPT-1 的论文。同年 8 月,google 根据 Encoders 部分发表了 bert 的论文。 其中,gpt 擅长于文本生成任务,而 bert 擅长文本理解任务。尽管bert 刚发表时看起来没什么创新,但它的实践能力很强,在当时的 11 项文本任务中做到了业界最强,而且 bert 还在不断更新中,它的变体有 roberta、albert、 xlnet 等等。

Transformer 是基于 Attention 机制的架构。在介绍 Attention机制之前,我们先介绍两个深度学习的基础架构:CNN(卷积神经网络)和 RNN (循环神经网络)。CNN 是在 CV 领域表现突出,而且存在计算复杂度、操作数等问题。而 RNN 及其变体 LSTM 在 NLP 方向表现不错,但它们存在一些天然的缺陷。下图为 RNN 的结构图。RNN 的架构中,后面的输出依赖于前面的,则后面的会包含前面的信息,这使其能获取上文信息,但这使得后面的运行依赖于前面,使得不能并行计算,所以它在处理速度上受限。第二,RNN 难以捕捉长序列的相关关系。另外,模型本身也存在梯度消失、梯度爆炸等问题。在这种情况下,完全基于 Attention 机制的序列转换模型 transformer 为 NLP 打开了新思路,它不仅能够捕捉文本的上下文信息,还能并行运行,大大提高了运算速度。

b2c028e939174bcc9f7a721089a3dae1.png

 Transformer 是一种序列转换模型,所谓序列转换模型,也就是说一个序列放到一个模型中,然后转换成另一个序列,比较常见的是机器翻译,就是将一种语言的文本转换成另一种语言。比较流行、而且效果比较好的序列转换模型通常都是 Encoder-Decoder 结构。Encoder 用于处理输入的序列信息,Decoder则利用加工后的输入信息生成另外一个序列然后输出。比方说在中英翻译任务中,我输入一段中文信息 “我是学生” 到 Encoders 中,对输入的信息进行编码,Decoder 就利用这段编码信息生成一段英文。需要注意的是这里面的中文翻译并不是传统的一个一个中文词语翻字典去翻译成一对一的英文单词,它是模型真正地理解了输入文本的语义信息,然后根据语义信息翻译成其他语言。可能不同于人类的理解,但这也是机器的一种理解。而对于文本分类来说,Encoder 对于文本的理解信息正是我们需要的。以这个比赛为例,机器通过大量的非医学领域论文和医学领域论文理解了什么是医学,那么它就能判断一篇论文是不是医学领域的论文了。所以以 Encoder  部分构建的 bert 模型可以解决我们的问题。

b851c14a2062408490ac101801a54c81.png

 Transformer的 Encoders 由六个 Encoder 构成,Decoders 由六个 Decoder 构成,而 bert-base 用了 12 个 Encoder,bert-large 用了 24 个。

 4988fc4066e943f09ab35f56c1bc81bf.png

 一个 encoder 块由两个部分组成,一个部分是 multi-head attention,一个是全连接的 feed forward 网络层。feed forward 就是一个基础的人工神经网络,所以最重要的是 multi-head attention 部分。从 transformer 的论文可以看出 attention 是非常重要。而我们最主要的就是去知道 attention 是如何提取文本的上下信息的。简单来说,attention 就是通过分配权重来实现提取文本上下文信息的。

以一个人观看一篇文章为例,在同一画面,人们对于文章不同地方的注意力是不同的,通常更容易注视图片、标题和段首等。将这种思想转移到文本任务中,即假设在一句文本语句中,一个词语它可以看到句中其他词语,但它对每个词语分配的注意力是不同的,用权重大小表示。

a46bf9151ebd4dbf8cd9ddf080e55722.png

 比如,以图上 ”The animal didn't cross the street,because it was too tired.“ 这一句为例,这里面的 “it” 对句中上下不同词语的权重是不同的,颜色越深,代表权重越大。这要说明的是,我们想要找到文本中 “it” 到底指代句中哪个单词,可以通过 “it” 对于其他词的权重来判断,对此,我们可以判断最高权重的 “animal” 是正确答案。也就是 transformer 真的理解文本的语义信息,而且它甚至能用可视化的方式表示出来。将所有词语的所有权重分配完毕之后,就可以用周围词语的权重加和来表示当前词语 “it” 它的信息,其他词语也一样。

attention 的具体公式是:

eq?%5Cmathbf%7BAttention%28Q%2CK%2CV%29%7D%20%3D%20%5Cmathrm%7Bsoftmax%7D%28%5Cfrac%7BQK%5ET%7D%7B%5Csqrt%7Bd_k%7D%7D%29%5Cmathrm%7BV%7D

即 attention 可以表示为 Q, K, V 三个向量的公式。Transformer 里面有三种 attention,这里讲的是 Enconder 里的 attention。

84d499aa9dae45a587d6bc0eac8f6e13.png

 下面介绍Q, K, V 三个向量是怎么得到的。当输入一个句子时,会先对词语进行编码,可以理解为把人类语言转换为机器语言。这里以输入 ”I love NLP. " 为例,首先进行分词,然后对 “I” 、“love”、“NLP” 三个词用 embedding 进行编码,得到它们各自的初始变量,然后将其分别乘以一个权重矩阵 eq?%5Cmathrm%7BW%5EQ%7D,得到 Q 向量;将各自的初始向量分别乘以权重矩阵 eq?%5Cmathrm%7BW%5EK%7D ,得到 K 向量;将各自的 初始向量分别乘以权重矩阵  eq?%5Cmathrm%7BW%5EV%7D ,得到 V 向量。这样每个词语就会有四种向量表示。attention 的主要作用就是将当前词语的 Q 和周围词语的 K 和 V 映射到一个输出中,也就是上面的公式。eq?d_k是一个参数,一般取64。

c57b2a2acb3a45a6ae1a3f61029c858e.png

 这是一个 attention,每个词语最终得到一个Z向量。后来研究者发现使用多个 attention,尤其是 multi-head 能够得到更丰富的文本表达信息。所以在 transformer 和 Bert 中用的是 multi-head attention,最终生成的是多个 Z 向量,然后对这些 Z 向量做转换输出。从 attention 机制可以看到每个词语之间没有计算顺序,也就是说它能够并行运算,大大减少了运算时间成本。但从上面的距离可以看出 attention 各词语之间有一定关系表示,但并没有先后顺序之间,但文本信息是包含词语的先后顺序的。所以 ,transformer 中将初始向量和位置向量一起输入到了 Encoder 组块中。而 bert 输入还多了一个Segment Embedding。

Transformer 通过缩小实际输出序列和理想的输出序列之间的差异来训练模型。bert 只用了transformer 的 encoder 部分,它如何保证模型能够真正地理解输入文本的信息呢。对于这个问题,一方面,bert 引入 MLM (Masked Language Model)掩码模型来作为目标函数对模型进行预训练。掩码语言模型的操作将本文集合里 15% 的词语做 mask 操作,即掩盖操作。为了保证效果,这 15% 中会有 80% 会被替换成 mask 这种特殊字符,10% 会被替换成别的词语,10% 保持不变。这样就可以将预测这些词语作为训练目标。

除了用 MLM 做预训练的目标函数,bert 还用了 NLP (next sentence prediction)做句子的匹配任务,即预测句子 B 是不是句子 A 的下一句,主要是让模型具备回答问题、语言推理的能力。在做NLP 时会在每个句子后面加一个 [SEP] 字符来分割两个句子,并引入 [CLS] 特殊字符放在文本的最开端用于做文本分类。

除了预训练外,Bert还有一个微调阶段,及 Bert 采取预训练+微调范式。所谓预训练+微调范式,指先在海量文本数据上进行预训练,再针对特定的下游任务进行微调。上面说的 MLM 和 NSP都属于预训练的任务。在实践中,我们可以用 google 提供 bert 预训练模型来做自己的任务,主要是在这些预训练模型的基础上做一些微调。微调阶段主要是根据下游任务在预训练模型的基础上添加其他的一些网络层,通过微调模型参数来满足任务需求,比如可以在预训练模型上加全连接网络层、softmax 层,然后用我们自己标记好的数据来训练微调模型,然后用训练好的模型做预测分类。这种微调模型的方式,可以让我们在比较少的时间得到一个比较好的模型。在我们代码中,用的是一个全连接层 + 一个ReLU层 + 一个全连接层 + 一个Sigmoid层,实际上就是一个二层神经网络。

# 最后的预测层
self.predictor = nn.Sequential(
    nn.Linear(768, 256),
    nn.LeakyReLU(),
    nn.Linear(256, 1),
    nn.Sigmoid()
 )

代码运行

代码较长,这次笔记不放全代码。Datawhale 学习资料开源,有兴趣可以关注获取。修改代码前,先完成代码跑通任务,确认配置、操作没有问题。       

这次的任务,我在本地和服务器都跑了提供的 baseline 代码,本地并没有出现什么问题,主要就注意压缩文件要解压后,要将代码里面的导入模型的目录进行修改。

服务器的话需要注意文件加载问题。由于我一开始是在本地跑的代码,所以文件已经解压,所以我就直接在服务器的上传按键上上传解压的文件了,但服务器传输文件时,未完全上传成功前,文件就可以在文件框内看到,我是第一次操作,所以误认为上传已完成,就进行运行代码,结果发现运行报错。

a1cecaec1cda4173aeeab10672cf5460.png

 错误显示,不能从文件中载入权重。但其实就是文件未上传完成。

解决方法:用终端删除文件,如果删除非空文件夹使用 rm -r + /目录/文件夹。可以选择直接上传压缩包,可能更快。上传后在终端解压,使用 unzip + 文件.zip 。

注意:需要注意的是,1. 不建议在文件框里直接按键删除文件,这样可能会出现删除后,文件又重新出现的局面,有一种阴魂不散的即视感。这是因为删除的文件还在上传中。2. 其实上传文件的时候窗口下面会有unloading出现,尽管文件在服务器上已显示,但实际上并没有加载完成,所以会报错。unloading时间比较长,所以需要耐心等待。

a61a68355f2c449b9d6e57b7d7a8d4fd.jpeg

 另外就是我遇到一个警告提醒:

Some weights of the model checkpoint at D:\Transformers\bert-entity-extraction\input\bert-base-uncased_L-12_H-768_A-12 were not used when initializing BertModel:    
['cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight',   'cls.predictions.transform.LayerNorm.bias', 'cls.seq_relationship.bias',  
 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.weight',  
 'cls.predictions.bias']  
    - This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
    - This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).

我一开始以为是错误,细看过发现时提醒一些权重没有被使用,查询资料后,发现是警告,

解决方法:可以直接忽略,也可以执行操作

from transformers import logging

logging.set_verbosity_warning()

资料链接:Python:BERT 错误 - 初始化 BertModel 时未使用模型检查点的某些权重 - IT工具网 (coder.work)

解决完以上问题后,我就完成了跑通任务了。

这次 baseline 代码也一样注释得很详尽,学习资料也写得很细,我理解上也没有问题,所以这里就不做全部解析了。我比较注意理解的部分也在解题思路里多说明了一下,其他注意的就是可以修改的地方,即模型优化问题。

模型优化

模型优化主要可以从这几个方面入手:

1. 调整 batch_size

2. 调整 text_max_length

3. 调整 epochs

4. 将预测层更换成更复杂的模型

5. 修改损失函数和优化器

修改batch_size

修改 batch_size 很简单,只需要改数字就行,常见的 batch_size 有 16、 32。但除此外,我们可以去理解为什么 batch_size 可以影响训练结果,优秀参考资料:

https://blog.csdn.net/t18438605018/article/details/121880488
所谓的 batch, 实际上就是将数据分批处理,batch_size 指是一批的大小,从训练时输出的 step 中就可以看出,但选择的 batch_size 比较大的时候(即选择每个批次的样本数比较大),一次 epoch 的 step 就会减少(即跑完训练集所有样本的步骤就比较少)。

在深度学习中,使用梯度下降法,我们依靠损失的函数的梯度下降方向和学习率去更新参数,不断更新后,去寻求达到损失最小的模型。

b4ae0a6701d145ab9bf96868567e611d.webp

很多时候损失函数并不像上图显示得那么平滑,而如果使用随机梯度下降法,即一步选取一个样本计算梯度,那么可能会出现梯度消失的情况,所以使用多个样本进行损失求和取平均,会更为稳当。另外,就是可以选取 batch 比较大,可以提高运行速度。

但 batch 并不是越大越好,我在本地运行的时候选取batch为 5,8,16 等,有一次 batch 为 5 时效果最好。

调整 text_max_length

修改 text_max_length 的数值,即调整允许文本输入的最大长度。定义允许文本输入的最大长度可以保证每个样本文本长度相同。当定义文本最大长度时,若文本信息大于长度时,会被截断;若小于,会填充。所以最好的 text_max_length 要保证其大于数据集中最长的文本长度。所以可以适当增大 text_max_length 看看模型表现效果。

调整 epochs

一次 epoch 指跑完全部训练样本。由于训练具有一定的随机性,所以多次跑可以得到更好的结果,也可以观察损失是否会收敛到较好的结果。所以可以适当增加 epochs 的大小,但太多次会损失时间成本。

更改预测层模型

上文讲到 baseline 代码中,预测层模型为一个二层神经网络,那我们可以尝试将其修改为三层神经网络等。

 

修改损失函数和优化器

在 baseline 代码中用的损失函数和优化器分别是 Binary Cross Entropy 和 Adam 优化器。

定义出损失函数和优化器。这里使用Binary Cross Entropy:
criteria = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)

可以使用的分类相关的损失函数有nn.CrossEntropyLoss(),nn.SoftMarginLoss(),  nn.BCEWithLogitsLoss()等。更多可看PyTorch 学习笔记(六):PyTorch的十八个损失函数 - 知乎 (zhihu.com)

优化器可以使用的也有很多,如

  • torch.optim.Adadelta
  • torch.optim.Adagrad
  • torch.optim.Adam
  • torch.optim.AdamW
  • torch.optim.Adamax
  • torch.optim.LBFGS
  • torch.optim.RMSprop
  • torch.optim.Rprop
  • torch.optim.SGD
  • torch.optim.SparseAdam

其中 torch.optim.Adam 和 torch.optim.SGD 是比较常用的优化器。一般 torch.optim.Adam 是比较推荐的,所以我这里没有做过更改。

因为每次运行代码都需要比较长的时间,所以我并没有把以上修改方式全部进行一 一对比改动参照,但还是尽可能做了一些尝试。

一些实践结果

由于我非阿里新用户,所以主要还是在本地跑得比较多,修改比较受限。一开始只修改了 batch_size和 epochs(在 20 到 30 之间尝试发现影响不大,不用设太高)

b73804311c42486d8cbf025df345230c.png

 最后结果最好的设置 batch_size = 5 在本地跑的结果(有一定偶然性),loss 后期达到 0.0001这里,提交后官网准确率 0.999。另外就是我在数据集运用那里把关键词项删了。

后期我还是对预测模型修改做了一些,将其修改了三层网络,并将 text_max_length  扩大为 150,得到准确率为0.998,还是没有超过 0.999 。

e70b4324b8824b40a2ed57cfe55db0f3.png

虽然一些尝试没能得到正反馈,但应用广。这说明模型可能不是越复杂越好,也可能尝试还不够。但能确定的是使用 bert-base 模型准确率完全可以达到 99% 以上,但上 1 比较困难。但将其换成 bert 的变体模型 roberta 是可以达到 1 的,而且比较稳定。

7ffad0882cfd4d7bb650e7dc122542f1.png

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值