参考链接:
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
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
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的设计理念出发的。
我没看懂