Почему мы должны явно обнулять градиенты в PyTorch? Почему нельзя loss.backward()
градиенты при loss.backward()
? Какой сценарий выполняется, если оставить градиенты на графике и попросить пользователя явно обнулить градиенты?
Почему мы должны явно вызывать zero_grad()?
Ответ 1
Нам явно нужно вызвать zero_grad()
потому что после loss.backward()
(когда вычисляются градиенты) нам нужно использовать optimizer.step()
для продолжения спуска градиента. Более конкретно, градиенты не обнуляются автоматически, потому что эти две операции, loss.backward()
и optimizer.step()
, разделены, а optimizer.step()
требует только что вычисленных градиентов.
Кроме того, иногда нам нужно накапливать градиент между некоторыми партиями; чтобы сделать это, мы можем просто вызвать backward
несколько раз и оптимизировать один раз.
Ответ 2
У меня есть вариант использования для текущей настройки в PyTorch.
Если вы используете рекуррентную нейронную сеть (RNN), которая делает прогнозы на каждом шаге, вам может потребоваться гиперпараметр, позволяющий накапливать градиенты во времени. Не обнуление градиентов на каждом временном шаге позволяет использовать обратное распространение во времени (BPTT) интересными и новыми способами.
Если вам нужна дополнительная информация о BPTT или RNN, см. Статью " Учебник по рекуррентным нейронным сетям", часть 3 "Обратное распространение во времени и исчезающие градиенты" или "Неоправданная эффективность рекуррентных нейронных сетей".
Ответ 3
Оставлять градиенты на месте перед вызовом .step()
полезно, если вы хотите накапливать градиент в нескольких пакетах (как уже упоминали другие).
Это также полезно после вызова .step()
если вы хотите реализовать импульс для SGD, и различные другие методы могут зависеть от значений из предыдущего градиента обновления.
Ответ 4
В PyTorch есть цикл:
- Вперед, когда мы получим вывод или
y_hat
из ввода, - Расчет потерь, где
loss = loss_fn(y_hat, y)
-
loss.backward
когда мы вычисляем градиенты -
optimizer.step
когда мы обновляем параметры
Или в коде:
for mb in range(10): # 10 mini batches
y_pred = model(x)
loss = loss_fn(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
Если мы не очистим градиенты после optimizer.step
, который является подходящим шагом или непосредственно перед тем, как будут накапливаться следующие backward()
градиенты. Вот пример, показывающий накопление:
import torch
w = torch.rand(5)
w.requires_grad_()
print(w)
s = w.sum()
s.backward()
print(w.grad) # tensor([1., 1., 1., 1., 1.])
s.backward()
print(w.grad) # tensor([2., 2., 2., 2., 2.])
s.backward()
print(w.grad) # tensor([3., 3., 3., 3., 3.])
s.backward()
print(w.grad) # tensor([4., 4., 4., 4., 4.])
loss.backward()
не имеет никакого способа указать это.
torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None)
Из всех опций, которые вы можете указать, нет никакого способа обнулить градиенты вручную. Вот так в предыдущем мини-примере:
w.grad.zero_()
Были некоторые дискуссии о том, чтобы каждый раз выполнять zero_grad()
с помощью backward()
(очевидно, предыдущих градиентов) и сохранять грады с preserve_grads=True
, но это никогда не воплощалось в жизнь.