date
May 18, 2026
summary
这不仅仅是<Build-LLM-From-Scratch>这本书的总结,更是一场从"这玩意儿到底怎么工作的?"到"哦,原来我也能造一个!"的奇妙旅程。
status
Published
tags
AI Agent
热门文章
必看精选
论文
slug
Build-LLM-From-Scratch
icon
category
AI
type
Post
从零构建大语言模型 —— 读完这篇你就懂了
这不仅仅是一本书的总结,更是一场从"这玩意儿到底怎么工作的?"到"哦,原来我也能造一个!"的奇妙旅程。
第1章:大语言模型是什么鬼?
1.1 LLM 到底是啥?
LLM(Large Language Model),翻译成人话就是:一个特别大的神经网络,吃了海量的文本数据,然后学会了说人话。
但你千万别被骗了——所谓的"理解",其实是模型能生成看起来通顺、上下文相关的内容,它并没有人类的意识。就像鹦鹉会说"你好",但它并不真的想跟你打招呼。
LLM 的核心训练任务特别简单:预测下一个词。给你一句话的前半段,猜后半段。比如:
- 输入:"我今天中午吃了——"
- 输出:"饭"
就这么朴素的逻辑,居然能训练出 ChatGPT 这种怪物。这就像你教孩子就教了"1+1=2",结果他自己学会了微积分——研究者自己都觉得很惊讶。
1.2 LLM 能干啥?
翻译、写小说、写代码、做客服、总结文档、回答法律问题……基本上跟文字沾边的事它都能干。现在的 LLM 不仅仅是聊天机器人,它正在重新定义我们和技术的交互方式。
1.3 构建 LLM 的两步走
- 预训练(Pretraining):用海量无标注文本训练模型做"填空",得到一个"基础模型"(foundation model)。这一步极其烧钱——GPT-3 的预训练花掉了 460 万美元的云计算费用。
- 微调(Fine-tuning):用少量标注数据进一步训练,让它变成某个领域的专家。又分两种:
- 分类微调:比如判断邮件是不是垃圾邮件
- 指令微调:比如让模型听懂"把这段话翻译成法语"
自定义的小模型能在特定领域干翻通用大模型,而且还省钱、保护隐私、能部署在手机上。苹果公司就在搞这个。
1.4 Transformer 架构:LLM 的"祖宗"
2017 年的神论文《Attention Is All You Need》提出了 Transformer 架构,它有两个子模块:
- 编码器(Encoder):读入输入文本,编码成向量
- 解码器(Decoder):拿编码器的输出,生成目标文本
原始 Transformer 是用于翻译的,编码器读英文,解码器吐中文。后来的 BERT 用了编码器部分(擅长分类),GPT 用了解码器部分(擅长生成)。本书只做 GPT,也就是只用解码器。
1.5 海量数据集
GPT-3 的训练数据包括:
- CommonCrawl(网络爬虫):4100 亿 token,占 60%
- WebText2:190 亿 token
- Books1 + Books2:670 亿 token
- Wikipedia:30 亿 token
总共训练了 3000 亿 token。什么概念?如果一个人每秒读一个字,不吃不睡要读 9.5 万年。
1.6 GPT 架构的精髓
GPT 本质上就是 Transformer 的解码器部分,它通过自回归的方式生成文本——每次预测一个词,然后把这个词拼回输入,再预测下一个词。
GPT-3 有 96 层 Transformer、1750 亿参数。这些模型还展现出涌现能力(emergent behavior)——没专门学过翻译,但能翻译;没专门学过写诗,但能写。就像你本来想养条看门狗,结果它自己学会了弹钢琴。
1.7 三阶段构建计划
本书把构建 LLM 分为三个阶段:
阶段 | 内容 | 章节 |
Stage 1 | 数据准备 + 注意力机制 | 第2-3章 |
Stage 2 | 构建 LLM + 预训练 | 第4-5章 |
Stage 3 | 微调(分类 + 指令) | 第6-7章 |

