GPU 활용도 최적화와 OOM 방지 설정
1. 학습 목표
당신은 멋진 AI 프로젝트를 찾았지만, 실행 시 GPU 메모리 부족이나 낮은 GPU 활용도로 인해 어려움을 겪고 있지는 않나요? 이 글에서는 EVA-01 시각 신경 동기화 시스템을 배포하고, 16GB 이상 GPU 메모리 환경에 맞춘 최적화 방법과 OOM(Out of Memory) 방지 설정을 소개합니다.
학습 후 얻을 수 있는 내용:
- Qwen2.5-VL-7B 기반의 EVA-01 시각 인터페이스 배포 방법
- 16GB+ GPU 메모리 환경에서의 최적화 설정
- OOM 방지 설정
- AI 애플리케이션 성능 향상 팁
2. 환경 준비 및 빠른 배포
2.1 시스템 요구 사항 확인
EVA-01을 실행하기 전에 다음 요구 사항을 확인하세요:
<b>최소 구성:</b>
- GPU: NVIDIA 그래픽 카드, 8GB 이상
- RAM: 16GB
- 디스크 공간: 20GB 이상
- OS: Linux 또는 Windows (Linux 권장)
<b>권장 구성:</b>
- GPU: RTX 4080/4090 또는 동등한 16GB+ GPU
- RAM: 32GB
- 디스크 공간: 50GB 이상
- CUDA 버전: 11.8 또는 12.1
2.2 간단 배포 단계
EVA-01 배포는 다음과 같습니다:
# 프로젝트 클론
git clone https://github.com/your-repo/eva-01.git
cd eva-01
# Python 가상 환경 생성
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
필수 의존성 설치:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install -r requirements.txt
pip install flash-attn --no-build-isolation
2.3 설치 검증
설치가 정상적으로 완료되었는지 확인:
import torch
import transformers
print(f"PyTorch 버전: {torch.__version__}")
print(f"CUDA 사용 가능: {torch.cuda.is_available()}")
print(f"GPU 개수: {torch.cuda.device_count()}")
print(f"현재 GPU: {torch.cuda.get_device_name(0)}")
print(f"총 GPU 메모리: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.2f} GB")
try:
import flash_attn
print("FlashAttention 2: 사용 가능")
except ImportError:
print("FlashAttention 2: 사용 불가")
3. 16GB+ GPU 메모리 환경 최적화
3.1 GPU 활용도 최적화
높은 GPU 활용도를 위해 다음과 같이 설정할 수 있습니다:
class InferenceConfig:
batch_size = 4
max_pixels = 1024 * 1024
use_flash_attention = True
precision = "bf16"
dynamic_batching = True
max_batch_tokens = 4096
3.2 혼합 정밀도 훈련
혼합 정밀도는 GPU 메모리를 줄이고 속도를 높입니다:
from torch.cuda.amp import autocast
def load_model():
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-VL-7B-Instruct",
torch_dtype=torch.bfloat16,
device_map="auto",
low_cpu_mem_usage=True
)
return model
@autocast()
def inference(image, prompt):
pass
3.3 KV 캐시 최적화
대화형 애플리케이션에서 KV 캐시를 사용하여 속도를 향상시킬 수 있습니다:
generation_config = {
"max_new_tokens": 512,
"do_sample": True,
"temperature": 0.7,
"top_p": 0.9,
"repetition_penalty": 1.1,
"use_cache": True,
"past_key_values": None
}
4. OOM 방지 설정
메모리 오버플로우(OOM) 문제를 방지하는 방법:
4.1 GPU 메모리 모니터링 및 경고
실시간 GPU 메모리 모니터링:
import torch
import psutil
import time
class MemoryGuard:
def __init__(self, safety_margin=0.8):
self.safety_margin = safety_margin
self.total_vram = torch.cuda.get_device_properties(0).total_memory
def check_memory(self):
allocated = torch.cuda.memory_allocated(0)
reserved = torch.cuda.memory_reserved(0)
usage_ratio = allocated / self.total_vram
return {
"allocated_gb": allocated / 1024**3,
"reserved_gb": reserved / 1024**3,
"total_gb": self.total_vram / 1024**3,
"usage_ratio": usage_ratio,
"is_safe": usage_ratio < self.safety_margin
}
def auto_clear_cache(self):
mem_info = self.check_memory()
if mem_info["usage_ratio"] > 0.7:
torch.cuda.empty_cache()
print("GPU 캐시 자동 정리")
return mem_info
4.2 지능형 다운그레이드 메커니즘
메모리 부족 시 더 적은 리소스를 사용하는 모드로 자동 전환:
class AdaptiveInference:
def __init__(self):
self.mode = "high"
self.available_modes = ["high", "medium", "low"]
def get_inference_config(self):
if self.mode == "high":
return {
"max_pixels": 1024 * 1024,
"use_flash_attention": True,
"batch_size": 4,
"precision": "bf16"
}
elif self.mode == "medium":
return {
"max_pixels": 768 * 768,
"use_flash_attention": False,
"batch_size": 2,
"precision": "fp16"
}
else:
return {
"max_pixels": 512 * 512,
"use_flash_attention": False,
"batch_size": 1,
"precision": "fp16"
}
def adapt_to_memory(self, mem_usage_ratio):
if mem_usage_ratio > 0.85:
self.mode = "low"
print("저성능 모드로 전환")
elif mem_usage_ratio > 0.7:
self.mode = "medium"
print("중간 성능 모드로 전환")
else:
self.mode = "high"
return self.get_inference_config()
4.3 예외 처리 및 복구
예외 처리를 통해 안정성을 높일 수 있습니다:
def robust_inference(image, prompt, max_retries=3):
for attempt in range(max_retries):
try:
result = model_inference(image, prompt)
return result
except torch.cuda.OutOfMemoryError:
print(f"메모리 오버플로우! 재시도 {attempt + 1}/{max_retries}")
torch.cuda.empty_cache()
time.sleep(1)
if attempt == 0:
image = image.resize((image.width//2, image.height//2))
elif attempt == 1:
generation_config["max_new_tokens"] = 256
else:
print("CPU 백업 모드 사용")
return cpu_fallback_inference(image, prompt)
raise Exception("모든 복구 시도 실패")
5. 최적화된 EVA-01 배포
5.1 최적화 설정 파일 생성
`optimized_config.yaml` 파일 생성:
system:
project_name: "EVA-01 Visual Neural Sync System"
version: "1.0.0"
hardware:
gpu_memory_gb: 16
system_memory_gb: 32
optimization:
use_flash_attention: true
precision: "bf16"
batch_size: 4
max_pixels: 1048576
memory_safety_margin: 0.8
model:
name: "Qwen2.5-VL-7B-Instruct"
ui:
theme_color: "#60269E"
enable_animations: true
5.2 시작 스크립트 최적화
최적화 설정을 적용한 시작 스크립트 수정:
import yaml
from pathlib import Path
def load_config(config_path="optimized_config.yaml"):
with open(config_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
return config
def setup_environment(config):
total_vram_gb = torch.cuda.get_device_properties(0).total_memory / 1024**3
config['hardware']['detected_gpu_memory_gb'] = total_vram_gb
if total_vram_gb >= 16:
config['optimization']['batch_size'] = 4
config['optimization']['max_pixels'] = 1024 * 1024
elif total_vram_gb >= 12:
config['optimization']['batch_size'] = 2
config['optimization']['max_pixels'] = 768 * 768
else:
config['optimization']['batch_size'] = 1
config['optimization']['max_pixels'] = 512 * 512
return config
def main():
config = load_config()
config = setup_environment(config)
print("=" * 50)
print("EVA-01 최적화 버전 시작")
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"GPU 메모리: {config['hardware']['detected_gpu_memory_gb']:.1f} GB")
print(f"최적화 모드: {'고성능' if config['optimization']['batch_size'] >= 4 else '균형'}")
print("=" * 50)
5.3 실시간 성능 모니터링 패널
성능 모니터링 패널 추가:
import threading
from datetime import datetime
class PerformanceMonitor:
def __init__(self, update_interval=5):
self.update_interval = update_interval
self.metrics = {
"gpu_usage": [],
"memory_usage": [],
"inference_time": [],
"requests_processed": 0
}
self.running = False
def start_monitoring(self):
self.running = True
monitor_thread = threading.Thread(target=self._monitor_loop)
monitor_thread.daemon = True
monitor_thread.start()
print("성능 모니터링 시작")
def _monitor_loop(self):
while self.running:
gpu_util = self._get_gpu_utilization()
self.metrics["gpu_usage"].append(gpu_util)
mem_info = self._get_memory_info()
self.metrics["memory_usage"].append(mem_info["usage_ratio"])
if len(self.metrics["gpu_usage"]) > 100:
self.metrics["gpu_usage"] = self.metrics["gpu_usage"][-100:]
time.sleep(self.update_interval)
def _get_gpu_utilization(self):
return torch.cuda.utilization(0) if hasattr(torch.cuda, 'utilization') else 0
def _get_memory_info(self):
allocated = torch.cuda.memory_allocated(0)
total = torch.cuda.get_device_properties(0).total_memory
return {
"allocated_gb": allocated / 1024**3,
"total_gb": total / 1024**3,
"usage_ratio": allocated / total
}
def record_inference(self, inference_time):
self.metrics["inference_time"].append(inference_time)
self.metrics["requests_processed"] += 1
if len(self.metrics["inference_time"]) > 50:
self.metrics["inference_time"] = self.metrics["inference_time"][-50:]
def get_summary(self):
if not self.metrics["gpu_usage"]:
return "데이터 없음"
avg_gpu = sum(self.metrics["gpu_usage"]) / len(self.metrics["gpu_usage"])
avg_mem = sum(self.metrics["memory_usage"]) / len(self.metrics["memory_usage"])
if self.metrics["inference_time"]:
avg_inference = sum(self.metrics["inference_time"]) / len(self.metrics["inference_time"])
p95_inference = sorted(self.metrics["inference_time"])[int(len(self.metrics["inference_time"]) * 0.95)]
else:
avg_inference = p95_inference = 0
return f"""
성능 요약:
- 평균 GPU 사용률: {avg_gpu:.1f}%
- 평균 메모리 사용률: {avg_mem:.1%}
- 평균 추론 시간: {avg_inference:.2f}초
- P95 추론 시간: {p95_inference:.2f}초
- 처리 요청 수: {self.metrics['requests_processed']}
""".strip()
def stop(self):
self.running = False