Я пытаюсь полностью понять автоград pytorch, и я наткнулся на это: пусть f(x)=x, из базовой математики мы знаем, что f'(x)=1, однако, когда я делаю это упражнение в pytorch, я получаю это f' (х) = х.
z = torch.linspace(-1, 1, steps=5, requires_grad=True)
y = z
y.backward(z)
print("Z tensor is: {} \n Gradient of y with respect to z is: {}".format(z, z.grad))
Я бы ожидал получить тензор размера 5, полный 1, но вместо этого я получаю:
Z tensor is: tensor([-1.0000, -0.5000, 0.0000, 0.5000, 1.0000], requires_grad=True)
Gradient of y with respect to z is: tensor([-1.0000, -0.5000, 0.0000, 0.5000, 1.0000])
Почему это поведение pytorch?
После обсуждения с коллегой он обнаружил, что метод «назад()» на самом деле умножает градиент, оцененный в точке z, на саму z. Это имеет смысл для приложений нейронных сетей. Краткий фрагмент кода, чтобы понять это, выглядит следующим образом:
z = torch.linspace(1, 5, steps=5, requires_grad=True)
y = torch.pow(z,2)
y.backward(z)
print("Z tensor is: {} \n Gradient of y with respect to z is: {}".format(z, z.grad/z))
Результат:
Z tensor is: tensor([1., 2., 3., 4., 5.], requires_grad=True)
Gradient of y with respect to z is: tensor([ 2., 4., 6., 8., 10.], grad_fn=<DivBackward0>)
В этом случае вы можете видеть, что z.grad, деленное на z, является фактическим ожидаемым градиентом z, который будет равен 2*z.
Прежде всего, учитывая z = torch.linspace(-1, 1, steps=5, requires_grad=True)
и y = z
, функция является векторнозначной функцией, поэтому производная от y
относительно z
не так проста, как 1
, а представляет собой матрицу Якоби. На самом деле в вашем случае z = [z1, z2, z3, z4, z5]T
верхний регистр T
означает, что z
является вектором-строкой. Вот что говорит официальный документ:
Во-вторых, обратите внимание, что официальный документ говорит: Теперь в этом случае y больше не является скаляром. torch.autograd не может напрямую вычислить полный якобиан, но если нам просто нужно векторно-якобианское произведение, просто передайте вектор обратно в качестве аргумента связь. В этом случае x.grad
— это не фактическое значение градиента (матрица), а векторно-якобианское произведение.
Обновлено:
x.grad
- это фактический градиент, если ваш вывод y
является скаляром.
См. пример здесь:
z = torch.linspace(-1, 1, steps=5, requires_grad=True)
y = torch.sum(z)
y.backward()
z.grad
Это выведет:
tensor([1., 1., 1., 1., 1.])
Как видите, это градиент. Обратите внимание, что единственное отличие состоит в том, что здесь y
— это скалярное значение, а в вашем примере — векторное значение. grad может быть неявно создан только для скалярных выходов
Вы можете задаться вопросом, а что, если градиент не является константой, например, зависит от ввода z
, как в этом случае.
z = torch.linspace(-1, 1, steps=5, requires_grad=True)
y = torch.sum(torch.pow(z,2))
y.backward()
z.grad
Результат:
tensor([-2., -1., 0., 1., 2.])
Это то же самое, что
z = torch.linspace(-1, 1, steps=5, requires_grad=True)
y = torch.sum(torch.pow(z,2))
y.backward(torch.tensor(1.))
z.grad
Учебник по блицу довольно краток, поэтому его довольно сложно понять новичкам.
Спасибо! Я читал документацию раньше, но я не был полностью уверен, что это значит. Я думаю, что это немного вводит в заблуждение, называя векторно-якобианское произведение просто «выпускным».