Oryginalny kod, którego nie znalazłem już na stronie PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Problem z powyższym kodem nie ma funkcji opartej na tym, co obliczyć gradienty. Oznacza to, że nie wiemy, ile parametrów (argumentów przyjmuje funkcja) i jakie są wymiary parametrów.
Aby w pełni to zrozumieć, stworzyłem przykład zbliżony do oryginału:
Przykład 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])
Założyłem, że nasza funkcja jest, y=3*a + 2*b*b + torch.log(c)
a parametry to tensory z trzema elementami w środku.
Możesz pomyśleć o tym, gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
że to akumulator.
Jak słychać, obliczenia systemu PyTorch autograd są równoważne iloczynowi jakobian.
Jeśli masz funkcję, tak jak my:
y=3*a + 2*b*b + torch.log(c)
Jakobian byłby [3, 4*b, 1/c]
. Jednak ten jakobian nie jest tym, jak PyTorch robi rzeczy, aby obliczyć gradienty w pewnym momencie.
PyTorch wykorzystuje równolegle automatyczne różnicowanie w trybie do przodu i do tyłu (AD).
Nie ma tu żadnej symbolicznej matematyki ani numerycznego rozróżnienia.
Zróżnicowanie numeryczne należałoby obliczyć δy/δb
dla b=1
i b=1+ε
gdzie ε jest małe.
Jeśli nie używasz gradientów w y.backward()
:
Przykład 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.)
Będziesz prosty uzyskać wynik w punkcie, w oparciu o jak ustawić a
, b
, c
tensorów początkowo.
Bądź ostrożny, jak zainicjować a
, b
, c
:
Przykład 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])
Jeśli używasz torch.empty()
i nie używasz pin_memory=True
, możesz za każdym razem uzyskać inne wyniki.
Ponadto gradienty nut są jak akumulatory, więc zeruj je w razie potrzeby.
Przykład 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.)
Na koniec kilka wskazówek dotyczących terminów używanych przez PyTorch:
PyTorch tworzy dynamiczny wykres obliczeniowy podczas obliczania gradientów w biegu do przodu. To wygląda jak drzewo.
Dlatego często słyszysz, że liście tego drzewa są tensorami wejściowymi, a korzeń jest tensorem wyjściowym .
Gradienty są obliczane poprzez śledzenie wykresu od korzenia do liścia i mnożenie każdego gradientu na drodze za pomocą reguły łańcucha . To mnożenie następuje w przejściu wstecz.
Wyjaśnienie
W przypadku sieci neuronowych zwykle używamy
loss
do oceny, jak dobrze sieć nauczyła się klasyfikować obraz wejściowy (lub inne zadania).loss
Termin jest zazwyczaj wartość skalarna. Aby zaktualizować parametry sieci, musimy obliczyć gradientloss
wrt do parametrów, który faktycznie znajduje sięleaf node
na wykresie obliczeniowym (nawiasem mówiąc, te parametry to głównie waga i obciążenie różnych warstw, takich jak Convolution, Linear i wkrótce).Zgodnie z regułą łańcuchową, aby obliczyć gradient
loss
wrt do węzła liścia, możemy obliczyć pochodnąloss
wrt zmiennej pośredniej i gradient zmiennej pośredniej wrt do zmiennej liścia, wykonać iloczyn skalarny i zsumować wszystkie.Te
gradient
argumenty danejVariable
„sbackward()
metoda służy do obliczania sumy ważonej każdego elementu zmiennej wrt z liści zmienna . Ta waga jest po prostu pochodną końcowegoloss
wrt każdego elementu zmiennej pośredniej.Konkretny przykład
Aby to zrozumieć, weźmy konkretny i prosty przykład.
W powyższym przykładzie wynik pierwszego
print
toco jest dokładnie pochodną z_1 wrt do x.
Wynik drugiego
print
to:który jest pochodną z_2 wrt do x.
Teraz, jeśli użyjemy wagi [1, 1, 1, 1] do obliczenia pochodnej z wrt do x, otrzymamy wynik
1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Nic więc dziwnego, że wynik 3print
to:Należy zauważyć, że wektor wag [1, 1, 1, 1] jest dokładnie pochodną
loss
wrt do z_1, z_2, z_3 i z_4. Pochodnaloss
wrt tox
jest obliczana jako:Zatem wynik czwartego
print
jest taki sam jak trzeciegoprint
:źródło
gradient
lepiej wyjaśnić argument. Dziękuję za odpowiedź.[1, 1, 1, 1]
jest dokładnie pochodnaloss
wrt doz_1
,z_2
,z_3
iz_4
.” Myślę, że to stwierdzenie jest naprawdę kluczem do odpowiedzi. Patrząc na kod OP, duży znak zapytania wskazuje, skąd pochodzą te arbitralne (magiczne) liczby dla gradientu. Myślę, że w twoim konkretnym przykładzie bardzo pomocne byłoby od razu wskazanie relacji między np.[1, 0, 0 0]
Tensorem aloss
funkcją, aby można było zobaczyć, że wartości nie są w tym przykładzie dowolne.loss = z.sum(dim=1)
, stanie sięloss = z_1 + z_2 + z_3 + z_4
. Jeśli znasz prosty rachunek różniczkowy, będziesz wiedział, że pochodnąloss
wrt toz_1, z_2, z_3, z_4
jest[1, 1, 1, 1]
.Zwykle twój wykres obliczeniowy ma jedno wyjście skalarne
loss
. Następnie możesz obliczyć gradientloss
wrt weights (w
) wedługloss.backward()
. Gdzie domyślnym argumentembackward()
jest1.0
.Jeśli wynik ma wiele wartości (np.
loss=[loss1, loss2, loss3]
), Możesz obliczyć gradienty strat w stosunku do wagloss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))
.Ponadto, jeśli chcesz dodać wagi lub wagi do różnych strat, możesz użyć
loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))
.Oznacza to
-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw
jednoczesne obliczanie .źródło
grad_tensors
nie jest to, aby je ważyć inaczej, ale są to gradienty względem każdego elementu odpowiednich tensorów.Tutaj wynik forward (), tj. Y, jest 3-wektorowym.
Te trzy wartości to gradienty na wyjściu sieci. Zwykle są ustawione na 1,0, jeśli y jest końcowym wyjściem, ale mogą mieć również inne wartości, szczególnie jeśli y jest częścią większej sieci.
Np. jeśli x jest wejściem, y = [y1, y2, y3] jest wyjściem pośrednim, które jest używane do obliczenia końcowego wyniku z,
Następnie,
Więc tutaj trzy wartości do tyłu to
a następnie backward () oblicza dz / dx
źródło