序章:两个巨人的诞生 — The Birth of Two Titans
第 1 章 序章:两个巨人的诞生 — The Birth of Two Titans
Torch 与 Tensor 的童年故事,深度学习框架的演化史诗
---
在一个叫做 "深度学习" (Deep Learning) 的小村庄里,住着两个刚刚学会走路的孩子。一个叫 Torch,活泼好动,喜欢即兴玩耍;另一个叫 Tensor,沉稳内敛,总爱把玩具按顺序摆得整整齐齐。村里的大人们看着他们,嘴角上扬——他们知道,这两个孩子长大后,必将搅动整个 AI 世界。
但他们不知道的是,这两个孩子的童年,并非凭空而来。他们的血脉里,流淌着 Theano、Lua Torch、Caffe、DistBelief 这些"父辈"的基因;他们的性格,则是被 2015 到 2016 年那段风云际会的岁月,悄悄塑造出来的。
这是他们的故事。也是整个深度学习框架 (Deep Learning Frameworks) 演化史的开端。
---
1.1 深度学习框架的 "前传":Theano 与早期框架的兴衰
1.1.1 为什么我们需要 "框架"?
在 Torch 和 Tensor 出生之前,村民们手工种地。所谓 "种地",就是科学家们手写反向传播算法 (Backpropagation),手动实现卷积运算 (Convolution),一行一行地堆砌矩阵乘法。那时候的代码长这样:
```python
2008 年,一个研究生的"日常"
def forward_backward(X, W, b, y_true):
Forward
z1 = np.dot(X, W) + b a1 = sigmoid(z1) z2 = np.dot(a1, W2) + b2 y_pred = softmax(z2)
Backward
loss = cross_entropy(y_pred, y_true) dz2 = y_pred - y_true dW2 = np.dot(a1.T, dz2) db2 = np.sum(dz2, axis=0)
... 还要算 50 行
```
这段代码看起来不算长,但当网络层数从 3 层变成 30 层、当激活函数从 sigmoid 换成 ReLU/BatchNorm/Attention、研究者们就发现一个规律:99% 的代码都是重复劳动。
于是问题被提了出来:能不能把 "搭网络" 和 "算梯度" 这两件事,写成可复用的工具?
这就是深度学习框架的原始诉求。它不是科学家异想天开,而是被痛苦逼出来的——是一种 "再也不要手撕 BP 了" 的工程师呐喊。
1.1.2 Theano:第一个真正的 "符号计算图" 框架
2007 年,深度学习教父之一 Yoshua Bengio 带领蒙特利尔大学 (MILA) 的团队,发布了 Theano。这是第一个真正意义上把 "符号计算图" (Symbolic Computational Graph) 这一概念落地为可用工具的框架。
Theano 的核心思想,可以用一句话概括:把数学表达式写成一个图 (Graph),然后交给编译器去求导和优化。
```python
Theano 风格:先 "声明" 计算图
import theano import theano.tensor as T
x = T.matrix('x') y = T.vector('y') W = theano.shared(np.random.randn(784, 10), name='W')
符号表达式:先定义,再编译
prediction = T.nnet.softmax(T.dot(x, W)) loss = T.mean(T.nnet.categorical_crossentropy(prediction, y))
一键求导
grad_W = T.grad(loss, W)
编译成函数
train = theano.function( inputs=[x, y], outputs=loss, updates=[(W, W - 0.01 * grad_W)] ) ```
注意这段代码的 节奏:先声明 `x`、`y`、`W` 都是符号变量,然后定义 `prediction`、`loss`、`grad_W` 这都是图上的节点,最后才 `function()` 把图编译成一个可执行的函数。
这种 "先构图,后执行" 的模式叫做 define-and-run(先定义后运行),它有一个巨大的好处:编译器在编译阶段就可以做很多优化(比如常量折叠、死代码消除),运行时效率非常高。
但它也有一个同样巨大的坏处:调试困难。当 `loss` 算出来是 NaN 的时候,研究者并不能像在 Python 里那样打断点、单步执行——因为代码里的 x 和 y 都不是真实的数据,只是 "符号"。
Theano 在 2010 年前后成为学术界的主流。Hinton、Bengio、LeCun 三巨头中,有两位都与 Theano 关系密切。它的影响力之大,以至于后来者几乎都从它的设计里学到了点什么——TensorFlow 学的最像。
2017 年 9 月 28 日,Bengio 在邮件列表里宣布 Theano 将停止主要开发。这一天,村里最老的"老人" 安静地闭上了眼睛。但他的 DNA,已经遗传给了下一代。
1.1.3 Torch (Lua):科学家的 "瑞士军刀"
就在 Theano 在蒙特利尔诞生的同一年(2002),在巴黎的 IDIAP 研究所 (现更名为 Idiap),研究员 Ronan Collobert、Koray Kavukcuoglu 和 Clement Farabet 启动了另一个项目:Torch。
Torch 的设计哲学与 Theano 截然不同。它没有走 "符号图" 的路线,而是选择了 "命令式 + 数组操作" 的路线——更接近 NumPy 的风格,但加上 GPU 加速和自动求导。
它的关键选择是 使用 Lua 作为脚本语言。
```lua -- Lua Torch 风格:命令式,所写即所得 require 'torch' require 'nn'
-- 创建一个两层网络 model = nn.Sequential() model:add(nn.Linear(784, 200)) model:add(nn.ReLU()) model:add(nn.Linear(200, 10)) model:add(nn.LogSoftMax())
criterion = nn.ClassNLLCriterion()
-- 训练循环 for epoch = 1, 25 do for i = 1, dataset:size() do local input = dataset[i][1] local target = dataset[i][2]
-- 前向 local output = model:forward(input) local loss = criterion:forward(output, target)
-- 反向 local gradOutput = criterion:backward(output, target) model:backward(input, gradOutput) model:updateParameters(0.01) end end ```
读这段代码你会发现,Torch 的哲学是 define-by-run 的雏形:每一个 `forward` 和 `backward` 调用都是立即执行的,你可以在 PyTorch 还没出现之前就感受到 "动态图" 的味道。
但问题来了:Lua 不是主流。在 2002-2015 年这段时期,Python 因为 SciPy、NumPy、matplotlib 的生态崛起,已经成为数据科学家的 "普通话"。Torch 选择 Lua,意味着它只能在少数实验室里流通——DeepMind、Facebook AI、纽约大学的 Yann LeCun 组——这让它保持着一种 "小而美" 的精英气质,但也限制了它的普及。
Torch 在计算机视觉 (Computer Vision) 领域是事实标准,Yann LeCun 长期使用它做研究。但 Yann 后来在多次访谈中承认:Torch 最大的错误是没有绑死 Python。这个判断,最终影响了 PyTorch 的诞生。
1.1.4 Caffe:贾扬清的 "工程杰作"
2013 年,在加州大学伯克利分校 (UC Berkeley) 的 BVLC 实验室,一个叫 贾扬清 (Yangqing Jia) 的博士生发布了 Caffe (Convolutional Architecture for Fast Feature Embedding)。
Caffe 的设计哲学可以概括为 "极致性能 + 配置文件驱动":
```protobuf
Caffe 风格:用 prototxt 描述网络
layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 20 kernel_size: 5 } } layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } ```
这种设计的优点显而易见:网络结构与训练逻辑完全分离。研究员不需要写任何 C++ 代码,只需要在 `.prototxt` 文件里 "画画" 就能搭网络。这让 Caffe 在 2013-2015 年成为工业界部署 CNN 的首选。
贾扬清后来加入了 Google,成为了 TensorFlow 核心开发者之一,又离开 Google 加入了 Alibaba,再之后加入了 Meta——他的职业生涯几乎穿越了每一个主要框架的演化史。但他本人也承认,Caffe 在 RNN 时代被甩下了,因为它对动态计算的支持非常薄弱。
1.1.5 其他同期选手:Chainer、CNTK、Keras
2015 年前后,框架的 "战国时代" 全面爆发,几位选手几乎同时上场:
Chainer(日本 Preferred Networks,2015)—— 它是第一个真正意义上实现 define-by-run 的 Python 框架:
```python
Chainer 风格:定义即运行
import chainer import chainer.links as L import chainer.functions as F
class MLP(chainer.Chain): def __init__(self): super().__init__() with self.init_scope(): self.l1 = L.Linear(784, 200) self.l2 = L.Linear(200, 10)
def __call__(self, x): h = F.relu(self.l1(x)) return self.l2(h)
model = MLP() optimizer = chainer.optimizers.SGD() optimizer.setup(model)
for epoch in range(25): for batch in dataset: x, t = batch y = model(x) # forward 即构建图 loss = F.softmax_cross_entropy(y, t) model.cleargrads() loss.backward() # 反向传播立即执行 optimizer.update() ```
注意它的代码和今天 PyTorch 几乎一模一样。这是因为 PyTorch 的核心团队(特别是 Adam Paszke)公开承认,Chainer 是 PyTorch 设计的最重要灵感来源。这个 "承认" 在深度学习社区里是公开的秘密。
CNTK (Microsoft Cognitive Toolkit,2016) —— 微软的作品。它在语音识别领域表现优异,支持多 GPU 扩展良好,但因为文档和生态问题,始终没有走出微软。
Keras(François Chollet,2015)—— 这是一个 "框架的框架",最初运行在 Theano 之上,后来扩展到 TensorFlow。它的设计哲学是 "用户体验第一",让新手能在 5 分钟内训练一个神经网络。但它本质上是一个 API 抽象层,不是一个独立的执行引擎。
这张图,是我们今天故事的起点:
```mermaid flowchart TD Theano["Theano (2007, MILA)<br/>符号计算图开创者"] Torch["Torch (2002, Lua)<br/>命令式 + GPU 加速"] Caffe["Caffe (2013, Berkeley)<br/>配置文件驱动 CNN"] Chainer["Chainer (2015, PFN)<br/>Define-by-run 先驱"] CNTK["CNTK (2016, Microsoft)<br/>语音识别导向"] Keras["Keras (2015, API 层)<br/>用户体验至上"] DistBelief["DistBelief (2012, Google)<br/>内部第一代 DL 框架"]
Theano --> TF["TensorFlow (2015)<br/>Google Brain"] Torch --> PT["PyTorch (2017)<br/>Facebook AI Research"] Chainer --> PT Caffe --> TF Caffe --> Caffe2["Caffe2 (2017)<br/>Facebook"] Caffe2 --> PT DistBelief --> TF Theano --> Keras CNTK --> TF Theano -. 2017年停止开发 .-> X1["❌"] Torch -. Lua 生态式微 .-> X2["❌"] ```
两条主血脉清晰可见:上方是 Theano → TensorFlow 的符号计算传统,下方是 Torch → Chainer → PyTorch 的命令式 + 动态图传统。这两条血脉的竞争,将贯穿整本书。
---
1.2 TensorFlow 的诞生背景:Google Brain 的工业野心与 DistBelief 的教训
1.2.1 DistBelief:Google 内部的 "丑小鸭"
时间倒回 2011 年。那时候的 Google 还没有转型为 "AI First",但内部已经在用深度学习做搜索广告 CTR 预估。Google Brain 团队(当时由吴恩达和 Jeff Dean 联合领导)开发了公司内部的第一代深度学习框架:DistBelief。
DistBelief 在 Google 内部解决了大问题:它让 Google 的工程师可以训练大规模的神经网络,支撑了语音识别 (Speech Recognition)、图像搜索等核心产品。2012 年,吴恩达和 Jeff Dean 联合发表的著名论文 "Building High-level Features Using Large Scale Unsupervised Learning"(用 1000 万张 YouTube 缩略图无监督学习猫脸),背后就是 DistBelief。
但 DistBelief 有一个致命的问题:它不是一个 "框架",而是一个 "系统"。
具体来说,DistBelief 的设计是基于 parameter server(参数服务器)架构的多机训练系统,它的目标是 "跑得动 1000 台机器"。但代价是:它极度难配置、不可移植、对外接口封闭。Google 内部研究员要用它写一个实验,往往需要请分布式系统工程师来协助。
更要命的是,DistBelief 的代码耦合严重:训练逻辑和 Google 内部的 Borg 调度器、Google 内部日志系统深度绑定。即使想把它开源出来也做不到——一旦脱离 Google 基础设施,它就跑不起来。
到了 2013 年底,Google Brain 内部出现了一个声音:"我们需要重写一个东西,能让一个普通的研究员独立完成从模型定义到分布式训练的全过程。"
这个声音,就是 TensorFlow 的种子。
1.2.2 TensorFlow 的设计哲学:数据流图 (Dataflow Graph)
2013 年下半年,Google Brain 团队启动了 TensorFlow 项目,Jeff Dean 是核心推动者。设计目标被明确地写在一份内部设计文档里,后来公开发布:
> "TensorFlow is an interface for expressing machine learning algorithms, and an implementation for executing such algorithms. A computation expressed using TensorFlow can be performed with little or no change on a wide variety of heterogeneous s