[Project] 고기 부위 분류 AI: 파인튜닝을 통한 정확도 70% → 90% 개선 전략
요약: EfficientNet-B2 기반 고기 부위 분류 모델을 점진적 파인튜닝과 체계적 테스트를 통해 정확도 70% → 80% → 90%로 개선한 경험을 공유합니다. 데이터 불균형 해소와 하이퍼파라미터 최적화가 핵심이었습니다.
📋 목차
- 문제 상황
- 핵심 해결 전략
- 1단계: 데이터셋 준비 (Dataset Balancer)
- 2단계: 파인튜닝 기법 적용 (Advanced Fine-tuning)
- 3단계: 테스트 자동화 및 신뢰성 확보
- 4단계: 클래스 최적화 및 버그 수정
- 성능 개선 결과
- 주요 교훈
01. 문제 상황
초기 상태
- 모델: EfficientNet-B2 + ImageNet 사전학습 가중치
- 클래스: 소고기 10부위 분류
- 초기 정확도: 약 70% (성능 포화 상태)
- 주요 문제점:
- 데이터 양이 적어 성능 개선의 일관성 부족
- 유사 부위(BottomRound vs Round 등) 간의 높은 혼동 발생
- 특정 데이터셋에만 치우친 평가(테스트 데이터 편향)
목표
- 정확도 90% 달성을 위한 파인튜닝 파이프라인 최적화
02. 핵심 해결 전략

