Level 9: 종합 프로젝트
🏆

Level 9

데이터 수집/준비

이미지 수집, 라벨링, 전처리

60분
데이터 수집/준비 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

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

학습 내용

데이터 파이프라인 - 수집, 라벨링, 전처리

학습 목표

  • 번호판 이미지 데이터를 체계적으로 수집하는 전략을 세운다
  • YOLO 형식과 OCR 형식의 라벨링 방법을 이해하고 적용한다
  • 전처리 파이프라인을 설계하고 구현한다
  • 데이터 증강으로 학습 데이터의 양과 다양성을 확보한다

데이터가 모델의 운명을 결정한다

비유: 최고급 오븐을 사도 밀가루가 상했으면 맛있는 빵을 만들 수 없습니다. AI 모델도 마찬가지입니다. 아무리 좋은 모델 구조를 설계해도, 학습 데이터의 품질이 나쁘면 성능은 바닥입니다. "Garbage In, Garbage Out"이라는 격언이 AI 분야에서도 그대로 적용됩니다.

실제 산업 현장에서 AI 프로젝트의 시간 배분을 보면 놀라운 사실을 발견합니다:

작업시간 비중설명
데이터 수집 및 정제약 40%가장 많은 시간 소요
데이터 라벨링약 20%노동집약적 작업
모델 설계 및 학습약 25%실제 "AI" 작업
평가 및 배포약 15%최종 마무리

데이터 관련 작업이 전체의 **60%**를 차지합니다. 그만큼 중요하다는 뜻이죠.


데이터 수집 전략

번호판 이미지를 모으는 방법은 크게 네 가지입니다. 각 방법의 장단점을 정확히 알고 조합하는 것이 핵심입니다.

수집 방법 비교

방법장점단점권장 비율
공개 데이터셋즉시 사용 가능, 라벨 포함한국 데이터 부족, 품질 편차30~40%
직접 촬영실제 환경 반영, 품질 통제 가능시간 소요, 개인정보 이슈20~30%
웹 크롤링대량 수집 가능저작권/개인정보 주의10~20%
합성 데이터무한 생성, 완벽한 라벨실제 환경과 차이20~30%

1. 공개 데이터셋 활용

python
# 주요 공개 데이터셋 소스 datasets = { "Roboflow Universe": "다양한 번호판 데이터셋, YOLO 형식 지원", "Kaggle": "Korean License Plate Dataset 검색", "AI Hub (한국)": "한국 차량 번호판 데이터셋 (정부 제공)", "GitHub": "오픈소스 프로젝트의 학습 데이터" } for source, desc in datasets.items(): print(f" {source}: {desc}")

2. 직접 촬영 가이드라인

촬영할 때 다양성을 확보하는 것이 가장 중요합니다:

조건변형 항목예시
시간대조명 변화아침, 낮, 저녁, 밤
날씨환경 조건맑음, 흐림, 비, 눈
거리번호판 크기3m, 5m, 10m, 20m
각도촬영 방향정면, 측면 15도, 30도
차종번호판 위치승용차, SUV, 트럭, 오토바이

3. 합성 데이터 생성

실제 데이터가 부족할 때, 프로그래밍으로 번호판 이미지를 만들 수 있습니다.

참고: 실제 이미지 생성은 PIL 라이브러리가 필요합니다. 아래는 개념 시뮬레이션입니다.

python
import numpy as np def generate_plate_simulation(text, noise_level=10): """합성 번호판 이미지 생성 시뮬레이션""" # 400x80 크기의 흰색 배경 (RGB) img = np.ones((80, 400, 3), dtype=np.uint8) * 255 # 테두리 시뮬레이션 (상하좌우 가장자리를 검은색으로) img[0:3, :, :] = 0 # 상단 img[-3:, :, :] = 0 # 하단 img[:, 0:3, :] = 0 # 좌측 img[:, -3:, :] = 0 # 우측 # 텍스트 영역 시뮬레이션 (중앙에 어두운 영역) img[20:60, 50:350, :] = np.random.randint(0, 50, (40, 300, 3)) # 노이즈 추가 noise = np.random.normal(0, noise_level, img.shape) img = np.clip(img + noise, 0, 255).astype(np.uint8) return img # 테스트 plates = ["12A 3456", "34B 7890", "56C 1234"] print("=== Synthetic Plate Generation ===") for text in plates: img = generate_plate_simulation(text) print(f"Plate '{text}': shape={img.shape}, dtype={img.dtype}") print(f" Pixel range: {img.min()} ~ {img.max()}")

