当前位置:   article > 正文

Bert源码讲解(Pytorch版本)

bert源码

先参考一个很好博主写的:

Bert源码详解(Pytorch版本) - 知乎 (zhihu.com)

从零实现BERT中的两个预训任务(上) – 月来客栈

Pytorch版本的BERT使用学习笔记_pytorch和bert-CSDN博客

等有时间再来详细总结。。。。

bert两个子任务:

(1)NSP 

(2)MLM

LLM:Large Language Model  大语言模型

bert:只用到transformer的encode部分

BERT(Bidirectional Encoder Representations from Transformers)是一种预训练的语言模型,它使用了Transformer的Encoder结构。BERT的主要目标是通过双向上下文建模来生成丰富的词汇表示。它通过遮蔽一部分输入标记(Masked Language Model,MLM)来预测缺失的单词,从而使模型学习到双向上下文信息。

gpt:只用transformer的decoder部分

GPT(Generative Pre-trained Transformer)则使用了Transformer的Decoder结构。GPT的主要任务是通过自回归的方式生成文本序列。它通过训练模型预测下一个标记,从而学到了语言的概率分布。GPT是一个生成模型,它可以生成连贯的文本序列,而不仅仅是学习词汇表示。

 代码具体总结:

 Bert模型结构 详细解释与transformer中encoder的区别之处&源代码 - 知乎 (zhihu.com)

bert-pytorch版源码详细解读_bert pytorch源码-CSDN博客

整体Bert一个结构:

由两部分构成:BertEmbeddingBertEncoder

BertEncoder层实质上就是由多个(num_hidden_layers)BertLayer层堆叠而成。

(1) BertEmbedding= token emb + position emb + segment emb三部分构成

(2) (encoder)BertLayer= attention+intermediate+output 三部分构成

其中,encoder部分比传统encoder多出了BertIntermediate 和 BertOutput两部分(BERTIntermediate和BERTOutput2个全连接层)

目的在于:

BertIntermediate是Bert模型中的一个中间层,它负责将输入通过全连接层进行映射,并应用激活函数(通常是GELU激活函数)。这个非线性映射引入了更丰富的特征表示能力,使得Bert模型能够学习到更复杂的语义信息。BertIntermediate充当了encoder层中的非线性变换,帮助模型更好地捕捉输入序列中的上下文关系和语义信息

BertOutput是Bert模型中的一个输出层,它接收BertIntermediate层的输出,并通过全连接层进行线性映射,将特征维度映射回原始维度。此外,BertOutput还通过残差连接(residual connection)和层归一化(layer normalization)技术,将BertIntermediate层的输出与输入进行相加和归一化,从而保留了输入的信息,并且有助于缓解梯度消失问题。这种残差连接和层归一化技术有助于提高模型的训练稳定性,使得Bert模型更容易训练并且能够更好地捕捉输入序列中的语义信息。

其出发点在于:

激活函数本身可以引入非线性变换。BertIntermediate的作用主要是在Bert模型的每个encoder层中引入更多的非线性变换,从而增加模型的表征能力。虽然激活函数(如GELU)可以引入一定的非线性,但是在Bert模型中,BertIntermediate通过全连接层在输入特征上进行映射,从而引入更加复杂的非线性变换。这有助于Bert模型更好地学习到输入序列中的复杂语义信息,提升模型的表示能力。

此外,BertIntermediate还可以控制Bert模型的表示空间的维度。Bert模型通常使用较大的隐藏层维度,例如768维或1024维,但是在某些任务中,可能需要将表示空间的维度减小到较小的维度,例如100维或200维,以便适应任务的特定需求。BertIntermediate可以通过全连接层将隐藏层维度映射到目标维度,从而灵活地调整模型的表示空间维度。

也就是在高维(非线性更强)的空间里进行操作,之后再映射回去

模型的训练loss采用两个下游任务,图示:

 主要代码

