从Caffe2到TensorFlow,构建同一个神经网络Network的十个框架的效率比较,比较了目前排名前十的深度学习框架,其中Caffe2和MXNet在准确率和训练时间上都处于领先地位。该项目甚至得到了FAIR研究人员和主要框架创始人(如贾扬清)的支持。机器之心整理了这篇文章。项目GitHub链接:https://github.com/ilkarman/DeepLearningFrameworks除了所有技术元素外,我发现这个项目最有趣的事情是来自开源社区的惊人贡献。Pullrequests,社区提出的问题,对于在准确性和训练时间上统一所有框架非常有帮助。看到FAIR研究人员、框架的创建者(如贾扬清)和GitHub的其他用户所做的贡献,我感到很震惊。没有他们,这个项目就不会完整。他们不仅提供代码建议,还提供针对不同框架的完整笔记本。您可以在此处查看项目的初始状态,然后再进行贡献:https://github.com/ilkarman/DeepLearningFrameworks/tree/0143957489e8adbecaa975f9b541443421db5c4bIssuesSearchingforTensorflow+MNIST出现了这个看似复杂的教程,它绕过了更高级的API(tf.layers或tf.nn),并且似乎与输入数据没有充分解耦,因此使用CIFAR(例如)而不是MNIST更舒服。一些教程有MNIST的自定义包装器,例如framework.datasets.mnist,以避免MNIST的冗长加载,但我对此有两个问题:初学者可能不太了解如何在数据上重新运行。将它与另一个框架进行比较可能会更棘手(预处理会有所不同吗?)其他教程将MNIST作为文本文件(或自定义数据库)保存到磁盘,然后使用TextReaderDataLoader再次加载它。这个想法展示了如果用户有一个太大而无法加载到RAM中并且需要大量即时转换的大数据集会发生什么。对于初学者来说,这可能具有误导性和恐吓性;我经常被问到,“当我有一个数组时,为什么我需要保存它!”目标本文的目标是如何使用10个***框架(在一个常见的自定义数据集上)构建相同的神经网络——深度学习框架的RosettaStone,让数据科学家能够在框架之间利用他们的专业知识(通过翻译而不是从头开始学习)。跨不同框架使用相同模型的一个结果是框架在训练时间和默认选项方面变得更加透明,我们甚至可以比较特定元素。能够快速将您的模型转换为另一个框架意味着您可以交换帽子。如果另一个框架有一个层需要您从头开始编写,以更有效的方式处理数据资源,或者使其与运行的平台(例如Android)更加兼容。对于这些教程,我尝试使用最先进的API,而不考虑违反默认选项,以便更容易比较不同的框架。这意味着笔记本不是为了速度而写的。这将证明,如果使用更高级别的API,代码结构会变得相似,并且可以粗略地描述为:将数据加载到RAM中;x_train,x_test,y_train,y_test=cifar_for_library(channel_first=?,one_hot=?)到内存;x_train,x_test,y_train,y_test=cifar_for_library(channel_first=?,one_hot=?)生成CNN符号(通常在最密集的层上没有激活)指定损失(交叉熵通常与softmax相关),优化小批量训练带有自定义迭代器的训练集(所有框架的通用数据源)对测试集的小批量进行预测,可能为层指定测试标记(例如dropout)评估准确性考虑因素我们实际上是在比较一组特定的数学运算(尽管初始化是相当随意的),所以比较框架的准确性没有意义,相反我们希望匹配框架的准确性以确保我们正在比较相同的模型架构进行比较。我之所以说比较速度毫无意义:使用数据加载器可以(仅)节省几秒钟,因为洗牌应该异步执行。然而,对于一个合适的项目,您的数据不太可能适合RAM,并且可能需要大量的预处理和操作(数据扩充)。这就是数据加载器所做的。贾扬清认为:我们在多个网络中都经历过主要的I/O瓶颈,所以告诉人们如果他想要最好的性能就使用异步I/O会有很大帮助。本例中仅使用了几个层(conv2d、max_pool2d、dropout、全连接)。对于合适的项目,您可能有3D卷积、GRU、LSTM等。添加自定义层的便利性(或k***池或分层softmax等层的可用性)以及它们运行的??速度可以使或打破你的框架选择。能够用Python代码编写自定义层并快速执行它对于研究项目至关重要。LSTM(GRU)onCIFAR-10onVGG-styleCNNIMDBexperience(matchingaccuracy/time)的结果下面是我的匹配多帧测试准确率,以及基于从GitHub上收集的issues/PR的一些要点。1.为了便于比较,以上示例(Keras除外)使用了相同级别的API和相同的生成器函数。我对MXNet和CNTK的实验使用了更高级别的API,在该API之上使用了框架的训练生成器函数。此实例中的加速几乎可以忽略不计,因为整个数据集作为NumPy数组加载到RAM中,并且每个epoch完成的唯一处理是洗牌。我怀疑框架的生成器也在异步进行洗牌。奇怪的是,框架似乎是在批级别而不是观察级别进行洗牌,因此测试准确率略低(至少在10个epoch之后)。在框架运行时发生IO活动、预处理和数据扩充的场景中,自定义生成器对性能的影响更大。2.启用CuDNN的自动调整/穷举搜索参数(为固定大小的图像选择最高效的CNN算法)将大大提高性能。在Caffe2、PyTorch和Theano中,必须手动启用。在CNTK、MXNet和Tensorflow中,这是默认完成的。我不确定Chainer的情况如何。贾扬清提到cudnnGet(默认)和cudnnFindi之间的性能增益小于TitanXGPU;似乎K80+newcudnn让这个问题更加突出。在对象检测的每个比例连接上运行cudnnFind会引入严重的性能回归,但是,正因为如此,可以在对象检测上禁用exhaustive_search。3、使用Keras时,一定要选择与后端框架相匹配的[NCHW]排序。CNTK首先使用通道运行,我错误地将Keras配置为使用通道。之后,Keras必须每批次更改顺序,这会导致严重的性能损失。4.Tensorflow、PyTorch、Caffe2和Theano需要向池化层提供一个布尔值来指示我们是否正在训练(这对测试准确率有巨大影响,72%对77%)。5.Tensorflow有点麻烦,需要修改两处:启用TF_ENABLE_WINOGRAD_NONFUSED提高速度;首先更改通道的维度,而不是最后更改它(data_format=”channels_first”)。当TF用作后端时,在卷积层上启用WINOGRAD也自然会提高Keras性能。6.对于大多数函数,Softmax通常与cross_entropy_loss()绑定。有必要检查是否需要激活最全连接层以节省应用两次激活的时间。7.内核初始化程序在不同帧中发生变化(我看到+/-1%对准确性的影响),我试图在可能/不是很长的情况下指定统一的xavier/gloro。8.SGD动量实现的动量类型。我不得不关闭unit_gain(默认情况下仅在CNTK中打开),以匹配其他框架的实现。9.Caffe2需要在网络的第一层进行额外的优化(no_gradient_to_input=1),通过不计算输入的梯度产生小的加速。Tensorflow和MXNet可能已经默认启用了此功能。计算梯度对于搜索和深度梦想网络很有用。10.在池化之后(而不是之前)应用ReLU激活意味着你在降维之后执行计算并节省时间。这有助于将MXNet时间缩短3秒。11.一些可能有用的进一步检查:Specifyingkernelas(3)becomessymmetrictuple(3,3)or1Dconvolution(3,1)?stride(forpooling)默认为(1,1),或者等同于kernel(Keras会这样做)?默认填充通常关闭(0,0)/有效,但有助于检查它是否打开/“相同”卷积层上的默认激活是“无”或“ReLu”(烤宽面条)?有偏见的初始程序可能会改变(有时没有任何偏见)。梯度截断和inifinty/NaNs处理在不同的框架中可能会有所不同。一些框架支持稀疏标签而不是单热标签(例如Tensorflow中的f.nn.sparse_softmax_cross_entropy_with_logits)。数据类型假设可能有所不同:我为X、y尝试了float32和int32。但是,例如,torch需要将y加倍(转换为torch.LongTensor(y).cuda)如果框架API级别有点低,请确保您在测试期间不要通过training=False等来计算梯度。原文:https://medium.com/@iliakarmanov/neural-net-in-8-frameworks-lessons-learned-6a5e8e78b481【本文为机器之心专栏原文翻译,微信公众号《机器之心》(id:almosthuman2014)》】点此阅读本作者更多好文
