Karpathy nanoGPT 教程到底讲了什么:从 micrograd 到 GPT-2 训练的路线图
CS336的教程功课难度较高,曲线救过,通过 karpathy 的 nanogpt 项目,深入理解大模型的底层原理,模型的网络架构以及一些训练的工程细节,培养一定的代码能力,才能更好的完成CS336的学习。
Andrej Karpathy 的大模型从零构建教程很适合系统学习,但它也有一个现实问题:链路长、信息密度高。很多人知道这套内容值得看,却不知道每一部分到底在讲什么,也不知道自己应该精读哪里、快速过哪里。
我最近花了两周时间,跟着这条路线从 micrograd、bigram、MLP、BPE tokenizer 一直走到 GPT-2 风格训练脚本,并整理了对应 notebook 和实现笔记。本文不是替代原视频,也不是把参考实现包装成原创项目,而是站在学习路线的角度,梳理这套教程真正想带读者建立的知识链路。
整条路线可以概括为:
text
micrograd
↓
bigram
↓
MLP
↓
BPE / tokenizer
↓
Transformer
↓
GPT-2 training
它不是一组零散 demo,而是从"神经网络为什么能训练",一步步走到"一个 GPT-2 风格预训练脚本需要哪些组件"。
micrograd:先弄清楚神经网络为什么能训练
micrograd 这一部分并不是为了讲大模型,而是为了拆开 loss.backward() 背后的机制。
教程从一个很小的 Value 对象开始,让每个节点保存数值、梯度、前置节点和局部反向传播函数。这样,一个普通的数学表达式就变成了计算图。前向传播得到结果,反向传播沿着计算图把梯度传回每个参数。
这一部分真正解决的问题是:神经网络不是"神秘地学会了东西",而是在计算图上反复执行:
text
前向计算 -> 得到 loss -> 反向传播 -> 更新参数
读者在这里应该重点看三个概念:局部导数、链式法则、梯度累积。后面无论是 MLP、Transformer 还是 GPT-2 训练脚本,本质上都在重复这套流程,只是计算图更大、参数更多、模块更复杂。
bigram:用最小语言模型建立 next-token prediction
进入语言模型之前,教程没有一上来讲 Transformer,而是先讲 bigram。
bigram 模型只做一件事:根据当前字符预测下一个字符。它可以先用统计方式实现,比如统计每个字符后面跟着另一个字符的次数,再归一化成概率分布。随后,教程把这个统计模型改写成一个可训练的神经网络版本:输入字符做 one-hot,经过权重矩阵得到 logits,再用 softmax 和交叉熵训练。
这一步的价值在于,它建立了语言模型最小闭环:
text
文本 -> token id -> 输入 x / 标签 y -> logits -> loss -> 采样生成
这也是后面 GPT 的核心训练目标。GPT 看起来复杂很多,但训练目标仍然是预测下一个 token。bigram 让读者先在最小规模下理解:数据怎么构造,loss 怎么计算,模型怎么生成文本。
所以 bigram 部分不应该只看成"很简单的字符模型"。它真正讲的是语言模型的基本形式。
MLP:通过名字生成讲清楚训练、参数和诊断
MLP 部分是整套教程里非常关键的一段。它不是简单介绍"多层感知机是什么",而是借助字符级名字生成任务,讲清楚一个神经网络如何从数据、参数、训练、诊断一路搭起来。
bigram 只能看一个字符,而 MLP 开始引入上下文窗口。模型会根据前面几个字符预测下一个字符。于是,任务变成:
text
字符上下文 -> embedding -> MLP -> 下一个字符概率
这里教程讲了很多容易被忽略的参数作用。比如 block_size 决定模型能看多长的历史上下文,embedding 维度决定每个字符被表示成多大的向量,隐藏层宽度影响模型容量,batch_size 影响每次更新的样本数量,learning rate 决定参数更新幅度,BatchNorm 的 gain 和 bias 又会影响归一化之后的激活分布。
这些参数不是孤立的。教程的重点不是告诉读者"这个参数应该设成多少",而是让读者通过训练过程看到:参数变化会如何影响 loss、激活值、梯度和生成效果。
MLP 部分还特别强调可视化诊断。教程不只是看最终 loss,而是画出激活分布、梯度分布、权重梯度和参数更新幅度。比如 Tanh 激活如果大量落在接近 -1 或 1 的区域,就会进入过饱和状态,导数变小,梯度很难继续传回去。这个时候,"梯度消失"不再是抽象概念,而是可以在图上直接看到的现象。
更重要的是,这一阶段还手写了反向传播。自动求导版本跑通之后,教程进一步拆开 cross entropy、softmax、BatchNorm、Tanh、Linear 和 Embedding 的梯度链路,手动计算每一步反向传播。
这一步不是为了替代 PyTorch,而是为了验证读者是否真的理解训练过程。后面写 Transformer 时,没人会手写每个梯度,但如果完全不知道梯度如何穿过 softmax、归一化层、激活函数和 embedding,那么 backward() 仍然只是黑盒。
随后,教程把 MLP 代码整理成 Linear、BatchNorm1d、Tanh、Embedding、Flatten 等模块。这一步也不是单纯"代码更优雅",而是为后面的 Transformer Block 做准备。深度学习模型需要由可组合、可观察、可替换的模块搭起来,而不是一大段一次性张量计算。
所以 MLP 部分的重点不是"学会写一个 MLP",而是通过一个足够小的任务,把上下文建模、embedding、参数作用、训练诊断、手写反向传播和模块化实现串起来。
BPE / tokenizer:为什么 tokenizer 不是简单预处理
很多人学习大模型时会跳过 tokenizer,把它当成模型前面的字符串处理工具。但 Karpathy 的 tokenizer 部分其实很重要。
Tokenizer 决定模型真正看到的输入单位。它会影响拼写、数字、非英语文本、空格、标点、上下文长度和词表规模。很多看起来像"模型能力"的问题,背后可能和 tokenization 有关。
BPE 的核心思想是从字节或字符开始,统计相邻 pair 的频率,把高频 pair 合并成新 token,不断重复这个过程。教程先讲为什么需要这样的机制,再讲 pair 统计、merge、encode 和 decode。
这里读者应该重点观察两个问题:第一,文本不是天然等于 token;第二,词表大小不是越大越好。词表变大可以压缩序列长度,但也会增加 embedding 和输出层的计算成本,并且让部分 token 训练不充分。
这一部分为 GPT-2 training 做了直接铺垫。最终训练脚本读入的不是原始字符,而是 tokenizer 编码后的 token id。理解 tokenizer,才能理解模型输入、上下文长度和词表大小之间的关系。
Transformer:把上下文建模能力真正做起来
Transformer 部分是前面所有内容的合流。
教程通常不会直接把完整 GPT 代码扔出来,而是先从 mask 开始,让读者理解自回归语言模型为什么不能看到未来 token。然后进入 self-attention:每个位置通过 Query、Key、Value 去和上下文中的其他位置交互。
attention 的核心是让模型根据当前 token 的需要,动态选择上下文里哪些信息更重要。multi-head attention 则是在多个子空间里并行做这种信息聚合。
随后,教程加入 FFN、残差连接、LayerNorm、position embedding 和多层 Block。一个 Transformer Block 可以理解为:
text
LayerNorm
↓
Multi-Head Attention
↓
Residual
↓
LayerNorm
↓
Feed Forward
↓
Residual
这里要注意:训练目标仍然没有变,还是预测下一个 token。真正变化的是模型利用上下文的方式。bigram 只能看一个字符,MLP 看固定窗口,Transformer 则通过 attention 在上下文窗口内建立更灵活的依赖关系。
这一部分直接通向 GPT-2 模型主体。理解了 attention、FFN、LayerNorm、残差和 position embedding,就能读懂 GPT 的大部分结构。
GPT-2 training:从模型结构走到训练工程
最后一步不是再介绍一个新概念,而是把前面的东西整合成训练脚本。
一个 GPT-2 风格训练脚本至少需要包含:
- tokenizer,把文本变成 token id;
- DataLoader,构造输入和右移一位的标签;
- token embedding 和 position embedding;
- 多层 Transformer Block;
- logits 和 cross entropy;
- AdamW 优化器;
- learning rate warmup 和 cosine decay;
- gradient accumulation;
- mixed precision;
- validation loss;
- 文本采样;
- checkpoint;
- DDP 入口。
这一部分的价值在于让读者看到:模型结构只是预训练系统的一部分。真正能跑起来的训练脚本,还需要处理数据、batch、显存、学习率、评估、保存、多卡同步等工程问题。
因此,GPT-2 training 部分不应该被理解为"我重新发明了 GPT-2"。更准确的说法是:通过复现一个 GPT-2 风格训练链路,理解预训练脚本需要把哪些组件组合起来。
不同读者应该怎么取舍
这套教程很完整,但不一定所有人都要从头到尾逐行精读。更合理的方式,是根据目标选择重点。
如果目标是应用开发,重点看 tokenizer、Transformer 基本结构和推理生成。你需要知道模型输入输出是什么,但不一定马上手写反向传播。
如果目标是做微调,建议重点看 tokenizer、数据管线、next-token prediction、训练 loop、loss 和 optimizer。微调不是只调 API 参数,本质上仍然是在已有模型上继续执行训练过程。
如果目标是理解预训练,最好完整走一遍 micrograd、bigram、MLP、BPE、Transformer 和 GPT-2 training。因为预训练不是单点知识,而是一条完整链路。
如果目标是训练优化,则可以在 GPT-2 training 之后继续看 DDP、混合精度、torch.compile、FlashAttention、Triton、DeepSpeed 或 Megatron-LM。这些内容属于更靠后的工程优化层。
简单说:
text
应用开发:tokenizer -> Transformer -> 推理
模型微调:tokenizer -> 数据管线 -> 训练 loop
预训练理解:micrograd -> bigram -> MLP -> BPE -> Transformer -> GPT-2 training
训练优化:DDP -> 混合精度 -> compile -> 高性能 attention
结尾
Karpathy 这套教程真正有价值的地方,不只是教你写出某个模型,而是把"大模型训练"拆成了一条可以理解的路线:
text
梯度如何产生
↓
语言模型如何定义
↓
神经网络如何训练
↓
文本如何变成 token
↓
Transformer 如何利用上下文
↓
GPT-2 训练脚本如何组织
对于觉得视频太长、不知道如何抓重点的人,这条路线比单个知识点更重要。因为一旦理解了每一部分在整条链路中的位置,就能根据自己的目标决定该精读哪里、快速过哪里、后续继续补哪里。
这也是整理这篇文章的目的:不是替代原教程,而是帮读者降低筛选成本,理解 Karpathy 这套内容为什么这样安排,以及它如何一步步把人带到 GPT-2 预训练的核心流程。
这是我从基础原理逐步走到 GPT-2 风格模型训练的学习与实践项目:https://github.com/hurrypeter02-cmd/gpt-from-scratch.git
觉得有帮助的朋友可以点点赞,谢谢大家。(◍・ᴗ・◍)❤