Level 9: 종합 프로젝트
🏆

Level 9

모델 배포

FastAPI, TorchScript, ONNX, 엣지 배포

60분
모델 배포 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.

학습 내용

모델 배포

학습 목표

  • 학습된 모델을 저장하고 최적화하는 방법을 배운다
  • FastAPI로 REST API 서버를 구축한다
  • TorchScript와 ONNX 변환을 이해한다
  • Docker 컨테이너화와 엣지 디바이스 배포를 알아본다

왜 배포가 중요한가?

비유: 아무리 맛있는 요리를 만들어도, 손님 테이블에 가져다주지 않으면 아무 소용이 없습니다.

모델 학습은 "요리"이고, 배포는 "서빙"입니다. 주방(개발 환경)에서 잘 되던 모델이 식당(운영 환경)에서도 잘 동작하도록 만드는 과정이 배포입니다.

배포에서 고려해야 할 핵심 요소들이 있습니다.

요소질문예시
성능충분히 빠른가?30ms 이내 응답
안정성24시간 안정적인가?에러 처리, 메모리 관리
확장성사용자가 늘면?로드 밸런싱
환경어디서 실행되는가?클라우드, 엣지, 모바일

모델 저장과 최적화

PyTorch 모델 저장 방식

import torch

# 방법 1: state_dict만 저장 (권장)
# 모델 구조는 코드에, 가중치만 파일에 저장
torch.save(model.state_dict(), "char_classifier_weights.pth")

# 로드할 때: 모델 구조를 먼저 만들고 가중치를 불러옴
model = CharClassifier(num_classes=50)
model.load_state_dict(torch.load("char_classifier_weights.pth"))
model.eval()

# 방법 2: 전체 모델 저장
# pickle 기반 - 코드 구조가 바뀌면 깨질 수 있음
torch.save(model, "char_classifier_full.pth")
model = torch.load("char_classifier_full.pth")

TorchScript 변환

TorchScript는 PyTorch 모델을 Python 없이 실행 가능한 형태로 변환합니다. C++ 환경에서도 실행할 수 있어 배포에 유리합니다.

import torch

# 방법 1: torch.jit.trace (입력 예시 기반)
dummy_input = torch.randn(1, 1, 32, 32)
traced_model = torch.jit.trace(model, dummy_input)
traced_model.save("classifier_traced.pt")

# 방법 2: torch.jit.script (코드 분석 기반)
scripted_model = torch.jit.script(model)
scripted_model.save("classifier_scripted.pt")

# 로드 (Python 없이도 가능)
loaded = torch.jit.load("classifier_traced.pt")
output = loaded(dummy_input)

ONNX 변환

비유: ONNX는 "만국 공통어"입니다. PyTorch로 만든 모델을 TensorFlow, TensorRT 등 어디서든 쓸 수 있게 변환합니다.

import torch

# ONNX로 변환
dummy_input = torch.randn(1, 1, 32, 32)
torch.onnx.export(
    model,
    dummy_input,
    "classifier.onnx",
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={"input": {0: "batch_size"}}  # 배치 크기 가변
)

# ONNX Runtime으로 추론
import onnxruntime as ort

session = ort.InferenceSession("classifier.onnx")
input_data = dummy_input.numpy()
result = session.run(None, {"input": input_data})
print(f"Output shape: {result[0].shape}")
변환 형식장점적합한 경우
state_dict유연, 디버깅 쉬움개발/실험 단계
TorchScriptPython 불필요C++ 서버, 모바일
ONNX프레임워크 독립TensorRT, 엣지 디바이스

FastAPI로 REST API 구축

프로젝트 구조

plate-api/
    main.py              # FastAPI 앱
    models/
        detector.pt      # YOLO 모델
        classifier.pth   # CNN 모델
    recognizer.py        # 인식 파이프라인
    requirements.txt     # 의존성
    Dockerfile           # 컨테이너 설정

FastAPI 서버 코드

python
⚠️ 로컬 실행 필요
from fastapi import FastAPI, UploadFile, File, HTTPException from fastapi.responses import JSONResponse import numpy as np import cv2 import time app = FastAPI( title="License Plate Recognition API", description="Upload a car image to recognize the license plate", version="1.0.0" ) # 모델은 서버 시작 시 한 번만 로드 recognizer = None @app.on_event("startup") async def load_models(): global recognizer recognizer = PlateRecognizer( detector_path="models/detector.pt", classifier_path="models/classifier.pth", class_names=CLASS_NAMES ) print("Models loaded successfully") @app.post("/recognize") async def recognize_plate(file: UploadFile = File(...)): """번호판 인식 API""" # 파일 형식 검증 if not file.content_type.startswith("image/"): raise HTTPException(status_code=400, detail="Image file required") # 이미지 읽기 contents = await file.read() nparr = np.frombuffer(contents, np.uint8) image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) if image is None: raise HTTPException(status_code=400, detail="Invalid image") # 인식 수행 및 시간 측정 start_time = time.time() result = recognizer.recognize(image) elapsed = time.time() - start_time result["processing_time_ms"] = round(elapsed * 1000, 1) return JSONResponse(content=result) @app.get("/health") async def health_check(): """서버 상태 확인""" return { "status": "healthy", "model_loaded": recognizer is not None }

