资讯 人工智能开发者
此为临时链接,仅用于文章预览,将在时失效

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

作者:skura
2019/11/28 18:00

有一次,我在 twitter 上看到 Jeremy Howard 引用 Yann LeCun 关于 batch 大小的话:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

Twitter 上关于 batch 的讨论

自从我在 Fastai 找到了一个非常好的学习率查找工具后,我就一直在想这个问题,我一直在想是否有一个有用的 batch 大小查找工具,人们可以用来快速地得到一个合适的 batch 大小来训练他们的模型。

提醒一下,Fastai 中使用的学习速率查找器通过测试不同的学习速率来确定能最大程度地减少损失的数值,从而帮助找到正确的学习速率。更详细的解释可以在这里找到:https://sgugger.github.io/how-do-you-find-A-good-learning-rate.html

在我的脑海中,做一个 bacth 大小查找器的想法已经有很长一段时间了,在得到 Jeremy 里米的激励后,我决定开始这一旅程,实现一个 batch 大小查找器来训练神经网络。

今天我想和大家分享完成一篇论文的历程,在我看来,这些文章都很有趣,也许也会激励你去尝试更多的东西!

1.一个关于大小的故事

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

有关 batch 大小的 OC meme

一个常见的看法是不应该使用大 batch,因为这只会导致模型过大,并且可能会耗尽内存。显然这个观点是正确的,但前者比后者更复杂,为了回答这个问题,我们将深入研究 OpenAI 论文「An Empirical Model of Large-Batch Training」。

我非常推荐这篇文章,它解释了许多易于理解、记忆的想法。

首先,我们的目标是通过随机梯度下降法将损失最小化,并且有一个真正的潜在环境,我们将在这个环境下最小化损失。然而,我们不能访问整个数据集上的真实梯度,因此我们必须用有限的 batch 大小来近似梯度。

因为我们在一个 batch 上取平均值,如果我们的 batch 很小,就会有很多噪音存在,我们可能只在噪音上训练我们的模型。尽管如此,应用几个连续的更新是正确的策略,但我们也可以直接使用更大的批处理大小,它在计算效率上更高,并直接将噪声平均化。然而,在一个特定的大小之后,如果梯度已经是精确的,就没有必要使批处理更大,因为这只是在计算上的浪费,精度几乎没有提高。

此外,通过使用更大的 batch 尺寸(达到 GPU 允许的合理数量),我们加快了训练速度,这相当于采用了几个大步骤,而不是许多小步骤。因此,对于更大的 batch 尺寸,在相同的时间段,我们有时可以在计算时间上获得 2 倍的增益!

其次,有一个称为「简单噪音等级」的统计数据,它帮助我们确定什么是好的 batch 大小,定义为:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

简单噪声标度方程

G 是损失 L 在 n 个参数上的实际梯度。

如果我们使用比简单噪声尺度小的 batch,可以通过增加 batch 来加快训练速度,反之,如果我们使用比简单噪声尺度大的 batch,我们只会浪费算力。

为了进一步了解这一统计数字的含义,让我们研究每一个术语:

因此,我们的梯度越嘈杂,我们想要的 batch 越大,这是自然的,因为我们想要在正确的方向上采取梯度步骤。相反,如果梯度没有噪声,我们将从较少的步骤中获益更多,因为我们不需要平均出大量的观测值并分别使用它们。

另一方面,我们离最小值越近,batch 处理尺寸就越大,因为我们希望采取更谨慎的步骤,离局部最小值越近,因为我们不想超过它,错过正确的方向。

最后,简单的噪音量表为我们提供了一个工具来回答「较大的 batch 将使我们过度拟合,而较小的 batch 有助于规范化」的问题:

不一定!如果你的任务已经很复杂,并且近似的梯度很吵,你可能会感兴趣的是有一个更大的 batch 大小来确保你的模型没有训练太多的噪音。这并不是说一个较大的 batch 会使你过拟合,而是一个较小的 batch 会通过噪声注入增加更多的正则化。但是如果你不能正确拟合,你会添加正则化吗?

