Pytorch 자동미분 팁(with. 내가 겪었던 오류들)

Tip1. 파라미터가 theta인 신경망으로 구한 값들에 대해서 업데이트를 할 때는 theta에 대해서 업데이트 해주어야한다.

RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [128, 2]], which is output 0 of AsStridedBackward0, is at version 2; expected version 1 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!

 

이런 오류가 뜬 적이 있었습니다. Inplace operation이란 +=, *= 과 같은 연산을 말합니다. 가변 자료형에 대해서는 새로 할당되지않고 값이 변하지만, 불변 자료형에 대해서는 다른 주소에 새로 만들어지게 됩니다. 이런 과정에서 연결되어있던 자동미분 계산들이 끊어지면서 계산이 불가능해져서 생긴 오류입니다. 실제로, inplace operation을 사용해서 생긴 오류라면 고치면 되겠지만, 저같은 경우는 사용하지 않았는데도 생겼습니다.

이유는 다음과 같았습니다.

# this code occurs Runtime error
        for G, log_prob in zip(self.returns, self.log_probs):
            self.optimizer.zero_grad()
            loss = G * log_prob * (-1)
            loss.backward()
            self.optimizer.step()

 

REINFORCE 알고리즘에서 log_prob와 G값을 모두 더한 후 pseudo code에 하나씩 업데이트하라고 되어있어서 하나씩 업데이트를 했습니다. 이렇게 하면 오류가 납니다. 오류가 나는 시점은 한번 업데이트를 하고 두 번째 업데이트를 할 때 였습니다.

제가 생각한 원인은 하나의 에피소드에서 s1, a1, s2  ... 모두 신경망의 파라미터가 $\theta$일 때 구해진 것입니다. 그런데 첫 번째 업데이트를 하고 $\theta'$으로 네트워크의 파라미터가 변경되었고, 변경된 파라미터를 두 번째에 업데이트를 할려고하니 위 오류가 생긴 것으로 추정됩니다. 그래서 아래와 같이 한꺼번에 다 계산한 후 한번에 업데이트하는 방식으로 하니 학습이 잘 되었습니다.

self.optimizer.zero_grad()
        for G, log_prob in zip(self.returns, self.log_probs):
            loss = G * log_prob * (-1)
            loss.backward()

self.optimizer.step()

 

TIp2. 동일한 데이터를 사용해 역전파를 수행한 것은 아닌지 확인해보자.

RuntimeError: Trying to backward through the graph a second time
(or directly access saved tensors 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 tensors after calling backward.

 

학습에 사용한 데이터를 초기화하지않아 생긴 오류였습니다. 한번 업데이트를 한 후에 데이터를 잘 초기화해주었는지 살펴봅시다. 이때, retain_graph = True를 설정해버리면 대참사가 일어납니다.

 

Tip3. with torch.no_grad()를 사용해 미분계산이 필요없는 항의 추적을 꺼버리자

detach()함수를 활용하는 방법도 있지만, 잘하는 사람들의 코드를 본 결과 torch.no_grad()함수를 통해 미리 계산추적기능을 꺼버렸습니다. 이로인해 성능도 더 좋아진다고 합니다.

 

Tip4. 역전파하고자 하는 값의 requires_grad가 끊긴 것은 아닌지 확인해보기

RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

 

텐서의 grad와 grad_fn이 없어서 역전파가 불가능하다는 오류문구입니다. 역전파하는 값의 grad와 grad_fn이 어디서 끊겼는지 디버거를 통해서 알아봐야합니다. 저의 경우, requires_grad가 true인 텐서를 새로운 텐서에 대입할 때 requires_grad가 끊겼습니다.