서버 실행과 테스트

bash
# 설치 pip install fastapi uvicorn python-multipart # 개발 모드 (코드 변경 시 자동 재시작) uvicorn main:app --reload --host 0.0.0.0 --port 8000 # 프로덕션 모드 (워커 여러 개) uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 # API 문서 자동 생성: http://localhost:8000/docs

API 호출 예시

python
import requests # 이미지 파일 전송 with open("test_car.jpg", "rb") as f: response = requests.post( "http://localhost:8000/recognize", files={"file": ("test_car.jpg", f, "image/jpeg")} ) result = response.json() print(f"Plate: {result.get('plate_text', 'N/A')}") print(f"Confidence: {result.get('confidence', 0):.2%}") print(f"Processing time: {result.get('processing_time_ms', 0)}ms")

Docker 컨테이너화

비유: Docker는 "이삿짐 박스"입니다. 모든 것(코드, 모델, 라이브러리)을 하나의 박스에 담아서, 어떤 집(서버)으로 옮겨도 똑같이 동작하게 만듭니다.

Dockerfile

dockerfile
FROM python:3.9-slim WORKDIR /app # 시스템 패키지 (OpenCV 의존성) RUN apt-get update && apt-get install -y libgl1-mesa-glx libglib2.0-0 && rm -rf /var/lib/apt/lists/* # Python 패키지 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 코드와 모델 복사 COPY . . EXPOSE 8000 # 헬스체크 HEALTHCHECK --interval=30s --timeout=10s CMD curl -f http://localhost:8000/health || exit 1 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

빌드와 실행

bash
# 이미지 빌드 docker build -t plate-recognition:v1 . # CPU로 실행 docker run -d -p 8000:8000 --name plate-api plate-recognition:v1 # GPU 사용 시 (nvidia-docker 필요) docker run -d --gpus all -p 8000:8000 --name plate-api plate-recognition:v1 # 로그 확인 docker logs -f plate-api

엣지 디바이스 배포

모든 시스템이 클라우드에 있을 필요는 없습니다. 카메라 옆에 소형 컴퓨터를 두고 현장에서 바로 인식하는 것이 엣지 배포입니다.

디바이스GPU용도
NVIDIA Jetson Nano128 CUDA cores소규모 현장
NVIDIA Jetson Xavier512 CUDA cores중규모 시설
Raspberry Pi + Coral TPUEdge TPU초저전력 환경
Intel NUC + OpenVINOCPU 최적화서버룸 없는 환경

엣지 배포 최적화 전략

python
# 1. 모델 경량화 # YOLOv8n (nano) 사용 - 가장 작은 모델 model = YOLO("yolov8n.pt") # 2. ONNX + TensorRT 변환 (Jetson에서) model.export(format="engine") # TensorRT 엔진 생성 # 3. 입력 해상도 줄이기 # 640x640 -> 320x320 (속도 4배 향상, 정확도 소폭 감소) model = YOLO("best.pt") results = model.predict(image, imgsz=320) # 4. FP16 (반정밀도) 사용 model.export(format="onnx", half=True) # 모델 크기 절반, 속도 향상

배포 아키텍처 비교

구성장점단점
클라우드 전용강력한 GPU, 확장 쉬움네트워크 지연, 비용
엣지 전용즉시 응답, 오프라인 가능하드웨어 제한, 업데이트 어려움
하이브리드균형잡힌 구성복잡한 관리

하이브리드 구성에서는 엣지에서 빠른 1차 인식을 하고, 확신이 낮은 경우만 클라우드로 보내 정밀 인식을 수행합니다.


배포 체크리스트

실제 서비스에 배포하기 전에 확인해야 할 항목들입니다.

항목확인 내용
모델 버전어떤 버전의 모델이 배포되었는가?
에러 처리잘못된 입력에 대해 적절한 응답을 하는가?
로깅요청/응답/에러가 기록되는가?
보안API 인증이 설정되었는가?
모니터링응답 시간, 에러율을 추적하는가?
롤백문제 발생 시 이전 버전으로 돌아갈 수 있는가?

핵심 정리

  1. 모델 저장: state_dict(유연), TorchScript(C++ 호환), ONNX(범용)
  2. FastAPI: 비동기 REST API로 이미지를 받아 인식 결과를 JSON으로 반환
  3. Docker: 코드, 모델, 의존성을 하나로 패키징하여 어디서든 동일하게 실행
  4. 엣지 배포: Jetson, Coral TPU 등 현장 디바이스에서 실시간 처리
  5. 최적화: TensorRT, FP16, 해상도 조정으로 추론 속도 향상

레슨 정보

레벨
Level 9: 종합 프로젝트
예상 소요 시간
60분
참고 영상
YouTube 링크

💡실습 환경 안내

이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.

Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.