起源与编年史:两大框架的十年演化
第 1 章:起源与编年史:两大框架的十年演化
> Chapter 1: Origins and Chronicles — A Decade of Evolution of Two Frameworks > > 核心问题 (Core Question): PyTorch 与 TensorFlow 的演化历史如何塑造了它们今天的技术形态?十年间两大框架如何相互塑造、此消彼长,并最终形成了今天研究-产业分工的双轨格局?
2014 年 1 月,Google 收购了一家名为 DeepMind 的英国初创公司,标志着一个新时代即将到来;同年,Torch7 正在 Yann LeCun 主导的 Facebook AI 实验室(FAIR)内部被广泛使用,而一位名叫 Adam Paszke 的波兰博士生在 Twitter Cortex 团队用 Torch7 写下了后来影响整个社区的代码。短短三年后,2017 年的 NeurIPS 大会上,一个用 Torch7 灵感重写、用 Python 替代 Lua 的新框架悄然登台——这就是 PyTorch。再往前数两年,2015 年 11 月,Google Brain 团队将内部使用多年的 DistBelief 系统重写并以 Apache 2.0 协议开源,命名为 TensorFlow,标志着符号式(symbolic)深度学习框架正式进入工业界主流视野。从 2014 到 2024,整整十年,两个框架如同镜像般相互映照:一个从研究基因里生长出来,逐步攻入产业;另一个从工业生产中长成,反过来试图贴近研究。本章将沿着时间线,复盘这条曲折但有迹可循的演化路径。
> In January 2014, Google acquired DeepMind, signaling a new era; meanwhile, Torch7 was heavily used in Yann LeCun's Facebook AI Research (FAIR) lab, and a Polish PhD student named Adam Paszke was writing code in Torch7 at Twitter Cortex that would later influence an entire community. Just three years later, at NeurIPS 2017, a new framework—rewritten with Torch7's spirit but replacing Lua with Python—quietly debuted: PyTorch. Two years before that, in November 2015, the Google Brain team open-sourced their internal DistBelief system under Apache 2.0, naming it TensorFlow, marking the entry of symbolic deep learning frameworks into the industrial mainstream. From 2014 to 2024—a full decade—these two frameworks have mirrored each other: one grew from research DNA and gradually conquered industry; the other grew from industrial production and tried to reach back toward research. This chapter traces this tortuous yet traceable evolutionary path.
---
1.1 Torch7 的遗产:动态图先驱的兴衰
1.1.1 2002 年 IDIAP 的 Torch 起点
要理解 PyTorch 的一切设计选择,必须先理解 Torch7。Torch 的源头远比 PyTorch 古老:它诞生于 2002 年的瑞士洛桑,IDIA(IDIAP)研究所的 Ronan Collobert 在 Torch 1.0 中实现了一套以 Lua 为前端、以 C/CUDA 为后端的科学计算库。Torch 的核心数据结构是 `Tensor`(注意,这里的 Tensor 概念和今天 PyTorch 的 `torch.Tensor` 几乎一脉相承),其哲学可以用一句话概括:让研究者的代码尽可能贴近数学公式。
```lua -- Torch7 中的典型写法(2010-2014) require 'torch' require 'nn'
-- 一个简单的全连接层 local model = nn.Sequential() model:add(nn.Linear(784, 256)) model:add(nn.Tanh()) model:add(nn.Linear(256, 10))
-- 前向计算 local input = torch.Tensor(32, 784):normal() local output = model:forward(input) ```
读者请注意这段代码的三个特征:第一,它没有显式的 Session、Graph、Placeholder——任何东西都是命令式(imperative)的对象;第二,`model:forward(input)` 是真正的函数调用,可以打断点、可以打印中间结果;第三,整个程序是"逐行解释"式的,Python 用户看到这种风格会觉得异常熟悉。这就是 PyTorch 十年后追求的"Pythonic + Eager"哲学的真正源头。
> Bilingual note: Torch7 之所以能在 2010-2014 年间成为深度学习研究的主流工具,关键在于它将"贴近数学"做到了极致。但这一优势同时被一个更根本的问题所限制:Lua 是一个非常小众的编程语言,其生态系统的规模大约只有 Python 的 1/50。
1.1.2 2014-2016:Torch7 的黄金时代与隐性裂痕
2014 年是 Torch7 的巅峰之年。Yann LeCun 在 Facebook 主导成立了 FAIR(Facebook AI Research),并明确将 Torch7 选定为研究团队的官方框架。同年,Twitter Cortex 团队(后被 Twitter 解散,部分核心成员并入 Facebook)也在用 Torch7 实现大规模时间序列模型。Adam Paszke 在 Twitter Cortex 期间深度参与了 Torch7 生态,他后来成为 PyTorch 的核心作者之一绝非偶然——PyTorch 早期 API 几乎就是对 Torch7 的"Python 化翻译"。
但 Torch7 有一个被研究者长期忽略的硬伤:没有自动微分。在 2010-2014 年间,研究者必须手动写反向传播,或者使用第三方包 `autograd`(一个由 Koray Kavukcuoglu 早期在 DeepMind 时期为 Torch7 写的反向模式自动微分库)。这种"半自动"的状态对于实现 RNN、变分自编码器等结构复杂的网络来说极其痛苦——每修改一个网络结构,就需要重写相应的反向传播代码。这一痛点直接催生了 2015 年 Chainer 的出现:日本 Preferred Networks 的作者用纯 Python 实现了运行时的计算图构建(define-by-run),这正是 PyTorch 一年后采用的范式。
与此同时,2014 年的另一条平行线是 Theano。Yoshua Bengio 领衔的蒙特利尔大学 MILA 实验室维护着 Theano,它比 Torch7 更早实现了符号式计算图和自动微分,是当时学术界最流行的框架(直到 2017 年作者宣布停止维护)。Theano 启发了 TensorFlow 的设计,而 Chainer 启发了 PyTorch 的设计——这是十年框架战争中一条隐藏的师承线索。
> 设计哲学对照 (Design Philosophy Comparison): > - Torch7 / Chainer / PyTorch 阵营(命令式 / Define-by-Run):图的构建发生在代码运行时,调试容易,灵活性高,但优化困难。 > - Theano / TensorFlow 1.x 阵营(符号式 / Define-and-Run):先构建完整静态图,再送入数据执行,部署和优化容易,但调试困难。 > 这条分界线直到 2019 年 TF 2.0 和 2023 年 PyTorch 2.0 才开始真正模糊。
1.1.3 2016 年 9 月:PyTorch 的诞生与 Lua→Python 迁移
2016 年 9 月,Meta(当时仍叫 Facebook)在 GitHub 上发布了 PyTorch 0.1.0。表面上看,这只是 Torch7 的一个 Python 重写版,但实质上 PyTorch 做了三件关键的事:
1. 完全用 Python 重写 C 后端的所有胶水层。Torch7 时代,用户在 Lua 中调用 C 库有显著的性能损失;PyTorch 通过 Cython 和 pybind11 让 Python 端调用 Tensor 操作的 overhead 几乎为零。 2. 内置反向模式自动微分(torch.autograd)。这是对 Torch7 痛点的彻底解决:任何包含 `torch.Tensor` 操作的代码,调用 `loss.backward()` 即可获得梯度。 3. 完全重写 GPU 调度器,使其与现代 CUDA 的 stream 模型对齐。
```python
PyTorch 0.1.0 的典型用法(2016 年 9 月)
import torch from torch import nn
定义网络
model = nn.Sequential( nn.Linear(784, 256), nn.Tanh(), nn.Linear(256, 10) )
前向 + 反向
input = torch.randn(32, 784) output = model(input) loss = output.sum() loss.backward() # 自动微分,无需手写 ```
对比 1.1.1 中的 Lua 代码,会发现二者结构几乎一致——这就是 PyTorch 团队有意为之的"API 迁移最小化"策略。Torch7 时代训练好一个项目的研究者,几乎可以"按行翻译"到 PyTorch。
但这次迁移并非没有阻力。2016-2017 年间,大量 FAIR 的研究员在内部同时维护着 Torch7 和 PyTorch 两套代码;外部社区则在争论"Lua 的轻量"和"Python 的生态"到底孰优孰劣。最终胜负在 2018 年揭晓:Torch7 的 GitHub 仓库(torch/torch7)最后一次有意义的提交停在 2017 年 8 月 31 日,而 PyTorch 在 2017 年底即在 NeurIPS 学术圈基本完成"事实标准"的建立。
> Bilingual note: The migration from Lua to Python was not merely a language change. It reflected a deeper recognition that the productivity of researchers depends not just on the framework's internal performance, but on the size of the ecosystem that surrounds the language: Jupyter notebooks, NumPy, SciPy, Matplotlib, scikit-learn, Pandas—all of these "spoke" Python. The day PyTorch supported `torch.from_numpy(ndarray)` with zero copy was the day the community's choice was sealed.
---
1.2 TensorFlow 的工业血统:从 DistBelief 到全球部署
1.2.1 2011-2014:DistBelief 的奠基年代
TensorFlow 的"工业血统"可以追溯到 2011 年。那一年,Jeff Dean 和 Andrew Ng 在 Google 内部启动了一个代号为 DistBelief(有时也称 "Google Brain v1")的项目,目标明确:让 Google 内部的大规模神经网络训练成为常规基础设施。
DistBelief 有几个关键设计选择是当时 Theano 和 Torch7 都不具备的:
- **参数服务器(Parameter Server)架构**:将模型参数分布在数千台机器上,通过异步或同步协议聚合梯度。Google 在 2012 年发表的系统论文《Large Scale Distributed Deep Networks》中详细描述了这一架构。
- **粗粒度算子(Coarse-grained Operators)**:每个算子对应一段大粒度的 C++ 实现(如 `Conv2D`、`MatMul`),整个网络被表示为算子的 DAG,调度器直接执行 DAG。
- **紧耦合生产部署**:DistBelief 在 2012 年起即被用于 Google Search Rank、YouTube 推荐、Google Play 商店的广告点击率预测等核心业务。
然而,DistBelief 也有严重的"研究不友好"问题:它是一套闭源、单语言(C++)、不可扩展的内部系统,几乎不可能被外部研究者使用。2014 年 Jeff Dean 在一次 Google 内部反思中明确表示:"DistBelief 是为生产环境设计的,牺牲了研究者的灵活性。"这一反思直接催生了 TensorFlow。
1.2.2 2015 年 11 月:TensorFlow 的设计抉择
TensorFlow 在 2015 年 11 月开源时,其设计文档《TensorFlow: A System for Large-Scale Machine Learning》清楚地披露了它从 DistBelief 和 Theano 继承的设计语言:
```python
TensorFlow 1.x 的典型用法(2015-2017)
import tensorflow as tf
第一步:定义静态图
a = tf.placeholder(tf.float32, shape=[None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(a, W) + b
第二步:执行图
with tf.Session() as sess: sess.run(tf.global_variables_initializer()) result = sess.run(y, feed_dict={a: data}) # 数据通过 feed_dict 注入 ```
请注意这段代码与 1.1 节 PyTorch 代码的本质区别:在 PyTorch 中,数据流动是 Python 函数的自然调用;而在 TF 1.x 中,数据和图的执行是分开的——你必须先 `sess.run` 才会真正计算。这种"Define-and-Run"范式在生产部署中有一个显著优势:图可以在部署前被完整优化、序列化为 protobuf、在不依赖 Python 解释器的环境下运行。这也是为什么 TF 1.x 在 2015-2019 年间成为移动端(TF Lite)、浏览器(TensorFlow.js)、服务器(TF Serving)首选的工业部署方案。
但从研究角度看,这种范式造成了巨大的生产力损失。研究者必须通过 TensorBoard 才能"看到"图的执行过程,必须使用 `tf.cond` 而不是 Python `if` 来写条件分支,必须使用 `tf.while_loop` 而不是 Python `while` 来写循环。这种"图作为一等公民"的设计,让一个简单的 GAN 实现也常常需要 200-300 行代码——这正是 PyTorch 在 2017 年迅速占领研究市场的根本原因。
> Bilingual note: TensorFlow's early design was optimized for deployment throughput (how many inference requests per second), not researcher iteration speed. The years 2015-2019 were essentially a contest: did industrial deployment matter more than research productivity? The answer, ultimately, was "both, and PyTorch 1.0 and TF 2.0 converged on this answer through different paths."
1.2.3 2017-2018:双重压力下的 TensorFlow 1.x
到 2017 年底,TensorFlow 面对两个问题:
1. 研究市场被 PyTorch 蚕食:NeurIPS 2017 大会的非官方统计显示,使用 PyTorch 的论文占比从 2016 年的 ~5% 上升到 2017 年底约 30%。 2. API 复杂度被开发者抱怨:TF 1.x 的 API 表面面积(API surface area)过大,graph mode 与 eager mode 并存,tf.contrib 中充斥着半生不熟的实验性 API。
Google 内部对此的应对是著名的 "rebrand + integrate" 战略:2018 年中期,François Chollet 的 Ke