У меня проблемы с вычислением градиента с использованием PyTorch.

У меня есть выходы и скрытые состояния последнего временного шага T RNN.

Я хотел бы клонировать свои скрытые состояния и вычислить их градиент после обратного распространения, но это не работает.

После прочтения pytorch, как вычислить градиент после клонирования тензора, я безуспешно использовал retain_grad().

Вот мой код

        hidden_copy = hidden.clone()
        hidden.retain_grad()
        hidden_copy.retain_grad()
        outputs_T = outputs[T]
        targets_T = targets[T]
        loss_T = loss(outputs_T,targets_T)
        loss_T.backward()
        print(hidden.grad)
        print(hidden_copy.grad)

hidden_grad дает массив, а hidden_copy.grad дает None.

Почему hidden_copy.grad дает None ? Есть ли способ вычислить градиенты клонированного тензора?

0
Julien 25 Мар 2020 в 20:00
Используется ли hidden_copy для вычисления outputs_T? Если нет, то имеет смысл, что он не получит градиент, поскольку loss_T не зависит от него.
 – 
jodag
27 Мар 2020 в 12:48
Спасибо за ваш ответ, hidden_copy не используется для вычисления outputs_T put hidden. Поскольку hidden_copy является копией hidden (который используется для вычисления outputs_T), разве не имеет смысла, что его градиенты не None ?
 – 
Julien
27 Мар 2020 в 16:25

1 ответ

Судя по комментариям, проблема в том, что hidden_copy никогда не посещается во время обратного прохода.

Когда вы выполняете обратную операцию, pytorch следует графу вычислений в обратном направлении, начиная с loss_T, и работает в обратном направлении ко всем листовым узлам. Он посещает только те тензоры, которые использовались для вычисления loss_T. Если тензор не является частью этого обратного пути, он не будет посещен, и его элемент grad не будет обновлен. По сути, создание копии тензора, а затем не использование его для вычисления loss_T приводит к «тупику» в графе вычислений.

Для иллюстрации взгляните на эту диаграмму, представляющую упрощенный вид графа вычислений. Каждый узел в графе представляет собой тензор, ребра которого указывают на прямых потомков.

Обратите внимание, что если мы идем по обратному пути от loss_T к листьям, мы никогда не посещаем hidden_conv. Обратите внимание, что лист — это тензор без потомков, и в этом случае input — единственный лист.

Это чрезвычайно упрощенный график вычислений, используемый для демонстрации точки. Конечно, на самом деле между input и hidden и между hidden и output_T, вероятно, гораздо больше узлов, а также других тензоров листьев, поскольку веса слоев почти наверняка равны листьям. .

1
jodag 27 Мар 2020 в 22:15
Спасибо за ваше объяснение. Я хотел скопировать скрытые состояния моего RNN в список и получить градиенты потерь для каждого скрытого состояния. Разве это не означает, что я не могу получить градиент потерь по отношению к скрытому состоянию на временном шаге t, поскольку скрытые состояния не могут быть клонированы и оставаться в графе вычислений?
 – 
Julien
27 Мар 2020 в 22:25
Вы не должны использовать операции на месте для скрытых состояний. Например, если вы используете hidden_1 = hidden, а затем переназначаете hidden чему-то новому, hidden_1 по-прежнему указывает на исходный тензор hidden, а hidden теперь ссылается на другой тензор. В основном попробуйте удалить .clone() и посмотрите, как это работает.
 – 
jodag
27 Мар 2020 в 22:26