핵심 팁: 합성 데이터만으로는 실제 환경의 복잡함을 재현할 수 없습니다. 반드시 실제 데이터와 혼합해서 사용하세요.


데이터셋 규모 가이드

모델 성능과 데이터 양의 관계를 이해하는 것이 중요합니다.

데이터 규모검출 성능 (mAP)인식 성능 (Accuracy)적합한 용도
100장50~60%60~70%프로토타입, 파이프라인 테스트
1,000장70~80%80~85%개념 증명 (PoC)
5,000장85~90%90~93%초기 제품 수준
10,000장+90~95%95%+상용 서비스 수준

비유: 시험 공부를 할 때 기출문제 10개만 풀면 감을 잡을 수 있지만, 100개를 풀어야 실전에서 안정적으로 고득점할 수 있는 것과 같습니다.


데이터 라벨링

라벨링은 "이 이미지에서 정답이 무엇인지"를 표시하는 작업입니다. 우리 프로젝트는 두 종류의 라벨이 필요합니다.

라벨링 유형 비교

대상라벨 형식용도예시
번호판 위치바운딩 박스 좌표 (YOLO 형식)검출 모델 학습0 0.5 0.45 0.3 0.1
번호판 텍스트문자열인식 모델 학습12가3456

YOLO 형식 라벨링 상세

# YOLO 라벨 형식 (labels/img001.txt)
# <class_id> <x_center> <y_center> <width> <height>
# 모든 값은 이미지 크기로 정규화 (0~1 사이)

# 예: 1920x1080 이미지에서 번호판이 (760, 440)~(960, 480)에 있을 때
# x_center = (760 + 960) / 2 / 1920 = 0.448
# y_center = (440 + 480) / 2 / 1080 = 0.426
# width    = (960 - 760) / 1920      = 0.104
# height   = (480 - 440) / 1080      = 0.037

0 0.448 0.426 0.104 0.037

라벨링 도구 비교

도구유형출력 형식난이도추천 용도
LabelImg데스크톱 앱YOLO, VOC쉬움소규모 (100장 이하)
CVAT웹 기반다양한 형식보통중규모, 팀 작업
Roboflow클라우드YOLO + 증강쉬움빠른 프로토타이핑
Label Studio웹 기반커스텀 가능보통복잡한 라벨링 작업

문자 인식용 라벨링

python
# 라벨 파일 구조 (CSV 형식) import numpy as np # 파일명과 번호판 텍스트 매핑 labels = [ ["img001.jpg", "12가3456"], ["img002.jpg", "34나7890"], ["img003.jpg", "123가4567"], ["img004.jpg", "56다1234"], ] print("=== 라벨 데이터 예시 ===") for fname, text in labels: num_digits = sum(1 for c in text if c.isdigit()) num_hangul = sum(1 for c in text if ord(c) >= 0xAC00) print(f" {fname} -> {text} (숫자 {num_digits}개, 한글 {num_hangul}개)")

라벨링 품질 관리

경고: 라벨 하나가 잘못되면 모델이 그 잘못된 정보를 "정답"으로 학습합니다. 100장의 깨끗한 라벨이 1,000장의 지저분한 라벨보다 낫습니다.

검증 항목체크 방법흔한 실수
바운딩 박스 정확성시각적 검토번호판 일부만 포함, 너무 넓게 잡기
텍스트 오타이중 검수숫자 0과 영문 O 혼동
클래스 일관성라벨 통계 확인같은 문자를 다른 클래스로 라벨링
누락 검출이미지당 라벨 수 확인이미지에 번호판이 있는데 라벨 없음

데이터 전처리 파이프라인

수집한 원본 이미지를 모델이 학습할 수 있는 형태로 가공하는 과정입니다.

전처리 단계 흐름

[원본 이미지] → [크기 조정] → [색상 변환] → [정규화] → [텐서 변환] → [모델 입력]

1. 크기 조정 (Resize)

