四时宝库

程序员的知识宝库

Tensorflow好差劲!(tensorflow 缺点)

作者简介:Nico Jimenez开发了Mathpix(https://mathpix.com),这种图形处理API每个月为全球各地的数百万用户处理 2000万个图像。他还开发了Losswise(https://losswise.com),他在Mathpix公司开发的这种内部机器学习监控和分析解决方案已向公众开放。

引言

每隔几个月,我往谷歌搜索引擎输入下列查询:“Tensorflow sucks”(Tensorflow好差劲了)或“f*** Tensorflow”(让Tensorflow滚蛋),希望可以在互联网上找到持相同观点的人。遗憾的是,虽然Tensorflow问世至今已有大概两年,我还是找不到一篇让我完全满意的抨击Tensorflow的文章。

虽然我想可能自己问错了搜索引擎,不过我认为这方面有一个不同的心理因素在作祟:嫉妒谷歌。“极度嫉妒谷歌”这个现象归因于全球各地的工程师们心里都有这一系列假设:

?在谷歌工作的人比你自己更聪明、更能干;

?如果你学习Tensorflow,也许能在谷歌找到一份深度学习工作!

?如果你那家平庸的初创公司使用Tensorflow,你的博客夸赞其优点和好处,也许谷歌会想要收购;

?如果你没有“搞明白”Tensorflow缺乏直观的设计,那也是由于你太笨了;

姑且把上述这些假设搁在一边,不妨客观地分析一下Tensorflow。

Tensorflow横空出世时,谷歌向我们承诺:可以从此告别设计糟糕或维护糟糕的深度学习框架这个无休止的噩梦(比如https://github.com/BVLC/caffe/issues)。我们得到的是相当于Java的深度学习框架(编写一次,即可到处运行),可是用起来不太好玩,它是一种纯粹声明式的模式,这令人讨厌。

问题在哪里出了岔子?由于试图构建一种满足所有人的要求的工具,谷歌似乎开发出了一种满足所有人的要求方面表现一般般的产品。

对于研究人员而言,Tensorflow学习和使用起来都很难。研究工作注重的是灵活性,而缺乏灵活性恰恰是Tensorflow根深蒂固的弊端。

想要提取神经网络中间层(intermediate layer)的值?你需要定义一个图,然后用作为字典来传递的数据来执行该图,另外还不能忘了添加中间层作为该图的输出,否则你就无法检索它们的值。这很麻烦,不过好歹可以实现。

想要有条件地执行层,比如只要句末(EOS)标记生成、就会停止的循环神经网络(RNN)?等到你完成了这一步,人家使用Pytorch的早开办第三家AI初创公司了。

对于像我本人这样的机器学习从业人员而言,Tensorflow也不是一种很好的选择。这种框架具有声明性,因而调试起来困难得多。能够在Android或iOS上运行模型的优点似乎很棒,不过你看一下框架二进制文件有多庞大(20MB+),或者看一下几乎不存在的C++说明文档,或者你想要完成任何一种有条件的网络执行(这在像移动这些资源很少的环境下超有用),就会发现Tensorflow只是看起来很美。

与其他框架之间的比较

诚然,Tensorflow的开发人员是深度学习界的超级明星。然而,Tensorflow的元老级开发者贾扬清(Yangqing Jia)最近却离开了谷歌,加入了Facebook。他在Facebook搞的Caffe2项目正悄然受到追捧:(https://github.com/caffe2/caffe2/graphs/contributors和https://github.com/caffe2/caffe2/issues)。不像Tensorflow,Caffe2让用户可以在一个数据上执行层,只用一行代码。这颇具革命性!

此外,Pytorch在顶尖AI研究人员当中的人气正迅速蹿升起来。虽然Torch用户在调理编写Lua代码以处理简单的字符串操作引起的重复性过度劳损(RSI)损伤,但根本没有大批转向Tensorflow,他们在改用Pytorch。看来,Tensorflow对顶尖的AI实验室来说根本就不够好。谷歌,不好意思,恕我如此直言。

在我看来最值得关注的问题是,为何谷歌为Tensorflow选择了一种纯粹声明性的模式,尽管这种方法存在明显的缺点。是否谷歌觉得:将所有的计算封装在单单一个计算图中会简化在其Tensorflow处理单元(TPU)上执行模型,从而可以从英伟达手中夺取从云托管基于深度学习的应用系统赚取的数百万美元?这很难说。总的来说,Tensorflow并不像是一种谋取共同利益的纯粹的开源项目。与谷歌其他出色的开源项目(比如Protobuf、Golang和Kubernetes)相比,Tensorflow相差了不止一点点。

虽然声明性模式对用户界面(UI)编程来说很好,但是许多理由表明它对于深度学习来说却是个有问题的选择。

以React Javascript库为例,这是如今交互式Web应用程序的标准选择。在React中,数据如何流经应用程序方面的复杂性隐藏了起来、不让开发人员看见,这有其必要性,因为Javascript执行的速度通常比DOM的更新快几个数量级。React开发人员可不想为状态如何传播方面的底层细节而操心,只要最终用户体验“够好”就行。

另一方面,在深度学习中,单单一个层实际上会执行数十亿个FLOP!而深度学习研究人员非常关注计算如何执行方面的底层细节,还想要精细化控制,因为他们在不断逼近现有技术的极限(比如动态网络方面),想要易于获取中间结果。

一个具体的例子

不妨看一下这个简单例子:训练一个模型,将输入乘以3。

首先,不妨看一下Tensorflow例子:

import tensorflow as tf

import numpy as np

X = tf.placeholder("float")

Y = tf.placeholder("float")

W = tf.Variable(np.random.random(), name="weight")

pred = tf.multiply(X, W)

cost = tf.reduce_sum(tf.pow(pred-Y, 2))

optimizer = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

init = tf.global_variables_initializer()

with tf.Session() as sess:

sess.run(init)

for t in range(10000):

x = np.array(np.random.random()).reshape((1, 1, 1, 1))

y = x * 3

(_, c) = sess.run([optimizer, cost], feed_dict={X: x, Y: y})

print c

Now let’s look at a Pytorch example that does the same thing:

现在看一下实现同样功能的Pytorch例子:

import numpy as np

import torch

from torch.autograd import Variable

model = torch.nn.Linear(1, 1)

loss_fn = torch.nn.MSELoss(size_average=False)

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

for t in range(10000):

x = Variable(torch.from_numpy(np.random.random((1,1)).astype(np.float32)))

y = x * 3

y_pred = model(x)

loss = loss_fn(y_pred, y)

optimizer.zero_grad()

loss.backward()

optimizer.step()

print loss.data[0]

虽然Pytorch例子少一行代码,但操作起来却要明晰得多,而且在下面这个训练循环(training loop)里面,语法遵循实际学习过程要严谨得多。

1. 向前传递输入

2. 生成损失函数

3. 计算梯度

4. 反向传播

而在Tensorflow中,核心操作是神奇的sess.run调用。

为何你想要编写更多行代码,开发出到头来理解和维护起来更困难的系统?客观地说,Pytorch的接口比Tensorflow的接口好得多。两者甚至不在一个档次。

结束语

谷歌开发的Tensorflow这个框架太过低级,无法轻松地用于迅速构建原型,同时又太过高级,无法轻松地用于前沿研究或资源受限的生产环境。

老实讲,如果外头有四五个开源高级库建立在你已经很高级的库的基础上,以便你的库有用,那么你就知道什么地方不对劲了:

?http://tflearn.org/

?https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/slim

?https://github.com/fchollet/keras

?https://github.com/tensorflow/skflow

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接