文章目录

  • 自然语言推断:微调BERT
    • 1 - 加载预训练的BERT
    • 2 - 微调BERT的数据集
    • 3 - 微调BERT
    • 4 - 小结

自然语言推断:微调BERT

在前几章中,我们已经为SNLI数据集上的自然语言推断任务设计了一个基于注意力的结构。现在,我们通过微调BERT来重新审视这项任务,自然语言推断任务时一个序列级别的文本对分类问题,而微调BERT只需要一个额外的基于多层感知机的架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ArZ3KHum-1665475659087)(images/f1.png)]
在本节中,我们将下载一个预训练好的小版本BERT,然后对其进行微调,以便在SNLI数据集上进行自然语言推断

import json
import multiprocessing
import os
import torch
from torch import nn
from d2l import torch as d2l

1 - 加载预训练的BERT

我们以前在WikiText-2数据集上预训练BERT(注意,原始的BERT模型是在更大的语料库上预训练的),原始的BERT模型有数以亿计的参数。在下面,我们提供了两个版本的预训练的BERT:"bert.base"与原始的BERT基础模型一样大,需要大量的计算资源才能进行微调。而"bert.small"是一个小版本,便于演示

d2l.DATA_HUB['bert.base'] = (d2l.DATA_URL + 'bert.base.torch.zip',
'225d66f04cae318b841a13d32af3acc165f253ac')
d2l.DATA_HUB['bert.small'] = (d2l.DATA_URL + 'bert.small.torch.zip',
'c72329e68a732bef0452e4b96a1c341c8910f81f')

两个预训练好的BERT模型都包含一个定义词表的“vocab.json”文件和一个预训练参数的“pretrained.params”文件。我们实现了以下load_pretrained_model函数来加载预先训练好的BERT参数

def load_pretrained_model(pretrained_model,num_hiddens,ffn_num_hiddens,num_heads,num_layers,dropout,max_len,devices):data_dir = d2l.download_extract(pretrained_model)# 定义空词表以加载预定义词表vocab = d2l.Vocab()vocab.idx_to_token = json.load(open(os.path.join(data_dir,'vocab.json')))vocab.token_to_idx = {token: idx for idx,token in enumerate(vocab.idx_to_token)}bert = d2l.BERTModel(len(vocab),num_hiddens,norm_shape=[256],ffn_num_input=256,ffn_num_hiddens=ffn_num_hiddens,num_heads=4,num_layers=2,dropout=0.2,max_len=max_len,key_size=256,query_size=256,value_size=256,hid_in_features=256,mlm_in_features=256,nsp_in_features=256)# 加载预训练BERT参数bert.load_state_dict(torch.load(os.path.join(data_dir,'pretrained.params')))return bert,vocab

为了便于在大多数机器上演示,我们将在本节中加载和微调经过预训练的BERT小版本(“bert.small”)。在练习中,我们将展示如何微调大得多的“bert.base”以显著提高测试精度

devices = d2l.try_all_gpus()
bert,vocab = load_pretrained_model('bert.small',num_hiddens=256,ffn_num_hiddens=512,num_heads=4,num_layers=2,dropout=0.1,max_len=512,devices=devices)

2 - 微调BERT的数据集

对于SNLI数据集的下游任务自然语言推断,我们定义了一个定制的数据集类SNLIBERTDataset。在每个样本中,前提和假设形成一对文本序列,并被打包成一个BERT输入序列。利用预定义的BERT输入序列的最大长度(max_len),持续移除输入文本对中较长文本的最后一个标记,直到满足max_len。为了加速生成用于微调BERT的SNLI数据集,我们使用4个工作进程并行生成训练或测试样本

class SNLIBERTDataset(torch.utils.data.Dataset):def __init__(self,dataset,max_len,vocab=None):all_premise_hypothesis_tokens = [[p_tokens,h_tokens] for p_tokens,h_tokens in zip(*[d2l.tokenize([s.lower() for s in sentences]) for sentences in dataset[:2]])]self.labels = dataset[2]self.vocab = vocabself.max_len = max_len(self.all_token_ids,self.all_segments,self.valid_lens) = self._preprocess(all_premise_hypothesis_tokens)print('read' + str(len(self.all_token_ids)) + ' examples')def _preprocess(self,all_premise_hypothesis_tokens):pool = multiprocessing.Pool(4) # 使用4个进程out = pool.map(self._mp_worker,all_premise_hypothesis_tokens)all_token_ids = [token_ids for token_ids,segments,valid_len in out]all_segments = [segments for token_ids,segments,valid_len in out]valid_lens = [valid_len for token_ids,segments,valid_len in out]return (torch.tensor(all_token_ids,dtype=torch.long),torch.tensor(all_segments,dtype=torch.long),torch.tensor(valid_lens))def _mp_worker(self,premise_hypothesis_tokens):p_tokens,h_tokens = premise_hypothesis_tokensself._truncate_pair_of_tokens(p_tokens,h_tokens)tokens,segments = d2l.get_tokens_and_segments(p_tokens,h_tokens)token_ids = self.vocab[tokens] + [self.vocab['<pad>']] * (self.max_len - len(tokens))segments = segments + [0] * (self.max_len - len(segments))valid_len = len(tokens)return token_ids,segments,valid_lendef _truncate_pair_of_tokens(self,p_tokens,h_tokens):# 为BERT输入中的'<CLS>'、'<SEP>'和'<SEP>'词元保留位置while len(p_tokens) + len(h_tokens) > self.max_len - 3:if len(p_tokens) > len(h_tokens):p_tokens.pop()else:h_tokens.pop()def __getitem__(self,idx):return (self.all_token_ids[idx],self.all_segments[idx],self.valid_lens[idx],self.labels[idx])def __len__(self):return len(self.all_token_ids)

