返回 登录
0

黄金三镖客之TensorFlow版

原文链接:The Good, Bad, & Ugly of TensorFlow
作者:Dan Kuster
译者:刘翔宇 审校:赵屹华
责编:周建丁(zhoujd@csdn.net)

自从TensorFlow半年前发布以来,我们一直使用它来进行日常研究和工程。在此过程中我们也学习到了很多知识。是时候写一些新体会了!

因为TensorFlow上没有很多主观的文章和有用的文档,我必须尽可能地使用我能找到的样例、教程、文档和代码片段。

社区参与是最重要的。

当涉及到机器学习时,很容易把注意力集中于技术上(特征、功能、基准等)。但是优秀的程序员都知道,编写人们愿意使用的代码要比机器可以编译执行的代码要难上许多。所以,对于TensorFlow我最欣赏的一点就是,机器学习社区的所有人都能意识到这点,他们愿意去尝试它,而且之中有许多人使用它来编写有用的东西。解决问题有更多的思路,更多的经验可供借鉴!

现在大量的开发者和学生都对深度学习感兴趣,因为它们听说了TensorFlow。谷歌Deepmind最近宣布,他们将会把Torch迁移到TensorFlow上,所以我们可能会在不久的将来看到TensorFlow中引入了强化学习模型。当社区拥抱开放,透明API,有用模块,以及为互联网做贡献,前途会一片光明。

技术封锁因素已基本消除。

当我们去年11月份写的第一篇评估TensorFlow的文章时,它还存在一些实际和潜在的封锁因素。我很高兴的告诉你们,大多数这些封锁因素现在都已经消除了。

  • 支持多GPU。它能正常工作;文档很简洁。不过你仍然需要弄清楚如何分解并解决问题,这也很有趣,不是吗?

  • 分布式资源训练(比如云)。在v0.8版本中,已经支持了分布式训练

  • 支持队列,在运算图上进行数据加载和预处理等操作。

  • 使用TensorBoard可视化运算图。在构建、调试新模型时,很容易迷失方向。对我来说,针对我构建用于解决困难问题的新框架和模型,要保持心理语境已经是相当繁重的任务了,因此对模型有一个完全不同的表示非常有用;TensorBoard的图形可视化工具在这里非常有用。

  • TensorBoard交互式记录事件日志。在UNIX/Linux下,我喜欢使用命令tail -f 来监测命令行任务的输出,做快速明智的检查。TensorFlow的事件日志记录可以达到同样的效果,从图表中得出时间和汇总,然后TensorBoard随着时间推移来监控输出(比如学习率,损失值,训练/测试精度)。

  • 模型检查点。训练模型一段时间。然后停止并进行评估。重新载入检查点继续训练。

  • 性能和GPU内存使用量与Theano和其他使用CUDNN的库相似。早期版本的性能不足似乎是因为使用了CUDNNv2,那么在TensorFlow v0.8(使用CUDNNv4)中有了极大改善。

一些高品质的元框架

  • Keras包装了TensorFlow和Theano的后端。如果你不想深入TensorFlow(或是Theano)的细节,Keras会是个不错的参考。

  • TensorFlow Slim是构建图像模型不错的选择。即使你更喜欢自己写底层的TensorFlow代码,对TensorFlow API使用和模型设计等,Slim是不错的参考。

  • SkflowTensorFlow方法包装成了scikit-learn风格的API。在我看来,与导入、内嵌各种scikit-learn指标的Python代码相比,这么做有点奇怪。

  • PrettyTensor提供了具有类似张量行为的东西,有一种可链接的语法,你可以快速构建特定类型的模型。

发布时间表

维护一个流行的开源项目是一大挑战,特别是像TensorFlow这样具有技术复杂性的项目。感谢维护者!我们首先感谢他们整合新特性和测试的策略,以便早期采用者可以在它们形成文档之前尝尝鲜。如果你对发布细节感兴趣,看看版本记录:https://www.tensorflow.org/versions/r0.8/resources/versions.html

