标准回答
一、先把结论讲清楚
可以先把公式说出来:Self-Attention = softmax(QKᵀ / √d_k) V。
一、Q、K、V 分别干什么?
Q 可以理解成“我想找什么”,K 是“我能被什么匹配到”,V 是“真正要汇总的信息”。
二、拆开机制和判断点
输入 X 先分别乘 Wq、Wk、Wv 得到 Q、K、V。
二、为什么要除以 √d_k?
QKᵀ 的点积维度越大,数值方差越大。直接进 softmax 容易饱和,梯度会变小。所以要除以 √d_k,让分数尺度更稳定。
三、补上例子、边界和取舍
三、代码里最容易错什么?
softmax 要沿最后一维做,而且要先减去每行最大值,防止 exp 溢出。如果是解码器自注意力,还要在 softmax 前加 causal mask。下面给出纯 NumPy 实现:
面试里可以补一句:手撕代码:用 NumPy/PyTorch 实现 Self 在大模型场景下通常要同时权衡效果、延迟、成本和安全边界。回答时最好带一个例子,比如上下文过长、幻觉、缓存命中或工具调用失败时,系统应该如何降级和观测。
import numpy as np
def softmax(x, axis=-1):
# 减去最大值保证数值稳定,避免 exp 溢出
x = x - np.max(x, axis=axis, keepdims=True)
e = np.exp(x)
return e / np.sum(e, axis=axis, keepdims=True)
def self_attention(X, Wq, Wk, Wv):
# X: (L, d_model) 输入序列;W*: (d_model, d_k) 投影矩阵
Q = X @ Wq # (L, d_k)
K = X @ Wk # (L, d_k)
V = X @ Wv # (L, d_v)
d_k = Q.shape[-1]
scores = Q @ K.T / np.sqrt(d_k) # (L, L) 缩放点积
attn = softmax(scores, axis=-1) # 每行归一化为注意力权重
out = attn @ V # (L, d_v) 加权聚合
return out, attn
if __name__ == '__main__':
np.random.seed(0)
L, d_model, d_k = 4, 8, 8
X = np.random.randn(L, d_model)
Wq = np.random.randn(d_model, d_k)
Wk = np.random.randn(d_model, d_k)
Wv = np.random.randn(d_model, d_k)
out, attn = self_attention(X, Wq, Wk, Wv)
print(out.shape, attn.sum(axis=-1)) # (4, 8) 权重每行和为 1常见误区
⚠️ 常见踩坑
误区一:忘记除以 √d_k。 不缩放会让 softmax 更容易饱和,训练不稳。
误区二:softmax 不减最大值。 分数稍大就可能 exp 溢出,最后得到 NaN。
追问
追问 1:复杂度是多少?如何优化?
序列长度 L 时,QKᵀ 和 attn@V 都是 O(L²·d),显存也和 L² 强相关。优化可以提 FlashAttention、稀疏/线性注意力,以及推理时的 KV Cache。
追问 2:如何加入因果掩码实现解码器自注意力?
在 softmax 之前,把未来位置的 scores 设成 -inf 或很大的负数。这样 softmax 后未来位置权重就是 0,保证当前位置 只能看见自己和过去 token。
🔗 相似问题
同一考点的不同问法,换着练更稳
- 中级深度学习
Transformer 计算 attention 用点乘还是加法?为什么?并解释为何要除以根号 dk
- 高级大语言模型
手撕代码:实现多头注意力(Multi-Head Attention)
- 中级深度学习
self-attention 中的 Q 和 K 分别用来做什么?如果让 Q=K 用同一个矩阵会怎样?
- 中级大语言模型
手撕代码:实现正弦位置编码(Positional Encoding)
- 高级大语言模型
解释 DeltaNet 如何利用 Delta Rule 实现线性注意力,与传统 softmax attention 的优劣对比
- 中级大语言模型
请解释 Transformer 中 Self-Attention 的计算过程
延伸学习
按主题分类的相关资源,便于系统复习