1.主函数入口
  1. class BertModel(nn.Module):
  2. def __init__(self, config: BertConfig):
  3. super(BertModel, self).__init__()
  4. self.embeddings = BERTEmbeddings(config)
  5. self.encoder = BERTEncoder(config)
  6. self.pooler = BERTPooler(config)
  7. def forward(self, input_ids, token_type_ids=None, attention_mask=None):
  8. if attention_mask is None:
  9. attention_mask = torch.ones_like(input_ids)
  10. if token_type_ids is None:
  11. token_type_ids = torch.zeros_like(input_ids)
  12. # attention_mask的维度应保持和多头的hidden_states一致
  13. #!!!个人感觉这里extended_attention_mask 还应该扩展一下,感觉这个维度不太对!
  14. extended_attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
  15. extended_attention_mask = extended_attention_mask.float()
  16. # mask部分token的权重直接给-10000,使其在self-att的时候基本不起作用。
  17. extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0
  18. #根据input_ids, token_type_ids以及position_ids来确定初始embeddings
  19. embedding_output = self.embeddings(input_ids, token_type_ids)
  20. #核心层,由以多层self_attention为主的神经网络构成
  21. all_encoder_layers = self.encoder(embedding_output, extended_attention_mask)
  22. #最后一层隐藏层
  23. sequence_output = all_encoder_layers[-1]
  24. #取出最后一层隐藏层的[cls]的表征,经过网络层(self.pooler)后得到pooled_output
  25. pooled_output = self.pooler(sequence_output)
  26. return all_encoder_layers, pooled_output
2.BertEmbedding层
  1. class BERTEmbeddings(nn.Module):
  2. def __init__(self, config):
  3. super(BERTEmbeddings, self).__init__()
  4. self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size)
  5. self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
  6. self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.hidden_size)
  7. self.LayerNorm = BERTLayerNorm(config)
  8. self.dropout = nn.Dropout(config.hidden_dropout_prob)
  9. def forward(self, input_ids, token_type_ids=None):
  10. #根据每个token的位置生成position_ids,很直观
  11. seq_length = input_ids.size(1)
  12. position_ids = torch.arange(seq_length, dtype=torch.long, device=input_ids.device)
  13. position_ids = position_ids.unsqueeze(0).expand_as(input_ids)
  14. if token_type_ids is None:
  15. token_type_ids = torch.zeros_like(input_ids)
  16. #这三个embeddings相信大家可以参见下图就一目了然了
  17. words_embeddings = self.word_embeddings(input_ids)
  18. position_embeddings = self.position_embeddings(position_ids)
  19. token_type_embeddings = self.token_type_embeddings(token_type_ids)
  20. embeddings = words_embeddings + position_embeddings + token_type_embeddings
  21. #最后过一个layerNorm和dropout层
  22. embeddings = self.LayerNorm(embeddings)
  23. embeddings = self.dropout(embeddings)
  24. return embeddings
3.BertEnocder层
  1. class BERTEncoder(nn.Module):
  2. def __init__(self, config):
  3. super(BERTEncoder, self).__init__()
  4. layer = BERTLayer(config)
  5. self.layer = nn.ModuleList([copy.deepcopy(layer) for _ in range(config.num_hidden_layers)])
  6. def forward(self, hidden_states, attention_mask):
  7. all_encoder_layers = []
  8. for layer_module in self.layer:
  9. hidden_states = layer_module(hidden_states, attention_mask)
  10. all_encoder_layers.append(hidden_states)
  11. return all_encoder_layers
  12. class BERTLayer(nn.Module):
  13. def __init__(self, config):
  14. super(BERTLayer, self).__init__()
  15. self.attention = BERTAttention(config)
  16. self.intermediate = BERTIntermediate(config)
  17. self.output = BERTOutput(config)
  18. def forward(self, hidden_states, attention_mask):
  19. attention_output = self.attention(hidden_states, attention_mask)
  20. intermediate_output = self.intermediate(attention_output)
  21. layer_output = self.output(intermediate_output, attention_output)
  22. return layer_output