下载完SNLI数据集后,我们通过实例化SNLIBERTDataset类来生成训练和测试样本,这些样本在自然语言推断的训练和测试期间进行小批量读取

import os
import redef read_snli(data_dir,is_train):"""将SNLI数据集解析为前提、假设和标签"""def extract_text(s):# 删除我们不会使用的信息s = re.sub('\\(','',s)s = re.sub('\\(','',s)# 用一个空格替换两个或多个连续的空格s = re.sub('\\s{2,}',' ',s)return s.strip()#label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2}label_set = {0:'entailment',1: 'contradiction',2: 'neutral'}file_name = os.path.join(data_dir, 'train.txt' if is_train else 'test.txt')with open(file_name,'r') as f:rows = [row.split('\t') for row in f.readlines()[1:]]premises = [extract_text(row[0]) for row in rows]hypotheses = [extract_text(row[1]) for row in rows]labels = [label_set[int(row[2].replace('\n',''))] for row in rows]return premises,hypotheses,labels
# 如果出现显存不足的错误,请减少“batch_size”,在原始的BERT模型中,max_len=512batch_size,max_len,num_workers = 512,128,d2l.get_dataloader_workers()
data_dir = 'SNLI'
train_set = SNLIBERTDataset(read_snli(data_dir, True), max_len, vocab)
test_set = SNLIBERTDataset(read_snli(data_dir, False), max_len, vocab)
train_iter = torch.utils.data.DataLoader(train_set, batch_size, shuffle=True,num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(test_set, batch_size,num_workers=num_workers)

3 - 微调BERT

用于自然语言推断的微雕BERT只需要一个额外的多层感知机,该多层感知机由两个全连接层组成(参见下面BERTClassifier类中的self.hidden和self.output)。这个多层感知机将特殊的“<cls>”词元的BERT表示进行了转换,该词元同时编码前提和假设的信息为自然语言推断的三个输出:蕴涵、矛盾和中性

class BERTClassifier(nn.Module):def __init__(self,bert):super(BERTClassifier,self).__init__()self.encoder = bert.encoderself.hidden = bert.hiddenself.output = nn.Linear(256,3)def forward(self,inputs):tokens_X,segments_X,valid_lens_x = inputsencoded_X = self.encoder(tokens_X,segments_X,valid_lens_x)return self.output(self.hidden(encoded_X[:,0,:]))

在下文中,预训练的BERT模型bert被送到用于下游应用的BERTClassifier实例net中,在BERT微调的常见实现中,只有额外的多层感知机(net.output)的输出层的参数将从零开始学习。预训练BERT编码器(net.encoder)和额外的多层感知机的隐藏层(net.hidden)的所有参数都将进行微调

net = BERTClassifier(bert)

回想一下,MaskLM类和NextSentencePred类在其使用的多层感知机中都有一些参数,这些参数是预训练BERT模型bert中参数的一部分,因此是net中参数的一部分。然而,这些参数仅用于计算预训练过程中的遮蔽语言模型损失和下一句预测损失。这两个损失函数与微调下游应用无关,因此当BERT微调时,MaskLM和NextSentencePred中采用的多层感知机的参数不会更新(陈旧的,staled)

为了允许具有陈旧梯度的参数,标注ignore_stale_grad=Ture在step函数d2l.train_batch_ch13中被设置。我们通过该函数使用SNLI训练集(train_iter)和测试集(test_iter)对net模型进行训练和评估。由于计算资源有限,训练和测试精度可以进一步提高:我们把对它的讨论留在练习中

lr,num_epochs = 1e-4,5
trainer = torch.optim.Adam(net.parameters(),lr=lr)
loss = nn.CrossEntropyLoss(reduction='none')
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices)

4 - 小结

  • 我们可以针对下游应用对预训练的BERT模型进行微调,例如在SNLI数据集上进行自然语言推断
  • 在微调过程中,BERT模型成为下游应用模型的一部分。仅与训练前损失相关的参数在微调期间不会更新

