为什么我们需要在PyTorch中调用zero_grad() ?

为什么在训练期间需要调用zero_grad() ?

|  zero_grad(self)
|      Sets gradients of all model parameters to zero.
200152 次浏览

PyTorch中,对于培训阶段的每个小批处理,我们通常希望在开始反向传播(即更新<强> W < / >强8< >强b < / >强ias)之前显式地将梯度设置为零,因为PyTorch在随后的向后传递中累积梯度。这种累积行为在训练rnn时很方便,或者当我们想要计算多个mini-batches相加损失的梯度时。因此,在每次loss.backward()调用时,默认动作都被设置为累积(即求和)梯度

正因为如此,当你开始你的训练循环时,理想情况下你应该zero out the gradients,以便你正确地进行参数更新。否则,梯度将是旧梯度(您已经用于更新模型参数)和新计算的梯度的组合。因此,它将指向一些其他方向,而不是指向最低(或最大,在最大化目标的情况下)的预期方向。

这里有一个简单的例子:

import torch
from torch.autograd import Variable
import torch.optim as optim


def linear_model(x, W, b):
return torch.matmul(x, W) + b


data, targets = ...


W = Variable(torch.randn(4, 3), requires_grad=True)
b = Variable(torch.randn(3), requires_grad=True)


optimizer = optim.Adam([W, b])


for sample, target in zip(data, targets):
# clear out the gradients of all Variables
# in this optimizer (i.e. W, b)
optimizer.zero_grad()
output = linear_model(sample, W, b)
loss = (output - target) ** 2
loss.backward()
optimizer.step()

或者,如果你在做香草梯度下降,那么:

W = Variable(torch.randn(4, 3), requires_grad=True)
b = Variable(torch.randn(3), requires_grad=True)


for sample, target in zip(data, targets):
# clear out the gradients of Variables
# (i.e. W, b)
W.grad.data.zero_()
b.grad.data.zero_()


output = linear_model(sample, W, b)
loss = (output - target) ** 2
loss.backward()


W -= learning_rate * W.grad.data
b -= learning_rate * b.grad.data

请注意:

如果你使用梯度方法来减少错误(或损失),zero_grad()将从最后一步开始重新启动循环而不损失。

如果你不使用zero_grad(),损失将会增加而不是减少。

例如:

如果你使用zero_grad(),你会得到以下输出:

model training loss is 1.5
model training loss is 1.4
model training loss is 1.3
model training loss is 1.2

如果你不使用zero_grad(),你将得到以下输出:

model training loss is 1.4
model training loss is 1.9
model training loss is 2
model training loss is 2.8
model training loss is 3.5

虽然这个想法可以从选定的答案中推导出来,但我觉得我想把它写清楚。

能够决定何时调用optimizer.zero_grad()optimizer.step()为优化器在训练循环中如何积累和应用梯度提供了更多的自由。当模型或输入数据很大,并且一个实际的训练批次不适合gpu卡时,这是至关重要的。

在google-research的这个例子中,有两个参数,命名为train_batch_sizegradient_accumulation_steps

  • train_batch_size是向前传递的批处理大小,在loss.backward(). 0之后。这受到gpu内存的限制。

  • gradient_accumulation_steps是实际的训练批大小,其中累积了多个向前传递的损失。这是受gpu内存限制的

从这个例子中,你可以看到optimizer.zero_grad()后面可能跟着optimizer.step(),而后面跟着loss.backward()loss.backward()在每一次迭代中被调用(第216行),但是optimizer.zero_grad()optimizer.step()只在累积的列车批数等于gradient_accumulation_steps时被调用(第227行位于第219行中的if块内)。

https://github.com/google-research/xtreme/blob/master/third_party/run_classify.py

还有人问TensorFlow中的等效方法。我猜特遣部队。GradientTape起到同样的作用。

(我对AI库还是个新手,如果我说错了请指正)

你不需要调用grad_zero()或者你可以衰减渐变,例如:

optimizer = some_pytorch_optimizer
# decay the grads :
for group in optimizer.param_groups:
for p in group['params']:
if p.grad is not None:
''' original code from git:
if set_to_none:
p.grad = None
else:
if p.grad.grad_fn is not None:
p.grad.detach_()
else:
p.grad.requires_grad_(False)
p.grad.zero_()
                

'''
p.grad = p.grad / 2

这样学习就更容易继续

在前馈传播过程中,权重被分配给输入,在第一次迭代后,权重被初始化,模型已经看到样本(输入)。当我们开始反向传播时我们想要更新权重以使代价函数的损失最小。因此,我们清除之前的权重,以获得更多更好的权重。我们在训练中一直这样做,而在测试中不这样做,因为我们在训练时得到了最适合我们数据的权重。希望这能让你更清楚!

简单来说,我们需要ZERO_GRAD

,因为当我们开始一个训练循环时,我们不希望过去的梯度或过去的结果干扰我们当前的结果,因为PyTorch在反向传播中收集/累积梯度时是如何工作的,如果过去的结果可能会混淆并给我们错误的结果,所以我们每次经过循环时都将梯度设置为零。 这里有一个例子: ' < / p >

# let us write a training loop
torch.manual_seed(42)


epochs = 200
for epoch in range(epochs):
model_1.train()


y_pred = model_1(X_train)


loss = loss_fn(y_pred,y_train)


optimizer.zero_grad()


loss.backward()
< p > optimizer.step () ` 在这个for循环中,如果我们不将优化器设置为零,那么每次过去的值都会被加起来并改变结果。 所以我们使用zero_grad来避免错误的累积结果