快速上手大模型:深度学习15(BERT、优化算法)
目录
BERT、优化算法必学。
1 迁移学习
(1)使用预训练好的模型抽取词、句子的特征(如word2vec或语言模型);
(2)不更新预训练好的模型;
(3)需要构建新的网络来抓去新任务需要的信息(word2vec忽略了时序信息,语言模型只看了一个方向)。
2 BERT
2.1 定义
基于微调的NLP模型;
预训练的模型抽取了足够多的信息;
新任务只需要增加一个简单的输出层。
2.2 架构
只有编码器的Transformer;
两个版本,Base:#blocks=12,hidden size=768,#heads=12,#parameters=110M
Large:#blocks=24,hidden size=1024,#heads=16,#parameters=340M
2.3 创新
(1)输入
Transformer每个样本是一个句子对,BERT通过分隔符拼起来,ID做区分,位置编码机器学不人工设计。
(2)带掩码的语言模型
Transformer的编码器是双向,标准语言模型要求单向;
带掩码的语言模型每次随机将一些词元换成mask;
因微调时不出现mask,80%概率将选中的次元变成mask、10%概率换成一个随机次元、10%概率保持原有词元。
(3)下一句子预测
预测一个句子对中两个句子是不是相邻,训练样本中50%概率选中相邻句子对、50%概率选择随机句子对,将对应输出放到全连接层预测。
2.4 代码
import torch from torch import nn from d2l import torch as d2l(1)输入表示
#@save def get_tokens_and_segments(tokens_a, tokens_b=None): """获取输入序列的词元及其片段索引""" tokens = ['<cls>'] + tokens_a + ['<sep>'] # 0和1分别标记片段A和B segments = [0] * (len(tokens_a) + 2) if tokens_b is not None: tokens += tokens_b + ['<sep>'] segments += [1] * (len(tokens_b) + 1) return tokens, segments #@save class BERTEncoder(nn.Module): """BERT编码器""" def __init__(self, vocab_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout, max_len=1000, key_size=768, query_size=768, value_size=768, **kwargs): super(BERTEncoder, self).__init__(**kwargs) self.token_embedding = nn.Embedding(vocab_size, num_hiddens) self.segment_embedding = nn.Embedding(2, num_hiddens) self.blks = nn.Sequential() for i in range(num_layers): self.blks.add_module(f"{i}", d2l.EncoderBlock( key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, True)) # 在BERT中,位置嵌入是可学习的,因此我们创建一个足够长的位置嵌入参数 self.pos_embedding = nn.Parameter(torch.randn(1, max_len, num_hiddens)) def forward(self, tokens, segments, valid_lens): # 在以下代码段中,X的形状保持不变:(批量大小,最大序列长度,num_hiddens) X = self.token_embedding(tokens) + self.segment_embedding(segments) X = X + self.pos_embedding.data[:, :X.shape[1], :] for blk in self.blks: X = blk(X, valid_lens) return X vocab_size, num_hiddens, ffn_num_hiddens, num_heads = 10000, 768, 1024, 4 norm_shape, ffn_num_input, num_layers, dropout = [768], 768, 2, 0.2 encoder = BERTEncoder(vocab_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout) tokens = torch.randint(0, vocab_size, (2, 8)) segments = torch.tensor([[0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1]]) encoded_X = encoder(tokens, segments, None) encoded_X.shape(3)预训练
#@save class MaskLM(nn.Module): """BERT的掩蔽语言模型任务""" def __init__(self, vocab_size, num_hiddens, num_inputs=768, **kwargs): super(MaskLM, self).__init__(**kwargs) self.mlp = nn.Sequential(nn.Linear(num_inputs, num_hiddens), nn.ReLU(), nn.LayerNorm(num_hiddens), nn.Linear(num_hiddens, vocab_size)) def forward(self, X, pred_positions): num_pred_positions = pred_positions.shape[1] pred_positions = pred_positions.reshape(-1) batch_size = X.shape[0] batch_idx = torch.arange(0, batch_size) # 假设batch_size=2,num_pred_positions=3 # 那么batch_idx是np.array([0,0,0,1,1,1]) batch_idx = torch.repeat_interleave(batch_idx, num_pred_positions) masked_X = X[batch_idx, pred_positions] masked_X = masked_X.reshape((batch_size, num_pred_positions, -1)) mlm_Y_hat = self.mlp(masked_X) return mlm_Y_hat mlm = MaskLM(vocab_size, num_hiddens) mlm_positions = torch.tensor([[1, 5, 2], [6, 1, 5]]) mlm_Y_hat = mlm(encoded_X, mlm_positions) mlm_Y_hat.shape mlm_Y = torch.tensor([[7, 8, 9], [10, 20, 30]]) loss = nn.CrossEntropyLoss(reduction='none') mlm_l = loss(mlm_Y_hat.reshape((-1, vocab_size)), mlm_Y.reshape(-1)) mlm_l.shape(4)下一句预测
#@save class NextSentencePred(nn.Module): """BERT的下一句预测任务""" def __init__(self, num_inputs, **kwargs): super(NextSentencePred, self).__init__(**kwargs) self.output = nn.Linear(num_inputs, 2) def forward(self, X): # X的形状:(batchsize,num_hiddens) return self.output(X) encoded_X = torch.flatten(encoded_X, start_dim=1) # NSP的输入形状:(batchsize,num_hiddens) nsp = NextSentencePred(encoded_X.shape[-1]) nsp_Y_hat = nsp(encoded_X) nsp_Y_hat.shape nsp_y = torch.tensor([0, 1]) nsp_l = loss(nsp_Y_hat, nsp_y) nsp_l.shape
2.5 微调
(1)选择任务类型
句子分类、命名实体识别、问题回答等。
(2)构建输入格式
(3)在BERT顶部加一个小的任务头
(4)反向传播 + 更新所有参数
3 优化算法
3.1 梯度下降(Gradient Descent)
每次要用全部数据,太慢。
3.2 随机梯度下降SGD
快,收敛慢、震荡大、容易掉到局部最优。
3.3 冲量法Momentum
3.4 Adam
AdamW是Adam的改进版,对权重衰减进行了更正确的实现。大模型(如Transformer、BERT、GPT)几乎都用AdamW。
更多推荐







所有评论(0)