(图片来自gpt-image_prompt: A three-stage pipeline diagram showing Stage 1 (Data Preparation & Attention Mechanism), Stage 2 (GPT Architecture & Pretraining), and Stage 3 (Fine-tuning for Classification & Instruction Following), with arrows connecting each stage, colorful modern flat design style)
第2章:跟文本数据打交道——把"人话"变成机器能啃的饲料
2.1 词嵌入:给每个词发张"身份证"
神经网络不认识文字,只认识数字。所以我们需要嵌入(embedding)——把每个词映射成一串浮点数。
比如"猫"可能被映射成
[0.43, 0.15, 0.89],"狗"映射成 [0.41, 0.18, 0.85]。相似的词向量距离近,不相似的离得远。GPT-2 Small 的嵌入维度是 768,GPT-3 最大版是 12,288。维度越高能捕捉的关系越细腻,但计算量也越大。这就好比用 4K 电视看《猫和老鼠》——画质再好,内容还是那只猫追那只老鼠。
2.2 分词:把句子大卸八块
分词(tokenization) 就是把一段话切成小碎片。作者用了一篇叫《The Verdict》的短篇小说(20,479 个字符)做示例。
先用 Python 正则表达式手动实现分词器,处理了单词、空格、逗号、句号、问号、引号、双破折号等特殊情况。最终把这篇小说切成了 4,690 个 token。
2.3 Token → Token ID:编号发牌
每个唯一 token 分配一个唯一整数 ID。作者实现了
SimpleTokenizerV1 类,有 encode(文本→ID列表)和 decode(ID列表→文本)两个方法。但有个大问题:遇到没见过的词(比如"Hello"),直接报
KeyError。2.4 特殊 Token:给 LLM 加点暗号
为了解决这个问题,引入两个特殊 token:
<|unk|>:表示"我不认识这个词"
<|endoftext|>:分隔不相关的文本片段
GPT 系列只用
<|endoftext|> 就够了,其他模型还会用 [BOS](开始)、[EOS](结束)、[PAD](填充)。2.5 Byte Pair Encoding(BPE):GPT 的秘密武器
SimpleTokenizer 遇到未知词就变成
<|unk|>,太粗暴了。GPT 用的是 BPE 分词器(来自 OpenAI 的 tiktoken 库)。BPE 的牛X之处:不认识整个词?没关系,拆成子词甚至单个字符。所以 BPE 永远不会遇到 unknown token——它总能把输入拆成它能处理的碎片。
GPT-2 的 BPE 词汇表有 50,257 个 token。
2.6 滑动窗口采样:制造训练数据
LLM 的训练是预测下一个词,所以需要构造"输入-输出"对。
用滑动窗口在文本上移动:
max_length 控制输入长度,stride 控制步长。比如 stride=1,输入是 [1,2,3],输出就是 [2,3,4]——每次往后挪一位。从一篇短篇小说就能造出海量的训练样本。
2.7-2.8 Token 嵌入 + 位置编码
Token ID 经过
nn.Embedding 层变成向量(一个大查询表)。但嵌入层有个致命缺陷:不管词在句子的哪个位置,向量都一样。解决方案是位置编码。GPT 用可训练的位置嵌入——每个位置有自己的向量,直接加到 token 嵌入上。位置 1 加位置 1 的向量,位置 2 加位置 2 的。简单粗暴但有效。

