YOLO-v8.3을 시작할 때 "CUDA out of memory" 오류는 흔한 문제입니다. 이는 모델 가중치, 입력 데이터, 그리고 중간 계산 결과가 GPU 메모리를 모두 차지하기 때문입니다. 이 글에서는 실제 코드와 함께 이 문제를 해결하는 세 가지 방법을 소개합니다.
1. 문제 진단: GPU 메모리 사용량 확인
먼저, 다음과 같은 코드로 GPU 메모리 사용량을 모니터링할 수 있습니다.
import torch
from ultralytics import YOLO
# 초기 메모리 사용량
print(f"초기 메모리: {torch.cuda.memory_allocated()/1024**2:.2f} MB")
# 모델 로드
model = YOLO('yolov8s.pt').to('cuda')
print(f"모델 로드 후: {torch.cuda.memory_allocated()/1024**2:.2f} MB")
# 추론 실행
results = model.predict('bus.jpg')
print(f"추론 중 최대 메모리: {torch.cuda.max_memory_allocated()/1024**3:.2f} GB")
2. 첫 번째 방법: 입력 이미지 크기 조정
YOLO-v8.3의 기본 입력 크기는 640x640이지만, 이 값을 줄이면 메모리 사용량이 크게 감소합니다.
from ultralytics import YOLO
model = YOLO('yolov8s.pt')
# 입력 크기를 절반으로 줄임
results = model.predict('image.jpg', imgsz=320) # 기본값은 640
효과 비교
| 입력 크기 | 메모리 사용량 | 속도 | 정밀도 (mAP) |
|---|---|---|---|
| 640x640 | 2.1 GB | 22 ms | 0.68 |
| 480x480 | 1.4 GB | 15 ms | 0.65 |
| 320x320 | 0.8 GB | 8 ms | 0.61 |
적합한 상황: 사람 얼굴이나 자동차처럼 멀리 있는 작은 물체를 정밀하게 탐지할 필요가 없는 경우에 효과적입니다.
3. 두 번째 방법: 반정밀도(FP16) 추론 활성화
현대 GPU는 반정밀도 연산을 지원하여 메모리 사용량을 절반으로 줄일 수 있습니다.
model = YOLO('yolov8s.pt')
# 반정밀도 활성화
results = model.predict('image.jpg', half=True)
주의 사항
- mAP가 약 0.5~1% 정도 감소할 수 있지만, 대부분의 애플리케이션에는 큰 영향이 없습니다.
- NVIDIA Pascal 아키텍처 이상의 GPU가 필요합니다.
- `imgsz` 매개변수와 함께 사용할 수 있습니다.
4. 세 번째 방법: 모델 선택과 배치 처리 최적화
4.1 모델 크기 선택
| 모델 | 파라미터 수 | 메모리 사용량 | 적합한 용도 |
|---|---|---|---|
| yolov8n | 3.2M | ~1.2GB | 모바일, 엣지 디바이스 |
| yolov8s | 11.4M | ~1.8GB | 일반적인 용도 |
| yolov8m | 26.3M | ~2.5GB | 높은 정밀도 필요 |
| yolov8l | 44.1M | ~3.8GB | 서버 배포 |
| yolov8x | 68.9M | ~5.2GB | 최고 정밀도 |
권장: `yolov8s`부터 시작하여 필요할 때만 더 큰 모델로 전환하세요.
4.2 비디오 스트림 처리 최적화
import cv2
from ultralytics import YOLO
model = YOLO('yolov8s.pt')
cap = cv2.VideoCapture('video.mp4')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 한 프레임씩 처리하여 메모리 절약
results = model.predict(frame, imgsz=320, half=True)
cv2.imshow('YOLOv8', results[0].plot())
if cv2.waitKey(1) == ord('q'):
break
cap.release()
5. 고급 기법: 대용량 이미지 분할 처리
4K/8K 같은 고해상도 이미지는 분할하여 처리할 수 있습니다.
import cv2
import torch
import numpy as np
from ultralytics import YOLO
def process_large_image(model, img_path, tile_size=640, overlap=100):
img = cv2.imread(img_path)
h, w = img.shape[:2]
results = []
for y in range(0, h, tile_size - overlap):
for x in range(0, w, tile_size - overlap):
tile = img[y:y+tile_size, x:x+tile_size]
if tile.size == 0:
continue
tile_results = model.predict(tile, imgsz=tile_size, half=True)
for r in tile_results:
r.boxes.data[:, [0, 2]] += x
r.boxes.data[:, [1, 3]] += y
results.append(r)
return results
model = YOLO('yolov8s.pt')
large_results = process_large_image(model, 'large_image.jpg')
6. 전체 최적화 예제
모든 기법을 결합한 완전한 예제입니다.
import torch
from ultralytics import YOLO
import cv2
class OptimizedYOLOv8:
def __init__(self, model_type='s', device='cuda'):
self.device = device
self.model = YOLO(f'yolov8{model_type}.pt').to(device)
self.model.eval()
def predict(self, img_path, imgsz=320, half=True):
with torch.inference_mode():
results = self.model.predict(img_path, imgsz=imgsz, half=half)
torch.cuda.empty_cache()
return results
detector = OptimizedYOLOv8(model_type='s')
# 단일 이미지 처리
results = detector.predict('image.jpg')
# 카메라 스트림 처리
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
results = detector.predict(frame)
cv2.imshow('Optimized YOLOv8', results[0].plot())
if cv2.waitKey(1) == ord('q'):
break
cap.release()
7. 메모리 자동 조절 기능
장기 실행 서비스를 위해 메모리 부족 시 자동으로 모델을 전환하는 로직을 추가할 수 있습니다.
import torch
from ultralytics import YOLO
class AutoScaleYOLOv8:
def __init__(self):
self.model = None
self.current_model_size = None
def load_model(self, model_size):
if self.model is not None:
del self.model
torch.cuda.empty_cache()
self.model = YOLO(f'yolov8{model_size}.pt').to('cuda')
self.current_model_size = model_size
return self.model
def smart_predict(self, img_path, initial_size='s'):
if self.model is None:
self.load_model(initial_size)
try:
return self.model.predict(img_path, imgsz=320, half=True)
except RuntimeError as e:
if 'CUDA out of memory' in str(e):
print('메모리 부족, 더 작은 모델로 전환합니다...')
model_sizes = ['s', 'n']
current_idx = model_sizes.index(self.current_model_size)
if current_idx + 1 < len(model_sizes):
new_size = model_sizes[current_idx + 1]
self.load_model(new_size)
return self.smart_predict(img_path)
else:
raise RuntimeError('가장 작은 모델에서도 메모리 부족')
else:
raise
smart_detector = AutoScaleYOLOv8()
results = smart_detector.smart_predict('large_image.jpg')
8. 최종 권장 사항
- 초급 최적화: 입력 크기 축소, 반정밀도 활성화, 적절한 모델 선택
- 중급 최적화: 비디오 스트림 단일 프레임 처리, 주기적인 CUDA 캐시 정리, `torch.inference_mode()` 사용
- 고급 최적화: 대용량 이미지 분할, 동적 모델 전환, 다중 GPU 처리
대부분의 경우, `yolov8s` 모델 + 320 입력 크기 + 반정밀도 추론 조합으로 1GB 이하의 메모리 사용량을 유지할 수 있습니다.