点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”
作者:David Page
编译:ronghuaiyang
导读
这个系列介绍了如何在CIFAR10上高效的训练ResNet,到第4篇文章为止,我们使用单个V100的GPU,可以在79s内训练得到94%的准确率。里面有各种各样的trick和相关的解释,非常好。
我们建立了一个基线,在CIFAR10上训练Residual网络达到94%的测试精度,在单个V100 GPU上需要297秒。
我们复制了一个基线,在6分钟内训练CIFAR10,然后稍微加快速度——我们观察到,在GPU耗尽FLOPs之前,还有很大的改进空间。
在过去的几个月里,我一直在研究如何快速训练深层神经网络。或者至少没有那么慢。
我对这个问题的兴趣始于今年早些时候,当时我和Sam Davis在默特尔大学(Myrtle)合作进行一个项目。我们正在压缩大规模的循环网络,以便将自动语音识别部署到FPGAs上,并需要对模型进行再训练。Mozilla的基线实现花了一周时间在16个gpu上进行训练。Sam做了一些伟大的工作来消除瓶颈,并在Nvidia Volta GPU上转到混合精度计算之后,我们能够在一个GPU上将训练时间减少100倍以上,并减少迭代次数,将时间降低到一天以内。
这对我们能力的进步至关重要,让我想知道我们还能加快什么,以及这可能会带来什么应用。
大约在同一时间,斯坦福大学的人们也在思考类似的问题,并发起了DAWNBench竞赛,比较一系列深度学习基准的训练速度。最感兴趣的是训练图像分类模型的基准,在CIFAR10上测试准确率94%,在ImageNet上测试准确率前5名93%。图像分类是深度学习研究中一个特别受欢迎的领域,但最初并没有反映出在现代硬件上的最新实际应用,而是花费了很多个小时进行训练。
到今年4月比赛结束时,情况已经发生了变化,在CIFAR10上,最快的单GPU参赛选手(来自fast.ai的学生Ben Johnson在不到6分钟(341s)的时间内达到了94%的准确率。主要的创新是混合精度训练,选择一个较小的网络,有足够的能力完成任务,并使用更高的学习率来加快随机梯度下降(SGD)。
所以一个显而易见的问题是:341s在CIFAR10上训练到94%的测试准确度有多好?提交速度最快的网络是18层resnet网络,如下图所示。在这种情况下,层数是指(紫色)卷积和(蓝色)全连接层的串行深度,尽管这个术语并不是通用的:
该网络使用SGD进行了35个epoch的训练,其动量和略显奇怪的学习率策略如下:
让我们看看在这个设置中需要多长时间的训练,假设在一个NVIDIA Volta V100 GPU上有100%的计算效率 。在32×32×3的CIFAR10图像上通过网络的正向和反向传输大约计算量为2.8×109 FLOPs。假设参数更新计算基本上是不花时间的,那么在50,000个图像数据集上的35个训练周期应该在大约需要5×1015 FLOPs。
配备640个Tensor Cores,Tesla V100提供125 TeraFLOPS的深度学习性能
假设我们可以实现100%的计算效率,训练应该在……40秒内完成。即使在现实的假设下,341s的技术水平似乎仍有提高的空间。
所以有了目标,是时候开始训练了。第一项任务是使用上面的网络重现CIFAR10的基线结果。由于我们计划稍后进行更改,所以我在PyTorch中构建了一个版本的网络,并复制了来自DAWNBench提交的学习率策略和超参数。在一个 AWS p3.2×large instance上,使用一个V100 GPU进行训练,3/5的运行在356秒内达到94%的最终测试精度。
随着基线的适当复制,下一步是寻找可以立即执行的简单改进。第一个观察:网络在第一个(紫色)卷积后,接了两个连续(黄-红)batch-norm-ReLU组。这可能不是一个有意的设计,所以让我们删除重复。同样,epoch 15的学习速率也会出现奇怪的问题,尽管这不会影响训练时间。有了这些变化,网络和学习率看起来更简单,更重要的是,4/5运行达到94%的最终测试精度,时间为323秒!新纪录!
第二个观察:每次通过训练集时都需要进行一些图像预处理(填充、正常化和换位),但是每次都要重复这项工作。其他预处理步骤(随机裁剪和翻转)在不同的epoch之间是不同的,因此延迟使用这些步骤是有意义的。虽然通过使用多个CPU进程来完成这项工作可以减少预处理开销,但是PyTorch dataloader(从0.4版开始)为数据集的每次迭代启动新的进程。这方面的设置时间非常重要,尤其是在CIFAR10这样的小数据集上。通过在训练前做一次普通的工作,消除预处理工作的压力,我们可以将需要与GPU保持同步的进程减少到一个。在更重的任务中,需要更多的预处理或提供多个GPU,另一种解决方案可能是让dataloader进程在两个时点之间保持活动。在任何情况下,消除重复工作和减少dataloader进程的数量的效果是进一步节省了15秒的训练时间(几乎每个epoch半秒!),新的训练时间为308秒。
进一步挖掘发现,剩下的大部分预处理时间都花在调用随机数生成器来选择数据扩充,而不是扩充本身。在一次完整的训练运行中,我们向随机数生成器发出数百万个单独的调用,通过在每个epoch开始时将这些调用合并为少量的批量调用,我们可以再节省7秒的训练时间。最后,在这一点上,我们发现,即使启动一个进程来执行数据扩充,开销也超过了好处,我们可以通过在主线程上执行工作来节省更多的4s,从而导致今天的最终训练时间为297秒。复制这个结果的代码可以在这里找到:https://github.com/davidcpage/cifar10-fast/blob/master/ents.ipynb。
在第2部分中,我们增加了batch的大小。训练时间持续下降…
—END—
英文原文:https://myrtle.ai/how-to-train-your-resnet-1-baseline/
请长按或扫描二维码关注本公众号
喜欢的话,请给我个好看吧!