组件深讲(概念版):每个模块到底解决什么问题
1. Attention:为什么它是“信息路由器”
1.1 先问问题
给定当前 token,模型要决定:
“我应该从历史的哪些 token 取信息?每个取多少?”
Attention 就是这套动态路由机制。
1.2 机制与公式
给定
有
解释:
- :匹配分数(query 与 key 的相似度)。
- :每行是概率分配(该看哪些位置)。
- :按概率把 value 聚合成上下文表示。
这就是“信息路由”:由当前查询动态决定读取历史的路径和权重。
1.3 为什么要缩放
不缩放时,维度越高,打分方差越大,softmax 更容易饱和,梯度变差。
缩放把分数控制在更稳定区间,训练更平稳。
1.4 因果 mask 的意义
保证位置 不能看到未来 的 token。
这不是小细节,而是自回归定义本身;否则训练与推理行为会不一致。
1.5 代码定位
- Q/K/V 与投影:
src/model/executor_forward.cpp - 注意力核:
src/kernel/attention.cpp、src/kernel/attention_accelerate.cpp
2. GQA:为什么要让 KV 头更少
2.1 先问问题
长上下文推理时,瓶颈往往是 KV cache 的内存和带宽。
如何在质量损失可控的前提下降低这部分成本?
2.2 核心思想
让 query 头数 保持较大,但 key/value 头数降为 (常见 )。
等价于“多个 query 头共享一组 KV 头”。
2.3 效果与代价
- 收益:KV cache 内存与带宽压力明显下降(近似随 下降)。
- 代价:表达自由度受限,极端设置可能影响质量。
它本质是工程折中:速度/内存 vs 容量。
2.4 代码定位
- 配置字段:
src/model/model_types.h - 前向展开逻辑:
src/model/executor_forward.cpp
3. RoPE:为什么它天然携带相对位置信息
3.1 先问问题
模型要区分“谁在前谁在后”,并且希望在长长度外推时仍保持稳定。
RoPE 提供的是一种“旋转式位置编码”。
3.2 机制与公式
对每个二维子空间做旋转:
关键点:
- 不是“加一个位置向量”,而是“按位置旋转方向”;
- 当 Q/K 都按位置旋转后,它们内积对相对位置差更敏感;
- decode 时每步可独立计算,天然适配 KV cache。
3.3 本项目折中
rope_dim < head_dim 表示只旋转部分维度,兼顾效果和开销。
3.4 代码定位
- RoPE 缓存与应用:
src/kernel/core.cpp、src/kernel/core_accelerate.cpp - 前向调用点:
src/model/executor_forward.cpp
4. RMSNorm:为什么只做“尺度归一化”也够用
4.1 先问问题
深层网络常因激活尺度漂移导致训练不稳。
我们需要一种便宜、稳定的归一化。
4.2 公式
4.3 概念解释
- 分母是 RMS(均方根),把向量缩放到稳定幅值。
- 允许每个通道再学习性重标定。
- 不减均值(相比 LayerNorm 更轻量),在 Transformer 中通常足够稳。
4.4 代码定位
- 算子与反向:
src/kernel/core.cpp、src/kernel/norm_rope.cpp、src/kernel/attention.cpp(IO 端反向算子已迁入src/engine/decode.cpp/src/engine/embedding.cpp,详见 §2.1) - 在 block 中的位置:
src/model/executor_forward.cpp
5. SwiGLU:为什么比普通 FFN 更有选择性
5.1 先问问题
普通 FFN 会放大特征,但“选择性”不够细。
SwiGLU 用门控机制让网络决定“哪些信息该通过”。
5.2 公式
5.3 概念解释
- 是内容支路, 是门控支路;
- 作为软门,逐通道调节 的通过量;
- 相比普通 FFN,门控能更细粒度控制信息流。
5.4 代码定位
- 线性与激活组合:
src/model/executor_forward.cpp - 底层算子:
src/kernel/core.cpp
6. Pre-Norm + 残差:为什么训练更稳
本项目层结构是:
解释:
- 残差提供短路径,避免梯度在深层被完全扭曲。
- Pre-Norm 让子层输入尺度更可控,训练初期更稳。
这也是当前 LLM 常见选择。
7. 组件级调试:每个模块最常见的错
- Attention:mask 维度不对、softmax 数值饱和。
- RoPE:位置索引错、旋转维度切分错。
- RMSNorm: 太小导致不稳定。
- SwiGLU:门控分支梯度或广播维度错误。
建议先做三类检查:shape、finite(无 NaN/Inf)、边界 case(空/短序列)。
8. 在这个仓库做“单组件实验”的标准流程
- 固定 seed 与数据采样。
- 每次只改一个变量(如
num_kv_heads、rope_dim)。 - 统一比较三组指标:
- 训练 loss/accuracy;
- 任务指标(exact match);
- 推理耗时(prefill/decode)。
这样才能把“数学改动”与“工程效果”建立清晰因果关系。