我们继续用 "我 爱" 这个极简的例子。为了让你看清完整的闭环,我们设定:
- 输入 X =
[[1, 0], [0, 1]](2行2列) - 瞎填的 WqW_qWq =
[[1, 1], [1, 1]](2行2列) - 瞎填的 WkW_kWk =
[[1, 1], [1, 1]](2行2列) - 瞎填的 WvW_vWv =
[[1, 1], [1, 1]](2行2列)
准备好草稿纸,我们一步一步来,见证奇迹!
第一步:前向传播(生成 Q, K, V)
1. 算 Q = X × WqW_qWq
[[1, 0], [0, 1]] × [[1, 1], [1, 1]] = Q = [[1, 1], [1, 1]]
2. 算 K = X × WkW_kWk
[[1, 0], [0, 1]] × [[1, 1], [1, 1]] = K = [[1, 1], [1, 1]]
3. 算 V = X × WvW_vWv
[[1, 0], [0, 1]] × [[1, 1], [1, 1]] = V = [[1, 1], [1, 1]]
第二步:计算 Attention 权重(心动分数)
4. 算 Q × K 的转置
K 的转置 KTK^TKT = [[1, 1], [1, 1]]
Q × KTK^TKT = [[1, 1], [1, 1]] × [[1, 1], [1, 1]] = [[2, 2], [2, 2]]
5. 除以 d\sqrt{d}d (这里特征维度 d=2,2≈1.414\sqrt{2} \approx 1.4142 ≈1.414)
[[2, 2], [2, 2]] ÷ 1.414 = [[1.414, 1.414], [1.414, 1.414]]
6. 做 Softmax(归一化变成概率)
因为每个数字都一样,Softmax 后,每行的概率加起来等于 1。
所以权重矩阵 Weights = [[0.5, 0.5], [0.5, 0.5]]
(大白话:因为瞎填的参数全一样,模型不知道谁跟谁配,干脆对"我"和"爱"各分配 50% 的关注度)
第三步:提取信息(融合特征)
7. 算 Weights × V
[[0.5, 0.5], [0.5, 0.5]] × [[1, 1], [1, 1]] = Attention 输出 = [[1, 1], [1, 1]]
8. 加上残差连接(Output = Attention输出 + 原始输入 X)
[[1, 1], [1, 1]] + [[1, 0], [0, 1]] = 最终输出 = [[2, 1], [1, 2]]
第四步:计算误差(Loss)
9. 假设标准答案(Label)是 [[0, 0], [0, 0]]
模型瞎猜输出了 [[2, 1], [1, 2]]。
Loss = 所有数字差的平方和 = (2−0)2+(1−0)2+(1−0)2+(2−0)2(2-0)^2 + (1-0)^2 + (1-0)^2 + (2-0)^2(2−0)2+(1−0)2+(1−0)2+(2−0)2 = 4 + 1 + 1 + 4 = 10
第五步:反向传播(算梯度,绝不跳步!)
现在,我们要算 WqW_qWq 的梯度 。根据链式法则,梯度 = ∂Loss/∂最终输出 × ∂最终输出/∂Attention输出 × ∂Attention输出/∂Weights × ∂Weights/∂(QK^T) × ∂(QK^T)/∂Q × ∂Q/∂W_q
我们一步步算:
10. ∂Loss/∂最终输出
因为 Loss = (y−y^)2(y - \hat{y})^2(y−y^)2,求导就是 2×(y^−y)2 \times (\hat{y} - y)2×(y^−y)
= 2 × ([[2, 1], [1, 2]] - [[0, 0], [0, 0]]) = [[4, 2], [2, 4]]
11. ∂最终输出/∂Attention输出
因为 最终输出 = Attention输出 + X,X是常数,求导就是 1 。
所以传过来的梯度依然是:[[4, 2], [2, 4]]
12. ∂Attention输出/∂Weights
因为 Attention输出 = Weights × V,对 Weights 求导,结果是梯度矩阵 × VTV^TVT
[[4, 2], [2, 4]] × [[1, 1], [1, 1]] = [[6, 6], [6, 6]]
13. ∂Weights/∂(QK^T)
(这一步是 Softmax 的求导,稍微复杂点,但结果很直观)
当 Softmax 输出全是 0.5 时,它的局部梯度矩阵是:对角线是 0.5×(1-0.5)=0.25,非对角线是 -0.5×0.5=-0.25。
我们用上一步的 [[6, 6], [6, 6]] 乘这个局部梯度矩阵,算出来结果是:[[0, 0], [0, 0]]
(大白话解释:因为所有权重都是 0.5 极其均匀,稍微动一下 Q,对最终的 0.5 概率几乎没有影响,所以传回 Q 的梯度是 0!)
14. ∂(QK^T)/∂Q
因为 QK^T 对 Q 求导就是 KTK^TKT。
上一步的 [[0, 0], [0, 0]] × KTK^TKT = [[0, 0], [0, 0]]
15. ∂Q/∂W_q
因为 Q = X × W_q,对 W_q 求导就是 X。
上一步的 [[0, 0], [0, 0]] × X = [[0, 0], [0, 0]]
第六步:更新参数(梯度下降)
16. 算出 WqW_qWq 的最终梯度
经过上面 10~15 步的连环乘法,WqW_qWq 的梯度 = [[0, 0], [0, 0]]
17. 更新 WqW_qWq
假设学习率 = 0.1
新 WqW_qWq = 旧 WqW_qWq - (梯度 × 0.1)
= [[1, 1], [1, 1]] - [[0, 0], [0, 0]] = [[1, 1], [1, 1]]
终极真相揭晓!
看到这里,你是不是恍然大悟?!
为什么第一步算出来 WqW_qWq 没变?!
因为一开始瞎填的参数 [[1, 1], [1, 1]] 太对称了!在 Softmax 阶段,所有的注意力权重都被平均分配成了 0.5。在数学上,这种极度均匀的分布,导致反向传播时梯度互相抵消,变成了 0!
这就是大模型学习的真实写照:
AI 第一次尝试,发现"瞎调没用"(梯度为0)。
于是它必须引入随机初始化(打破对称性),或者经过多次不同数据的冲刷,让参数变得"不均匀",梯度才会真正生效,参数才会开始真正发生变化!
这是,从 X 进去,到 QKV 矩阵乘法,到 Softmax,到 Loss,再到一步步链式法则求导。