自然语言推断:微调BERT相关推荐

  1. 自然语言推理:微调BERT

    自然语言推理:微调BERT Natural Language Inference: Fine-Tuning BERT SNLI数据集上的自然语言推理任务设计了一个基于注意力的体系结构.现在通过微调BE ...

  2. 自然语言处理NLP星空智能对话机器人系列:深入理解Transformer自然语言处理 基于BERT模型微调实现句子分类

    自然语言处理NLP星空智能对话机器人系列:深入理解Transformer自然语言处理 基于BERT模型微调实现句子分类 目录 基于BERT模型微调实现句子分类案例实战 Installing the H ...

  3. bert 多义词_自然语言处理:Bert及其他

    以下内容主要参考了文末列出的参考文献,在此表示感谢! 2018年被认为是NLP技术的new era的开始.在这一年,提出了多种有创新性的技术,而且最后的集大成者Bert在NLP的多项任务中屠榜,造成的 ...

  4. NLP应用:情感分析和自然语言推断

    0 序言 回顾: 如何在文本序列中表示词元 训练了词元的表示 这样的预训练文本可通过不同的模型架构,放入不同的下游NLP任务 之前的提到的NLP应用没有使用 预训练 本章: 重点:如何应用 DL表征学 ...

  5. 微调BERT:序列级和令牌级应用程序

    微调BERT:序列级和令牌级应用程序 Fine-Tuning BERT for Sequence-Level and Token-Level Applications 为自然语言处理应用程序设计了不同 ...

  6. 【小白学习PyTorch教程】十六、在多标签分类任务上 微调BERT模型

    @Author:Runsen BERT模型在NLP各项任务中大杀四方,那么我们如何使用这一利器来为我们日常的NLP任务来服务呢?首先介绍使用BERT做文本多标签分类任务. 文本多标签分类是常见的NLP ...

  7. 【论文解读】(如何微调BERT?) How to Fine-Tune BERT for Text Classification?

    文章目录 论文信息 1. 论文内容 2. 论文结论 2.1 微调流程 2.2 微调策略(Fine-Tuning Strategies) 2.3 Further Pretrain 3. 论文实验介绍 3 ...

  8. Pytorch环境下微调BERT以及调参教程

    使用Hugginface的Transformers库快速微调BERT等预训练模型,使其适应下游任务,本文以Quora问题对任务为例,对两问题表意是否一致进行预测 介绍 之前写了个微调BERT的入门教程 ...

  9. 用于多标签Tweets 分类的微调bert模型转载于论文(适用于小白讨论,大佬可以过来凑个热闹)

    分享来自  用于多标签Tweets分类的微调Bert模型 为了解决数据不平衡问题,本文 采用自适应的方式为类赋权 大家好,很高兴认识各位 第一次发文章  我是只会一本正经胡说八道,又菜又爱玩  爱水文 ...

  10. 全面改进Transformer类预训练模型,自然语言任务超越BERT

    近日 arXiv 上一篇深度学习文章引起业内广泛关注: 论文标题:SegaBERT: Pre-training of Segment-aware BERT 论文链接:https://arxiv.org ...

最新文章

  1. 批量安装zabbix-agent脚本
  2. Sql Server中Case函数的使用(上篇)----转载
  3. java input是什么意思_java中的【...】表示什么意思
  4. imp-00017 oracle2298,急,imp怪異問題,請高手協助
  5. 实战:Redis 性能优化方案
  6. 电子工程专业评副高总结_微电子科学与工程专业怎么样?
  7. linux的文本,Linux文本处理
  8. pytho tkinter 应用第一个窗口
  9. curl实现发送Get和Post请求(PHP)
  10. 大疆DJI Thermal SDK Linux libdirp.so: cannot open shared object file: No such file or directory
  11. 【时间序列分析】02. 线性平稳序列
  12. 计算机病毒是不会破坏计算机软件的,计算机病毒是不会破坏计算机硬件的。
  13. instagram图片下载_如何使用Python下载Instagram个人资料图片
  14. 学计算机的该不该参加培训机构
  15. ref、reactive、toRef、toRefs
  16. 在Windows系统上对hfds中的文件进行操作
  17. WZOI-216猴子吃桃
  18. android音乐播放器sd,Android音乐播放器(2)从SD卡中读取音乐
  19. 蓝桥算法提高ADV-381 分割项链题解
  20. .dat数据文件怎么打开_SPSS统计分析,之一 SPSS数据文件读取

热门文章

  1. 触宝发布2018年第四季度财报 净收入增长147%
  2. 整理了18个可以免费学习编程的网站
  3. 2020年新年新气象
  4. 个人开发者基于可编程Web的产品尝试:QCon会前采访FaWave作者李华煜
  5. java 及时释放内存_Java里可以自动释放的不只是内存,只要是“资源”,都可以自动释放!轻松加愉快!...
  6. 无线电波段和频谱的划分
  7. U盘插入电脑提示未能成功安装设备驱动程序,这个要怎么处理呢
  8. AutoHotKey实现百度云批量离线下载工具
  9. DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰)
  10. 匹配查询(Match)