
📓Google Colab에서 실습하기
이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.
학습 내용
번호판 검출 모델 - YOLO 기반 Object Detection
학습 목표
- •Object Detection의 개념과 이미지 분류와의 차이를 이해한다
- •YOLO의 작동 원리와 One-stage 검출 방식을 파악한다
- •YOLOv8로 번호판 검출 모델을 설정하고 학습시킨다
- •학습된 모델로 추론하고 결과를 해석한다
Object Detection이란?
비유: 이미지 분류가 "이 사진은 고양이 사진이다"라고 말하는 것이라면, Object Detection은 "이 사진에서 왼쪽 위에 고양이가 있고, 오른쪽 아래에 강아지가 있다"라고 위치까지 알려주는 기술입니다. 단순히 "뭐가 있나?"가 아니라 "어디에 뭐가 있나?"를 답하는 것이죠.
이미지 분류 vs Object Detection vs Segmentation
| 작업 | 질문 | 출력 | 예시 |
|---|---|---|---|
| 이미지 분류 | "이 사진에 뭐가 있나?" | 클래스 1개 | "자동차" |
| Object Detection | "어디에 뭐가 있나?" | 바운딩 박스 + 클래스 (여러 개) | (100,200,300,250) = "번호판" |
| Instance Segmentation | "정확히 어디까지가 그 물체인가?" | 픽셀 단위 마스크 | 번호판 모양의 마스크 |
번호판 인식의 1단계에는 Object Detection이 가장 적합합니다. 번호판의 정확한 위치(사각형 영역)만 알면 되기 때문이죠.
바운딩 박스 이해하기
python# 바운딩 박스의 두 가지 표현 방식 import numpy as np # 방식 1: (x_center, y_center, width, height) - YOLO 형식 yolo_box = np.array([0.5, 0.45, 0.3, 0.1]) # 정규화된 값 print("=== YOLO 형식 (정규화) ===") print(f" 중심: ({yolo_box[0]}, {yolo_box[1]})") print(f" 크기: {yolo_box[2]} x {yolo_box[3]}") # 방식 2: (x1, y1, x2, y2) - 좌상단/우하단 좌표 # 1920x1080 이미지에서의 실제 좌표로 변환 img_w, img_h = 1920, 1080 x1 = int((yolo_box[0] - yolo_box[2]/2) * img_w) y1 = int((yolo_box[1] - yolo_box[3]/2) * img_h) x2 = int((yolo_box[0] + yolo_box[2]/2) * img_w) y2 = int((yolo_box[1] + yolo_box[3]/2) * img_h) print() print(f"=== 절대 좌표 (1920x1080 기준) ===") print(f" 좌상단: ({x1}, {y1})") print(f" 우하단: ({x2}, {y2})") print(f" 번호판 크기: {x2-x1} x {y2-y1} 픽셀")
Two-stage vs One-stage 검출기
Object Detection 모델은 크게 두 부류로 나뉩니다.
접근 방식 비교
| 항목 | Two-stage (예: Faster R-CNN) | One-stage (예: YOLO, SSD) |
|---|---|---|
| 작동 방식 | 후보 영역 생성 → 분류 | 한 번에 위치 + 분류 |
| 속도 | 느림 (5~10 FPS) | 빠름 (30~100+ FPS) |
| 정확도 | 일반적으로 높음 | 최신 모델은 거의 동등 |
| 적합한 용도 | 정밀 검출 (의료 등) | 실시간 처리 (자율주행, CCTV) |
| 대표 모델 | Faster R-CNN, Mask R-CNN | YOLO, SSD, RetinaNet |
비유: Two-stage 검출기는 시험에서 "일단 답이 될 만한 것을 추려내고(1단계), 그중에서 정답을 고르는(2단계)" 방식입니다. One-stage 검출기는 "문제를 보자마자 바로 답을 쓰는" 방식이죠. 번호판 인식처럼 빠른 응답이 필요한 경우에는 One-stage가 유리합니다.
YOLO 심층 이해
YOLO는 You Only Look Once의 약자입니다. 이름 그대로, 이미지를 한 번만 봐서 모든 물체를 검출합니다.
YOLO의 핵심 아이디어
python# YOLO의 그리드 분할 개념을 이해해봅시다 import numpy as np # 이미지를 SxS 그리드로 나눕니다 grid_size = 13 # 13x13 그리드 total_cells = grid_size * grid_size boxes_per_cell = 3 # 각 셀당 예측하는 박스 수 print(f"=== YOLO 그리드 분할 ===") print(f"그리드 크기: {grid_size} x {grid_size} = {total_cells}개 셀") print(f"셀당 예측 박스: {boxes_per_cell}개") print(f"총 예측 박스: {total_cells * boxes_per_cell}개") # 각 박스가 예측하는 정보 predictions_per_box = 5 + 1 # (x, y, w, h, confidence) + class print() print(f"각 박스가 예측하는 값:") print(f" - x, y: 박스 중심 좌표 (2개)") print(f" - w, h: 박스 크기 (2개)") print(f" - confidence: 물체 존재 확률 (1개)") print(f" - class: 번호판 클래스 확률 (1개)") print(f" 합계: {predictions_per_box}개 값/박스")
NMS (Non-Maximum Suppression)
하나의 물체에 대해 여러 박스가 겹칠 수 있습니다. NMS는 가장 좋은 박스만 남기는 후처리 기법입니다.
pythonimport numpy as np # NMS의 작동 원리 시뮬레이션 # 같은 번호판에 대해 3개의 겹치는 박스가 검출되었다고 가정 boxes = [ {"confidence": 0.95, "box": [100, 200, 300, 250]}, {"confidence": 0.87, "box": [105, 198, 295, 248]}, {"confidence": 0.72, "box": [110, 205, 310, 255]}, ] print("=== NMS 전: 중복 박스 3개 ===") for i, b in enumerate(boxes): print(f" 박스 {i+1}: confidence={b['confidence']}, 좌표={b['box']}") # NMS 적용: 가장 높은 신뢰도의 박스만 선택 # (실제로는 IoU 기반으로 겹침 정도를 계산) best = max(boxes, key=lambda x: x["confidence"]) print() print(f"=== NMS 후: 최적 박스 1개 ===") print(f" 선택: confidence={best['confidence']}, 좌표={best['box']}")
YOLO 버전별 발전 과정
| 버전 | 연도 | Backbone | 핵심 개선 | mAP (COCO) |
|---|---|---|---|---|
| YOLOv1 | 2016 | GoogLeNet | 실시간 검출의 시작 | 63.4% |
| YOLOv2 | 2017 | Darknet-19 | Batch Norm, Anchor Box | 76.8% |
| YOLOv3 | 2018 | Darknet-53 | FPN, 다중 스케일 | 55.3% |
| YOLOv5 | 2020 | CSPNet | PyTorch 기반, 사용 편리 | 56.8% |
| YOLOv8 | 2023 | CSPNet-v2 | Anchor-free, 최신 구조 | 53.9% |
우리 프로젝트에서는 가장 최신이고 사용이 편리한 YOLOv8을 사용합니다.
YOLOv8 모델 크기 선택
YOLOv8은 동일한 구조에서 크기만 다른 5가지 변형을 제공합니다.
| 모델 | 파라미터 수 | 추론 속도 (ms) | mAP | 추천 상황 |
|---|---|---|---|---|
| yolov8n | 3.2M | 1.2ms | 37.3% | 모바일, 엣지 디바이스 |
| yolov8s | 11.2M | 2.1ms | 44.9% | 경량 서버, 실시간 처리 |
| yolov8m | 25.9M | 5.0ms | 50.2% | 범용 서버 |
| yolov8l | 43.7M | 8.0ms | 52.9% | 고성능 서버 |
| yolov8x | 68.2M | 12.0ms | 53.9% | 최고 정확도 요구 시 |
pythonimport numpy as np # 모델 크기에 따른 파라미터 수와 속도 시각화 models = ["yolov8n", "yolov8s", "yolov8m", "yolov8l", "yolov8x"] params = np.array([3.2, 11.2, 25.9, 43.7, 68.2]) # 백만 단위 speed = np.array([1.2, 2.1, 5.0, 8.0, 12.0]) # ms 단위 mAP = np.array([37.3, 44.9, 50.2, 52.9, 53.9]) # % print("=== YOLOv8 모델 크기별 비교 ===") print(f"{'모델':<12} {'파라미터':<12} {'속도(ms)':<10} {'mAP':<8} {'추천도'}") print("-" * 55) for i, m in enumerate(models): rec = "***" if m == "yolov8s" else "**" if m in ["yolov8n", "yolov8m"] else "*" print(f"{m:<12} {params[i]:>6.1f}M {speed[i]:>5.1f} {mAP[i]:>5.1f}% {rec}") print() print(f"번호판 검출 추천: yolov8s (속도와 정확도의 균형)")
YOLOv8 커스텀 데이터셋 학습
데이터셋 구조
설정 파일 작성
yaml# dataset.yaml path: ./dataset # 데이터셋 루트 경로 train: train/images # 학습 이미지 경로 (상대) val: val/images # 검증 이미지 경로 (상대) # 클래스 정의 names: 0: plate # 번호판 (단일 클래스) # 참고: 여러 클래스가 필요하면 추가 가능 # names: # 0: plate_normal # 일반 번호판 # 1: plate_new # 신형 번호판 # 2: plate_commercial # 영업용 번호판
학습 실행
pythonfrom ultralytics import YOLO # 사전 학습된 모델을 기반으로 Fine-tuning model = YOLO("yolov8s.pt") # 학습 시작 results = model.train( data="dataset.yaml", epochs=100, # 학습 반복 횟수 batch=16, # 배치 크기 (GPU 메모리에 따라 조정) imgsz=640, # 입력 이미지 크기 (640 권장) device="cuda", # GPU 사용 patience=20, # Early stopping (20 에포크 개선 없으면 중단) lr0=0.01, # 초기 학습률 lrf=0.01, # 최종 학습률 비율 augment=True, # 내장 데이터 증강 활성화 name="plate_detect", # 실험 이름 )
학습 결과 파일 구조
학습 결과 해석
학습이 끝나면 반드시 결과를 분석해야 합니다.
핵심 메트릭 이해
| 메트릭 | 의미 | 좋은 값 | 해석 |
|---|---|---|---|
| mAP@50 | IoU 50% 기준 평균 정밀도 | 90%+ | 번호판을 얼마나 잘 찾는가 |
| mAP@50:95 | 다양한 IoU 기준 평균 | 70%+ | 박스 위치가 얼마나 정확한가 |
| Precision | 검출한 것 중 맞는 비율 | 90%+ | 거짓 양성(오탐)이 적은가 |
| Recall | 실제 중 찾아낸 비율 | 90%+ | 놓친 번호판이 적은가 |
| Loss | 학습 오차 | 꾸준히 감소 | 학습이 잘 진행되는가 |
pythonimport numpy as np # Precision과 Recall의 트레이드오프 이해 # 예시: 100장 이미지에 총 100개의 번호판이 있을 때 total_plates = 100 # 실제 번호판 수 detected = 95 # 검출한 수 correct = 90 # 그 중 맞은 수 missed = total_plates - correct # 놓친 수 false_positive = detected - correct # 오탐 수 precision = correct / detected * 100 recall = correct / total_plates * 100 f1 = 2 * precision * recall / (precision + recall) print("=== 검출 성능 분석 ===") print(f"실제 번호판: {total_plates}개") print(f"검출 시도: {detected}개") print(f"정확한 검출: {correct}개") print(f"놓친 번호판: {missed}개") print(f"오탐 (없는데 있다고 한 것): {false_positive}개") print() print(f"Precision: {precision:.1f}% (검출한 것 중 맞은 비율)") print(f"Recall: {recall:.1f}% (실제 중 찾아낸 비율)") print(f"F1 Score: {f1:.1f}% (Precision과 Recall의 조화 평균)")
학습된 모델로 추론
python⚠️ 로컬 실행 필요from ultralytics import YOLO import cv2 # 학습된 모델 로드 model = YOLO("runs/detect/plate_detect/weights/best.pt") # 이미지 추론 results = model("test_image.jpg", conf=0.5) # 신뢰도 50% 이상만 # 결과 파싱 for box in results[0].boxes: x1, y1, x2, y2 = box.xyxy[0].int().tolist() conf = box.conf[0].item() print(f"번호판 위치: ({x1}, {y1}) ~ ({x2}, {y2})") print(f"신뢰도: {conf:.2%}") # 번호판 영역 크롭 (다음 단계 입력용) plate_crop = results[0].orig_img[y1:y2, x1:x2] cv2.imwrite("cropped_plate.jpg", plate_crop)
검출 성능 향상 팁
학습 결과가 만족스럽지 않을 때 시도할 수 있는 개선 방법들입니다:
| 문제 | 증상 | 해결 방법 |
|---|---|---|
| 과적합 | 학습 Loss 낮은데 검증 mAP 안 오름 | 데이터 증강 강화, Dropout 추가 |
| 과소적합 | 학습 Loss도 높음 | 에포크 증가, 학습률 조정 |
| 작은 번호판 놓침 | 멀리 있는 차량 미검출 | imgsz 키우기 (640 → 1280) |
| 야간 성능 저하 | 어두운 이미지에서 미검출 | 야간 데이터 추가, 밝기 증강 |
| 오탐 많음 | 번호판 아닌 것을 검출 | Hard Negative Mining, conf 임계값 상향 |
핵심 정리
| 개념 | 설명 | 비유 |
|---|---|---|
| Object Detection | 이미지에서 물체 위치 + 클래스 | "어디에 뭐가 있나?" |
| YOLO | 한 번에 검출하는 One-stage 방식 | 문제 보자마자 답 쓰기 |
| NMS | 중복 박스 제거 후처리 | 같은 답 중 가장 자신 있는 것만 선택 |
| mAP | 검출 정확도 종합 지표 | 시험 총점 |
| Fine-tuning | 사전학습 모델에 내 데이터로 추가 학습 | 기본기 있는 선수를 특화 훈련 |
- •Object Detection: 위치(바운딩 박스) + 클래스를 동시에 예측하는 기술
- •YOLO: One-stage 검출기로 실시간 처리에 최적. 번호판 검출에 적합
- •YOLOv8s: 속도와 정확도의 균형이 좋은 모델. Ultralytics로 쉽게 사용
- •학습 파이프라인: 데이터셋 준비 → YAML 설정 → train() → best.pt 생성
- •결과 분석: mAP, Precision, Recall을 함께 보고 약점을 파악해 개선
레슨 정보
- 레벨
- Level 9: 종합 프로젝트
- 예상 소요 시간
- 90분
- 참고 영상
- YouTube 링크
💡실습 환경 안내
이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.
Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.