测试相当棒!

测试对验证功能和临时观测运行情况非常有用。当你在TensorFlow中发现一些与你预期不符的东西,或者你在学习某个方法或某些参数的怪癖行为时,在Github上搜索测试,看看测试是如何做的!

与Theano相比仍缺少RNN。

Theano团队花了多年时间投入了大量精力去优化递归神经网络实现。令人高兴的是,这个差距正在迅速缩小,在几个月后,TensorFlow可能是RNN的首选平台。特别是:

  • 我们还没找到优雅地处理可变长度序列输入的方式。用额外的复杂度来处理,大多数模型不需要这么做。填补所有序列至一个固定长度在许多情况下效果不错(特别是使用批处理和GPU),但有些人可能认为这不是令人满意的做法。动态展开RNN可能是个解决方案,但是tensorflow.python.ops.rnn模块中的dynamic_rnn实现是新功能,还没有形成文档。我们还在进行实验。

  • 性能和内存使用量。虽然很难做出确切的同类比较,但是在在两个框架中实现许多同样的模型给我们的印象是,对于RNN,在给定的GPU上,Theano可能要快一些而且使用更少的内存,也许是因为有逐点操作。TensorFlow在多GPU上和“编译”时间上更胜一筹。

缺少数据摄取权威样例。

TensorFlow文档和样例集中于使用一些著名的学术数据集来演示各种特性和功能。这完全有道理,按事情轻重缓急来处理是一件好事。但现实世界的问题很少能够替代这些数据集。在学习一个新的深度学习框架时,处理张量输入和形状会是一大绊脚石,所以给出一些处理凌乱输入数据(怪异的形状,填充,分布,标记化等)的样例可以为将来开发者/工程师解决许多问题。

文档可能会不一致

TensorFlow有许多不错的教程,而且代码有很好的注释(感谢作者)。但是机器学习/深度学习是很深而且很广的领域,而且在新功能和文档/教程之间会有滞后。一些我们喜欢的教程有:

  • Nathan Github上的简单教程。可以快速查看机器学习的工作原理。如果你熟悉numpy或Theano,你可以从这里开始。

  • 谷歌Vincent Vanhoucke的Udacity课程。如果你是深度学习初学者,那么从这里开始。

  • MNIST官方教程。如果你是深度学习初学者,在学习完Udacity课程后,可以看看这里的教程。MNIST是“机器学习果蝇”,有良好的基准和完整性检查。

  • TensorFlow API文档。TensorFlow的首选参考。Control-F来搜索!

不幸的是,尤其对于RNN,在文档和教程之间还是有概念差距,比如简单样例和全面顶尖样例之间的差距。这对那些同时学习概念和框架的开发者来说的确是一大障碍。例如,Udacity教程和RNN教程使用宾州树库数据(Penn TreeBank data)来建立语言模型具有很好的说明性,因为它们简单。它们对于学习一个概念是不错的教程,但是对于现实世界的建模任务来说又太基础了。我们意识到TensorFlow RNN教程唯一权威之处就是就是全面序列-序列模型,它使用多单元RNN(GRU或LSTM),采用了attention,bucketing,和采样softmax。哇!这就像你学习滑雪,不需要从在山坡上训练开始,然后就直接在布满树木的山顶上使用雪上特技(危险,可怕对么!?)…你可能不应该在实现简单功能后,就实现最复杂的功能。根据你正在解决的问题,逐步增加复杂度。

图片描述

高品质的教程会逐渐提高复杂度,从简单的RNN语言模型到能够学习反转词语的普通序列-序列RNN编码器-解码器架构,到具有attention的神经翻译序列-序列LSTM,然后到具有多单元RNN,bucketing以及所有对初期TensorFlow用户社区有极大帮助的技巧。我猜测由于缺乏这种循序渐进的样例可能可以解释为什么社区已经产生了许多流行的TensorFlow模型,但我们还没看到许多创新架构和聪明的混用。