2.论文实践

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

现在我们已经了解了为什么选择正确的 batch 大小很重要,以及如何通过简单的噪声规模统计找到一个好的 batch 大小,现在是时候实现它了!

记住,简单的噪声比例方程是:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果




简单噪声标度方程

问题是,我们不仅需要知道实际的梯度,而且还需要知道这个梯度的方差,这就增加了难度。为了解决这个问题,作者提出了两种不同的统计方法来近似简单噪声尺度的分子和分母。

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

尺度估计量

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

噪声估计

在这里,我们使用两种不同的 batch 大小——B big 和 B small,使用以下公式计算实际梯度的两种不同估计:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

给定批大小的近似梯度

一旦我们有了这两个近似值,我们就可以用公式计算简单的噪声标度:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

简单噪声尺度的近似

为了确保该估计量具有很小的方差,作者在整个训练过程中计算了几个简单的噪声尺度估计量,并对其进行了平均。

如文中所述,一种自然的方法是利用多个 GPU 计算每个 GPU 的局部梯度,即小梯度,然后将其与不同 GPU 的平均梯度(即大梯度)进行比较。尽管如此,这个方法假设我们有多个 GPU,这不是我们大多数人所面临的情况。

因此,必须找到一种有效的方法来实现这种单一的 GPU。现在,我将和你们分享我如何解决这个问题的具体推理过程!

本文其余部分使用的代码可以在这里找到:https://colab.research.google.com/drive/15lTG_r03yqSwShZ0JO4XaoWix LMXMEmv        

在第一行代码中,我建立了一个 Fastai 环境,在 MNIST 上运行一个模型,因为这个数据集已经在论文中进行了测试,他们得到了平均 900 的简单噪声等级。

我不会太详细地解释这些代码,因为我需要一整篇文章来解释 Fastai 是如何将所有东西与其 API 结合在一起的,但是这些代码应该是一个好的开始。如果您想进一步帮助理解代码,请在注释中告诉我,我可以解释它,甚至可以写一篇关于编码部分的文章。

A. 使用指数移动平均的第一种方法

考虑到我在论文中提出的统计数字并不是很有用,我没有多 GPU ,所以我想我可以跳过它,直接计算方差,通过做近似计算:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

首先,我用给定的 batch 估计梯度近似实际梯度。

然后,当协方差矩阵的计算可以看作两个平均值时,我试图用指数移动平均来近似它,因为我不想在训练中储存许多梯度。

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果




计算出的批次上噪声和简单噪声刻度的运行平均值

正如你在这里看到的,结果很奇怪,简单的噪音等级太颠簸了,噪音比噪音大得多,这给了一个非常糟糕的简单噪音等级,没有意义。

B. 存储梯度

我们看到使用指数移动平均不是一个好的近似协方差矩阵的方法。

解决这个问题的另一种方法是简单地预先设置 n 个梯度保持,然后我们将简单地计算 n 个不同的梯度,并使用那些 n 个梯度来近似协方差矩阵。

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

它开始显示结果,但是它的计算非常棘手:X 轴是以这种方式存储的简单的噪声等级计算的 batch 数。虽然它似乎提供了某种结果,但在实践中是不可用的,因为我已经存储了数百个梯度!

C. 进行两次训练

在又一次失败后,我决定按照论文的思路,计算他们的两个统计数字。尽管如此,当我只有一个 GPU 的时候,我需要有一个方法在训练中得到两批不同尺寸的产品。

然后我想,为什么要做一个单一的训练,我实际上可以运行两个不同 batch 大小的训练,然后计算它?

所以我采用了这个想法,使用 B big=2 * B small,这将允许我计算它们各自的梯度,并使用它们以指数移动平均的方式计算 G 和 S,如本文所述。

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

和第一种方法一样,它产生了奇怪的结果!此外,当我思考这个问题时,我得到的 batch 可能在两次运行之间不一样,因为没有任何东西强迫小 batch 包含在大 batch 批中。另外,我需要运行两个训练阶段来计算这个,所以这个方法不是很好。