(图片来自gpt-image_prompt: A flowchart showing text processing pipeline: raw text → tokenization → token IDs → embedding layer → embedding vectors + positional encoding → final input vectors, modern flat design with bright colors)
第3章:注意力机制——让 LLM 学会"察言观色"
3.1 RNN 的"记忆缺陷"
在 Transformer 出来之前,RNN 是处理序列数据的主流。它的工作方式是把输入压缩成一个"记忆状态",然后基于这个状态做生成。
但 RNN 记不住太长的东西。就像让你传话,传了十个人之后,原话早就走样了。
3.2 注意力的诞生
2017 年的 Transformer 论文提出**自注意力(Self-Attention)**机制——模型在生成每个词时,可以"回头看"输入序列中的所有位置,决定哪些地方更重要。
3.3 丐版自注意力
先看不带可训练权重的简化版本:
对于句子中每个词,计算它跟所有词的相关性(用点积衡量),然后 softmax 归一化成概率,再用这些概率加权求和所有词的向量——得到上下文向量。
这个上下文向量融合了整个句子的信息,解决了 RNN 的"健忘"问题。
3.4 完全体:缩放点积注意力
真正的 LLM 用的是带三个可训练权重矩阵的自注意力:
- Wq(Query):你在问"这段话里谁跟我有关系?"
- Wk(Key):每个词在说"我的特征是这些"
- Wv(Value):"如果觉得我重要,就拿走这个信息"
计算步骤:
- 输入分别乘 Wq、Wk、Wv 得到 Q、K、V
attn_scores = Q @ K.T算相关性
- 除以
√d_k(缩放),防止高维度下点积过大导致梯度消失
- softmax 归一化
output = attention_weights @ V得到上下文向量
3.5 因果注意力:不许"开天眼"
训练时,模型不能看到未来的词。比如预测 "I love you" 中的 "you" 时,只能看到 "I love",不能偷看答案。
实现方式:在注意力权重矩阵的右上角加一个上三角掩码,把未来位置的权重全变成 0。方法是在 softmax 之前把右上角全设为
-inf,这样 softmax 后自然就是 0。还引入了 Dropout,随机丢弃一部分注意力权重(训练时用 0.1-0.2),防止过拟合。
3.6 多头注意力:一个脑袋不够用
多头注意力就是并行运行多个因果注意力机制。不同头关注不同信息——一个可能关注语法关系,另一个关注语义相似性。
GPT-2 Small(1.24 亿参数)有 12 个头,嵌入维度 768,每个头处理 64 维。
高效实现方式:先做一次大 QKV 投影,然后拆分成多个头,用批量矩阵乘法并行计算所有头。

第4章:搭积木——实现 GPT 模型架构
4.1 GPT-2 的"身份证"
GPT-2 Small(1.24 亿参数)的配置:
参数 | 值 |
词汇量 | 50,257 |
上下文长度 | 1,024 |
嵌入维度 | 768 |
注意力头数 | 12 |
Transformer 层数 | 12 |
Dropout | 0.1 |
为什么用 GPT-2 不是 GPT-3?因为 GPT-3 的 1750 亿参数在单张 V100 上要训 355 年。等你训完,世界末日可能都过去好几轮了。
4.2 Layer Normalization:情绪稳定剂
深度神经网络的梯度容易消失或爆炸。层归一化把每一层的输出拉成均值为 0、方差为 1 的标准分布。
LayerNorm 不依赖 batch size(不像 BatchNorm),所以特别适合 LLM 训练。
而且它还有可训练的
scale 和 shift 参数——如果模型觉得标准化后不好用,它能自己调回去。就像你妈让你把房间收拾整齐,但你之后可以把枕头摆歪一点——舒服就行。4.3 GELU:比 ReLU 更"温柔"
ReLU 简单粗暴:正数留着,负数变零。
GELU 就温柔多了:负数区域也保留非零梯度,只是很小。ReLU 像"不及格就滚",GELU 像"不及格还可以补考"。
前馈网络(FeedForward)结构:Linear(768 → 3072) → GELU → Linear(3072 → 768)。先扩大 4 倍再缩小,维度没变但内涵丰富了。
4.4 Shortcut Connections:高速通道
随着网络加深,梯度在反向传播时会越来越小。**Shortcut(残差连接)**就是把输入直接加到输出上,给梯度修了一条"高速公路"。
没有 shortcut 时第一层比第五层梯度小 25 倍;加了 shortcut,各层梯度大小基本一致。
4.5 Transformer Block:组装成型
每个 Transformer Block 的结构:
- LayerNorm → Multi-Head Attention → Dropout → Shortcut
- LayerNorm → FeedForward → Dropout → Shortcut
注意这里用的是 Pre-LayerNorm(先归一化再进子层),比 Post-LayerNorm 训练更稳定。
关键是:输入输出形状完全一样!所以你可以像叠罗汉一样叠 12 层。
4.6 GPTModel:终极合体
GPTModel = Token Embedding + Positional Embedding + 12× TransformerBlock + Final LayerNorm + Output Head(映射到 50,257 维词汇表)
1.63 亿参数,float32 存储需要 621 MB。跟今天 Llama 3 70B(存权重就要 140GB)比,简直是小巫见大巫。
4.7 文本生成
用
generate_text_simple 做自回归生成:输入 "Hello, I am",输出 "Featureiman Byeswickattribute argue"。这是啥玩意儿?别急——模型还没训练,权重是随机的,跟刚出生的婴儿一样,话都不会说。
第5章:预训练——从"胡言乱语"到"能说会道"
5.1 评估文本质量:怎么量化"说得好不好"?
交叉熵损失(Cross-Entropy Loss):模型预测的概率分布跟实际情况差多远。值越小越好。
困惑度(Perplexity) =
exp(loss)。初始值 48,725,相当于模型在 50,257 个词里完全随机乱猜。理想目标:接近 1。训练数据集:《The Verdict》(只有 5,145 个 token),90% 训练 / 10% 验证。
5.2 训练过程:见证 AI 的成长
用 AdamW 优化器,训练 10 个 epoch(约 5 分钟):
Epoch | Loss | 生成文本 |
1 | 9.78 | Every effort moves you,,,,,,,,,, (变逗号复读机) |
2 | 6.66 | Every effort moves you, and, and, and, ... ("and"复读机) |
9 | 0.54 | 开始输出语法正确的句子 |
10 | 0.39 | 能写出通顺的英文句子 |
从逗号复读机到能写出完整句子——这 5 分钟的学习曲线比某些大学生四年的进步还明显。
但验证 loss 在第 2 个 epoch 后就停滞在 6.45 了,而训练 loss 降到了 0.39——过拟合了。解决办法:用更大的数据集。
5.3 解码策略:让模型有创意而不是背答案
贪心解码(Greedy Decoding):每次选概率最高的 token。安全但无聊,像每次都点同一道菜。
Temperature 缩放:控制随机性。
- temperature = 0.1:几乎确定,像严谨的科学家
- temperature = 1:正常发挥
- temperature = 5:放飞自我,有 4% 概率生成 "Every effort moves you pizza"
Top-k 采样:只保留概率最高的 k 个 token 再采样。k=3 时,"forward、toward、closer" 竞争,"pizza" 直接出局。
把两者结合:先用 top-k 筛选候选池,再用 temperature 调整概率分布后再采样。
5.4 保存和加载模型
如果要继续训练,记得连 optimizer 状态一起保存——AdamW 记住了每个参数的历史学习率,丢了就失忆了。
5.5 白嫖 OpenAI 的预训练权重
自己训太贵?直接用 OpenAI 的 GPT-2 权重!下载 498MB 的权重文件,需要做一个"翻译"工作——OpenAI 的变量命名和我们不一样,得手动映射。
加载成功的标志:输入 "Every effort moves you",不再输出乱码,而是输出 "toward finding an ideal new way to practice something!"——这才是正常人该说的话。

