# Qwen-Image-Layered로 포스터 자동 레이어 분해 (4/10): 로컬 환경 세팅과 첫 추론 ## 개발 환경 준비 이론은 충분하다. 이제 실제로 모델을 실행해보자. ### 시스템 요구사항 **하드웨어**: - GPU: NVIDIA GPU with CUDA support - 최소: GTX 1080 (8GB VRAM) - 권장: RTX 3090 (24GB VRAM) - RAM: 16GB 이상 - 저장공간: 30GB (모델 가중치 + 캐시) **소프트웨어**: - OS: Ubuntu 20.04+ / Windows 11 with WSL2 - Python: 3.10 or 3.11 - CUDA: 11.8 or 12.1 - cuDNN: 8.9+ ### GPU 드라이버 확인 ```bash # NVIDIA 드라이버 확인 nvidia-smi # 출력 예시: # +-----------------------------------------------------------------------------+ # | NVIDIA-SMI 535.129.03 Driver Version: 535.129.03 CUDA Version: 12.2 | # |-------------------------------+----------------------+----------------------+ # | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | # | 0 NVIDIA GeForce ... Off | 00000000:01:00.0 Off | N/A | ``` CUDA가 없다면: ```bash # Ubuntu에서 CUDA 설치 wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda_12.1.0_530.30.02_linux.run sudo sh cuda_12.1.0_530.30.02_linux.run # 환경 변수 추가 echo 'export PATH=/usr/local/cuda-12.1/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc ``` ## Python 환경 구축 ### 가상환경 생성 ```bash # 프로젝트 디렉토리 생성 mkdir qwen-image-layered cd qwen-image-layered # Python 가상환경 python3.11 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # pip 업그레이드 pip install --upgrade pip ``` ### 필수 라이브러리 설치 `requirements.txt`: ```txt # Core AI Libraries torch==2.1.2 torchvision==0.16.2 transformers>=4.51.3 diffusers>=0.27.0 accelerate>=0.26.0 # Image Processing Pillow>=10.0.0 opencv-python>=4.8.0 numpy>=1.24.0 scipy>=1.11.0 # HuggingFace huggingface-hub>=0.20.0 safetensors>=0.4.0 # Utilities tqdm>=4.66.0 pyyaml>=6.0 ``` 설치: ```bash pip install -r requirements.txt # GPU 확인 python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')" # 출력: CUDA available: True ``` ### 트러블슈팅: CUDA 인식 안 됨 ```bash # PyTorch CUDA 버전 확인 python -c "import torch; print(torch.version.cuda)" # CUDA 버전과 PyTorch 버전 불일치 시 재설치 pip uninstall torch torchvision pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 ``` ## Qwen-Image-Layered 모델 다운로드 ### HuggingFace CLI 설정 ```bash # HuggingFace CLI 설치 pip install -U "huggingface_hub[cli]" # 로그인 (선택, 공개 모델은 불필요) huggingface-cli login ``` ### 모델 다운로드 스크립트 `download_model.py`: ```python #!/usr/bin/env python3 """Qwen-Image-Layered 모델 다운로드""" from huggingface_hub import snapshot_download import os def download_qwen_layered(): """모델 다운로드""" print("Qwen-Image-Layered 모델 다운로드 시작...") # 캐시 디렉토리 설정 cache_dir = "./models/qwen-image-layered" os.makedirs(cache_dir, exist_ok=True) # 모델 다운로드 model_path = snapshot_download( repo_id="Qwen/Qwen-Image-Layered", cache_dir=cache_dir, resume_download=True, # 중단 시 이어받기 local_files_only=False ) print(f"✓ 다운로드 완료: {model_path}") print(f" 크기: ~12GB") return model_path if __name__ == "__main__": download_qwen_layered() ``` 실행: ```bash python download_model.py # 출력: # Fetching 15 files: 100%|████████████████| 15/15 [03:42<00:00, 14.85s/it] # ✓ 다운로드 완료: ./models/qwen-image-layered/... ``` 다운로드 시간: 약 10-20분 (인터넷 속도에 따라) ## 첫 추론 실행 ### 테스트 이미지 준비 ```bash # 테스트 이미지 디렉토리 mkdir test_images # 샘플 이미지 다운로드 (또는 직접 준비) wget https://example.com/sample_poster.jpg -O test_images/poster.jpg ``` ### 기본 추론 스크립트 `test_inference.py`: ```python #!/usr/bin/env python3 """Qwen-Image-Layered 첫 추론 테스트""" import torch from PIL import Image from transformers import Qwen2VLForConditionalGeneration, AutoProcessor from diffusers import DiffusionPipeline import time def load_model(): """모델 로딩""" print("모델 로딩 중...") start_time = time.time() # GPU 설정 device = "cuda" if torch.cuda.is_available() else "cpu" print(f"Device: {device}") # Qwen2.5-VL 백본 로드 model = Qwen2VLForConditionalGeneration.from_pretrained( "Qwen/Qwen-Image-Layered", torch_dtype=torch.float16, # FP16으로 메모리 절약 device_map="auto", cache_dir="./models/qwen-image-layered" ) # Diffusion 파이프라인 pipeline = DiffusionPipeline.from_pretrained( "Qwen/Qwen-Image-Layered", torch_dtype=torch.float16, cache_dir="./models/qwen-image-layered" ) pipeline = pipeline.to(device) elapsed = time.time() - start_time print(f"✓ 모델 로딩 완료 ({elapsed:.1f}초)") return model, pipeline, device def decompose_image( pipeline, image_path, num_layers=5, resolution=1024, num_steps=50 ): """이미지를 레이어로 분해""" print(f"\n이미지 분해 시작: {image_path}") # 1. 이미지 로드 image = Image.open(image_path).convert("RGB") print(f" 원본 크기: {image.size}") # 2. 리사이즈 image = image.resize((resolution, resolution)) print(f" 처리 크기: {resolution}x{resolution}") # 3. 추론 print(f" 레이어 수: {num_layers}") print(f" Inference steps: {num_steps}") print(" 처리 중...") start_time = time.time() with torch.no_grad(): # 메모리 절약 layers = pipeline( image=image, num_layers=num_layers, resolution=resolution, num_inference_steps=num_steps, guidance_scale=7.5, ).images elapsed = time.time() - start_time print(f"✓ 분해 완료 ({elapsed:.1f}초)") return layers def save_layers(layers, output_dir="output"): """레이어 저장""" import os os.makedirs(output_dir, exist_ok=True) print(f"\n레이어 저장 중: {output_dir}/") for i, layer in enumerate(layers): output_path = f"{output_dir}/layer_{i}.png" layer.save(output_path, format="PNG") # 파일 크기 확인 size_kb = os.path.getsize(output_path) / 1024 print(f" layer_{i}.png - {size_kb:.1f} KB") print(f"✓ {len(layers)}개 레이어 저장 완료") def main(): """메인 실행""" # 1. 모델 로드 model, pipeline, device = load_model() # 2. GPU 메모리 확인 if device == "cuda": allocated = torch.cuda.memory_allocated() / 1024**3 reserved = torch.cuda.memory_reserved() / 1024**3 print(f"\nGPU 메모리 사용량:") print(f" Allocated: {allocated:.2f} GB") print(f" Reserved: {reserved:.2f} GB") # 3. 이미지 분해 layers = decompose_image( pipeline, image_path="test_images/poster.jpg", num_layers=5, resolution=1024, num_steps=50 ) # 4. 결과 저장 save_layers(layers, output_dir="output") print("\n✓ 모든 작업 완료!") if __name__ == "__main__": main() ``` ### 실행 ```bash python test_inference.py ``` **예상 출력**: ``` 모델 로딩 중... Device: cuda ✓ 모델 로딩 완료 (8.3초) GPU 메모리 사용량: Allocated: 6.42 GB Reserved: 6.50 GB 이미지 분해 시작: test_images/poster.jpg 원본 크기: (1920, 1080) 처리 크기: 1024x1024 레이어 수: 5 Inference steps: 50 처리 중... ✓ 분해 완료 (54.2초) 레이어 저장 중: output/ layer_0.png - 523.4 KB layer_1.png - 312.8 KB layer_2.png - 156.2 KB layer_3.png - 89.5 KB layer_4.png - 67.1 KB ✓ 5개 레이어 저장 완료 ✓ 모든 작업 완료! ``` ## 결과 검증 ### 레이어 시각화 ```python # visualize_layers.py import matplotlib.pyplot as plt from PIL import Image def visualize_layers(layer_dir="output", num_layers=5): """레이어 시각화""" fig, axes = plt.subplots(1, num_layers, figsize=(20, 4)) for i in range(num_layers): layer = Image.open(f"{layer_dir}/layer_{i}.png") axes[i].imshow(layer) axes[i].set_title(f"Layer {i}") axes[i].axis('off') plt.tight_layout() plt.savefig("layers_visualization.png", dpi=150) print("✓ 시각화 저장: layers_visualization.png") if __name__ == "__main__": visualize_layers() ``` ### Alpha Channel 검증 ```python # check_alpha.py from PIL import Image import numpy as np def analyze_alpha_channel(layer_path): """Alpha channel 분석""" layer = Image.open(layer_path) if layer.mode != "RGBA": print(f"⚠ {layer_path}는 RGBA가 아님: {layer.mode}") return # Alpha channel 추출 alpha = np.array(layer)[:, :, 3] # 통계 transparent = np.sum(alpha == 0) semi = np.sum((alpha > 0) & (alpha < 255)) opaque = np.sum(alpha == 255) total = alpha.size print(f"\n{layer_path}:") print(f" 투명 픽셀: {transparent:,} ({transparent/total*100:.1f}%)") print(f" 반투명 픽셀: {semi:,} ({semi/total*100:.1f}%)") print(f" 불투명 픽셀: {opaque:,} ({opaque/total*100:.1f}%)") # 모든 레이어 분석 for i in range(5): analyze_alpha_channel(f"output/layer_{i}.png") ``` **출력 예시**: ``` output/layer_0.png: 투명 픽셀: 12,845 (1.2%) 반투명 픽셀: 45,203 (4.3%) 불투명 픽셀: 990,152 (94.5%) output/layer_1.png: 투명 픽셀: 678,912 (64.8%) 반투명 픽셀: 23,401 (2.2%) 불투명 픽셀: 345,887 (33.0%) ``` Layer 0 (배경)은 대부분 불투명, Layer 1 이상은 투명 영역 많음. 정상! ## 성능 최적화 첫 단계 ### FP16 vs FP32 비교 ```python # benchmark.py import time import torch def benchmark_precision(): """FP16 vs FP32 비교""" precisions = [torch.float16, torch.float32] for dtype in precisions: print(f"\n{dtype} 테스트:") # 모델 로드 pipeline = DiffusionPipeline.from_pretrained( "Qwen/Qwen-Image-Layered", torch_dtype=dtype ).to("cuda") # 추론 start = time.time() layers = pipeline( image=test_image, num_layers=5, num_inference_steps=50 ).images elapsed = time.time() - start # 메모리 memory = torch.cuda.max_memory_allocated() / 1024**3 print(f" 시간: {elapsed:.1f}초") print(f" 메모리: {memory:.2f} GB") benchmark_precision() ``` **결과** (RTX 3090): ``` torch.float16: 시간: 54.2초 메모리: 6.42 GB torch.float32: 시간: 112.8초 메모리: 12.85 GB ``` **결론**: FP16이 2배 빠르고 메모리 절반. 품질 차이 거의 없음. ### Inference Steps 최적화 ```python steps_list = [20, 30, 50, 70] for steps in steps_list: start = time.time() layers = pipeline(..., num_inference_steps=steps).images elapsed = time.time() - start print(f"Steps {steps}: {elapsed:.1f}초") ``` **결과**: ``` Steps 20: 22.1초 (품질 저하 눈에 띔) Steps 30: 33.5초 (약간 거칠지만 사용 가능) Steps 50: 54.2초 (권장) Steps 70: 76.8초 (품질 향상 미미) ``` **권장**: 50 steps (품질/속도 균형) ## 일반적인 문제 해결 ### 문제 1: CUDA Out of Memory ``` RuntimeError: CUDA out of memory. Tried to allocate 2.34 GiB ``` **해결**: ```python # 1. Resolution 낮추기 layers = pipeline(..., resolution=640) # 1024 → 640 # 2. Batch size 1로 pipeline.enable_attention_slicing() # 3. CPU 오프로드 pipeline.enable_model_cpu_offload() ``` ### 문제 2: 느린 추론 속도 **체크리스트**: ```bash # GPU 사용 확인 nvidia-smi # GPU 활용률이 낮다면 # 1. CUDA 버전 확인 python -c "import torch; print(torch.version.cuda)" # 2. cuDNN 설치 확인 python -c "import torch; print(torch.backends.cudnn.enabled)" # 3. PyTorch 재설치 pip install torch --force-reinstall --index-url https://download.pytorch.org/whl/cu121 ``` ### 문제 3: 빈 레이어 생성 ```python # 레이어 유효성 검사 def is_valid_layer(layer): """레이어에 내용이 있는지 확인""" alpha = np.array(layer)[:, :, 3] opaque_ratio = np.sum(alpha > 128) / alpha.size return opaque_ratio > 0.01 # 1% 이상 불투명 # 유효한 레이어만 필터링 valid_layers = [l for l in layers if is_valid_layer(l)] ``` ## 다음 단계 v5에서는 **FastAPI 백엔드 구축**을 다룬다: - REST API 엔드포인트 설계 - 파일 업로드 처리 - 비동기 작업 큐 (Redis) - WebSocket 실시간 업데이트 로컬 추론이 성공했으니, 이제 웹 서비스로 감싸자. --- **이전 글**: [Qwen-Image-Layered 모델 깊이 이해 (3/10)](./qwen-image-layered-v3.md) **다음 글**: [FastAPI 백엔드 구축 (5/10)](./qwen-image-layered-v5.md)