Skip to content

Vineyard Wildlife Multiclass Training

ehdwo0427 edited this page May 27, 2026 · 4 revisions

야생동물 5종 multi-class 학습 변천사

이 문서를 보는 이유

처음에는 단일 종(노루)만 fine-tune해서 RPi에서 돌려보는 것을 목표로 시작했습니다. 그런데 운영 관점에서 노루 한 종만 잡아도 멧돼지·오소리·너구리·조류가 들어오면 의미가 없다는 점이 명확해졌고, 결국 5종 multi-class로 확장했습니다.

이 문서는 단일 종 → 다종 확장 과정에서 어떤 의사결정이 필요했는지, 그리고 imgsz 320/640 병렬 학습 결과를 비교하면서 어떤 점이 보였는지 정리합니다.

데이터

  • 출처: 한국도로공사 EX 포털 생태통로 모니터링 datasetId 868·870·872·867·873
  • 5종: 노루(roe_deer) / 멧돼지(wild_boar) / 오소리(badger) / 너구리(raccoon) / 조류(bird)
  • 종별 약 5,000장씩, 합쳐서 24,844장 (조류 156장은 ZIP 손상으로 누락 처리)
  • 라벨 포맷: COCO JSON → YOLO txt 자동 변환 스크립트로 통일

Split 전략

종마다 자연 환경·카메라·시기 분포가 달라서 한 번에 합쳐서 split하면 종별 분포가 깨질 위험이 있었습니다. 그래서:

  1. 종별로 먼저 70/20/10 split (seed=42)
  2. 각 종의 train/val/test 폴더에 심볼릭 링크 생성
  3. 종별 split 결과를 processed_multiclass/ 하나로 합집합

이렇게 하면 종별 분포가 split 단위에서 보존됩니다. 디스크 절약을 위해 모든 데이터는 NAS에 두고 심볼릭 링크로 처리했습니다.

학습 설정

  • 모델: YOLO11n (Ultralytics 8.4.32 시작, RPi 쪽은 8.4.36)
  • pretrained: COCO
  • optimizer: AdamW
  • scheduler: cos_lr=True
  • imgsz: 320 / 640 두 개 병렬
항목 imgsz=320 imgsz=640
의도 RPi 5 FPS 목표용 정확도 상한 측정용
학습 시간 짧음 길음
메모리 부담 낮음 높음 (batch 키울 여지 ↑)

학습 도중 GPU 메모리 사용량이 절반 이하로 보여서 batch size를 키워 다시 돌리기도 했습니다. 학습 자체는 dev 머신에서 돌리고, RPi에는 .pt → ONNX → ncnn 변환 산출물만 SCP로 보냈습니다.

결과 (test split 기준)

imgsz=320 vs 640 (FPS / mAP)

imgsz mAP50 mAP50-95 비고
320 0.92x 0.684 실시간 데모 권장
640 0.95x 0.787 오프라인 정밀 분석용

640이 +9%p 좋지만 RPi에서 FPS가 절반 이하로 떨어져서, 실시간에는 320이 정답이라고 판단했습니다.

종별 mAP 분포

Per-class mAP (320 vs 640) — bird is weakest

5종이라고 다 같은 난이도가 아니었습니다. RPi 4 + ncnn FP16, test split 2,484장 실측 mAP50-95:

320 mAP50-95 640 mAP50-95 비고
roe_deer (노루) 0.768 0.849 5종 중 최고. 학습 데이터 풍부 + 야간 IR 실루엣 또렷
wild_boar (멧돼지) 0.679 0.749 야간 IR 비중 큼. 노루와 자세·실루엣 유사해 헷갈릴 여지
badger (오소리) 0.704 0.784 저자세·짧은 다리. 작지만 형태 일관되어 안정적
raccoon (너구리) 0.653 0.735 5종 중 320에서 최저. 야간 IR 잡음 + 너구리·오소리 외형 유사
bird (조류) 0.617 0.744 작은 객체 비율 높음 — 320에서 가장 큰 폭 하락 (640 대비 -13%p). 실내 인공물(선풍기 등) false positive 위험
all (5종 평균) 0.684 0.787 320 → 640: 평균 +10%p. raccoon·bird가 가장 큰 폭으로 향상

