赞
踩
Datewhle29期__NLP之transformer :
学习资料地址:
https://datawhalechina.github.io/learn-nlp-with-transformers/#/
github地址:
https://github.com/datawhalechina/learn-nlp-with-transformers
class BertForPreTraining(BertPreTrainedModel):
def __init__(self, config):
super().__init__(config)
self.bert = BertModel(config) #默认add_pooling_layer=True,即会提取[CLS]对应的输出用于 NSP 任务
self.cls = BertPreTrainingHeads(config) # 负责两个预测模块
self.init_weights()
# ...
BertPreTrainingHeads
细节----- LMPredictionHead
以及代表 NSP 任务的线性层class BertPreTrainingHeads(nn.Module):
def __init__(self, config):
super().__init__()
self.predictions = BertLMPredictionHead(config)
self.seq_relationship = nn.Linear(config.hidden_size, 2)
def forward(self, sequence_output, pooled_output): #代表 NSP 任务的线性层
prediction_scores = self.predictions(sequence_output)
seq_relationship_score = self.seq_relationship(pooled_output)
return prediction_scores, seq_relationship_score
LMPredictionHead
-----用于预测[MASK]位置的输出在每个词作为类别的分类输出-----MLMclass BertLMPredictionHead(nn.Module): def __init__(self, config): super().__init__() self.transform = BertPredictionHeadTransform(config) #完成线性变换 # The output weights are the same as the input embeddings, but there is # an output-only bias for each token. self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) self.bias = nn.Parameter(torch.zeros(config.vocab_size)) #全0向量作为权重 # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` self.decoder.bias = self.bias def forward(self, hidden_states): hidden_states = self.transform(hidden_states) hidden_states = self.decoder(hidden_states) return hidden_states
注意:
labels:形状为[batch_size, seq_length] ,代表 MLM 任务的标签,注意这里对于原本未被遮盖的词设置为 -100,被遮盖词才会有它们对应的 id,和任务设置是反过来的。
例如,原始句子是I want to [MASK] an apple,这里我把单词eat给遮住了输入模型,对应的label设置为[-100, -100, -100, 【eat对应的id】, -100, -100];
为什么要设置为 -100 而不是其他数?因为torch.nn.CrossEntropyLoss默认的ignore_index=-100,也就是说对于标签为 100 的类别输入不会计算 loss。
next_sentence_label:这一个输入很简单,就是 0 和 1 的二分类标签。
两部分 loss 的组合-------直接相加
# ...
total_loss = None
if labels is not None and next_sentence_label is not None:
loss_fct = CrossEntropyLoss()
masked_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), labels.view(-1))
next_sentence_loss = loss_fct(seq_relationship_score.view(-1, 2), next_sentence_label.view(-1))
total_loss = masked_lm_loss + next_sentence_loss
# ...
这一模型用于句子分类(也可以是回归)任务,比如 GLUE benchmark 的各个任务。
句子分类的输入为句子(对),输出为单个分类标签。
结构上很简单,就是BertModel(有 pooling)过一个 dropout 后接一个线性层输出分类:
class BertForSequenceClassification(BertPreTrainedModel):
def __init__(self, config):
super().__init__(config)
self.num_labels = config.num_labels
self.bert = BertModel(config)
self.dropout = nn.Dropout(config.hidden_dropout_prob)
self.classifier = nn.Linear(config.hidden_size, config.num_labels)
self.init_weights()
# ...
在前向传播时,和上面预训练模型一样需要传入labels输入。
如果初始化的num_labels=1,那么就默认为回归任务,使用 MSELoss;
否则认为是分类任务。
这一模型用于多项选择,如 RocStories/SWAG 任务。
这一模型用于序列标注(词分类),如 NER 任务。
这一模型用于解决问答任务,例如 SQuAD 任务。
以上就是关于 BERT 源码的介绍,下面介绍一些关于 BERT 模型实用的训练细节。
BERT 的训练中另一个特点在于 Warmup,其含义为:
在训练初期使用较小的学习率(从 0 开始),在一定步数(比如 1000 步)内逐渐提高到正常大小(比如上面的 2e-5),避免模型过早进入局部最优而过拟合;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。