赞
踩
原文:https://zhuanlan.zhihu.com/p/295248694
本篇文章记录的是一个pytorch初学者在完成NER任务中踩过的坑。希望写下的这篇文章能帮助也在学习pytorch的同学。接下来,我将按照模型构建的过程逐一记录我所遇到的坑。希望能和大家交流心得。
1、如何方便的使用bert(或其他预训练模型)。
最优选的方法是,使用官方代码,仔细研读,并作为一个模块加入到代码中。可是通过这样的方式使用预训练模型,准备的周期较长。在这里我选择了一个次优选项-调包。“transformers”是一个目前很火的github项目,使用这个工具包能够很轻松的完成预训练模型的构建。同时transformers已经集成了几十种预训练模型,使用起来非常方便。transformers刚刚在EMNLP2020获得了最佳demo奖,大家值得一试。
但是transformers有一个需要注意的点是:需要注意transformers的版本及其对应的说明文档和示例。
写稿的当前时间为2020年11月15日,使用pip安装transformers的最新版本是3.4.0。当前版本的问题是:对于pytorch的要求是1.2版本以上(我的版本是1.7可用),tensorflow的版本要求是2.3以上(而不是官网上写的tensorflow2.0),因此使用最新版transformers的同学需要注意更新cuda版本到11左右,以免代码调用transformers的时候会发生tensorflow缺少文件的情况(忘记截图记录错误了)。在github的issure中有人提了和我相同的问题,作者的回复是将会在下一个版本修复这个问题。
另外一个就是transformers的前身是一个名为pytorch-transformers的库,transformers和transformers对于预训练模型的使用方法是有区别的。现在网上的教程和资源两者的资料是混在一起的,在大家参考教程的时候千万要看清调用的包是否是同一个。在这里有一个小小的建议是:我推荐使用transformers,放弃使用pytorch-transformers。因为目前pytorch-transformers的官方文档已经从github上下掉了,无论是使用还是后期维护都可能有潜在的问题。我建议大家如果想使用transformers,一定要在一开始就把transformers所需要的环境装好,而不要退而求其次去使用pytorch-transformers。
另外就是transformers对于很多常见的任务都已经预先设置好了不同的模型。只需要很简单的调用(整个代码100行)就能finetuning出一个分类模型。在获取baseline的时候,可以用transformers的预设模型方便的快速试错。
但是我不得不吐一个槽,官方文档放出来的示例代码实在是太少了。想要使用transformers往往需要参考很多网上的资料还有github上其他大神的代码。这也是我写这篇文章的初衷,希望同学们可以少走弯路。
2、使用transformers做bert的tokenizer
在官方文档中,bert的tokenizer有BertTokenizer, BertTokenizerFast。我比较推荐使用BertTokenizerFast。理由很简单,虽然中文bert是基于char级别实现的,可是对于数字、英文,完全拆分成[0-9a-zA-Z]的字符则完全没有任何意义。因此再做Tokennize的时候,我们需要知道哪些字符被tokenize到了一个input_id上,这样我们在做NER预测的时候,才能知道边界应该切在哪个位置。这种映射我们统一称为offset先。(当然如果是做分类或者seq2seq的时候,offset就不是那么必须的)。
BertTokenizerFast可以在帮助我们完成tokennize的同时输出我们需要的offset,就不用我们在寻找一遍映射关系了。
from transformers import BertTokenizerFast
tokenizer = BertTokenizerFast.from_pretrained(‘bert-base-chinese’)
tokens = tokenizer(string_list,
return_offsets_mapping=True,
max_length=max_seq_length,
truncation=True)
3、模型的输入
BERT的输入需要三个,inputids、input_mask、segment_ids(也就是token_type_ids)。除此之外根据NER实现的思路要准备不同格式的标签做对比。(1)seq识别,即使用softmax或者crf按照传统序列标注的方法识别的话,只需要输入id化后的每一个tokenize的label即可。(2)span识别,需要用两个序列分别标记槽位的begin位置和end位置,label需要两个list。
4、Bert模型的调用
from transformers import BertModel
self.bert = BertModel.from_pretrained(“bert-base-chinese”)
outputs = self.bert(input_ids = input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids)
sequence_output = outputs[0]
5、模型存储
我建议使用torch.save储存模型,而不用transformers的包来做。
6、warm_up
def get_cosine_schedule_with_warmup(optimizer, num_warmup_steps, num_training_steps, num_cycles=.5, last_epoch=-1):
“”" Create a schedule with a learning rate that decreases following the
values of the cosine function between 0 and pi * cycles
after a warmup
period during which it increases linearly between 0 and 1.
“”"
def lr_lambda(current_step):
if current_step < num_warmup_steps:
return float(current_step) / float(max(1, num_warmup_steps))
progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
return max(0., 0.5 * (1. + math.cos(math.pi * float(num_cycles) * 2. * progress)))
return LambdaLR(optimizer, lr_lambda, last_epoch)
scheduler = get_cosine_schedule_with_warmup(optimizer,
num_warmup_steps=num_warmup_step,
num_training_steps=num_training_steps)
7、实验曲线记录
from tensorboardX import SummaryWriter
writer = SummaryWriter(log_dir=os.path.join(args.output_dir, “writer_summary”))
writer.add_scalar(“loss/train”, tr_loss.item(), global_step)
8、梯度累积trick
由于bert模型较大,使用单卡跑模型的时候很容易out of memory。因此batch size会调的很小。batch size小了就可能个别case会造成loss的波动,影响模型收敛效果。使用梯度累积的trick,意思是再跑了多个batch之后,再一次性回传梯度。这样就可以达到和大batch size相同的效果,避免实验的偶然性。
9、向transformers bert中新增字符的方法
BertTokenizer.add_tokens([““”, “””])
BertModel.resize_token_embeddings(len(BertTokenizer))
10、计算指标的问题
实体识别评价指标为 precision、 recall、 f1,注意此处统计是针对“实体”为概念进行统计的。
在做暴力枚举的span base的时候,遇上的问题有:
(1)当golden标记为非实体,predict也为非实体时,不应算作混淆矩阵中的TP。原因是,在这种情况下,golden和predict都不判断此处有实体存在,因此没有一个所谓“实体”的概念可供统计。所以应该跳过,不参加评价打分。如果没有过滤此部分测试样本,那么会造成模型评测的结果异常的高,接近99.99%(因为负样本的数目远远大于正样本)。
(2)在计算golden标记为实体,而predict为非实体时,只增加recall的分母(因为预测不认为此处有实体,所以precision的分母不要加1)
在计算predict标记为实体,而golden为非实体时,只增加precision的分母(因为golden不认为此处有实体,所以golden的分母不要加1)
如果此处有问题,可能的表现是,评价指标偏低
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。