El código original ya no lo he encontrado en el sitio web de PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
El problema con el código anterior es que no hay una función basada en qué calcular los gradientes. Esto significa que no sabemos cuántos parámetros (argumentos toma la función) y la dimensión de los parámetros.
Para entender completamente esto, creé un ejemplo cercano al original:
Ejemplo 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])
Supuse que nuestra función es y=3*a + 2*b*b + torch.log(c)
y los parámetros son tensores con tres elementos en su interior.
Puedes pensar en algo así gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
como en el acumulador.
Como puede escuchar, el cálculo del sistema de autogrado PyTorch es equivalente al producto jacobiano.
En caso de que tenga una función, como hicimos nosotros:
y=3*a + 2*b*b + torch.log(c)
Jacobian sería [3, 4*b, 1/c]
. Sin embargo, este jacobiano no es la forma en que PyTorch está haciendo las cosas para calcular los gradientes en cierto punto.
PyTorch utiliza la diferenciación automática (AD) del modo de avance y retroceso en conjunto.
No hay matemáticas simbólicas involucradas ni diferenciación numérica.
La diferenciación numérica sería calcular δy/δb
, para b=1
y b=1+ε
donde ε es pequeño.
Si no usa degradados en y.backward()
:
Ejemplo 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.)
Se le sencillo obtener el resultado en un punto, en base a la configuración de las a
, b
, c
tensores inicialmente.
Tener cuidado de cómo usted hace funcionar su a
, b
, c
:
Ejemplo 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])
Si usa torch.empty()
y no usa, pin_memory=True
es posible que obtenga resultados diferentes cada vez.
Además, los gradientes de notas son como acumuladores, así que ajústelos a cero cuando sea necesario.
Ejemplo 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, algunos consejos sobre los términos que usa PyTorch:
PyTorch crea un gráfico computacional dinámico al calcular los gradientes en el pase hacia adelante. Esto se parece mucho a un árbol.
Por lo tanto, a menudo escuchará que las hojas de este árbol son tensores de entrada y la raíz es un tensor de salida .
Los gradientes se calculan trazando el gráfico desde la raíz hasta la hoja y multiplicando cada gradiente en la forma usando la regla de la cadena . Esta multiplicación ocurre en el pase hacia atrás.