TroubleShooting

[PyTorch] Error 늪에서 빠져나오기 | Troubleshooting, OOM, GPU Util

cstory-bo 2024. 1. 12. 14:33

가끔 내가 짠 코드도 아닌데 에러 났다고 하고...
그냥 다른 거 하나 고쳐봤는데 디버깅이 되는 어이없는 상황들을 마주하기 쉽다.
이번 포스팅은 그 늪에서 탈출하기 위한 방법들이다.

Troubleshooting

OOM (Out-Of-Memory)

이런 문제는 왜 발생했는지, 어디서 발생했는지 알기 어렵다...

Error backtracking이 이상한데로 갈 때도 있고

메모리 이전 상황을 파악하기도 어렵다.

그러면 이런 문제들은 어떻게 해결할까?

가장 기본적인 방법으로는

Batch Size 줄이고 ⇒ GPU clean ⇒ RUN

GPU Util 사용하기

  • nvidia-smi 처럼 GPU의 상태를 보여주는 모듈이다.
  • Colab 환경에서 GPU상태 보기 편하다.
  • Iter마다 메모리가 늘어나는지 확인할 수 있다! 만약 반복문을 돌면서 메모리가 계속해서 늘어나면 어디선가 메모리가 새고 있을 수 있다.
    import GPUtil
    GPUtil.showUtilization()

cuda.empty_cache() 써보기

목적

이전 메모리로 부터 영향을 덜 받기 위해

학습 전에 한 번 돌려보는 것이 좋다!!

특징

  • Backward시 미분에 사용되는 버퍼를 깨끗하게 해준다.
  • GPU를 다시 돌리는 것,reset 대신에 쓰기 좋다.
  • 사용되지 않은 GPU상 cache를 정리해준다.
  • 가용 메모리를 확보해준다.
  • del과는 구분이 필요하다.

del 은 memory free를 시켜주지만 시점을 잘 봐야한다.

이 메모리를 쓸 수 있는 시점은 garbage collector가 작동을 할 때부터 사용할 수 있다.

Code

from GPUil import showUtilization as gpu_usage

#gpu 상태 확인
gpu_usage()

# empty cache
torch.cuda.empty_cache()
gpu_usage() # 비어있는 것 확인 가능

training loop에 tensor로 축적되는 변수 확인

연산 시 많은 메모리를 잡아먹고 특히 require_grad=True일 때는 버퍼까지 써야하기 때문에 더욱 많은 메모리를 필요로 한다.

이런 연산이 loop안에 있다면 GPU에 computational graph를 생성하면서 메모리를 잠식하는 현상이 나타난다.

예시로

for i in range(1000):
	optimizer.zero_grad()
	output = model(input)
	loss= criterion(output)
	loss.backward()
	optimizer.step()
	total_loss += loss

여기서 loss가 한 번만 필요한데 loss는 tensor 형태로 total loss에 계속해서 쌓인다. 그래서 쓸데없이 computational graph를 그리게 된다.

해결책으로 1-d tensor의 경우에는 python 기본객체로 변환하여 처리하는 것이 좋다

그래서 loss.item으로 받거나 float(loss) 바꿔주는 것이 좋다.

del 명령어를 적절히 사용하기

  • 필요가 없어진 변수는 적절한 삭제가 필요하다.
  • Python 메모리 배치 특성상 loop이 끝나도 메모리를 차지한다.
for i in range(5):
	intermediate = f(input[i])
	result += g(intermediate)
output = h(result)
return output

이 경우 for문 밖에서 intermediate를 사용할 수 있다.

그러나 result에 저장을 하고 있기 때문에 intermediate는 그다지 필요가 없다.

그래서 output전에 del intermediate를 넣어주는 것이 좋다.

가능 batch 사이즈 실험해보기

배치 사이즈가 어디까지 가능한지 실험해보는 것도 하나의 좋은 방법이다.

oom = False
try:
	run_model(batch_size)
except RuntimeError:
	oom = True

if oom:
	for _ in range(batch_size):
		run_model(1)

torch.no_grad() 사용하기

  • Inference 시점에서 torch.no_grad()구문 사용하기
  • backward pass로 인해 쌓이는 메모리에서 자유롭다.
  • 이 명령어로 더이상 메모리를 더 사용하지 않는다.
with torch.no_grad():
	pass

OOM일종으로 생각할 수 있는 다른 에러

OOM말고 유사한 에러들이 발생할 수 있다.

CUDNN_STATUS_NOT_INIT, device-side-assert 등으로 OOM의 일종으로 생각 될 수 있다.

그럴 경위 이 블로그를 참고하는 것도 좋을 것 같다. 블로그

그외...

이외에 에러를 줄이기 위한 추천방법으로는

  • colab에서 너무 큰 사이즈는 실행하지 않을 것을 추천한다. 특히 LSTM
  • CNN의 대부분은 크기가 안 맞아서 생기는 경우가 많다.
    • 이때는 torchsummary를 사용해보자