做AI这行的人,可以不知道很多网红博主,但很难绕开Andrej Karpathy这个名字。他早年做过OpenAI的创始成员,又跑去特斯拉带了几年Autopilot视觉团队,2023年前后短暂去了Anthropic,后来又回到OpenAI,2024年干脆自己拉了个Eureka Labs专门做AI教育。很多人提起他总是聊履历,但真正让他对普通工程师产生巨大影响的,其实是他的公开项目和教学视频。我花了两三周时间,把他的micrograd、nanoGPT、minbpe、llm.c,还有Zero to Hero那一整套视频全过了一遍,最大的感受是:andrej-karpathy-skills这套东西,与其说是AI大牛的神仙操作,不如说是一套可以复制的学习方法和工程习惯。这篇文章就是想把这些能直接抄作业的部分,全部拆开讲清楚。
1. Karpathy到底强在哪里:先拆解他的技术能力版图
1.1 从物理背景到AI大牛的路径
看Karpathy的资料很容易产生一种错觉:这人本来就是个天才。但仔细看他走的路线,其实有很多普通工程师也能借鉴的地方。他本科读的是物理和计算机双学位,后来去斯坦福读博时才彻底转向深度学习,师从李飞飞,做的是图像描述和视觉语言结合的方向。那个阶段他写了大家后来很熟悉的CS231n课程,也因为这门课第一次在公众视野里建立起了“能把深度学习讲明白”的口碑。
这段经历非常关键。很多人以为大牛都是算法理论一路读到顶,但Karpathy的底子其实是“物理思维+工程动手+教学输出”三条线同步推进。物理训练给他的是第一性原理的拆解习惯,什么问题都要从最底层的机制开始推演;工程动手能力让他在特斯拉那种量产车环境里能搞定数据管道、模型部署、场景验证这些脏活累活;而CS231n这种公开课则强迫他不断把一个复杂系统讲给零基础的人听。这三样东西堆在一起,才形成了他后来那套独特的方法论。
1.2 硬核工程与教学能力为什么能共存
很多技术能力强的人并不会讲课,很多会讲课的人又没有深度。Karpathy比较特殊的地方在于,他的教学能力不是那种“把PPT念一遍”的表面功夫,而是建立在亲手重新实现系统的基础上。他讲反向传播,就手写一个micrograd;他讲分词器,就手写一个minbpe;他讲大模型训练流程,就手写一个nanoGPT;前几年他还觉得不够彻底,直接用纯C写了llm.c,把GPT-2级别的模型从零训练跑通。
这意味着他讲的每个概念,他都亲自用最原始的方式实现过一遍。对听众来说,这就有个巨大的好处:你听到的不是二手转述,而是从代码边界、数据流、梯度传播这种具体细节里长出来的解释。所以他的课程和文章能一直保持“别人讲不清楚的地方他能讲清楚”的水准。
我自己在学习和复盘时有个习惯:如果一个人只在方法论层面讲得天花乱坠,但拿不出一个可以运行的最小实现,那他的技能树基本要打个问号。Karpathy恰好是反例,他的几乎所有公开项目都能在一台普通电脑上直接跑通,这本身就是最好的背书。
1.3 把他公开的东西放在一起看,是一棵完整的技能树
如果只盯着某一个项目看,很容易觉得Karpathy只是在做教学玩具。但把micrograd、nanoGPT、minbpe、llm.c、Zero to Hero视频、Eureka Labs这串东西放在一起,你就会看到一条完整的路径:
- micrograd解决的是“深度学习训练时梯度从哪来”的问题;
- nanoGPT解决的是“一套最简GPT训练脚本该长什么样”的问题;
- minbpe解决的是“数据怎么变成token序列”的问题;
- llm.c解决的是“如果不依赖框架,模型底层又是怎么运行的”问题;
- Zero to Hero则把所有环节串成了一条教学路线。
这其实就是一个工程师从0到1构建大模型完整认知所需要的地图。很多人学习深度学习跳来跳去,今天看Transformer论文,明天调几个API,后天又去读优化器源码,最后知识是碎片的。Karpathy的价值在于,他帮你把这条路踏平了,你按他的顺序走,就能从基础数学一步步走到能自己训模型的位置。这也是andrej-karpathy-skills最值得深挖的地方:它不是某一项技能的胜利,而是一整套系统化学习路径的胜利。
2. 最能体现"Karpathy式"学习法的核心项目
2.1 micrograd:一百多行代码讲透反向传播
micrograd是我建议所有人都应该至少手敲一遍的项目。它的核心是一个叫Value的类,总共一百多行代码,外加一百多行测试。Value类里保存着数据、梯度、参与运算的子节点,以及一个反向传播函数。
举个例子,它的加法实现是这样的:
class Value: def __init__(self, data, _children=(), _op=''): self.data = data self.grad = 0 self._backward = lambda: None self._prev = set(_children) self._op = _op def __add__(self, other): out = Value(self.data + other.data, (self, other), '+') def _backward(): self.grad += 1.0 * out.grad other.grad += 1.0 * out.grad out._backward = _backward return out你看到的就是这个加法节点自己定义了“反向传播时要怎么把梯度分给两个输入”。乘法和tanh也完全类似。当你有几十个这样的基本运算节点连成一张计算图后,反向传播就变成了一次从输出到输入的拓扑排序。你不需要任何框架,就能看到一个标量模型的梯度是怎么算出来的。
我当时把这段代码敲完,最深的体会是:之前用PyTorch的backward()总觉得像魔法,敲完micrograd之后才明白,所谓自动求导,其实是每个算子自己知道怎么对自己局部做链式法则,框架只是帮你把这些局部动作按顺序串起来。这个认知价值极高,因为它减少了你对框架的迷信,也让你在后面读nanoGPT的时候不会被各种自动微分细节绊住。
2.2 nanoGPT:最简GPT训练脚本
如果说micrograd是理解梯度的入口,那nanoGPT就是理解大模型训练闭环的入口。这个项目去掉注释不到1500行,但数据准备、分词、模型定义、训练循环、采样生成全都有。模型结构就是标准decoder-only Transformer,里面有Token Embedding、位置编码、多头因果自注意力、前馈网络、层归一化,一个不少。
它的价值不在于性能。你千万别指望它能在普通显卡上训练出ChatGPT级别的模型。它的价值在于让你看到一个大语言模型从数据到损失的完整路径。我当时拿它跑莎士比亚数据集,在一个消费级显卡上训了一个很小的模型,几分钟就能看到loss下降,然后采样生成出看起来有点像原文本的句子。那种感觉比读十篇论文都来得直观。
这里有个很容易被忽略的小细节:nanoGPT的配置里有block_size、n_embd、n_layer、n_head、dropout等一系列超参数。很多人只是照抄默认配置,但如果你把它全部调小到一个极小规模,然后观察loss变化,你能真正理解“这个参数到底在控制什么”。比如减小n_embd会让模型变得笨,增大n_layer在数据不够时容易过拟合。这种体感是只有亲手调过才有的。
2.3 minbpe:从零手写BPE分词器
分词器是很多初学者最容易跳过的一块,但Karpathy专门花了两期视频讲这个问题,还写了minbpe这个项目。他实现了两个版本:一个基础版,把BPE的训练和编码逻辑用最直白的方式写出来;一个正则版,用正则表达式先把文本按单词边界切开,更接近GPT系列实际使用的策略。
我一开始也觉得分词器有什么好学的,不就合并高频字节对吗?但真正动手实现之后才发现,里面有很多精妙细节,比如词表大小怎么定、pecial token怎么处理、Unicode字符如何用UTF-8编码拆成字节再合并、训练时如何统计相邻pair频率。这些细节直接决定了一个模型能吃进什么样的文本分布。
举个例子,如果你不知道BPE是怎么把“hello world”变成一组token id的,你在处理长文本、设置max_length、调上下文窗口时就会经常犯迷糊。Karpathy用一句话点醒过我:“如果你不理解分词器,你就不理解为什么模型会在某些拼写上表现得很奇怪。”所以建议你花一个下午把这个项目敲一遍,后面看大模型相关的任何东西都会顺畅很多。
2.4 llm.c:用纯C语言重新训练大模型
llm.c这个项目可能对大多数人来说偏硬核了一点,但它是Karpathy工程能力的最好证明。他用纯C语言、直接操作GPU内存的方式,不依赖PyTorch或其他深度学习框架,把GPT-2的训练流程完整实现了一遍。项目里甚至能看到他自己写的CUDA kernel、矩阵乘法优化、内存布局管理。
我第一眼看到这个项目的反应是:这也太疯了。但仔细看下来,它其实是在回答一个很少有人愿意深挖的问题:“当你把框架这层皮剥掉,一个Transformer在硬件层面到底是怎么跑的?”PyTorch替你管理了太多东西:张量内存怎么分配、kernel怎么调度、梯度怎么在设备间同步。llm.c把这些全部暴露出来,你不得不去面对。
当然,我并不是建议每个初学者都去啃llm.c。但如果你的研究方向开始涉及性能优化、推理加速、内核定制,llm.c是市面上少有的高质量参考。它是通往“工程型AI工程师”这条路上非常难得的教材。
| 项目 | 解决问题 | 建议人群 | 上手难度 |
|---|---|---|---|
| micrograd | 自动求导和反向传播原理 | 所有初学者 | 低 |
| nanoGPT | 最简GPT训练闭环 | 想理解大模型训练的工程师 | 中 |
| minbpe | 分词器原理与实现 | 想深入了解数据处理的工程师 | 中 |
| llm.c | 不依赖框架的模型底层实现 | 关注性能与内核细节的工程师 | 高 |
3. 从Karpathy的代码风格里,我抄到的三条作业
3.1 极简主义:先跑通再抽象
Karpathy写项目有个非常鲜明的习惯:代码量被压缩到极限,但逻辑完整,没有任何炫技。他很少用装饰器、元类、复杂继承、设计模式这类东西,几乎全是直白到不行的函数和类。你读他的代码,不需要在抽象层里跳来跳去,跟着主流程走就能看懂。
我在自己项目里也学着做了调整。以前我写训练代码,喜欢一上来就拆一堆模块:dataset.py、model.py、trainer.py、config.py、utils.py,结果核心逻辑被分散得到处都是,改一个参数要翻好几个文件。后来我学nanoGPT的风格,先把一个train.py写成一个完整的、能跑的脚本,主流程全在眼前。等确定核心逻辑没问题了,再按需拆文件。这个习惯帮我省了很多排查时间,也让我更愿意直接面对核心代码而不是逃避到抽象层里。
3.2 第一性原理:自己动手造一次轮子
Karpathy在大模型相关课程里反反复复做同一件事:把别人用库实现的东西,自己从零造一遍。别人用PyTorch的optimizer,他自己写一个简易SGD;别人用transformers库加载模型,他直接把GPT类的forward路径写出来;别人用tokenizers库,他手写BPE。
落到你身上,我建议你对待任何你“以为懂了”的库函数都多问一句:如果我自己实现需要多少行代码?比如你以为你懂LayerNorm,那你能否在纯numpy里把它的mean、variance、normalize、scale、shift每一步写出来?我试过,这个练习的收获比读十篇关于LayerNorm的文章都大。因为你会在写的途中发现,你自己对“归一化到底是在哪个维度上做的”这个问题的理解,可能是有偏差的。
3.3 从输出倒逼输入:教学是最好的学习方式
Karpathy之所以能把深度学习讲得这么清楚,有一个很重要的客观原因是:他每次讲一个主题,都被迫要把这个主题的逻辑链条完整梳理一遍。这本质上是通过输出倒逼输入。他在视频里经常说“我不确定这里,我要现场写一下”,然后真的就在黑板上推演或当场写代码验证。这种“不确定就现场查证”的态度,比“装作什么都懂”珍贵得多。
我可以告诉你一个自己的经验。我在学nanoGPT的过程中,尝试给它写一个中文版注释和脑图,为了解释清楚每一行的作用,我不得不反复查Transformer的结构、数据流的形状、mask的实现方式。结果就是,我写完注释之后,对GPT的理解比那些只看过论文的人扎实很多。这就是输出倒逼输入的力量。
4. 想复刻这套技能树,我的建议和踩坑记录
4.1 学习路线上的三个常见误区
第一个误区是只刷视频不敲代码。Karpathy视频讲得好,看的时候你觉得什么都懂了,但关上视频,让你自己实现一个softmax或者写一段自注意力你都可能卡壳。视频只是导航,真正把你送到目的地的是自己动手。我建议你每看完一节课,至少把核心代码手敲一遍。
第二个误区是跳过基础原理直接上大模型。很多人一上来就想搞千亿参数的模型,连反向传播怎么算的都不清楚,连词表大小怎么定都不明白。这种情况下你做再多实验,也只是在套模板。Karpathy自己都把micrograd当成整个课程体系的起点,你凭什么觉得自己可以跳过?
第三个误区是用调参来替代深入理解。我在一些群里见过有人训练小模型失败了,第一反应是把learning rate调低、batch size调大,但说不清为什么这么做。这样就算偶尔成功了,下次换个任务照样不会。正确做法是遇到问题先回到原理上分析一下:loss是NaN了吗?梯度是爆炸还是消失?数据预处理是不是有问题?Karpathy在视频里也会跑偏,但他会停下来找原因,这就是值得学习的地方。
4.2 实操时几个很重要的注意点
环境版本不要乱动。我试过在跑nanoGPT时用它要求的PyTorch版本跑别的项目,结果CPU和GPU上行为不一致,损失函数曲线直接飘了。建议单独为教学项目建一个新的虚拟环境,固定住核心依赖版本,别和其他环境混在一起。
看源码尽量先读测试代码。Karpathy写项目时会配不少测试,很多人直接忽略。其实测试代码是在告诉你,这个模块在什么输入下应该输出什么结果,比正文注释更容易理解。我每次新开一个仓库,都会先看test目录,这已经成了节省时间的习惯。
不要追求完全复现数据。Karpathy在视频里用的数据集、超参数、随机种子,在你自己机器上大概率不可能得到一模一样的结果。你只要验证“训练loss是在下降的”“采样出来的文本是有意义的”,这就说明代码逻辑走通了。非要抠每个数字完全一致,只会浪费大量时间。
4.3 从模仿到独立项目,分三步走
第一步是照葫芦画瓢。下载源码,读懂每一行,改一点点参数,跑通。这个阶段的目标是把“知道”转成“会跑”。
第二步是默写。不打开源码,凭记忆把核心功能自己实现出来。比如你学完nanoGPT之后,尝试自己写一个只保留必要组件的最小Transformer。如果卡住了,回去看源码,看自己漏掉了什么。这个过程会迅速暴露你的理解盲区。
第三步是迁移创新。在默写能通过的基础上,换一个自己的数据集,或者给模型加一个小功能,比如增加一种注意力变体、改一下训练策略。这个阶段才是真正把Karpathy的技能变成你自己的技能的关键一步。
我个人在走这套路时的体会是,第三步最容易被忽略,但其实最值得投入。因为只有当你把学到的东西用到自己的场景里,你才算真的完成了知识内化。否则你只是又读了一遍别人的读书笔记。
5. 最后再分享一个我很受用的技巧
如果你打算认真学nanoGPT,我建议你别一开始就一头扎进模型代码。先去看它的数据准备脚本,比如prepare.py,看看它是怎么把一段莎士比亚文本变成一个又一个训练样本的。很多人以为大模型最核心的是Transformer结构,但Karpathy会告诉你,数据处理、tokenization、batch采样这些“脏活”反而是最容易出错也最影响模型效果的地方。
还有一点——你在读Karpathy代码或者看视频的时候,如果哪里没懂,不要刷过去,停在那里,反复看他怎么一步步从问题推到这个解法的。他的视频里有很多“我先写一个最简单的版本,然后发现问题,再改”的过程,这才是真正值钱的部分。那些顺理成章的地方,反而没啥可学的。