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.