D. 连续批处理

最后,我意识到最好的方法似乎是第二种方法,但有些东西必须修改,因为我不想保留大量的梯度来计算统计数据。

然后,一个非常简单但有效的想法浮现在脑海中:如果我不是像论文中那样以并行方式平均几个 batch,而是以顺序方式平均连续的 batch 呢?

这就意味着我只需要设置一个参数,我调用 n_batch,这是在计算大小梯度之前我必须存储的 batch 数,然后我就可以按顺序计算论文的统计数据了!

这样实施之后,我得到了以下结果:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

结果很不错!在这篇论文中,他们认为增长趋势是可以预期的,因为噪声可能保持不变,而梯度的尺度将随着我们接近最小值而减小,这将导致一个越来越简单的噪声尺度。

因为我们很可能设置不同,我也无法访问他们的代码,所以我们的结果略有不同,但是在论文中,作者提到了一个简单的噪声等级,从 50 开始,达到 900,这才是重要的。考虑到在理论上和实践中起作用的许多近似值,结果可以变化,但是如文中所解释的,不应该有超过一个数量级的变化。

因此,经过这段漫长的旅程,似乎有一个正在起作用的实现。尽管论文对此几乎没有帮助,但最好的部分是,要在实践中使用它,你只需要一行代码!

这里的参数对应关系是:

3.在不同任务上测试 batch 大小查找器

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

是时候迈出大步了!

现在我们已经有了一个有用的实现,看看它在实践中如何辅助找到一个好的 batch 大小可能会很有趣。

首先,我们将研究 Rossmann 数据集。这个数据集已经在 Fastai courses v3 中进行了探索,你可以在这里找到它:https://github.com/Fastai/course-v3/blob/master/nbs/dl1/lesson6-rossmann.ipynb 

在这里,我将简单地运行我的 batch 大小查找器,并做与原来完全相同的训练,但 batch 大小要考虑到简单噪声比例。

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

现在如何解释?这意味着,对于给定的学习速率,训练似乎收敛到一个大约 500 的简单噪声标度,即噪声和标度在训练后期趋于稳定。因此,计算时间和效率之间的最佳折衷似乎是拥有 512 的 batch 大小。

在对 512 和 64 的 batch 进行相同的训练之后,我们可以观察到一些情况:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

batch 512的第一个单周期训练

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

批处理大小为 64 的第一个单周期训练

batch 大小为 512,训练速度比 batch 大小为 64 的快了近 4 倍!此外,尽管 batch 大小 512 采取的步骤较少,但最终它具有更好的训练损失和稍差的验证损失。

然后,如果我们看看每个 batch 的第二个训练周期损失:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

batch 大小为 512 的第二个单周期训练损失

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

批量为 64 的第二个单周期训练损失

在这里我们可以看到,与 batch 大小 512 相比,64 的训练要坎坷得多,后者的结果并不过分,因为验证损失继续减少。

最后,我们可以观察到最后一个训练周期的以下结果:

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

大小为 512 的 batch 最后一个单周期训练损失

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

batch 大小为 64 的最后一个单周期训练损失

最后,如果我们把 Rossmann 的结果加起来,使用 512 而不是 64 的 batch 大小:

我研究过文本和图像数据,但是考虑到它们要大得多,特别是预训练模型非常大,当我尝试用 batch 训练时,我使用了 CUDA,由于内存不足,所以我不会在这里显示结果,但你可以在 Colab Notebook 上查看。

结论

我们在这篇文章中看到了很多东西!我希望你喜欢这趟旅程,如果你有什么需要记住的话,那就是下面这些:

via:https://medium.com/@danielhuynh_48554/implementing-a-batch-size-finder-in-fastai-how-to-get-a-4x-speedup-with-better-generalization-813d686f6bdf 

雷锋网雷锋网雷锋网

长按图片保存图片,分享给好友或朋友圈

如何设置合适的 batch 大小收获 4 倍加速 & 更好的泛化效果

扫码查看文章

正在生成分享图...

取消
相关文章