Batch size
Batch size大小对训练过程的收敛性,以及训练模型的最终准确性具有关键影响。
Batch size较大时可能使模型陷入局部最优,
小的Batch size会导致模型收敛速度慢,由于数据中必定存在噪音,当batch size越小,来自于噪音的梯度就越多,梯度估计的准确度就越低。也就是说,较小的batch size可能会使学习过程波动性更大,延长算法收敛的时间。
训练模型过程中,batch size越大对GPU的内存容量要求越高。训练过程中,显存中包含以下内容:
- 模型参数:模型中的权重和偏差
- 优化器变量:优化器算法所需要的变量
- 中间计算变量:网络模型计算产生的中间值会产生临时的内存,如每层激活的输出
- 工作区Workspace:计算过程中产生的中间变量,如D=A+B/C中B/C的结果
因此,Batch size越大,训练神经网络需要的样本越多,导致需要存储在AI芯片内存变量激增,则对GPU的显存要求越高。
如何使用较大的batch size
为解决GPU显存限制的问题,将运行大Batch size的一种方法是将Sample的Batch拆分为更小的Batch,称之为Mini-Batch,可以通过两种方式使用Mini-batch来完成模型训练任务:数据并行和梯度累积。
- 数据并行:在多个GPU上训练模型,每个GPU上都有一份Mini-batch数据,在Epoch结束时梯度求和并更新网络参数。
- 梯度累积:按顺序执行Mini-Batch,同时对梯度进行累积,累积的结果在Mini-Batch计算后求平均更新模型变量。
两者的区别在于数据并行使用的是多卡环境,当仅有一块GPU的时候可以使用梯度累积。
梯度累计原理
训练时,数据输入模型,中间层对数据进行相应计算,最终输出预测值,通过损失函数计算每个样本的损失值。通过反向传播,计算损失值相对于模型参数的梯度,最后将这些梯度信息用于网络模型中的参数进行更新。以SGD为例,利用loss函数来更新模型参数:
\theta_i = \theta_(i-1) - lr \times grad_i
其中\theta是网络模型中的可训练参数,lr为学习率,grad是参数相对于损失函数的梯度。这种更新模型参数的方式可以理解为“实时的”,计算完成后当即更新模型参数。但梯度累积并不会及时更新网络参数,而是在计算过程中累积计算时得到的梯度,最后统一使用累积的梯度来对参数进行更新。
比如说,现在要训练某神经网络,将数据分为若干Batch,但由于硬件环境受限无法直接将Batch输入模型,所以使用梯度累积的方式训练网络模型。将每个Batch分为若干mini-batch,每个mini-batch计算完毕后记录梯度信息,当前batch都计算完毕后,使用Batch中所有Mini-batch的梯度信息来对模型中的参数进行更新。算法上等价于没有切分的batch的训练方式。
在实际工程当中,关于调参和算法上需要注意两天:
- 一定条件下,batch size越大训练效果越好,梯度累积则模拟了batch size增大而效果。如果accumulation steps为4,则相当于将batch size 增大4倍,此时需要把学习率适当调大。
- Batch size放大4倍时和真实batch的数据分布并不完全相同,4倍batch的BN计算出来的方差和均值与原始batch的方差和均值不太相同,因此有些实现中会使用Group Norm来代替Batch Norm
代码实现
正常batch训练模型的过时如下所示。
1 | for i, (images, labels) in enumerate(train_data): |
即输入一个batch的数据,计算一次梯度,当即更新换一次网络,使用梯度累的实现方式如下所示。
1 | # 梯度累加参数 |
DataParallel
数据并行(DataParallel,DP),用于单机多卡的环境,所有卡都负责计算和训练模型,与此同时,device[0]还负责整合梯度,更新参数。主要过程包含三个部分,如图所示。

- 各卡分别计算损失和梯度(红色部分)
- 所有梯度整合到device[0](蓝色部分)
- device[0]进行参数更新,其他卡拉取device[0]的参数进行更新(绿色部分)
虽然DP只能实现单机训练不能算是严格意义上的分布式训练,但其原理和分布式训练算法里的Parameter Server架构很相近,PS的架构如下。
PS架构由server节点和worker节点组成。server借钱的主要功能是初始化和保存模型参数、接收worker节点计算出的局部梯度、汇总计算全局梯度,并更新模型参数
worker节点的主要功能是各自保存部分训练数据,初始化模型,从server节点拉取最新的都模型参数,根据训练数据计算局部梯度,上传给server节点。 - PS架构下的DP,会造成负载不均衡,因为充当server的GPU需要一定的显存用来保存worker节点计算出的局部梯度,另外还需要将更新后的模型参数传递给每个worker,server的带宽会成为通信瓶颈,server与worker之间的通信成本会随着worker数目的增加而线性增加。
PS的并行梯度下降流程分为4个部分。 - Task Scheuler:负责加载数据并分发数据至各个worker节点,进行多轮迭代。
- 在每轮迭代中,worker负责:
- 初始化:载入数据并从server节点拉取全部模型参数
- 梯度计算:利用该节点的数据计算梯度,并将梯度传输到server节点
- Server负责:
- 汇总梯度
- 更新参数
以上就是DP所使用的算法。