Pytorch - почему предварительное выделение памяти вызывает «попытку перевернуть график во второй раз» - PullRequest
2 голосов
/ 20 марта 2019

Предположим, у меня есть простая сеть с одним скрытым слоем, которую я тренирую обычным образом:

    for x,y in trainData:
        optimizer.zero_grad()
        out = self(x)
        loss = self.lossfn(out, y)
        loss.backward()
        optimizer.step() 

Это работает, как и ожидалось, но если я вместо этого предварительно выделю и обновлю выходной массивЯ получаю сообщение об ошибке:

    out = torch.empty_like(trainData.tensors[1])
    for i,(x,y) in enumerate(trainData):
        optimizer.zero_grad()
        out[i] = self(x)
        loss = self.lossfn(out[i], y)
        loss.backward()
        optimizer.step()  

RuntimeError: Попытка выполнить обратный просмотр графика во второй раз, но буферы уже освобождены.Укажите retain_graph = True при обратном вызове в первый раз.

Что здесь происходит, когда во второй версии Pytorch пытается снова пройти назад по графику?Почему это не проблема в первой версии?(Обратите внимание, что эта ошибка возникает, даже если я не zero_grad())

1 Ответ

0 голосов
/ 21 марта 2019

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

Вот подробное объяснение того же.

Краткий ответ

Используйте loss.backward(retain_graph=True).Это не приведет к удалению вычислительного графа.

Подробный ответ

В первой версии в каждой итерации цикла новый вычислительный граф генерируется каждый раз при запуске out = self(x).

Every loop's graph
out = self(x) -> loss = self.lossfn(out, y)

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

           - out[i] = self(x) -> loss = self.lossfn(out[i], y) 
out[i] - | - out[i] = self(x) -> loss = self.lossfn(out[i], y) 
           - out[i] = self(x) -> loss = self.lossfn(out[i], y)

Следовательно, вот временная шкалао том, что происходит.

  1. Первая итерация выполняется
  2. Граф вычислений удален, включая родительский узел
  3. Вторая итерация пытается выполнить обратное распространение, но не удалась, так как она ненайти родительский узел
...