【梯度下降】zero_grad (二)

参考链接:
https://www.jb51.net/article/189433.htm
https://www.jianshu.com/p/c59b75f1064c


一、zero_grad的用途

零梯度可改变:可利用requires_grad_()方法修改tensor的requires_grad属性.可以调用.detach()或with torch.no_grad():,姜不再计算张量的梯度,跟踪张量的历史记录.这点在评估模型、测试模型阶段中常常用到.

用法:

model.zero_grad()
optimizer.zero_grad()

二、zero_grad的原代码

model.zero_grad()的原代码

for p in self.parameters():
    if p.grad is not None:
        p.grad.detach_()
        p.grad.zero_() 

optimizer.zero_grad()的原代码

for group in self.param_groups:
    for p in group['params']:
        if p.grad is not None:
            p.grad.detach_()
            p.grad.zero_()

当使用optimizer = optim.Optimizer(net.parameters())设置优化器时,此时优化器中的param_groups等于模型中的parameters(),此时,二者是等效的,从二者的源码中也可以看出来。

  • 当多个模型使用同一个优化器时,二者是不同的,此时需要根据实际情况选择梯度的清除方式。
  • 当一个模型使用多个优化器时,二者是不同的,此时需要根据实际情况选择梯度的清除方式

三、在每轮训练的时候梯度清零的原因

首先了解pytorch的机制。在pytorch中有前向计算图反向计算图两个独立的机制。并且pytorch会默认对梯度进行累加默认累加的好处是当在多任务中对前面共享部分的tensor进行了多次计算操作后,调用不同任务loss的backward,那些tensor的梯度会自动累加,缺点是当你不想先前的梯度影响到当前梯度的计算时需要手动清零 。
总结知乎大佬的:
EPO1:

for idx, data in enumerate(train_loader):
    xs, ys = data
    pred1 = model1(xs)
    pred2 = model2(xs)
    

    loss1 = loss_fn1(pred1, ys)
    loss2 = loss_fn2(pred2, ys)
    
    ******
    loss = loss1 + loss2
    optmizer.zero_grad()
    loss.backward()
    ++++++
    optmizer.step()

从PyTorch的设计原理上来说,在每次进行前向计算得到pred时,会产生一个用于梯度回传的计算图,这张图储存了进行back propagation需要的中间结果,当调用了.backward()后,会从内存中将这张图进行释放上述代码执行到******时,内存中是包含了两张计算图的,而随着求和得到loss,这两张图进行了合并,而且大小的变化可以忽略执行到++++++时,得到对应的grad值并且释放内存。这样,训练时必须存储两张计算图,而如果loss的来源组成更加复杂,内存消耗会更大
作者:Forever123
链接:https://www.zhihu.com/question/303070254/answer/608153308
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. Edition2为了减小每次的内存消耗,借助梯度累加,又有 ,有如下变种
for idx, data in enumerate(train_loader):
    xs, ys = data
    
    optmizer.zero_grad()
    # 计算d(l1)/d(x)
    pred1 = model1(xs) #生成graph1
    loss1 = loss_fn1(pred1, ys)
    loss1.backward()  #释放graph1

    # 计算d(l2)/d(x)
    pred2 = model2(xs)#生成graph2
    loss2 = loss_fn2(pred2, ys)
    loss2.backward()  #释放graph2

    # 使用d(l1)/d(x)+d(l2)/d(x)进行优化
    optmizer.step()

可以从代码中看出,利用梯度累加,可以在最多保存一张计算图的情况下进行multi-task任务的训练。另外一个理由就是在内存大小不够的情况下叠加多个batch的grad作为一个大batch进行迭代,因为二者得到的梯度是等价的综上可知,这种梯度累加的思路是对内存的极大友好,是由FAIR的设计理念出发的。

我没看懂

上一篇:【梯度下降】原理和过程(一)

你可能感兴趣的:(资料积累,pytorch,python,随机梯度下降)