纯 C 语言环境下训练大型语言模型?
使用简单、原始的 C/CUDA 代码训练大型语言模型(LLM)
作者在推上介绍llm.c
你是否曾经想过在纯 C 语言环境下训练大型语言模型 (LLM),而无需使用 245MB 的 PyTorch 和 107MB 的 cPython?没有?好吧,现在你可以了!有了 llm.c:
https://github.com/karpathy/llm.c
首先,它实现了在 CPU/fp32 上的 GPT-2 训练,代码简洁,仅约 1,000 行。它可以立即编译和运行,并且与 PyTorch 参考实现完全匹配。
我选择 GPT-2 作为起点,因为它可以说是大型语言模型的鼻祖,是 LLM 技术栈第一次以现代形式组合在一起,并且模型权重可用。
作者简介
Andrej Karpathy 是一位在人工智能领域具有深远影响的研究科学家和教育者。他的职业生涯跨越了多个重要的机构和项目,他的工作不仅推动了技术的发展,也启发了一代又一代的学者和从业者。
从2017年到2022年,Karpathy担任特斯拉(Tesla)的高级人工智能总监,领导了特斯拉Autopilot的计算机视觉团队。他的团队负责内部数据标注、神经网络训练、实现技术的科学原理以及在定制的推理芯片上进行生产部署。他们的最终目标是为特斯拉不断增长的车队开发并部署全自动驾驶技术。
在加入特斯拉之前,Karpathy是OpenAI的创始成员和研究科学家,参与了该组织的早期工作。他的博士研究集中在卷积和循环神经网络及其在计算机视觉和自然语言处理中的应用。在斯坦福大学,他与Fei-Fei Li教授合作,并有幸与Daphne Koller、Andrew Ng、Sebastian Thrun和Vladlen Koltun等杰出学者共事。
Karpathy在教育方面的贡献同样卓越,他是斯坦福大学首个深度学习课程CS 231n:卷积神经网络与视觉识别的主要设计者和讲师。这门课程迅速成为斯坦福大学最受欢迎的课程之一,学生人数从2015年的150人增长到2017年的750人。
除了他的正式职务,Karpathy还开发了一些个人项目,如micrograd,这是一个小型的标量值自动微分引擎,实现了具有PyTorch类似API的反向传播和小型神经网络库。他还创建了ConvNetJS,这是一个完全用JavaScript编写的深度学习库,使得在浏览器中训练卷积神经网络成为可能。
Karpathy还关注生产力跟踪,他开发了ulogme,这是一个注重隐私的个人生产力跟踪器。此外,他还构建了许多其他有趣的项目,如Rubik’s cube color extractor、predator-prey神经进化多智能体模拟、sketcher bots、计算机游戏竞赛的游戏、Tetris AI和多人合作俄罗斯方块等。
在学术出版物方面,Karpathy在顶级会议上发表了多篇论文,包括ICLR、CVPR、ICRA和SIGGRAPH等,这些工作展示了他在深度学习和人工智能领域的深厚造诣。
总的来说,Andrej Karpathy是一个在学术界和工业界都有着杰出贡献的科学家,他的工作不仅推动了人工智能技术的发展,也为后来者提供了丰富的学习资源和灵感。
llm.c 简介
简洁至上的编程哲学
llm.c项目的核心理念是“简洁”。项目作者通过约1000行的C代码,展示了如何在不依赖庞大框架如PyTorch或cPython的情况下,实现GPT-2模型的训练。这种方法不仅降低了入门门槛,也使得代码更加透明和易于理解。开发者可以直观地看到模型的每一部分是如何工作的,从而更深入地理解模型的内部机制。
高效的CUDA加速
除了C语言的简洁性,llm.c项目还利用了CUDA技术来加速模型的训练。通过直接在GPU上执行并行计算,项目大大提高了训练效率。这种直接的CUDA实现有望在性能上接近甚至超越PyTorch这样的成熟框架,同时保持了代码的简洁性。
易于上手的快速开始
项目的README文件提供了清晰的快速开始指南。开发者首先需要下载并处理数据集,然后使用预训练的GPT-2权重进行微调。项目提供了详细的编译和运行指令,使得即使是没有太多C/CUDA经验的开发者也能够轻松上手。
持续优化与未来发展
目前,llm.c项目正在持续优化中。作者计划通过SIMD指令进一步加速CPU版本的训练,并探索更多现代化的架构,如Llama2和Gemma等。这些优化将使得项目在未来更加强大和灵活。
快速实战
下面是在我的笔记本上的实战记录,
硬件信息
MacBook Pro M2 Max (12核CPU,38核GPU)
数据准备
下载并tokenize(分词化)一个数据集,tinyshakespeare[1]
python prepro_tinyshakespeare.py输出,
Saved 32768 tokens to data/tiny_shakespeare_val.bin
Saved 305260 tokens to data/tiny_shakespeare_train.bin这些 .bin 文件是原始的 int32 整数字节流,表示使用 GPT-2 分词器的标记 ID。或者,您也可以使用 prepro_tinystories.py 分词 TinyStories[2] 数据集。
原则上,我们已经准备好在这里训练模型了。然而,基线 CPU/单精度浮点数(fp32)参考代码效率如此低下,以至于从头开始训练这些模型还不切实际。于是,我们使用 OpenAI 发布的 GPT-2 权重进行初始化,只进行微调。为此,我们必须下载 GPT-2 权重并将它们保存为我们可以在 C 语言中加载的检查点:
python train_gpt2.py你会认出这段代码是从 nanoGPT 来的,它是 PyTorch 中一个简单的 GPT-2 参考实现。这个脚本将下载 GPT-2(124M)模型,对单个数据批次进行 10 次迭代的过拟合,运行几步生成操作,最重要的是,它将保存两个文件:
1.
gpt2_124M.bin文件,包含用于在 C 语言中加载的原始模型权重2.
gpt2_124M_debug_state.bin,它还包含更多的调试状态:输入、目标、逻辑值和损失。这对于调试 C 代码、进行单元测试以及确保我们完全匹配 PyTorch 参考实现非常有用。
目前,我们只关心 gpt2_124M.bin 中的模型权重。现在我们可以用它们进行初始化,并用原始的 C 语言进行训练。首先编译代码:
make train_gpt2你可以查看 Makefile 文件及其注释。它会尝试自动检测你的系统上是否有 OpenMP 可用,这对于在代码复杂度非常低的情况下加速代码非常有帮助。一旦 train_gpt2 编译完成,你可以运行它:
OMP_NUM_THREADS=8 ./train_gpt2您应该根据 CPU 的核心数量调整线程数。程序将加载模型权重和标记,它将使用 Adam lr(学习率)1e-4 运行几个迭代的微调循环,然后从模型中生成一个样本。我认为该文件非常易读,您应该看看。简单来说,所有层的前向和后向传递都有实现,它们被串联成一个大型的手动前向/后向/更新循环。输出在我的 MacBook Pro(Apple Silicon M2 Max,没有作者的好)上如下所示:
[GPT-2]
max_seq_len: 1024
vocab_size: 50257
num_layers: 12
num_heads: 12
channels: 768
num_parameters: 124439808
train dataset num_batches: 1192
val dataset num_batches: 128
num_activations: 73323776
val loss 5.252019
step 0: train loss 5.356184 (took 2028.821000 ms)
step 1: train loss 4.301032 (took 1842.393000 ms)
step 2: train loss 4.623316 (took 1891.935000 ms)
step 3: train loss 4.600415 (took 1814.071000 ms)
... (trunctated) ...
step 39: train loss 3.970822 (took 1902.594000 ms)
val loss 4.107841
generated: 50256 16773 18133 31656 11 198 14150 257 28774 11 290 198 14108 3656 318 284 1944 11906 2802 287 198 8071 1313 2890 393 3281 284 262 44509 25 198 72 13 68 1539 35831 298 284 5938 15024 2918 198 1169 44790 286 2011 944 13 198 72 13 68 1539 711 262 662 7785 295 25 26412 319 198 1820 2300
step 40: train loss 4.377796 (took 1847.367000 ms)验证
生成的只是 token ID,我们必须将其解码回文本。我们也可以很容易地在 C 中实现这一点,因为解码非常简单,它只是字符串块查找和打印。现在我们可以使用 tiktoken:
import tiktoken
enc = tiktoken.get_encoding("gpt2")
print(enc.decode(list(map(int, "50256 16773 18162 21986 11 198 13681 263 23875 198 3152 262 11773 2910 198 1169 6002 6386 2583 286 262 11858 198 20424 428 3135 7596 995 3675 13 198 40 481 407 736 17903 11 329 703 6029 706 4082 198 42826 1028 1128 633 263 11 198 10594 407 198 2704 454 680 1028 262 1027 28860 286 198 3237 323".split()))))执行上面代码得到,
<|endoftext|>Come Running Away,
Greater conquer
With the Imperial blood
the heaviest host of the gods
into this wondrous world beyond.
I will not back thee, for how sweet after birth
Netflix against repounder,
will not
flourish against the earlocks of
Allay碰到的问题及解决办法
数据下载可能需要魔法
huggingface.co的可以考虑使用国内镜像hf-mirror.com
我的clang不支持OpenMP
make train_gpt2
# 返回
Makefile:27: OOPS Compiling without OpenMP support
clang -O3 -Ofast train_gpt2.c -lm -o train_gpt2• 安装OpenMP支持库
brew install libomp• 测试 依然不行, 查了网上得知xcode自带的clang版本太低(刚刚更新系统到最新版本,包括了xcode更新),
• 安装最新版llvm
brew install llvm输出
To use the bundled libc++ please add the following LDFLAGS:
LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++"llvm is keg-only, which means it was not symlinked into /opt/homebrew,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have llvm first in your PATH, run:
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
For compilers to find llvm you may need to set:
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
• 使用最新安装的llvm版本的clang
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
if echo | clang -fopenmp -x c -E - > /dev/null 2>&1; then
echo "OpenMP is supported by clang"
else
echo "OpenMP is not supported by clang"
fi
返回
OpenMP is supported by clang然后就可以训练了,
make train_gpt2
返回
NICE Compiling with OpenMP support
clang -O3 -Ofast -Xclang -fopenmp -DOMP -I/opt/homebrew/opt/libomp/include -L/opt/homebrew/opt/libomp/lib train_gpt2.c -lm -lomp -o train_gpt2结语
llm.c项目是一次对传统机器学习框架的挑战,它展示了如何用简洁的代码实现复杂的功能。这种编程哲学不仅使得代码更易于维护和理解,也为机器学习社区提供了新的视角和可能性。随着项目的不断发展和完善,我们期待看到更多创新的实现和应用。
引用链接
[1] tinyshakespeare: https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt[2] TinyStories: https://huggingface.co/datasets/roneneldan/TinyStories