在今年 5 月初召开的 Facebook F8 开发者大会上,Facebook 宣布将推出旗下机器学习开发框架 PyTorch 的新一代版本 PyTorch 1.0。据 Facebook 介绍,PyTorch 1.0 结合了 Caffe2 和 ONNX 模块化、面向生产的特性,和 PyTorch 自身灵活、面向研究的特性结合起来,为广泛的 AI 项目提供了一个从科研原型到生产部署的快速、无缝途径,让用户可以快速实验,通过一个能在强制执行模式和声明执行模式之间无缝切换的混合前端优化性能。
除了将研究和生产特性结合起来,PyTorch 1.0 还将 ONNX 包含进来。ONNX 是 Facebook 去年联合多家软硬件公司发布的神经网络模型转换协议,现在,它新增了对苹果的 Core ML、百度 PaddlePaddle、高通 SNPE 的支持,再加上原本支持的 MXNet、Caffe2、PyTorch、TensorFlow、CNTK 等框架,实现了神经网络模型在各种主流框架之间的转换。
PyTorch 1.0 beta 版将在今年夏天和用户见面。
整体来看,刚发布一年左右的 PyTorch 仍然是款比较新的框架,尤其是和 TensorFlow 相比较而言,但 PyTorch 发展非常迅猛。
比如今年 3 月,人工智能大神 Andrej Karpathy 在推特上晒出了过去 6 年 aXriv 论文中各主要深度学习框架的使用比例(单次计算):TensorFlow 14.3%,PyTorch 4.7%,Keras 4.0%,Caffe 3.8%,Theano 2.3%,Torch 1.5%,MXNet、Chainer 和 CNTK 均小于1%:
如果我们细看会发现 PyTorch 增长势头惊人:
因此,未来在深度学习框架领域预计 PyTorch 会成为 TensorFlow 的主要挑战者。
那么这两种框架有何不同之处?自己学习使用时,该选哪一种?
Cinimex DataLab CTO Kirill Dubovikov 发文详细分析了 PyTorch 和 TensorFlow 的区别,可以参考一下。(注:本文写于去年,当前一些性能细节可能已经更新)。
本文我(Kirill Dubovikov——译者注)想探究一下 PyTorch 和 TensorFlow 这两种框架之间的重要区别。为何选它们?现在市面上有很多深度学习框架,其中有些都是很不错的工具,我选这两个是因为我对详细比较它们很感兴趣。
起源
TensorFlow 由谷歌大脑开发,并被谷歌应用在研究和产品需求中。它的前身是闭源工具DistBelief。
PyTorch 算是基于 Lua 语言的框架 Torch 的近亲,Torch 广泛应用在 Facebook 研发中。然而,PyTorch 并非简单的只是一套支持流行编程语言的封装器,它被重新编写,运行速度更快,感觉更原生。
比较这两种框架的最好方式就是用它们写写代码。我给本文也写了个比较它们的 Jupyter notebook,可以点击这里查看(https://github.com/kdubovikov/tf-vs-pytorch/blob/master/pytorch_vs_tf_simple_model.ipynb)。后面我也会引用里面的代码。
首先,我们用这两个框架为下面这个函数写一个简单的逼近器(approximator):
我们会试着根据数据X和函数值f(x)找到未知参数phi。没错,用随机梯度下降解决这个问题有点大材小用,用分析解法会容易些,但我们这个问题作为一个简单的例子能很好的用于比较这两种框架。
我们首先用 PyTorch:
如果你有过使用深度学习框架的经验,你可能会注意到我们是在手动实现梯度下降。觉得不是很方便?幸好 PyTorch 有 optimize 模块,应用了不少流行的优化算法比如 RMSProp 和Adam。
我们会使用 SGD 算法:
可以看到,我们从训练数据中快速的引用了真正的指数。下面我们试试TensorFlow:
同样可以看到,在TensorFlow中也能实现。虽然TensorFlow获得指数用的迭代次数更多,但我确定是因为我没有认真调整优化器的参数,达到可以比较两者结果的水平。
现在,我们接着探究更多的不同之处。
区别 #0 —— 应用
目前很多研究人员和从业人员把 TensorFlow 看作一款随手可用的工具。TensorFlow 官方提供了很好的使用文档,如果文档中没有,网上也有很多写的非常详细的教程。GitHub 上也能找到一大堆用 TensorFlow 实现和训练的模型。
PyTorch 和 TensorFlow 相比,还是个比较新的工具,但发展势头很猛。官方文档和教程也不错。PyTorch 也包含了流行计算机视觉框架的实现,非常易用。
区别 #1——动态及静态图形定义
两种框架都在张量上运行,把任何模型都看作一个有向非循环图(DAG),但对于如何定义它们,PyTorch 和 TensorFlow 区别很大。
TensorFlow 遵循“数据即是代码,代码就是数据”的理念。在 TensorFlow 中,在跑模型之前会静态的定义图形。和外界的所有联系都是通过 tf.Session 对象和 tf.Placeholder,它们都是会在模型运行被外部数据取代的张量。
在 PyTorch 中,会更动态一些:你可以随着进展定义、更改和执行节点,没有特殊的会话界面或占位符。整体来看,PyTorch 和 Python 结合的更紧凑些,多数时候会感觉更原生。而在 TensorFlow 里写东西时,有时你会觉得你的模型好像躲在一堵墙后面一样,就通过墙上的几个洞洞跟你交流。当然了,这也看每个人的喜好和品味。
不过,不单单是在软件工程方面有区别,一些动态神经网络架构可以从这种动态方法种受益。回想一下循环神经网络:有静态图形,输入序列长度会保持不变。这意味着如果你开发一个应用于英语句子的情绪分析模型,就必须将序列长度修正为某些最大值,用 0 填补所有较小的序列。这比较麻烦吧。在递归循环神经网络和树形循环神经网络方面,你会遇到更多问题。
目前,TensorFlow 对于动态输入的支持比较有限,而 PyTorch 则是默认的支持动态输入。
区别 #2—— 调试
由于 PyTorch 的计算图是在运行时定义,因此可以用 pdb,ipdb,PyCharm 这些 Python 调试工具或者以前的可靠的打印语句也行。
TensorFlow 则不同,你可以选择用一个叫 tfdbg 的特殊工具,它能让你在运行时评估 TensorFlow 表达式,浏览所有张量,在会话范围中操作。当然,无法用它调试 Python 代码,因此无需单独使用 pdb。
区别 #3——可视化
在可视化方面,TensorFlow 的 Tensorboard 是个非常棒的功能。它内置在 TensorFlow 中,在调试和比较不同的训练状况时非常有用。例如,假设你训练了一个模型,然后调整一些超参数后又训练一次。而在 Tensorboard 上可以将这两次模型运行状况同时展现出来,从而看出两次的差异。Tensorboard 可以:
- 展示模型图形
- 绘制标量变量
- 可视化分布和直方图
- 可视化图形
- 播放音频
Tensorboard 可以展示多种总结,通过 tf.summary 模块就可以收集到。我们可以为前面的指数例子定义总结操作,用 tf.summary.FileWriter 将它们保存到桌面。
执行 tensorboard --logdir=./tensorboard 就可启动 Tensorboard。由于它是 web 应用,因此可以很方便的用在云实例上。
目前 PyTorch 并没有可以和 Tensorboard 匹敌的工具,不过倒是存在一些集成功能。虽然也能用一些绘图工具比如 matplotlib 和 seaborn,但在可视化这方面,PyTorch 要逊于 TensorFlow。
区别 #4——部署
在部署这方面,TensorFlow 很明显目前略胜一筹:其内置框架 TensorFlow Serving 能让你在特制的 gPRC 服务器上部署你的模型。也同样支持移动端。
在 PyTorch 上,我们就需要用 Flask 或其它工具在模型上编写一个 REST API。在使用 TensorFlow 的时候,如果 gPRC 不是很适用,我们同样可以这么做。不过,如果考虑性能的话,TensorFlow Serving 会是更好的选择。
TensorFlow 同样支持分布式训练,这点 PyTorch 目前尚不具备。
区别 #5—— 数据并行
PyTorch 不同于 TensorFlow 的最大特性之一就是声明式数据并行:你可以用 torch.nn.DataParellel 封装任何模型,而且模型能在批处理维度上实现并行。这样你就可以毫不费力的使用多个 GPU。
另一方面,TensorFlow 能让你调试在具体设备上运行的所有操作。不过,数据并行不仅需要手动调整,还需要深思熟虑。我们看看在 TensorFlow 中实现数据并行的代码:
def make_parallel(fn, num_gpus, **kwargs):
in_splits = {}
for k, v in kwargs.items():
in_splits[k] = tf.split(v, num_gpus)
out_split = []
for i in range(num_gpus):
with tf.device(tf.DeviceSpec(device_type="GPU", device_index=i)):
with tf.variable_scope(tf.get_variable_scope(), reuse=tf.AUTO_REUSE):
out_split.append(fn(**{k : v[i] for k, v in in_splits.items()}))
return tf.concat(out_split, axis=0)
def model(a, b):
return a + b
c = make_parallel(model, 2, a=a, b=b)
我们可以看到,用 TensorFlow 也能实现用 PyTorch 做到的所有操作,但是要麻烦的多(不过相应会有更多的控制权)。
此外值得一提的是,两个框架都支持分布式执行,提供用于定义集群的高水平界面。
区别 #6——一个更像框架,一个更像库
我们搭建一个分类手写数字的 CNN 分类器。PyTorch 开始会看起来很像一个框架。回想一下,编程框架会在特定领域为我们提供有用的抽象,用它们可以很方便的解决具体问题。而这是框架和库的的本质区别之处。
这里我们引入 datasets 模块,它包含的封装器适用于众多用于基准测试深度学习架构的常见数据集。此外,nn.Module 用于搭建自定义 CNN 分类器,就好比 PyTorch 中的程序块(building block),能让我们创建复杂的深度学习架构。在 torch.nn 包中有很多现成可用的模块,可以作为我们模型的基础。PyTorch 用面向对象的方法来定义基本的程序块,在通过子类化拓展功能的同时,也给了我们一些“通道”继续前行。
这是略微修改后的模型版本:https://github.com/pytorch/examples/blob/master/mnist/main.py
相比之下,TensorFlow 给人的感觉更像是一个库,而非一个框架:所有的操作都为低阶操作,你需要写很多样板代码,即便你可能并不想写(比如,一遍又一遍的定义方差和权重···)。随着时间推移,也渐渐出现了一批围绕 TensorFlow 的高级封装器,都是为了简化我们使用 TensorFlow 的方式。它们大部分目前都在 tensorflow.contrib 模块中(很多人并不将它看做一个稳定的 API),有些也迁移到了主库中(见 tf.layers)。
所以,你在如何使用 TensorFlow 上有很大的自由度,同样也能自由选择使用最匹配任务的框架:TFLearn,tf.contrib.learn,Sonnet,Keras,plain tf.layers 等等。说实话,仅 Keras 这一个框架,就能单独写一篇文章讨论。不过这已超出本文讨论范围,暂且不表。
这里我们用 tf.layers 和 tf.contrib.learn 搭建我们的 CNN 分类器,代码和 tf.layers 官方教程一致:https://www.tensorflow.org/tutorials/layers
因此,TensorFlow 和 PyTorch 都能提供有用的抽象,减少样板代码的数量,加快模型的部署速度。这两者的主要不同之处是 PyTorch 感觉更有“Python 味”一些,采用面向对象的方法,而 TensorFlow 有多种供你选择的选项。
总结
TensorFlow 是一款强大而成熟的深度学习库,有强大的可视化性能,以及用于高水平模型开发的多个选项。它具备生产就绪的部署选项,也支持移动平台。如果你符合以下情况, TensorFlow 会是个很好的选择:
- 开发用于生产的模型
- 开发需要在移动平台上部署的模型
- 想要非常好的社区支持和较为全面的帮助文档
- 想要丰富的多种形式的学习资源
- 想要或需要使用 Tensorboard
- 需要用到大规模的分布式模型训练
PyTorch 仍然是个比较年轻的框架,但发展迅速。如果符合以下情况,PyTorch 就比较适合你:
- 正在做机器学习研究,或开发的产品在非功能性需求方面要求不高
- 想要获得更好的开发和调试经验
- 喜欢很有“Python 味”的东西
- 如果你有时间也有条件,这两种都可以试试,看看哪个更符合你的需求和使用习惯。