Как вычислить градиент ошибки относительно входных данных модели? - PullRequest
1 голос
/ 21 мая 2019

Учитывая простую двухслойную нейронную сеть, традиционная идея состоит в том, чтобы вычислить градиент по весам / параметрам модели.Для эксперимента я хочу вычислить градиент ошибки по входным данным.Существуют ли методы Pytorch, которые могут позволить мне сделать это?

Конкретнее, рассмотрим следующую нейронную сеть:

import torch.nn as nn
import torch.nn.functional as F

class NeuralNet(nn.Module):
    def __init__(self, n_features, n_hidden, n_classes, dropout):
        super(NeuralNet, self).__init__()

        self.fc1 = nn.Linear(n_features, n_hidden)
        self.sigmoid = nn.Sigmoid()
        self.fc2 = nn.Linear(n_hidden, n_classes)
        self.dropout = dropout

    def forward(self, x):
        x = self.sigmoid(self.fc1(x))
        x = F.dropout(x, self.dropout, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

Я создаю модель и оптимизатор для весов следующим образом:

import torch.optim as optim
model = NeuralNet(n_features=args.n_features,
            n_hidden=args.n_hidden,
            n_classes=args.n_classes,
            dropout=args.dropout)
optimizer_w = optim.SGD(model.parameters(), lr=0.001)

Во время тренировок я обновляю веса как обычно.Теперь, учитывая, что у меня есть значения для весов, я должен иметь возможность использовать их для вычисления градиента по отношению к входным данным.Я не могу понять, как.

def train(epoch):
    t = time.time()
    model.train()
    optimizer.zero_grad()
    output = model(features)
    loss_train = F.nll_loss(output[idx_train], labels[idx_train])
    acc_train = accuracy(output[idx_train], labels[idx_train])
    loss_train.backward()
    optimizer_w.step()

    # grad_features = loss_train.backward() w.r.t to features
    # features -= 0.001 * grad_features

for epoch in range(args.epochs):
    train(epoch)

1 Ответ

2 голосов
/ 21 мая 2019

Возможно, просто установите input.requires_grad = True для каждой входной партии, в которую вы вводите, а затем после loss.backward() вы увидите, что input.grad содержит ожидаемый градиент.Другими словами, если ваши входные данные для модели (которую вы называете features в вашем коде) имеют некоторый M x N x ... тензор, features.grad будет тензором той же формы, где каждый элемент grad содержит градиентпо отношению к соответствующему элементу features.В моих комментариях ниже я использую i в качестве обобщенного индекса - если ваш parameters имеет, например, 3 измерения, замените его на features.grad[i, j, k] и т. Д.

Что касается получаемой ошибки: PyTorchОперации строят дерево, представляющее математическую операцию, которую они описывают, которая затем используется для дифференцирования.Например, c = a + b создаст дерево, в котором a и b являются листовыми узлами, а c не является листом (так как это результат других выражений).Ваша модель - это выражение, а его входы и параметры - это листья, тогда как все промежуточные и конечные результаты не являются листьями.Вы можете думать о листьях как о «константах» или «параметрах» и обо всех других переменных как о функциях тех.Это сообщение говорит вам, что вы можете установить только requires_grad листовых переменных.

Ваша проблема в том, что на первой итерации features является случайным (или, в противном случае, вы инициализируете) и, следовательно, действительным листом.После вашей первой итерации features больше не является листом, поскольку он становится выражением, рассчитанным на основе предыдущих.В псевдокоде у вас есть

f_1 = initial_value # valid leaf
f_2 = f_1 + your_grad_stuff # not a leaf: f_2 is a function of f_1

, чтобы справиться с этим, вам нужно использовать detach, который разрывает связи в дереве и заставляет автограда обрабатывать тензор так, как если бы онбыл постоянным, независимо от того, как он был создан.В частности, расчеты градиента не будут распространяться через detach.Поэтому вам нужно что-то вроде

features = features.detach() - 0.01 * features.grad

Примечание: возможно, вам нужно посыпать еще пару detach здесь и там, что трудно сказать, не видя весь ваш код и не зная точную цель.

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