(图片来自gpt-image_prompt: Illustration showing pretraining process: unlabeled text data flowing into a GPT model, the model predicting next tokens, loss curve decreasing from high to low over epochs, and the output changing from gibberish to coherent sentences, colorful educational style)
第6章:分类微调——让 LLM 当"专才"
6.1 微调的两种路子
- 分类微调:让模型变成"偏科生",只会做一件事(比如判断垃圾邮件)
- 指令微调:培养"全才",能听懂各种指令
全才比偏科生难训练得多。作者说得很直白:全才难做,偏科容易。
6.2 准备垃圾邮件数据集
用 SMS Spam Collection 数据集(5,572 条短信)。原始数据不平衡(垃圾 747 vs 正常 4,825),用欠采样平衡到 747 vs 747。
数据集按 70% 训练、10% 验证、20% 测试拆分。
6.3 创建数据加载器
短信长短不一,用
<|endoftext|> token 填充到一样长。每个 batch 内所有序列统一长度。6.4 加载预训练权重
加载 GPT-2 预训练权重。试一下未经微调的模型:你问它"这是垃圾邮件吗?",它不回答,只会继续补全文本——光预训练不微调,模型就是个复读机。
6.5 加装分类头
把原来的输出层(映射到 50,257 个 token)换成新的(映射到 2 个类别:spam / not spam)。
技巧:
- 冻结大部分参数(设置
requires_grad = False),只训练输出层 + 最后一个 Transformer block + 最后的 LayerNorm
- 只用最后一个 token 的输出做分类——因为因果注意力掩码让最后一个 token 能看到前面所有信息
6.6 训练结果
超参数:AdamW,学习率 5e-5,weight_decay 0.1,5 个 epoch。
在 M3 MacBook Air 上跑约 5.65 分钟:
指标 | 数值 |
训练集准确率 | 97.21% |
验证集准确率 | 97.32% |
测试集准确率 | 95.67% |
测试集 95.67%,相当能打。而且训练和验证曲线贴得很近——没有过拟合,nice。
6.7 保存模型
第7章:指令微调——让 LLM 当"通才"
7.1 目标
把 GPT-2 medium(3.55 亿参数)训练成一个能听懂人话指令的模型。"把这句话改成被动语态"、"伦敦的首都是哪里"——这些活儿它都得会干。
跟分类微调不同,指令微调不限制输出——输出空间大得没边,难度高得多。
7.2 准备指令数据集
1,100 条指令-回答对(JSON 格式),每条包含
instruction、input、output 三个字段。使用 Alpaca 格式组织 prompt:
数据集划分:85% 训练(935 条)、10% 测试(110 条)、5% 验证(55 条)。
7.3 自定义 Collate 函数
指令长度不一,需要每个 batch 各自填充到当前 batch 的最长长度,而不是整个数据集的最长长度。这样节省大量计算和内存。
关键技巧:把 padding token 在 target 中对应的位置设为 -100。PyTorch 的交叉熵损失默认忽略 -100,这样 padding 部分就不会参与损失计算。不然模型会去学预测那些无聊的填充 token。
7.4 加载 GPT-2 medium(355M)
为什么用 3.55 亿参数而不是 1.24 亿?因为指令微调任务复杂,小模型撑不住。
没微调的模型试一下:让它把主动句改被动句——它直接复读原句,还顺带重抄了一遍指令。就像学生考试时把题目抄了一遍当答案。
7.5 正式微调
超参数:AdamW,学习率 5e-5,只跑 2 个 epoch(数据集小,再多就过拟合了)。
在 A100 GPU 上只需 52 秒。训练 loss 从 2.637 降到 0.300。
第 1 个 epoch 结束:已经能把 "The chef cooks the meal every day." 改成 "The meal is prepared every day by the chef."——虽然用了 "prepared" 而不是标准答案的 "cooked",但语法和语义完全正确。
第 2 个 epoch 结束:变成正确的 "The meal is cooked every day by the chef."
7.6 评估
人工评估 110 条回复太累?用另一个 LLM(Llama 3 8B)来打分!
评分 prompt 里包含:指令 + 标准答案 + 模型回答,让 Llama 3 从 0-100 打分。
测试样例:
- "快如子弹"(标准答案是"快如闪电")→ 85 分(没毛病但不够精确)
- "积云"(标准答案是"积雨云")→ 40 分(方向对了但答错了)
- "简·奥斯汀" → 95 分(接近完美,但稍微啰嗦了点)
110 条测试集平均分:50.32 分。可以用来做基准对比。

