MENU

【神经网络】有关batch_size选取的一些想法

June 2, 2021 • 神经网络

本文主要记述了深度学习中 batch_size 这一超参数的选取以及一些其他经验


注意:本文所针对的是 SGD 来说,Adam 等方式不在讨论的范围之内,另外似乎我并没有见到有关 Adam 收敛性的有关证明,如果 Adam 其实已经被证明了,烦请给菜狗提个醒,菜狗不胜感激 QAQ。


前言

前一阵想到了个不错的点子,准备水个论文(不是)。大概的方向是用特殊的网络结构来缩减运算量而不影响精度,经过理论上的推断觉得可行后,在几周前,我开始着手网络结构的搭建和测试。令人感到困惑的是,test 集的 acc 始终上不去(后来证明是数据集的问题,不过那是后话了)。当时的我将注意力放在了超参数上面,而作为最好调的超参数没有之一,batch_size 自然就成了我关注的第一个参数,由此引发了后面的一系列问题。

一些问题

事情的起因是我看到了知乎上的一篇回答,它的中心思想是

也就是最好的实验表现都是在 batch size 处于 2~32 之间得到的,这和最近深度学习界论文中习惯的动辄上千的 batch size 选取有很大的出入。

这个想法让我有点迷糊,诚然,现在已经证明了过大的 batch_size 对训练神经网络无益,在训练神经网络的过程中,我们需要小一些的 batch_size 来引入一些必要的噪声,以此来帮助神经网络摆脱鞍点,提升网络的泛化性能。并且受到算力的影响,90% 的用户是没有办法拿出足够的显存来运算上千的 batch_size。但我依然对 2-32 大小这个结果抱有疑问,在我看来,太小的 batch_size 同样存在致命的问题。一方面,过小的 batch_size 引入的噪声可能过大,这并不是一个好消息。噪声小,数据整体分布较稳定,这是 BN 运行良好的条件。过大的噪声会导致是神经网络产生震荡,甚至是无法收敛。另一方面,过小的 batch_size 并不能充分发挥你显卡的性能,并且让整个网络并行度降到非常低的水平。用人话说就是训练过程十分漫长且折磨人(事实上我已经被折磨了,详见后文)。这些都是无法避免的问题。

我又检索了一下,事实上网络上用很小很小的 batch_size 的声音还是有一些的,他们中的大多数都指向了一篇文章:Revisiting Small Batch Training for Deep Neural Networks 。这篇文章详细的论述了在 SGD 下小 batch 所体现出的优越性以及一些理论证明。但实验部分我还是存疑的,这实验结果我并不敢苟同,遇到类数多的,噪声较大的数据集,感觉 32-2 大小的 batch_size 甚至并不能收敛。我觉得不能一味听信论文瞎扯,有时候还是得自己试试 23333

实验

由于是学生党,实验设备着实有限。在这里我用到了两张 3090 和两张 RTX TITAN 来进行实验,3090 跑一遍,TITAN 再来一遍。

数据集我选用了 cifar-10 作为数据集,这个数据集类数相对较少,且噪声相对稳定。在论文中其也是用到了这一数据集作为一组实验数据,优化器当然使用了 SGD,每 40 代将学习率乘 0.2,每个不同的 batch_size 都使用了他们自己的最优学习率。每个训练跑 200 个 epoch。

epochbatch_sizebatch_size512batch_size128batch_size32batch_size2
epoch200.83300.82270.84870.5849
epoch400.91800.88200.90140.7467
epoch600.91310.89040.90060.7589
epoch800.92500.89200.90960.7852
epoch1000.92690.89380.91320.7921
epoch1200.93190.89320.9146-
epoch1400.93340.89380.9129-
epoch1600.93230.89460.9140-
epoch1800.93170.89500.9149-
epoch2000.93160.89630.9154-
平均一个 epoch 用时110 s145 s310 s3200 s

TITAN 与 3090 跑出来的结果高度一致。可以看到,过小的 batch_size 效果并没有得到多少改善,甚至略有缩水。其间,我观察到了多次小 batch 下 test 集合的 acc 反复震荡, loss 函数也一直不能很好的改善的情况,这确实和我预想的一致。至于 batch 为 2 的实验组,它确实让我感受到了折磨(笑),按照计划,我设置了每个实验组跑 200 个 epoch,512 与 128 组在 24 小时之内就跑了个差不多,一天后 32 组也跑完了全部内容,而截止到这篇文章发布,batch 为 2 的这个实验组只跑了 113 代(已经过去 6 天了,我预计大概 11 天能跑完),并且出现了根本无法收敛的情况,test 的 acc 反复震荡。而且这个用时,我觉得是非常不友好的,至少我认为不是大多数人愿意看到的用时(笑)。