성능 개선을 위해 단순히 학습을 반복하는 대신, 데이터-학습-평가 전 과정에 고도화된 기법을 적용했습니다.
- 데이터셋 균형화: 부위별 100장씩 3개 배치를 구성하여 데이터 다양성 확보
- 고급 파인튜닝 기법: Mixup, Weighted Sampler, Cosine Annealing 스케줄러 적용
- 검증 자동화: Grad-CAM을 통한 모델 해석성 확보 및 다중 배치 통합 테스트
- 포스트 프로세싱: 클래스 병합을 통한 실제 사용성 최적화
03. 1단계: 데이터셋 준비
문제: 적은 데이터와 높은 편차
원본 데이터는 부위별 이미지 수가 제각각이었습니다. 이를 해결하기 위해 각 부위별로 정확히 100장씩 추출하여 3개의 독립적인 배치를 생성하는 Dataset Balancer를 구현했습니다.
1.1 Dataset Balancer 구현
# dataset_balancer.py 핵심 로직
import shutil
import os
import random
from collections import defaultdict
class DatasetBalancer:
def __init__(self, raw_dir, output_dir, images_per_class=100, split_ratio=(0.7, 0.15, 0.15)):
self.raw_dir = raw_dir
self.output_dir = output_dir
self.split_ratio = split_ratio
self.images_per_class = images_per_class
def resplit(self, batch_num):
"""배치번호별로 무작위 split 생성 및 파일 이동"""
# (생략: 클래스별 이미지 수집 및 random.shuffle 로직)
# 100장을 70:15:15로 나누어 train/val/test 폴더로 분배
print(f"✅ Batch {batch_num} 생성 완료")
결과: train_dataset_1/2/3 총 3개의 세트가 생성되었으며, 각 배치는 서로 다른 이미지 조합을 가져 모델의 일반화 성능을 극대화합니다.
04. 2단계: 파인튜닝 기법 적용
2.1 주요 적용 기법
- Weighted Sampler: 학습 시 데이터 수가 적은 부위를 더 자주 노출시켜 클래스 불균형 문제를 해결합니다.
- Warmup + Cosine Annealing: 초기 학습의 불안정성을 잡고, 후반부 미세 조정을 최적화했습니다.
- Mixup: 두 이미지를 섞어 학습시킴으로써 경계가 모호한 데이터에 대한 내성을 키웠습니다.
2.2 학습 핵심 코드 (PyTorch)
# finetune.py 설정값
CONFIG = {
"batch_size": 16,
"warmup_epochs": 5,
"learning_rate": 1e-4,
"mixup_alpha": 0.2,
"label_smoothing": 0.1,
}
# 스케줄러 설정 예시
optimizer = optim.AdamW(model.parameters(), lr=CONFIG["learning_rate"])
warmup_scheduler = optim.lr_scheduler.LambdaLR(optimizer, lambda e: (e+1)/5 if e < 5 else 1.0)
cosine_scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=95)
05. 3단계: 테스트 자동화
3.1 다중 배치 통합 테스트 (test_all.py)
단일 테스트셋에서 높은 점수가 나오는 '우연'을 배제하기 위해, 모든 배치(1/test, 2/test, 3/test)에서 한 번에 성능을 측정하는 자동화 스크립트를 구축했습니다.
3.2 Grad-CAM을 통한 시각적 검증
모델이 고기 이미지의 어떤 특징(마블링, 육색, 근막 등)을 보고 판단했는지 히트맵으로 시각화하여 오분류 원인을 분석했습니다.
06. 4단계: 클래스 최적화
4.1 문제: 유사 부위 혼동
BottomRound와 Round는 육안으로도 구분이 매우 어렵습니다. 분석 결과 모델이 이 두 부위를 자주 혼동하는 것을 확인하고, 확률 병합(Post-processing) 방식을 도입했습니다.
4.2 버그 수정 및 최적화
단순 병합 시 발생할 수 있는 인덱스 오류를 방지하기 위해 조건부 로직을 적용했습니다.
# predict_b2.py 내 병합 로직
if len(CLASS_NAMES) == 10: # 10클래스 모델일 때만 실행 (버그 방지)
probs[0, 5] += probs[0, 0] # Round(5)에 BottomRound(0) 확률 합산
probs[0, 0] = 0 # BottomRound 인덱스 초기화
07. 성능 개선 결과
최종 정확도 추이
| 파인튜닝 단계 | Set 1 | Set 2 | Set 3 | 평균 정확도 |
| 초기 상태 | 67.8% | 69.2% | 71.5% | 69.5% |
| 1차 파인튜닝 | 68.2% | 71.5% | 69.8% | 69.8% |
| 2차 파인튜닝 | 78.9% | 79.5% | 81.1% | 79.8% |
| 3차 파인튜닝 | 89.1% | 90.3% | 91.5% | 90.3% |
최종 성과: 초기 대비 약 20.8%p 정확도 향상 달성! 🚀
08. 마무리 정리
- 데이터 품질이 양보다 중요하다: 균형 잡힌 100장의 이미지가 불균형한 1,000장보다 모델 안정성에 더 기여했습니다.
- 테스트 편향을 경계하라: 하나의 테스트 결과에 일희일비하지 않고, 다중 배치를 통한 '평균의 힘'을 믿어야 합니다.
- 모델의 생각을 읽어라: Grad-CAM 분석을 통해 오분류의 정성적 원인을 파악하는 과정이 성능 개선의 실마리가 되었습니다.
Project: MeatHub 고기 부위 분류 AI
Model: EfficientNet-B2
Date: 2026. 02. 12
'1. AI 논문 + 모델 분석 > AI 모델 분석' 카테고리의 다른 글
| [LLaVA] 학습 스크립트로 보는 멀티모달 모델의 구현 원리 (0) | 2026.02.20 |
|---|---|
| [OCR] PaddleOCR 축산물 이력번호 인식 모델 학습 성공 과정 (0) | 2026.02.07 |
| [OCR] PaddleOCR Rec 학습 원리 및 소량 데이터 분석 (0) | 2026.02.04 |
| [Vision] MobileNet 모델 조사 및 실험 | 논문 리뷰 (0) | 2026.01.26 |
| EfficientNet-B0와 Grad-CAM 분석해보기 (0) | 2026.01.18 |