(图片来自gpt-image_prompt: Flow diagram of instruction fine-tuning: instruction dataset → formatting into Alpaca-style prompts → GPT-2 medium model → fine-tuning → model generates responses → evaluation by another LLM (Llama 3) giving scores, modern clean design)
写在最后
从零构建大语言模型的完整流程:
- 第 2 章:把原始文本变成 token,再变成嵌入向量
- 第 3 章:实现核心注意力机制
- 第 4 章:组装 GPT 模型架构
- 第 5 章:预训练——从随机权重到能说完整句子
- 第 6 章:分类微调——让模型当"专才"
- 第 7 章:指令微调——让模型当"通才"
这本书最牛的地方在于:所有代码都能在你的笔记本电脑上跑。不需要几百张 GPU,不需要几百万美元。你只需要一台普通电脑、Python、PyTorch,还有这本书(或者这篇总结)。
作者在最后说:从零实现大模型是理解 LLM 工作原理最有效的方式。而我要说:能坚持看到这里的人,已经比 99% 只会用 ChatGPT 的人更懂 LLM 了。
恭喜你,毕业了!🎓
所有代码和资源见原书 GitHub:https://github.com/rasbt/LLMs-from-scratch
- 作者:zion
- 链接:https://gendlee.github.io/Build-LLM-From-Scratch
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。