python
import numpy as np # YOLO 모델은 고정 크기 입력을 받습니다 # 원본 이미지가 다양한 크기이므로 통일해야 합니다 original_sizes = [(1920, 1080), (1280, 720), (640, 480), (3840, 2160)] target_size = (416, 416) # YOLOv8 기본 입력 크기 print("=== 크기 조정 예시 ===") for w, h in original_sizes: scale_w = target_size[0] / w scale_h = target_size[1] / h print(f" {w}x{h} -> {target_size[0]}x{target_size[1]}") print(f" 가로 축소 비율: {scale_w:.3f}, 세로 축소 비율: {scale_h:.3f}")

2. 정규화 (Normalization)

python
import numpy as np # 픽셀 값을 0~255에서 0~1로 변환 # 신경망은 작은 범위의 값에서 더 잘 학습합니다 sample_pixels = np.array([0, 64, 128, 192, 255]) normalized = sample_pixels / 255.0 print("=== 정규화 전후 비교 ===") for orig, norm in zip(sample_pixels, normalized): print(f" {orig:3d} -> {norm:.4f}") print() print(f"원래 범위: [{sample_pixels.min()}, {sample_pixels.max()}]") print(f"정규화 후: [{normalized.min():.1f}, {normalized.max():.1f}]")

3. 색상 변환

python
import numpy as np # OpenCV는 BGR 순서, PyTorch/YOLO는 RGB 순서 # 반드시 변환이 필요합니다! # BGR -> RGB 변환 시뮬레이션 def bgr_to_rgb(img): """BGR to RGB conversion""" return img[:, :, ::-1] # 그레이스케일 변환 def to_grayscale(img): """RGB to Grayscale: 0.299*R + 0.587*G + 0.114*B""" return np.dot(img[..., :3], [0.299, 0.587, 0.114]) # 이진화 (Otsu 방식 시뮬레이션) def threshold_otsu(gray, threshold=128): """Simple thresholding (Otsu simulation)""" return (gray > threshold).astype(np.uint8) * 255 # 테스트 sample_img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) gray = to_grayscale(sample_img) binary = threshold_otsu(gray) print("=== Color Conversion Demo ===") print(f"Original (BGR): shape={sample_img.shape}") print(f"Grayscale: shape={gray.shape}, range=[{gray.min():.0f}, {gray.max():.0f}]") print(f"Binary: unique values={np.unique(binary)}")

4. 데이터셋 분할

python
import numpy as np # 전체 데이터를 학습/검증/테스트로 분할 total_images = 5000 np.random.seed(42) # 일반적인 비율: 학습 70%, 검증 15%, 테스트 15% splits = { "학습 (Train)": int(total_images * 0.70), "검증 (Validation)": int(total_images * 0.15), "테스트 (Test)": int(total_images * 0.15), } print("=== 데이터셋 분할 ===") for name, count in splits.items(): ratio = count / total_images * 100 print(f" {name}: {count}장 ({ratio:.0f}%)") print(f" 합계: {sum(splits.values())}장")

주의: 검증 세트와 테스트 세트는 학습에 절대 사용하지 않습니다. 같은 차량의 사진이 학습 세트와 테스트 세트에 동시에 들어가지 않도록 주의하세요 (데이터 누수 방지).


데이터 증강 (Data Augmentation)

비유: 한 명의 학생이 같은 수학 문제만 반복하면 그 문제는 잘 풀지만 새로운 유형에는 약합니다. 같은 문제를 숫자만 바꿔서 여러 변형을 만들면 훨씬 유연하게 대응할 수 있죠. 데이터 증강이 바로 이런 역할입니다.

번호판 검출에 유용한 증강 기법

기법시뮬레이션 대상적용 확률주의사항
밝기/대비 변화주간/야간, 역광50%너무 극단적이면 비현실적
가우시안 노이즈카메라 센서 노이즈30%문자 가독성 유지
모션 블러이동 중 촬영20%과도하면 학습 방해
회전기울어진 촬영30%번호판은 보통 +-15도 이내
비/안개 효과악천후20%번호판이 완전히 가려지면 안됨
색상 변환 (Hue)다양한 조명 색온도30%번호판 색상 왜곡 주의

Albumentations 증강 파이프라인

