transformer模型介绍——大语言模型 LLMBook 学习(二)

devtools/2025/3/11 9:49:47/

transformer_0">1. transformer模型

1.1 注意力机制

**注意力机制(Attention Mechanism)**在人工智能中的应用,实际上是对人类认知系统中的注意力机制的一种模拟。它主要模仿了人类在处理信息时的选择性注意(Selective Attention),即我们不会平均分配注意力,而是会集中关注关键信息。
在这里插入图片描述

1.2 注意力机制原理

注意力机制是一种动态加权机制,用于在输入数据中选取最相关的信息,并赋予不同的权重。它最初用于神经机器翻译(Neural Machine Translation, NMT),后来广泛应用于自然语言处理(NLP)、计算机视觉(CV)和异常检测等任务。


1. 注意力计算流程

如图所示,注意力机制主要包括 Query(查询)、Key(键)、Value(值) 三个核心概念,它们的计算过程分为三步
在这里插入图片描述

第一步:计算 Query 和 Key 之间的相似度
  • 我们用查询向量(Query,记作 q q q,去匹配多个键向量(Keys,记作 k j k_j kj,计算它们的相似度
  • 这里采用的是点积(Dot Product来度量相似度:
    e i j = q T k j e_{ij} = q^T k_j eij=qTkj
  • 其中:
    • q q q 是查询向量(query)
    • k j k_j kj是键向量(key)
    • e i j e_{ij} eij表示查询 q q q 与键 k j k_j kj的相似度得分

直观理解

  • 如果 q q q k j k_j kj 方向相似(即表示的信息相似),则它们的点积 e i j e_{ij} eij 会较大。
  • 反之,如果 q q q k j k_j kj 方向不同,则 e i j e_{ij} eij 会较小。
第二步:将相似度进行 Softmax 归一化
  • 由于点积计算出的相似度值 e i j e_{ij} eij 可能较大,因此需要进行Softmax 归一化,使其变成一个概率分布:
    a i j = exp ⁡ ( e i j ) ∑ j ′ exp ⁡ ( e i j ′ ) a_{ij} = \frac{\exp(e_{ij})}{\sum_{j'} \exp(e_{ij'})} aij=jexp(eij)exp(eij)
  • 这样可以确保所有注意力权重 a i j a_{ij} aij 的总和为 1,便于后续加权计算。

直观理解

  • Softmax 归一化后,最相关的 Key 对应的权重 a i j a_{ij} aij 会更大,而不相关的 Key 权重会接近 0。

第三步:加权求和 Value
  • 计算出的注意力权重 a i j a_{ij} aij作用于对应的 Value(值向量),最终得到输出:
    o i = ∑ j a i j v j o_i = \sum_{j} a_{ij} v_j oi=jaijvj
  • 其中:
    • v j v_j vj 是对应的值向量(value)
    • o i o_i oi 是最终的加权求和结果(即注意力输出)

直观理解

  • 这个过程类似于信息检索,即:
    • 如果某个 Key 与 Query 相似度高(即权重 a i j a_{ij} aij大),那么它的 Value( v j v_j vj) 会被重点关注
    • 最终的输出 o i o_i oi 是所有 Value 的加权平均

2. 直观理解

可以把注意力机制类比为搜索引擎

  • Query(查询):用户输入的搜索关键词(如 “Transformer 论文”)。
  • Keys(键):数据库中的所有文档标题(如 “Attention is All You Need”)。
  • Values(值):数据库中的所有文档内容(如 Transformer 论文的正文)。
  • 计算相似度:搜索引擎会计算用户输入的关键词与文档标题的相关性。
  • Softmax 归一化:搜索引擎会给每个文档一个相关性分数,并归一化为一个排名概率。
  • 加权求和:最终,搜索引擎会按照相关性高低,返回最相关的文档内容。

3. 在 Transformer 中的应用

Transformer 结构(如 BERT、GPT)中,
注意力机制被用于自注意力Self-Attention计算:

  • 输入是一个句子,每个单词都有一个 Query、Key、Value。
  • 计算 Query 与所有 Key 的相似度,确定哪些单词对当前单词最重要。
  • 对 Value 进行加权求和,生成新的单词表示。

示例

  • 句子:“The cat sat on the mat.”
  • 计算 “cat” 的注意力:
    • “cat” 可能关注 “The” 和 “sat” 的信息,而忽略 “mat”。
    • Transformer 通过注意力机制动态调整单词之间的关系,提高理解能力。

4. 计算复杂度
  • 传统 RNN 计算复杂度:( O(n) )(必须按顺序计算)
  • Transformer 注意力计算复杂度:( O(n^2) )(可以并行计算)

虽然 Transformer 的注意力计算复杂度较高,但由于可以并行计算,在实际应用中比 RNN 更高效。


1.3 编码器

在这里插入图片描述

1. 编码器的作用

编码器(Encoder)是 Transformer 结构的核心组件之一,它的主要作用是将输入数据转换为隐藏层特征,以便后续的解码器(Decoder)进一步处理或用于下游任务(如分类、翻译等)。

在 Transformer 结构中,编码器由 N 个堆叠的编码器层(Encoder Layers) 组成,每个编码器层都包含:

  • 多头注意力机制(Multi-Head Attention, MHA)
  • 前馈神经网络(Feed Forward Network, FFN)
  • 残差连接(Residual Connection)和层归一化(Layer Normalization)

2. 编码器的结构

编码器的计算流程如下:

(1)多头注意力(Multi-Head Attention, MHA)
  • 计算输入序列中每个词对其他词的注意力权重,提取全局依赖关系。
  • 采用多头注意力机制(多个注意力头并行计算),以学习不同的特征表示。
(2)残差连接 + 层归一化
  • 计算多头注意力的输出后,使用残差连接(Residual Connection):
    X l ′ = LayerNorm ( MHA ( X l − 1 ) + X l − 1 ) X'_l = \text{LayerNorm}(\text{MHA}(X_{l-1}) + X_{l-1}) Xl=LayerNorm(MHA(Xl1)+Xl1)
    其中:
    • X l − 1 X_{l-1} Xl1 是上一层的输出
    • MHA 是多头注意力
    • LayerNorm 是层归一化,防止梯度消失或梯度爆炸
(3)前馈神经网络(Feed Forward Network, FFN)
  • 由两个全连接层(MLP)组成:
    FFN ( X ′ ) = ReLU ( X ′ W 1 + b 1 ) W 2 + b 2 \text{FFN}(X') = \text{ReLU}(X' W_1 + b_1) W_2 + b_2 FFN(X)=ReLU(XW1+b1)W2+b2
  • 作用是增加非线性变换能力,提升模型的表达能力。
(4)再次残差连接 + 层归一化
  • 计算 FFN 输出后,再次应用残差连接:
    X l = LayerNorm ( FFN ( X l ′ ) + X l ′ ) X_l = \text{LayerNorm}(\text{FFN}(X'_l) + X'_l) Xl=LayerNorm(FFN(Xl)+Xl)
  • 这样可以稳定梯度传递,提高训练效果

3. 公式解析

从公式来看,编码器的计算流程可以分为两步:

  1. 多头注意力层(MHA)+ 残差连接 + 层归一化
    X l ′ = LayerNorm ( MHA ( X l − 1 ) + X l − 1 ) X'_l = \text{LayerNorm}(\text{MHA}(X_{l-1}) + X_{l-1}) Xl=LayerNorm(MHA(Xl1)+Xl1)
  2. 前馈神经网络(FFN)+ 残差连接 + 层归一化
    X l = LayerNorm ( FFN ( X l ′ ) + X l ′ ) X_l = \text{LayerNorm}(\text{FFN}(X'_l) + X'_l) Xl=LayerNorm(FFN(Xl)+Xl)

其中:

  • ( X_{l-1} ):表示编码器第 ( l-1 ) 层的输出
  • ( X’_l ):表示经过多头注意力后的中间结果
  • ( X_l ):表示编码器第 ( l ) 层的最终输出
  • LayerNorm:层归一化,稳定训练
  • MHA:多头注意力
  • FFN:前馈神经网络

4. 编码器的特点
  • 并行计算:不像 RNN 需要逐步处理序列,Transformer 编码器可以一次性处理整个输入序列,提高计算效率。
  • 长距离依赖建模:通过自注意力机制(Self-Attention),编码器可以捕捉远距离的词语关系。
  • 层归一化 + 残差连接:防止梯度消失,提高模型稳定性。
  • 多头注意力:增强模型的表达能力,让不同的注意力头关注不同的信息。

5. 代码实现

Transformer 编码器层(Encoder Layer)的 PyTorch 实现

import torch
import torch.nn as nnclass TransformerEncoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout=0.1):"""Transformer 编码器层参数:- d_model: 输入特征维度(如 512)- num_heads: 多头注意力的头数- d_ff: 前馈神经网络的隐藏层维度(一般是 4*d_model)- dropout: Dropout 概率"""super(TransformerEncoderLayer, self).__init__()# 多头注意力层self.self_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 前馈神经网络self.ffn = nn.Sequential(nn.Linear(d_model, d_ff),nn.ReLU(),nn.Linear(d_ff, d_model))# 层归一化self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)# Dropoutself.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)def forward(self, x):"""前向传播参数:- x: 输入张量 (batch_size, seq_len, d_model)返回:- 编码器层的输出 (batch_size, seq_len, d_model)"""# 多头注意力attn_output, _ = self.self_attn(x, x, x)x = self.norm1(x + self.dropout1(attn_output))  # 残差连接 + 层归一化# 前馈神经网络ffn_output = self.ffn(x)x = self.norm2(x + self.dropout2(ffn_output))  # 残差连接 + 层归一化return x# 测试编码器层
d_model = 512
num_heads = 8
d_ff = 2048
seq_len = 10
batch_size = 2encoder_layer = TransformerEncoderLayer(d_model, num_heads, d_ff)
input_tensor = torch.rand(batch_size, seq_len, d_model)  # 随机输入
output_tensor = encoder_layer(input_tensor)print("编码器层输出形状:", output_tensor.shape)  # (batch_size, seq_len, d_model)

组件作用
多头注意力(MHA)计算输入序列中不同位置的相关性,提取全局信息
前馈神经网络(FFN)增强特征表达能力
残差连接(Residual)防止梯度消失,提升训练稳定性
层归一化(LayerNorm)归一化激活值,稳定训练

编码器的关键作用是将输入转换为隐藏层特征,并通过层层堆叠提取更高级的语义信息。

1.4 解码器

1. 解码器的作用

在这里插入图片描述

解码器(Decoder)是 Transformer 结构的另一核心组件,它的主要作用是将编码器的隐藏层特征转换为输出序列(如翻译任务中的目标语言句子)。

在 Transformer 结构中,解码器由 N 个堆叠的解码器层(Decoder Layers) 组成,每个解码器层包含:

  • 掩码多头注意力(Masked Multi-Head Attention, Masked MHA)
  • 交叉多头注意力(Cross Multi-Head Attention, Cross MHA)
  • 前馈神经网络(Feed Forward Network, FFN)
  • 残差连接(Residual Connection)和层归一化(Layer Normalization)

2. 解码器的结构

解码器的计算流程如下:

(1)掩码多头注意力(Masked Multi-Head Attention, Masked MHA)
  • 作用:在生成序列时,模型不能看到未来的信息,因此需要掩码(Mask),确保解码器在预测第 t t t 个词时,只能利用 t t t 之前的词,而不能看到 t + 1 t+1 t+1 及之后的词。
  • 计算方式
    Y l ′ = LayerNorm ( MaskedMHA ( Y l − 1 ) + Y l − 1 ) Y'_l = \text{LayerNorm}(\text{MaskedMHA}(Y_{l-1}) + Y_{l-1}) Yl=LayerNorm(MaskedMHA(Yl1)+Yl1)
    • Y l − 1 Y_{l-1} Yl1 是解码器前一层的输出
    • Masked MHA 计算当前词与之前词的注意力
    • LayerNorm 进行层归一化

(2)交叉多头注意力(Cross Multi-Head Attention, Cross MHA)
  • 作用:在翻译任务中,解码器需要关注编码器的输出,以获取源语言的信息。
  • 计算方式
    Y l ′ ′ = LayerNorm ( CrossMHA ( Y l ′ , X L ) + Y l ′ ) Y''_l = \text{LayerNorm}(\text{CrossMHA}(Y'_l, X_L) + Y'_l) Yl′′=LayerNorm(CrossMHA(Yl,XL)+Yl)
    • Y l ′ Y'_l Yl 是掩码注意力的输出
    • X L X_L XL 是编码器的最终输出
    • Cross MHA 计算解码器的隐藏状态与编码器输出之间的注意力关系

(3)前馈神经网络(Feed Forward Network, FFN)
  • 作用:增强特征表达能力,提高模型的非线性变换能力。
  • 计算方式
    Y l = LayerNorm ( FFN ( Y l ′ ′ ) + Y l ′ ′ ) Y_l = \text{LayerNorm}(\text{FFN}(Y''_l) + Y''_l) Yl=LayerNorm(FFN(Yl′′)+Yl′′)
    • FFN 由两个全连接层(MLP)组成:
      FFN ( Y ′ ′ ) = ReLU ( Y ′ ′ W 1 + b 1 ) W 2 + b 2 \text{FFN}(Y'') = \text{ReLU}(Y'' W_1 + b_1) W_2 + b_2 FFN(Y′′)=ReLU(Y′′W1+b1)W2+b2
    • 采用 ReLU 激活函数,增强非线性表达能力

4. 计算解析

解码器的计算流程可以分为三步:

  1. 掩码多头注意力(Masked MHA)+ 残差连接 + 层归一化
    Y l ′ = LayerNorm ( MaskedMHA ( Y l − 1 ) + Y l − 1 ) Y'_l = \text{LayerNorm}(\text{MaskedMHA}(Y_{l-1}) + Y_{l-1}) Yl=LayerNorm(MaskedMHA(Yl1)+Yl1)
  2. 交叉多头注意力(Cross MHA)+ 残差连接 + 层归一化
    Y l ′ ′ = LayerNorm ( CrossMHA ( Y l ′ , X L ) + Y l ′ ) Y''_l = \text{LayerNorm}(\text{CrossMHA}(Y'_l, X_L) + Y'_l) Yl′′=LayerNorm(CrossMHA(Yl,XL)+Yl)
  3. 前馈神经网络(FFN)+ 残差连接 + 层归一化
    Y l = LayerNorm ( FFN ( Y l ′ ′ ) + Y l ′ ′ ) Y_l = \text{LayerNorm}(\text{FFN}(Y''_l) + Y''_l) Yl=LayerNorm(FFN(Yl′′)+Yl′′)

其中:

  • Y l − 1 Y_{l-1} Yl1 ):表示解码器第 l − 1 l-1 l1 层的输出
  • Y l ′ Y'_l Yl:表示掩码多头注意力后的中间结果
  • Y l ′ ′ Y''_l Yl′′:表示交叉多头注意力后的中间结果
  • Y l Y_l Yl:表示解码器第 l l l层的最终输出
  • X L X_L XL:编码器的最终输出
  • LayerNorm:层归一化,稳定训练
  • Masked MHA:掩码多头注意力(防止未来信息泄露)
  • Cross MHA:交叉多头注意力(关注编码器输出)
  • FFN:前馈神经网络

5. 编码器 vs. 解码器
组件编码器(Encoder)解码器(Decoder)
输入输入序列(如源语言)目标序列(如目标语言)
多头注意力标准多头注意力(Self-Attention)掩码多头注意力(Masked Self-Attention)
交叉注意力✅(关注编码器输出)
前馈神经网络
层归一化 & 残差连接

6. 代码实现

下面是 Transformer 解码器层(Decoder Layer)的 PyTorch 实现

import torch
import torch.nn as nnclass TransformerDecoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout=0.1):"""Transformer 解码器层参数:- d_model: 输入特征维度(如 512)- num_heads: 多头注意力的头数- d_ff: 前馈神经网络的隐藏层维度(一般是 4*d_model)- dropout: Dropout 概率"""super(TransformerDecoderLayer, self).__init__()# 掩码多头注意力self.masked_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 交叉多头注意力(关注编码器输出)self.cross_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 前馈神经网络self.ffn = nn.Sequential(nn.Linear(d_model, d_ff),nn.ReLU(),nn.Linear(d_ff, d_model))# 层归一化self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)self.norm3 = nn.LayerNorm(d_model)# Dropoutself.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)self.dropout3 = nn.Dropout(dropout)def forward(self, x, encoder_output):"""前向传播参数:- x: 解码器输入 (batch_size, seq_len, d_model)- encoder_output: 编码器输出 (batch_size, seq_len, d_model)返回:- 解码器层的输出 (batch_size, seq_len, d_model)"""# 掩码多头注意力masked_attn_output, _ = self.masked_attn(x, x, x)x = self.norm1(x + self.dropout1(masked_attn_output))  # 残差连接 + 层归一化# 交叉多头注意力cross_attn_output, _ = self.cross_attn(x, encoder_output, encoder_output)x = self.norm2(x + self.dropout2(cross_attn_output))  # 残差连接 + 层归一化# 前馈神经网络ffn_output = self.ffn(x)x = self.norm3(x + self.dropout3(ffn_output))  # 残差连接 + 层归一化return x# 测试解码器层
decoder_layer = TransformerDecoderLayer(d_model=512, num_heads=8, d_ff=2048)
input_tensor = torch.rand(2, 10, 512)  # 解码器输入
encoder_output = torch.rand(2, 10, 512)  # 编码器输出
output_tensor = decoder_layer(input_tensor, encoder_output)print("解码器层输出形状:", output_tensor.shape)  # (batch_size, seq_len, d_model)

解码器的核心在于:

  • 掩码多头注意力(Masked MHA):防止未来信息泄露
  • 交叉多头注意力(Cross MHA):关注编码器的输出
  • 前馈神经网络(FFN):增强特征表达能力

1.5 多头注意力层

在这里插入图片描述

多头注意力(Multi-Head Attention, MHA)是 Transformer 结构的核心组件之一,它能够让模型关注输入序列中不同位置的关系,并通过多个注意力头(heads)学习不同的特征表示


1. Scaled Dot-Product Attention(缩放点积注意力)

多头注意力的基础是 缩放点积注意力(Scaled Dot-Product Attention),其计算流程如下:

(1)输入映射到 Q、K、V

给定输入矩阵 X X X ,我们将其映射为:

  • 查询(Query, Q) Q = X W Q Q = X W^Q Q=XWQ
  • 键(Key, K) K = X W K K = X W^K K=XWK
  • 值(Value, V):$ V = X W^V$

其中:

  • W Q , W K , W V W^Q, W^K, W^V WQ,WK,WV是可训练的权重矩阵
  • X X X 是输入特征(如词向量)
(2)计算注意力权重

注意力分数使用点积计算
S = Q K T S = QK^T S=QKT
然后进行缩放(防止梯度爆炸):
S scaled = Q K T d k S_{\text{scaled}} = \frac{QK^T}{\sqrt{d_k}} Sscaled=dk QKT
其中:

  • d k d_k dk是键向量的维度
  • d k \sqrt{d_k} dk 作为缩放因子,防止数值过大导致梯度消失或梯度爆炸
(3)计算 Softmax 权重

α = softmax ( S scaled ) \alpha = \text{softmax}(S_{\text{scaled}}) α=softmax(Sscaled)
Softmax 归一化后,表示不同位置的注意力分布。

(4)加权求和

计算最终的注意力输出:
Attention ( Q , K , V ) = α V \text{Attention}(Q, K, V) = \alpha V Attention(Q,K,V)=αV


2. 多头注意力(Multi-Head Attention, MHA)

在单头注意力机制中,所有信息都通过同一个注意力机制计算,可能会忽略一些重要特征。多头注意力的核心思想是:使用多个注意力头(heads),让模型关注不同的特征信息。

(1)多头注意力计算流程
  1. 输入映射到多个 Q、K、V

    • 对输入 X X X进行多组不同的线性变换,得到多个查询、键和值:
      Q i = X W i Q , K i = X W i K , V i = X W i V , i = 1 , 2 , … , h Q_i = X W_i^Q, \quad K_i = X W_i^K, \quad V_i = X W_i^V, \quad i = 1,2,\dots,h Qi=XWiQ,Ki=XWiK,Vi=XWiV,i=1,2,,h
    • 其中 h h h是注意力头的数量,每个注意力头都有自己的权重矩阵 W i Q , W i K , W i V W_i^Q, W_i^K, W_i^V WiQ,WiK,WiV
  2. 每个头独立计算注意力

    • 每个头独立计算缩放点积注意力:
      Attention i ( Q i , K i , V i ) = softmax ( Q i K i T d k ) V i \text{Attention}_i(Q_i, K_i, V_i) = \text{softmax} \left( \frac{Q_i K_i^T}{\sqrt{d_k}} \right) V_i Attentioni(Qi,Ki,Vi)=softmax(dk QiKiT)Vi
  3. 拼接多个头的输出

    • 将所有注意力头的输出拼接在一起:
      MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , … , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, \dots, \text{head}_h) W^O MultiHead(Q,K,V)=Concat(head1,head2,,headh)WO
    • 其中 W O W^O WO是最终的线性变换矩阵,将拼接后的结果映射回原始维度。

3. 公式总结
1.单头注意力(Scaled Dot-Product Attention)

Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax} \left( \frac{Q K^T}{\sqrt{d_k}} \right) V Attention(Q,K,V)=softmax(dk QKT)V

2.多头注意力(Multi-Head Attention)

MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , … , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, \dots, \text{head}_h) W^O MultiHead(Q,K,V)=Concat(head1,head2,,headh)WO
其中:
head i = Attention ( Q i , K i , V i ) \text{head}_i = \text{Attention}(Q_i, K_i, V_i) headi=Attention(Qi,Ki,Vi)


