2022년 1월 24일(월)부터 28일(금)까지 네이버 부스트캠프(boostcamp) AI Tech 강의를 들으면서 개인적으로 중요하다고 생각되거나 짚고 넘어가야 할 핵심 내용들만 간단하게 메모한 내용입니다. 틀리거나 설명이 부족한 내용이 있을 수 있으며, 이는 학습을 진행하면서 꾸준히 내용을 수정하거나 추가해 나갈 예정입니다.
PyTorch로 딥러닝 하는 과정에서 문제 발생 시 해결하기
ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory
DL 모델링을 하면서 자주 보게 될 오류로 GPU 상에서의 OOM(Out Of Memory)가 있다.
이는 다음과 같은 문제를 지닌다.
- 왜 발생했는지 알기 어렵다.
- 어디서 발생했는지 알기 어렵다.
- Error BackTracking이 이상한 데로 간다.
- 메모리의 이전 상황의 파악이 어렵다.
주로 iteration을 돌면서 특정한 시점에 문제가 발생하는데, 이를 해결할 수 있는 가장 좋은 방법은 Batch Size를 줄여서 Launch를 다시 하거나 kernel을 다시 켜서 실행하는 방법이다.
Batch Size ↓ + GPU clean + Run
# 가능한 배치사이즈들을 실험하고, OOM이 발생하면 batch_size = 1로 만드는 방법
oom = False
try:
run_model(batch_size)
except RuntimeError: # Out of memory
oom = True
if oom:
for _ in range(batch_size):
run_model(1)
이 외에도 시도할 수 있는 방법들이 있다.
GPUUtil 사용하기
- nvidia-smi처럼 GPU의 상태를 보여주는 모듈
- Colab은 환경에서 GPU 상태를 보여주기에 편하다.
- iteration마다 메모리가 늘어나는지 확인한다.
예시 코드
!pip install GPUtil
import GPUtil
GPUtil.showUtilization()
torch.cuda.empty_cache() 사용해 보기
- 사용되지 않은 GPU상 cache를 정리한다.
- 가용 메모리를 확보할 수 있다.
- del과는 구분이 필요하다. del은 관계를 끊어놓는 것이다. del을 사용하면 garbage collector이 수행된 이후에 메모리를 확보한다.
- torch.cuda.empty_cache()를 사용하면 원하는 시점에 메모리를 확보할 수 있다.
- reset 대신 쓰기 좋은 함수이다.
- 학습 전에 실행해주면 이전 학습에 의한 메모리 낭비를 줄일 수 있다.
trainning loop에 tensor로 축적되는 변수 확인하기
- tensor로 처리된 변수는 GPU상의 메모리를 사용한다.
- 특히 loop 안에 tensor 변수에 대한 연산이 있을 때 GPU에 computational graph를 생성하여 메모리를 잠식한다.
- 1-d tensor의 경우에는 python 기본 객체로 변환하여 처리한다.
예시 코드
# 메모리 잠식 예시(1)
total_loss = 0
for i in range(10000):
optimizer.zero_grad()
output = model(input)
loss = criterion(output)
loss.backward()
optimizer.step()
# 한번만 필요한 loss들이 불필요하게 메모리를 차지
total_loss += loss
# 메모리 잠식 예시(2)
total_loss = 0
for x in range(10):
# assume loss is computed
iter_loss = torch.randn(3,4).mean()
iter_loss.requires_grad = True
# total_loss가 가지고 있는 iter_loss들이 tensor
# 또한 requires_grad = True인 상태로 많은 메모리 차지
total_loss += iter_loss
# 메모리 잠식 해결 예시
total_loss = 0
for x in range(10):
# assume loss is computed
iter_loss = torch.randn(3,4).mean()
iter_loss.requires_grad = True
total_loss += iter_loss.item
# 또는
total_loss += float(iter_loss)
del 명령어 사용
- 필요 없어진 변수는 적절한 삭제가 필요하다.
- python의 메모리 배치 특성상 loop이 끝나도 메모리를 차지한다.
예시 코드
# output을 얻는 것만이 목적이라면,
# loop이 끝난 시점에서는 i, intermediate, result 모두 불필요한 변수가 된다.
for i in range(5):
intermediate = f(input[i])
result += g(intermediate)
output = h(result)
가능한 batch 사이즈 실험해보기
- 학습 시 OOM이 발생했다면 batch size를 1로 해서 실험해보고, 이를 점차 늘려볼 수 있다.
torch.no_grad() 사용하기
- inference 시점(학습하는 과정이 아닌 경우)에서는 torch.no_grad() 구문을 사용한다.
- Backward pass로 인해 쌓이는 메모리에서 자유롭다.
- 학습이 끝나고 성능 평가하는 시점에서 사용하면 유용하다.
예시 코드
with torch.no_grad():
for data, target in test_loader:
output = network(data)
test_loss += F.nll_loss(output, target, size_average=False).item()
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).sum()