PyTorch autograd grad может быть неявно создан только для скалярных выходов.

Я использую инструмент autograd в PyTorch и оказался в ситуации, когда мне нужно получить доступ к значениям в одномерном тензоре с помощью целочисленного индекса. Что-то вроде этого:

def basic_fun(x_cloned):
    res = []
    for i in range(len(x)):
        res.append(x_cloned[i] * x_cloned[i])
    print(res)
    return Variable(torch.FloatTensor(res))


def get_grad(inp, grad_var):
    A = basic_fun(inp)
    A.backward()
    return grad_var.grad


x = Variable(torch.FloatTensor([1, 2, 3, 4, 5]), requires_grad=True)
x_cloned = x.clone()
print(get_grad(x_cloned, x))

Я получаю следующее сообщение об ошибке:

[tensor(1., grad_fn=<ThMulBackward>), tensor(4., grad_fn=<ThMulBackward>), tensor(9., grad_fn=<ThMulBackward>), tensor(16., grad_fn=<ThMulBackward>), tensor(25., grad_fn=<ThMulBackward>)]
Traceback (most recent call last):
  File "/home/mhy/projects/pytorch-optim/predict.py", line 74, in <module>
    print(get_grad(x_cloned, x))
  File "/home/mhy/projects/pytorch-optim/predict.py", line 68, in get_grad
    A.backward()
  File "/home/mhy/.local/lib/python3.5/site-packages/torch/tensor.py", line 93, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/home/mhy/.local/lib/python3.5/site-packages/torch/autograd/__init__.py", line 90, in backward
    allow_unreachable=True)  # allow_unreachable flag
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

Я в целом немного скептически отношусь к тому, как использование клонированной версии переменной должно сохранять эту переменную при вычислении градиента. Сама переменная фактически не используется при вычислении A, поэтому, когда вы вызываете A.backward(), она не должна быть частью этой операции.

Я ценю вашу помощь с этим подходом или если есть лучший способ избежать потери истории градиента и по-прежнему индексировать через одномерный тензор с requires_grad=True!

** Редактировать (15 сентября): **

res - это список нульмерных тензоров, содержащих значения в квадрате от 1 до 5. Чтобы объединить в один тензор, содержащий [1.0, 4.0, ..., 25.0], я изменил return Variable(torch.FloatTensor(res)) на torch.stack(res, dim=0), что дает tensor([ 1., 4., 9., 16., 25.], grad_fn=<StackBackward>).

Однако я получаю эту новую ошибку, вызванную строкой A.backward().

Traceback (most recent call last):
  File "<project_path>/playground.py", line 22, in <module>
    print(get_grad(x_cloned, x))
  File "<project_path>/playground.py", line 16, in get_grad
    A.backward()
  File "/home/mhy/.local/lib/python3.5/site-packages/torch/tensor.py", line 93, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/home/mhy/.local/lib/python3.5/site-packages/torch/autograd/__init__.py", line 84, in backward
    grad_tensors = _make_grads(tensors, grad_tensors)
  File "/home/mhy/.local/lib/python3.5/site-packages/torch/autograd/__init__.py", line 28, in _make_grads
    raise RuntimeError("grad can be implicitly created only for scalar outputs")
RuntimeError: grad can be implicitly created only for scalar outputs

person mhyousefi    schedule 13.09.2018    source источник
comment
Какая у вас PyTorch версия. Я выполнил ваш код в версии 0.3.1, и он работал как надо. Ошибок нет.   -  person papabiceps    schedule 14.09.2018
comment
Я использую версию 0.4.1 в Python 3.5. Пожалуйста, взгляните на мое новое обновление.   -  person mhyousefi    schedule 15.09.2018


Ответы (2)


Я изменил свой basic_fun на следующий, что решило мою проблему:

def basic_fun(x_cloned):
    res = torch.FloatTensor([0])
    for i in range(len(x)):
        res += x_cloned[i] * x_cloned[i]
    return res

Эта версия возвращает скалярное значение.

person mhyousefi    schedule 15.09.2018

в функции basic_fun переменная res уже является переменной torch-autograd, вам не нужно преобразовывать ее снова. ИМХО

def basic_fun(x_cloned):
    res = []
    for i in range(len(x)):
        res.append(x_cloned[i] * x_cloned[i])
    print(res)
    #return Variable(torch.FloatTensor(res))
    return res[0]

def get_grad(inp, grad_var):
    A = basic_fun(inp)
    A.backward()
    return grad_var.grad


x = Variable(torch.FloatTensor([1, 2, 3, 4, 5]), requires_grad=True)
x_cloned = x.clone()
print(get_grad(x_cloned, x))
person Salih Karagoz    schedule 14.09.2018
comment
Спасибо за наблюдение! Я пытался составить тензор из списка тензоров. Я решил проблему, изменив return Variable(torch.FloatTensor(res)) на torch.stack(res, dim=0). - person mhyousefi; 15.09.2018