4. 代码实现

多头注意力(Multi-Head Attention, MHA)的 PyTorch 实现

import torch
import torch.nn as nnclass MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads, dropout=0.1):"""多头注意力层参数:- d_model: 输入特征维度(如 512)- num_heads: 注意力头的数量- dropout: Dropout 概率"""super(MultiHeadAttention, self).__init__()assert d_model % num_heads == 0, "d_model 必须能被 num_heads 整除"self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_heads  # 每个头的维度# 线性变换矩阵self.W_q = nn.Linear(d_model, d_model)self.W_k = nn.Linear(d_model, d_model)self.W_v = nn.Linear(d_model, d_model)self.W_o = nn.Linear(d_model, d_model)self.dropout = nn.Dropout(dropout)def attention(self, Q, K, V, mask=None):"""计算缩放点积注意力"""scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))if mask is not None:scores = scores.masked_fill(mask == 0, float('-inf'))attn_weights = torch.softmax(scores, dim=-1)attn_weights = self.dropout(attn_weights)return torch.matmul(attn_weights, V), attn_weightsdef forward(self, X):batch_size, seq_len, _ = X.shape# 计算 Q, K, VQ = self.W_q(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)K = self.W_k(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)V = self.W_v(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)# 计算多头注意力attn_output, attn_weights = self.attention(Q, K, V)# 拼接多个头的输出attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)# 线性变换回原始维度output = self.W_o(attn_output)return output, attn_weights# 测试多头注意力
d_model = 512
num_heads = 8
seq_len = 10
batch_size = 2mha = MultiHeadAttention(d_model, num_heads)
input_tensor = torch.rand(batch_size, seq_len, d_model)  # 随机输入
output_tensor, attention_weights = mha(input_tensor)print("多头注意力输出形状:", output_tensor.shape)  # (batch_size, seq_len, d_model)

  • 多头注意力(MHA)能够学习不同的注意力模式,提高模型的表达能力。
  • 通过多个注意力头,模型可以关注输入序列的不同方面。
  • 缩放点积注意力(Scaled Dot-Product Attention)是 MHA 的基础,计算 Query-Key 相关性并加权 Value。

