PyTorch 设计哲学¶
本文档旨在帮助贡献者和模块维护者理解 PyTorch 在长期发展过程中形成的高层次设计原则。这些原则并非一成不变的硬性规定,而是作为权衡不同考量和解决在 PyTorch 开发过程中可能出现的争议的指南。有关贡献、模块维护以及如何将争议升级给核心维护者的更多信息,请参阅 PyTorch 治理。
设计原则¶
原则 1:易用性优于性能¶
这个原则可能令人惊讶!正如一位 Hacker News 用户所写:PyTorch 太棒了![...] 尽管我有点困惑。一个机器学习框架怎么可能不痴迷于速度/性能呢? 参见 Hacker News 上关于 PyTorch 的讨论。
Soumith 关于 PyTorch 社区发展 的博客文章深入探讨了这一点,但概括地说,
PyTorch 的首要目标是易用性
次要目标是拥有合理的性能
我们认为,保持灵活性以支持基于我们抽象层构建的研究人员至关重要。我们无法预见未来的工作负载会是什么样子,但我们知道我们希望它们首先在 PyTorch 上构建,而这需要灵活性。
更具体地说,我们以易用性优先的方式运作,尽量避免在没有清晰权衡考量的情况下事先进入限制优先模式(例如,静态形状、仅图模式)。通常会有一种诱惑,即事先实施严格的用户限制,因为它能简化实现,但这伴随着风险
性能提升可能不值得用户付出的努力,原因可能是性能收益不够吸引人,或者它只适用于相对狭窄的子问题集。
即使性能提升引人注目,这些限制也可能将生态系统碎片化为不同的限制集,这会迅速变得让用户难以理解。
我们希望用户能够无缝地将 PyTorch 代码迁移到不同的硬件和软件平台,与不同的库和框架进行互操作,并体验 PyTorch 用户体验的全部丰富性,而不是一个最小公分母子集。
原则 2:简单胜过容易¶
这里,我们借鉴了 Python 之禅
明晰胜于隐晦
简单胜于复杂
更简洁地描述这两个目标的说法是 简单胜过容易。让我们从一个例子开始,因为在日常英语中,简单 (simple) 和 容易 (easy) 经常被互换使用。考虑一下如何在 PyTorch 中对 设备 进行建模
简单/明晰(易于理解、调试):每个张量都与一个设备关联。用户明确指定张量的设备移动。需要跨设备移动的操作会导致错误。
容易/隐晦(易于使用):用户无需担心设备;系统会自动找出全局最优的设备放置方案。
在这种特定情况下,以及作为一般设计哲学,PyTorch 倾向于暴露简单明晰的构建块,而不是对实践者来说“容易使用”的 API。简单版本对于 PyTorch 新用户来说是立即可理解和可调试的:如果你在程序中实际调用需要跨设备移动的算子的地方调用它,你会得到一个清晰的错误。容易解决方案可能让新用户一开始进展更快,但调试这样的系统可能很复杂:系统是如何做出决定的?接入这样的系统的 API 是什么,以及对象在其 IR 中是如何表示的?
支持这种设计的经典论点来自 关于分布式计算的笔记 (TLDR: 不要统一建模性能特性差异很大的资源,细节会泄露) 和 端到端原则 (TLDR: 在堆栈的较低层构建智能功能可能会阻碍在堆栈较高层构建高性能特性,而且通常也行不通)。例如,我们可以构建算子级或全局设备移动规则,但精确的选择并不明显,并且构建一个可扩展的机制会产生不可避免的复杂性和延迟成本。
这里需要注意的是,这并不意味着更高层次的“容易”API 没有价值;当然,在堆栈的更高层次支持在大型集群中异构计算上的高效张量计算是有价值的。相反,我们的意思是,专注于简单的底层构建块有助于指导“容易”API 的设计,同时仍然在用户需要偏离常规路径时保持良好的体验。它还为创新和更多有主见的工具的增长留下了空间,这些工具的增长速度是 PyTorch 核心库无法支持的,但最终我们受益于此,这可以从我们丰富的生态系统中看出。换句话说,一开始不自动化使我们能够更快地达到良好的自动化水平。
原则 3:Python优先,并提供一流的语言互操作性¶
这个原则最初是 Python 优先
PyTorch 不是一个将整体式 C++ 框架绑定到 Python 的接口。它的构建旨在与 Python 深度集成。你可以像使用 NumPy、SciPy、scikit-learn 或其他 Python 库一样自然地使用它。你可以直接用 Python 编写新的神经网络层,使用你喜欢的库,并使用 Cython 和 Numba 等包。我们的目标是在适当的情况下不重复造轮子。
多年来,PyTorch 需要解决的一个问题是 Python 的开销:我们首先用 C++ 重写了 autograd 引擎,然后是大部分算子定义,接着开发了 TorchScript 和 C++ 前端。
尽管如此,在 Python 中工作为用户提供了最佳体验:它灵活、熟悉,也许最重要的是,拥有庞大的科学计算库和扩展生态系统可供使用。这一事实推动了我们最近的一些贡献,这些贡献试图在曲线的 Python 易用性一端附近达到帕累托最优。
TorchDynamo,一个 Python 帧评估工具,能够在用户干预最少的情况下加速现有的 eager-mode PyTorch 程序。
torch_function 和 torch_dispatch 扩展点,这些扩展点使得能够基于 C++ 内部实现构建 Python 优先的功能,例如 torch.fx tracer 和 functorch。
这些设计原则并非一成不变的规定,而是来之不易的选择,它们支撑着我们将 PyTorch 打造成一个可调试、可修改和灵活的框架。随着我们拥有更多的贡献者和维护者,我们期待与您一起将这些核心原则应用于我们的库和生态系统中。随着我们学习新知识和 AI 领域的发展(我们知道它会发展),我们也愿意不断完善这些原则。