自注意力机制(Self-Attention):Q、K、V计算
一句话概述
自注意力机制(Self-Attention)是Transformer架构的核心创新,也是现代大语言模型(如GPT、BERT)的基石。它的核心思想是:让序列中的每个元素都能直接"关注"序列中所有其他元素,动态计算每个元素与其他元素的相关性权重,从而生成富含上下文信息的表示。自注意力通过三个可学习的线性变换——查询(Query, Q)、键(Key, K)、值(Value, V)——来实现这一过程:Q表示"我想找什么",K表示"我这里有什么",V表示"我这里的实际内容"。通过Q和K的点积计算注意力分数,经Softmax归一化后得到注意力权重,再对V加权求和,每个位置就获得了融合全局信息的表示。这一机制彻底解决了传统RNN无法并行计算、长距离依赖衰减的问题,使得训练长序列、大规模模型成为可能。
💡 核心要点:①自注意力机制让每个词能直接看到序列中所有其他词,计算复杂度O(n²)但路径长度O(1) ②Q、K、V三个矩阵通过输入X乘以三个不同的权重矩阵W^Q、W^K、W^V得到 ③注意力计算的核心公式为Attention(Q,K,V)=softmax(QK^T/√d_k)V,其中除以√d_k是为了防止点积过大导致梯度消失 ④自注意力是Transformer、BERT、GPT等所有现代大模型的基础构建块
教学与演示
一、自注意力的核心思想:从"看全部"到"关注重点"
是什么(定义):自注意力(Self-Attention)是一种让序列中每个位置都能直接与序列中所有位置交互的机制。与RNN不同——RNN需要逐步传递信息,距离为n的两个位置需要n步才能交互——自注意力中任意两个位置之间的信息传递路径长度恒为O(1)。给定输入序列X∈R^{n×d}(n个位置,每个位置d维),自注意力通过三个权重矩阵W^Q、W^K、W^V∈R^{d×d_k}将每个位置映射为查询Q、键K、值V三个向量,然后通过QK^T计算位置间的相关性分数,经Softmax后对V加权求和。
大白话 自注意力就像"开会讨论"。每个词(每个位置)都是一个"参会者"。当轮到某个参会者发言时,他会问一个问题(Q:查询),然后听所有其他参会者自我介绍(K:键),判断谁的意见和自己相关(Q·K^T算相似度),最后综合所有人的意见(对V加权求和),形成自己的新观点。关键区别是:RNN是"传话游戏"——第一个人说给第二个人听,第二个人加上自己的理解说给第三个人……传到第100个人时,第一个人的原话早就面目全非了。自注意力是"圆桌会议"——所有人同时发言,每个人都能直接听到所有人。
为什么(原理):自注意力解决了序列建模中的三个核心难题。第一,长距离依赖:RNN中距离为n的两个位置需要n步信息传递,梯度在反向传播时经过n步容易消失或爆炸;自注意力中任意两个位置直接相连,无论距离多远,信息传递和梯度流动都只需一步。第二,并行计算:RNN必须按时间步顺序计算,无法并行;自注意力中所有位置的计算相互独立,可以完全并行化。第三,动态权重:CNN的卷积核权重是固定的(同一套权重扫描所有位置),自注意力的权重是动态的——根据输入内容的不同,每个位置对其他位置的关注程度也不同。
import numpy as np
# 自注意力机制的核心计算:Q、K、V的生成和注意力得分
# 这是Transformer的基石,理解它就理解了现代大模型的核心
class SelfAttention:
def __init__(self, d_model=8, d_k=4):
"""
初始化自注意力层
d_model: 输入向量的维度(每个词的表示维度)
d_k: Q、K、V向量的维度(通常d_k = d_model / num_heads)
"""
np.random.seed(42)
# 三个权重矩阵:将输入映射到Q、K、V空间
# 这些矩阵是模型需要学习的参数
self.W_Q = np.random.randn(d_model, d_k) * 0.1 # 查询权重矩阵
self.W_K = np.random.randn(d_model, d_k) * 0.1 # 键权重矩阵
self.W_V = np.random.randn(d_model, d_k) * 0.1 # 值权重矩阵
def forward(self, X):
"""
前向传播:计算自注意力
X: 输入序列,形状为 (seq_len, d_model)
返回: 融合了上下文信息的输出序列,形状为 (seq_len, d_k)
"""
# 步骤1:线性变换生成Q、K、V
# Q = X · W_Q:每个位置"我想找什么"的查询向量
Q = X @ self.W_Q # 形状: (seq_len, d_k)
# K = X · W_K:每个位置"我这里有什么"的键向量
K = X @ self.W_K # 形状: (seq_len, d_k)
# V = X · W_V:每个位置"我这里的实际内容"的值向量
V = X @ self.W_V # 形状: (seq_len, d_k)
# 步骤2:计算注意力分数
# scores = Q · K^T:每个查询与所有键的点积,衡量相关性
scores = Q @ K.T # 形状: (seq_len, seq_len)
# 步骤3:缩放(除以√d_k,防止点积过大)
# 当d_k较大时,点积的方差会增大,导致softmax梯度消失
d_k = K.shape[1] # 键向量的维度
scores_scaled = scores / np.sqrt(d_k) # 缩放注意力分数
# 步骤4:Softmax归一化(将分数转换为概率分布)
# 对每一行(每个查询)做softmax,使权重和为1
exp_scores = np.exp(scores_scaled - np.max(scores_scaled, axis=1, keepdims=True))
attention_weights = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
# 步骤5:加权求和(用注意力权重对值向量加权平均)
# 每个位置的新表示 = 所有位置的值向量 × 注意力权重
output = attention_weights @ V # 形状: (seq_len, d_k)
return output, attention_weights, Q, K, V
# 演示:一个简化的句子 "我 爱 人工 智能"
# 每个词用一个8维向量表示(实际中通常是512或768维)
X = np.array([
[0.8, 0.1, 0.3, 0.2, 0.5, 0.1, 0.0, 0.4], # "我"的向量
[0.1, 0.9, 0.2, 0.1, 0.3, 0.6, 0.2, 0.1], # "爱"的向量
[0.3, 0.2, 0.8, 0.4, 0.1, 0.2, 0.5, 0.3], # "人工"的向量
[0.1, 0.1, 0.3, 0.9, 0.2, 0.1, 0.4, 0.6], # "智能"的向量
])
words = ["我", "爱", "人工", "智能"]
sa = SelfAttention(d_model=8, d_k=4)
output, attn_weights, Q, K, V = sa.forward(X)
print("=== 自注意力机制:Q、K、V 计算演示 ===\n")
# 展示Q矩阵
print("Q矩阵(查询:'我想找什么'):")
for i, w in enumerate(words):
print(f" {w}: {Q[i]}")
print("\nK矩阵(键:'我这里有什么'):")
for i, w in enumerate(words):
print(f" {w}: {K[i]}")
print("\nV矩阵(值:'我这里的实际内容'):")
for i, w in enumerate(words):
print(f" {w}: {V[i]}")
# 展示注意力权重矩阵
print("\n注意力权重矩阵(每个词对其他词的关注程度):")
print(f"{'':>6}", end="")
for w in words:
print(f" {w:>6}", end="")
print()
for i, w1 in enumerate(words):
print(f"{w1:>6}", end="")
for j in range(len(words)):
print(f" {attn_weights[i][j]:.4f}", end="")
print()
print("\n解读:")
print("- 对角线上权重通常较高,因为每个词与自身最相关")
print("- '爱'对'我'和'智能'的权重更高,说明语义上更相关")
print("- 这就是自注意力:每个词动态地决定关注哪些其他词")
大白话 这个公式可以拆成三步理解。第一步:QK^T——这是"相亲配对",每个单身人士(Q的每一行)和所有候选人(K的每一行)打分,分数越高越匹配。第二步:softmax——把打分变成"好感度百分比",所有候选人的好感度加起来等于100%。第三步:乘以V——每个候选人都有自己的"内涵"(V),好感度越高,这个人的内涵在新组合中占比越大。除以√d_k是一个技术细节——当向量维度很高时,点积的数值会很大,导致softmax变得极端(几乎全是0或1),除以√d_k让数值保持在合理范围。
二、Q、K、V的线性变换:从输入到注意力空间
是什么(定义):Q、K、V三个矩阵不是凭空产生的,而是通过对输入X进行三个独立的线性变换得到的。具体来说,给定输入X∈R^{n×d},定义三个可学习的权重矩阵W^Q, W^K, W^V∈R^{d×d_k},则Q=XW^Q, K=XW^K, V=XW^V。这三个线性变换将输入映射到不同的"语义空间"——Q空间用于提问,K空间用于被检索,V空间用于存储实际内容。这三个权重矩阵是模型在训练中学习到的核心参数。
大白话 Q、K、V就像"三个人格"。同一个输入X,经过三个不同的"变形镜"(W^Q, W^K, W^V),投射出三个不同的"分身":Q是"提问者人格"——说出我想找什么;K是"标签人格"——标出我有什么特点;V是"内容人格"——展示我的真实内涵。为什么需要三个不同的变换?因为同一个词在"提问"和"被检索"时应该有不同的表现——比如"苹果"这个词,作为提问者时(Q)可能想找"水果"相关信息,但作为被检索者时(K)应该展示自己是"科技公司"还是"水果"。
为什么(原理):使用三个不同的线性变换(而非共享权重)是自注意力成功的关键设计。如果Q=K=V(共享权重),那么注意力分数矩阵QK^T就会变成对称矩阵——位置i对位置j的关注度必须等于位置j对位置i的关注度——这限制了模型的表达能力。例如,在"猫追老鼠"这个句子中,"追"对"猫"的关注度(谁在追?)和"猫"对"追"的关注度(猫在做什么?)是不同的,需要不对称的注意力。三个独立的权重矩阵提供了这种不对称性,使模型能够学习更丰富的语义关系。
import numpy as np
# 深入理解Q、K、V线性变换的几何意义
# 演示为什么需要三个不同的权重矩阵
class QKVTransformation:
def __init__(self, d_model=6, d_k=3):
np.random.seed(123)
# 三个独立的权重矩阵——这是注意力机制的核心参数
# 每个矩阵将同一个输入映射到不同的语义空间
self.W_Q = np.random.randn(d_model, d_k) * 0.5 # Q的变换矩阵
self.W_K = np.random.randn(d_model, d_k) * 0.5 # K的变换矩阵
self.W_V = np.random.randn(d_model, d_k) * 0.5 # V的变换矩阵
def transform_and_compare(self, X):
"""演示Q、K、V变换后的差异"""
# 对同一输入做三种不同的线性变换
Q = X @ self.W_Q # 查询变换
K = X @ self.W_K # 键变换
V = X @ self.W_V # 值变换
# 计算Q和K之间的相似度
# 如果Q=K(共享权重),相似度矩阵就是对称的
QK_sim = Q @ K.T # Q和K的点积相似度
# 计算K和K之间的相似度(对称情况)
KK_sim = K @ K.T # 如果Q=K就是这个
return Q, K, V, QK_sim, KK_sim
def attention(self, X):
"""完整的自注意力计算"""
Q = X @ self.W_Q
K = X @ self.W_K
V = X @ self.W_V
d_k = K.shape[1]
# 缩放点积注意力
scores = Q @ K.T / np.sqrt(d_k)
# 数值稳定的softmax
scores = scores - np.max(scores, axis=1, keepdims=True)
attn = np.exp(scores) / np.sum(np.exp(scores), axis=1, keepdims=True)
output = attn @ V
return output, attn
# 创建示例句子:"猫 追 老鼠"
X = np.array([
[0.9, 0.1, 0.2, 0.1, 0.3, 0.1], # "猫"的向量
[0.2, 0.8, 0.3, 0.1, 0.5, 0.2], # "追"的向量
[0.1, 0.2, 0.9, 0.1, 0.1, 0.3], # "老鼠"的向量
])
words = ["猫", "追", "老鼠"]
transformer = QKVTransformation(d_model=6, d_k=3)
Q, K, V, QK_sim, KK_sim = transformer.transform_and_compare(X)
print("=== Q、K、V 线性变换的几何意义 ===\n")
print("输入X(三个词,每个6维):")
for i, w in enumerate(words):
print(f" {w}: {X[i]}")
print("\nQ矩阵(查询空间):")
for i, w in enumerate(words):
print(f" {w}提问: {Q[i]}")
print("\nK矩阵(键空间):")
for i, w in enumerate(words):
print(f" {w}标签: {K[i]}")
print("\nV矩阵(值空间):")
for i, w in enumerate(words):
print(f" {w}内容: {V[i]}")
# 比较QK^T和KK^T
print("\nQK^T 相似度矩阵(非对称——Q和K使用不同权重):")
print(f"{'':>6}", end="")
for w in words:
print(f" {w:>6}", end="")
print()
for i, w1 in enumerate(words):
print(f"{w1:>6}", end="")
for j in range(len(words)):
print(f" {QK_sim[i][j]:.4f}", end="")
print()
print("\nKK^T 相似度矩阵(对称——如果Q=K共享权重):")
print(f"{'':>6}", end="")
for w in words:
print(f" {w:>6}", end="")
print()
for i, w1 in enumerate(words):
print(f"{w1:>6}", end="")
for j in range(len(words)):
print(f" {KK_sim[i][j]:.4f}", end="")
print()
print("\n解读:")
print("- QK^T不是对称的:'追'对'猫'的关注 ≠ '猫'对'追'的关注")
print("- KK^T是对称的:如果Q=K,每个位置对另一个位置的关注度相同")
print("- 这就是需要三个独立权重矩阵的本质原因!")
大白话 线性变换就是"换一个角度看同一个东西"。输入X就像一个物体,W^Q、W^K、W^V是三个不同角度的"镜子"。从W^Q这面镜子看,你看到的是"提问者"版本;从W^K那面镜子看,你看到的是"标签"版本;从W^V那面镜子看,你看到的是"内容"版本。这三个镜像虽然源自同一个物体,但看起来不同——就像同一个人,在工作场合是"经理"(Q),在社交场合是"朋友"(K),在家庭中是"父母"(V)。同一个人的不同"角色"对应不同的互动方式。
什么用(应用):Q、K、V的线性变换是自注意力机制的核心操作。在GPT系列模型中,每个Transformer层都包含自注意力子层,其中的Q、K、V变换矩阵包含了模型的大量参数。在BERT中,自注意力使模型能够理解"bank"在"river bank"和"bank account"中的不同含义——因为上下文中的其他词会通过注意力机制影响"bank"的表示。在实际应用中,Q、K、V的思想已经超越了NLP——Vision Transformer(ViT)将图像切分为patch,每个patch作为序列中的一个位置,通过自注意力学习图像中不同区域之间的关系,在图像分类任务上超越了传统CNN。
哪些坑(缺点):Q、K、V变换的主要挑战是计算和内存开销。三个权重矩阵的参数量为3×d×d_k,对于大模型(如GPT-3的d=12288),仅Q、K、V的权重就约有4.5亿参数。另一个问题是注意力计算的O(n²)复杂度——每个位置的Q需要与所有位置的K计算点积,当序列长度n很大时(如长文档或高分辨率图像),计算和内存开销急剧增长。此外,如果不使用缩放因子√d_k,当d_k较大时,QK^T的点积值会很大,导致softmax输出接近one-hot(梯度消失),这也是为什么缩放操作是必需的。
三、缩放点积注意力:√d_k的数学原理
是什么(定义):缩放点积注意力(Scaled Dot-Product Attention)是标准自注意力的计算方式。其核心操作是QK^T/√d_k——在计算Q和K的点积后,除以√d_k(d_k是键向量的维度)。这个缩放操作看似简单,但对于注意力的稳定训练至关重要。
大白话 缩放操作就像是"调节音量"。当Q和K的维度很高时(比如512维),点积的结果会非常大(因为512个数的乘积求和),这就像把音量调到最大——softmax变得极端,几乎只关注一两个词,其他词完全被忽略。除以√d_k就像是把音量调回正常范围,让softmax的输出更加平滑,每个词都能获得合理的关注度。
为什么(原理):缩放操作的数学原理源于概率论。假设Q和K的每个分量是独立同分布的,均值为0,方差为1,那么Q和K的点积(d_k个乘积之和)的均值为0,方差为d_k。当d_k很大时(如64或128),点积的值可能达到很大的正数或负数,使得softmax函数的输出接近one-hot向量(梯度接近0)。除以√d_k将方差归一化回1,使得点积值保持在合理范围,softmax的梯度更加平滑。
import numpy as np
import matplotlib
matplotlib.use('Agg') # 非交互式后端
# 演示缩放操作的重要性:有无√d_k对softmax的影响
# 这是理解为什么需要缩放的关键实验
def demo_scaling_effect():
"""演示缩放对softmax分布的影响"""
np.random.seed(42)
print("=== 缩放操作(√d_k)的重要性演示 ===\n")
# 模拟不同维度下的点积效果
for d_k in [4, 16, 64, 256]:
print(f"\n--- 键向量维度 d_k = {d_k} ---")
# 模拟Q和K的点积:均值为0,方差为d_k的随机值
# 假设Q和K的每个分量 ~ N(0,1),则点积 ~ N(0, d_k)
dot_products = np.random.randn(5) * np.sqrt(d_k)
print(f"原始点积值(方差≈{d_k}):{dot_products}")
# 不缩放:直接softmax
# 当d_k很大时,最大值会主导softmax输出
raw_softmax = np.exp(dot_products) / np.sum(np.exp(dot_products))
# 缩放后:除以√d_k
scaled = dot_products / np.sqrt(d_k)
scaled_softmax = np.exp(scaled) / np.sum(np.exp(scaled))
print(f"不缩放的Softmax输出:{np.round(raw_softmax, 4)}")
print(f" 最大值占比:{np.max(raw_softmax):.4f}(越大越接近one-hot)")
print(f"缩放后的Softmax输出:{np.round(scaled_softmax, 4)}")
print(f" 最大值占比:{np.max(scaled_softmax):.4f}")
# 计算梯度(softmax梯度 = softmax_i * (1 - softmax_i))
raw_grad = raw_softmax * (1 - raw_softmax)
scaled_grad = scaled_softmax * (1 - scaled_softmax)
print(f"不缩放的平均梯度:{np.mean(raw_grad):.6f}(越小梯度越容易消失)")
print(f"缩放后的平均梯度:{np.mean(scaled_grad):.6f}")
demo_scaling_effect()
# 额外演示:注意力权重的可视化
print("\n\n=== 缩放对注意力分布的影响可视化 ===\n")
# 模拟一个5个词的句子,Q·K^T的点积值
scores = np.array([2.0, 1.5, 0.5, -0.5, -1.0]) # 未缩放的注意力分数
# 场景1:d_k=4,缩放因子=2
d_k_small = 4
scaled_small = scores / np.sqrt(d_k_small)
attn_small = np.exp(scaled_small) / np.sum(np.exp(scaled_small))
# 场景2:d_k=64,缩放因子=8
d_k_large = 64
scaled_large = scores / np.sqrt(d_k_large)
attn_large = np.exp(scaled_large) / np.sum(np.exp(scaled_large))
# 场景3:不缩放(假设d_k=64但不除以√d_k)
attn_no_scale = np.exp(scores) / np.sum(np.exp(scores))
print("同样的注意力分数,不同缩放处理:")
print(f"原始分数: {scores}")
print(f"不缩放: {np.round(attn_no_scale, 4)}")
print(f"d_k=4缩放: {np.round(attn_small, 4)}")
print(f"d_k=64缩放: {np.round(attn_large, 4)}")
print("\n解读:")
print("- 不缩放时,注意力分布更极端(高分词几乎独占注意力)")
print("- d_k越大,缩放效果越明显,分布越平滑")
print("- 平滑的分布意味着梯度更稳定,训练更容易")
大白话 softmax就像一个"聚光灯"。如果某个分数特别大,聚光灯就只照那一个人,其他人全在黑暗中——梯度为零,学不到东西。如果分数差不多大,聚光灯就均匀照所有人——梯度适中,每个人都能学到东西。除以√d_k的目的就是防止聚光灯"过曝"——让灯光合理分布,训练更稳定。
什么用(应用):缩放点积注意力是Transformer、BERT、GPT等所有现代大模型的标准注意力实现方式。在GPT-4中,d_k通常为128(12288维/96头),如果不做缩放,点积方差为128,softmax将极度稀疏,模型几乎无法训练。缩放操作虽然在数学上只是一个除法,但它是深度学习实践中被验证的"关键技巧"——没有它,深层Transformer的训练将极其困难。
哪些坑(缺点):缩放操作本身没有明显缺点,但它的存在暗示了一个更深层的问题:标准点积注意力假设Q和K的分量是独立同分布的,而实际训练中,Q和K的分布可能偏离这一假设(例如,某些维度的方差远大于其他维度)。此外,即使有了缩放,当序列长度n非常大时,softmax的计算仍然涉及对n个指数的求和,数值稳定性(防止溢出)是一个实际工程问题——通常通过减去最大值(x - max(x))来稳定计算。这也是FlashAttention等高效注意力实现需要处理的核心数值问题。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| 自注意力(Self-Attention) | 序列中每个位置通过Q、K、V与其他所有位置交互 | Transformer的核心组件,现代大模型的基础 | 多头注意力、交叉注意力 |
| Query(Q) | 查询向量,表示"当前位置想找什么信息" | 驱动注意力计算,决定关注哪些位置 | Key、Value、W^Q |
| Key(K) | 键向量,表示"当前位置能提供什么信息标签" | 与Q计算相似度,决定被关注的程度 | Query、Value、W^K |
| Value(V) | 值向量,表示"当前位置的实际内容" | 被注意力权重加权求和,形成新表示 | Query、Key、W^V |
| 缩放因子√d_k | 防止点积值过大导致softmax梯度消失 | 保证深层Transformer训练稳定性 | 梯度消失、softmax |
| 点积注意力 | 通过QK^T计算位置间相关性 | 注意力权重计算的核心运算 | 矩阵乘法、softmax |
重点答疑
Q1: 为什么Q和K要用不同的权重矩阵,而不是共享?
如果Q=K(共享权重),那么注意力分数矩阵QK^T=KK^T是对称的——位置i对位置j的关注度必须等于位置j对位置i的关注度。但在自然语言中,这种对称性不成立:在"猫追老鼠"中,"追"对"猫"的关注(谁在追?)和"猫"对"追"的关注(猫在做什么?)是不同的语义关系。使用不同的W^Q和W^K,允许模型学习不对称的注意力模式,这是理解复杂语义关系的关键。
Q2: 除以√d_k的缩放因子可以换成其他值吗?
理论上可以,但√d_k有严格的数学依据。假设Q和K的分量独立同分布,均值为0方差为1,则QK^T的方差为d_k,除以√d_k将方差归一化回1。如果用其他缩放因子(如d_k或log(d_k)),要么缩放不足(方差仍大于1),要么过度缩放(方差小于1),都会影响训练效果。在实践中,√d_k是经过大量实验验证的最优选择,几乎所有的Transformer实现都使用这个缩放因子。
Q3: 自注意力和交叉注意力(Cross-Attention)有什么区别?
自注意力中,Q、K、V都来自同一个输入序列X——"自己关注自己"。交叉注意力中,Q来自一个序列(如解码器的当前状态),K和V来自另一个序列(如编码器的输出)——"关注别人"。在Transformer的编码器-解码器结构中,编码器使用自注意力,解码器既使用自注意力(关注已生成的部分)又使用交叉注意力(关注编码器的输出)。自注意力学习序列内部的依赖关系,交叉注意力学习序列之间的对齐关系。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| Self-Attention | /self əˈtenʃən/ | 自注意力,序列中每个位置关注所有其他位置的机制 |
| Query | /ˈkwɪri/ | 查询向量,表示"我想找什么" |
| Key | /kiː/ | 键向量,表示"我这里有什么" |
| Value | /ˈvæljuː/ | 值向量,表示"我这里的实际内容" |
| Scaled Dot-Product | /skeɪld dɑːt ˈprɑːdʌkt/ | 缩放点积,QK^T/√d_k的注意力计算方式 |
| Softmax | /ˈsɒftmæks/ | 归一化指数函数,将分数转换为概率分布 |
| Linear Transformation | /ˈlɪniər ˌtrænsfərˈmeɪʃən/ | 线性变换,通过矩阵乘法将输入映射到新空间 |
| Attention Weight | /əˈtenʃən weɪt/ | 注意力权重,表示某个位置被关注的程度 |
| Context Vector | /ˈkɑːntekst ˈvektər/ | 上下文向量,注意力加权求和后的输出表示 |
| Gradient Vanishing | /ˈɡreɪdiənt ˈvænɪʃɪŋ/ | 梯度消失,深层网络中梯度趋于零导致训练困难 |
面试练习
Q1 [单选] 自注意力机制中,Q、K、V分别代表什么?
- A. Q=Query查询, K=Knowledge知识, V=Vector向量
- B. Q=Query查询, K=Key键, V=Value值
- C. Q=Quality质量, K=Key关键, V=Volume容量
- D. Q=Quick快速, K=Kernel核, V=Variable变量
解答:Q、K、V分别代表Query(查询)、Key(键)、Value(值),这是从信息检索中的"查询-键-值"模型借鉴而来的概念。Q用于提问,K用于被检索,V存储实际内容。
Q2 [单选] 自注意力公式中,为什么要除以√d_k?
- A. 为了增加计算速度
- B. 防止点积值过大导致softmax梯度消失
- C. 为了减少内存使用
- D. 为了让注意力权重之和为1
解答:除以√d_k是为了缩放。当d_k很大时,QK^T的点积值方差为d_k,导致softmax输出接近one-hot(梯度接近0)。除以√d_k将方差归一化回1,使softmax输出平滑,梯度正常流动。
Q3 [单选] 自注意力机制的计算复杂度是?
- A. O(n)
- B. O(n log n)
- C. O(n²)
- D. O(2^n)
解答:每个位置的Q需要与所有n个位置的K计算点积(QK^T),因此复杂度为O(n²),其中n为序列长度。这是Transformer处理长序列时的瓶颈,催生了稀疏注意力、FlashAttention等优化。
Q4 [多选] 关于自注意力机制,以下哪些说法是正确的?
- A. 每个位置可以直接与序列中任意位置交互,路径长度O(1)
- B. Q、K、V通过对输入X进行三个不同的线性变换得到
- C. 自注意力中Q和K必须使用相同的权重矩阵
- D. 注意力权重矩阵的每一行是一个概率分布(行和为1)
- E. 自注意力的计算复杂度与序列长度呈线性关系
解答:自注意力的核心优势是任意位置之间直接交互(路径长度O(1)),Q、K、V通过三个独立的线性变换得到,注意力权重经过softmax后每行和为1。但Q和K使用不同的权重矩阵(否则注意力矩阵对称),复杂度为O(n²)而非线性。
Q5 [单选] 在自注意力中,如果输入序列长度为n,嵌入维度为d,注意力权重矩阵的形状是什么?
- A. (d, d)
- B. (n, n)
- C. (n, d)
- D. (d, n)
解答:注意力权重矩阵由QK^T得到,Q的形状为(n, d_k),K^T的形状为(d_k, n),因此QK^T的形状为(n, n)。矩阵中的元素(i, j)表示位置i对位置j的注意力权重。
Q6 [单选] 自注意力中,如果没有缩放操作(不除以√d_k),当d_k=64时可能出现什么问题?
- A. 注意力权重全为0
- B. 注意力权重接近one-hot,梯度消失
- C. 计算速度变慢
- D. 输出维度改变
解答:当d_k=64时,QK^T点积的方差约为64。不缩放时,softmax输入值很大,输出接近one-hot(只有一个位置权重接近1,其余接近0),此时梯度接近0,模型无法有效学习。
Q7 [多选] 以下哪些是自注意力相对于RNN的优势?
- A. 可以完全并行计算
- B. 任意两个位置之间信息传递路径长度为O(1)
- C. 计算复杂度更低
- D. 更好地处理长距离依赖
- E. 参数量更少
解答:自注意力可以并行计算所有位置,任意位置间直接交互(路径O(1)),因此能更好地处理长距离依赖。但计算复杂度为O(n²),高于RNN的O(n·d²)(当n较大时),且参数量没有必然更少。
Q8 [单选] 自注意力的输出中,每个位置的新表示是如何得到的?
- A. 对输入X直接做线性变换
- B. 取所有位置Q向量的平均
- C. 对所有位置的值向量V做注意力加权平均
- D. 取注意力权重最大的位置的值向量
解答:输出 = Attention(Q,K,V) = softmax(QK^T/√d_k)V。每个位置的新表示是所有位置的值向量V的加权平均,权重由注意力分数决定。这使得每个位置都能融合全局信息。
Q9 [多选] 关于Q、K、V的线性变换,以下哪些说法是正确的?
- A. W^Q、W^K、W^V是三个独立的可学习权重矩阵
- B. 三个权重矩阵的形状通常相同(d×d_k)
- C. 使用相同的权重矩阵可以提升模型性能
- D. 不同的权重矩阵允许模型学习非对称的注意力模式
- E. 线性变换是可选的,可以省略直接使用输入X
解答:Q、K、V通过三个独立的权重矩阵W^Q、W^K、W^V变换得到,它们形状相同(d×d_k),独立学习。这种设计允许非对称注意力(位置i对j的关注≠位置j对i的关注),这是理解复杂语义的关键。如果共享权重,注意力矩阵变成对称的,表达能力受限。
Q10 [单选] 在Transformer中,自注意力层的输出维度通常如何与输入维度保持一致?
- A. 通过池化操作
- B. 通过额外的线性变换(输出投影矩阵W^O)
- C. 直接使用V的维度
- D. 通过padding补齐
解答:多头注意力中,每个头的输出维度为d_k=d/h,拼接后总维度为d。但为了增加模型的灵活性,还会通过一个额外的输出投影矩阵W^O∈R^{d×d}进行线性变换,使输出维度与输入维度保持一致。