2.5 深度学习主流开源框架

所谓“工欲善其事,必先利其器”,深度学习的快速发展及其在工业界和学术界的迅速流行,离不开3个要素:数据、硬件和框架。

深度学习框架是深度学习的工具,其简单来说就是库,如Caffe、TensorFlow等。深度学习框架的出现降低了深度学习入门的门槛,用户不需要进行底层编码,可以在高层进行配置。目前已有大量深度学习框架可免费供大家学习使用。

下面将对当前深度学习主流开源框架做一个概述性介绍。表2.1给出了深度学习主流开源框架的参数对比。

表2.1 深度学习主流开源框架参数对比

没有什么框架是完美的,不同的框架适用的领域也不完全一致,所以如何选择合适的框架是一个需要探索的过程。总体而言,深度学习框架提供的深度学习组件对于通用的算法是非常容易上手的。

2.5.1 Caffe

Caffe是基于C++语言及CUDA开发的框架,支持MATLAB、Python接口和命令行,可直接在GPU与CPU中进行切换,训练效率有保障,在工业中应用较为广泛。

在Caffe中,网络层通过C++定义,网络配置使用Protobuf定义,可以较方便地进行深层网络的训练与测试。Caffe官方提供了大量实例,它的训练过程、梯度下降算法等模块都被封装。学习prototxt语法后,用户基本能自己构造深度卷积神经网络。Caffe代码易懂好理解,高效实用,上手简单,比较成熟和完善,实现基础算法方便快捷,适合工业快速应用与部署。

Caffe通过Blob以4维数组的方式存储和传递数据。Blob提供了一个统一的内存接口,用于批量图像(或其他数据)的操作与参数更新。Model以Google Protocol Buffers的方式存储在磁盘上,大型数据存储在LevelDB数据库中。

同时,Caffe提供了一套完整的层类型。一个层(Layer)采用一个或多个Blob作为输入,并产生一个或多个Blob作为输出。Caffe保留所有的有向无环层图,确保正确地进行前向传播和反向传播。其模型是终端到终端的机器学习系统。一个典型的网络开始于数据层,结束于损失层,通过一个单一的开关,该网络运行在CPU或GPU上。

Caffe相对于TensorFlow等使用pip一键安装的方式来说,编译安装稍微麻烦一些。以Ubuntu 16.04为例,首先需要一些依赖库;完成之后,要去Git上将源代码复制到本地,配置好相关库的路径后就可以进行编译安装。对于GPU,还需要安装CUDA及NVIDIA驱动。

2.5.2 TensorFlow

TensorFlow是Google Brain推出的开源机器学习库,与Caffe一样,主要用作深度学习相关的任务。与Caffe相比,TensorFlow的安装简单得多,可以使用pip命令安装。

TensorFlow中的Tensor是张量,代表N维数组,与Caffe中的Blob类似;Flow即流,代表基于数据流图的计算。神经网络的运算过程,就是数据从一层流动到下一层的过程,Tensorflow更直接强调了这个过程。

TensorFlow最大的特点是计算图机制,即需要先定义好图,然后进行运算,所以所有的TensorFlow代码,都包含以下两部分。

第一部分,创建计算图。这是用于表示计算的数据流,实际上就是定义好了一些操作,可以将它看作Caffe中的Prototxt的定义过程。

第二部分,运行会话,执行图中的运算,可以看作Caffe中的训练过程。只是TensorFlow的会话比Caffe灵活很多,由于是Python接口,其取中间结果分析和调试非常方便。

TensorFlow也有内置的TF.Learn和TF.Slim等上层组件来帮助快速地设计新网络,并且兼容Scikit-learn Estimator接口,可以方便地实现Evaluate、Grid Search、Cross Validation等功能。同时,TensorFlow不只局限于神经网络,其数据流式图支持非常自由的算法表达,可以轻松实现深度学习以外的机器学习算法。