可以预见的是,如果数据变得更加复杂(比如数据集变成类数更多的 cifar100 或者其他噪声更大的数据集),batch_size 为 32 的实验组大概也会变得难以收敛,这是非常不好的。相对来说,可能稍微大一些的 batch_size 确实会更加稳定。

一些总结和建议

  • 在你的显卡不是特别强劲的情况下,请优先考虑把你的显卡盛满
  • 当你的显卡比较优异,可以容纳较大的 batch_size 时,请优先考虑 128 或者 256 大小的 batch_size。在我学习深度学习时跑过的诸多模型中,这两个值是我觉得还不错的。在此基础上,如果你觉得并不理想,请乘二或者除二,再来试试看。或者如果没有那么大的内存以及时间充裕的话,64 或者 32 也可能会是不错的想法,但是不要再小了。
  • 当然,小 batch 也有小 batch 的好处。在训练完一个模型后,用很小的 batch_size 再跑一跑,没准会有很好的效果(比如多了一个点的准确率),但是千万千万不要直接跑小 batch,这绝对是致命的。
Last Modified: June 7, 2021
Leave a Comment

4 Comments
  1. 实验设备着实有限。在这里我用到了两张 3090 和两张 RTX TITAN 来进行实验

    好家伙,重新定义装备有限。之前我用1060跑NLP那个东西,数据集是整个中文维基百科,勉勉强强。

    从直观上理解的话,我个人觉得BatchSize决定了单个样本影响整体参数变化的权重。BatchSize小,说明每一个样本产生的误差能够更有力的修改;BatchSize大,那多个样本产生的误差一平均,每个样本产生的影响就小了(假如样本是乱序的)。

    对于那个NLP,我的策略是先去外面自费租一个显存大点的服务器,然后BatchSize给到256或者384这样子,把整个数据集跑三遍,然后换成相对便宜一点的机器,用内存跑128,最后把模型下载到自己电脑上,用验证集跑个16或者32精调一下。但是由于用的是RNN(主要还是BiLSTM),所以显卡的加成不大,而且生成的结果比较一般。

    1. @天空Blond你的策略确实是正确的,极小的batch放在最后确实能提升一部分正确率,个人感觉小batch的意义所在就是引入了噪声这一不确定性以此来摆脱鞍点。(当然前提是你已经用大batch达到了鞍点附近)。我记得看过一篇论文,证明了学习率和batch_size确实有一定的线性关系。
      另外如果使用cpu来进行训练的话,我似乎记得cpu还是比较讨厌2的幂次方的batch的,建议换个整数(笑),事实上只要初期收敛到一定程度,直接上小batch也没有问题。直接省掉中间128的步骤多跑几遍256,正确率上升不那么剧烈了直接上32,也是不错的选择。

  2. 另外减少运算量的话,之前听说谷歌用超大的ReLU结构完成了复杂的事情,并总结说只要网络足够宽,就算是单纯线性的激活函数也能完成复杂的事情。但至于有多宽,考虑到谷歌能搞出BERT,可能真就是得集群了。现实一点说,要在小规模网络上减少计算量,除了激活函数本身避免使用复杂的浮点运算之外,使用通用的预训练层也能在某种程度上减少运算量,比如词向量对于广泛的NLP任务都比较有用,以及CNN那些预训练的卷积核之类的。

    至于从网络结构入手的话,莫非是复用某一些层?

    1. @天空Blond我理解的减少运算量主要有两个方向,减少整个模型的大小以及运用更好的网络结构。前几年的话减少整个模型的运算量(模型剪枝)是一个比较火的方向,大家都在通过减少特征的维度或者减少卷积层的计算量来进行计算量的缩减。不过近几年这个方向热度衰减蛮快的,大家发现与其减少一部分参数,直接使用更好的网络结构会更加简单有效,比如我最前一阵近看的可微分搜索,大体上来说它预置了很多小的block,神经网络需要找到最合适的block组合来构建自身,然后训练这个刚刚搭好的,最适合这个机器或者这个训练任务的模型,这也算是最近的热点了吧。