Il codice originale non l'ho più trovato sul sito di PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Il problema con il codice sopra non esiste alcuna funzione basata su cosa calcolare i gradienti. Ciò significa che non sappiamo quanti parametri (argomenti accetta la funzione) e la dimensione dei parametri.
Per capirlo appieno ho creato un esempio vicino all'originale:
Esempio 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])
Ho assunto la nostra funzione y=3*a + 2*b*b + torch.log(c)
e i parametri sono tensori con tre elementi all'interno.
Puoi pensare a gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
come questo è l'accumulatore.
Come puoi sentire, il calcolo del sistema PyTorch autograd è equivalente al prodotto Jacobiano.
Nel caso tu abbia una funzione, come abbiamo fatto noi:
y=3*a + 2*b*b + torch.log(c)
Jacobian lo sarebbe [3, 4*b, 1/c]
. Tuttavia, questo Jacobiano non è il modo in cui PyTorch sta facendo le cose per calcolare i gradienti in un certo punto.
PyTorch utilizza la differenziazione automatica (AD) in modalità passata in avanti e all'indietro in tandem.
Non vi è alcuna matematica simbolica coinvolta e nessuna differenziazione numerica.
La differenziazione numerica sarebbe calcolare δy/δb
, per b=1
e b=1+ε
dove ε è piccolo.
Se non usi i gradienti in y.backward()
:
Esempio 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.)
Sarà semplice ottenere il risultato in un punto, in base a come si impostano i a
, b
, c
tensori inizialmente.
Fare attenzione a come si inizializza il vostro a
, b
, c
:
Esempio 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])
Se usi torch.empty()
e non usi pin_memory=True
potresti avere risultati diversi ogni volta.
Inoltre, i gradienti delle note sono come accumulatori, quindi azzerali quando necessario.
Esempio 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.)
Infine alcuni suggerimenti sui termini utilizzati da PyTorch:
PyTorch crea un grafico computazionale dinamico durante il calcolo dei gradienti nel passaggio in avanti. Questo assomiglia molto a un albero.
Quindi sentirai spesso che le foglie di questo albero sono tensori di input e la radice è un tensore di output .
I gradienti vengono calcolati tracciando il grafico dalla radice alla foglia e moltiplicando ogni gradiente nel modo in cui si utilizza la regola della catena . Questa moltiplicazione si verifica nel passaggio all'indietro.