斯坦福 CS336 从零构建大模型 (2025春) - 第一讲:(超详细且易懂的笔记,且最后有核心问答部分解决你的所有困惑)
斯坦福 CS336 从零构建大模型 (2025春) - 第一讲:课程概述与分词
这个视频是斯坦福大学 CS336 “从零开始构建语言模型” 2025年春季课程的第一讲:课程概述与分词(Overview and Tokenization),由 Percy Liang 和 Tatsu 教授主讲。视频极其硬核,详细探讨了为什么我们需要从零手写大模型,拆解了构建大模型的五个核心模块(基础、系统、缩放定律、数据、对齐),并在最后深入讲解了分词(Tokenization)技术的底层逻辑。
以下是不遗漏任何知识点的详细拆解:
一、 为什么要“从零开始”构建大模型?
- 研究危机与抽象漏洞(Leaky Abstractions): 现在的研究人员越来越脱离底层技术,习惯把闭源模型(如GPT-4)当黑盒,通过Prompting(提示词)来调用API。虽然这带来了一些便利,但“抽象漏洞”意味着如果你不理解底层的运作机制,就无法进行核心的基础研究。
- 前沿模型的封闭性与误导性: 像 GPT-4 这种拥有 1.8 万亿参数、耗资 1 亿美元训练的模型,其技术细节是不公开的。如果我们只在小规模模型上做研究,很容易得出错误结论。例如,在小模型中,注意力机制(Attention)和全连接层(MLP)的算力开销差不多;但在 175B 的大模型中,MLP 的开销会占据绝对主导。此外,“上下文学习(In-context learning)”等涌现能力,在算力没有达到一定规模前是完全观察不到的。
- 课程的三大教学目标:
- 机制(Mechanics): 知道Transformer怎么运作,了解模型并行等底层构件。
- 思维方式(Mindset): 拥抱“缩放(Scaling)”理念,追求极致榨干硬件性能。这也是OpenAI引领新一代AI革命的核心思维。
- 直觉(Intuitions): 知道什么架构和数据在什么规模下有效(比如 SwiGLU 激活函数的成功往往来自于实验结果,被戏称为“神圣的恩典”)。
- 重新理解“苦涩的教训(Bitter Lesson)”: 并不是只要无脑砸钱扩大算力就行,真正的核心是**“规模化下的算法效率(Algorithms at scale)”**。2012到2019年间,算法效率提升了44倍(超越了摩尔定律),这也是为什么研究人员必须极度关注系统和算法效率的原因。
二、 课程的五个核心模块(五大支柱)
这门课包含五个纯手写代码的作业(不提供脚手架代码),涵盖大模型的全生命周期。
1. 基础(Basics)
- 现代架构改进: 现在的 Transformer 相比 2017 年有了很多细节改进,比如 SwiGLU 激活函数、旋转位置编码(RoPE)、RMSNorm 归一化、混合专家系统(MoE),以及为了解决注意力平方级爆炸而引入的 GQA、MLA、滑动窗口注意力和线性注意力机制。
- 优化器: 主流依旧是 AdamW,但课程也会提及 Muon 和 SOAP 等新优化器。学生需要在 90 分钟的 H100 算力内训练模型,尽可能降低困惑度(Perplexity)。
2. 系统(Systems)
- 内核(Kernels): GPU 的计算单元在芯片上,而显存(Memory)在芯片外。计算的真正瓶颈是数据的搬运成本。课程将教授使用 Triton 编写算子,通过算子融合(Fusion)和分块(Tiling)来最大化 GPU 利用率。
- 并行(Parallelism): 学习跨 GPU 的数据并行(Data Parallelism)、张量并行以及 FSDP 等技术。
- 推理(Inference): 推理分为两个阶段:“预填充(Prefill)”阶段可以并行处理所有 Prompt 的 Token,受限于计算能力;而“解码(Decode)”阶段只能自回归地逐个生成 Token,受限于内存带宽,很难让 GPU 满载。课程会教授**投机解码(Speculative decoding)**技术,用小模型快速生成草稿,再用大模型并行验证。
3. 缩放定律(Scaling Laws)
- 在给定的算力(FLOPs)预算下,应该训练一个“参数多但数据少”的模型,还是“参数少但数据多”的模型?
- Chinchilla 最优法则: 经验法则是,训练数据的 Token 数量应该是模型参数量的 20 倍(例如 1.4B 的模型应使用 28B 的 Token 训练)。
- 学生需要通过一个“训练 API”在小算力预算下盲测,拟合出缩放曲线,从而预测大规模训练的最优超参数。
4. 数据(Data)
- 模型的能力完全取决于数据。真实的网络数据(如 Common Crawl)充满了垃圾和无意义的代码。
- 前沿实验室常常需要花钱购买高质量数据。
- 数据处理链路非常复杂,包括:将 HTML 转换为纯文本(这是有损的过程)、训练分类器过滤有害或低质量内容、以及进行数据去重(Deduplication)。
5. 对齐(Alignment)
- 预训练得到的“基础模型”只会接话把儿(预测下一个词),需要通过对齐让它听从指令、带有特定语气,并具备拒绝回答危险问题的安全性。
- 监督微调(SFT): 仅仅需要约 1000 条高质量的 Prompt-Response 数据,就能让模型具备听从指令的能力。
- 基于反馈的学习(强化学习): 当 SFT 数据标注太贵时,可以使用偏好数据(比较A和B哪个好),或者用验证器(针对数学/代码,或训练一个语言模型作为裁判)来进行强化学习。
- 课程将要求实现 DPO(直接偏好优化,极其简单有效)以及 DeepSeek 提出的 GRPO(去除了价值网络的相对偏好优化)。
三、 深入技术细节:分词(Tokenization)
分词是将人类的字符串(Unicode文本)映射为模型能看懂的整数序列(Tokens)的过程。词表大小(Vocabulary size)就是这些整数的取值范围。
关于分词的几个冷知识:
- 空格通常被算作 Token 的一部分(且通常放在 Token 的开头)。
hello和hello(带前导空格)会被切分成两个完全不同的 Token。- 压缩率(Compression Ratio): 即平均每个 Token 代表多少个字节。GPT-2 的压缩率大约是 1.6。
三种失败/低效的分词尝试:
- 基于字符(Character-based): 每个 Unicode 字符对应一个整数。缺点:极其浪费词表空间(罕见的 Emoji 会和常见字母占用同等权重的槽位),而且导致序列长度很长。
- 基于字节(Byte-based): 将文本转为 UTF-8 的 256 个字节。优点是词表极小(只有256),非常优雅。致命缺点: 压缩率为 1,文本会被切得极度零碎。因为 Transformer 的注意力计算量随序列长度呈平方级增长,序列过长会导致计算效率极其低下。
- 基于词(Word-based): 传统的 NLP 做法,按空格切分。缺点是真实世界的词汇量是无限的,遇到没见过的词只能输出未知(UNK)标记,非常令人头疼。
目前的标准解法:字节对编码(BPE, Byte Pair Encoding)
BPE 本来是 1994 年用于数据压缩的算法,后来被 GPT-2 发扬光大。它的核心思想是:让分词器根据语料库的统计规律自适应学习。
- 先把所有文本转为字节序列。
- 统计相邻两个字节(或已合并的Token)结对出现的频次。
- 找到出现频次最高的一对(例如 116 和 104),将它们合并为一个全新的 Token(例如赋予索引 256),并扩大词表。
- 在整个文本中替换这个新 Token,然后重复上述合并过程,直到达到预设的合并次数。
BPE的优势:
它巧妙地让高频常见的词组融合成一个单独的 Token,而罕见词则保留为较短的碎片或字节,完美平衡了词表大小和序列长度(提升了压缩率)。实践中通常会先用正则表达式(Pre-tokenizer)把文本切成块,然后再在每个块内运行 BPE 算法以提升效率。
四、 核心概念问答 (Q&A)
Q1:基于字节的分词方法为什么会把文本切得细碎?
A: 基于字节的分词方法(Byte-based tokenization)之所以会把文本切得非常细碎,是因为它将字符串直接转换为底层的字节序列(例如使用UTF-8编码),并把每一个字节(0-255)都强制作为一个独立的Token输入给模型。
这种做法虽然让词表变得极其小巧优雅(词表大小固定为256),但它的压缩率(Compression Ratio)仅为 1,即平均每个Token只能代表一个字节的信息量。
举个例子: 在UTF-8编码体系下,一个普通的英文字母(如 a)占用1个字节,而一些复杂的字符(例如某些罕见字符或Emoji表情)最多可以占用4个字节。
假设我们要处理一个Emoji表情,如果是基于字符或词的分词,它可能只算作1个Token;但在基于字节的分词法下,这一个单独的Emoji字符就会被直接切碎成 4 个毫无语义关联的独立Token。同样地,如果输入一句包含中文字符的简短句子,每个中文字符通常占用3个字节,这会导致原本很短的句子被拆解成一条极其冗长的字节序列。
这种“细碎”会带来致命的计算问题:因为文本被切分成了漫长的字节序列,模型的输入长度会急剧增加。在Transformer架构中,注意力机制(Attention)的计算成本是随着序列长度呈平方级(Quadratic)爆炸式增长的。因此,将文本切得过碎会导致序列过长,进而使得算力开销变得极其庞大且低效,这在实际工程中是不可接受的。
Q2:处理纯英文文本时,字节和字符分词的细碎程度有区别吗?
A: 在处理纯标准英文字母时,两者的细碎程度没有区别。
在UTF-8编码体系下,普通的标准英文字母(例如 a)恰好只占用1个字节。因此一个由5个字母组成的英文单词(如“hello”)在两种方法下都会被切分成5个独立的Token。
但如果我们目光放到真实世界的英文应用场景中,会有以下区别:
- 遇到特殊字符时,字节分词依然更细碎:真实英文文本往往不仅包含基础字母,还会有特殊的标点符号、生僻符号或Emoji表情(如地球Emoji的代码点多达6位数)。基于字符的方法将其映射为1个Token,而基于字节的方法(UTF-8)会将其拆解成至多4个毫无语义的字节Token。
- 不管哪种,对大模型来说都“太碎了”:这两种方法的压缩率都太低。原本用一个词(如“university”)就能表达的概念,会被强制切分成10个字母/字节片段。输入序列长度激增会产生无法承受的平方级算力开销。
所以,大模型必须采用BPE(字节对编码)等自适应算法,让常见的英文单词融合成为1个整体Token,从而大幅缩短序列长度并提升计算效率。
Q3:BPE将频次最高的一对合并为新Token后,还需要删除原Token吗?
A: 不需要也不可以删除原来的两个token(例如基于字节的 116 和 104)。
合并的过程实际上是在**“扩大词表(expanding the vocab)”**。原本的词表包含了 0 到 255 的基础字节,合并则是创造了一个新的插槽并追加到词表中。我们需要区分“词表”和“被处理的文本序列”:
- 在词表中(绝不能删除):必须保留旧Token作为“保底”。在复杂文本中,它们随时可能单独出现或与其他不同字节组合。如果删除,遇到单独的116就会变成未知标记(UNK)。
- 在被处理的文本序列中(会被替换):扫描训练语料时,一旦发现 116 和 104 相邻出现,分词器就会将这两个旧Token**替换(replace)**成新的Token。序列长度变短,实现了压缩。
合并操作相当于给高频固定搭配发了“VIP专属通行证”,但单字的“普通通行证”依然留在词表库里,以备其他不常见的拼写情况使用。
Q4:BPE的循环处理不会导致效率很低吗?
A: 如果盲目地循环遍历所有的合并规则,效率确实会非常低下。真实工程中(比如 GPT-2 的实现)为了解决这个严重的效率瓶颈,采用了一个关键的加速策略:预分词(Pre-tokenization / Pre-processing)。
具体的优化做法如下:
- 先用正则表达式切块:在运行 BPE 算法之前,先使用类似基于词(Word-based)的分词器或正则表达式,将长篇原始文本快速切分成独立的、较短的片段或词块(例如按空格标点切分)。
- 在小片段内局部运行 BPE:随后,BPE 算法只在这些被切分好的独立小片段内部单独运行,而不是每次都在全局大段文本上进行扫描和替换。
这种“分而治之”的策略将极其昂贵的全局字符串替换操作,转换成了范围极小的局部操作,极大提升了分词器的运行速度。
最后,讲师预告第二节课将深入 PyTorch 的底层,并讲解如何进行计算资源(FLOPs)的核算。
更多推荐
所有评论(0)