返回 动态 学习 CMS 文章

用通俗语言解释 llm.c:将 GPT-2 训练压缩至 1000 行 C 代码

llm.c 用 1000 行 C 代码实现 GPT-2 训练,去除 PyTorch 等抽象,展示 LLM 训练的本质。

llm.cGPT-2C语言PyTorch
成长分 / 100 76 综合收获、行动、留存与影响

为什么值得读理解 LLM 训练底层原理,摆脱高级框架的抽象。

了解 llm.c 项目的动机、权衡和潜在价值。

关键洞察
  1. PyTorch 有 332 万行代码,而 llm.c 仅需约 1000 行 C 代码训练 GPT-2。
  2. llm.c 直接实现 GPT-2 训练,无库依赖,仅依赖汇编。
  3. 放弃灵活性:修改网络需更多专业知识,可能涉及手动推导反向传播。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:4384

用通俗语言解释 llm.c

训练大型语言模型(LLM),如 ChatGPT,涉及大量代码和复杂性。

例如,一个典型的 LLM 训练项目可能会使用 PyTorch 深度学习库。PyTorch 相当复杂,因为它实现了一个非常通用的张量抽象(一种排列和操作保存神经网络参数和激活值的数字数组的方式)、一个非常通用的用于反向传播的 Autograd 引擎(训练神经网络参数的算法),以及大量你可能希望在神经网络中使用的深度学习层。PyTorch 项目有 3,327,184 行代码,分布在 11,449 个文件中。

此外,PyTorch 是用 Python 编写的,而 Python 本身是一种非常高级的语言。你必须运行 Python 解释器将训练代码转换为低级的计算机指令。例如,执行此转换的 cPython 项目有 2,437,955 行代码,分布在 4,306 个文件中。

我正在删除所有这些复杂性,将 LLM 训练简化为其最本质的部分,直接使用一种非常低级的语言(C)与计算机对话,并且没有其他库依赖。在此之下唯一的抽象是汇编代码本身。我认为人们会惊讶地发现,与上述相比,训练像 GPT-2 这样的 LLM 实际上只需要大约 1000 行 C 代码,且在一个文件中。我通过直接在 C 中实现 GPT-2 的神经网络训练算法来实现这种压缩。这很困难,因为你需要详细了解训练算法,能够为所有层推导出反向传播的所有前向和反向传递,并且非常仔细地实现所有数组索引计算,因为你没有 PyTorch 的张量抽象可用。所以这是一件非常脆弱的事情,但一旦你完成了,并通过与 PyTorch 对比验证正确性,你留下的东西就非常简单、小巧,而且我认为相当漂亮。

好的,那么为什么人们不总是这样做呢?

第一:你放弃了大量的灵活性。如果你想改变你的神经网络,在 PyTorch 中你可能只需更改一行代码。在 llm.c 中,更改很可能会涉及更多代码,可能更加困难,并且需要更多专业知识。例如,如果是一个新操作,你可能需要进行一些微积分,并为其编写前向传播和反向传播的反向传递,并确保其在数学上是正确的。

第二:你放弃了速度,至少最初是这样。没有完全免费的午餐——你不应该期望在 1000 行代码中就能获得最先进的速度。PyTorch 在后台做了大量工作以确保神经网络非常高效。不仅所有张量操作都非常小心地调用最高效的 CUDA 内核,而且还有例如 torch.compile,它进一步分析并优化你的神经网络以及它如何在你的计算机上最高效地运行。现在,原则上,llm.c 应该能够调用所有相同的内核并直接执行。但这需要更多的工作和关注,并且就像在(1)中一样,如果你对神经网络或运行计算机的任何内容进行更改,你可能必须调用不同的内核,使用不同的参数,并且可能必须手动进行更多更改。

所以 TLDR:llm.c 是 GPT-2 训练的直接实现。事实证明,这个实现出奇地简短。它不支持其他神经网络,只支持 GPT-2,如果你想对网络进行任何更改,就需要专业知识。幸运的是,所有最先进的 LLM 实际上与 GPT-2 的差异并不大,所以这个限制并不像你想象的那么严格。而且 llm.c 还需要进一步调整和优化,但原则上我认为,对于大多数现代 LLM,它应该能够以比现在多不了多少的代码,几乎匹配(甚至可能超越,因为我们摆脱了所有开销?)PyTorch。

我为什么致力于此?因为有趣。它也具有教育意义,因为那 1000 行非常简单的 C 代码就是全部所需,没有别的。它只是几个数字数组,以及一些对元素进行的简单数学运算,如 + 和 *。而且随着正在进行的一些工作,它甚至可能变得实用。