1新智元编译
来源:Github;Medium
作者:Ilia Karmanov
编译:文强
【新智元导读】微软数据科学家Ilia Karmanov做了一个项目,使用高级API测试8种常用深度学习框架的性能(因为Keras有TF,CNTK和Theano,所以实际是10种)。Karmanov希望这个项目能够帮助数据科学家更好地跨框架进行深度学习。
Karmanov表示,他这个项目的目标是创建一个深度学习框架的罗塞塔石碑,让数据科学家能够轻松地将他们在一个框架上的专长转移到另一个框架上(而不是从头开始学习)。具体说,就是在8种最常用的框架上构建同一个神经网络。而且,有了这样一个模型后,也就有了比较各个框架的基准,各个框架的训练时间和默认选项也变得更加透明。
Karmanov发现,许多在线教程都使用低级别的API,虽然写很详细,但对于大多数用例而言,除非想要创建新的层,否则意义不大。因此,他在项目中使用了最高级别的API,以便更容易地在框架之间进行比较。
结果证明,一旦使用更高级的API,不同框架的代码结构实际上变得非常相似,并且可以粗略地表示为:
加载数据;x_train,x_test,y_train,y_test = cifar_for_library(channel_first=?, one_hot=?)
生成CNN符号(通常在最后的dense层上没有激活)
指定损失(交叉熵与softmax一起),优化和初始化权重 + session
使用custom迭代器训练训练集中的mini-batch
从测试集中预测全新的mini-batch
评估准确性
本质上这里是在对一系列确定的数学运算(尽管是随机初始化的)进行比较,因此在结果中比较各个框架的准确性并没有什么意义。Karmanov将精度作为一个去匹配(而非对比)的指标,确保比较的是相同的模型架构。
结果:Caffe2、MXNet表现不俗
硬件是Nvidia K80 GPU,环境是Ubuntu,所有框架都是最新版本:
但是,比较训练速度意义不大
虽然对10大常用框架进行了对比,但Karmanov进一步在他的Medium文章里解释了,比较速度并没有意义:
使用本地数据加载器可能会省掉几秒钟(实际上意义也不大),因为shuffle是异步执行的。但是,对于常规项目(而不是这里的这个实验),数据不太可能放进RAM里,还需要大量的预处理和数据增强。这就是数据加载器派上用场的地方。Karmanov表示,Facebook的贾扬清对他的这一项目给予了很多帮助,贾扬清告诉他,Facebook的好几个in-production网络,最大瓶颈都是I/O,如果想要实现一流的性能,贾扬清建议最好使用异步I/O,这样会有很大的帮助。
其次,Karmanov在这个示例中只使用了几层(conv2d,max_pool2d,dropout,全连接层)。平时工作中的项目可能会有3D卷积、GRU,LSTMS等。
你能很轻松地添加自定义的层,这些层的运行速度会影响你选择用什么框架。所以在这里比较速度也没多大用。能够用Python代码编写一个自定义层并快速执行它才是研究项目的关键
在实际应用中,你会用到TensorBoard这样的高级日志来查看模型是否收敛,帮助调整超参数。但在这个例子中并不涉及。
10点心得
以下是Karmanov在所有框架上匹配训练精度,参考Github上所有issue/Pull request后的一些见解:
1. 上面的框架(除了Keras),为了方便比较,都尝试使用相同级别的API,所以都使用相同的生成函数。对于MXNet和CNTK,我尝试了一个更高级别的API,使用框架的训练生成器函数。这个例子中速度的提升是可以忽略的,因为整个数据集作为NumPy数组加载到RAM中,每个epoch完成的处理是就是一次shuffle。我怀疑框架的生成器运行了异步shuffle。奇怪的是,NXNet和CNTK似乎在batch级别而不是observation级别上shuffle,因此测试精度稍微降低(至少在10个epoch之后)。如果有IO,或者有预处理和数据增强的情况,custom生成器对性能的影响将会更大。
2. 启用CuDNN的auto-tune/穷举搜索参数(为固定大小的图像选择最有效的CNN算法)能够大幅提升性能。在Caffe2,PyTorch和Theano,这个功能得手动开启。CNTK,MXNet和Tensorflow则是默认启用这项功能。Chainer是什么情况我还不清楚。扬清说,cudnnGet(默认)和cudnnFind之间的性能提升在Titan X GPU上要小得多;在这里,K80 +新的cudnn看来使问题更加突出了。在目标检测时,不论组合为何,运行cudnnFind都严重影响了性能回归,所以在目标检测时应该禁用exhaustive_search
3. 使用Keras时,选择与后端框架匹配的[NCHW]排序非常重要。CNTK是最先是针对通道(channel)运算的,但我不小心把Keras配置为最后用通道了。结果每个批次都必须改变顺序,严重降低了性能。
4. Tensorflow,PyTorch,Caffe2和Theano需要向pooling层提供一个布尔值,表示有没有在训练(这对测试精度有很大影响,72%比77%)。在这种情况下,不应该使用dropout来进行测试。
5. TensorFlow有点烦人,需要做两个调整:启用TF_ENABLE_WINOGRAD_NONFUSED,以及将维度最先(而不是最后)提供给通道(data_format='channels_first')可以提升速度。启用WINOGRAD进行卷积,当然也可以提升TF做后端的Keras
6. 对大多数函数而言,Softmax通常跟cross_entropy_loss() 捆绑在一起,如果你在最后的全连接层上需要激活,最好检查一下,这样可以节省时间避免做两次
7. 不同框架的内核初始化函数可能会有所不同(我发现这对准确性有+/- 1%的影响),只要有可能我都会指定xavier/glorot
8. SGD-momentum的实现,我需要关闭unit_gain(在CNTK是默认打开的)来匹配其他框架的实现
9. Caffe2对网络的第一层(no_gradient_to_input = 1)有一个额外的优化,通过不计算输入的梯度,使速度有一个较小的提升。很可能,TensorFlow和MXNet是默认启用这项功能的。但是,对于一些研究目的和像DeepDream这样的项目,计算这个梯度可能还是有用的
10. 在max-pooling之后(而不是之前)应用ReLU激活意味着在降维后执行计算,这会省掉几秒钟的时间。在MXNet上面,这减少了3秒钟的时间
Karmanov对整个开源社区,包括Caffe2作者、Facebook的贾扬清表示感谢,他在Github写了更多心得体会,并欢迎你提供提升训练时间的建议:
Github地址:https://github.com/ilkarman/DeepLearningFrameworks
Medium文章:https://medium.com/@iliakarmanov/neural-net-in-8-frameworks-lessons-learned-6a5e8e78b481
【号外】新智元正在进行新一轮招聘,飞往智能宇宙的最美飞船,还有N个座位
点击阅读原文可查看职位详情,期待你的加入~