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.
Explicação
Para redes neurais, geralmente usamos
loss
para avaliar o quão bem a rede aprendeu a classificar a imagem de entrada (ou outras tarefas). Oloss
termo geralmente é um valor escalar. A fim de atualizar os parâmetros da rede, precisamos calcular o gradiente deloss
wrt para os parâmetros, que está realmenteleaf node
no gráfico de computação (a propósito, esses parâmetros são principalmente o peso e o viés de várias camadas, como Convolução, Linear e em breve).De acordo com a regra da cadeia, para calcular o gradiente de
loss
wrt para um nó folha, podemos calcular a derivada deloss
wrt alguma variável intermediária, e o gradiente da variável intermediária wrt para a variável folha, fazer um produto escalar e somar tudo isso.Os
gradient
argumentos de umVariable
'sbackward()
método é usado para calcular uma soma ponderada de cada elemento de uma variável wrt a variável folha . Este peso é apenas a derivada final deloss
cada elemento da variável intermediária.Um exemplo concreto
Vamos dar um exemplo concreto e simples para entender isso.
No exemplo acima, o resultado do primeiro
print
éque é exatamente a derivada de z_1 em relação a x.
O resultado da segunda
print
é:que é a derivada de z_2 em relação a x.
Agora, se usar um peso de [1, 1, 1, 1] para calcular a derivada de z para x, o resultado é
1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Portanto, não é de surpreender que o resultado do terceiroprint
seja:Deve-se notar que o vetor de peso [1, 1, 1, 1] é exatamente derivado de
loss
wrt para z_1, z_2, z_3 e z_4. A derivada deloss
wrt tox
é calculada como:Portanto, a saída da 4ª
print
é a mesma da 3ªprint
:fonte
gradient
melhor o argumento. Obrigado pela sua resposta.[1, 1, 1, 1]
é exatamente derivado doloss
wrt paraz_1
,z_2
,z_3
ez_4
." Acho que essa afirmação é realmente a chave para a resposta. Ao examinar o código do OP, um grande ponto de interrogação é de onde vêm esses números arbitrários (mágicos) para o gradiente. Em seu exemplo concreto, acho que seria muito útil apontar a relação entre o[1, 0, 0 0]
tensor eg e aloss
função imediatamente para que possamos ver que os valores não são arbitrários neste exemplo.loss = z.sum(dim=1)
, ele se tornaráloss = z_1 + z_2 + z_3 + z_4
. Se você conhece cálculo simples, saberá que a derivada deloss
wrt toz_1, z_2, z_3, z_4
é[1, 1, 1, 1]
.Normalmente, seu gráfico computacional tem uma saída escalar, diz
loss
. Então você pode calcular o gradiente deloss
wrt os pesos (w
) porloss.backward()
. Onde o argumento padrão debackward()
é1.0
.Se sua saída tiver vários valores (por exemplo
loss=[loss1, loss2, loss3]
), você pode calcular os gradientes de perda em relação aos pesos porloss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))
.Além disso, se você quiser adicionar pesos ou importâncias a diferentes perdas, você pode usar
loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))
.Isso significa calcular
-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw
simultaneamente.fonte
grad_tensors
não é para pesá-los de forma diferente, mas eles são gradientes em relação a cada elemento dos tensores correspondentes.Aqui, a saída de forward (), ou seja, y é um 3-vetor.
Os três valores são os gradientes na saída da rede. Eles geralmente são definidos como 1.0 se y for a saída final, mas podem ter outros valores também, especialmente se y fizer parte de uma rede maior.
Por exemplo. se x é a entrada, y = [y1, y2, y3] é uma saída intermediária que é usada para calcular a saída final z,
Então,
Então, aqui, os três valores para trás são
e então backward () calcula dz / dx
fonte