YOLO-v8.3 메모리 부족 해결: 세 가지 방법으로 추론 최적화

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)
640x6402.1 GB22 ms0.68
480x4801.4 GB15 ms0.65
320x3200.8 GB8 ms0.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 모델 크기 선택

모델파라미터 수메모리 사용량적합한 용도
yolov8n3.2M~1.2GB모바일, 엣지 디바이스
yolov8s11.4M~1.8GB일반적인 용도
yolov8m26.3M~2.5GB높은 정밀도 필요
yolov8l44.1M~3.8GB서버 배포
yolov8x68.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 이하의 메모리 사용량을 유지할 수 있습니다.

태그: YOLOv8 GPU 메모리 추론 최적화 반정밀도 입력 크기 조정

7월 7일 17:21에 게시됨