pytorch 梯度累积(gradient accumulation)

梯度累积 - gradient accumulation

在深度学习训练的时候,数据的batch size大小受到GPU内存限制,batch size大小会影响模型最终的准确性和训练过程的性能。在GPU内存不变的情况下,模型越来越大,那么这就意味着数据的batch size只能缩小,这个时候,梯度累积(Gradient Accumulation)可以作为一种简单的解决方案来解决这个问题。

梯度累积(Gradient Accumulation)是一种不需要额外硬件资源就可以增加批量样本数量(Batch Size)的训练技巧。这是一个通过时间换空间的优化措施,它将多个Batch训练数据的梯度进行累积,在达到指定累积次数后,使用累积梯度统一更新一次模型参数,以达到一个较大Batch Size的模型训练效果。累积梯度等于多个Batch训练数据的梯度的平均值

所谓梯度累积过程,其实很简单,我们梯度下降所用的梯度,实际上是多个样本算出来的梯度的平均值,以batch_size=128为例,你可以一次性算出128个样本的梯度然后平均,我也可以每次算16个样本的平均梯度,然后缓存累加起来,算够了8次之后,然后把总梯度除以8,然后才执行参数更新。当然,必须累积到了8次之后,用8次的平均梯度才去更新参数,不能每算16个就去更新一次,不然就是batch_size=16了。

传统的深度学习

for i, (inputs, labels) in enumerate(trainloader):
    optimizer.zero_grad()                   # 梯度清零
    outputs = net(inputs)                   # 正向传播
    loss = criterion(outputs, labels)       # 计算损失
    loss.backward()                         # 反向传播,计算梯度
    optimizer.step()                        # 更新参数
    if (i+1) % evaluation_steps == 0:
        evaluate_model()

具体流程:

  1. optimizer.zero_grad(),将前一个batch计算之后的网络梯度清零
  2. 正向传播,将数据和标签传入网络,过infer计算得到预测结果
  3. 根据预测结果与label,计算损失值
  4. loss.backward() ,利用损失进行反向传播,计算参数梯度
  5. optimizer.step(),利用计算的参数梯度更新网络参数

简单的说就是进来一个batch的数据,计算一次梯度,更新一次网络。

梯度累积方式

for i, (inputs, labels) in enumerate(trainloader):
    outputs = net(inputs)                   # 正向传播
    loss = criterion(outputs, labels)       # 计算损失函数
    loss = loss / accumulation_steps        # 梯度均值,损失标准化
    loss.backward()                         # 梯度均值累加,反向传播,计算梯度
    
	# 累加到指定的 steps 后再更新参数
	if (i+1) % accumulation_steps == 0:     
        optimizer.step()                    # 更新参数
        optimizer.zero_grad()               # 梯度清零
        if (i+1) % evaluation_steps == 0:
            evaluate_model()

具体流程:

  1. 正向传播,将数据传入网络,得到预测结果
  2. 根据预测结果与label,计算损失值
  3. 利用损失进行反向传播,计算参数梯度
  4. 重复1-3,不清空梯度,而是将梯度累加
  5. 梯度累加达到固定次数之后,更新参数,然后将梯度清零

梯度累积时,每个batch 仍然正常前向传播以及反向传播,但是反向传播之后并不进行梯度清零,因为 PyTorch 中的backward() 执行的是梯度累加的操作,所以当我们调用N次 loss.backward() 后,这N个batch 的梯度都会累加起来。但是,我们需要的是一个平均的梯度,或者说平均的损失,所以我们应该将每次计算得到的 loss除以 accum_steps。

        总结来讲,梯度累积就是每计算一个batch的梯度,不进行清零,而是做梯度的累加,当累加到一定的次数(accumulation_steps)之后,再更新网络参数,然后将梯度清零。
        通过这种参数延迟更新的手段,可以实现与采用大batch size相近的效果。在平时的实验过程中,我一般会采用梯度累加技术,大多数情况下,采用梯度累加训练的模型效果,要比采用小batch size训练的模型效果要好很多。

注意事项:

  • 一定条件下,batchsize越大训练效果越好,梯度累加则实现了batchsize的变相扩大,如果accumulation_steps为8,则batchsize '变相' 扩大了8倍,是实验室解决显存受限的一个不错的trick,使用时需要注意,学习率也要适当放大

  • 15
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PyTorch中的梯度累积是指在训练过程中,将多个小批量数据的梯度进行累加,而不是每次反向传播后自动清零梯度。这个特性可以通过调用`loss.backward()`来实现,但在梯度累积时需要手动将梯度清零。 梯度累积的好处在于可以在内存有限的情况下使用更大的批量大小,从而提高模型的训练效果。另外,梯度累积还能够支持多任务训练,因为在多任务中共享的张量的梯度会自动累加。 具体实现梯度累积的代码示例如下: ``` optimizer.zero_grad() # 将梯度清零 for i, data in enumerate(train_loader): inputs, labels = data # 前向传播 outputs = model(inputs) loss = criterion(outputs, labels) # 反向传播 loss.backward() if (i+1) % accum_steps == 0: # 每经过 accum_steps 个小批量数据进行一次梯度更新 optimizer.step() # 更新参数 optimizer.zero_grad() # 将梯度清零 ``` 在这个示例中,我们在每经过 `accum_steps` 个小批量数据时进行一次参数更新,并在更新之后将梯度清零。这样就实现了梯度累积的效果。需要注意的是,`accum_steps`需要根据具体的情况进行调整,以平衡内存占用和训练效果。 参考资料: PyTorch默认会对梯度进行累加。即,PyTorch会在每一次backward()后进行梯度计算,但是梯度不会自动归零,如果不进行手动归零的话,梯度会不断累加。 梯度累积时,每个batch仍然正常前向传播以及反向传播,但是反向传播之后并不进行梯度清零,因为PyTorch中的backward()执行的是梯度累加的操作,所以当我们调用N次loss.backward()后,这N个batch的梯度都会累加起来。 在PyTorch的设计原理上,利用梯度累加可以在最多保存一张计算图的情况下进行多任务的训练。另外一个理由是在内存不足的情况下,可以叠加多个batch的梯度作为一个大batch进行迭代。由于PyTorch的动态图和autograd机制,设置梯度为0比较复杂。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值