Stanford CS336 · Spring 2026 · 中文学习载体
CS336 语言模型从零认知课
这不是 Stanford 原课的搬运版,也不是完整作业训练营。它把 CS336 的公开课程、讲义和作业压缩成一条工程认知链路:一个语言模型从文本、模型、训练系统、数据、评测到 post-training,最终怎样成为可用的 AI 产品底座。
这门课该怎么读
CS336 的原始设计很硬:它希望学生亲手实现 tokenizer、Transformer、optimizer、training loop、profiling、FlashAttention、DDP/FSDP、scaling law、数据清洗和 reasoning RL。对非 AI Infra 工程师,最有价值的不是把每个 kernel 写到最优,而是理解训练栈中每一层为什么存在、输入输出是什么、成本瓶颈在哪里、什么时候该把问题交给专门的 Infra 团队。
因此这份 HTML 采用三层读法:
- 第一遍看链路:顺序读“总地图”和 19 章正文,先知道语言模型训练和服务的端到端路径。
- 第二遍看作业:读 A1-A5 的实验地图,理解每个作业验证哪类工程能力。
- 第三遍做轻练习:每讲做一个闭卷问题,不需要完整实现,但要能用自己的话解释机制。
完整教材正文
这一节是主课。19 章按官方讲次顺序重写成中文正文,外链只作为来源和下钻入口;读者不打开视频或 slide,也能建立从 token 到训练系统、数据、评测和 post-training 的完整链路。
01. Overview, Tokenization
本讲要建立两个东西:第一,语言模型不是一个 API,而是一条从原始文本到可训练参数的工程链路;第二,tokenizer 是这条链路的入口,它会影响模型能看到什么、上下文有多长、训练和推理要花多少钱。
1. 先把语言模型还原成一条数据路径
你平时调用模型时看到的是一段文本输入和一段文本输出。训练系统看到的不是文本,而是一串整数。更完整地说,语言模型训练的路径大致是:
这条路径里,每一步都会把问题改写成下一层能处理的形式。tokenizer 把不定长、混合语言、带 emoji、带代码和符号的文本,变成一个整数序列。embedding 把整数查表成向量。Transformer 对向量序列做计算。最后模型在每个位置预测下一个 token,loss 把“预测错了多少”变成可以反向传播的数字。
这个视角比“Transformer 很强”更有用。因为你做 Agent 时经常碰到的痛点,比如上下文太长、JSON 输出不稳定、中文和代码成本不同、tool trace 占满窗口,最早都可以追到 token 序列这一层。模型没有看到“一个 JSON 对象”或“一句中文”,它看到的是 token id 序列和位置。
2. tokenizer 的合同:可逆,但不等于语义理解
CS336 在第一讲里给 tokenizer 一个很朴素的接口:encode(str) -> list[int],以及 decode(list[int]) -> str。一个合格 tokenizer 至少要能 round-trip:文本编码成 token,再解码回来,应该回到原文本。
class Tokenizer:
def encode(self, string: str) -> list[int]:
...
def decode(self, indices: list[int]) -> str:
...
但“可逆”不代表“按人类语义切分”。同一个英文单词,开头出现和前面带空格出现,可能是不同 token。一个 emoji 在 Unicode 里是一个 code point,但在 UTF-8 中可能占 4 个 byte。一个罕见中文词可能被拆成多个 token,而一个常见英文片段可能是单 token。模型训练时不关心你主观上觉得哪个是“一个字”或“一个词”。
因此 tokenizer 同时承担两个互相拉扯的目标:一方面,词表不能无限大,否则很多 token 很少出现,embedding 学不好;另一方面,序列不能太长,否则 attention 和推理成本上升。这个取舍很早就进入系统设计。
| 方案 | 好处 | 问题 | 工程后果 |
|---|---|---|---|
| Character | 概念简单,能处理 Unicode 字符。 | Unicode 空间很大,很多字符极少出现。 | 词表稀疏,压缩率也不好。 |
| Byte | 词表固定为 256,任何文本都能表示。 | 一个自然语言片段会被拆成很多 byte。 | 序列变长,attention 成本变高。 |
| Word | token 更接近人类词语。 | 词表随语料增长,罕见词和未登录词难处理。 | 需要 UNK 或复杂规则,perplexity 也会被影响。 |
| BPE | 从 byte 出发,按数据里常见相邻片段合并。 | 切分仍是启发式,不保证语义边界。 | 在词表大小和序列长度之间取得实用平衡。 |
3. BPE 的机制:从 byte 开始,把常见 pair 合起来
Byte Pair Encoding 最早来自数据压缩,后来被用于神经机器翻译,并进入 GPT 系列 tokenizer。它的核心流程不复杂:
- 把训练文本转成 UTF-8 bytes,每个 byte 先是一个 token。
- 统计当前 token 序列里最常见的相邻 pair。
- 把这个 pair 合并成一个新 token,并把它加入词表。
- 重复这个过程,直到达到目标词表大小或 merge 次数。
比如训练文本里 the 经常出现,BPE 可能先把 t 和 h 合并成一个 token,再把这个 token 和 e 合并。最终常见片段会变短,罕见片段仍然能退回 byte 表示。这样不会出现“完全没见过的字符无法表示”的问题。
这也是为什么 tokenizer 是“训练出来的组件”。它不是一套固定语法规则,而是语料分布的产物。换语料、换语言比例、换代码占比、换 special tokens,都会改变 token 成本和模型看到的片段边界。
自测:为什么 byte tokenizer 看起来最稳,却不是现代 LLM 的常规选择?
byte tokenizer 的词表小,覆盖所有输入,但序列太长。Transformer 的 attention 和上下文管理都按 token 序列工作,序列变长会直接提高训练和推理成本。BPE 用更大的词表换更短序列,通常更适合当前架构。
4. special tokens 是模型和系统之间的协议
Assignment 1 会要求处理 special tokens,例如 <|endoftext|>。special token 不是普通字符串。它经常用来表达文档边界、对话角色、工具调用边界、消息结束或其他控制信息。训练和推理系统会把这些 token 当成协议的一部分。
这点对 Agent 开发很实际。你定义 tool schema、消息 role、函数调用格式、observation 边界时,本质上是在给模型造一套输入输出协议。如果这些协议切成很多罕见 token,或者和训练时格式不一致,模型就更容易出格式错、漏参数、生成奇怪边界。prompt 不是一段“自然语言说明”,它是 token-level interface。
5. 这一讲应保留的判断力
- 语言模型从一开始就是系统工程:文本会先变成 token,token 数决定很多后续成本。
- tokenizer 的好坏不能只看是否能编码文本,还要看压缩率、词表稀疏性、语言覆盖和 special token 协议。
- BPE 是当前工程上的折中,不是最终答案。byte-level 或 tokenizer-free 方向存在,但大规模主流模型仍大量使用 tokenizer。
- Agent 里的长上下文、工具调用和结构化输出,都应当被当成 token 预算问题来设计。
闭卷复述题:请解释 string → token ids → logits → loss 这条路径。
参考答案:原始 string 先由 tokenizer 编成 token ids;embedding 层把 ids 变成向量;Transformer 根据上下文更新每个位置的向量;输出层为每个位置给出词表上所有 token 的 logits;cross-entropy loss 比较 logits 和真实下一个 token;loss 反向传播产生梯度,optimizer 用梯度更新参数。
官方可执行 Lecture 1 Lecture 1 源码 Assignment 1 Basics Tokenizer 交互观察工具
02. PyTorch, Einops, Resource Accounting
第二讲的核心不是 PyTorch API,而是资源账本。你要能拿到一个模型配置后,大致算出参数、梯度、optimizer state、activation、FLOPs、显存和带宽压力。没有这张账本,就很难判断一个训练或推理方案是否现实。
1. tensor 是所有状态的共同形式
深度学习代码看起来有很多对象:dataset、model、loss、optimizer、checkpoint。落到计算层,它们大多是 tensor 和 tensor operation。CS336 第二讲先把所有训练状态放到同一张表里:
| 状态 | 什么时候出现 | 为什么占资源 | 常见优化 |
|---|---|---|---|
| parameters | 模型本体,训练和推理都需要。 | 每次计算都要读权重,大模型权重本身就是显存压力。 | 低精度、分片、量化、MoE。 |
| gradients | 训练反向传播后产生。 | 通常和参数同规模。 | 梯度累积、分片、混合精度。 |
| optimizer state | AdamW 等 optimizer 更新参数时保存动量。 | Adam 通常要保存一阶和二阶矩,常用 fp32。 | optimizer sharding、8-bit optimizer、换 optimizer。 |
| activations | 前向过程中每层中间结果。 | 训练时反向传播需要它们,随 batch、seq len、层数增长。 | activation checkpointing、micro-batch。 |
| data / tokens | 输入 batch 和 label。 | 长序列增加 attention、activation 和数据加载压力。 | packing、截断、混合策略、数据管线优化。 |
这个表直接改变你看训练的方式。显存不只是“模型参数放不放得下”。训练时还要放梯度、optimizer state 和 activations。推理时通常没有梯度和 optimizer state,但会出现 KV cache。不同阶段的账本不同,不能混在一起。
2. dtype 是内存、速度和数值稳定性的合同
一个 tensor 的内存占用由两个数决定:元素个数和每个元素的字节数。float32 是 4 bytes,float16 和 bfloat16 是 2 bytes。听起来 fp16/bf16 只是省一半内存,但它们的数值行为不同。
fp16 的 exponent 位更少,小数值容易 underflow。bf16 也用 2 bytes,但保留了接近 fp32 的动态范围,精度更粗。深度学习训练常用 mixed precision:参数、activation、梯度用 bf16/fp16,optimizer state 用 fp32,原因是动量和二阶矩要跨很多 step 累积,数值稳定性更重要。
用这个粗账算 70B 参数模型,光参数、梯度和 AdamW state 就是约 840GB,还没算 activation、通信 buffer、fragmentation 和 checkpoint。8 张 80GB H100 只有 640GB 显存。于是你马上知道:全量 AdamW 训练不可能只是把模型塞进 8 卡就结束,必须分片、重算或换策略。
自测:为什么“70B 模型参数 bf16 只有 140GB”这个说法会误导训练预算?
因为 140GB 只算了参数。训练还需要梯度、optimizer state 和 activations。AdamW 常见粗账是 12 bytes/parameter,不含 activations,所以 70B 会到约 840GB。
3. FLOPs:先会算矩阵乘法,再看训练近似
FLOPs 是完成了多少浮点操作,FLOP/s 是硬件每秒能做多少浮点操作。两者很容易混淆。一个矩阵乘法 x @ w,如果 x 形状是 B × D,w 形状是 D × K,输出是 B × K。每个输出元素要做 D 次乘法和接近 D 次加法,所以粗略是:
训练时还要反向传播。对一个线性层,前向大约是 2 × batch × parameters FLOPs;反向要算输入梯度和权重梯度,大约是前向的 2 倍。于是总训练 FLOPs 粗略是:
这个公式在短上下文 Transformer 上是很好用的第一版估算。它不会替你处理 attention 在长上下文下的额外成本,也不会算 optimizer、通信和数据加载,但它能让你在几秒内判断数量级。
例如训练一个 70B 参数模型,数据量 15T tokens:
如果你有 1024 张 H100,假设 bf16 dense 峰值约等于 989.5 TFLOP/s,每张卡实际只拿到 50% MFU,那么每天能完成的 FLOPs 大约是:
这不是精确训练计划,但它能校准直觉:大模型训练首先是资源组织问题。任何“我们训练一个 70B”这种说法,后面都应该跟着 tokens、硬件、MFU、并行策略和失败恢复。
4. arithmetic intensity:为什么有些算子天生被内存卡住
一个 GPU 操作可以粗略拆成三步:从 HBM 读输入到计算单元,执行计算,把输出写回 HBM。硬件有两个上限:算力上限 FLOP/s 和内存带宽 bytes/s。workload 也有一个比例:每搬 1 byte 数据能做多少 FLOPs,这叫 arithmetic intensity。
ReLU 这类 elementwise 操作,每个元素读一次、写一次,只做很少计算。它通常是 memory-bound。GeLU 比 ReLU 算得多,但单独看仍可能是 memory-bound。大矩阵乘法则不同:读入一块矩阵后可以参与很多乘加,数据复用多,arithmetic intensity 高,更容易 compute-bound。
这解释了很多反直觉现象:一个算子 FLOPs 少,不代表它快;一个优化减少了内存读写,可能比减少少量计算更有效。FlashAttention 的核心价值也在这里:不是改变 attention 数学,而是通过 tiling 和在线 softmax 减少 HBM 往返。
5. Einops 的意义:给 tensor 维度命名
第二讲还讲了 Einops。它不是为了少写几个字符,而是减少维度错误。Transformer 里常见 tensor 形状包含 batch、sequence、heads、hidden。如果代码里到处是 transpose(-2, -1),你很快会忘记每个维度代表什么。
# 普通写法:你必须记住 -2 和 -1 是哪两个维度
scores = q @ k.transpose(-2, -1)
# einops/einsum 写法:维度名字进入表达式
scores = einsum(
q, k,
"batch heads query d, batch heads key d -> batch heads query key",
)
这对自学 A1 很重要。很多 Transformer bug 不是公式错,而是 shape 错、broadcast 错、head 维度拆错、mask 维度错。维度命名是工程防线。
6. activation memory:batch size 为什么不能无限加
训练时,反向传播需要用到前向的中间结果。层数越多、batch 越大、sequence 越长,activation memory 越大。你想增大 batch size 来提高吞吐或稳定训练,但显存可能先爆。
两种常见方法:
- Gradient accumulation:把大 batch 拆成多个 micro-batch,分别前向和反向,梯度累积起来,最后再做一次 optimizer step。它降低单次 activation 峰值,但训练 step 变成多个 micro-step。
- Activation checkpointing:前向时只保存部分层的 activation,反向时从最近 checkpoint 重新计算缺失部分。它用额外计算换显存。
这两者都是 AI Infra 里反复出现的取舍:显存、计算和时间三者互换。没有免费午餐,只有哪个资源更紧。
闭卷计算题:10B 参数模型,用 AdamW,bf16 参数和梯度、fp32 两个 moment,不算 activation,训练状态大约多少显存?
每个参数约 12 bytes。10B × 12 = 120GB。这个数字还不含 activation、通信 buffer 和框架开销,所以单张 80GB GPU 放不下完整训练状态。
7. 这一讲应保留的判断力
- 任何训练计划都应该先写资源账本:参数、梯度、optimizer state、activation、FLOPs、显存、带宽。
- 训练 FLOPs 的第一版估算是
6 × tokens × parameters,它能快速给出数量级。 - 性能瓶颈要用 arithmetic intensity 和 roofline 思路判断,不能只看 FLOPs。
- gradient accumulation 和 activation checkpointing 是用时间或计算换显存的工具。
- Agent 推理成本也要按资源账本看:prefill、decode、KV cache、batching、模型大小都会进账。
官方可执行 Lecture 2 Lecture 2 源码 Assignment 1 Basics Roofline 参考
03. Architectures, Hyperparameters
这一讲把 Transformer 从论文图还原成一组可实现、可算账、可替换的模块。你要带走的不是“Transformer 有 attention”,而是每个结构选择会改变表达能力、训练稳定性、显存和推理成本。
1. decoder-only LM 的最小骨架
现代语言模型大多可以先看成 decoder-only Transformer。输入是一段 token ids,输出是在每个位置预测下一个 token 的 logits。最小骨架是:
一个 Transformer block 内部通常有两条主路径:self-attention 和 feed-forward network。attention 让每个位置读取前面位置的信息,MLP/FFN 对每个位置做非线性变换。residual connection 让信息能跨层流动,也让梯度更容易回传。normalization 用来控制激活尺度,避免深层训练时数值发散。
2. attention 不是一个函数,而是一套张量合同
对每个 token 的 hidden state,模型先用线性层投影出 query、key、value。query 表示“我想找什么”,key 表示“我有什么可被匹配的信息”,value 表示“如果被匹配上,我提供什么内容”。在实现上它们都是 tensor,不是语义对象。
causal mask 很重要。语言模型在位置 t 预测下一个 token 时,不能偷看未来 token。mask 把未来位置的 score 变成不可选。没有 mask,训练 loss 会很好看,但模型学到的是作弊。
multi-head attention 把 hidden dimension 切成多个 head。每个 head 可以学习不同的匹配方式。实现时常见 bug 是 shape 搞错:batch, seq, heads, d_head 和 batch, heads, seq, d_head 只差一次转置,但 attention score 的语义完全变了。
3. 2024-2026 模型里的常见替换件
| 部件 | 旧直觉 | 常见现代选择 | 为什么影响系统 |
|---|---|---|---|
| Normalization | LayerNorm 放在 block 中控制尺度。 | RMSNorm、pre-norm、QK norm。 | 影响训练稳定性和 kernel 形状。 |
| Activation | ReLU/GELU 给 MLP 非线性。 | SwiGLU、GeGLU。 | 改变 MLP 参数量、FLOPs 和模型质量。 |
| Position | 加固定或可学习位置向量。 | RoPE、ALiBi、RoPE scaling。 | 影响长上下文泛化和 KV cache 使用。 |
| Attention | 每个 head 都有完整 K/V。 | MQA、GQA、MLA。 | 直接改变 KV cache 大小和 decode 带宽。 |
| MLP | 每个 token 经过同一个 dense FFN。 | MoE、shared experts。 | 改变参数量、激活计算量和跨设备通信。 |
4. hyperparameter 不是配置噪声
模型配置里的 d_model、层数、head 数、d_ff、vocab size、context length、batch size、learning rate、weight decay、warmup、dropout,都不是随手填的。它们共同决定三件事:模型能表达什么、训练是否稳定、资源是否够。
例如把 context length 翻倍,不只是让输入变长。attention score 矩阵的尺寸会变,activation 会变,KV cache 会变,数据 packing 会变,训练吞吐也会变。把 vocab size 变大,embedding 和输出投影都会变大。把 head 数改掉,单个 head 的维度会变,RoPE 和 attention kernel 的 shape 也会变。
自测:如果一个架构改动让 perplexity 基本不变,但 KV cache 小很多,为什么它仍然可能更适合 Agent 产品?
Agent 产品经常有长上下文和并发 decode。KV cache 小意味着同样显存能承载更多请求或更长上下文,decode 读取压力也更低。perplexity 不变时,服务成本下降本身就是收益。
5. 这一讲应保留的判断力
- Transformer 是一组张量合同,不是一个不可拆的黑盒。
- architecture choice 经常是质量、稳定性和系统成本的共同取舍。
- attention 相关改动要特别看 KV cache 和长上下文成本。
- 做 A1 时,shape 命名和测试比追论文术语更重要。
Lecture 3 slides Assignment 1 Basics Attention Is All You Need
04. Attention Alternatives and Mixture of Experts
这一讲回答一个现实问题:full attention 和 dense MLP 很强,但成本曲线不总是能接受。模型结构的很多变化,都是在试图改变“每个 token 需要多少计算、多少内存、多少通信”。
1. full attention 的成本来自哪里
标准 self-attention 需要每个 query 和所有可见 key 计算相似度。sequence length 是 S 时,score 矩阵大致是 S × S。上下文越长,这部分成本增长越快。训练时还要保存中间结果用于反向传播;推理时虽然每次只生成一个 token,但要不断读取历史 KV cache。
所以“长上下文”不是一个单纯产品开关。它会改变训练数据切分、attention kernel、KV cache 管理、显存预算和评测方式。很多 attention alternative 不是为了概念优雅,而是为了让成本曲线变得能承受。
2. 几类 attention 替代路线
| 路线 | 基本想法 | 得到什么 | 付出什么 |
|---|---|---|---|
| Local / sparse attention | 每个 token 只看局部窗口或少量全局位置。 | 降低长上下文 attention 成本。 | 远距离依赖需要额外机制。 |
| Linear attention | 把 attention 写成可线性累积的形式。 | 让序列长度成本从平方接近线性。 | 改变 softmax attention 的表达形态。 |
| State-space / recurrent | 用状态递推处理序列。 | 长序列吞吐和 memory 可能更好。 | 训练稳定性和能力边界需要实验证明。 |
| Latent / compressed KV | 压缩 K/V 表示,减少 cache。 | 改善 decode 和长上下文服务成本。 | 实现复杂度和质量风险上升。 |
3. MoE:参数很多,但每个 token 只用一部分
Mixture of Experts 的直觉是把一个大的 MLP 拆成多个 expert。router 根据 token 表示选择少数 expert,例如 top-1 或 top-2。这样模型总参数量可以很大,但每个 token 激活的参数只是其中一部分。
这解释了为什么 MoE 常被用来提高参数规模和能力,同时控制每 token 计算。但 MoE 没有让系统问题消失。router 可能把太多 token 分到同一个 expert,造成负载不均;expert 分布在不同 GPU 时,会产生 all-to-all 通信;训练时还需要 load balancing loss 或其他机制避免 expert 闲置。
4. Agent 视角的结构选择
Agent 工作负载和普通 chat 不太一样。它更长、更碎、更容易出现工具调用和多轮状态。attention alternative 和 MoE 对它的影响不只在 benchmark 分数上,还在服务形态上。
- 长上下文任务会放大 attention 和 KV cache 设计差异。
- 多轮短请求会放大 decode latency、batching 和 cache reuse。
- MoE 模型可能有更好的能力/计算比,但 serving 需要处理路由、并发和 expert 负载。
- 结构改变后,tool calling、格式稳定性和长任务评测必须重测。
自测:MoE 为什么经常需要 all-to-all 通信?
token 初始分布在不同 GPU 上,但 router 选择的 expert 可能在其他 GPU。系统需要把 token 表示发送到对应 expert,再把结果发回来。这个动态路由会形成 all-to-all 模式。
5. 这一讲应保留的判断力
- attention alternative 主要是在改变长序列成本,不是简单替换公式。
- MoE 把总参数量和每 token 计算量部分解耦,但引入路由和通信问题。
- 结构选择要同时看训练、推理、长上下文和服务系统。
- 没有一种架构在所有 workload 上天然胜出,必须回到资源账本和评测。
Lecture 4 slides Outrageously Large Neural Networks Switch Transformers
05. GPUs, TPUs
这一讲把“GPU 很快”拆成可操作的硬件模型。你需要知道 GPU 快在哪里,也要知道它什么时候会被数据搬运、分支、同步或内存层级拖住。
1. GPU 不是很多个强 CPU
CPU 优化的是少量复杂线程的低延迟执行。GPU 优化的是大量简单线程的总吞吐。CPU 有复杂分支预测、大 cache、乱序执行;GPU 把更多面积给算术单元和高带宽内存,通过大量线程隐藏内存等待。
这带来一个后果:GPU 喜欢形状规则、工作量大、数据访问连续、分支少的计算。大矩阵乘法非常适合 GPU。大量小算子、频繁 Python 调用、乱序访问和复杂控制流则容易浪费 GPU。
2. GPU 内存层级决定优化方向
| 层级 | 特点 | 你需要关心什么 |
|---|---|---|
| Registers | 每个 thread 私有,最快,数量有限。 | 寄存器用太多会降低 occupancy。 |
| Shared memory | block 内线程共享,速度快。 | 适合 tile 复用,但要避免 bank conflict。 |
| L2 cache | SM 间共享缓存。 | 影响跨 block 数据复用和 cache hit。 |
| HBM | 容量大、带宽高,但比片上内存慢得多。 | 大模型权重、activation、KV cache 都在这里制造压力。 |
| Interconnect | NVLink、PCIe、InfiniBand 等。 | 多 GPU 时,通信比单卡 HBM 更难隐藏。 |
3. SIMT、warp 和 coalescing
CUDA 程序写的是很多 thread,但硬件通常按 warp 执行。一个 warp 内的 thread 同步执行同一条指令。如果分支不同,有些 lane 会被 mask,warp divergence 就出现了。访存也类似:相邻 thread 访问连续地址时,硬件可以合并内存事务;地址分散时,带宽会浪费。
所以 GPU 优化经常是把计算重新排布成硬件喜欢的形状。你不是在“让代码更短”,而是在让 warp、memory transaction、shared memory 和 tensor core 都更接近理想工作状态。
4. Tensor Core 和低精度
现代 GPU 有专门做矩阵乘加的 Tensor Core。它们对 bf16、fp16、tf32、fp8 等 dtype 有不同吞吐。CS336 第二讲讲 dtype,到了这一讲要把 dtype 和硬件吞吐连起来:选 bf16 不只是省显存,也是走更快的硬件路径;选 fp32 可能更稳,但吞吐会低很多。
TPU 的设计目标类似,也是为机器学习矩阵计算提供高吞吐和高带宽互联。不同厂商硬件的编程模型不同,但资源账本相似:算力、片上内存、外部内存、互联、编译器和 runtime。
自测:为什么 GPU 利用率高,不一定说明模型训练效率好?
GPU 利用率可能只说明设备有 kernel 在跑。你还要看 MFU、内存带宽、kernel 时间分布、通信等待、数据加载和端到端 tokens/sec。忙不等于有效计算多。
5. 这一讲应保留的判断力
- GPU 快是因为吞吐模型,不是因为单个线程更强。
- 访存形状、分支和 tile 复用经常比源代码表面复杂度更重要。
- Tensor Core、dtype 和矩阵形状共同决定实际 FLOP/s。
- AI Infra 的很多优化都是减少 HBM 和跨设备数据搬运。
06. Kernels, Triton
这一讲把性能优化从“感觉慢”推进到“测出慢在哪里,然后改 kernel 或调用路径”。你不一定每天写 Triton,但要能读懂为什么 fused kernel、tiling 和 profiling 会改变模型速度。
1. PyTorch 操作背后是 kernel launch
在 PyTorch 里写 y = torch.relu(x) 看起来是一行。对 GPU 来说,它通常意味着启动一个 kernel:把输入从 HBM 读进来,计算 ReLU,再把输出写回 HBM。如果你连续写很多 elementwise 操作,每一步都可能读写一次 HBM。
这就是 fusion 的直觉。它不改变数学结果,但减少中间 tensor 的内存往返和 kernel launch 开销。对 memory-bound 操作,这类优化可能很明显。
2. benchmark 和 profiling 要先于优化
GPU 调用通常是异步的。你在 Python 里记录开始和结束时间,如果没有同步,测到的可能只是 CPU 提交 kernel 的时间,不是 GPU 真正执行的时间。正确 benchmark 要 warmup、同步、重复多次,并报告分布。
profiling 的目标是定位时间花在哪里。你要区分 Python overhead、kernel launch、单个 kernel 慢、数据拷贝、通信等待和内存分配。没有 profiling,优化容易变成改自己熟悉的代码,而不是改真正的瓶颈。
3. Triton 的思维单位是 program/block
Triton 让你用 Python 风格写 GPU kernel,但它不是普通 Python。你描述的是一组 program,每个 program 处理一个 block/tile。典型流程是:根据 program id 计算这一块负责的数据范围,从 HBM load 到寄存器或 SRAM 近似层级,做向量化计算,再 store 回 HBM。
这个模型适合写 fused elementwise、reduction、softmax、matmul tile、attention tile。它比手写 CUDA 更高层,但仍要求你理解 block size、mask、stride、coalescing 和 shared memory 相关问题。
4. FlashAttention 为什么是 kernel 课的核心例子
标准 attention 的问题不是公式难,而是中间矩阵太大。QKᵀ 生成 seq × seq score,再 softmax,再乘 V。如果把这些中间结果完整写回 HBM,成本会很高。FlashAttention 用 tiling 分块处理 Q、K、V,并用在线 softmax 保持数值正确,尽量不把巨大 score 矩阵写回 HBM。
这件事很适合训练系统直觉:同样的数学,内存访问计划不同,性能差很多。模型研究里的“attention”到了系统层会变成 tile size、SRAM、HBM、mask、数值稳定性和 backward pass。
自测:为什么 naive GeLU 可能比 fused GeLU 慢很多?
naive 写法可能拆成多个 kernel,每一步都读写 HBM。fused kernel 在一次 kernel 内完成多步计算,减少中间 tensor 的内存往返,也减少 launch 开销。
5. 这一讲应保留的判断力
- kernel 优化首先是测量问题,不是写底层代码的冲动。
- fusion 和 tiling 的共同目标是减少慢内存访问,提高数据复用。
- Triton 把 GPU kernel 写作门槛降低了,但没有消除硬件模型。
- FlashAttention 是“同样数学,不同数据搬运”的代表案例。
官方可执行 Lecture 6 Lecture 6 源码 Assignment 2 Systems Triton docs
07. Parallelism
单卡放不下或跑不快时,训练系统进入多 GPU。第七讲的核心是:多 GPU 不是把 GPU 数乘上去,而是把参数、梯度、optimizer state、activation、batch 和通信重新分配。
1. collective operations 是分布式训练的语言
多 GPU 程序不能只说“同步一下”。实际同步由一组 collective 操作组成:broadcast、scatter、gather、reduce、all-reduce、all-gather、reduce-scatter、all-to-all。每个操作对应不同的数据移动模式。
| 操作 | 直觉 | 训练里常见位置 |
|---|---|---|
| all-reduce | 每张卡贡献一份数据,所有卡拿到聚合结果。 | DDP 同步梯度。 |
| all-gather | 每张卡持有一片,所有卡收集完整数据。 | FSDP 前向前聚合参数。 |
| reduce-scatter | 先聚合,再把结果分片给各卡。 | ZeRO/FSDP 梯度分片。 |
| all-to-all | 每张卡给每张卡发送不同片段。 | MoE expert routing、sequence/expert parallel。 |
2. data parallel:最容易理解,也最容易遇到通信墙
Data Parallel 的做法是每张 GPU 放一份完整模型,处理不同 batch shard。反向传播后,每张卡得到自己的梯度;系统通过 all-reduce 求平均,让所有卡更新到同一组参数。
它的优点是简单,计算重复少,适合模型能放进单卡的情况。问题是每一步都要同步梯度,模型越大,通信越重。batch 太小,计算时间不够长,通信就更难隐藏。
3. FSDP/ZeRO:把训练状态切开
当完整模型、梯度和 AdamW state 放不进单卡时,就要分片。ZeRO 和 FSDP 的思路是把参数、梯度、optimizer state 分散到多张卡上。前向或反向需要某层参数时,再 all-gather;梯度出来后用 reduce-scatter 分回各卡。
这样能显著降低单卡显存压力,但通信模式更复杂。它把显存问题换成通信和调度问题。什么时候 gather、什么时候释放、能不能和计算重叠,都会影响吞吐。
4. tensor parallel 和 pipeline parallel
Tensor Parallel 把单层里的矩阵按维度切到多张卡。例如一个大 matmul,列切或行切后由多卡共同完成,再通过 collective 合并结果。它需要很快的互联,常用于节点内 GPU。
Pipeline Parallel 把模型层切成多个 stage。前几层在一组 GPU,后几层在另一组 GPU。它降低每个 stage 持有的层数,但会有 bubble:某些阶段等待输入或等待反向。micro-batch 可以让 pipeline 更满,但也增加调度复杂度。
自测:DDP 和 FSDP 的最核心差别是什么?
DDP 通常每张卡持有完整参数,只同步梯度。FSDP/ZeRO 会把参数、梯度或 optimizer state 分片,前向和反向时按需聚合,从而降低单卡显存。
5. 这一讲应保留的判断力
- 多 GPU 训练是在显存、计算和通信之间重新分摊成本。
- DDP 简单,但要求单卡能放下模型和训练状态。
- FSDP/ZeRO 省显存,但增加 all-gather 和 reduce-scatter。
- tensor/pipeline/expert parallel 要和硬件拓扑一起设计。
官方可执行 Lecture 7 Lecture 7 源码 Assignment 2 Systems ZeRO paper
08. Parallelism at Scale
第七讲讲了并行策略,第八讲把它放到更大的训练集群里。真实大训练不是单一并行方法,而是多种并行、通信层级、checkpoint、故障恢复和作业调度的组合。
1. 先看硬件拓扑,再谈并行策略
一台 8 GPU 机器内部可能有 NVLink/NVSwitch,跨机器可能是 InfiniBand 或 RoCE。节点内带宽和延迟通常比节点间好得多。并行策略如果忽视拓扑,就会把高频通信放到慢链路上。
经验上,tensor parallel 这类频繁同步的并行更适合节点内高速互联;data parallel 可以跨更多节点,因为每 step 同步一次梯度;pipeline parallel 的通信相对局部,但要处理 bubble。MoE 的 all-to-all 对网络压力更特殊,需要单独设计 expert 放置和负载均衡。
2. 组合并行:从二维到多维
大模型训练常常同时使用 data parallel、tensor parallel、pipeline parallel、sequence parallel、expert parallel。每一种并行切的是不同维度。你可以把它理解成一个多维网格:某些 GPU 共同负责一层的矩阵,某些 GPU 负责不同层,某些 GPU 负责不同 batch shard,某些 GPU 负责不同 expert。
组合越多,工程复杂度越高。每个维度都会引入自己的 collective、同步点、内存布局和 failure mode。一个训练 recipe 成功,不只是数学配置成功,也是系统配置成功。
3. checkpoint 和故障恢复不是后勤问题
长训练会失败。GPU、网络、存储、驱动、作业调度都可能出问题。checkpoint 决定失败后损失多少计算,也决定训练期间要花多少 I/O。checkpoint 太频繁,会拖慢训练;太少,失败一次损失巨大。
分布式 checkpoint 还要处理 sharded state:参数、optimizer state、random state、data loader 位置、scheduler 状态都要能恢复。否则恢复后 loss 曲线可能变,或者数据重复/跳过,实验不可复现。
4. 通信计算重叠
多 GPU 训练的理想状态是计算某层时,同时通信另一层的梯度或参数。DDP 会在反向过程中按 bucket 触发梯度 all-reduce,FSDP 会尝试提前 gather 下一层参数。重叠做得好,通信时间被藏在计算下面;做不好,GPU 会在同步点空等。
这也是为什么 profiling 多卡训练比单卡复杂。你要看 kernel timeline、NCCL timeline、stream、bucket 大小、拓扑和 straggler。吞吐下降可能不是算子慢,而是一张卡慢导致所有卡等。
自测:为什么几千张 GPU 的训练不能只报 GPU 数?
GPU 数只说明资源规模。真正决定训练效率的是拓扑、并行切分、通信重叠、checkpoint、故障恢复、数据加载和 MFU。GPU 多但网络或调度差,吞吐会很低。
5. 这一讲应保留的判断力
- 大训练要先看拓扑和通信层级,再选并行组合。
- 组合并行是在多个维度切模型和数据,复杂度会快速上升。
- checkpoint、恢复和可复现性是训练系统的一部分。
- 多卡性能诊断要同时看计算、通信和 straggler。
Lecture 8 slides Assignment 2 Systems How to Scale Your Model
09. Scaling Laws
Scaling law 的作用不是给大模型套神秘曲线,而是用小实验降低大训练的错误成本。第九讲要你学会把“训练多大模型、用多少 token、花多少 compute”变成可估算的工程决策。
1. 为什么需要 scaling law
大模型训练贵,不能靠一次大 run 试错。你真正想知道的是:给定计算预算 C,应该选多大的模型 N,喂多少数据 D,用什么学习率和训练 recipe,最终 loss 大概会到哪里。
最粗的训练 compute 关系是第二讲已经见过的:
如果 C 固定,N 和 D 就在抢预算。模型太大,token 不够,模型会 undertrained;模型太小,token 再多也可能容量不够。Scaling law 试图用一组小规模实验拟合这个关系,再外推到目标预算。
2. ISOFLOP 的直觉
一种常见做法是画 ISOFLOP 曲线:固定某个 compute budget,训练不同模型大小和 token 数的组合,找 loss 最低的点。对多个预算重复这个过程,就能观察最优模型大小如何随 compute 增长。
Chinchilla 的结论常被简化成“每个参数约 20 个 token”。这只是一个有用锚点,不是永恒定律。数据质量、模型结构、optimizer、上下文长度、重复数据和推理成本都会改变最佳点。
3. scaling recipe 比单点超参更重要
Scaling law 不是只拟合一条 loss 曲线。更完整的做法是设计一个 scaling recipe:模型变大时,宽度、深度、batch size、learning rate、warmup、训练 tokens、正则和初始化如何一起变化。你要评估的是这条 recipe 是否可预测。
如果小模型和大模型的超参不能迁移,小实验就没有意义。muP 等参数化方法想解决的正是这个问题:让某些超参在 scale 改变时更稳定。对工程团队来说,可预测有时比局部最优更重要,因为它能减少昂贵训练失败。
4. scaling law 的边界
Scaling law 通常预测 loss,不直接预测产品体验。loss 更低不一定意味着 tool calling 更稳、coding agent 更强或用户更满意。它也可能被数据污染、重复样本、评测选择和训练 recipe 改变。外推距离越大,风险越大。
因此 scaling law 应被看作预算决策工具。它回答“在这类数据和 recipe 下,大概怎么分配 compute 更合理”,不回答“哪个模型有智能”这种模糊问题。
自测:为什么不能只用一次 10B run 来决定 100B run 的训练配置?
单次小 run 不能分离模型大小、token 数、学习率、batch 和数据质量的影响。Scaling 需要多个规模和多个配置点,才能判断趋势是否稳定并支持外推。
5. 这一讲应保留的判断力
- Scaling law 是昂贵训练前的风险控制工具。
- 固定 compute 下,模型大小和训练 token 数存在取舍。
- 预测的是某个 recipe 的行为,不是所有模型的普遍命运。
- 产品能力还需要 task-level evaluation,不能只看 loss。
Lecture 9 slides Assignment 3 Scaling Kaplan scaling laws Chinchilla
10. Inference
训练结束后,模型还不能自动成为产品。第十讲讨论 inference:给定 prompt 生成 token。对 Agent 开发者来说,这讲很重要,因为 Agent 会把推理成本、延迟和上下文管理放大。
1. prefill 和 decode 是两种不同工作负载
LLM 推理通常分两段。prefill 处理用户给出的 prompt,所有 prompt token 已知,可以并行计算每个位置的 hidden state,并建立 KV cache。decode 每次生成一个新 token,新 token 生成后才知道下一步输入。
| 阶段 | 输入 | 特点 | 常见瓶颈 |
|---|---|---|---|
| Prefill | 完整 prompt tokens。 | 并行度高,像训练前向。 | 长 prompt 的 attention 和 activation。 |
| Decode | 已经生成的上下文 + 当前 token。 | 逐 token 串行。 | 权重读取、KV cache 读取、batch 调度。 |
这就是为什么长 prompt 会影响 Time To First Token,而长输出会影响总延迟和吞吐。Agent trace 很长时,prefill 会变重;Agent 反复思考或输出很多步骤时,decode 会变重。
2. KV cache 是推理系统的核心状态
没有 KV cache,模型每生成一个 token 都要重新计算全部历史 token 的 K/V。KV cache 保存每层每个历史 token 的 key 和 value,下一个 token 只需要追加新的 K/V,并读取历史 K/V 做 attention。
KV cache 省计算,但占显存。它随 batch size、sequence length、layer 数、head 数、head dimension 和 dtype 增长。GQA/MQA/MLA 等 attention 变体的一个重要收益,就是减少 K/V 的存储和读取。
3. serving 指标要分开看
推理系统常见指标有 TTFT、TPOT、latency、throughput、tokens/sec、request/sec、显存占用和成本。它们经常互相冲突。continuous batching 可以提高吞吐,但可能增加等待。更大的 batch 可以提高硬件利用率,但也会增加 KV cache 压力和尾延迟。
对用户交互而言,TTFT 和流式输出的稳定性很重要。对离线评测或 RL rollout 而言,总吞吐可能更重要。把这两类 workload 混在一个指标里,会得到错误结论。
4. 几种加速方向
- 模型变小:剪枝、蒸馏、量化、MoE 路由、选择更小模型。
- kernel 更好:FlashAttention、fused kernels、quantized GEMM、paged KV。
- 调度更好:dynamic batching、continuous batching、prefix cache、请求优先级。
- 生成更少:上下文压缩、工具结果裁剪、早停、speculative decoding。
Speculative decoding 值得单独记:小 draft model 先生成多个 token,大模型并行验证。接受的 token 可以一次推进多步,同时保持目标模型分布。它不是简单换小模型,而是用小模型减少大模型串行 decode 次数。
自测:为什么 Agent 比普通 chat 更容易把推理成本打爆?
Agent 通常有多轮工具调用、长 trace、失败重试、多样本搜索和评测循环。每一步都会增加 prompt tokens、generated tokens、KV cache 或等待时间,所以单次模型调用成本会被链路放大。
5. 这一讲应保留的判断力
- 推理不是训练的附属品,它有自己的瓶颈和指标。
- prefill 和 decode 要分开诊断。
- KV cache 是长上下文和并发服务的主要状态。
- Agent 系统要把模型调用次数、上下文长度和输出长度纳入设计。
11. Scaling in Practice
第九讲讲 scaling law 的概念,第十一讲讲它为什么难落地。实践里,外推失败常常不是公式错,而是小模型实验、参数化、optimizer、batch、数据和评测口径没对齐。
1. 小规模实验必须像大规模实验
如果你希望小模型实验预测大模型,二者必须属于同一条 recipe。数据来源、tokenizer、architecture 比例、optimizer、schedule、batch 规则、训练时长和评测集都要可比。否则你拟合的是实验噪声。
一个常见错误是小模型调参很随意,大模型训练又换了数据、换了 normalization、换了 batch 和 learning rate。这样小实验最多说明“小模型这样跑还行”,不能支撑大训练下注。
2. hyperparameter transfer 是实践核心
模型变宽、变深、变长以后,激活尺度和梯度尺度可能改变。学习率在小模型上合适,不代表大模型上稳定。muP 等方法希望让某些超参在宽度变化时更可迁移。它不是魔法,而是让实验设计更像“同一族模型”。
实践里你会关心 loss curve 是否平滑、是否出现 spike、不同规模是否有相似趋势、学习率是否能从小规模迁移。一个可预测 recipe 的价值很大,因为它能减少大训练中途崩掉或训练完才发现配置差的风险。
3. batch size 和 learning rate 不只是优化器问题
batch size 影响统计效率、硬件利用率、activation memory 和通信。learning rate schedule 影响收敛速度和稳定性。warmup 太短可能早期不稳,decay 策略影响后期 loss。WSD、cosine 等 schedule 不是装饰,它们改变训练 compute 的使用方式。
大 batch 还会遇到 critical batch size:超过某个点后,继续增大 batch 对训练效率的收益下降。系统上大 batch 更容易喂饱硬件,优化上却可能浪费样本效率。训练 recipe 必须同时看这两边。
4. 开源模型 recipe 的价值
开放模型论文和训练报告最有价值的部分,常常不是最终 benchmark,而是数据混合、训练 tokens、学习率、batch、硬件、失败记录和 ablation。CS336 会反复引用 open models,就是因为这些资料让课程可以讲真实模型工程,而不只能猜闭源模型。
读模型报告时,建议按固定表格摘录:模型大小、tokens、context length、optimizer、lr schedule、batch、数据来源、训练硬件、推理结构、评测。这样你能比较 recipe,而不是只比较名称。
自测:为什么“小模型最佳 learning rate”不能直接搬到大模型?
宽度、深度、初始化和 normalization 会改变激活与梯度尺度。没有稳定参数化或迁移规则时,小模型的学习率可能让大模型不稳定或训练效率差。
5. 这一讲应保留的判断力
- 实践 scaling 的单位是 recipe,不是单次 run。
- 超参迁移失败会让小实验失去预测力。
- batch 和 learning rate 同时影响优化、显存和硬件利用率。
- 开放训练报告要按工程字段读,不要只看模型名和榜单。
12. Evaluation
Evaluation 决定你会优化什么,也决定你会忽略什么。第十二讲要你把“模型好不好”拆成多个评测层次:预训练 loss、考试题、开放回答、代码任务、Agent 任务和真实产品数据。
1. perplexity 有用,但它不是全部
Perplexity 来自 next-token prediction。它衡量模型给真实文本分配概率的能力。预训练和 scaling 阶段,perplexity 很有用,因为它平滑、便宜、可以跨规模比较。
但 perplexity 不等于真实任务质量。一个模型在网页文本上 perplexity 更低,不一定更会调用工具、更会写你的业务代码、更会遵守格式。数据分布不一致时,perplexity 还可能误导你。
2. benchmark 类型和盲区
| 评测类型 | 例子 | 适合看什么 | 盲区 |
|---|---|---|---|
| Exam QA | MMLU、GPQA、HLE。 | 知识和推理题。 | 可能离真实使用很远,也有污染风险。 |
| Chat eval | Arena、AlpacaEval。 | 开放回答偏好。 | 受 judge、长度和风格影响。 |
| Code eval | HumanEval、SWE-bench。 | 代码生成和修复。 | 脚手架、环境和测试质量影响很大。 |
| Agent eval | Terminal-Bench、浏览器任务。 | 模型加工具后能否完成任务。 | 评估的是模型、工具、状态和执行环境的组合。 |
3. contamination 和生态有效性
训练数据可能包含 benchmark 题目或相似内容,这会让模型看起来更强。去重、私有评测集和时间切分可以降低污染风险,但不能完全消除。尤其是网页、GitHub、论坛和论文数据都可能进入训练语料。
另一个问题是生态有效性:评测任务是否像真实任务。很多用户任务有上下文、工具权限、文件系统、网络、失败恢复和人工反馈。单轮选择题无法覆盖这些条件。Agent 产品应该建立自己的离线 eval,并持续和真实用户失败样本对齐。
4. LM judge 的使用边界
用另一个模型当 judge 很方便,尤其适合开放回答。但 LM judge 会有偏好:更长答案、某种格式、某类措辞、和 judge 同源模型相似的输出,都可能被偏爱。它还可能被 prompt 注入或标准不清影响。
可验证任务应优先使用外部 verifier:代码跑测试、数学答案比对、SQL 结果校验、工具副作用检查。开放任务再用人类偏好或 LM judge 补充。不要把 judge 分数当成唯一真相。
自测:为什么 Agent eval 不能只评模型本身?
Agent 成败取决于模型、prompt、工具 schema、记忆、执行环境、错误恢复、权限和评测器。换一个脚手架,同一模型表现可能不同,所以评估对象是整个系统。
5. 这一讲应保留的判断力
- 评测定义优化方向,错误评测会让工程走偏。
- perplexity、benchmark、产品指标各自回答不同问题。
- 污染和生态有效性是评测可信度的两个大问题。
- Agent 需要自己的任务集、失败样本库和回归测试。
官方可执行 Lecture 12 Lecture 12 源码 Assignment 3 Scaling SWE-bench
13. Data Sources and Datasets
第十三讲把模型能力拉回数据。预训练数据不是“网上文本”四个字,它是一条从 raw crawl 到训练 shard 的数据系统。模型会学到什么,首先取决于你让它反复预测什么。
1. 原始数据不是训练数据
网页抓取下来通常是 HTML、模板、导航、广告、脚本、重复内容、乱码、表格、评论、样式和正文混在一起。PDF、代码仓库、论坛、书籍、论文、对话数据也都有各自格式。把这些东西变成训练文本,需要 extraction、cleaning、filtering、deduplication、mixing 和 sharding。
这和普通后端数据管线类似,但目标不同。普通业务管线常常追求字段准确;预训练数据管线追求让模型在海量 token 上学到有用分布,同时避免污染、违法、过多重复和低质量内容。
2. 数据来源决定能力,也决定风险
| 来源 | 可能带来什么 | 主要风险 |
|---|---|---|
| Common Crawl / Web | 覆盖面广,多语言、多主题。 | 噪声、重复、垃圾内容、版权和安全问题。 |
| Wikipedia / books | 较稳定的知识和长文本。 | 覆盖有限,风格不等于对话或任务。 |
| GitHub / code | 代码生成、API、工程模式。 | 许可证、低质量仓库、泄漏 secrets、重复 fork。 |
| arXiv / papers | 技术和数学文本。 | PDF 抽取质量、公式处理、领域偏差。 |
| Dialog / agent traces | 指令跟随、工具调用、任务执行。 | 隐私、格式污染、低质量示范会教坏模型。 |
3. pretraining、mid-training、post-training 数据不同
Pretraining 数据大而杂,目标是让模型学习广泛语言、知识和模式。Mid-training 常常更高质量、更接近目标能力,例如长上下文、代码、数学或特定领域。Post-training 数据更小,但离产品行为近,例如 instruction-response、preference、tool use trace、reasoning trajectory。
这三类数据不能混为一谈。预训练解决“模型知道世界和语言”;post-training 解决“模型按产品期望行动”。Agent 能力尤其依赖 post-training 数据和外部脚手架:工具调用格式、错误恢复、观察结果解释、状态更新,都要在数据或训练目标里出现。
4. 数据许可和可追溯性
模型数据不是纯技术问题。网页、书籍、代码、论坛和对话都有许可、隐私和合规边界。公开模型报告里越来越多会说明数据来源、过滤策略和许可处理,但细节仍经常不足。工程上至少要能追溯:某个训练 shard 来自哪里,经过哪些过滤,是否包含敏感数据,是否可能污染评测集。
自测:为什么 Common Crawl 不能直接当高质量训练文本?
它是网页原始抓取,包含导航、广告、模板、重复、乱码、垃圾内容和潜在有害文本。必须先做正文抽取、过滤、去重和质量控制。
5. 这一讲应保留的判断力
- 数据管线本身就是模型能力的一部分。
- 不同数据来源带来不同能力,也带来不同法律和质量风险。
- pretraining、mid-training、post-training 服务于不同目标。
- Agent traces 是高价值数据,但也更容易包含隐私、工具副作用和错误示范。
14. Filtering, Deduplication, Mixing, Synthetic Data
第十四讲进入数据加工细节。它回答的是:给定一堆 raw text,怎样决定哪些 token 值得让模型学习,哪些 token 会浪费 compute,哪些 token 会污染评测或损害产品行为。
1. filtering:选择你愿意让模型模仿的分布
Filtering 可以是规则、分类器、语言模型 judge 或组合系统。常见过滤包括语言识别、质量分类、有害内容过滤、PII 检测、代码许可检查、文档长度和重复模板过滤。每个 filter 都有误杀和漏杀。
高质量 filter 不是越严格越好。过严会丢掉少数语言、边缘领域、非标准表达和真实用户风格;过松会引入垃圾内容、模板噪声和不良行为。Filtering 实际上是在定义模型的训练分布。
2. deduplication:省 compute,也保护评测
重复数据会让训练浪费在同一内容上,也会增加记忆风险。Exact dedup 能删除完全相同文档,near dedup 需要 MinHash、LSH 或 embedding 相似度等方法。代码数据还要处理 fork、vendored dependencies 和生成文件。
去重对评测也重要。如果 benchmark 题目或相似答案出现在训练集中,模型可能靠记忆拿高分。训练集和测试集之间的去重,是评测可信度的一部分。
3. data mixing:比例就是训练目标
数据来源清洗完后,还要决定混合比例。代码、数学、网页、书籍、对话、多语言、长上下文数据各占多少?小而高质量的数据是否重复采样?某些低质量但覆盖面广的数据是否降权?这些选择会改变模型能力。
Data mixing 的难点在于目标不止一个。你可能希望模型会代码、会中文、会数学、会聊天、会长上下文,但每类数据都会占 token 预算。固定 compute 下,喂一种数据就少喂另一种。
4. synthetic data:便宜扩展,也可能自我污染
Synthetic data 可以把低质量文本改写成更适合训练的格式,也可以生成数学题、代码任务、工具调用轨迹或偏好样本。它的优势是可控、便宜、贴近目标任务。风险是模型生成数据会带来偏差、错误和风格收缩。
对 Agent 来说,synthetic traces 很有吸引力:可以批量生成任务、工具调用和错误恢复。但这些 trace 必须被 verifier 或真实环境检查。否则模型会学到看起来像任务执行、实际不可运行的轨迹。
自测:为什么去重既能省 compute,也能提高评测可信度?
重复样本会让训练反复学习同一内容,浪费 token 预算。训练集和测试集重复还会造成污染,让模型看似泛化,实际可能是记忆。
5. 这一讲应保留的判断力
- Filtering、dedup 和 mixing 是模型训练目标的一部分。
- 每个数据 filter 都有误杀和漏杀,必须用样本审计。
- 数据混合比例决定能力取舍,不能只追总 token 数。
- Synthetic data 需要 verifier 或真实任务闭环,否则容易制造漂亮噪声。
15. Mid/Post-training, SFT/RLHF
前面的大部分内容都在讲 base model:用大量 token 训练下一个 token 预测。第十五讲开始回答另一个问题:一个会续写文本的模型,怎样变成会按指令行动的 assistant 或 Agent。
1. base model 不天然知道产品行为
Base model 学的是训练语料里的文本分布。给它一个问题,它可能回答,也可能续写问题、换风格、编故事、输出不符合格式的内容。它没有天然的“应该帮助用户完成任务”这个目标。
Supervised Fine-tuning 用 instruction-response 数据继续训练模型。输入是指令或对话上下文,目标是人类示范答案。SFT 的作用是让模型模仿期望行为:回答问题、遵守格式、拒绝某些请求、使用工具调用语法、保持对话角色。
2. SFT 的边界
SFT 能显著改变模型“像不像 assistant”,但它不一定让模型推理更强。模型只是模仿示范数据。示范里有严谨推理、工具使用、错误恢复,它才有机会学到;示范只是漂亮话,它就会学漂亮话。
这对 Agent 很现实。很多 Agent 能力不是 prompt 写得玄,而是数据里有任务轨迹:怎么读文件、怎么计划、怎么调用工具、怎么解释错误、怎么验证结果、什么时候停止。如果 SFT 数据没有这些结构,模型很难稳定执行。
3. RLHF:当评价比示范容易
有些任务很难写出唯一正确答案,但人能比较两个答案哪个更好。RLHF 的基本流程是:让模型生成多个回答,收集人类偏好,训练 reward model 或直接用偏好优化,再更新 policy,让模型更倾向于高分回答。
PPO 是早期 RLHF 常用算法,但实现复杂、训练不稳定、系统成本高。DPO 用偏好数据直接优化 policy,省掉显式 reward model 和在线 RL 的一部分复杂度。不同算法背后共同的问题是:奖励信号是否可信,模型是否在 reward 上投机。
4. mid-training 和 post-training 的数据配方
Mid-training 常用于把模型从通用预训练推向更高质量或更贴近目标能力的数据,例如代码、数学、长上下文、特定语言。Post-training 更靠近产品:SFT、preference、tool use、safety、reasoning traces。
这解释了为什么公开模型报告里 post-training 细节常常最少,但产品差异很大。Base model 的能力是一部分,post-training 数据和评测闭环会决定模型最终行为。
自测:为什么 SFT 后模型更像 assistant,但不一定推理更强?
SFT 学的是示范答案分布。它能学会语气、格式和指令跟随;如果示范数据没有覆盖长推理、工具使用或验证步骤,模型未必获得更强任务能力。
5. 这一讲应保留的判断力
- Base model 学文本分布,assistant 行为需要 post-training。
- SFT 是模仿示范,不是自动获得深推理。
- Preference/RLHF 在“评价比生成容易”时有价值。
- Agent 的 tool use 和错误恢复能力依赖数据、训练目标和脚手架共同塑造。
Lecture 15 slides Assignment 5 Alignment InstructGPT Direct Preference Optimization
16. Post-training: RLVR
RLVR 指 Reinforcement Learning with Verifiable Rewards。它和普通偏好训练的差别是:奖励不主要来自“看起来更好”,而来自可验证结果,例如数学答案、代码测试、形式化检查或工具执行结果。
1. 为什么 reasoning model 适合可验证奖励
很多推理任务有明确答案。数学题可以比对最终值,代码题可以跑单元测试,定理可以交给 proof checker,数据库任务可以校验查询结果。相比开放聊天,这些任务的奖励更客观,也更适合 RL。
RLVR 的基本流程是:对同一个 prompt 采样多个 response 或 trajectory,用 verifier 打分,把高分样本和低分样本之间的差异转成 policy 更新。模型会被直接推向“能通过验证器”的行为。
2. GRPO 的直觉
GRPO(Group Relative Policy Optimization)常见于 reasoning RL 讨论。它的核心直觉是对同一题采样一组回答,用组内相对奖励估计哪个回答更好。这样可以减少对单独 value model 的依赖。
这只是直觉。真实实现还要处理 KL penalty、advantage normalization、mask、micro-batch、采样温度、长输出、off-policy 数据和系统吞吐。A5 的价值就在于把这些细节拉回代码。
3. RLVR 的系统成本
RLVR 不是只多一个 loss。它需要大量采样,采样本身就是 inference;还要跑 verifier、收集轨迹、计算 reward、再训练 policy。训练过程会在 inference 和 training 之间来回切换,系统复杂度比 SFT 高很多。
此外,on-policy 程度和系统效率会冲突。越想用最新 policy 采样,系统越难批量化和缓存;越想提高吞吐,样本可能越旧。大规模 RL 训练要在这个取舍里工作。
4. reward hacking 对 Agent 更危险
Verifier 定义了模型会优化什么。代码测试不完整,模型可能写出只过测试的补丁;工具环境不隔离,模型可能修改评测脚本;数学答案只看最终值,模型可能学会猜格式。RL 会放大奖励设计的漏洞。
Agent 场景天然适合 RLVR,因为环境可执行。但也天然危险,因为工具有副作用。好的训练和评测环境需要权限隔离、隐藏测试、日志审计和回滚。
自测:为什么 coding agent 比普通聊天更适合 RLVR?
代码任务有外部验证器:编译、测试、lint、运行结果和隐藏用例。奖励比“回答看起来好不好”更客观,可以直接优化任务完成率。
5. 这一讲应保留的判断力
- RLVR 的价值来自可验证奖励,不是来自 RL 这个词本身。
- GRPO 用组内相对奖励训练 reasoning 行为,但实现细节很多。
- RLVR 系统需要大量 inference、verifier 和训练闭环。
- 奖励设计不严,模型会优化漏洞而不是任务。
Lecture 16 slides Assignment 5 Alignment DeepSeekMath / GRPO
17. Alignment: Multimodality
第十七讲把文本模型扩展到图像、视频和音频。核心问题仍然是表示:Transformer 吃的是 token 或向量序列,非文本信号必须先被转换成它能处理的形式。
1. 图像如何进入语言模型
常见做法是用视觉 encoder 把图像切成 patch 或特征,再通过 projector 映射到语言模型的 hidden space。语言模型看到的是一串视觉 token 或视觉特征,加上文本 token。LLaVA 这类路线就是把视觉 encoder、projection layer 和 LLM 连接起来,再用图文指令数据训练。
CLIP/SigLIP 这类模型提供图文对齐能力:让图像表示和文本表示在同一个空间里接近。它们不直接等于多模态 assistant,但经常是视觉理解组件的基础。
2. 分辨率、patch 和 token 预算
图像越大,patch 越多,视觉 token 越多。高分辨率能保留细节,但会增加 attention、显存和延迟。AnyRes、裁剪、多尺度、局部放大等方法都在处理这个问题:什么时候看全图,什么时候看局部,什么时候牺牲细节换成本。
这对 UI Agent 特别重要。截图里真正有用的信息可能只在一个按钮、表格单元格或错误提示里。整张高分辨率截图直接送入模型很贵,也不一定更准。更好的系统会结合 OCR、布局解析、局部裁剪和交互状态。
3. 视频和音频更像序列问题
视频比图像多时间维度。帧数、分辨率和采样率都会增加 token 或 feature 数量。音频也需要切成帧或离散 token。模型要处理时间顺序、跨帧变化和同步信息。多模态系统的成本很容易因为输入长度失控。
所以多模态不是“给 LLM 加一个图片接口”。它会改变 tokenizer/encoder、上下文长度、缓存、数据管线、评测任务和产品交互。
4. 统一 token 路线
有些模型尝试把图像也离散成 token,统一用 autoregressive modeling 处理文本和图像。这类路线的吸引力是接口统一:不同模态都变成 token 序列。但它也会带来长序列成本、离散化损失和生成质量问题。
无论采用哪条路线,多模态能力都不是纯架构问题。图文数据、视频数据、指令数据、对齐数据和评测集同样重要。模型会不会读表格、看 UI、理解图表,很大程度取决于训练数据里有没有这样的任务。
自测:为什么高分辨率图片会变成推理系统问题?
高分辨率会产生更多 patch/token 或更大的视觉特征,增加计算、显存、attention 和上下文成本。系统必须在细节保留和资源预算之间取舍。
5. 这一讲应保留的判断力
- 多模态的入口是表示转换:非文本信号要变成 token 或向量序列。
- 分辨率和采样率是推理成本参数,不只是质量参数。
- UI/文档/视频 Agent 需要输入裁剪、结构解析和任务评测。
- 多模态能力同样依赖数据和 post-training。
18. Guest Lecture: Daniel Selsam
官方 schedule 把 Daniel Selsam 的 guest lecture 放在课程末尾,但当前公开页面没有稳定讲义链接。这里不编造讲义内容,只说明它在 CS336 知识链里的位置,以及你应该带着哪些问题去补这类前沿实践材料。
1. guest lecture 在课程中的作用
前 17 讲已经把语言模型从 tokenizer、architecture、training systems、scaling、data、evaluation 到 post-training 串起来。Guest lecture 通常承担另一个角色:把这些基础件放到真实研究或产业实践里,看哪些环节在现实中最脆弱。
自学时不应该把 guest lecture 当作必备基础。它更像一个检查点:你是否已经能听懂实践者说的瓶颈,例如数据质量、训练稳定性、评测污染、inference 成本、RL 系统吞吐或组织协作问题。
2. 应该带着什么问题听
- 真实模型研发中,返工最多的是数据、训练系统、评测还是 post-training?
- 小规模实验有哪些结论能迁移到大规模,哪些不能?
- 训练失败通常怎样被发现:loss spike、eval 退化、样本质量、系统错误,还是人工观察?
- Agent 或 reasoning 任务里,评测器和奖励设计怎样避免投机?
3. 这一讲应保留的判断力
完整学习 CS336,不等于每讲都有同等公开材料。对资料缺口诚实标注,比把未知内容写成摘要更有价值。后续如果官方补充 slides 或录播,这一章应该按原材料更新。
自测:如果只能问 guest lecturer 一个问题,问什么最能补你的工程认知?
建议问:在真实模型研发中,哪类证据最能提前发现大训练或 post-training 会失败?这个问题会逼对方给出可执行的诊断信号,而不是泛泛经验。
19. Guest Lecture: Dan Fu
最后一讲同样是 guest lecture,且与 A5 截止接近。课程结构暗示:CS336 的最终整合点是 post-training、systems、data 和 evaluation 的闭环,而不是某个单独算法。
1. 把全课收束成一张工程图
学完 CS336,你应该能把一个模型产品拆成下面这些问题:
这张图比背术语更重要。以后你听到“我们要换模型”“要做长上下文”“要训练 agentic model”“要降低推理成本”,都可以把问题放回这张图,追问缺少哪类证据。
2. 下一步该深入哪里
如果你做 Agent 开发,最值得继续深入的顺序通常是:Inference、Evaluation、Post-training Data、Systems。原因很直接:它们离产品成本和能力闭环最近。你不一定要马上写分布式训练框架,但应该能设计任务评测、理解推理瓶颈、知道什么样的 trace 对训练有价值。
如果以后要转 AI Infra,CS149 可以补并行计算和硬件性能模型,MIT 6.5940 可以补模型压缩、量化、TinyML 和高效 LLM 部署。它们不是和 CS336 并列的三份资料,而是同一张能力地图的不同切面。
3. 结课自检
- 能不能解释 tokenizer 为什么影响上下文成本?
- 能不能手算一个训练计划的 FLOPs 和显存粗账?
- 能不能说明 FlashAttention 为什么减少 HBM 往返?
- 能不能区分 DDP、FSDP、tensor parallel 和 pipeline parallel?
- 能不能解释 scaling law 的用途和边界?
- 能不能为一个 Agent 产品设计离线 eval 和失败样本库?
- 能不能说明 SFT、DPO/RLHF、RLVR 分别依赖什么数据和奖励?
闭卷复述题:用 3 分钟解释 CS336 的完整知识链。
参考答案:原始数据经清洗、去重和混合后被 tokenizer 编成 token;Transformer 在 next-token loss 下训练,训练成本由 FLOPs、显存、activation、optimizer state 和并行通信决定;scaling law 用小实验规划大训练;evaluation 定义优化方向并检查污染和真实任务;inference 把模型变成服务,需要处理 prefill、decode、KV cache 和 batching;post-training 用 SFT、偏好或可验证奖励把 base model 塑造成 assistant、reasoning model 或 Agent。
CS336 官方 schedule Stanford CS336 GitHub CS336 YouTube playlist
总地图:语言模型训练栈
站在 Agent 开发者视角,最该抓住的一点是:Agent 产品里的“模型能力”不是一个黑盒参数。它受到 tokenizer、数据分布、训练目标、推理系统、评测口径和 post-training 的共同约束。你在做 tool use、long context、reasoning、SSE 输出或评测时,都会撞到这些底层事实。
课程模块
速查与进度卡片
这里用于快速回忆和勾选进度。每张卡片只保留问题、知识链路、工程判断和轻练习;需要系统阅读时,以上方“完整教材正文”为准。
A1-A5 作业地图
如果你没有时间完整做作业,至少要按下面的“轻量路径”读测试和接口。测试名会告诉你课程真正希望你掌握什么,而不只是 lecture 里提到了什么。
和 Agent 开发的关系
你不需要立刻成为训练工程师
如果你的主业是 Agent 应用,最常见的误区是把模型能力当成 API 供应商给出的常量。CS336 能帮你判断:为什么长上下文贵,为什么 agentic trace 会改变 post-training 数据,为什么 evaluation 不是简单 accuracy,为什么 inference 的动态 batching 会影响产品响应。
但你需要懂底层约束
当你设计 tool schema、上下文压缩、并发策略、模型路由、推理预算、离线评测、human feedback 数据闭环时,CS336 的概念会变成工程边界。你不一定写 FlashAttention,但要知道“瓶颈是 HBM 访问还是算力”这类问题为什么重要。
术语速查
公开资料来源
页面内容以公开资料为依据,正文为中文重写和工程化组织;不复制 lecture slides 或 assignment handout 的长段原文。需要深入时,优先打开官方材料。