这些题共同在检查什么
这些题看着散——损失函数、掩码、归一化、RNN、CNN——其实只分三类:梯度有没有一条不被衰减的路、训练和推理是不是同一件事、不变性是结构自带的还是花钱买的。认出是哪一类,答案就不用背了。
下面每题给三层:一句话先说什么 → 撑住第一次追问 → 能拉开差距的那一层。
先立骨架:attention 到底怎么算
后面三题都挂在这张图上,先立着。
X [B, T, d_model]
├─ Q = X·Wq ─┐ [B, h, Tq, d_k]
├─ K = X·Wk ─┤ split 成 h 个头 [B, h, Tk, d_k]
└─ V = X·Wv ─┘ [B, h, Tk, d_v]
│
① scores = Q·Kᵀ / √d_k [B, h, Tq, Tk]
② scores = scores + mask ← 因果掩码在这一步
③ A = softmax(scores, -1) 每行和为 1
④ out = A·V [B, h, Tq, d_v]
⑤ concat 各头,过 Wo
$$\text{Attention}(Q,K,V) = \text{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}} + M\right)V$$
怎么读 $A$:第 $i$ 行是「token $i$ 把注意力分给谁」的概率分布。加了因果掩码(causal mask)后,第 $i$ 行只在列 $\le i$ 上非零。第 1 行是退化的——它只能看自己,softmax 出来恰好 1.0。
为什么除 $\sqrt{d_k}$,不是 $\sqrt{d_v}$ 或 $\sqrt{d_{\text{model}}}$:分数来自 $QK^\top$,每个分数恰好加了 $d_k$ 个乘积。Q/K 最后一维必须相同,V 的特征维可以不同;只是在标准 MHA 中通常令 $d_v=d_k$。例如 768 维、12 个头,单头 $d_k=64$,所以除 $\sqrt{64}$。方差(variance)推导见多头注意力,$Q=K$ 的追问见同一篇的面试常见问题。
时间 $O(T^2 d)$、显存 $O(T^2)$。那个 $T\times T$ 矩阵就是长上下文的瓶颈,也是 FlashAttention 的动机:根本不把它算出来存下。
参考实现在 00-foundations/code/attention_numpy.py。
第一类:梯度有没有一条不被衰减的路
三道题,同一个骨架。只要出现「连乘」,就要问能不能换成「加法」。
p = σ(z),y 是 0/1。写出 MSE 和 BCE,说说该用哪个
$$\mathcal{L}_{\text{MSE}} = (p-y)^2 \qquad \mathcal{L}_{\text{BCE}} = -\big[y\log p + (1-y)\log(1-p)\big]$$
这题真正问的是梯度,不是让你默写公式。 关键事实:$\sigma'(z) = p(1-p)$。
$$\frac{\partial \mathcal{L}_{\text{MSE}}}{\partial z} = 2(p-y)\cdot p(1-p) \qquad\qquad \frac{\partial \mathcal{L}_{\text{BCE}}}{\partial z} = p - y$$
BCE 那边,$\sigma'$ 被约掉了:
$$\frac{\partial \mathcal{L}_{\text{BCE}}}{\partial p} = \frac{p-y}{p(1-p)} \;\Longrightarrow\; \frac{\partial \mathcal{L}}{\partial z} = \frac{p-y}{p(1-p)}\cdot p(1-p) = p-y$$
后果:$y=1$ 而模型极自信地说 0($z\to-\infty$,$p\to0$)时——
- MSE 梯度 $\approx 2(0-1)\cdot 0\cdot 1 = 0$。预测偏差最大时梯度反而消失,模型学不动。
- BCE 梯度 $= -1$。预测偏差最大时仍能保留有效梯度。
再深一层:logistic regression 里 BCE 关于权重是凸的,MSE + sigmoid 不是。
别把优化和校准(calibration)混为一谈:对二分类概率,平方误差对应 Brier score,也是 proper scoring rule。它和 BCE 在理想总体目标下都偏好真实条件概率,但有限数据和受限模型并不保证校准。这里比较的是梯度,而不是谁“有资格”预测概率。
工程上:永远用 binary_cross_entropy_with_logits。先算 $p$ 再取 log,$p$ 下溢到 0 就 $-\infty$。稳定形式:
$$\mathcal{L} = \max(z,0) - zy + \log\big(1+e^{-|z|}\big)$$
RNN 和 LSTM 的区别
先说:普通 RNN 难学长距离关系,一个原因是梯度要沿时间反复传递。
$$\frac{\partial h_t}{\partial h_{t-k}} = J_t J_{t-1}\cdots J_{t-k+1},\qquad J_t=\operatorname{diag}\big(\tanh'(a_t)\big)W_h$$
上式是状态对状态的 Jacobian;反向传播乘的是这些矩阵的转置。虽然每一步共享 $W_h$,激活值(activations)不同,$J_t$ 也会不同。如果所有 $\|J_t\|_2$ 都不超过某个 $c<1$,这条路径的梯度就会随距离衰减。某一步的范数大于 1 则不等于一定爆炸,还要看方向和后续乘积。
梯度裁剪能限制一次更新的幅度,不能把已经消失的信号找回来,也不能代替对训练不稳定原因的排查。
LSTM 的修法:加一条 cell state,更新是加法的。
$$c_t = f_t \odot c_{t-1} + i_t \odot g_t, \qquad h_t = o_t \odot \tanh(c_t)$$
把门值暂时固定,沿 cell state 的直接通路有 $\partial c_t/\partial c_{t-1}=\operatorname{diag}(f_t)$。这条通路只做逐元素缩放;$f_t$ 接近 1 时,更容易保留长距离信号。但总导数还包含门值对历史状态的依赖,不能据此说 LSTM 完全解决了梯度消失(vanishing gradient)。
一句话:LSTM 把「每步乘一个矩阵」换成了「门控(gating)的加法累积」。
细节:sigmoid 把门值限制在 0 到 1,tanh 让候选(candidate)值有正有负。这符合门和内容各自的作用,也会影响梯度;饱和问题并没有凭空消失。
两个能拔高的连接:
- 可以把 cell state 和残差流放在一起理解,但不能当成同一个结构。 两者都有加法通路;LSTM 沿时间用门控制记忆,残差连接(residual connection)通常沿深度保留恒等项。
- Transformer 的优势不只有一个。 训练时可以并行处理序列位置,attention 缩短了可见位置之间的信息路径,也让模型按内容访问历史。LSTM 缓解了长依赖问题,但并没有消除它;自回归 Transformer 的生成也仍然逐 token 进行。
为什么要 LayerNorm?为什么不用 BatchNorm?放在哪里?
为什么要归一化:让子层接收到的数值尺度更可控,通常更容易训练深层模型。它和初始化、残差缩放、优化器一起起作用,不代表所有没有归一化的网络都训不动。
为什么 Transformer 常用 LayerNorm,而不是 BatchNorm?
- 对变长序列,如果统计轴包含 padding 又没有排除它,BN 的统计量会受影响;
- BN 依赖 batch 大小与组成,默认在推理时改用 running statistics;自回归解码(autoregressive decoding)的负载与训练不同,需要特别处理;
- 若把时间轴纳入训练时的 BN 统计,未来 token 会影响前面的位置,因果 attention mask 并不能阻止这种泄漏;
- 按特征维做的 token-wise LN 不混合 batch 或时间位置,归一化本身在训练和推理使用相同统计规则。这里不是说整个模型两种模式完全相同。
放哪里——两种都要能写:
Post-LN(2017 原版) Pre-LN(现代)
x = LN(x + Attn(x)) x = x + Attn(LN(x))
x = LN(x + FFN(x)) x = x + FFN(LN(x))
...
x = LN(x) ← 常见的 final LN
为什么常用 Pre-LN:它在残差主干保留恒等项,梯度不必每层都先穿过 LN,所以对 warmup 的依赖更低;但这不保证所有模型都能取消 warmup。Jacobian 推导和 Xiong 等人的结论适用到哪里,见归一化第 3 问。
实现时检查:Pre-LN 的最终残差输出通常还会经过一次归一化,再进入输出头。这是常见训练配方,不是数学上唯一可行的做法;复现时要跟具体模型的配置一致。
再往下:RMSNorm 不减均值,通常也没有加性 bias,只按 RMS 缩放并学习增益。它说明某些模型不做中心化也能训得很好,不说明中心化在所有场景都没有作用。参见 LayerNorm 分析 与 RMSNorm。
第二类:训练和推理必须是同一件事
为什么要 causal mask?放在哪一步?为什么放那儿?
先说:它让「一次前向并行训练 $T$ 个位置」等价于「一个位置一个位置地训」。
自回归目标是 $\prod_t p(x_t\mid x_{<t})$。常见实现里,位置 $t$ 输入 $x_t$,预测 $x_{t+1}$;看见自己没有问题,看见未来的 $x_{t+1}$ 才会泄漏答案。causal mask 允许看当前位置和过去,不允许看未来,让训练时的条件信息与生成时一致。
所以掩码不是为了让模型更强,是为了让训练和推理是同一个模型。 没有它你得跑 $T$ 次前向。
放在骨架图的第 ② 步:缩放点积(inner product)之后、softmax 之前。
mask = np.triu(np.ones((T, T), dtype=bool), k=1) # 严格上三角 = 禁止
scores = np.where(mask, -np.inf, scores) # 加 -inf,不是置零
为什么必须在 softmax 之前(追问重点):加 $-\infty$ 后 $e^{-\infty}=0$,softmax 在剩余位置上重新归一化,数学上等价于「那些位置不存在」。
在 softmax 之后置零则破坏归一化——每行不再和为 1,而且破得不均匀:位置 1 只能看自己,被删掉的质量最多,输出被缩得最狠。等于给每个位置乘了一个意义不明的衰减系数。用两个分数算一遍,见多头注意力。
工程细节:全被屏蔽的行没有合法的概率分布。直接对全 -inf 行做 softmax 会产生 NaN;换成有限负数可能得到均匀分布,也不是正确答案,而且 -1e9 超出了 fp16 的有限范围。应明确处理无有效 key 的 query,检查所用 attention API 的全掩码行为,并让 padding 不参与后续 loss。softmax 后屏蔽再正确归一化在数学上可以等价,但只置零不行。
BERT 为什么不需要:它不是自回归的,训练目标是 MLM,双向可见是设计而不是漏洞。
第三类:不变性是免费的还是买来的
CNN 里图像旋转会不会影响特征提取?
会,而且影响很大。 先把两个被混用的概念分开:
卷积自带的是平移等变(equivariance),不是不变(invariance):
$$f(T_x(I)) = T_x\big(f(I)\big)$$
输入平移,特征图跟着平移同样的量。不变性(输出完全不变)是后面 pooling 给的,而且只是近似的、局部的。
旋转:既不等变也不不变。 卷积核朝向固定,一条 45° 的边和一条 135° 的边激活的是完全不同的滤波器,网络没有任何结构上的理由把它们当成同一个东西。
为什么这个不对称是结构性的:平移等变来自权重共享 + 局部性,是算子白送的;旋转等变不在算子里。所以只有两条路:
- 用数据买:旋转增广。网络学出一组冗余滤波器、每个朝向一份。代价是花模型容量买不变性,且只覆盖增广过的角度范围。
- 改算子:Group-equivariant CNN、Steerable CNN、Harmonic Networks;或 Spatial Transformer——让网络先学会把输入摆正。
一句话收:平移等变是卷积自带的,旋转等变或不变要付钱,付法是数据或者算子。
能让对话变有意思的一条:连平移不变性都没有大家以为的那么好——带 stride 的下采样会引入混叠,输入平移一个像素预测就可能翻。解法是抗混叠的模糊下采样。
加一道:Egg Drop 为什么换一个状态就简单了
有 $k$ 个鸡蛋、$n$ 层楼,记 $T(k,n)$ 为最坏情况下确定临界楼层所需的最少扔蛋次数。直接想法是二维 DP:
$$T(k,n)=1+\min_{1\le x\le n}\max\big(T(k-1,x-1),\;T(k,n-x)\big).$$
在第 $x$ 层扔:碎了,只能往下且少一个蛋;没碎,只能往上且蛋数不变。
min 选楼层,max 表示要为较坏的分支负责。它是对的,但每个状态还要枚举 $x$。
更好的问法不是「这些楼需要几步」,而是:给我 $m$ 次行动和 $k$ 个鸡蛋,最多能覆盖多少层? 记作 $F(m,k)$:
$$F(m,k)=F(m-1,k-1)+1+F(m-1,k),\qquad F(0,k)=F(m,0)=0.$$
第一次扔下去后,碎的分支能覆盖下面 $F(m-1,k-1)$ 层,当前层算 1,没碎的分支能覆盖上面 $F(m-1,k)$ 层。于是每加一次行动,搜索空间就是两个旧子空间再加当前点。
def min_moves(eggs, floors):
cover = [0] * (eggs + 1)
moves = 0
while cover[eggs] < floors:
moves += 1
for k in range(eggs, 0, -1):
cover[k] = cover[k] + cover[k - 1] + 1
return moves
倒序更新是为了让右侧都来自上一轮。100 层时:2 个鸡蛋要 14 次,因为 $1+\cdots+14=105$;3 个鸡蛋要 9 次,因为 $F(8,3)=92<100$,而 $F(9,3)=129\ge100$。
面试里的核心:原题是「鸡蛋 × 楼层」二维 minimax DP;反转问题后变成「行动次数 × 鸡蛋」的覆盖 DP,并能压成一维。直觉上的“每扔一次就排除一段楼层”就是这个递推在计算的东西。
再加一道:Binary Tree Maximum Path Sum
这题最容易错的地方,是混淆「当前节点形成的完整答案」和「可以返回给父节点的状态」。
定义 $G(u)$:必须从节点 $u$ 出发,只能沿一侧向下延伸的最大路径和。空节点返回 0,负贡献直接丢掉:
$$L=\max(0,G(u.left)),\qquad R=\max(0,G(u.right)).$$
以 $u$ 为最高点的完整路径可以同时使用左右两侧:
$$\text{candidate}=u.val+L+R.$$
但返回给父节点时不能分叉,只能选择一侧:
$$G(u)=u.val+\max(L,R).$$
def max_path_sum(root):
best = float("-inf")
def gain(node):
nonlocal best
if node is None:
return 0
left = max(0, gain(node.left))
right = max(0, gain(node.right))
best = max(best, node.val + left + right)
return node.val + max(left, right)
gain(root)
return best
每个节点只访问一次,时间 $O(n)$;递归栈为 $O(h)$。全局答案必须初始化成 $-\infty$,不能是 0,否则全负数树会错误地选择一条不存在的空路径。
如果输入是 list,先问清楚表示法:带 None 的 level-order serialization,还是
heap-style 的 left=2i+1, right=2i+2?对稀疏树,二者不等价。面试官给了不熟悉的序列化格式时,先定义输入语义不是拖延,是在保护算法的正确性。
附:Transformer 结构怎么讲
被问「讲一下结构」时别按论文插图顺序背,按每个部件解决什么问题讲:
| 部件 | 它解决什么 |
|---|---|
| 位置信息 | attention 是置换等变的,打乱输入输出跟着乱——它自己看不出词序 |
| 残差 | 给梯度一条恒等通路(思路类似 LSTM 的 cell state,但结构不同) |
| $\sqrt{d_k}$ 缩放 | 点积是 $d_k$ 项之和,方差随 $d_k$ 增长;不缩放则 softmax 容易接近 one-hot,经它传回分数的梯度会变得很小 |
| 多头 | 一个 softmax 只能表达一种注意力模式;多头在不同子空间并行关注不同关系 |
| FFN(约 4× 放大) | 大部分参数在这里,有一种常见解读把它看作 key-value 记忆 |
| causal mask | 见上,训练/推理一致性 |
然后主动说这一段,它区分「读过 2017 那篇」和「知道现在的模型长什么样」:
| 2017 原版 | 现代 LLM | 为什么换 |
|---|---|---|
| Post-LN | Pre-LN + RMSNorm | Pre-LN 降低对 warmup 的依赖、更容易堆深;RMSNorm 省去中心化,计算更省 |
| 正弦 / 学习式绝对位置 | RoPE | 位置只以相对距离 $n-m$ 进入 $q\cdot k$,分数仍取决于内容;超出训练长度仍要靠 RoPE scaling 等扩展 |
| ReLU FFN | SwiGLU | 同等算力下更好 |
| MHA | GQA / MQA,或 MLA(只缓存低维 latent) | KV cache 是推理显存瓶颈 |
| 每层都是 softmax attention | 部分层换成线性注意力 / 循环状态,例如 Qwen3.5-397B-A17B 每 4 层里 3 层是 Gated DeltaNet(截至 2026-10) | 状态大小固定,这些层 decode 时不再读完整 KV |
继续阅读
- Vanilla Transformer · 多头注意力 · Decoder-only
- 归一化 · 残差连接
- 语言模型的目标函数
- 参考实现:
attention_numpy.py从零实现,attention_torch.py对照
快速学习:八股题其实在追什么
一分钟总答法,以及最容易漏掉的边界条件
快速记忆
大多数 Transformer 八股都在追四件事:shape 是否闭合、信息是否因果、梯度是否能传、训练与推理是否一致。
面试回答
我会先写清输入输出 shape,再说明信息沿哪条轴流动;随后检查 causal mask 或数据边界有没有泄漏,检查 residual path 和 normalization 对梯度的影响,最后比较 training、prefill 与 decode 是否执行等价计算。
深挖:为什么先讲 invariant 比先背结论更稳?
诸如 Q/K 维度必须一致、V 维度可以不同、KV Cache 必须与 full forward 等价,都不是孤立事实,而是由矩阵乘法和语义 invariant 推出来的。面试官改变符号或实现时,背句子容易失效;从 shape、causality 和 equivalence 重新推导仍然成立。
本页术语 10
- 因果掩码 causal mask
- 禁止位置 i 看见 i 之后的内容
- 方差 variance
- 估计量的波动幅度;重要性加权最容易在这里爆炸
- 校准 calibration
- 模型给出的置信度是否等于真实正确率
- 激活值 activations
- 神经网络前向计算的中间输出;训练时会保存部分中间结果供反向使用,显存占用取决于长度、batch 和重计算策略
- 梯度消失 vanishing gradient
- 激活饱和导致梯度趋近 0,深层学不动
- 门控 gating
- 用可学习权重控制信息写入、保留或输出
- 候选 candidate
- 召回阶段捞出来、等待精排挑选的内容
- 残差连接 residual connection
- x + f(x),让梯度有一条不经过 f 的通路
- 自回归解码 autoregressive decoding
- 把刚生成的 token 作为下一步输入并逐步继续生成
- 点积 inner product
- 两个向量对应分量相乘再求和,双塔的打分方式