ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从micrograd到nanoGPT:复刻Karpathy式深度学习技能树

从micrograd到nanoGPT:复刻Karpathy式深度学习技能树 1. 先给 Andrej Karpathy 的“技能树”画个像1.1 研究、工程与教学三栖能力为何稀缺做 AI 这行的人多少都看过 Karpathy 的代码、视频或者博客。从斯坦福的 CS231n到特斯拉 Autopilot 视觉团队再到 OpenAI 早期成员身份他的履历几乎横跨了深度学习从学术到产品落地的全过程。但真正让他和普通研究者、普通工程师拉开距离的不是某一个论文或者某一个开源库而是一套可复制的“技能组合”。圈子里绝大部分人只占据一个位置有人把论文讲得头头是道但一写代码就崩有人工程能力强到能把模型压到手机端跑但让他解释梯度下降为什么有效他就只能背公式。Karpathy 的特别之处在于他同时能站在三个位置能推导数学、能写高效代码、能把复杂概念讲到让初学者听懂。这种能力不靠天赋靠的是长期刻意训练。我自己的体会是很多人误以为“懂原理”和“会实现”是一回事。其实不是。原理是压缩后的知识实现是展开后的知识。能把两者随时互相转换才算真正吃透。Karpathy 的 micrograd、nanoGPT 这些项目本质上就是“用代码展开原理”的示范。1.2 技能树的四个主干数学直觉、系统编程、模型实现、表达输出我把 Karpathy 的技能体系拆成四个主干每个主干背后都有对应的代表作和可学习的方法。技能主干代表作品/行为核心能力数学直觉CS231n 课程、神经网络反向传播推导能把矩阵运算和梯度流对应到计算图上系统编程nanoGPT、minGPT、llm.c能写出干净、高效、可复现的训练代码模型实现micrograd、makemore能用最小代码实现完整训练闭环表达输出博客、视频教程、开源 README能把技术判断转化成他人可理解的内容这四个主干不是孤立的。数学直觉告诉你损失函数长什么样系统编程告诉你数据怎么高效流转模型实现帮你验证理论是否真的跑得通表达输出则强迫你把所有模糊的地方逼到死角。任何一环缺失都会在某个阶段卡住。所以这篇文章不讲“如何成为 Karpathy”那没必要。我想拆解的是这套技能组合背后的底层逻辑以及普通人怎么照着练。无论你是刚入门的学生、写业务代码的工程师还是已经在训练大模型的算法岗这套方法都能直接落在日常工作上。2. 从 micrograd 到 nanoGPT最小实现是最好的学习脚手架2.1 micrograd手写自动微分的三个认知拐点micrograd 大概只有 200 多行 Python却是一个完整的自动微分引擎。第一次跑它的时候我最大的震撼不是它有多强而是它把反向传播拆得那么干净。第一个认知拐点是理解Value这个类。每一个节点保存三样东西自身的数值、梯度、以及反向传播函数。前向计算构建起一张计算图反向传播则按照拓扑序把梯度一棒一棒传回去。Karpathy 用“数值 梯度 backward”三个字段就把整个反向传播浓缩了这种抽象能力比任何论文都直观。第二个拐点是手动实现一个乘法节点的 backward。你会突然意识到链式法则不是什么神秘公式它就是一个局部求导和乘法累加过程。每经过一个操作符梯度就被拆解并传递到它的输入上。自己手写一遍exp和log的 backward 之后以后再看到 PyTorch 里loss.backward()心里就有了画面。第三个拐点是用 micrograd 训练一个两层网络。数据、权重、前向、损失、反向、更新这六步构成了深度学习的全部骨架。就算你后面用 PyTorch、TensorFlow、JAX这个骨架都不会变。很多人学了一堆花活却连最小闭环都讲不清问题就出在没走过一遍最原始的实现。我的实操建议是不要只看把代码抄一遍然后做三件事。第一给Value增加一个__repr__方法打印出完整的计算图结构第二换一个损失函数比如从 MSE 换成交叉熵观察梯度变化第三把学习率调到极大或极小手动制造一次训练发散再顺着梯度找原因。这三件事做完反向传播的直觉基本就建立了。2.2 nanoGPT 与 GPT-2 复现把论文变成可运行代码如果说 micrograd 是玩具那 nanoGPT 就是入门大模型训练的最佳过渡品。它并不是一个生产级框架却能以很短的代码量复现 GPT-2 级别的训练流程。我第一次读 nanoGPT 的源码时最大的感受是“原来 GPT 就这”。整个项目按文件拆得非常清楚model.py里是 Transformer 结构train.py里是训练循环sample.py里是推理采样。Karpathy 没有把代码写成一坨而是按职责拆开这本身就是一种工程教学。关键步骤有三个。第一数据侧要理解 BPE字节对编码分词。GPT 的输入本质上是 token 序列不是字符序列也不是直接塞单词。BPE 把文本拆成子词单元这个环节看着不起眼但它是大模型所有行为的地基。第二训练循环要理解“采样一个 batch - 前向算 loss - 反向传播 - 更新参数”的循环关系尤其是context length和batch size怎么影响显存和训练稳定性。第三采样阶段要理解 temperature 和 top-k 的作用它们决定了生成文本的随机性和多样性。读完代码之后我强烈建议自己动手改一遍把n_layer从 12 改成 2把n_embd从 768 改成 64先在小规模上复现训练。很多人一上来就想复现完整 GPT-2结果显存不够、训练时间太长最后放弃。正确的姿势是先让一个极小的模型在莎士比亚全集上过拟合观察 loss 从 10 往下降的曲线然后再往上加规模。2.3 我复刻这套技能时的实操清单我整理了一份复刻 Karpathy 开源项目的实操清单按顺序做能少踩很多坑。下载代码后先看README理解项目目标和运行方式不要急着跑。用默认配置跑通一次。如果跑挂了优先排查 Python 版本、CUDA 版本和依赖库版本。跑通后先打印模型参数量、batch 的 shape、loss 的初始值确认数据流是通的。把模型尺度缩小到一个“能在一分钟之内完成训练”的规模再做参数实验。加上检查点保存逻辑每 N 步保存一次权重防止训练中断白跑。用日志记录每一步的 loss 和 learning rate训练结束画 loss 曲线。最后再尝试改模型结构比如把注意力头数翻倍观察训练行为的变化。这套清单现在也是我带团队做模型实验时的基础流程。它不解决所有问题但能保证你不会在最基本的环节浪费大量时间。3. 深度学习与大模型的硬核技能Karpathy 方法论拆解3.1 从 CS231n 到 Zero to Hero为什么始终强调从零写CS231n 课程当年最大的特点是作业设计得好。每个作业都故意留出“让你自己实现”的部分比如手写反向传播、实现 batch normalization 的前向和反向、训练一个小型图像分类器。Karpathy 后来做的 Zero to Hero 视频系列延续了同样的思路micrograd 从零实现自动微分makemore 从零实现字符级语言模型nanoGPT 从零实现 Transformer。这种“从零写”的方法论核心价值不在于你的代码比框架更高效而在于它能暴露所有模糊地带。用现成框架时你只需要调用nn.Linear但框架替你完成的初始化策略、权重绑定的细节、梯度流的连接方式你都没看到。一旦模型不收敛你连排查方向都没有。BatchNorm 是我见过最典型的例子。看论文公式大多数人觉得 BN 就是“减均值除方差”。但真的自己实现时你会发现训练阶段和推理阶段的行为完全不同训练时用 batch 统计量推理时用滑动平均统计量。如果理解不到这一层模型在训练集上表现很好、一到测试集就崩你都不知道该从哪里查起。所以我的建议是无论你是不是科班出身至少完整实现一次线性层、ReLU、交叉熵损失、反向传播。这四个组件是一切的基石。实现完再去看大模型你会觉得很多原本神秘的设计其实都是围绕梯度流和训练稳定性展开的。3.2 Token、分词、训练循环大模型里容易被忽略的环节很多人一谈大模型就盯着 Transformer 结构注意力机制背得滚瓜烂熟但一到实际训练就发现问题几乎全出在数据和训练配置上。分词是最典型的隐蔽环节。BPE 算法会把训练语料里所有字节对做频率统计然后不断合并最常见的对最终形成一个词表。这个过程中有大量细节会直接影响模型效果词表大小选太大会让嵌入层参数爆炸选太小又会让文本被拆得稀碎。Karpathy 曾多次提到分词问题是 LLM 领域最容易被忽视的 bug 源头之一。一个很简单的验证方法是单独写一个脚本把一个长句子切分成 token然后还原成原始文本看看是否有信息丢失。很多诡异模型行为比如对某些拼写错误极度敏感根子就在分词。训练循环的设计同样关键。学习率调度、梯度裁剪、warmup 步数、权重衰减这些超参数单独看都不难理解但组合在一起模型行为会非常复杂。我见过不少人在 NanoGPT 上训练loss 一开始不降第一反应是改网络结构但真正的解决方法是把学习率从3e-4降到1e-4同时把 batch size 调大。还有一个很多人忽略的指标初始 loss。如果你在训练开始前用随机初始化的权重跑一个 batch打印出 loss这个值大概在log(vocab_size)左右。如果差太多说明数据加载或者模型输出维度有问题。这个检查只需一分钟却能省下半天排查时间。3.3 模型评估与“可靠性”哲学Karpathy 反复强调的软技能Karpathy 在很多场合提到过软件工程中的可靠性问题。他的观点很直接一个大模型系统能不能上线不只是看指标刷得多高更要看它在真实场景里可不可靠。这种可靠性来自哪里来自对全链路的掌控。我自己的理解是可靠性是一种“带着怀疑去验证”的态度。每一行代码、每一个数据管线、每一个模型输出都假设它有 bug然后通过实验去证明它没有问题。比如训练一个新模型第一件事不是看 loss 曲线有多漂亮而是先做一个过拟合测试拿一小部分数据反复训练看模型能不能把训练集的 loss 降到很低。如果不能说明模型容量或代码实现有问题而不是数据不够。评估阶段也一样。只看整体准确率是不够的还要按类别、按文本长度、按输入难度做分层评估。Karpathy 在做项目时特别喜欢用“样本级别的人工检查”因为很多问题在平均指标里会被掩盖。我自己现在也保留这个习惯每次训练完一个模型不看测试集指标先随机抽 50 条输出人工看一遍。这个习惯帮我抓出了大量端到端评估发现不了的问题比如重复生成、格式错乱、语料污染。4. 学习方法论“教是最好的学”背后的系统设计4.1 博客、视频、课程把一次学习变成多种产出很多人觉得写作和录视频是“额外的负担”但从 Karpathy 的产出方式来看表达从来不是浪费时间而是学习过程的一部分。他写一篇博客、录一集视频背后一定有一个完整的项目作为支撑。项目在前表达在后表达只是把项目经验结构化。我自己实践过之后发现这个工作流的效率高到惊人。第一次学一个新模型时我通常会做四件事自己实现一遍、写一段笔记、做一个展示用的 notebook、录一段讲解视频。同一个知识点经过这四轮加工之后记忆深度远超单纯读论文。因为每一轮都在强迫你换一个角度审视它实现逼你处理细节写作逼你理清逻辑展示逼你提炼重点讲解逼你应对问题。而且这些产出不是一次性消耗品。博客可以变成面试时的作品集notebook 可以变成团队内部分享材料视频可以留给自己三个月后复习。你花在学习上的时间没有被“用掉”而是被转化成了可复用的资产。我的建议是不要追求一次就产出一个完美作品。先写一版粗糙的笔记跑一个能用的 demo再逐步细化。完成比完美重要。4.2 Hacker 式学习方法逆向工程与“从外到内”阅读代码Karpathy 读代码的方式很接近黑客逆向工程的方式先跑起来再往里钻。读一个大型框架时如果从头到尾按顺序读很容易在抽象层迷路。更好的顺序是先通过调用接口感受行为再通过断点或调试器看内部状态最后才去读核心实现。我读 PyTorch 源码的体会是不要从Tensor这个类开始而是从一个具体操作开始比如torch.matmul。先写一个小的矩阵乘法打断点进去观察它怎么分发到不同后端。你会看到 CPU 和 GPU 走的是完全不同的路径。这种由外到内的阅读方式效率远高于从一个抽象类看到另一个抽象类。Karpathy 在项目里也频繁用这个方法先用.random 初始化模型跑通再逐渐替换成预训练权重先看输出是否合理再检查内部激活值。他做 nanoGPT 的时候就是把 GPT-2 当作“答案”用一个小实现去逼近它的行为。对初学者我建议从“仿写 对比”开始。拿一个成熟开源项目先复制它的核心逻辑再改成自己的实验设置然后观察差异。碰到报错就沿着调用栈一层层往上追。这个过程一开始慢但积累到某个节点你会发现自己能直接预测代码的行为。4.3 习惯、节奏与精力管理长期积累的技术人修养Karpathy 的产出密度很高但这背后不是超人式的工作强度而是一套稳定的节奏。我观察了大量长期高产的技术人之后发现他们都有一个共同点不靠灵感靠系统。具体来说就是把大目标拆成每天能推进的单元。比如“学会 GPT 实现”这个目标对一个成年人来说太模糊。拆开之后就是第一天实现注意力机制第二天实现多头第三天写训练循环。每完成一天的任务就有一次正向反馈这种反馈是坚持下去的最大动力。另一个关键习惯是“保护深度工作时间”。写代码、读源码、调试模型这些任务最怕被打断。我自己的做法是每天留出两到三个小时关掉即时通讯工具只保留一个终端和一个编辑器。哪怕当天只推进了一个小模块也比碎片化忙十个小时强。还有一个容易被人忽视的点人的记忆是靠项目锚定的。你读过一百篇论文不如亲手实现过一篇论文的代码。所以不要用“看很多资料”来替代“做一个小项目”。5. 常见问题与避坑指南我在学习这些 skills 时踩过的坑5.1 问题一代码跑通了但什么都不懂这是最常见的困惑。跑通了 nanoGPT 的训练脚本loss 也在下降但让你解释每一行代码在干什么你却说不出来。原因很简单你是在“运行”代码不是在“理解”代码。解决方法是给代码做注释翻译。不是翻译语法而是翻译计算意图。比如看到logits logits.view(B, T, -1)你要问自己这里的 B 和 T 分别代表什么为什么要把维度合并后续的交叉熵是如何在这个形状上计算。每行代码都要能回答“它在算什么、为什么这么算、算完之后形状是什么”这三个问题。我还有一个土办法把训练脚本里的每一行打印语句都打开观察中间变量的数值。看到 attention 矩阵的分布、残差连接之后的梯度范数很多玄学问题瞬间就变成直观现象了。5.2 问题二陷在“看教程”的舒适区收藏了一堆视频和博客收藏夹越来越满自己的能力却没有明显提升。这是另一种坑。看教程是输入型活动做项目是输出型活动。人类天生偏好低风险的输入因为看视频不会出错、不会卡住。但技能的提升恰恰发生在卡住之后。我第一次写 nanoGPT 的时候光是让注意力矩阵的 shape 正确就花了一个晚上。那个晚上之后我彻底理解了B, T, C三个维度是如何参与矩阵乘法的。解决办法是强制自己产出。观看任何技术内容之后48 小时内必须写一段总结或者做一个最小实验。如果做不到就不要打开下一个视频。这个规则看起来简单粗暴但真的有效。5.3 问题三大模型太大跑不动怎么办很多人在学习 Transformer 时遇到的直接阻碍是复现一个 GPT-2 需要几十 GB 显存自己的显卡只有几 GB。于是干脆放弃实践只看书。其实这是一个可以轻松绕过的障碍。NanoGPT 在 CPU 上也能训练一个很小的字符级语言模型只是慢一点。关键是训练一个“玩具级”模型也能暴露很多原理比如 attention 是怎么在短序列上工作的学习率设太大为什么会让 loss 发散权重初始化对训练的影响有多大。如果你连玩具级训练都不想等那就先用前向传播观察计算图手动构造一个 batch看看 logits 的形状和 loss 的数值。这个过程不需要 GPU也不需要复杂环境只要有一个 Python 解释器就行。5.4 常见问题速查现象可能原因排查方法初始 loss 异常大输出层维度不对或数据加载错打印 token 数量与随机初始化 loss 对比loss 下降很慢学习率过低或数据没有归一化先跑一个 batch 过拟合测试loss 不降反升学习率太高或梯度爆炸降低学习率增加梯度裁剪训练集 loss 低但生成文本乱码分词还原有问题单独测试 tokenizer 的重建能力相同代码两次结果不同缺少随机种子固定 seed对比训练曲线6. 把这些技能迁移到我的日常工作6.1 研究型技能迁移到业务项目的路径很多人以为 Karpathy 的那套东西只适合学术或大模型研发离自己很远。其实不是。最小实现、数据排查、可观测性、版本管理这些技能在任何代码项目里都通用。我在做业务模型时最常用到的是“最小实现验证”这个方法。遇到一个新的推荐模型或者一个新的排序算法我不会立刻套用开源代码而是先写一个极简版本用自己的数据跑通。这个极简版本不追求 SOTA只求能验证核心逻辑。一旦它跑通再去替换组件、增加复杂度问题就能被一步步隔离。数据排查的技能也特别重要。Karpathy 处理数据的那种“先怀疑数据”的态度放在业务场景里就是模型效果差先别急着改模型先看看训练数据是不是有脏标签、特征是不是有空值、样本分布是不是和线上不一致。我统计过团队里 60% 以上的模型问题最终根因都在数据侧。6.2 表达输出对职业成长的正反馈循环表达能力在技术圈的长期价值被严重低估了。Karpathy 的例子只是极端案例但哪怕你只是做到“团队里最能把技术讲清楚的人”职业发展都会明显加速。我现在的习惯是每做完一个重要技术模块就写一份技术文档内容包括目标、方案、关键代码、遇到的坑、最终效果。这份文档对内可以作为团队知识库对外可以作为个人博客素材。坚持半年之后你会发现自己对技术方案的思考深度远超那些只写代码不总结的同事。还有一个很实用的技巧给代码写 README 和注释时想象读者是三个月后的自己。那个“未来的自己”已经忘掉了所有当时的上下文只有你能通过注释帮他快速恢复记忆。这是一个工作量不大但复利极高的习惯。6.3 从 Karpathy 身上我学到最重要的一件事如果要我从这套技能体系中提炼出一个最核心的底层能力我的答案不是编程不是数学而是“自己动手解决问题的闭环能力”。遇到一个不懂的东西不要等别人喂给你不要等教程更新直接打开一个 Python 终端或者翻开一个开源项目动手试。报错了就查查不到就猜猜完了就验证。这个“试错 - 反馈 - 修正”的循环才是技能增长的真正驱动力。Karpathy 的所有项目、所有教程、所有产出本质上都是这个循环的外化。根据我个人的经验这套方法最难得地方不是开头而是在你已经“会了一些”之后还愿意继续从最小实现做起。保持这个习惯你会在某个时刻突然发现面对一个全新模型时你不再害怕了。因为你知道哪怕现在不会你也能用它两三天时间自己把它弄明白。这个信心比任何具体知识都值钱。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进