用户可以通过写内层循环代码控制计算图分支的计算,TensorFlow会自动将相关的分支转为子图并执行迭代运算。TensorFlow也可以将计算图中的各个节点分配到不同的设备执行,充分利用硬件资源。定义新的节点只需要写一个Python函数,如果没有对应的底层运算核,那么可能需要写C++或CUDA代码来实现运算操作。

2.5.3 PyTorch

Torch是纽约大学的一个机器学习开源框架,几年前在学术界非常流行。但是由于其只支持小众的Lua语言,因此没有普及。后来随着Python的生态越来越完善,Facebook人工智能研究院推出了PyTorch并开源。

PyTorch不是简单地封装Torch并提供Python接口,而是对Tensor以上的所有代码进行了重构,同TensorFlow一样,其增加了自动求导。PyTorch入门简单,上手快,堪比Keras;代码清晰,设计直观,符合人类直觉。其定位是用于快速实验研究,所以可以直接用Python写新的网络层。后来Caffe2全部并入PyTorch,其如今已经成了非常流行的框架。很多最新的研究如风格化、GAN等大多采用PyTorch源代码。

PyTorch的特点主要有以下两点。

第一,动态图计算。TensorFlow是采用静态图的,要先定义好图,然后在Session中运算。图一旦定义好后,是不能随意修改的。现在TensorFlow虽然也引入了动态图机制Eager Execution,只是不如PyTorch直观。TensorFlow要查看变量结果,必须在Session中,Session的角色仿佛是C语言的执行,之前的图定义是编译。而PyTorch就好像脚本语言,可随时随地修改,随处Debug,没有一个类似编译的过程,这比TensorFlow要灵活很多。

第二,简单。TensorFlow的学习成本不低,对于新手来说,Tensor、Variable、Session等概念充斥,数据读取接口频繁更新,tf.nn、tf.layers、tf.contrib各自重复,PyTorch则是从Tensor到Variable再到nn.Module,分别是从数据张量到网络的抽象层次的递进。

这几大框架都有基本的数据结构,Caffe是Blob,TensorFlow和PyTorch是Tensor,它们都是高维数组。PyTorch中的Tensor使用与Numpy的数组非常相似,两者可以互转且共享内存。PyTorch也为张量和Autograd库提供CUDA接口。使用CUDA GPU,不仅可以加速神经网络的训练和推断,还可以加速任何映射至PyTorch张量的工作负载。通过调用torch.cuda.is_available()函数,可检查PyTorch中是否有可用CUDA。

2.5.4 Theano

Theano由蒙特利尔大学的Lisa Lab团队开发并维护,是一个高性能的符号计算及深度学习库,适用于处理大规模神经网络的训练。其整合了Numpy,可以直接使用Ndarray等功能,无须直接进行CUDA编码即可方便地进行神经网络结构设计。因为核心是数学表达式编辑器,计算稳定性好,其可以精准地计算输出值很小的函数,如log(1+x)。

用户可自行定义各种运算,优化和评估多维数组的表达式,Theano可以将其编译为高效的底层代码,连接BLAS、CUDA等加速库,并自动求导,使用户省去了完全手工写神经网络反向传播算法的麻烦。Theano对卷积神经网络的支持很好,同时它的符号计算API支持循环控制(内部名scan),让RNN的实现非常简单且具有高性能,全面的功能也让Theano可以支持大部分state-of-the-art的网络。

虽然Theano支持Linux、Mac和Windows,但没有底层C++的接口,因此其模型的部署非常不方便,依赖各种Python库,并且不支持各种移动设备,所以几乎没有用在工业生产上。Theano在CPU上的执行性能比较差,但在单GPU上的执行效率不错,性能和其他框架类似。但是其运算时需要将用户的Python代码转换成CUDA代码,再编译为二进制可执行文件,编译复杂模型的时间非常久。此外,Theano在导入时也比较慢,而且一旦设定了选择某个GPU,就无法切换到其他设备。

2.5.5 Keras

