跳到主要内容

通读笔记 — Build a Large Language Model (From Scratch)

格式:每章记「讲了什么机制 / 关键数字 / 值得引的段落行号(搜词)」。行号 = text/*.txt 行号。

前置材料

  • preface(04):作者 2022 年 ChatGPT 发布后写;「best way to understand LLMs is to code one from scratch」(L28-29);Iris 对照:两个参数 90% 准确率 vs LLM 几十亿参数(L11-14);LLM 「don't have to be a black box」(L14)。
  • about this book(06):写给会 Python 的人,高中数学即可(L34-36);代码全在 GitHub rasbt/LLMs-from-scratch(L80-82);刻意设计能在普通笔记本跑(L92-94);PyTorch 为主线,附录 A 教 PyTorch。七章路线:L46-77。
  • about the author(07):Sebastian Raschka,PhD;Lightning AI 的 staff research engineer(实现和训练 LLM);之前是 UW-Madison 统计系助理教授;写了多本 Python 机器学习畅销书。教育者+从业者双重身份。

第 1 章 Understanding LLMs(text/09,613 行)

  • LLM = 深度神经网络,海量文本训练;「large」指参数量+数据集(L55-58);next-word prediction「very simple task…surprising」(L59-62)。
  • AI⊃ML⊃DL 层级图(L93-96);传统 ML 手工特征 vs DL 免特征工程,但两者都要标签(L115-125)。
  • §1.3 两阶段:预训练(无标注文本、自监督 self-supervised,模型自己造标签 L221-226)→ 基座/基础模型(GPT-3 是例子,能文本补全+有限 few-shot L229-235)→ 微调(有标注),两类:指令微调 / 分类微调(L239-244)。自建理由:领域定制(BloombergGPT)、数据隐私、端侧部署(L171-183)。
  • §1.4 Transformer:2017「Attention Is All You Need」arxiv.org/abs/1706.03762(L250-252),本为英德/英法翻译;encoder+decoder+self-attention;BERT=encoder 侧,遮词预测,用于分类(X 用它检测有毒内容 L321-322);GPT=decoder 侧,生成。「Transformers vs LLMs」框:不是所有 transformer 都是 LLM(可用于视觉),不是所有 LLM 都是 transformer(有 RNN/CNN 架构,动机是计算效率)(L383-394)。
  • §1.5 数据:GPT-3 训练集表(L404-418):CommonCrawl 410B tokens 60% / WebText2 19B 22% / Books1 12B 8% / Books2 55B 8% / Wikipedia 3B 3%;合计 499B 但只训了 300B,作者没说为什么(L428-433);CommonCrawl 410B tokens ≈ 570GB;LLaMA 加了 Arxiv 92GB、StackExchange 78GB(L434-438);GPT-3 预训练成本估 $4.6M(L446-448);公开替代数据集 Dolma(3T tokens,Soldaini et al. 2024,arxiv 2402.00159,但有版权问题 L439-443)。
  • §1.6 GPT:原始论文 Radford et al.「Improving Language Understanding by Generative Pre-Training」(L461-462);ChatGPT 初代 = GPT-3 + InstructGPT 方法指令微调(arxiv 2203.02155)(L464-466);decoder-only、自回归 autoregressive(先前的输出当下一步输入 L486-493);原始 transformer 6 层 vs GPT-3 96 层 175B 参数(L494-496);GPT-3 是 2020 年的,「considered a long time ago」但 Llama 仍是同一套概念、只有小改(L519-524);emergent behavior:没专门训翻译却会翻译(L526-538)。
  • §1.7 三阶段施工图(L540-573):Stage1 数据准备+注意力+架构;Stage2 预训练成 foundation model;Stage3 微调成分类器或个人助理。预训练 GPT 级模型要几千到几百万美元,所以 Stage2 只用小数据集教学(L579-582)。

第 2 章 Working with text data(text/10,1442 行)

  • §2.1 嵌入:text is categorical → continuous-valued vectors(L60-63);embedding = mapping discrete→continuous vector space(L104-106);句/段嵌入用于 RAG(检索增强,书说超出范围 L107-112);Word2Vec:similar contexts→similar meanings(L113-117);LLM 自己学嵌入(输入层的一部分,训练中更新)比 Word2Vec 好:针对手头任务和数据优化(L144-150);GPT-2 最小 117M/125M 参数用 768 维,GPT-3 175B 用 12,288 维(L155-161)。
  • §2.2 分词:练习文本是 Edith Wharton 短篇 "The Verdict"(公有领域,Wikisource),20,479 字符(L194-226);正则切分演示:保留大小写(大写帮 LLM 分专名/普通名词 L251-254);空白去留取决于应用(代码对缩进敏感 L272-279);全文切出 4,690 个 token(L310-311)。
  • §2.3 token ID:词表按字母序、去重,1,130 词(L373);SimpleTokenizerV1 encode/decode;对新文本 "Hello, do you like tea?" 报 KeyError: 'Hello'(L498-507)——词表外词问题。
  • §2.4 特殊 token:<|unk|> 与 <|endoftext|>,词表变 1,132;多文档拼接时 <|endoftext|> 标记边界(L545-574);BOS/EOS/PAD 三兄弟(L667-678);GPT 只用 <|endoftext|>(既当 EOS 又当 PAD,因为有 mask,PAD 用哪个 token 无所谓 L679-684);GPT 不用 <|unk|>,因为 BPE(L685-687)。
  • §2.5 BPE:tiktoken 库(OpenAI 开源,Rust 实现),书用 0.7.0(L694-707);"gpt2" 编码;词表 50,257,<|endoftext|> 是 50256(L739-742);生词 "someunknownPlace" 能正确编解码——BPE 把词表外的词拆成子词或单字符,如 "Akwirw ier"→"Ak","w","ir","w","ier"(L743-769);原理一句话:从单字符起,反复合并高频相邻对,frequency cutoff(L778-787)。
  • §2.6 滑窗采样:BPE 后全书 5,145 token(L828);x=[290,4920,2241,287],y 是 x 右移一位 [4920,2241,287,257] (L838-848);文本版 "and → established"…(L877-880);GPTDatasetV1 + DataLoader:batch_size/max_length/stride;stride=1 相邻两行只差一位(L1011-1024);drop_last=True 防 loss spikes(L978-981);小 batch 省显存但更新噪声大,是超参权衡(L1030-1059);stride=max_length 不重叠,重叠多了会过拟合(L1097-1099)。
  • §2.7 嵌入层:torch.nn.Embedding 是查表;演示:vocab 6 × 3 维,seed 123,真实权重矩阵(L1149-1167);token ID 3 取向量 = 第 4 行(L1170-1181);嵌入层 ≈ one-hot+矩阵乘的高效实现,可反向传播优化(L1183-1190)。
  • §2.8 位置编码:自注意力本身不知道位置/顺序(L1211-1216);绝对 vs 相对位置嵌入(L1275-1296);GPT 用绝对位置嵌入,且是学出来的(原 transformer 是固定的 L1301-1305);演示 256 维(比 GPT-3 的 12,288 小),8×4×256 张量;pos_embedding 输入是 arange(0..context_length-1)(L1361-1377);token+pos 相加 = input embeddings(L1379-1391)。

第 3 章 Coding attention mechanisms(text/11,1879 行)

  • §3.1 为什么需要注意力:德译英例子 "Kannst du mir helfen diesen Satz zu uebersetzen",逐词翻译语法错(L83-99);RNN encoder-decoder:整个句子压进一个 hidden state(memory cell),解码时无法直接访问更早的隐状态 → 长句丢上下文(L143-149)。
  • §3.2 Bahdanau 注意力 2014(解码器每步选择性访问所有输入);三年后 transformer 用自注意力,甩掉 RNN(L160-193)。「self」= 同一序列内部互相关照,对比 seq2seq 的两个序列之间(L233-241)。
  • §3.3 简化自注意力(无可训权重):输入 "Your journey starts with one step",6 个 3 维向量,真实数值(L299-307);三步走查:① 注意力分数 = query 与每个输入的点积(dot product 是对齐度/相似度的度量 L362-369),query=journey 得 [0.9544, 1.4950, 1.4754, 0.8434, 0.7070, 1.0865] (L345);② 归一化成和为 1;softmax 更好:管极端值、梯度性质好、保证为正(L399-418);naive softmax 数值不稳,实战用 torch.softmax(L419-426);③ context vector z(2) = 加权求和 = [0.4419, 0.6515, 0.5683] (L446-448);全序列:双重 for 循环 → 矩阵乘 inputs @ inputs.T,再 softmax(dim=-1),再 attn_weights @ inputs(L527-577)。
  • §3.4 加可训权重(= scaled dot-product attention):三个权重矩阵 Wq/Wk/Wv 把输入投影成 query/key/value(L648-652);演示 d_in=3→d_out=2(GPT 里通常 d_in=d_out L687-689);query_2=[0.4306,1.4551] (L710);attn_score_22=1.8524(L766);缩放:分数除以 sqrt(d_k);为什么——嵌入维常 >1000,点积大 → softmax 趋近阶跃函数 → 梯度近零 → 训练停滞;这就是「scaled dot-product attention」名字的由来(L815-824);「weight parameters vs attention weights」框:前者是学出来的网络系数,后者是动态的、随上下文变的值(L712-719);Q/K/V 术语借自信息检索/数据库(query 像检索词,key 像索引,value 是实际内容 L868-882);SelfAttention_v1(nn.Parameter)→ v2(nn.Linear,更好的初始化方案)(L884-1027)。
  • §3.5 因果注意力(掩码注意力):只许看当前位置及之前(L1052-1059);三步法:softmax → tril 掩码置 0 → 重新归一化(L1095-1180);「information leakage」框:先 softmax 再掩码再归一化,等价于只在未遮蔽位置上算 softmax,不泄漏(L1182-1197);高效法:先把上三角填 -inf 再 softmax(e^-inf→0),一步搞定(L1199-1246);dropout:训练时随机丢弃隐藏单元防过拟合,只在训练用;50% 演示,剩下来的乘 1/0.5=2 补偿,保持总影响一致(L1253-1334);真训 GPT 用 0.1/0.2(L1264-1266);CausalAttention 类:register_buffer 注册 mask(随模型自动搬 CPU/GPU L1415-1420);带下划线的是原地操作(L1408-1411)。
  • §3.6 多头:stack wrapper(多个 CausalAttention 实例拼接,torch.cat dim=-1)vs 一体化 MultiHeadAttention:一次矩阵乘再按 head_dim = d_out/num_heads 切分(view/transpose 四维张量 (b, heads, tokens, head_dim)),out_proj 输出投影非必须但常见(L1614-1811);效率差异:wrapper 每个头单独做矩阵乘,一体化只做一次(L1805-1811);GPT-2 small(117M):12 头 768 维;GPT-2 XL(1.5B):25 头 1600 维;context length 1024(L1845-1855)。

第 4 章 Implementing a GPT model(text/12,1604 行)

  • §4.1 骨架:对准 GPT-2 small(124M,原报告写 117M 是错的,后更正 L63-66);参数=可训权重,2,048×2,048 矩阵 = 4,194,304 个参数(L105-108);GPT-2 vs GPT-3 框:架构相同只差规模,选 GPT-2 因为权重公开且笔记本跑得动;Lambda Labs 估单卡 V100 训 GPT-3 要 355 年(L110-120);GPT_CONFIG_124M 字典七个键逐条解释(vocab 50257/context 1024/emb 768/heads 12/layers 12/drop 0.1/qkv_bias False)(L123-153);DummyGPTModel 占位→数据流 forward:tok_emb+pos_emb→drop→trf_blocks→final_norm→out_head(logits)(L180-240);演示输入 "Every effort moves you"/"Every day holds a" → token IDs [6109, 3626, 6100, 345] 等(L296-298);输出 [2,4,50257] logits(L309-328)。
  • §4.2 LayerNorm:梯度消失/爆炸让训练不稳(L333-340);层归一化 = 把每层输出调成均值 0、方差 1(单位方差),位置:多头注意力前后+最终输出前(L347-354);演示:5 输入 6 输出 Linear+ReLU(ReLU:负的归零 L395-399),mean/var(dim=-1, keepdim),手工 (x-mean)/sqrt(var)(L378-481);LayerNorm 类:eps=1e-5 防除零,scale/shift 两个可训参数(L509-530);biased variance 框:unbiased=False(除 n 不除 n-1),为了和 GPT-2/TensorFlow 默认一致、兼容预训练权重(L532-543);LayerNorm vs BatchNorm 框:layer norm 沿特征维、与 batch 大小无关,利于分布式/受限环境(L587-598)。
  • §4.3 GELU 与前馈:ReLU 简单但 LLM 用 GELU(Gaussian error linear unit)和 SwiGLU(L607-614);GELU(x)=x·Φ(x),实际用 tanh 近似(GPT-2 也用这个近似,曲线拟合来的 L615-619);GELU 平滑、负值也有非零输出(约 -0.75 处除外),优化性质更好(L653-668);FeedForward = Linear(768→3072)+GELU+Linear(3072→768),先扩 4 倍再压回,进出同维便于堆叠(L674-765)。
  • §4.4 捷径连接(残差连接):为计算机视觉 ResNet 提出,对付梯度消失(L790-797);五层网演示:无捷径梯度 0.0002→0.005(逐层缩小),有捷径 0.22→1.32(稳定)(L930-960);做法 = 层输出加上层输入,给梯度开短路(L798-802)。
  • §4.5 TransformerBlock:组装 norm1→att→drop→+shortcut→norm2→ff→drop→+shortcut;Pre-LayerNorm(先归一化再进子层),旧架构 Post-LayerNorm 训练动态更差(L1075-1079);输入输出同形 [2,4,768],形状保持是刻意设计,内容被重编码进上下文(L1101-1112)。
  • §4.6 GPTModel 总装:124M 版 transformer block 重复 12 次,最大 GPT-2(1542M)重复 48 次(L1150-1154);最终 LayerNorm → 线性输出头映到 50,257 维(L1155-1158);数参数:numel 合计 163,009,536 ≠ 124M——weight tying:原 GPT-2 输出层复用 token 嵌入层权重(都是 [50257,768]);扣掉输出层 = 124,412,160(L1300-1338);作者经验:分开反而训得更好,现代 LLM 也都分开,但第 6 章加载 OpenAI 权重时要处理 tying(L1339-1343);内存:163M × 4 字节(float32)= 621.83 MB(L1348-1364)。
  • §4.7 生成文本:逐步迭代,输出接回输入(图 4.16 "Hello, I am" → 6 轮 → "...a model ready to help.");generate_text_simple:裁剪 context[-context_size:]→no_grad→logits[:,-1,:]→softmax→argmax(贪心解码 greedy decoding)→cat 接回(L1462-1488);softmax 是单调的,argmax 直接作用 logits 结果一样,写出来只是为了完整直觉(L1489-1495);model.eval() 关 dropout;未训练的模型输出乱码 "Hello, I am Featureiman Byeswickattribute argue"——只搭了骨架、权重还是随机的(L1570-1578)。

第 5 章 Pretraining on unlabeled data(text/13,1863 行)

  • §5.1.1 本章把 context_length 砍到 256(笔记本可训;GPT-2 原本 1024,训完再改回)(L107-115);未训练模型输出 "Every effort moves you rentingetic wasn't..."(L184)。
  • §5.1.2 损失:走查 inputs "every effort moves"/"I really like" → 目标右移一位(L251-262);未训练模型目标 token 的概率 ≈ 1/50257 ≈ 0.00002(L347-351);真实数:target_probas Text1 [7.45e-05, 3.11e-05, 1.16e-05] (L400-401);训练目标 = 最大化正确 token 的概率(L403-406);反向传播框(L407-418);六步算损失:logits→softmax 概率→取目标位置→log→平均→取负 = 交叉熵损失 cross entropy(L420-479),真实数:log_probas [-9.50, -10.38, ...] 平均 -10.7940,取负 10.7940;torch cross_entropy 一步算完,结果同(L524-529);困惑度 perplexity = exp(loss):10.79→48,725,解释成「模型每一步等效于在 48,725 个词里拿不准」(词表才 50,257)(L531-546)。
  • §5.1.3 训练/验证损失:The Verdict 5,145 token;90/10 切分;batch 2 × 256;9 个训练 batch、1 个验证 batch(L744-749);真实损失:Train 10.99 / Val 10.98(L812-813);预训练成本框:Llama 2 7B,184,320 GPU 小时 A100,2 万亿 token,AWS 8×A100 约 $30/时 → 约 $690,000(L592-598);可改用 Project Gutenberg 6 万本公有领域书(附录 D)(L587-590)。
  • §5.2 训练循环:八步流程图(epoch→batch→清零梯度→算损失→backward→optimizer.step→评估→打印样本)(L848-891);train_model_simple 代码(L899-937);evaluate_model 用 model.eval()+no_grad(L941-959);AdamW 框:Adam 变体,改进 weight decay(惩罚大权重防过拟合),LLM 常用(L984-990);真实训练:lr=0.0004, weight_decay=0.1, 10 epoch,MacBook Air 约 5 分钟(L1010-1012);损失曲线:Train 9.781→0.391,Val 9.933→6.452,第 2 轮后分叉 = 过拟合(小数据+多 epoch 必然;背原文验证:生成文本里 "quite insensible to the irony" 能在原文搜到)(L1014-1070);真实大模型通常只训 1 epoch(L1079-1081)。
  • §5.3 解码策略:贪心的问题——背原文;multinomial 按概率抽样:1,000 次实验 "forward" 582 次、"toward" 343 次、"closer" 73 次、"inches" 2 次(L1211-1228);temperature = logits 除以一个数(L1229-1234):T=1 不变,T=0.1 尖锐化(≈argmax),T=5 平坦化("every effort moves you pizza" 约 4% 概率 L1273-1280);top-k:只留概率最高 k 个,其余 logits 填 -inf 再 softmax(和第 3 章因果掩码同一个 -inf 技巧 L1326-1330);演示 k=3:forward 0.5775/toward 0.3610/closer 0.0615(L1366-1370);合并版 generate():top_k+temperature+eos_id 提前停止(L1381-1409);效果:top_k=25,T=1.4 → 生成全新句子而非背书(L1423-1431)。
  • §5.4 存取权重:state_dict(层→参数字典),torch.save/load;.pth 是惯例(L1472-1479);续训要连 optimizer 状态一起存(AdamW 有历史动量,丢了可能训不动 L1492-1498)。
  • §5.5 加载 OpenAI GPT-2 权重:原始权重是 TensorFlow 存的(L1533-1535);124M 七个文件(model.ckpt.data 498MB 等 L1566-1582);settings {n_vocab 50257, n_ctx 1024, n_embd 768, n_head 12, n_layer 12};params 键 blocks/b/g/wpe/wte(L1599-1601);四种尺寸对照表:124M/355M/774M/1558M(注意与第 4 章 345/762/1542 写法不同——书上两处数字不一样,第 5 章用 OpenAI 官方口径)(L1627-1684);qkv_bias=True:OpenAI 的 GPT-2 在 QKV 线性层用了偏置,现代 LLM 不用了(不提升性能),但加载老权重必须匹配(L1700-1706);assign() 查形状;load_weights_into_gpt 逐块搬运:c_attn 的 w 按列三切成 q/k/w 再转置;out_head 直接用 wte(weight tying 现身 L1790-1799);作者坦白:这个函数「took a lot of guesswork」,靠 assign 的形状检查和「模型能不能说人话」来验证(L1807-1811);加载后生成连贯文本 "Every effort moves you toward finding an ideal new way..."(L1832-1838)。

第 6 章 Fine-tuning for classification(text/14,1596 行)

  • §6.1 两类微调:指令微调(指令+答案对,任务广)vs 分类微调(只能输出训练时见过的类;专用模型比通用模型好做 L101-103);选择建议框:指令微调要更多数据和算力,分类微调少数据少算力但限死类别(L105-115)。
  • §6.2 数据:SMS Spam Collection(UCI),5,572 条,ham 4,825 / spam 747(L197-216);下采样到各 747 条(类别不平衡的其他方法超出范围 L222-224);标签 ham→0 spam→1;70/10/20 训练/验证/测试(L264-286)。
  • §6.3 数据加载:不等长消息的两种处理:截到最短 or 补到最长;选补长(不丢信息 L306-312);用 50256(<|endoftext|>)当 PAD;最长消息 120 token(L431-432);验证/测试集补到与训练集同长,超长截断;batch 8;130/19/38 个 batch(L555-557)。
  • §6.4 加载预训练模型:BASE_CONFIG(qkv_bias=True 匹配 OpenAI 权重,drop_rate=0);先验证模型能生成连贯文本;直接提示它判垃圾短信失败(只经过预训练,不会听指令 L651-676)——这就是要微调的证据。
  • §6.5 分类头:把 out_head(768→50257)换成 768→2;为什么不用 1 个输出节点:那样要改损失函数,2 节点方案通用(L685-692);冻结全模型(requires_grad=False),只留新头可训;作者实验:再放开最后一个 transformer block + 最终 LayerNorm 明显更好(L804-811);只看最后一个 token 的输出:因果掩码下,只有最后一个 token 能 attend 到全部前序 token,信息最全(L986-989);softmax 可省(argmax 直接作用 logits)(L1073-1080)。
  • §6.6 损失与准确率:微调前准确率 ≈ 46-49%(接近瞎猜 50%)(L1142-1147);准确率不可导,用交叉熵当代理(L1148-1152);初始损失约 2.3-2.6(L1201-1203)。
  • §6.7 微调:train_classifier_simple 与预训练循环几乎一样,只换评估方式(算准确率不生成文本);AdamW lr=5e-5;5 epoch;M3 MacBook Air 约 6 分钟,V100/A100 半分钟(L1322-1325);曲线:loss 2.15→0.08,准确率 70%→100%(训练)/97.5%(验证)(L1347-1364);全量评估:训练 97.21% / 验证 97.32% / 测试 95.67%,差距小 = 几乎没过拟合;验证集略高于测试集很常见(超参是照着验证集调的)(L1466-1479);选 epoch 数框:没有通解,5 是常用起点,看损失曲线调(L1423-1431)。
  • §6.8 用起来:classify_review 函数;两条真实短信都判对;存盘 review_classifier.pth(L1546-1577)。

第 7 章 Fine-tuning to follow instructions(text/15,2092 行)

  • §7.1 预训练模型会续写但不会听指令("Fix the grammar" "Convert to passive voice" 这类会失败 L57-64);指令微调 = 监督式指令微调。
  • §7.2 数据:本书特制的 1,100 条指令-回答对,JSON 204KB(L114-123);条目结构 instruction/input/output,input 可空(L163-176);提示风格:Alpaca(### Instruction:/### Input:/### Response:)vs Phi-3(<|user|>/<|assistant|>),书用 Alpaca 因为它最早公开指令微调流程、定义了原始做法(L209-218);format_input 函数;85/10/5 切分:935/110/55(L285-307)。
  • §7.3 自配 collate:DataLoader 默认 collate 不够,要自定义(L342-350);五步:① 套模板 ② 分词 ③ 按批内最长补齐(不同 batch 不同长度,省 padding L471-476)④ 目标 = 输入右移一位+尾部加 50256 ⑤ 目标里的 pad 换成 -100(L374-398);为什么留一个 50256:让模型学会回答完毕时生成结束符(L675-678);-100 的机关:PyTorch cross_entropy 默认 ignore_index=-100,直接忽略;演示:2 token 损失 1.1269,加第三个目标 1 → 0.7936,改成 -100 → 又回到 1.1269(证明被忽略)(L803-853);指令遮蔽(把指令部分的目标也设 -100,只对回答算损失,防背指令):研究者意见不一,2024 Shi et al.「Instruction Tuning With Loss Over Instructions」(arxiv 2405.14394)发现不遮蔽反而更好;本书不遮蔽(L857-898)。
  • §7.4 device 放进 collate:后台搬运不阻塞 GPU(L947-951);partial 预填参数;allowed_max_length=1024。
  • §7.5 换 gpt2-medium (355M):124M 容量不够学指令跟随(L1048-1081);1.42GB 下载;基线测试:预训练模型对「主动转被动」指令只会复读原句(L1192-1197);提取回答 = generated_text[len(input_text):]。
  • §7.6 训练:复用第 5 章 train_model_simple;初始损失 3.83/3.76;AdamW lr=5e-5,2 epoch;硬件表:355M 在 M3 CPU 15.78 分钟 / L4 1.83 / A100 0.86 分钟(2 epoch)(L1271-1283);训完 loss 0.30/0.66,验证集例子学会了 "The meal is cooked every day by the chef."(L1323-1352);2 epoch 就够,再多会过拟合(L1344-1347)。
  • §7.7 提取回答:三个测试例:明喻(bullet vs lightning 对)、雷雨云(cumulus 错,应为 cumulonimbus)、Pride and Prejudice(对但啰嗦)(L1459-1502);指令微调评估不像分类那样一个准确率搞定:三种做法——MMLU 这类选择题基准(arxiv 2009.03300)、人类偏好评比(LMSYS chatbot arena)、让另一个 LLM 打分(AlpacaEval)(L1503-1519);110 条测试集生成回答存 JSON(A100 1 分钟/M3 6 分钟 L1572-1573)。
  • §7.8 用 LLM 评 LLM:本地 Ollama(包装 llama.cpp,纯 C/C++,只推理不训练 L1612-1616)跑 8B Llama 3(4.7GB,要 16GB RAM;phi3 3.8B 只要 8GB;70B 更强)(L1675-1699);REST API query_model(seed 123, temperature 0 求确定);逐条打分 0-100:bullet 85 分、cumulus 40 分(部分给分)、Jane Austen 95 分(L1860-1927);平均分 50.32;参照:Llama 3 8B 基座 58.51,Llama 3 8B instruct 82.6(L1997-2001);Ollama 不确定定性,分数会漂,可多跑几次取平均(L1982-1985);改进方向四条(超参/数据/提示格式/更大模型 L1986-1995)。
  • §7.9 收尾:后面还有偏好微调(preference fine-tuning,DPO,GitHub 有补充材料 L2014-2020);跟进领域:arXiv cs.LG、r/LocalLLaMA、作者博客;推荐工具 Axolotl、LitGPT(作者参与开发 LitGPT——利益相关)(L2071-2074)。

附录 A Introduction to PyTorch(text/16,1703 行)

  • PyTorch 三件套:张量库(NumPy+GPU)、自动微分引擎 autograd、深度学习工具(L32-62);2019 年起研究界用得最多(Papers With Code),Kaggle 2022 约 40%(L18-25);书用 PyTorch 2.4.0(L186-189)。
  • 张量:标量 0 维/向量 1 维/矩阵 2 维;默认 int64/float32(32 位是精度与效率的平衡,GPU 为 32 位优化 L343-347);.view vs .reshape(连续性);@ 即 matmul。
  • autograd:logistic 回归走查 y=1.0, x1=1.1, w1=2.2, b=0.0 → z=x1*w1+b → sigmoid → BCE loss;grad(loss, w1) = -0.0898(L462-573);计算图算完即销毁,retain_graph=True 保留;.backward() 自动算所有叶节点梯度存 .grad;反向传播 = 微积分链式法则在计算图上的实现(L499-504);读者只需记住「PyTorch 替我们算微积分」(L591-595)。
  • nn.Module:init 定义层,forward 定义数据流;随机小初始化是为了「打破对称」(否则各节点做同样运算学不到东西 L740-746);推理用 torch.no_grad() 省内存省算(L796-799);PyTorch 惯例:模型直接吐 logits,损失函数内部做 softmax(数值稳定)(L809-814)。
  • Dataset/DataLoader:init/getitem/len 三件套;shuffle 每轮换序防循环更新;drop_last 防小批扰乱收敛;num_workers:0 = 主进程加载(GPU 会空等),经验值 4(L1008-1057)。
  • 训练循环(A.7):model.train() → logits → cross_entropy → zero_grad → backward → step;SGD lr=0.5 演示 3 epoch 收敛到 0;zero_grad 忘了会梯度累积(L1137-1139);model.eval()/train() 对 dropout/BN 有意义,习惯上永远写(L1122-1128)。
  • 存取:state_dict + torch.save/load,架构要匹配(L1263-1285)。
  • GPU(A.9):.to("cuda"),所有张量必须同一设备否则 RuntimeError(L1337-1350);小模型 GPU 反而慢(搬运开销),LLM 才见得着加速(L1408-1410);DDP:每 GPU 一份模型副本+DistributedSampler 切数据+梯度跨卡平均同步;两卡≈一倍速;Jupyter 里跑不了 DDP;CUDA_VISIBLE_DEVICES 选卡(L1430-1627)。

附录 D 训练循环三件套(text/19,397 行)

  • 学习率预热 warmup:从 initial_lr 线性升到 peak_lr(0.0001→0.01,20-27 步),warmup 步数取总步数 0.1%-20%,防开局大步打乱(L78-102)。
  • 余弦衰减 cosine decay:warmup 后按半个余弦周期降到 ≈0(min_lr = 0.1×initial_lr),后期小步防冲过极小点(L157-166);公式 lr = min_lr + (peak-min)0.5(1+cos(π·progress))(L189-191)。
  • 梯度裁剪 gradient clipping:clip_grad_norm_(max_norm=1.0),L2 范数(欧几里得长度)超阈值就按比例缩;演示:裁剪前最大梯度 0.0411 → 后 0.0185(L215-281)。
  • 合体 train_model:warmup 后才裁剪;The Verdict 上 15 epoch,Train 0.041 / Val 6.915(照样过拟合,小数据使然)(L381-398)。

附录 E LoRA(text/20,669 行)

  • LoRA(Hu et al. 2021,arxiv 2106.09685):不学完整 ΔW,学两个小矩阵 A(in×r)、B(r×out)使 AB≈ΔW;r 是超参,alpha 是缩放因子(常取 rank 的一半/等值/两倍)(L9-58);分配律:W·x + AB·x 可分开算 → 预训练权重不动,LoRA 矩阵外挂,每个客户/应用只存小矩阵(L60-77)。
  • LoRALayer:A 用 kaiming_uniform 初始化,B 初始化为零 → 一开始 AB=0 不改动原输出(所以初始准确率与第 6 章逐位相同)(L322-332, L387-389, L559-568)。
  • LinearWithLoRA = 原 Linear(冻结)+ LoRA 旁路相加;replace_linear_with_lora 递归替换全模型 Linear(L371-399)。
  • 真实数字:冻结后 0 可训;rank=16, alpha=16 时 LoRA 参数 2,666,528 vs 124,441,346,省约 50 倍(L449-475)。
  • 结果:M3 约 12 分钟(比第 6 章全量微调 6 分钟还慢——小模型上前向多了一步 LoRA 计算,不划算;大模型反向贵才显出快 L619-620);训练 100% / 验证 96.64% / 测试 98.00%(原文 L660-662;L666-667 里 97.33% 与 98.00% 两个数不一致,原文如此);结论:只调 270 万个权重就达到全量微调水平(L664-670)。

附录 B/C(略读)

  • B = 各章参考文献列表(导航章性质,不引)。有用条目:BloombergGPT arxiv 2303.17564;GPT-3 论文「Language Models are Few-Shot Learners」Brown et al. 2020, arxiv 2005.14165。
  • C = 习题答案。有用数字:习题 4.1 答案——一个 transformer block 里前馈模块 4,722,432 参数 ≈ 注意力模块 2,360,064 的两倍(L75-90)。

差异化(三本同族)

  • 梁楠《从零构建大模型》(已拆):中文、广度优先零件目录、工程主题(算力/压缩/实战)。
  • Grigorov《Building LLMs from Scratch》(已拆):2025 现代配方(RMSNorm/RoPE/GQA/MoE/SwiGLU)+ CUDA + 对齐四阶段。
  • Raschka 这本:经典 GPT-2 配方(绝对可学位置/LayerNorm/GELU/完整多头),但它是「一台机器从零到能听指令」的完整连续构建,每一步都印真实数字(张量值、损失曲线、运行时长、准确率),教学序(简化→可训→因果→多头)是精心设计的学习坡道;收尾有完整微调循环(分类+指令+LLM 评 LLM)和 LoRA 附录。作者身份:独立教育者(UW-Madison 前助理教授、Lightning AI 研究工程师),Manning 出版,代码开源在 rasbt/LLMs-from-scratch。

书架锚(② 类,已验证文件存在)

  • minbpe#01-bpe-core.md — BPE 算法本体(书说 BPE 实现超出范围)
  • pytorch#03-autograd-engine.md — autograd 引擎(附录 A)
  • nanogpt#01-model.md — Karpathy 的 GPT 实现(第 4 章对照)
  • flash-attention#01-io-bottleneck.md — 注意力效率(第 3 章边界)
  • peft#01-lora-math.md — LoRA(附录 E)