自注意力机制------Transformer详细介绍

发布时间:2026/11/4 2:30:00
自注意力机制------Transformer详细介绍 Transformer 是一种完全基于注意力机制attention构建的神经网络架构由编码器和解码器两部分组成编码器负责把输入序列的每个词元映射为融合全局上下文的表示解码器则在此基础上逐步生成输出序列。它最核心的创新是用自注意力取代了 RNN 的循环结构使序列中任意两个位置都能直接建立联系从而既捕捉长距离依赖又让整个计算可以高度并行训练效率远超传统循环模型。凭借这一设计Transformer 成为 GPT、BERT 等大模型的基础架构也奠定了当今自然语言处理乃至多模态领域的主流范式。看着很复杂但是原理其实很简单我们详细介绍。一.自注意力机制和位置编码1.1自注意力机制自注意力机制self-attention是让序列中每个位置都能“看”到同一序列里所有其他位置、并根据相关性动态分配权重来聚合信息的一种计算方式。它把每个词元分别线性变换成查询Query、键Key、值Value三个向量用当前词的 Q 与所有词的 K 做点积并除以 √d 缩放、经 softmax 归一化得到注意力权重再用这些权重对所有词的 V 加权求和作为该位置的新表示——这样一来每个词的表示都融合了全局上下文且所有位置的计算可完全并行不像 RNN 那样必须逐步循环同时通过 mask 还能灵活控制“哪些位置可见”如解码器里遮住未来词元是 Transformer 的核心组件。1.2位置编码在处理词元序列时循环神经网络是逐个的重复地处理词元的 而自注意力则因为并行计算而放弃了顺序操作。为了使用序列的顺序信息通过在输入表示中添加位置编码positional encoding来注入绝对的或相对的位置信息。位置编码可以通过学习得到也可以直接固定得到。这里我们介绍基于正弦函数和余弦函数的固定位置编码#save class PositionalEncoding(nn.Module): 位置编码 def __init__(self, num_hiddens, dropout, max_len1000): super(PositionalEncoding, self).__init__() self.dropout nn.Dropout(dropout) # 创建一个足够长的P self.P torch.zeros((1, max_len, num_hiddens)) X torch.arange(max_len, dtypetorch.float32).reshape( -1, 1) / torch.pow(10000, torch.arange( 0, num_hiddens, 2, dtypetorch.float32) / num_hiddens) self.P[:, :, 0::2] torch.sin(X) self.P[:, :, 1::2] torch.cos(X) def forward(self, X): X X self.P[:, :X.shape[1], :].to(X.device) return self.dropout(X)除了捕获绝对位置信息之外上述的位置编码还允许模型学习得到输入序列中相对位置信息。这是因为对于任何确定的位置偏移a位置处于i的位置编码可以线性投影位置处ia的位置编码来表示。利用正弦和余弦函数的数学特性模型可以通过一个固定的线性变换矩阵乘法极其轻松地计算出两个词之间的“相对距离”。二.Transformer架构左右两边分别是编码器和解码器的俩个块transformer由多个相同的块叠加而成。2.1逐位前馈网络#save class PositionWiseFFN(nn.Module): 基于位置的前馈网络 def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs, **kwargs): super(PositionWiseFFN, self).__init__(**kwargs) self.dense1 nn.Linear(ffn_num_input, ffn_num_hiddens) self.relu nn.ReLU() self.dense2 nn.Linear(ffn_num_hiddens, ffn_num_outputs) def forward(self, X): return self.dense2(self.relu(self.dense1(X)))2.2残差链接和规范化注意层规范化和批量规范化的区别#save class AddNorm(nn.Module): 残差连接后进行层规范化 def __init__(self, normalized_shape, dropout, **kwargs): super(AddNorm, self).__init__(**kwargs) self.dropout nn.Dropout(dropout) self.ln nn.LayerNorm(normalized_shape) def forward(self, X, Y): return self.ln(self.dropout(Y) X)2.3编码器编码器由多个块组成每个块代码如下#save class EncoderBlock(nn.Module): Transformer编码器块 def __init__(self, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, use_biasFalse, **kwargs): super(EncoderBlock, self).__init__(**kwargs) self.attention d2l.MultiHeadAttention( key_size, query_size, value_size, num_hiddens, num_heads, dropout, use_bias) self.addnorm1 AddNorm(norm_shape, dropout) self.ffn PositionWiseFFN( ffn_num_input, ffn_num_hiddens, num_hiddens) self.addnorm2 AddNorm(norm_shape, dropout) def forward(self, X, valid_lens): Y self.addnorm1(X, self.attention(X, X, X, valid_lens)) return self.addnorm2(Y, self.ffn(Y))整体代码如下#save class TransformerEncoder(d2l.Encoder): Transformer编码器 def __init__(self, vocab_size, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout, use_biasFalse, **kwargs): super(TransformerEncoder, self).__init__(**kwargs) self.num_hiddens num_hiddens self.embedding nn.Embedding(vocab_size, num_hiddens) self.pos_encoding d2l.PositionalEncoding(num_hiddens, dropout) self.blks nn.Sequential() for i in range(num_layers): self.blks.add_module(blockstr(i), EncoderBlock(key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, use_bias)) def forward(self, X, valid_lens, *args):# 因为位置编码值在-1和1之间 # 因此嵌入值乘以嵌入维度的平方根进行缩放 # 然后再与位置编码相加。X self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) self.attention_weights [None] * len(self.blks) for i, blk in enumerate(self.blks): X blk(X, valid_lens) self.attention_weights[ i] blk.attention.attention.attention_weights return X2.4解码器块代码class DecoderBlock(nn.Module): 解码器中第i个块 def __init__(self, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, i, **kwargs): super(DecoderBlock, self).__init__(**kwargs) self.i i self.attention1 d2l.MultiHeadAttention( key_size, query_size, value_size, num_hiddens, num_heads, dropout) self.addnorm1 AddNorm(norm_shape, dropout) self.attention2 d2l.MultiHeadAttention( key_size, query_size, value_size, num_hiddens, num_heads, dropout) self.addnorm2 AddNorm(norm_shape, dropout) self.ffn PositionWiseFFN(ffn_num_input, ffn_num_hiddens, num_hiddens) self.addnorm3 AddNorm(norm_shape, dropout) def forward(self, X, state): enc_outputs, enc_valid_lens state[0], state[1] # 训练阶段输出序列的所有词元都在同一时间处理 # 因此state[2][self.i]初始化为None。 # 预测阶段输出序列是通过词元一个接着一个解码的 # 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表示 if state[2][self.i] is None: key_values X else: key_values torch.cat((state[2][self.i], X), axis1) state[2][self.i] key_values if self.training: batch_size, num_steps, _ X.shape # dec_valid_lens的开头:(batch_size,num_steps), # 其中每一行是[1,2,...,num_steps] dec_valid_lens torch.arange( 1, num_steps 1, deviceX.device).repeat(batch_size, 1) else: dec_valid_lens None # 自注意力 X2 self.attention1(X, key_values, key_values, dec_valid_lens) Y self.addnorm1(X, X2) # 编码器解码器注意力。 # enc_outputs的开头:(batch_size,num_steps,num_hiddens) Y2 self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens) Z self.addnorm2(Y, Y2) return self.addnorm3(Z, self.ffn(Z)), state这里介绍一下state:要关注的是有俩个注意力层底下的自注意力层上面的是用的解码器输出状态的注意力层。对于自注意力层情况主要分为预测和训练预测时自注意力层qx,key,valuex和已经生成的序列。训练时qx, key和 valuex和当前时间步能看到的序列。所以训练是有效长度要单独处理预测时有效长度为none就可以。用的解码器输出状态的注意力层qy(自注意力层输出后的残差连接和规范化)key,value编码器输出状态。X (B, T, H) ──────────────────────────────┐│ │(残差)│ │▼ │┌──────────────────────────────────────────┐ ││ attention1 QX (B,T,H) │ ││ KVkey_values (B,T,H) │ ││ maskdec_valid_lens (B,T) │ ││ 因果掩码: [1,2,...,T] │ │└──────────────────────────────────────────┘ ││ X2 (B, T, H) │▼ │┌─────────┐ ││AddNorm1 │ ◄─────────────────────────────┘└─────────┘│ Y (B, T, H) ───────────────────┐│ │(残差)│ │▼ │┌──────────────────────────────────────────┐ ││ attention2 QY (B,T,H) │ ││ KVenc_outputs (B,S,H) │ ││ maskenc_valid_lens (B,) │ ││ padding 掩码 │ │└──────────────────────────────────────────┘ ││ Y2 (B, T, H) │▼ │┌─────────┐ ││AddNorm2 │ ◄──────────────────────────┘└─────────┘│ Z (B, T, H) ─────────────────┐│ │(残差)▼ │┌─────────┐ ││ FFN │ 逐位置全连接 │└─────────┘ ││ ffn(Z) (B, T, H) │▼ │┌─────────┐ ││AddNorm3 │ ◄────────────────────────┘└─────────┘│▼输出 (B, T, H) statestate[2][i] 仍为 X训练时无缓存增长整体代码class TransformerDecoder(d2l.AttentionDecoder): def __init__(self, vocab_size, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout, **kwargs): super(TransformerDecoder, self).__init__(**kwargs) self.num_hiddens num_hiddens self.num_layers num_layers self.embedding nn.Embedding(vocab_size, num_hiddens) self.pos_encoding d2l.PositionalEncoding(num_hiddens, dropout) self.blks nn.Sequential() for i in range(num_layers): self.blks.add_module(blockstr(i), DecoderBlock(key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, i)) self.dense nn.Linear(num_hiddens, vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): return [enc_outputs, enc_valid_lens, [None] * self.num_layers] def forward(self, X, state): X self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) self._attention_weights [[None] * len(self.blks) for _ in range (2)] for i, blk in enumerate(self.blks): X, state blk(X, state) # 解码器自注意力权重 self._attention_weights[0][ i] blk.attention1.attention.attention_weights # “编码器解码器”自注意力权重 self._attention_weights[1][ i] blk.attention2.attention.attention_weights return self.dense(X), state property def attention_weights(self): return self._attention_weights这里初始化代码def init_state(self, enc_outputs, enc_valid_lens, *args): return [enc_outputs, enc_valid_lens, [None] * self.num_layers]

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询