PyTorch: «одна из переменных, необходимых для вычисления градиента, была изменена с помощью операции на месте»

#python #pytorch #recurrent-neural-network

Вопрос:

Я тренирую PyTorch RNN на текстовом файле текстов песен, чтобы предсказать следующий персонаж, заданный персонажем.

Вот как определяется мой RNN:

 
import torch.nn as nn
import torch.optim

class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(RNN, self).__init__()
        
        self.hidden_size = hidden_size
        
        # from input, previous hidden state to new hidden state
        self.i2h = nn.Linear(input_size   hidden_size, hidden_size)
        
        # from input, previous hidden state to output
        self.i2o = nn.Linear(input_size   hidden_size, output_size)
        
        # softmax on output
        self.softmax = nn.LogSoftmax(dim = 1)
    
    def forward(self, input, hidden):
        
        combined = torch.cat((input, hidden), 1)
        
        #get new hidden state
        hidden = self.i2h(combined)
        
        #get output
        output = self.i2o(combined)
        
        #apply softmax
        output = self.softmax(output)
        return output, hidden
    
    def initHidden(self): 
        return torch.zeros(1, self.hidden_size)

rnn = RNN(input_size = num_chars, hidden_size = 200, output_size = num_chars)
criterion = nn.NLLLoss()

lr = 0.01
optimizer = torch.optim.AdamW(rnn.parameters(), lr = lr)

 

Вот моя функция обучения:

 def train(train, target):
    
    hidden = rnn.initHidden()
    
    loss = 0
    
    for i in range(len(train)):
        
        optimizer.zero_grad()

        # get output, hidden state from rnn given input char, hidden state
        output, hidden = rnn(train[i].unsqueeze(0), hidden)

        #returns the index with '1' - indentifying the index of the right character
        target_class = (target[i] == 1).nonzero(as_tuple=True)[0]
        
        loss  = criterion(output, target_class)
        
    
        loss.backward(retain_graph = True)
        optimizer.step()
        
        print("done "   str(i)   " loop")
    
    return output, loss.item() / train.size(0)
 

Когда я запускаю свою функцию обучения, я получаю эту ошибку:

 RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [274, 74]], which is output 0 of TBackward, is at version 5; expected version 3 instead. Hint: the backtrace further above shows the operation that failed to compute its gradient. The variable in question was changed in there or anywhere later. Good luck!

 

Интересно, что он проходит через два полных цикла функции обучения, прежде чем выдать мне эту ошибку.

Теперь, когда я удаляю retain_graph = True из loss.backward() , я получаю эту ошибку:

 RuntimeError: Trying to backward through the graph a second time (or directly access saved variables after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved variables after calling backward.
 

Здесь не следует пытаться несколько раз вернуться назад по графику. Возможно, график не очищается между циклами обучения?

Ответ №1:

Проблема в том, что вы накапливаете свои значения потерь (и в то же время связанные с ними графики вычислений) в переменной loss , здесь:

     loss  = criterion(output, target_class)
 

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

Простое решение состоит в том, чтобы накопить loss базовое значение, т. Е. скалярное значение, а не сам тензор, с помощью item . И, обратное распространение на тензор текущих потерь:

 total_loss = 0
    
for i in range(len(train)):
    optimizer.zero_grad()
    output, hidden = rnn(train[i].unsqueeze(0), hidden)
    target_class = (target[i] == 1).nonzero(as_tuple=True)[0]
        
    loss = criterion(output, target_class)
    loss.backward()

    total_loss  = loss.item()
 

Поскольку вы обновляете параметр модели сразу после выполнения обратного распространения, вам не нужно сохранять график в памяти.

Комментарии:

1. Спасибо! Это сработало! Объяснение имеет смысл.