O código original não encontrei mais no site do PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
O problema com o código acima não existe função baseada no que calcular os gradientes. Isso significa que não sabemos quantos parâmetros (argumentos que a função usa) e a dimensão dos parâmetros.
Para entender isso, criei um exemplo próximo ao original:
Exemplo 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])
Presumi que nossa função é y=3*a + 2*b*b + torch.log(c)
e os parâmetros são tensores com três elementos dentro.
Você pode pensar gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
que este é o acumulador.
Como você pode ouvir, o cálculo do sistema autogradado de PyTorch é equivalente ao produto Jacobiano.
Caso você tenha uma função, como nós:
y=3*a + 2*b*b + torch.log(c)
Jacobian seria [3, 4*b, 1/c]
. No entanto, este Jacobiano não é como PyTorch está fazendo as coisas para calcular os gradientes em determinado ponto.
O PyTorch usa a diferenciação automática (AD) de modo de avanço e retrocesso em conjunto.
Não há matemática simbólica envolvida e nenhuma diferenciação numérica.
A diferenciação numérica seria calcular δy/δb
, para b=1
e b=1+ε
onde ε é pequeno.
Se você não usa gradientes em y.backward()
:
Exemplo 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.)
Você vai simples obter o resultado em um ponto, com base em como você definir suas a
, b
, c
tensores inicialmente.
Tenha cuidado como você inicializa o seu a
, b
, c
:
Exemplo 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 você usar torch.empty()
e não usar, pin_memory=True
poderá ter resultados diferentes a cada vez.
Além disso, gradientes de nota são como acumuladores, então zere-os quando necessário.
Exemplo 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.)
Por último, algumas dicas sobre os termos que o PyTorch usa:
O PyTorch cria um gráfico computacional dinâmico ao calcular os gradientes na passagem para frente. Isso se parece muito com uma árvore.
Portanto, você ouvirá frequentemente que as folhas desta árvore são tensores de entrada e a raiz é o tensor de saída .
Os gradientes são calculados traçando o gráfico da raiz até a folha e multiplicando todos os gradientes no caminho usando a regra da cadeia . Essa multiplicação ocorre na passagem para trás.