Keras是一个高度模块化的神经网络库,使用Python实现,并可以同时运行在TensorFlow和Theano上;无须额外的文件来定义模型,仅通过编程的方式改变模型结构和调整超参数,旨在让用户进行最快速的原型实验,比较适合在探索阶段快速地尝试各种网络结构。其组件都是可插拔的模块,只需要将一个个组件(如卷积层、激活函数等)连接即可,但其设计新模块或新的层不大方便。在Keras中,通过几行代码就可实现MLP,AlexNet的实现也仅需十几行代码。

Theano和TensorFlow的计算图支持更通用的计算,而Keras则专精于深度学习,它同时支持卷积网络和循环网络,支持级联的模型或任意图结构的模型,从CPU上计算切换到GPU加速无须任何代码的改动。

因为底层使用Theano或TensorFlow,用Keras训练模型相比于前两者基本没有什么性能损耗(还可以享受前两者持续开发带来的性能提升),只是简化了编程的复杂度,节约了尝试新网络结构的时间。可以说模型越复杂,使用Keras的收益就越大,尤其是在高度依赖权值共享、多模型组合、多任务学习等模型上,Keras表现得非常突出。Keras也包括绝大部分最前沿的算法,如Adam、RMSProp、Batch Normalization、PReLU、ELU、LeakyReLU等。但是,目前Keras最大的问题是不支持多GPU,没有分布式框架。

2.5.6 MXNet

MXNet是DMLC(Distributed Machine Learning Community)开发的一款开源的、轻量级的、可移植的、灵活的深度学习库,它让用户可以混合使用符号编程模式和指令式编程模式来最大化效率和灵活性,目前已经是AWS官方推荐的深度学习框架。

MXNet是各个框架中率先支持多GPU和分布式的框架,同时其分布式性能也非常高。MXNet的核心是一个动态的依赖调度器,支持自动将计算任务并行化到多个GPU或分布式集群(支持AWS、Azure、Yarn等)。基于上层的计算图优化算法不仅加速了符号计算的过程,而且内存占用较小,在开启mirror模式之后,甚至可以在小内存的GPU上训练深层神经网络模型,同样可以在移动设备(Android、iOS)上运行基于深度学习的图像识别等任务。此外,MXNet支持多语言封装,基本涵盖了所有主流的脚本语言,如MATLAB、JavaScript、Julia、C++、Python、R语言等。虽然MXNet构造并训练网络的时间长于高度封装类框架,如Keras和PyTorch,但明显快于Theano框架。

2.5.7 Chainer

Chainer是一个由Preferred Networks公司推出并获得英特尔支援、专门为高效研究和开发深度学习算法而设计的开源框架。Chainer使用纯Python和NumPy提供命令式的API,为复杂神经网络的实现提供了更大的灵活性,加快了迭代速度,提高了实现深度学习算法的能力。

现在的复杂神经网络(如循环神经网络或随机神经网络)带来了新的性能改进和突破,虽然常用框架同样可以实现这些复杂神经网络,但编程非常冗余复杂,可能会降低代码的开发效率和可维护性。而Chainer的特点即在训练时“实时”构建计算图,正适合此类复杂神经网络的构建。这种方法可以让用户在每次迭代时对每个样本根据条件更改计算图。同时用户也很容易使用标准调试器和分析器来调试与重构基于Chainer的代码。

不同于其他基于Python接口的框架(如TensorFlow),Chainer通过支持兼容Numpy的数组间运算的方式,提供了声明神经网络的命令式方法。其可以通过反向追踪从最终损失函数到输入的完整路径来实现反向计算。完整路径可在执行正向计算的过程中存储,而不用预先定义计算图。标准神经网络的运算在Chainer类中是通过Link类,如线性全连接层和卷积层实现的。可以把Link看作其相应层的学习参数的一个函数,用户也可以创建一个包含许多其他Link的Link。这样的一个Link容器被命名为Chain。这使得Chainer将神经网络建模成一个包含多个Link和多个Chain的层次结构。Chainer还支持最新的优化方法、序列化方法及使用CuPy的由CUDA驱动的更快速计算。Chainer目前已在丰田汽车、松下和FANUC等公司投入工业使用。