Soup's Blog

Back

模型训练(四)梯度累计Gradient AccumulationBlur image

梯度累计(Gradient Accumulation)是一种在显存受限情况下模拟大批次(large batch size)训练的技术。它的核心思想是:用多次小 batch 的前向/反向计算,累积梯度,对梯度除以 batch size得到平均梯度,然后一次性更新模型参数,从而在不增加显存占用的前提下,获得大 batch 训练的优化效果。

通常,深度学习训练中:前向传播 + 反向传播 会为一个 batch 计算梯度;优化器立即用该梯度更新模型参数。但当 batch size 很大时,中间激活和梯度会占用大量显存,可能超出 GPU 显存。当batch size很小,比如为1时不会超过GPU显存,它会每训练一个样本,然后计算一次梯度,由于样本之间的差异很大,导致更新的梯度忽大忽小不可控,导致训练过程不稳定、收敛缓慢,甚至无法收敛。

例如,下面是一个前向传播的计算图: 在这里插入图片描述 其中,a,b,c都是参数,他们会在反向传播的过程中会进行更新。更新的过程如下: 在这里插入图片描述 通过链式法则,可以得到损失函数对参数b的梯度,然后b-lr*该梯度作为新的b,再继续进行前向传播、反向传播更新,ac同理。在这个过程中,模型的参数例如a,b,c等,称为叶子节点张量,其梯度会被累积到.grad属性中,并长期驻留现存,直到下一次optimizer.zero_grad()清空,对于中间非叶子张量,例如v,其值等于v=b*c,其梯度默认不会保留,计算完后立即释放。

首先准备训练集和标签:

x,y=sklearn.datasets.load_digits(return_X_y=True)
x=torch.tensor(x/16).float().cuda() # FP32
y=torch.tensor(y).long().cuda()
print(x.shape,x.dtype)
print(y.shape,y.dtype)
bash

定义一个模型:

class MLP(torch.nn.Module):
    def __init__(self,input_size,hidden_size,output_size):
        super(MLP,self).__init__()
        self.fc1=torch.nn.Linear(input_size, hidden_size)
        self.fc2=torch.nn.Linear(hidden_size, output_size)

    def forward(self,x):
        out=self.fc1(x)
        out=torch.relu(out)
        out=self.fc2(out)
        return out
bash

接下来实现梯度累计:

accum_steps用于梯度累计计数,其值为4时,更新一次参数。需要注意loss=loss/4,这是因为前面累积了4个batch的梯度,在更新的时候,应该要取batch size的均值进行更新。使用loss=loss/4并不影响计算图,可以理解为在loss后面增加new_loss=loss*(1/4),当求梯度d_new_loss/d_loss时,值就是1/4,从而实现对loss乘以1/4

模型训练(四)梯度累计Gradient Accumulation
http://www.soupcola.top/blog/distri_trainning/distri_trainning-4
Author Soup Cola
Published at 2026年2月6日