一. 基础介绍
我们先从理论角度来解释QKV机制,QKV机制是注意力机制的核心,尤其在Transformer模型中,注意力机制源于人类感知世界的方式:在处理信息时,我们会选择性地关注一部分信息,而忽略其他信息。在机器学习中,注意力机制允许模型在处理序列数据时,对不同的部分赋予不同的权重,从而更有效地利用信息。
1. QKV的定义
在注意力机制中,每个输入元素会生成三个向量:Query(查询)、Key(键)和Value(值)。这些向量是通过对输入进行线性变换得到的。
- Query(Q):表示当前元素想要查询其他元素的信息。它类似于一个提问,用来匹配其他元素的Key。
- Key(K):表示当前元素的标识,用于被其他元素的Query匹配。Query和Key的匹配程度决定了注意力权重。
- Value(V):表示当前元素实际提供的信息。注意力权重会作用在Value上,从而得到加权的输出。
2. 直观理解
结合图书馆来理解,好比我们去图示馆借阅书籍:
- Value:图书馆里所有的书籍本身,包含丰富的知识和信息
- Key:每本书的索引卡,简要描述了书的内容特征
- Query:我们的借阅请求,比如"我想找一本关于人工智能入门的书"
借书过程就是QKV机制:
- 1. 管理员将我们的Query与所有Key进行匹配
- 2. 找到最相关的几本书(计算匹配度)
- 3. 根据匹配程度决定每本书的"重要性权重"
- 4. 把这些书的Value按照权重组合起来给你
二、计算过程
给定输入序列X ∈ R^(n×d) ,其中n是序列长度,d是特征维度。我们通过线性变换得到Q、K、V,然后计算注意力权重,公式偏数学理论,先了解大概,记住公式内容,在示例中对比逐步领悟。
1. 输入表示:X ∈ R^(n×d)
- n:序列长度(如一句话有10个词,n=10)
- d:每个token的嵌入维度(如BERT-base中d=768)
X = [[x₁₁, x₁₂, ..., x₁d], # 第1个token的d维向量
[x₂₁, x₂₂, ..., x₂d], # 第2个token的d维向量
...
[xₙ₁, xₙ₂, ..., xₙd]] # 第n个token的d维向量
# 假设句子:"I love AI" 有3个token,每个token用4维向量表示
X = [[0.1, 0.2, 0.3, 0.4], # "I"的向量表示
[0.5, 0.6, 0.7, 0.8], # "love"的向量表示
[0.9, 0.1, 0.2, 0.3]] # "AI"的向量表示
# 这里 n=3, d=4
2. 线性变换:生成Q, K, V
计算公式:
- Q = X × W_Q,其中 W_Q ∈ R^(d×d_k)
- K = X × W_K,其中 W_K ∈ R^(d×d_k)
- V = X × W_V,其中 W_V ∈ R^(d×d_v)
维度变换过程:
- 输入: X ∈ R^(n×d)
- 经过线性变换:
- Q = X_(n×d) × W_Q_(d×d_k),其中 Q ∈ R^(n×d_k)
- K = X_(n×d) × W_K_(d×d_k),其中 K ∈ R^(n×d_k)
- V = X_(n×d) × W_V_(d×d_v),其中 V ∈ R^(n×d_v)
数学含义:
每个权重矩阵的作用:
- W_Q - Query权重矩阵:
- 学习如何提问
- 将原始输入映射到查询空间
- 决定每个token应该关注什么类型的信息
- W_K - Key权重矩阵:
- 学习如何被识别
- 将原始输入映射到键空间
- 决定每个token如何被其他token识别和匹配
- W_V - Value权重矩阵:
- 学习"提供什么信息"
- 将原始输入映射到值空间
- 决定每个token在被关注时应该贡献什么信息
实际计算示例:
# 假设 d=4, d_k=3, d_v=3
W_Q = [[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
[0.2, 0.3, 0.4]] # 4×3
# 计算第一个token的Query向量
token1 = [0.1, 0.2, 0.3, 0.4]
Q1 = [0.1×0.1 + 0.2×0.4 + 0.3×0.7 + 0.4×0.2,
0.1×0.2 + 0.2×0.5 + 0.3×0.8 + 0.4×0.3,
0.1×0.3 + 0.2×0.6 + 0.3×0.9 + 0.4×0.4]
= [0.01 + 0.08 + 0.21 + 0.08,
0.02 + 0.10 + 0.24 + 0.12,
0.03 + 0.12 + 0.27 + 0.16]
= [0.38, 0.48, 0.58]
3. 注意力分数:S = (Q × K^T) / √d_k
详细公式:
- S[i,j] = (Q[i] · K[j]) / √d_k = (∑_{k=1}^{d_k} Q[i,k] × K[j,k]) / √d_k
维度变换:
- Q ∈ R^(n×d_k), K^T ∈ R^(d_k×n)
- S = Q_(n×d_k) × K^T_(d_k×n),其中 S ∈ R^(n×n)
数学含义:
点积相似度:
- 测量Query向量和Key向量之间的相似性
- 值越大表示两个token之间的关联性越强
缩放因子 1/√d_k:
- 防止点积值过大导致softmax梯度消失
- 当d_k很大时,点积的方差也会变大,缩放保持数值稳定性
实际意义:
- S矩阵是关联强度矩阵:
- S[i,j]:第i个token对第j个token的关注程度
- 对角线元素 S[i,i]:token对自己的关注度
- 非对角线元素 S[i,j]:token之间的相互关注度
# 示例:3个token的关联矩阵
S = [[0.9, 0.3, 0.1], # token1: 很关注自己,不太关注其他
[0.2, 0.8, 0.6], # token2: 关注自己和token3
[0.1, 0.5, 0.7]] # token3: 比较关注token2
4. 注意力权重:A = softmax(S)
详细公式:
- 对于矩阵S的每一行i: A[i,j] = exp(S[i,j]) / ∑_{k=1}^n exp(S[i,k])
数学含义:
Softmax操作:
- 将注意力分数转换为概率分布
- 确保每行的权重和为1
- 放大较大值,抑制较小值
概率解释:
- A[i,j] = P(关注token_j | 当前是token_i)
- 即:给定当前处理的是第i个token,它应该以多大概率关注第j个token
实际效果:
# Softmax前
S = [[1.0, 0.5, 0.1],
[0.2, 2.0, 0.3],
[0.1, 0.4, 0.8]]
# Softmax后(每行和为1)
A = [[0.57, 0.29, 0.14], # 主要关注自己
[0.12, 0.73, 0.15], # 强烈关注自己
[0.18, 0.27, 0.55]] # 主要关注自己,也关注其他
5. 输出计算:Z = A × V
详细公式:
- 对于输出Z的每一行i:Z[i] = ∑_{j=1}^n A[i,j] × V[j]
维度变换:
- A ∈ R^(n×n), V ∈ R^(n×d_v)
- Z = A_(n×n) × V_(n×d_v),其中 Z ∈ R^(n×d_v)
实际意义:
上下文感知的表示:
- 原始输入X:每个token的独立表示
- 输出Z:每个token包含全局上下文信息的表示
# 示例:代词消解
句子:"The cat ate the fish because it was hungry"
# 计算"it"的输出时:
Z_it = 0.02 × V_The + 0.01 × V_cat + 0.01 × V_ate +
0.02 × V_the + 0.01 × V_fish + 0.90 × V_cat + # 主要关注"cat"
0.03 × V_was + 0.00 × V_hungry
6. 计算过程示例
6.1 输入准备
假设我们有3个词的句子:"猫 吃 鱼",每个词用4维向量表示:
- 猫: [0.8, 0.2, 0.4, 0.1]
- 吃: [0.1, 0.9, 0.3, 0.7]
- 鱼: [0.3, 0.1, 0.9, 0.2]
计算得出输入矩阵 X = [[0.8, 0.2, 0.4, 0.1], [0.1, 0.9, 0.3, 0.7], [0.3, 0.1, 0.9, 0.2]]
6.2 生成Q、K、V
模型有三个可学习的权重矩阵(开始时是随机值,通过训练学习):
- W_Q = [[0.5, 0.2], [0.1, 0.3], [0.4, 0.6], [0.2, 0.1]] ,形状: [4, 2]
- W_K = [[0.1, 0.5], [0.3, 0.2], [0.6, 0.4], [0.2, 0.3]] ,形状: [4, 2]
- W_V = [[0.2, 0.4], [0.5, 0.1], [0.3, 0.6], [0.1, 0.2]] ,形状: [4, 2]
计算过程:
- Q = X × W_Q
- K = X × W_K
- V = X × W_V
让我们手动计算"猫"这个词的Q向量:
猫的Q = [0.8, 0.2, 0.4, 0.1] × [[0.5, 0.2], [0.1, 0.3], [0.4, 0.6], [0.2, 0.1]]
= [0.8×0.5 + 0.2×0.1 + 0.4×0.4 + 0.1×0.2, 0.8×0.2 + 0.2×0.3 + 0.4×0.6 + 0.1×0.1]
= [0.4 + 0.02 + 0.16 + 0.02, 0.16 + 0.06 + 0.24 + 0.01]
= [0.6, 0.47]
计算最终得到:
Q = [[0.60, 0.47], # 猫的Query
[0.58, 0.90], # 吃的Query
[0.72, 0.65]] # 鱼的Query
K = [[0.32, 0.56], # 猫的Key
[0.50, 0.71], # 吃的Key
[0.68, 0.42]] # 鱼的Key
V = [[0.35, 0.42], # 猫的Value
[0.62, 0.28], # 吃的Value
[0.54, 0.63]] # 鱼的Value
6.3 计算注意力分数
现在计算每个Query与所有Key的匹配度:
- 注意力分数 = Q × K^T
- K^T = [[0.32, 0.50, 0.68], [0.56, 0.71, 0.42]]
计算过程:
- 分数[0,0] = [0.60,0.47]·[0.32,0.56] = 0.60×0.32 + 0.47×0.56 = 0.192 + 0.263 = 0.455
- 分数[0,1] = [0.60,0.47]·[0.50,0.71] = 0.60×0.50 + 0.47×0.71 = 0.300 + 0.334 = 0.634
- 分数[0,2] = [0.60,0.47]·[0.68,0.42] = 0.60×0.68 + 0.47×0.42 = 0.408 + 0.197 = 0.605
- ... 以此计算所有组合
得到注意力分数矩阵:
注意力分数 = [[0.455, 0.634, 0.605], # 猫对猫/吃/鱼的关注度
[0.572, 0.869, 0.753], # 吃对猫/吃/鱼的关注度
[0.533, 0.767, 0.763]] # 鱼对猫/吃/鱼的关注度
6.4 Softmax归一化
对每一行进行Softmax,使得每行之和为1:
- 第一行: [0.455, 0.634, 0.605]
- exp: [e^0.455, e^0.634, e^0.605] ≈ [1.576, 1.886, 1.831]
- 和: 1.576 + 1.886 + 1.831 = 5.293
- Softmax: [1.576/5.293, 1.886/5.293, 1.831/5.293] ≈ [0.298, 0.356, 0.346]
最终注意力权重:
注意力权重 = [[0.298, 0.356, 0.346], # 猫: 35.6%关注"吃", 34.6%关注"鱼"
[0.246, 0.422, 0.332], # 吃: 最关注自己(42.2%)
[0.276, 0.354, 0.370]] # 鱼: 37.0%关注自己, 35.4%关注"吃"
6.5 加权求和得到输出
最后用注意力权重对Value进行加权求和:
对于"猫"这个词的输出:
输出[0] = 0.298×[0.35,0.42] + 0.356×[0.62,0.28] + 0.346×[0.54,0.63]
= [0.104,0.125] + [0.221,0.100] + [0.187,0.218]
= [0.512, 0.443]
最终输出:
输出 = [[0.512, 0.443], # 猫的上下文感知表示
[0.527, 0.422], # 吃的上下文感知表示
[0.519, 0.468]] # 鱼的上下文感知表示
7. 完整流程总结
整个流程包括输入、线性变换(生成Q、K、V)、计算注意力分数、Softmax归一化、加权求和得到输出。
流程详细说明:
- 1. 输入阶段
- X: 原始输入序列,形状为 n×d
- n = 序列长度(如10个词)
- d = 每个词的向量维度(如768维)
- 2. 线性变换阶段
- 通过三个不同的权重矩阵生成Q、K、V:
- Q (Query): 学习"如何提问" - 形状 n×d_k
- K (Key): 学习"如何被识别" - 形状 n×d_k
- V (Value): 学习"提供什么信息" - 形状 n×d_v
- 3. 注意力计算阶段
- S: 关联强度矩阵,形状 n×n
- 计算每个位置对其他所有位置的关注程度
- S[i,j] 表示第i个位置对第j个位置的关注度
- 4. 归一化阶段
- A: 注意力权重矩阵,形状 n×n
- 通过Softmax确保每行的权重和为1
- 将关注度转换为概率分布
- 5. 输出生成阶段
- Z: 最终输出序列,形状 n×d_v
- 每个位置都是所有Value向量的加权和
- 权重由注意力矩阵A决定
- 每个token都包含了整个序列的上下文信息
三、体现的价值
1. 实际作用示例
示例1:代词消解
- 句子:"李华看见张明,他笑了"
- 计算过程:
- - "他"的Query会与所有词的Key匹配
- - 发现与"李华"、"张明"的匹配度都很高
- - 但通过训练,模型学会"他"通常指向前面最近的人名"张明"
- - 因此给"张明"的Value更高权重
示例2:多义词理解
- 句子1:"苹果很好吃"
- 句子2:"苹果股价上涨"
- 在句子1中:"苹果"的Query主要与"好吃"、"水果"等Key高匹配
- 在句子2中:"苹果"的Query主要与"股价"、"公司"等Key高匹配
2. 多头注意力机制
实际中大模型使用多头注意力,让模型同时从多个角度关注信息:
比如在分析"我去银行取钱"时:
- 头1关注:"取"→"钱"(动作-对象关系)
- 头2关注:"银行"→"取钱"(地点-活动关系)
- 头3关注:"我"→"取"(主体-动作关系)
四、代码实现推导
import torch import torch.nn as nn import torch.nn.functional as F import numpy as np class SimpleSelfAttention(nn.Module): """简单的自注意力机制实现""" def __init__(self, d_model=4, d_k=2, d_v=2): super(SimpleSelfAttention, self).__init__() self.d_model = d_model # 输入维度 self.d_k = d_k # Query和Key的维度 self.d_v = d_v # Value的维度 # 定义Q、K、V的线性变换层 # 注意:nn.Linear的权重形状是 (out_features, in_features) self.W_Q = nn.Linear(d_model, d_k, bias=False) self.W_K = nn.Linear(d_model, d_k, bias=False) self.W_V = nn.Linear(d_model, d_v, bias=False) # 初始化权重,便于理解计算过程 self._initialize_weights() def _initialize_weights(self): """手动初始化权重,便于跟踪计算过程""" # 预设的权重值,注意形状要与nn.Linear匹配 # nn.Linear权重形状是 (out_features, in_features) = (d_k, d_model) preset_W_Q = torch.tensor([[0.5, 0.1, 0.4, 0.2], # 第一行对应第一个输出维度 [0.2, 0.3, 0.6, 0.1]], dtype=torch.float32) # 第二行对应第二个输出维度 preset_W_K = torch.tensor([[0.1, 0.3, 0.6, 0.2], [0.5, 0.2, 0.4, 0.3]], dtype=torch.float32) preset_W_V = torch.tensor([[0.2, 0.5, 0.3, 0.1], [0.4, 0.1, 0.6, 0.2]], dtype=torch.float32) self.W_Q.weight.data = preset_W_Q self.W_K.weight.data = preset_W_K self.W_V.weight.data = preset_W_V def forward(self, X, verbose=False): """ 前向传播 Args: X: 输入张量 [seq_len, d_model] verbose: 是否打印详细计算过程 """ if verbose: print("=" * 60) print("QKV自注意力机制详细计算过程") print("=" * 60) print(f"输入 X (shape: {X.shape}):") print(X) print() # 步骤1: 生成Q, K, V Q = self.W_Q(X) # [seq_len, d_k] K = self.W_K(X) # [seq_len, d_k] V = self.W_V(X) # [seq_len, d_v] if verbose: print("步骤1: 生成Q, K, V") print(f"Q (shape: {Q.shape}):") print(Q) print(f"K (shape: {K.shape}):") print(K) print(f"V (shape: {V.shape}):") print(V) print() # 步骤2: 计算注意力分数 Q × K^T attention_scores = torch.matmul(Q, K.transpose(0, 1)) # [seq_len, seq_len] if verbose: print("步骤2: 计算注意力分数 Q × K^T") print(f"注意力分数矩阵 (shape: {attention_scores.shape}):") print(attention_scores) print() # 打印详细的计算过程 print("详细点积计算:") for i in range(Q.shape[0]): for j in range(K.shape[0]): dot_product = torch.dot(Q[i], K[j]) print(f"Q[{i}] · K[{j}] = {Q[i].detach().numpy()} · {K[j].detach().numpy()} = {dot_product:.3f}") print() # 步骤3: 缩放 scaled_scores = attention_scores / np.sqrt(self.d_k) if verbose: print("步骤3: 缩放") print(f"缩放因子: 1/sqrt(d_k) = 1/sqrt({self.d_k}) = {1/np.sqrt(self.d_k):.3f}") print(f"缩放后的分数矩阵:") print(scaled_scores) print() # 步骤4: Softmax归一化 attention_weights = F.softmax(scaled_scores, dim=-1) # [seq_len, seq_len] if verbose: print("步骤4: Softmax归一化") print("对每一行进行Softmax:") for i in range(scaled_scores.shape[0]): row = scaled_scores[i] exp_row = torch.exp(row) sum_exp = torch.sum(exp_row) softmax_row = exp_row / sum_exp print(f"第{i}行: {row.detach().numpy()} -> exp: {exp_row.detach().numpy()} -> " f"softmax: {softmax_row.detach().numpy()} (sum: {sum_exp:.3f})") print() print(f"注意力权重矩阵 (每行和为1):") print(attention_weights) print() # 步骤5: 加权求和得到输出 output = torch.matmul(attention_weights, V) # [seq_len, d_v] if verbose: print("步骤5: 加权求和得到输出") print("每个位置的输出计算:") for i in range(attention_weights.shape[0]): weights = attention_weights[i] weighted_sum = torch.zeros_like(V[0]) for j in range(V.shape[0]): contribution = weights[j] * V[j] weighted_sum += contribution print(f" 位置 {i}: {weights[j]:.3f} × V[{j}] {V[j].detach().numpy()} = {contribution.detach().numpy()}") print(f" → 输出[{i}] = {weighted_sum.detach().numpy()}") print() print(f"最终输出 (shape: {output.shape}):") print(output) print("=" * 60) return output, attention_weights def demonstrate_attention(): """演示自注意力机制的工作过程""" # 创建模型 attention = SimpleSelfAttention(d_model=4, d_k=2, d_v=2) # 示例输入:3个词的序列,每个词用4维向量表示 # "猫" [0.8, 0.2, 0.4, 0.1] # "吃" [0.1, 0.9, 0.3, 0.7] # "鱼" [0.3, 0.1, 0.9, 0.2] X = torch.tensor([ [0.8, 0.2, 0.4, 0.1], # 猫 [0.1, 0.9, 0.3, 0.7], # 吃 [0.3, 0.1, 0.9, 0.2] # 鱼 ], dtype=torch.float32) print("输入句子: ['猫', '吃', '鱼']") print("每个词的向量表示:") words = ["猫", "吃", "鱼"] for i, word in enumerate(words): print(f" '{word}': {X[i].numpy()}") print() # 前向传播,显示详细计算过程 output, attention_weights = attention(X, verbose=True) # 可视化注意力权重 print("\n注意力权重热力图解释:") print("行: 当前词(Query), 列: 被关注的词(Key)") print(" 猫 吃 鱼") for i, word in enumerate(words): weights = attention_weights[i].detach().numpy() print(f"{word} {weights}") return output, attention_weights def demonstrate_multihead_attention(): """演示多头注意力的概念""" print("\n" + "=" * 70) print("多头注意力机制概念演示") print("=" * 70) # 使用PyTorch内置的多头注意力 multihead_attn = nn.MultiheadAttention(embed_dim=4, num_heads=2, batch_first=True) # 同样的输入,但增加batch维度 X = torch.tensor([[ [0.8, 0.2, 0.4, 0.1], # 猫 [0.1, 0.9, 0.3, 0.7], # 吃 [0.3, 0.1, 0.9, 0.2] # 鱼 ]], dtype=torch.float32) # [batch_size=1, seq_len=3, embed_dim=4] print("输入形状:", X.shape) # 前向传播 output, attention_weights = multihead_attn(X, X, X) print("输出形状:", output.shape) print("注意力权重形状:", attention_weights.shape) print("\n多头注意力的优势:") print("- 每个头可以关注不同类型的信息") print("- 头1可能关注: 语法关系(主谓宾)") print("- 头2可能关注: 语义关系(猫→吃→鱼)") print("- 最终融合所有头的信息,获得更丰富的表示") if __name__ == "__main__": # 演示基础自注意力 output, weights = demonstrate_attention() # 演示多头注意力 demonstrate_multihead_attention()
输出结果:
输入句子: ['猫', '吃', '鱼']
每个词的向量表示:
'猫': [0.8 0.2 0.4 0.1]
'吃': [0.1 0.9 0.3 0.7]
'鱼': [0.3 0.1 0.9 0.2]
============================================================
QKV自注意力机制详细计算过程
============================================================
输入 X (shape: torch.Size([3, 4])):
tensor([[0.8000, 0.2000, 0.4000, 0.1000],
[0.1000, 0.9000, 0.3000, 0.7000],
[0.3000, 0.1000, 0.9000, 0.2000]])
步骤1: 生成Q, K, V
Q (shape: torch.Size([3, 2])):
tensor([[0.6000, 0.4700],
[0.4000, 0.5400],
[0.5600, 0.6500]], grad_fn=)
K (shape: torch.Size([3, 2])):
tensor([[0.4000, 0.6300],
[0.6000, 0.5600],
[0.6400, 0.5900]], grad_fn=)
V (shape: torch.Size([3, 2])):
tensor([[0.3900, 0.6000],
[0.6300, 0.4500],
[0.4000, 0.7100]], grad_fn=)
步骤2: 计算注意力分数 Q × K^T
注意力分数矩阵 (shape: torch.Size([3, 3])):
tensor([[0.5361, 0.6232, 0.6613],
[0.5002, 0.5424, 0.5746],
[0.6335, 0.7000, 0.7419]], grad_fn=)
详细点积计算:
Q[0] · K[0] = [0.6 0.47000003] · [0.40000004 0.63 ] = 0.536
Q[0] · K[1] = [0.6 0.47000003] · [0.6 0.56] = 0.623
Q[0] · K[2] = [0.6 0.47000003] · [0.64000005 0.59 ] = 0.661
Q[1] · K[0] = [0.39999998 0.54 ] · [0.40000004 0.63 ] = 0.500
Q[1] · K[1] = [0.39999998 0.54 ] · [0.6 0.56] = 0.542
Q[1] · K[2] = [0.39999998 0.54 ] · [0.64000005 0.59 ] = 0.575
Q[2] · K[0] = [0.56 0.65000004] · [0.40000004 0.63 ] = 0.634
Q[2] · K[1] = [0.56 0.65000004] · [0.6 0.56] = 0.700
Q[2] · K[2] = [0.56 0.65000004] · [0.64000005 0.59 ] = 0.742
步骤3: 缩放
缩放因子: 1/sqrt(d_k) = 1/sqrt(2) = 0.707
缩放后的分数矩阵:
tensor([[0.3791, 0.4407, 0.4676],
[0.3537, 0.3835, 0.4063],
[0.4480, 0.4950, 0.5246]], grad_fn=)
步骤4: Softmax归一化
对每一行进行Softmax:
第0行: [0.37907997 0.440669 0.46760976] -> exp: [1.4609399 1.5537463 1.5961744] -> softmax: [0.31684756 0.33697537 0.34617713] (sum: 4.611)
第1行: [0.35369486 0.38353473 0.40630355] -> exp: [1.4243205 1.4674625 1.5012583] -> softmax: [0.32422197 0.33404252 0.34173554] (sum: 4.393)
第2行: [0.44795218 0.4949748 0.52460253] -> exp: [1.5651039 1.6404569 1.689787 ] -> softmax: [0.31971252 0.3351053 0.34518224] (sum: 4.895)
注意力权重矩阵 (每行和为1):
tensor([[0.3168, 0.3370, 0.3462],
[0.3242, 0.3340, 0.3417],
[0.3197, 0.3351, 0.3452]], grad_fn=)
步骤5: 加权求和得到输出
每个位置的输出计算:
位置 0: 0.317 × V[0] [0.39000005 0.6000001 ] = [0.12357055 0.19010855]
位置 0: 0.337 × V[1] [0.63 0.45] = [0.21229446 0.1516389 ]
位置 0: 0.346 × V[2] [0.4 0.71000004] = [0.13847084 0.24578576]
→ 输出[0] = [0.47433585 0.58753324]
位置 1: 0.324 × V[0] [0.39000005 0.6000001 ] = [0.12644659 0.19453323]
位置 1: 0.334 × V[1] [0.63 0.45] = [0.21044679 0.15031913]
位置 1: 0.342 × V[2] [0.4 0.71000004] = [0.13669422 0.24263225]
→ 输出[1] = [0.4735876 0.5874846]
位置 2: 0.320 × V[0] [0.39000005 0.6000001 ] = [0.12468789 0.19182752]
位置 2: 0.335 × V[1] [0.63 0.45] = [0.21111631 0.15079737]
位置 2: 0.345 × V[2] [0.4 0.71000004] = [0.1380729 0.2450794]
→ 输出[2] = [0.47387707 0.5877043 ]
最终输出 (shape: torch.Size([3, 2])):
tensor([[0.4743, 0.5875],
[0.4736, 0.5875],
[0.4739, 0.5877]], grad_fn=)
============================================================
注意力权重热力图解释:
行: 当前词(Query), 列: 被关注的词(Key)
猫 吃 鱼
猫 [0.31684753 0.33697534 0.3461771 ]
吃 [0.324222 0.33404252 0.34173554]
鱼 [0.3197125 0.33510527 0.34518224]
=====================================================================
多头注意力机制概念演示
=====================================================================
输入形状: torch.Size([1, 3, 4])
输出形状: torch.Size([1, 3, 4])
注意力权重形状: torch.Size([1, 3, 3])
多头注意力的优势:
- 每个头可以关注不同类型的信息
- 头1可能关注: 语法关系(主谓宾)
- 头2可能关注: 语义关系(猫→吃→鱼)
- 最终融合所有头的信息,获得更丰富的表示
五、总结
QKV注意力机制是现代Transformer架构的核心突破,其本质是一个高效的信息检索与融合系统。该机制通过线性变换将输入序列转化为三组向量:
- Query代表当前需要信息的"提问者"
- Key作为其他位置的"身份标识"
- Value则是实际承载的"信息内容"
核心计算流程首先通过Query与所有Key的点积相似度建立关联矩阵,再经Softmax归一化为概率分布,最终对Value进行加权融合。QKV机制赋予模型动态上下文感知能力,每个位置的输出都融合了全局相关信息,而非固定窗口内的局部特征,这使模型能有效处理代词指代、一词多义等复杂语言现象。相比传统循环神经网络的序列处理,注意力机制能直接捕获任意距离的依赖关系,且完全并行化计算。