如果缺乏文档,那么就去看测试!通常情况下测试比文档更具说明性。由于谷歌将此项目开源,你可以在Github上搜索相关的测试看看作者是如何使用的。

我们完全能够理解TensorFlow团队首要专注于功能和特性,其次是文档…我们可能也会这么做!良好的文档是一种投资,我见到过最好的文档是出自非作者之手,因为那至少能够保证没接触过的人能够理解。如果TensorFlow社区编写文档与开发新特性同样急切,那会是件非常棒的事情!

我们仍然在等待跟踪监测工具,脑电图。

(⊙_☉) 哈哈。

异构资源利用增加了复杂性。

控制力和简单性之间典型的工程权衡——如果你希望在操作执行上有细粒度的控制(例如,使用哪些GPU节点),那么你需要维护这些约束。在某些情况下,细粒度控制对最大限度提高性能是必要的。例如,在给GPU输入数据之前,使用多线程获取并预处理批量数据,那么GPU就不会等待这些操作。想了解在CPU上使用异步运行方式输入数据给GPU的细节,或是为你自己的队列创建基准,可以看看Luke这篇优秀的文章,TensorFlow Data Input (Part 2): Extensions

TensorFlow会拱曲GPU。

同样,在启动时,TensorFlow会给自己分配所有可用的GPU内存。取决于你使用的情形,这是把双刃剑。如果你在积极开发模型,并且本机有GPU可用,你可能想将GPU分成多分,分给不同应用。但是,如果你在云环境下部署一个模型,你想知道你的模型可以在可用的硬件下运行,而不会与可能与此硬件通讯的其他代码发生不可预测的交互。

你可以使用类似于下面的代码片段来设定每个线程可用GPU内存的上限,但是如果在一台机器上有多个GPU,我们还不知道用什么方式控制每个GPU的分配。

设置选项:

gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.5)

然后将它作为配置传给会话:

sess = tf.Session(config = tf.ConfigProto(gpu_options = gpu_options))

默认情况下,Theano与TensorFlow会冲突。

我们有许多代码都依赖于Theano,从加载数据到各种实用功能。我们也阅读了许多Theano中实现的研究代码。但是,如果你在同一范围内导入Theano和TensorFlow,它们将竞争分配GPU内存,这会导致坏事发生。在完全不同的GPU(例如,两个GPU运行两个单独的模型)上执行完全不同的环境,你可以在shell环境下限制设备对CUDA的可见性。那么当你启动Python代码的时候,它将只会看到(并且分配)CUDA能看到的GPU。如果你使用bash,那么这么设置:

export CUDA_VISIBLE_DEVICES=0,1  # only the first two GPUs are usable

注意:上面的CUDA设备的数字可能与你的不一样,查看设备ID使用nvidia-smi!

另外,如果你想让Theano只在CPU上执行,你可能想让数据和实用功能这么做,那么你可以在Python代码中设置。下面是实现这效果的Python代码。将它放在所有导入语句的上方:

import os
os.environ['THEANO_FLAGS'] = "floatX=float32,device=cpu,fastmath=True,ldflags=-lopenblas"

当然你也可以在代码里设定CUDA的环境标志,但对我的模型开发工作流程来说,记住“每个shell脚本一个GPU”更容易。

总结

在任何框架中实现端到端的工作流要付出一定的努力,TensorFlow也不例外。TensorFlow的一些东西(队列,某些图操作,资源分配/上下文管理,图形可视化)相对于深度学习场景来说还比较新,我们仍然在学习利用这些特性的最佳途径。其他一些东西在其他框架中已经存在一段时间了。虽然整体概念相似,但实现细节不尽相同。我们感谢所有的谷歌开发者为实现良好抽象(例如队列中的流式数据)而付出的努力。

开放工具最好的地方就是,社区人员为解决一个问题实现了一个非常聪明的技巧或创新方式。尽管大多数人还在学习TensorFlow,我认为这种情况发生的可能性会水涨船高!展望下一个时代!

有问题需要咨询?欢迎邮件告知:contact@indico.io。

评论