Pourquoi devons-nous explicitement mettre à zéro les gradients dans PyTorch? Pourquoi les gradients ne peuvent-ils pas être mis à zéro lorsque loss.backward()
est appelé? Quel scénario est servi en conservant les gradients sur le graphique et en demandant à l'utilisateur de mettre à zéro explicitement les gradients?
Réponses
Trop de publicités?Nous avons explicitement besoin d'appeler zero_grad()
parce que, après l' loss.backward()
(lorsque les gradients sont calculés), nous avons besoin d'utiliser optimizer.step()
pour passer de descente de gradient. Plus précisément, les gradients ne sont pas automatiquement mis à zéro, parce que ces deux opérations, loss.backward()
et optimizer.step()
, sont séparés, et optimizer.step()
nécessite la juste calculé dégradés.
En outre, parfois, nous avons besoin d'accumuler de dégradé entre certains lots; pour ce faire, il suffit de l'appeler backward
plusieurs fois et d'optimiser la fois.
Il y a un cycle dans PyTorch:
- Avant quand nous arrivons à la sortie ou à l'
y_hat
à partir de l'entrée, - Le calcul de la perte où
loss = loss_fn(y_hat, y)
-
loss.backward
lorsque nous calculer les gradients -
optimizer.step
lorsque nous mettons à jour les paramètres
Ou le code:
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()
Si nous ne serait pas clair dégradés après l' optimizer.step
, ce qui est l'étape appropriée ou juste avant le prochain backward()
gradients permettrait de s'accumuler.
Voici un exemple montrant l'accumulation:
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()
n'ont aucun moyen de définir ce.
torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None)
De toutes les options que vous pouvez spécifier on n'est pas à zéro les gradients manuellement. Comme ça dans les précédents mini exemple:
w.grad.zero_()
Il y a des discussions sur le fait de faire zero_grad()
à chaque fois avec backward()
(évidemment précédente dégradés) et de garder les diplômés avec preserve_grads=True
, mais ce n'est jamais venu à la vie.
J'ai un cas d'utilisation pour la configuration actuelle dans PyTorch.
Si l'on utilise un réseau de neurones récurrent (RNN), qui est de faire des prédictions, à chaque étape, on peut avoir envie d'avoir un hyper-paramètre qui permet d'accumuler des gradients dans le temps. Pas de réinitialisation de la dégradés à chaque pas de temps permet une utilisation de back-propagation à travers le temps (BPTT) intéressante et originale.
Si vous souhaitez plus d'info sur BPTT ou RNNs voir l'article de Réseaux de Neurones Récurrents Tutoriel, Partie 3 – Les à Travers le Temps et la Disparition des Gradients ou de La Déraisonnable Efficacité des Réseaux de Neurones Récurrents.
Laisser les dégradés en place avant d'appeler .step()
est utile si vous souhaitez accumuler le dégradé sur plusieurs lots (comme d'autres l'ont mentionné).
C'est également utile pour après avoir appelé .step()
si vous souhaitez mettre en œuvre l'élan pour SGD, et diverses autres méthodes peuvent dépendre des valeurs du dégradé de la mise à jour précédente.