1.6 位置编码

在这里插入图片描述
在这里插入图片描述

在 Transformer 模型中,由于 自注意力机制(Self-Attention) 没有内置的序列顺序信息,因此需要 位置编码(Positional Encoding) 来显式地提供序列位置信息,使模型能够区分不同单词的顺序。


1. 位置编码的数学定义

位置编码采用 正弦(sin)和余弦(cos)函数 生成,如下公式:
p t ( i ) = { sin ⁡ ( ω k ⋅ t ) , if  i = 2 k cos ⁡ ( ω k ⋅ t ) , if  i = 2 k + 1 p_t^{(i)} = \begin{cases} \sin(\omega_k \cdot t), & \text{if } i = 2k \\ \cos(\omega_k \cdot t), & \text{if } i = 2k+1 \end{cases} pt(i)={sin(ωkt),cos(ωkt),if i=2kif i=2k+1

其中:

  • p t ( i ) p_t^{(i)} pt(i) 表示 位置 t t t 处,第 i i i 维的编码值
  • ω k \omega_k ωk 是不同维度的频率,定义为:
    ω k = 1 1000 0 2 k / d \omega_k = \frac{1}{10000^{2k/d}} ωk=100002k/d1
    其中:
    • k k k表示当前维度的索引(偶数维和奇数维分别使用 sin 和 cos)。
    • d d d 是隐藏层维度(例如 512)。
    • 这个公式确保 不同维度的频率不同,以捕捉不同粒度的位置信息。

2. 位置编码的特点
  • 使用不同频率的正弦和余弦函数
    • 低维度使用较大的频率(变化较快),编码局部信息。
    • 高维度使用较小的频率(变化较慢),编码全局信息。
  • 编码值是固定的,不需要训练,不同位置的编码是固定的数学计算结果,不依赖数据训练。
  • 可以外推到更长的序列,因为是基于数学函数计算的,而不是学习得到的。

3. 位置编码的作用
  • 提供位置信息:帮助 Transformer 区分不同单词的顺序。
  • 支持长序列建模:由于编码是基于数学函数计算的,可以推广到比训练时更长的序列。
  • 平滑的连续变化:由于使用正弦和余弦函数,编码值在相邻位置之间连续变化,使得模型可以更容易学习相对位置信息。

4. 代码实现

可以使用 Python(PyTorch)实现位置编码:

import numpy as np
import matplotlib.pyplot as pltdef positional_encoding(seq_len, d_model):pos = np.arange(seq_len)[:, np.newaxis]  # 位置 (t)i = np.arange(d_model)[np.newaxis, :]  # 维度 (i)# 计算频率omega = 1 / (10000 ** (2 * (i // 2) / d_model))# 计算位置编码pos_encoding = np.zeros((seq_len, d_model))pos_encoding[:, 0::2] = np.sin(pos * omega[:, 0::2])  # 偶数维pos_encoding[:, 1::2] = np.cos(pos * omega[:, 1::2])  # 奇数维return pos_encoding# 生成位置编码
seq_len, d_model = 100, 512
pos_encoding = positional_encoding(seq_len, d_model)# 可视化
plt.figure(figsize=(10, 6))
plt.imshow(pos_encoding, aspect='auto', cmap='coolwarm')
plt.xlabel("Dimension i")
plt.ylabel("Position t")
plt.title("Positional Encoding Heatmap")
plt.colorbar()
plt.show()

位置编码通过 sin 和 cos 赋予 Transformer 位置信息
不同维度使用不同频率,捕捉局部和全局信息
数学计算得到,不需要训练,可推广到长序列

2. 问题备注

Transformer 结构 中,Q(Query)、K(Key)、V(Value) 的来源在 编码器(Encoder)解码器(Decoder) 中有所不同,具体如下:


1. 编码器(Encoder)中的 Q、K、V

编码器的自注意力(Self-Attention) 机制中:

  • 输入:编码器的输入是一个序列 ( X )(如源语言句子)。
  • Q、K、V 的来源
    Q = X W Q , K = X W K , V = X W V Q = X W^Q, \quad K = X W^K, \quad V = X W^V Q=XWQ,K=XWK,V=XWV
    • Query(查询):由输入 X X X 经过线性变换得到
    • Key(键):由输入 X X X 经过线性变换得到
    • Value(值):由输入 X X X 经过线性变换得到

特点

  • 自注意力(Self-Attention):Q、K、V 都来自相同的输入 ( X )。
  • 作用:让编码器的每个词关注输入序列中的其他词,捕捉全局信息。

2. 解码器(Decoder)中的 Q、K、V

解码器的注意力机制 中,注意力分为两种:

  1. Masked 自注意力(Masked Self-Attention)
  2. 交叉注意力(Cross-Attention)
(1)Masked 自注意力中的 Q、K、V
  • 输入:解码器的输入是目标序列 ( Y )(如目标语言句子)。
  • Q、K、V 的来源
    Q = Y W Q , K = Y W K , V = Y W V Q = Y W^Q, \quad K = Y W^K, \quad V = Y W^V Q=YWQ,K=YWK,V=YWV
    • Query(查询):由目标序列 Y Y Y 计算
    • Key(键):由目标序列 Y Y Y 计算
    • Value(值):由目标序列 Y Y Y 计算

特点

  • Masked 机制:在计算注意力时,屏蔽未来的信息,防止看到后续词语。
  • 作用:让解码器的每个词只能关注当前及之前的词,确保自回归特性。

(2)交叉注意力(Cross-Attention)中的 Q、K、V
  • 输入:交叉注意力的输入是 解码器的隐藏状态编码器的输出
  • Q、K、V 的来源
    Q = Y W Q , K = X L W K , V = X L W V Q = Y W^Q, \quad K = X_L W^K, \quad V = X_L W^V Q=YWQ,K=XLWK,V=XLWV
    • Query(查询):来自解码器的隐藏状态 Y Y Y
    • Key(键):来自编码器的输出 X L X_L XL
    • Value(值):来自编码器的输出 X L X_L XL

特点

  • Q 来自解码器,K 和 V 来自编码器,让解码器能够关注编码器的信息。
  • 作用:让解码器在生成目标序列时,能够参考源语言的信息。

位置Query(Q)来源Key(K)来源Value(V)来源作用
编码器(Encoder)输入 (X)输入 (X)输入 (X)让每个词关注整个输入序列
解码器(Masked 自注意力)目标序列 (Y)目标序列 (Y)目标序列 (Y )让解码器的每个词关注当前及之前的词
解码器(交叉注意力)目标序列 (Y)编码器输出 X L X_L XL编码器输出 X L X_L XL

  • 编码器的 Q、K、V 都来自输入 ( X ),用于捕捉全局信息。
  • 解码器的 Masked 自注意力的 Q、K、V 都来自目标序列 Y Y Y,用于保证自回归特性。
  • 解码器的交叉注意力的 Q 来自解码器,K 和 V 来自编码器的输出 X L X_L XL**,用于关注源语言信息。

3.为什么注意力模型计算选用点积(Dot-Product)?

在 Transformer 的注意力机制(Self-Attention 和 Cross-Attention)中,点积(Dot-Product) 是计算 Query(查询)和 Key(键)之间相似度的核心操作。主要原因包括以下几点:


1. 计算效率高

点积计算的核心公式是:
S = Q K T S = QK^T S=QKT
其中:

  • Q Q Q(查询): ( b a t c h , s e q _ l e n , d k ) (batch, seq\_len, d_k) (batch,seq_len,dk)
  • K K K(键): ( b a t c h , s e q _ l e n , d k ) (batch, seq\_len, d_k) (batch,seq_len,dk)

点积计算的时间复杂度为 O ( d k ) O(d_k) O(dk),相比其他相似度计算方法(如欧几里得距离、余弦相似度)更加高效,尤其适用于 GPU 并行计算。

对比计算复杂度:

  • 点积(Dot-Product): O ( d k ) O(d_k) O(dk)(只需乘法和加法)
  • 欧几里得距离(Euclidean Distance): O ( d k ) O(d_k) O(dk)(需要平方、开方)
  • 余弦相似度(Cosine Similarity): O ( d k ) O(d_k) O(dk)(需要归一化)

由于 Transformer 需要处理大规模数据(如 NLP 任务中的长文本、视频分析中的时序数据),选择计算效率高的点积是更优的选择。


2. 点积能够有效衡量相似性
(1)点积的几何解释

点积计算:
Q i ⋅ K j = ∑ d = 1 d k Q i d K j d Q_i \cdot K_j = \sum_{d=1}^{d_k} Q_{id} K_{jd} QiKj=d=1dkQidKjd

  • 如果 Q Q Q K K K 的方向相同(即两个向量相似),点积值较大。
  • 如果 Q Q Q K K K 的方向正交(即无关),点积值接近 0。
  • 如果 Q Q Q K K K 的方向相反,点积值较小(可能为负)。

这与注意力机制的需求一致:希望让 Query 关注与自己最相关的 Key

(2)点积 vs. 余弦相似度

余弦相似度计算如下:
cos ⁡ ( θ ) = Q ⋅ K ∥ Q ∥ ∥ K ∥ \cos(\theta) = \frac{Q \cdot K}{\|Q\| \|K\|} cos(θ)=Q∥∥KQK
虽然余弦相似度也能衡量向量的相似性,但它需要额外的归一化操作(计算向量模长),这会增加计算成本。而点积本质上是未归一化的余弦相似度,因此可以直接用于注意力计算。


3. 结合 Softmax 归一化

点积计算的结果:
S = Q K T S = QK^T S=QKT
通常会经过 Softmax 归一化
α = softmax ( Q K T d k ) \alpha = \text{softmax} \left(\frac{QK^T}{\sqrt{d_k}}\right) α=softmax(dk QKT)

  • Softmax 作用:将点积结果转换为概率分布,使得所有注意力权重之和为 1。
  • 缩放因子 d k \sqrt{d_k} dk :防止点积值过大导致梯度消失。

这种组合方式使得点积注意力(Scaled Dot-Product Attention)既高效又稳定。


4. 经验验证:点积注意力效果优于加性注意力

在早期的注意力机制(如 Bahdanau Attention)中,使用的是 加性注意力(Additive Attention)
score ( Q , K ) = V T tanh ⁡ ( W q Q + W k K ) \text{score}(Q, K) = V^T \tanh(W_q Q + W_k K) score(Q,K)=VTtanh(WqQ+WkK)
但研究发现:

  • 点积注意力(Dot-Product Attention)在大规模数据上效果更好
  • 点积计算可以更好地利用矩阵运算加速(GPU 友好)
  • 加性注意力需要额外的参数 W q , W k , V W_q, W_k, V Wq,Wk,V,而点积注意力不需要额外参数

因此,在 Transformer 及其变体(如 BERT、GPT、ViT)中,点积注意力成为主流选择


  • 点积计算高效,适合 GPU 并行计算(相比加性注意力更快)。
  • 点积能够有效衡量向量相似性(与余弦相似度类似,但计算更简单)。
  • 结合 Softmax 归一化,使注意力机制更加稳定(避免梯度消失)。
  • 实验验证:点积注意力在大规模数据上效果优于加性注意力

因此,Transformer 采用点积注意力(Dot-Product Attention)作为核心计算方式,并结合 Softmax 归一化和缩放因子 d k \sqrt{d_k} dk 进行优化。

4. 为什么要除以 D \sqrt{D} D (Scale)?

点积注意力(Dot-Product Attention) 计算中,我们使用 Query(Q)和 Key(K) 的点积来衡量相似性:
S = Q K T S = QK^T S=QKT
然后通过 Softmax 归一化计算注意力权重:
α = softmax ( S ) V \alpha = \text{softmax}(S)V α=softmax(S)V
但是,如果不进行缩放,点积值可能会变得 过大,导致 Softmax 输出极端化,影响模型训练。
因此,我们 除以 ( \sqrt{D} )(D 为特征维度) 来进行缩放,最终计算方式如下:
S = Q K T D S = \frac{QK^T}{\sqrt{D}} S=D QKT


1. 避免点积值过大,防止梯度消失

在高维空间(例如 D = 512 D=512 D=512 Q Q Q K K K 的元素通常服从 均值为 0,方差为 1 的正态分布(假设初始化时)。
那么,点积 S S S 的期望值和方差计算如下:

  • 点积的期望值
    E [ Q K T ] = 0 \mathbb{E}[QK^T] = 0 E[QKT]=0 $$
    因为 ( Q ) 和 ( K ) 服从均值为 0 的分布,所以它们的点积的均值仍然是 0。

  • 点积的方差
    Var ( Q K T ) = D \text{Var}(QK^T) = D Var(QKT)=D
    由于点积涉及 ( D ) 个独立变量的乘积求和,方差会随着 ( D ) 增大而增大。

D D D 很大时,例如 D = 512 或 1024 D = 512 或 1024 D=5121024,点积值的方差也会变得很大,导致 Softmax 计算时的指数函数 e x e^x ex的值过大,使得:

  • Softmax 输出接近 one-hot(即某个值接近 1,其他值接近 0)。
  • 反向传播时梯度变得很小(梯度消失),影响模型训练。

解决方案:

  • 通过 除以 ( \sqrt{D} ),让点积值的方差变为 1:
    Var ( Q K T D ) = 1 \text{Var} \left(\frac{QK^T}{\sqrt{D}}\right) = 1 Var(D QKT)=1
  • 这样,Softmax 输出更加平滑,梯度不会消失,有助于稳定训练。

2. 直观理解

可以用一个形象的例子来理解:

  • 未缩放的点积(大值输入 Softmax):就像一个考试成绩满分 1000 分的系统,99 分和 100 分的差距微乎其微,但 Softmax 会放大这个差距,使得 100 分的注意力远大于 99 分。
  • 缩放后的点积(小值输入 Softmax):就像一个满分 100 分的考试,99 分和 100 分的差距不会被过度放大,注意力分布更加合理。

防止 Softmax 过度偏向单个 token(避免 one-hot 现象)。
保持梯度稳定,防止梯度消失,使模型更容易训练
在高维空间(如 D=512)时,避免点积值过大,保证数值稳定性

因此,Transformer 采用 Scaled Dot-Product Attention,即在计算 Q K T QK^T QKT 后除以 D \sqrt{D} D ,确保模型训练更稳定,注意力分布更合理。

参考文献

Datawhale大模型组队学习地址


http://www.ppmy.cn/devtools/166253.html

相关文章

WPS的付费功能,这款软件可完美平替

因为作者有工作上的需求加上WPS使用批量提取图片需要会员,所以自己使用cursor制作了一个从excel中提取图片的工具。 支持提取Excel中的浮动图片和根据图片链接来下载图片。 Excel Image Extractor Excel图片提取工具 软件的功能非常强大,支持提取Excel中…

HTTP协议与Web开发

🌐 HTTP协议与Web开发完全指南:从原理到实战 一、HTTP协议是什么? HTTP(超文本传输协议) 是互联网上应用最广泛的网络协议,作为Web开发的基石,它具有以下核心特性: 无状态协议&am…

Rabbitmq--延迟消息

13.延迟消息 延迟消息:生产者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才会收到消息 延迟任务:一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时,就会…

调试正常 ≠ 运行正常:Keil5中MicroLIB的“量子态BUG”破解实录

调试正常 ≠ 运行正常:Keil5中MicroLIB的“量子态BUG”破解实录——从勾选一个选项到理解半主机模式,嵌入式开发的认知升级 📌 现象描述:调试与烧录的诡异差异 在线调试时 程序正常运行 - 独立运行时 设备无响应 ! 编译过程 0 Err…

MoonSharp 文档三

MoonSharp 文档一-CSDN博客 MoonSharp 文档二-CSDN博客 MoonSharp 文档四-CSDN博客 MoonSharp 文档五-CSDN博客 7.Proxy objects(代理对象) 如何封装你的实现,同时又为脚本提供一个有意义的对象模型 官方文档:MoonSharp 在现实世界的场景中,脚本往往会超出你的控制范…

RISC-V医疗芯片工程师复合型转型的路径与策略

从RISC-V到医疗芯片:工程师复合型转型的路径与策略 一、引言 1.1 研究背景 在科技快速发展的当下,芯片技术已然成为推动各行业进步的核心驱动力之一。其中,RISC-V 架构作为芯片领域的新兴力量,正以其独特的优势迅速崛起,对整个芯片产业的格局产生着深远影响。RISC-V 架…

【Linux docker 容器】关于想要让虚拟机在开机时候也docker自己启动,容器也自己启动,省去要自己开docker和容器

确认 Docker 服务状态: 首先,你需要确保 Docker 服务已经在虚拟机上安装并正确配置。你可以使用如下命令来检查 Docker 服务的状态: systemctl status docker.service 如果服务没有运行,你可以使用以下命令启动它: s…

Flink-DataStreamAPI-生成水印

下面我们将学习Flink提供的用于处理事件时间戳和水印的API,也会介绍有关事件时间、流转时长和摄取时间,下面就让我们跟着官网来学习吧 一、水印策略介绍 为了处理事件时间,Flink需要知道事件时间戳,这意味着流中的每个元素都需要…