한눈에 보기: 데이터 풍부도(노루 > 멧돼지 ≈ 오소리 > 너구리 > 조류)와 mAP50-95 순위가 거의 일치. bird만 320 ↔ 640 격차가 가장 큼 (작은 객체일수록 imgsz 영향이 크다는 일반 경향과 부합).

bird가 5종 중 가장 noisy한 점이 카메라 MVP에서 실내 선풍기 false positive로 그대로 드러났습니다 (카메라 실시간 검출 MVP 참고).

종별 검출 예시 (Before / After)

학습된 모델로 test 이미지에서 종별 2장씩 추론한 결과입니다. 좌측이 원본, 우측이 bbox + class · confidence 그려진 결과.

roe_deer (노루)

roe_deer before/after

wild_boar (멧돼지)

wild_boar before/after

badger (오소리)

badger before/after

raccoon (너구리)

raccoon before/after

bird (조류)

bird before/after

위 4종은 IR 카메라트랩 야간 이미지가 다수, bird만 주간 일반 카메라 이미지 비중이 큽니다. 이 분포 차이가 카메라 MVP에서 bird 클래스의 실내 false positive로 그대로 드러납니다 (카메라 실시간 검출 MVP 참고).

시도했지만 실패한 것

Multi-scale 학습

multi_scale=True 옵션으로 학습 도중 imgsz를 랜덤하게 바꿔서 일반화를 높이려고 시도했습니다. 그러나:

  • 첫 epoch에서 ZeroDivisionError
  • 원인: nn.functional.interpolate decomposition이 torch+ultralytics 특정 버전에서 multi_scale 경로에 버그가 있음
  • 우회: torch 다운그레이드 또는 ultralytics 패치 필요

지금은 일정 압박이 있어 보류했고, 다음 분기에 다시 시도할 예정입니다.

Cross-imgsz 추론

640으로 학습한 모델을 imgsz=320으로 추론하면 정확도가 더 좋을지 궁금해서 측정했습니다.

  • 결과: 320 학습 모델 320 추론과 거의 동일
  • 결론: 별 이득 없음, 학습 imgsz와 추론 imgsz를 맞추는 게 자연스럽다.

회고

1. 단일 종 → 다종 확장은 "한 번 더 학습"이 아니다

종별 데이터 분포·라벨 ID·split 전략을 처음부터 다종을 가정하고 짜야 했습니다. 단일 종 코드를 그대로 두고 다종으로 확장하면 split 분포가 깨지고 라벨 ID가 꼬입니다.

2. 정확도 1등 모델이 항상 deploy 1등이 아니다

imgsz 640이 mAP는 좋지만 RPi에서 못 돌면 의미가 없습니다. deploy 컨텍스트의 제약을 학습 단계부터 의식해야 합니다.

3. 종별 약점 종을 알고 있는 것이 운영의 시작

bird mAP 0.617은 학습 단계에서 그냥 "아 5종 중 제일 낮네" 였지만, 카메라 MVP에서 100% false positive로 그대로 드러났습니다. 종별 약점은 데이터 단계의 신호이자 운영 단계의 트리거입니다.

관련 자료

Woody's AI Backend Engineering Log


💼 About

Deepvisions | AI Engineer 2026.03 ~ 재직중


🚀 Projects (최신순)

CCTV 자전거 경로 & 공회전 탐지 — 한동대학교 리빙랩

2026.05 ~ | @ Deepvisions 캠퍼스 CCTV 4대 · 자전거 OCR + 차량 공회전 다중 신호

야생동물 탐지 — RPi 엣지 배포

2026.04 ~ | @ Deepvisions 포도밭 침입 탐지 (5종 multi-class · 라즈베리파이 4 실시간)

포도밭 병해충 탐지 및 수확량 예측

2026.03 ~ | @ Deepvisions 드론 이미지 기반 객체 탐지 + GSD calibration + 수확량 예측


📦 종료된 프로젝트

OnTheTop

2025.03 ~ 2025.08 | 카카오테크부트캠프 | ✅ 종료 AI 기반 데스크테리어 추천 서비스


AI Notes


About

Clone this wiki locally