梯度累积是什么?显存不够时用小批次模拟大批次训练
梯度累积(Gradient Accumulation)是一种在显存有限的情况下,模拟大批次训练效果的技巧。它把一个大的训练批次分成几个小批次,按顺序依次运行,把每个小批次计算出的梯度累加起来,等到累积了足够多的小批次后,才用累加的梯度更新一次模型参数。
先回顾一下训练过程
深度学习模型的训练是一个反复迭代的过程。每一步大致是这样的:取一批训练样本(一个 batch),让它们通过模型,得到预测结果,这叫前向传播;把预测和正确答案比较,计算出损失;再通过反向传播,计算出损失对每个参数的梯度,也就是每个参数应该往哪个方向调整、调整多少;最后,优化器根据梯度更新模型的参数。
每一批用多少个样本,叫作批次大小(batch size)。批次大一些通常有好处:用更多样本估计出的梯度更准确、更稳定,训练过程更平稳;很多模型的训练方法和超参数,也是按照较大的批次大小设计的。但批次越大,前向和反向传播时需要保存的中间结果越多,需要的显存也越多。当模型很大、或者每个样本很长时,显卡的显存可能连一个中等大小的批次都装不下。
梯度累积怎么做
假设我们希望用 64 个样本的批次来训练,但显存最多只能放下 16 个样本。梯度累积的做法是:取 16 个样本做前向和反向传播,算出梯度,但先不更新参数,而是把梯度存起来;再取下一组 16 个样本,算出梯度,加到之前存下的梯度上;如此重复 4 次,累积了相当于 64 个样本的梯度之后,再执行一次参数更新,然后把累积的梯度清零,开始下一轮。在数学上,这和直接用 64 个样本算梯度的效果基本是等价的(需要注意把损失或梯度按累积的次数做相应的平均)。
这样,用有限的显存,就达到了使用大批次训练的效果。代价是时间:原本一次能算完的批次,现在要分 4 次依次计算,训练速度会变慢,但可以在资源有限的情况下完成原本做不到的训练。
注意事项和应用
需要注意的是,有些依赖批次统计信息的操作,比如批归一化(BatchNorm),在梯度累积时看到的仍然只是小批次,可能和真正的大批次效果不同;不过大语言模型通常使用层归一化,不受这个问题影响。梯度累积是训练大模型时非常常用的技巧,在大规模分布式训练中,它也经常和数据并行等方法结合使用,比如多张显卡各自处理一部分数据,再配合梯度累积,凑出所需的全局批次大小。PyTorch、Hugging Face 等主流训练框架都提供了现成的梯度累积设置。