3.1BertTAttention
  1. class BERTAttention(nn.Module):
  2. def __init__(self, config):
  3. super(BERTAttention, self).__init__()
  4. self.self = BERTSelfAttention(config)
  5. self.output = BERTSelfOutput(config)
  6. def forward(self, input_tensor, attention_mask):
  7. self_output = self.self(input_tensor, attention_mask)
  8. attention_output = self.output(self_output, input_tensor)
  9. return attention_output
  10. class BERTSelfAttention(nn.Module):
  11. def __init__(self, config):
  12. super(BERTSelfAttention, self).__init__()
  13. if config.hidden_size % config.num_attention_heads != 0:
  14. raise ValueError(
  15. "The hidden size (%d) is not a multiple of the number of attention"
  16. "heads (%d)" % (config.hidden_size, config.num_attention_heads))
  17. self.num_attention_heads = config.num_attention_heads#多头self_attention
  18. self.attention_head_size = int(config.hidden_size /config.num_attention_heads)#每个头的维度,一般是768/12=64
  19. self.all_head_size = self.num_attention_heads * self.attention_head_size
  20. self.query = nn.Linear(config.hidden_size, self.all_head_size)
  21. self.key = nn.Linear(config.hidden_size, self.all_head_size)
  22. self.value = nn.Linear(config.hidden_size, self.all_head_size)
  23. self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
  24. def transpose_for_scores(self, x):
  25. new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
  26. x = x.view(*new_x_shape)
  27. return x.permute(0, 2, 1, 3)
  28. def forward(self, hidden_states, attention_mask):
  29. #经典生成QKV
  30. #(batch_size, max_sen_length, hidden_size)->(batch_size, max_sen_length, hidden_size)
  31. #(8, 512, 768)->(8, 512, 768)
  32. mixed_query_layer = self.query(hidden_states)
  33. mixed_key_layer = self.key(hidden_states)
  34. mixed_value_layer = self.value(hidden_states)
  35. #改变维度,形成多头,记住是在生成QKV之后才干的事
  36. #(batch_size, max_sen_length, hidden_size)->(batch_size, num_attention_heads, max_sen_length, attention_head_size)
  37. #(8, 512, 768)->(8, 12, 512, 64)
  38. query_layer = self.transpose_for_scores(mixed_query_layer)
  39. key_layer = self.transpose_for_scores(mixed_key_layer)
  40. value_layer = self.transpose_for_scores(mixed_value_layer)
  41. #QK tensor相乘,只对最后两维做矩阵乘法
  42. #(batch_size, num_attention_heads, max_sen_length, attention_head_size)*(batch_size, num_attention_heads, attention_head_size, max_sen_length)->(batch_size, num_attention_heads, max_sen_length, max_sen_length)
  43. #(8, 12, 512, 64)*(8, 12, 64, 512)->(8, 12, 512, 512)
  44. attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))
  45. #除以维度的开方,这是为了使QV的结果方差变为1,使得sortmax后不会发生梯度消失。
  46. attention_scores = attention_scores / math.sqrt(self.attention_head_size)
  47. #之前传的attention_mask在此刻发挥它的作用了!把mask掉的词的“权重”变成-10000,softmax后就基本等于0
  48. attention_scores = attention_scores + attention_mask
  49. # softmax加一个dropout, 这也没啥好说的
  50. attention_probs = nn.Softmax(dim=-1)(attention_scores)
  51. attention_probs = self.dropout(attention_probs)
  52. # 最后再和V相乘,至此就完成了经典的softmax(QK/sqrt(dk))*V的操作!
  53. #(8, 12, 512, 512)*(8, 12, 512, 64)->(8, 12, 512, 64)
  54. context_layer = torch.matmul(attention_probs, value_layer)
  55. #之后就是把维度进行还原
  56. #(8, 12, 512, 64)->(8, 512,12 ,64)->(8, 512, 768)
  57. context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
  58. new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
  59. context_layer = context_layer.view(*new_context_layer_shape)
  60. return context_layer
  61. class BERTSelfOutput(nn.Module):
  62. def __init__(self, config):
  63. super(BERTSelfOutput, self).__init__()
  64. self.dense = nn.Linear(config.hidden_size, config.hidden_size)
  65. self.LayerNorm = BERTLayerNorm(config)
  66. self.dropout = nn.Dropout(config.hidden_dropout_prob)
  67. def forward(self, hidden_states, input_tensor):
  68. #很平淡的全连接层加上dropout和LayerNorm
  69. hidden_states = self.dense(hidden_states)
  70. hidden_states = self.dropout(hidden_states)
  71. hidden_states = self.LayerNorm(hidden_states + input_tensor)
  72. return hidden_states
