RuntimeError происходит в обратной функции PyTorch - PullRequest
3 голосов
/ 16 марта 2020

Я пытаюсь вычислить градус переменной в PyTorch. Однако было RuntimeError, который говорит мне, что форма вывода и град должны быть одинаковыми. Однако в моем случае форма вывода и град не могут быть одинаковыми. Вот мой код для воспроизведения:

import numpy as np
import torch
from torch.autograd import Variable as V

ne = 3
m, n = 79, 164
G = np.random.rand(m, n).astype(np.float64)
w = np.random.rand(n, n).astype(np.float64)
z = -np.random.rand(n).astype(np.float64)

G = V(torch.from_numpy(G))
w = V(torch.from_numpy(w))
z = V(torch.from_numpy(z), requires_grad=True)
e, v = torch.symeig(torch.diag(2 * z - torch.sum(w, dim=1)) + w, eigenvectors=True, upper=False)
ssev = torch.sum(torch.pow(e[-ne:] * v[:, -ne:], 2), dim=1)
out = torch.sum(torch.matmul(G, ssev.reshape((n, 1))))
out.backward(z)
print(z.grad)

Сообщение об ошибке: RuntimeError: Mismatch in shape: grad_output[0] has a shape of torch.Size([164]) and output[0] has a shape of torch.Size([])

Подобные вычисления разрешены в TensorFlow, и я могу успешно получить желаемый градиент:

import numpy as np
import tensorflow as tf

m, n = 79, 164
G = np.random.rand(m, n).astype(np.float64)
w = np.random.rand(n, n).astype(np.float64)
z = -np.random.rand(n).astype(np.float64)

def tf_function(z, G, w, ne=3):
    e, v = tf.linalg.eigh(tf.linalg.diag(2 * z - tf.reduce_sum(w, 1)) + w)
    ssev = tf.reduce_sum(tf.square(e[-ne:] * v[:, -ne:]), 1)
    return tf.reduce_sum(tf.matmul(G, tf.expand_dims(ssev, 1)))

z, G, w = [tf.convert_to_tensor(_, dtype=tf.float64) for _ in (z, G, w)]
z = tf.Variable(z)
with tf.GradientTape() as g:
    g.watch(z)
    out = tf_function(z, G, w)
print(g.gradient(out, z).numpy())

Моя версия тензорного потока - 2.0, а моя версия PyTorch - 1.14.0. Я использую Python3 .6.9. По моему мнению, вычисление градиентов, когда выходные данные и переменные имеют разные формы, очень разумно, и я не думаю, что сделал какую-либо ошибку. Может кто-нибудь помочь мне с этой проблемой? Я действительно ценю это!

1 Ответ

1 голос
/ 16 марта 2020

Прежде всего вам не нужно использовать numpy, а затем преобразовать в переменную (что, кстати, не рекомендуется), вы можете просто использовать G = torch.rand(m, n) et c. Во-вторых, когда вы пишете out.backward(z), вы передаете z как градиент из out, то есть out.backward(gradient=z), вероятно, из-за неправильного представления, что "out.backward(z) вычисляет градиент z, то есть dout/dz "(но на самом деле dout/dz получается z.grad после выполнения out.backward(), и z не обязательно должно иметь ту же форму, что и out). Вместо этого этот аргумент должен быть gradient = d[f(out)]/dout для некоторой функции f (например, функция потерь), и это тензор, используемый для вычисления вектор-якобианского произведения dout/dz * df/dout. Следовательно, причина, по которой вы получили ошибку, состоит в том, что ваш out (и его градиент df/dout) является скаляром (тензор нульмерности), а z - тензор размера n, что приводит к несоответствию в формы.

Чтобы устранить проблему, как вы уже выяснили самостоятельно, просто замените out.backward(z) на out.backward(), что эквивалентно out.backward(gradient=torch.tensor(1.)), поскольку в вашем случае out является скаляром, а f(out) = out, значит d[f(out)]/dout = d(out)/d(out) = tensor(1.). Если ваш out был нескалярным тензором, то out.backward() не сработал бы, и вместо этого вам пришлось бы использовать out.backward(torch.ones(out.shape)) (снова предполагая, что f(out) = out). В любом случае, если вам нужно передать gradient в out.backward(), убедитесь, что он имеет ту же форму, что и out.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...