python网站开发流程图,微信小程序 开发教程,上海市云企业有限公司,求职网站网页设计前言#xff1a;本篇文章主要从代码实现角度研究 Bert Encoder和Transformer Encoder 有什么不同#xff1f;应该可以帮助你#xff1a; 深入了解Bert Encoder 的结构实现深入了解Transformer Encoder的结构实现 本篇文章不涉及对注意力机制实现的代码研究。 注#xff1a;… 前言本篇文章主要从代码实现角度研究 Bert Encoder和Transformer Encoder 有什么不同应该可以帮助你 深入了解Bert Encoder 的结构实现深入了解Transformer Encoder的结构实现 本篇文章不涉及对注意力机制实现的代码研究。 注本篇文章所得出的结论和其它文章略有不同有可能是本人代码理解上存在问题但是又没有找到更多的文章加以验证并且代码也检查过多遍。 观点不太一致的文章bert-pytorch版源码详细解读_bert pytorch源码-CSDN博客 这篇文章中存在 “这个和我之前看的transformers的残差连接层差别还挺大的所以并不完全和transformers的encoder部分结构一致。” 但是我的分析是代码实现上不太一样但是本质上没啥不同只是Bert Encoder在Attention之后多了一层Linear。具体分析过程和结论可以阅读如下文章。 如有错误或问题请在评论区回复。 1、研究目标
这里主要的观察对象是BertModel中Bert Encoder是如何构造的从Bert Tensorflow源码以及transformers库中源码去看。
然后再看TransformerEncoder是如何构造的从pytorch内置的transformer模块去看。
最后再对比不同。
2、tensorflow中BertModel主要代码如下
class BertModel(object):def __init__(...):...得到了self.embedding_output以及attention_mask# transformer_model就代表了Bert Encoder层的所有操作self.all_encoder_layers transformer_model(input_tensorself.embedding_output, attention_maskattention_mask,...)# 这里all_encoder_layers[-1]是取最后一层encoder的输出self.sequence_output self.all_encoder_layers[-1]...pooler层对 sequence_output中的first_token_tensor即CLS对应的表示向量进行densetanh操作with tf.variable_scope(pooler):first_token_tensor tf.squeeze(self.sequence_output[:, 0:1, :], axis1)self.pooled_output tf.layers.dense(first_token_tensor,config.hidden_size,activationtf.tanh,kernel_initializercreate_initializer(config.initializer_range))def transformer_model(input_tensor, attention_maskNone,...):...for layer_idx in range(num_hidden_layers):# 如下123就是每一层Bert Encoder包含的结构和操作with tf.variable_scope(layer_%d % layer_idx):# 1attention层主要包含两个操作获取attention_output对attention_output进行dense dropout layer_normwith tf.variable_scope(attention):# 1.1通过attention_layer获得 attention_outputattention_output# 1.2output层attention_output需要经过dense dropout layer_norm操作with tf.variable_scope(output):attention_output tf.layers.dense(attention_output,hidden_size,...)attention_output dropout(attention_output, hidden_dropout_prob)# “attention_output layer_input” 表示 残差连接操作attention_output layer_norm(attention_output layer_input)# 2intermediate中间层对attention_output进行dense激活(GELU)with tf.variable_scope(intermediate):intermediate_output tf.layers.dense(attention_output,intermediate_size,activationintermediate_act_fn,)# 3output层对intermediater_out进行dense dropout layer_normwith tf.variable_scope(output):layer_output tf.layers.dense(intermediate_output,hidden_size,kernel_initializercreate_initializer(initializer_range))layer_output dropout(layer_output, hidden_dropout_prob)# layer_output attention_output是残差连接操作layer_output layer_norm(layer_output attention_output)all_layer_outputs.append(layer_output)
3、pytorch的transformers库中的BertModel主要代码
其中BertEncoder对应要研究的目标
class BertModel(BertPreTrainedModel):def __init__(self, config, add_pooling_layerTrue):self.embeddings BertEmbeddings(config)self.encoder BertEncoder(config)self.pooler BertPooler(config) if add_pooling_layer else Nonedef forward(...):# 这是嵌入层操作embedding_output self.embeddings(input_idsinput_ids,position_idsposition_ids,token_type_idstoken_type_ids,...)# 这是BertEncoder层的操作encoder_outputs self.encoder(embedding_output,attention_maskextended_attention_mask,...)# 这里encoder_outputs是一个对象encoder_outputs[0]是指最后一层Encoder(BertLayer)输出sequence_output encoder_outputs[0]# self.pooler操作是BertPooler层操作是先取first_token_tensor即CLS对应的表示向量然后进行densetanh操作# 通常pooled_output用于做下游分类任务pooled_output self.pooler(sequence_output) if self.pooler is not None else Noneclass BertEncoder(nn.Module):def __init__(self, config):...self.layer nn.ModuleList([BertLayer(config) for _ in range(config.num_hidden_layers)])...def forward(...):for i, layer_module in enumerate(self.layer):# 元组的append做法将每一层的hidden_states保存到all_hidden_states# 第一个hidden_states是BertEncoder的输入后面的都是每一个BertLayer的输出if output_hidden_states:all_hidden_states all_hidden_states (hidden_states,)...# 执行BertLayer的forward方法包含BertAttention层 BertIntermediate中间层 BertOutput层layer_outputs layer_module(...)# 当前BertLayer的输出hidden_states layer_outputs[0]# 添加到all_hidden_states元组中if output_hidden_states:all_hidden_states all_hidden_states (hidden_states,)class BertLayer(nn.Module):def __init__(self, config):self.attention BertAttention(config)self.intermediate BertIntermediate(config)self.output BertOutput(config)def forward(...):# 1Attention是指BertAttention# BertAttention包含BertSelfAttention BertSelfOutput# BertSelfAttention包括计算AttentionDropout# BertSelfOutput包含densedropoutLayerNormLayerNorm之前会进行残差连接self_attention_outputs self.attention(...)# self_attention_outputs是一个元组取[0]获取当前BertLayer中的Attention层的输出attention_output self_attention_outputs[0]# 2BertIntermediate中间层包含densegelu激活# 3BertOutput层包含densedropoutLayerNormLayerNorm之前会进行残差连接# feed_forward_chunk的操作是BertIntermediate(attention_output) BertOutput(intermediate_output, attention_output)# BertIntermediate(attention_output)是densegelu激活# BertOutput(intermediate_output, attention_output)是densedropoutLayerNorm# 其中LayerNorm(intermediate_output attention_output)中的“intermediate_output attention_output”是残差连接操作layer_output apply_chunking_to_forward(self.feed_forward_chunk, ..., attention_output)
4、pytorch中内置的transformer的TransformerEncoderLayer主要代码
torch.nn.modules.transformer.TransformerEncoderLayer
class TransformerEncoderLayer(Module):Args:d_model: the number of expected features in the input (required).nhead: the number of heads in the multiheadattention models (required).dim_feedforward: the dimension of the feedforward network model (default2048).dropout: the dropout value (default0.1).activation: the activation function of intermediate layer, relu or gelu (defaultrelu).Examples:: encoder_layer nn.TransformerEncoderLayer(d_model512, nhead8) src torch.rand(10, 32, 512) out encoder_layer(src)def __init__(self, d_model, nhead, dim_feedforward2048, dropout0.1, activationrelu):super(TransformerEncoderLayer, self).__init__()self.self_attn MultiheadAttention(d_model, nhead, dropoutdropout)# Implementation of Feedforward modelself.linear1 Linear(d_model, dim_feedforward)self.dropout Dropout(dropout)self.linear2 Linear(dim_feedforward, d_model)self.norm1 LayerNorm(d_model)self.norm2 LayerNorm(d_model)self.dropout1 Dropout(dropout)self.dropout2 Dropout(dropout)self.activation _get_activation_fn(activation)def forward(...):# 过程# 1MultiheadAttention操作src2 self.self_attn# 2Dropout操作self.dropout1(src2)# 3残差连接src src self.dropout1(src2)# 4LayerNorm操作src self.norm1(src)# 如下是FeedForword做两次线性变换为了更深入的提取特征# 5Linear操作src self.linear1(src)# 6RELU激活默认RELU操作self.activation(self.linear1(src))# 7Dropout操作self.dropout(self.activation(self.linear1(src)))# 8Linear操作src2 self.linear2(...)# 9Dropout操作self.dropout2(src2)# 10残差连接src src self.dropout2(src2)# 11LayerNorm操作src self.norm2(src)src2 self.self_attn(src, src, src, attn_masksrc_mask,key_padding_masksrc_key_padding_mask)[0]src src self.dropout1(src2)src self.norm1(src)src2 self.linear2(self.dropout(self.activation(self.linear1(src))))src src self.dropout2(src2)src self.norm2(src)return src
5、区别总结 Transformer Encoder的结构如上图所示代码也基本和上图描述的一致不过代码中在Multi-Head Attention和Feed Forward之后都存在一个Dropout操作。可以认为每层网络之后都会接一个Dropout层是作为网络模块的一部分 可以将Transformer Encoder过程表述为 1MultiheadAttention Dropout 残差连接 LayerNorm 2FeedForwordLinear RELU Dropout Linear Dropout 残差连接 LayerNormTransformer默认的隐含层激活函数是RELU 可以将 Bert Encoder过程表述为 1BertSelfAttention MultiheadAttention Dropout 2BertSelfOutputLinear Dropout 残差连接 LayerNorm 注意这里的残差连接是作用在BertSelfAttention的输入上不是Linear的输入。 3BertIntermediateLinear GELU激活 4BertOutputLinear Dropout 残差连接 LayerNorm注意这里的残差连接是作用在BertIntermediate的输入上不是Linear的输入 进一步把12合并34合并 1MultiheadAttention Dropout Linear Dropout 残差连接 LayerNorm 2FeedForwordLinear GELU激活 Linear Dropout 残差连接 LayerNormBert默认的隐含层激活函数是GELU 所以Bert Encoder和Transformer Encoder最大的区别是Bert Encoder在做完Attention计算后还会用一个线性层去提取特征然后才进行残差连接。其次是FeedForword中的默认激活函数不同。Bert Encoder图结构如下 Bert 为什么要这么做或许是多一个线性层特征提取能力更强模型表征能力更好。
GELU和RELUGELU是RELU的改进版效果更好。 Reference
GeLU、ReLU函数学习_gelu和relu-CSDN博客