3.2 BertIntermediate&& BertOutput
  1. class BERTIntermediate(nn.Module):
  2. def __init__(self, config):
  3. super(BERTIntermediate, self).__init__()
  4. #之前一直不清楚这个intermediate_size是干嘛的,原来是self_attention后还跟了BERTIntermediate和BERTOutput2个全连接层。
  5. self.dense = nn.Linear(config.hidden_size, config.intermediate_size)
  6. self.intermediate_act_fn = gelu
  7. def forward(self, hidden_states):
  8. hidden_states = self.dense(hidden_states)
  9. hidden_states = self.intermediate_act_fn(hidden_states)
  10. return hidden_states
  11. class BERTOutput(nn.Module):
  12. def __init__(self, config):
  13. super(BERTOutput, self).__init__()
  14. self.dense = nn.Linear(config.intermediate_size, config.hidden_size)
  15. self.LayerNorm = BERTLayerNorm(config)
  16. self.dropout = nn.Dropout(config.hidden_dropout_prob)
  17. def forward(self, hidden_states, input_tensor):
  18. hidden_states = self.dense(hidden_states)
  19. hidden_states = self.dropout(hidden_states)
  20. hidden_states = self.LayerNorm(hidden_states + input_tensor)
  21. return hidden_states

!!!这个和我之前看的transformers的残差连接层差别还挺大的,所以并不完全和transformers的encoder部分结构一致。
这之后就是主函数里的几步骤收尾工作了,这里也不再赘述。

4.补充

下面补充一下中途涉及到的相关类(LayerNorm)的代码

4.1 BertLayerNorm
  1. class BERTLayerNorm(nn.Module):
  2. def __init__(self, config, variance_epsilon=1e-12):
  3. """Construct a layernorm module in the TF style (epsilon inside the square root).
  4. """
  5. super(BERTLayerNorm, self).__init__()
  6. self.gamma = nn.Parameter(torch.ones(config.hidden_size))
  7. self.beta = nn.Parameter(torch.zeros(config.hidden_size))
  8. self.variance_epsilon = variance_epsilon
  9. def forward(self, x):
  10. u = x.mean(-1, keepdim=True)
  11. s = (x - u).pow(2).mean(-1, keepdim=True)
  12. x = (x - u) / torch.sqrt(s + self.variance_epsilon)
  13. return self.gamma * x + self.beta

1.batchNorm是对多个样本进行标准化,而layerNorm是对单样本标准化。
2.BertLayerNorm除了标准化以外还加上了gamma和beta的变化。

4.2 BertPooler
  1. class BERTPooler(nn.Module):
  2. def __init__(self, config):
  3. super(BERTPooler, self).__init__()
  4. self.dense = nn.Linear(config.hidden_size, config.hidden_size)
  5. self.activation = nn.Tanh()
  6. def forward(self, hidden_states):
  7. #取出[cls]后过一个全连接层和激活函数。
  8. first_token_tensor = hidden_states[:, 0]
  9. pooled_output = self.dense(first_token_tensor)
  10. pooled_output = self.activation(pooled_output)
  11. return pooled_output

上文也提到了,BertPooler就是专门为[cls]设计的

4.3 gelu
  1. def gelu(x):
  2. """Implementation of the gelu activation function.
  3. """
  4. return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0)))
4.4 transpose_for_scores
  1. def transpose_for_scores(self, x):
  2. new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
  3. x = x.view(*new_x_shape)
  4. return x.permute(0, 2, 1, 3)

总结

到此基本就结束了,整体流程看下来其实很快,关键是理清里面每一步的维度的变换和几个核心的类就行。希望能对大家有所帮助。
代码参考来自于:https://github.com/DA-southampton/Read_Bert_Code

博客参考:

 bert-pytorch版源码详细解读_bert pytorch源码-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/763244
推荐阅读
相关标签
  

闽ICP备14008679号