참고: 실제 구현은 albumentations 라이브러리 설치 필요. 아래는 증강 개념 시뮬레이션입니다.

python
import numpy as np def augment_brightness(img, factor=0.3): """Brightness adjustment""" delta = np.random.uniform(-factor, factor) return np.clip(img * (1 + delta), 0, 255).astype(np.uint8) def augment_noise(img, var=25): """Gaussian noise""" noise = np.random.normal(0, var, img.shape) return np.clip(img + noise, 0, 255).astype(np.uint8) def augment_rotate(img, angle_limit=15): """Rotation simulation (simplified)""" angle = np.random.uniform(-angle_limit, angle_limit) return img, angle # Return angle for reference # Demo np.random.seed(42) sample = np.random.randint(100, 200, (64, 64, 3), dtype=np.uint8) print("=== Data Augmentation Demo ===") print(f"Original: mean={sample.mean():.1f}") bright = augment_brightness(sample, 0.3) print(f"Brightness adjusted: mean={bright.mean():.1f}") noisy = augment_noise(sample, 25) print(f"With noise: mean={noisy.mean():.1f}, std={noisy.std():.1f}") _, angle = augment_rotate(sample, 15) print(f"Rotation angle: {angle:.1f} degrees") print() print("Augmentation pipeline (Albumentations):") print(" 1. RandomBrightnessContrast (p=0.5)") print(" 2. GaussNoise (p=0.3)") print(" 3. MotionBlur (p=0.2)") print(" 4. Rotate (limit=15, p=0.3)") print(" 5. Resize(416, 416)")

증강 전후 데이터 규모 변화

python
import numpy as np # 증강으로 데이터 양을 몇 배로 늘릴 수 있습니다 original_count = 1000 augmentation_factor = 5 # 원본 1장 -> 5장의 변형 augmented_count = original_count * augmentation_factor print(f"=== 데이터 증강 효과 ===") print(f"원본 데이터: {original_count}장") print(f"증강 배수: {augmentation_factor}배") print(f"증강 후 데이터: {augmented_count}장") print(f"증가량: +{augmented_count - original_count}장") # 주의: 증강 데이터는 원본의 변형일 뿐 # 실제 다양한 데이터를 대체할 수는 없습니다 print() print(f"주의: 증강은 다양성을 높이지만,") print(f"새로운 장면을 만들어내지는 않습니다!")

디렉토리 구조 설계

체계적인 디렉토리 구조는 프로젝트 관리의 기본입니다.

project/ ├── data/ │ ├── raw/ # 원본 이미지 │ │ ├── images/ │ │ └── labels/ │ ├── processed/ # 전처리된 데이터 │ │ ├── train/ │ │ │ ├── images/ │ │ │ └── labels/ │ │ ├── val/ │ │ │ ├── images/ │ │ │ └── labels/ │ │ └── test/ │ │ ├── images/ │ │ └── labels/ │ └── dataset.yaml # YOLO 데이터셋 설정 ├── models/ # 학습된 모델 가중치 ├── notebooks/ # 실험 노트북 └── scripts/ # 전처리/증강 스크립트

핵심 정리

단계핵심 포인트주의사항
수집공개 데이터 + 직접 촬영 + 합성 데이터 혼합다양성 확보가 최우선
라벨링YOLO 형식(검출) + CSV(인식) 이중 라벨품질 > 수량, 이중 검수 필수
전처리크기 통일, 정규화, 색상 변환BGR/RGB 순서 혼동 주의
증강현실적인 변형만 적용과도한 증강은 오히려 해로움
분할70/15/15 비율, 데이터 누수 방지같은 차량 사진이 세트 간 섞이지 않도록
  1. 데이터가 60%: AI 프로젝트 시간의 대부분은 데이터 작업에 쓰인다
  2. 혼합 수집 전략: 공개 데이터 + 직접 촬영 + 합성 데이터를 적절히 조합
  3. 라벨링 품질 관리: 잘못된 라벨 1개가 모델 성능을 크게 떨어뜨린다
  4. 현실적 증강: 실제 환경에서 일어날 수 있는 변형만 적용
  5. 체계적 구조: 디렉토리, 파일명, 분할 규칙을 처음부터 정하고 시작

레슨 정보

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

💡실습 환경 안내

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

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