教程_预训练 小测验答案
1. 在自回归语言建模中,一条长度为 N 的 token 序列,可以产生多少个"预测下一个 token"的训练信号?
N-1 个。 序列被拆分为 X = input_ids[:-1](前 N-1 个 token)和 Y = input_ids[1:](后 N-1 个 token),每个位置对应一个"给定前文,预测下一个 token"的训练信号。模型通过一次前向传播同时计算所有 N-1 个位置的预测。
2. Teacher Forcing 解决了什么问题?训练时和推理时的输入有什么区别?
Teacher Forcing 解决了误差累积问题。如果训练时使用模型自身的预测作为下一步输入,一旦某步预测错误,后续所有输入都会偏离正确轨道,导致训练不稳定。
- 训练时:使用真实的历史 token 作为输入(Teacher Forcing),所有位置可以并行计算。
- 推理时:使用模型自己在上一步的预测作为输入,必须逐 token 自回归生成。
3. NanoQwen 的 PretrainDataset 中,loss_mask 的作用是什么?为什么不能简单地对所有位置计算损失?
loss_mask 用于标记哪些位置是真实的文本 token(mask=1),哪些是 padding 填充(mask=0)。由于 batch 内不同样本长度不同,较短的样本会被 <|endoftext|>(pad_token)填充到统一长度。如果对 padding 位置也计算损失,模型会学到"预测 padding token"这种无意义的模式,浪费训练容量并影响模型质量。
4. RMSNorm 与 LayerNorm 的核心区别是什么?RMSNorm 为什么更适合大模型?
核心区别:
- LayerNorm 同时减均值、除标准差,有 bias 项:$\text{LN}(x) = \frac{x - \mu}{\sigma} \cdot \gamma + \beta$
- RMSNorm 只除以均方根(RMS),不减均值,无 bias 项:$\text{RMS}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum x_i^2 + \epsilon}} \cdot \gamma$
RMSNorm 更适合大模型的原因:计算量更小(少了求均值和方差的步骤),参数更少(无 bias),且实验表明在大模型上效果与 LayerNorm 相当甚至更好。
5. 在 RoPE 旋转位置编码中,为什么 Query 和 Key 需要旋转,但 Value 不需要?
因为 RoPE 的目标是让注意力分数(Q 与 K 的点积)自然地编码相对位置信息。当 Q 和 K 都被旋转后,它们的点积结果只依赖于两者的相对位置差 $m - n$,从而实现了相对位置编码。Value 不参与注意力分数的计算,只是被注意力权重加权求和,因此不需要旋转。
6. NanoQwen 配置了 8 个 Query 头和 2 个 KV 头,这属于什么注意力机制?repeat_kv 函数的 n_rep 值是多少?
这属于 GQA(Grouped-Query Attention,分组查询注意力)。n_rep = num_attention_heads / num_key_value_heads = 8 / 2 = 4。即每 4 个 Query 头共享 1 组 Key-Value 头。repeat_kv 函数将 2 个 KV 头各复制 4 次,扩展为 8 个头以匹配 Query 的头数。
7. SwiGLU 前馈网络使用了几个线性投影层?写出 SwiGLU 的计算公式。
SwiGLU 使用了 3 个 线性投影层:gate_proj、up_proj、down_proj。
计算公式:
$$\text{SwiGLU}(x) = W_{\text{down}} \cdot [\text{SiLU}(W_{\text{gate}} \cdot x) \odot (W_{\text{up}} \cdot x)]$$其中 $\text{SiLU}(x) = x \cdot \sigma(x)$,$\odot$ 是逐元素乘法。
8. 在混合精度训练中,GradScaler 为什么要先放大(scale)损失再反向传播?
因为 FP16 的最小正数约为 6.1e-5,而梯度值通常很小(如 1e-7、1e-8),在 FP16 下会被截断为 0(下溢),导致梯度消失。GradScaler 在反向传播前将 loss 乘以一个较大的缩放因子(如 1024),使得对应的梯度值也被放大到 FP16 能表示的范围内。在更新参数前,再将梯度除以缩放因子恢复真实值。
9. 梯度累积 accumulation_steps=8,batch_size=32 时,等效的实际 batch size 是多少?
等效 batch size = 32 x 8 = 256。每 8 个 mini-batch 的梯度累积后才执行一次参数更新,效果等同于使用 batch_size=256 进行一次更新。
10. 余弦退火学习率调度中,NanoQwen 的最低学习率是最高学习率的几分之几?
最低学习率是最高学习率的 1/10。从代码 get_lr 函数可知:
| |
当 current_step = total_steps 时,cos(pi) = -1,学习率为 lr/10 + 0.5 * lr * 0 = lr/10。
11. KV-Cache 为什么只在推理时使用而不在训练时使用?
训练时使用 Teacher Forcing,所有位置的 token 同时输入模型,模型一次前向传播就计算出所有位置的 Key 和 Value,不存在"重复计算"的问题。而推理时是逐 token 生成,每生成一个新 token 都需要用到之前所有位置的 Key 和 Value。如果不缓存,每一步都要从头计算,计算量为 O(t*d);使用 KV-Cache 后只需计算新 token 的 K/V 并与缓存拼接,计算量降为 O(d)。
12. MOE 架构中,门控网络使用什么函数来选择 Top-K 个专家?辅助损失的目的是什么?
门控网络首先使用 softmax 函数计算每个 token 对各专家的得分(概率分布),然后使用 torch.topk 选择得分最高的 K 个专家。
辅助损失(Auxiliary Loss)的目的是防止专家坍塌(Expert Collapse),即避免所有 token 都被路由到少数几个专家而其他专家闲置的情况。辅助损失鼓励 token 在各专家之间均匀分配,保证所有专家都能得到充分训练。
13. NanoQwen 的 lm_head 和 embed_tokens 使用了权重绑定(weight tying),这样做有什么好处?
好处有两个:
- 减少参数量:embed_tokens 的权重矩阵形状为 (vocab_size, hidden_size) = (6400, 512),约 3.3M 参数。权重绑定后 lm_head 不需要额外的参数,节省了约 3.3M。
- 提高性能:让输入空间(token -> 向量)和输出空间(向量 -> token 概率)使用同一套表示,模型在两个方向上学习到的 token 语义保持一致,有助于提升生成质量。
14. vLLM 相比原生 transformers 的 model.generate() 有哪些推理优化?
vLLM 的主要优化包括:
- PagedAttention:将 KV-Cache 按页管理,避免内存碎片,支持更长的序列
- 连续批处理(Continuous Batching):动态地将不同请求组合成批次,提高 GPU 利用率
- 高效的内存管理:更精细地控制 GPU 内存分配,
gpu_memory_utilization参数可设定显存利用率 - Tensor Parallelism:支持多卡并行推理
- 更高的吞吐量:综合以上优化,吞吐量可达原生 transformers 的数倍
15. NanoQwen 预训练模型可以进行对话吗?为什么?要实现对话能力还需要什么步骤?
不能进行真正的对话。 预训练模型只学会了"根据前文预测下一个 token"(文本接龙),它不理解"用户提问 -> 助手回答"的对话模式。如果直接输入一个问题,模型会把它当作一段普通文本继续续写,而不是生成有针对性的回答。
要实现对话能力,还需要以下步骤:
- SFT(监督微调):使用大量"指令-回答"对数据对模型进行微调,让模型学会遵循指令并给出相关回答
- RLHF / DPO(对齐):通过人类反馈的强化学习或直接偏好优化,让模型的回答更符合人类偏好(更有帮助、更安全、更诚实)