Le code original que je n'ai plus trouvé sur le site Web de PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Le problème avec le code ci-dessus, il n'y a pas de fonction basée sur quoi calculer les gradients. Cela signifie que nous ne savons pas combien de paramètres (arguments la fonction prend) et la dimension des paramètres.
Pour bien comprendre cela, j'ai créé un exemple proche de l'original:
Exemple 1:
a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)
print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])
J'ai supposé que notre fonction est y=3*a + 2*b*b + torch.log(c)
et que les paramètres sont des tenseurs avec trois éléments à l'intérieur.
Vous pouvez penser gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
à ce que c'est l'accumulateur.
Comme vous pouvez l'entendre, le calcul du système d'autogradation PyTorch est équivalent au produit Jacobien.
Au cas où vous auriez une fonction, comme nous l'avons fait:
y=3*a + 2*b*b + torch.log(c)
Jacobien serait [3, 4*b, 1/c]
. Cependant, ce Jacobien n'est pas la façon dont PyTorch fait les choses pour calculer les gradients à un certain point.
PyTorch utilise la différenciation automatique (AD) en mode passe avant et arrière en tandem.
Il n'y a pas de mathématiques symboliques impliquées et aucune différenciation numérique.
La différenciation numérique serait de calculer δy/δb
, pour b=1
et b=1+ε
où ε est petit.
Si vous n'utilisez pas de dégradés dans y.backward()
:
Exemple 2
a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward()
print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)
Vous simplement obtenir le résultat à un point, en fonction de la façon dont vous définissez votre a
, b
, c
tenseurs initialement.
Soyez prudent lorsque vous initialisez votre a
, b
, c
:
Exemple 3:
a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])
Si vous utilisez torch.empty()
et n'utilisez pas, pin_memory=True
vous pouvez avoir des résultats différents à chaque fois.
De plus, les dégradés de notes sont comme des accumulateurs, alors mettez-les à zéro si nécessaire.
Exemple 4:
a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward(retain_graph=True)
y.backward()
print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)
Enfin, quelques conseils sur les conditions d'utilisation de PyTorch:
PyTorch crée un graphe de calcul dynamique lors du calcul des gradients en passe avant. Cela ressemble beaucoup à un arbre.
Ainsi, vous entendrez souvent que les feuilles de cet arbre sont des tenseurs d'entrée et la racine est un tenseur de sortie .
Les dégradés sont calculés en traçant le graphique de la racine à la feuille et en multipliant chaque dégradé de la manière en utilisant la règle de la chaîne . Cette multiplication se produit lors de la passe arrière.
Explication
Pour les réseaux de neurones, nous utilisons généralement
loss
pour évaluer dans quelle mesure le réseau a appris à classer l'image d'entrée (ou d'autres tâches). Leloss
terme est généralement une valeur scalaire. Afin de mettre à jour les paramètres du réseau, nous devons calculer le gradient deloss
wrt aux paramètres, qui se trouveleaf node
en fait dans le graphe de calcul (au fait, ces paramètres sont principalement le poids et le biais de diverses couches telles que Convolution, Linear et bientôt).Selon la règle de la chaîne, afin de calculer le gradient de
loss
wrt à un nœud feuille, nous pouvons calculer la dérivée deloss
wrt une variable intermédiaire et le gradient de la variable intermédiaire par rapport à la variable feuille, faire un produit scalaire et additionner tout cela.Les
gradient
arguments deVariable
labackward()
méthode a sont utilisés pour calculer une somme pondérée de chaque élément d'une variable par rapport à la variable feuille . Ce poids n'est que la dérivée de la valeur finale deloss
chaque élément de la variable intermédiaire.Un exemple concret
Prenons un exemple concret et simple pour comprendre cela.
Dans l'exemple ci-dessus, le résultat du premier
print
estqui est exactement la dérivée de z_1 par rapport à x.
Le résultat du second
print
est:qui est la dérivée de z_2 par rapport à x.
Maintenant, si vous utilisez un poids de [1, 1, 1, 1] pour calculer la dérivée de z par rapport à x, le résultat est
1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Donc, sans surprise, la sortie de 3rdprint
est:Il convient de noter que le vecteur de poids [1, 1, 1, 1] est exactement dérivé de
loss
wrt en z_1, z_2, z_3 et z_4. La dérivée deloss
wrt tox
est calculée comme suit:Ainsi, la sortie du 4e
print
est la même que celle du 3eprint
:la source
gradient
mieux expliquer l' argument. Merci pour votre réponse.[1, 1, 1, 1]
est exactement dérivé deloss
WRT àz_1
,z_2
,z_3
etz_4
. » Je pense que cette déclaration est vraiment la clé de la réponse. Quand on regarde le code de l'OP, un grand point d'interrogation est d'où viennent ces nombres arbitraires (magiques) pour le dégradé. Dans votre exemple concret, je pense qu'il serait très utile de souligner tout de suite la relation entre le[1, 0, 0 0]
tenseur par exemple et laloss
fonction afin que l'on puisse voir que les valeurs ne sont pas arbitraires dans cet exemple.loss = z.sum(dim=1)
, cela deviendraloss = z_1 + z_2 + z_3 + z_4
. Si vous connaissez le calcul simple, vous saurez que la dérivée deloss
wrt àz_1, z_2, z_3, z_4
est[1, 1, 1, 1]
.En règle générale, votre graphe de calcul a une sortie scalaire dit
loss
. Ensuite, vous pouvez calculer le gradient deloss
wrt les poids (w
) parloss.backward()
. Où l'argument par défaut debackward()
est1.0
.Si votre sortie a plusieurs valeurs (par exemple
loss=[loss1, loss2, loss3]
), vous pouvez calculer les gradients de perte par rapport aux poidsloss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))
.De plus, si vous souhaitez ajouter des poids ou des importances à différentes pertes, vous pouvez utiliser
loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))
.Cela signifie calculer
-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw
simultanément.la source
grad_tensors
n'est pas de les peser différemment mais ce sont des gradients pour chaque élément des tenseurs correspondants.Ici, la sortie de forward (), c'est-à-dire y est un vecteur 3.
Les trois valeurs sont les gradients en sortie du réseau. Ils sont généralement définis sur 1.0 si y est la sortie finale, mais peuvent également avoir d'autres valeurs, surtout si y fait partie d'un plus grand réseau.
Pour par exemple. si x est l'entrée, y = [y1, y2, y3] est une sortie intermédiaire qui est utilisée pour calculer la sortie finale z,
Ensuite,
Alors ici, les trois valeurs à remonter sont
puis backward () calcule dz / dx
la source