tinygrad

介于 PyTorchkarpathy/micrograd 之间的项目。由 tiny corp 维护。

[主页](https://github.com/tinygrad/tinygrad) | [文档](https://docs.tinygrad.org/) | [Discord](https://discord.gg/ZjZadyC7PK) tinygrad 是一个端到端的深度学习框架: - **张量库**,支持自动梯度 - **中间表示(IR)和编译器**,用于融合和降低内核 - **即时编译(JIT)+ 图执行** - **神经网络 / 优化器 / 数据集**,用于实际训练 它的灵感来自 PyTorch(人机工程学)、JAX(函数式转换和基于 IR 的自动微分)和 TVM(调度和代码生成),但始终保持小巧且易于修改。 --- ## tinygrad 对比 **PyTorch** - ✅ 相似之处:即时执行的 `Tensor` API、自动梯度、`optim`、基本数据集和层。 - ✅ 可以编写熟悉的训练循环。 - 🔁 与 PyTorch 不同的是,整个编译器和 IR 都是可见且可修改的。 **JAX** - ✅ 基于 IR 的原始操作自动微分(类似于 JAXPR + XLA)。 - ✅ 函数级 JIT(`TinyJit`),用于捕获和重放内核。 - 🔁 函数式转换较少(尚未完全支持 `vmap`/`pmap`),但易于阅读。 **TVM** - ✅ 多次降低传递、调度和内核上的束搜索。 - ✅ 用于批量执行的设备“图”。 - 🔁 tinygrad 还提供了**前端框架**(张量、神经网络、优化器),而不仅仅是编译器。 --- ### 懒惰计算 尝试一个矩阵乘法。可以看到,尽管采用了这种风格,但借助懒惰计算的力量,它被融合到一个内核中。 ```sh DEBUG=3 python3 -c "from tinygrad import Tensor; N = 1024; a, b = Tensor.empty(N, N), Tensor.empty(N, N); (a.reshape(N, 1, N) * b.T.reshape(1, N, N)).sum(axis=2).realize()" ``` 我们可以将 `DEBUG` 改为 `4` 来查看生成的代码。 ### 神经网络 事实证明,神经网络所需的 90% 就是一个不错的自动梯度/张量库。再加上一个优化器、一个数据加载器和一些计算,你就拥有了所需的一切。 ```python from tinygrad import Tensor, nn class LinearNet: def __init__(self): self.l1 = Tensor.kaiming_uniform(784, 128) self.l2 = Tensor.kaiming_uniform(128, 10) def __call__(self, x:Tensor) -> Tensor: return x.flatten(1).dot(self.l1).relu().dot(self.l2) model = LinearNet() optim = nn.optim.Adam([model.l1, model.l2], lr=0.001) x, y = Tensor.rand(4, 1, 28, 28), Tensor([2,4,3,7]) # 替换为真实的 mnist 数据加载器 with Tensor.train(): for i in range(10): optim.zero_grad() loss = model(x).sparse_categorical_crossentropy(y).backward() optim.step() print(i, loss.item()) ``` 完整版本请参见 [examples/beautiful_mnist.py](examples/beautiful_mnist.py),大约 5 秒即可达到 98% 的准确率。 ## 加速器 tinygrad 已经支持多种加速器,包括: - [x] [OpenCL](tinygrad/runtime/ops_cl.py) - [x] [CPU](tinygrad/runtime/ops_cpu.py) - [x] [METAL](tinygrad/runtime/ops_metal.py) - [x] [CUDA](tinygrad/runtime/ops_cuda.py) - [x] [AMD](tinygrad/runtime/ops_amd.py) - [x] [NV](tinygrad/runtime/ops_nv.py) - [x] [QCOM](tinygrad/runtime/ops_qcom.py) - [x] [WEBGPU](tinygrad/runtime/ops_webgpu.py) 而且添加更多加速器也很容易!您选择的加速器只需要支持总共约 25 个低级操作。 要检查默认加速器,请运行:`python3 -c "from tinygrad import Device; print(Device.DEFAULT)"` ## 安装 目前推荐的 tinygrad 安装方式是从源码安装。 ### 从源码安装 ```sh git clone https://github.com/tinygrad/tinygrad.git cd tinygrad python3 -m pip install -e . ``` ### 直接安装(主分支) ```sh python3 -m pip install git+https://github.com/tinygrad/tinygrad.git ``` ## 文档 文档以及快速入门指南可以在[文档网站](https://docs.tinygrad.org/)上找到,该网站是根据 [docs/](/docs) 目录构建的。 ### 与 PyTorch 对比的快速示例 ```python from tinygrad import Tensor x = Tensor.eye(3, requires_grad=True) y = Tensor([[2.0,0,-2.0]], requires_grad=True) z = y.matmul(x).sum() z.backward() print(x.grad.tolist()) # dz/dx print(y.grad.tolist()) # dz/dy ``` 在 PyTorch 中实现相同的功能: ```python import torch x = torch.eye(3, requires_grad=True) y = torch.tensor([[2.0,0,-2.0]], requires_grad=True) z = y.matmul(x).sum() z.backward() print(x.grad.tolist()) # dz/dx print(y.grad.tolist()) # dz/dy ``` ## 贡献 最近人们对 tinygrad 产生了浓厚的兴趣。遵循以下准则将有助于您的 PR 被接受。 我们首先说明哪些情况会导致您的 PR 被关闭,并会附上本节的链接: - 不要写代码高尔夫!虽然低代码行数是本项目的指导原则,但任何看起来像代码高尔夫的内容都将被关闭。真正的目标是降低复杂性和提高可读性,删除 `\n` 对此毫无帮助。 - 所有文档和空白字符的更改都将被关闭,除非您是知名贡献者。编写文档的人应该是对代码库最了解的人。没有证明这一点的人不应该乱动文档。空白字符的更改既无用,又存在引入错误的风险。 - 任何声称是“加速”的内容都必须经过基准测试。总的来说,目标是简单性,因此即使您的 PR 使性能略有提升,您也必须考虑在可维护性和可读性方面的权衡。 - 一般来说,核心 `tinygrad/` 文件夹之外的代码没有经过充分测试,因此除非那里的现有代码已损坏,否则您不应该更改它。 - 如果您的 PR 看起来“复杂”、差异很大或添加了很多行,则不会被审查或合并。请考虑将其分解为更小的 PR,每个 PR 都有明确的优点。我常见的一种模式是在添加新功能之前进行先决条件的重构。如果您可以(干净地)重构到该功能只需更改 3 行代码的程度,那就太好了,这样我们很容易审查。 现在,我们想要的是: - 错误修复(带有回归测试)很棒!这个库还不是 1.0 版本,所以如果您偶然发现了一个错误,修复它,编写一个测试,并提交 PR,这是非常有价值的工作。 - 解决悬赏问题!tinygrad [为库的某些改进提供现金悬赏](https://docs.google.com/spreadsheets/d/1WKHbT-7KOgjEawq5h5Ic1qUWzpfAzuD_J06N1JwOCGs/edit?usp=sharing)。所有新代码都应该是高质量的并且经过充分测试。 - 功能。但是,如果您要添加一个功能,请考虑代码行数的权衡。如果是 3 行代码,那么它的有用性门槛就比 30 行或 300 行的功能要低。所有功能都必须有回归测试。通常在没有其他限制的情况下,您的功能的 API 应与 torch 或 numpy 匹配。 - 明确有益的重构。一般来说,如果您的重构不是明确有益的,它将被关闭。但有些重构非常棒!从深度核心的角度考虑可读性。更改空白字符或移动几个函数是无用的,但如果您发现两个 100 行的函数实际上可以使用同一个带参数的 110 行函数,同时还能提高可读性,这将是一个巨大的胜利。重构应通过[过程重放](#过程重放测试)测试。 - 测试/模糊测试。如果您能添加非脆弱的测试,我们非常欢迎。我们这里也有一些模糊测试工具,通过它们和改进它们可以发现大量错误。发现错误,甚至使用 `@unittest.expectedFailure` 编写(本应通过但实际失败的)测试,都是很棒的。这是我们取得进步的方式。 - 移除核心 `tinygrad/` 文件夹中的死代码。我们不关心 extra 中的代码,但移除核心库中的死代码是非常好的。这样可以减少新开发者阅读时产生的困惑。 ### 运行测试 您应该使用 `pre-commit install` 安装预提交钩子。这将在每次提交时运行 linter、mypy 和一部分测试。 有关如何运行完整测试套件的更多示例,请参考 [CI workflow](.github/workflows/test.yml)。 在本地运行测试的一些示例: ```sh python3 -m pip install -e '.[testing]' # 安装测试所需的额外依赖 python3 test/backend/test_ops.py # 仅运行操作测试 python3 -m pytest test/ # 运行整个测试套件 ``` #### 过程重放测试 [过程重放](https://github.com/tinygrad/tinygrad/blob/master/test/external/process_replay/README.md) 将您的 PR 生成的内核与主分支进行比较。如果您的 PR 是重构或加速,且没有预期的行为变化,则应在拉取请求标题中包含 [pr]。 # 参考资料 * any list {:toc}