PyTorch中在反向传播前为什么要手动将梯度清零?

2019 年 1 月 23 日 极市平台

加入极市专业CV交流群,与6000+来自腾讯,华为,百度,北大,清华,中科院等名企名校视觉开发者互动交流!更有机会与李开复老师等大牛群内互动!

同时提供每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流点击文末“阅读原文”立刻申请入群~


作者:Pascal

原文:https://www.zhihu.com/question/303070254/answer/573037166


PyTorch中在反向传播前手动将梯度清零这种模式可以让梯度玩出更多花样,比如说梯度累加(gradient accumulation)


传统的训练函数,一个batch是这么训练的:

for i,(images,target) in enumerate(train_loader):
    # 1. input output
    images = images.cuda(non_blocking=True)
    target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
    outputs = model(images)
    loss = criterion(outputs,target)

    # 2. backward
    optimizer.zero_grad()   # reset gradient
    loss.backward()
    optimizer.step()            


  1. 获取loss:输入图像和标签,通过infer计算得到预测值,计算损失函数;

  2. optimizer.zero_grad() 清空过往梯度;

  3. loss.backward() 反向传播,计算当前梯度;

  4. optimizer.step() 根据梯度更新网络参数


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


使用梯度累加是这么写的:

for i,(images,target) in enumerate(train_loader):
    # 1. input output
    images = images.cuda(non_blocking=True)
    target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
    outputs = model(images)
    loss = criterion(outputs,target)

    # 2.1 loss regularization
    loss = loss/accumulation_steps   
    # 2.2 back propagation
    loss.backward()
    # 3. update parameters of net
    if((i+1)%accumulation_steps)==0:
        # optimizer the net
        optimizer.step()        # update parameters of net
        optimizer.zero_grad()   # reset gradient


  1. 获取loss:输入图像和标签,通过infer计算得到预测值,计算损失函数;

  2. loss.backward() 反向传播,计算当前梯度;

  3. 多次循环步骤1-2,不清空梯度,使梯度累加在已有梯度上;

  4. 梯度累加了一定次数后,先optimizer.step() 根据累计的梯度更新网络参数,然后optimizer.zero_grad() 清空过往梯度,为下一波梯度累加做准备;


总结来说:梯度累加就是,每次获取1个batch的数据,计算1次梯度,梯度不清空,不断累加,累加一定次数后,根据累加的梯度更新网络参数,然后清空梯度,进行下一次循环。


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


更新1:关于BN是否有影响,之前有人是这么说的:


As far as I know, batch norm statistics get updated on each forward pass, so no problem if you don't do .backward() every time.


BN的估算是在forward阶段就已经完成的,并不冲突,只是accumulation_steps=8和真实的batchsize放大八倍相比,效果自然是差一些,毕竟八倍Batchsize的BN估算出来的均值和方差肯定更精准一些。


更新2:根据@李韶华的分享,可以适当调低BN自己的momentum参数


bn自己有个momentum参数:x_new_running = (1 - momentum) * x_running + momentum * x_new_observed. momentum越接近0,老的running stats记得越久,所以可以得到更长序列的统计信息


我简单看了下PyTorch 1.0的源码:

https://github.com/pytorch/pytorch/blob/162ad945902e8fc9420cbd0ed432252bd7de673a/torch/nn/modules/batchnorm.py#L24,BN类里面momentum这个属性默认为0.1,可以尝试调节下。





*延伸阅读

算力限制场景下的目标检测实战浅谈

结构化数据的迁移学习:嫁接学习(分享竞赛大牛经验技巧)

有关语义分割的奇技淫巧有哪些?

图像分类算法优化技巧:Bag of Tricks for Image Classification


每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流点击左下角“阅读原文”立刻申请入群~

登录查看更多
39

相关内容

梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
【模型泛化教程】标签平滑与Keras, TensorFlow,和深度学习
专知会员服务
20+阅读 · 2019年12月31日
一文搞懂反向传播
机器学习与推荐算法
18+阅读 · 2020年3月12日
如何给你PyTorch里的Dataloader打鸡血
极市平台
15+阅读 · 2019年5月21日
PyTorch 学习笔记(七):PyTorch的十个优化器
极市平台
8+阅读 · 2019年5月19日
PyTorch 学习笔记(六):PyTorch的十七个损失函数
极市平台
47+阅读 · 2019年5月13日
目标检测中图像增强,mixup 如何操作?
极市平台
30+阅读 · 2019年2月5日
误差反向传播——CNN
统计学习与视觉计算组
30+阅读 · 2018年7月12日
【干货】基于Keras的注意力机制实战
专知
59+阅读 · 2018年5月4日
基于Numpy实现神经网络:反向传播
论智
5+阅读 · 2018年3月21日
一次 PyTorch 的踩坑经历,以及如何避免梯度成为NaN
Arxiv
5+阅读 · 2020年3月26日
Teacher-Student Training for Robust Tacotron-based TTS
Arxiv
4+阅读 · 2015年8月25日
VIP会员
相关资讯
一文搞懂反向传播
机器学习与推荐算法
18+阅读 · 2020年3月12日
如何给你PyTorch里的Dataloader打鸡血
极市平台
15+阅读 · 2019年5月21日
PyTorch 学习笔记(七):PyTorch的十个优化器
极市平台
8+阅读 · 2019年5月19日
PyTorch 学习笔记(六):PyTorch的十七个损失函数
极市平台
47+阅读 · 2019年5月13日
目标检测中图像增强,mixup 如何操作?
极市平台
30+阅读 · 2019年2月5日
误差反向传播——CNN
统计学习与视觉计算组
30+阅读 · 2018年7月12日
【干货】基于Keras的注意力机制实战
专知
59+阅读 · 2018年5月4日
基于Numpy实现神经网络:反向传播
论智
5+阅读 · 2018年3月21日
一次 PyTorch 的踩坑经历,以及如何避免梯度成为NaN
Top
微信扫码咨询专知VIP会员