PROJECT TECHNICAL REVIEW
MetalVision
AI
ResNet50 전이학습 기반 금속 미세조직 자동 분류 시스템
Grad-CAM 설명가능 AI(XAI) 검증 및 Streamlit 웹 배포까지의
3주 단계별 설계 리뷰
90.3%
Val Accuracy (Epoch 24)
7
미세조직 분류 클래스
961
NIST 현미경 이미지
PyTorch · ResNet50 · Grad-CAM · Streamlit · Docker · Hugging Face Spaces · 2026
학습 곡선
학습 곡선 — Epoch 24 수렴 (Val Acc 90.3%)
1 / 11
SYSTEM ARCHITECTURE

3주 단계별 파이프라인

각 단계가 다음 단계의 전제가 되는 종속 구조 — Week 1이 실패하면 Week 2·3은 성립 불가
WEEK 1
모델 학습
NIST UHCS 데이터셋
ResNet50 전이학습
레이어별 차등 LR
CosineAnnealingLR
best_metal_model.pth
WEEK 2
신뢰성 검증
Grad-CAM 히트맵
클래스별 F1 분석
혼동행렬 검토
금속공학적 해석
gradcam_results/
WEEK 3
웹 서비스 배포
Streamlit 앱
Docker 컨테이너화
Hugging Face Spaces
실시간 추론 UI
HF Spaces (Port 7860)
데이터 흐름
# NIST UHCS 데이터셋 (curl 다운로드, 257MB)
micrographs.zip → microstructures.sqlite
         ↓  클래스 레이블 조인
  961장 이미지 → train/val 80:20 분할
         ↓  DataLoader (batch=32)
  ImageNet Normalize → 학습 파이프라인
설계 원칙
단계별 독립 검증
각 주차마다 실행 가능한 결과물 산출 — 중간 실패 시 원인 레이어 즉시 특정 가능
정확도 + 설명가능성
90.3% 달성 후 Grad-CAM으로 "왜 맞췄는가"를 추가 검증 (XAI 필수 단계로 설계)
2 / 11
WEEK 1 — MODEL DESIGN

ResNet50 아키텍처 설계

ImageNet 사전학습(IMAGENET1K_V2) → 금속 분류 헤드 교체 + 레이어별 차등 파인튜닝
FC 헤드 교체 설계
# 원본 ResNet50 FC 교체
model.fc = nn.Sequential(
    nn.Linear(2048, 512),
    nn.BatchNorm1d(512),   # 학습 안정화
    nn.ReLU(inplace=True),
    nn.Dropout(p=0.4),     # 과적합 억제
    nn.Linear(512, 128),
    nn.BatchNorm1d(128),
    nn.ReLU(inplace=True),
    nn.Dropout(p=0.3),
    nn.Linear(128, 7)      # 7개 클래스
)
총 파라미터: 24,624,967개
2단 병목 구조(2048→512→128→7)는 급격한 차원 축소를 완화하고, Dropout을 두 곳에 배치해 과적합 경로를 차단
레이어별 차등 학습률 설계
Layer1
엣지·텍스처
×0.01
Layer2
패턴·형태
×0.05
Layer3
구조 패턴
×0.10
Layer4
고수준 조직
×0.50
FC Head
분류기
×1.00
설계 근거: ImageNet에서 학습된 저수준 특징(엣지, 텍스처)은 금속 현미경 도메인에서도 유효하므로 거의 동결(×0.01). 금속 조직 특유의 고수준 패턴은 Layer4·FC에서 집중 학습
학습 구성
OptimizerAdamW (weight_decay=1e-4)
LR SchedulerCosineAnnealingLR (T_max=30, η_min=1e-6)
LossCrossEntropyLoss (label_smoothing=0.1)
Gradient Clipmax_norm=1.0
Batch / Epoch32 / 30 (CPU, ~3시간 소요)
3 / 11
WEEK 1 — DATA & AUGMENTATION

데이터 불균형 분석 & 금속공학 기반 증강 설계

클래스별 샘플 분포 (총 961장)
spheroidite
374
network
212
pearlite
124
pearlite+spheroidite
107
sph.+widmanstatten
81
martensite
36
pearlite+widmanstatten
27
불균형 비율 13.8:1 (spheroidite 374장 vs pearlite+widmanstatten 27장)
→ label_smoothing=0.1 + 강화 Augmentation으로 희소 클래스 보완
Augmentation — 금속공학적 설계 근거
기법 (파라미터)설계 근거
RandomRotation(360°) 금속 조직은 등방성(isotropic) → 어느 각도 촬영도 동일 조직. 단, 마르텐사이트 침상 방향 다양화에 필수
ColorJitter(b=0.3, c=0.3, s=0.1, h=0.05) 나이탈/피크랄 등 에천트 종류·농도에 따라 밝기·대비가 달라짐. 색조는 최소화(금속=주로 회색조)
RandomAffine(shear=10°, scale=0.9~1.1) 시편 연마 과정의 미세 왜곡, 현미경 배율 변화 시뮬레이션
GaussianBlur(σ=0.1~1.0) 현미경 초점 불일치(focus drift), 배율별 해상도 차이 대응
RandomErasing(p=0.2) 비금속 개재물(inclusion), 기공(porosity) 등 시편 결함 노이즈 모방
RandomGrayscale(p=0.1) 흑백 광학현미경 이미지 대응 — 일부 NIST 이미지가 grayscale
4 / 11
WEEK 1 — TRAINING ANALYSIS

학습 수렴 패턴 분석

학습 곡선
Loss / Accuracy 학습 곡선 (30 epochs)
Epoch별 주요 구간 분석
구간Train AccVal Acc해석
E1–3 40→70% 43→76% Val이 Train을 앞섬: ImageNet 사전학습 특징의 즉각 전이
E4–11 76→84% 79→84% Train≈Val 수렴: 정상적인 파인튜닝 진행
E12–23 85→90% 87→89% 고원(plateau) 구간 — CosineAnnealingLR이 LR을 점진 감소
E24 88.8% 90.3% ★ 최고 Val Acc — 모델 저장
E25–30 88→89% 88→89% 과적합 없음: Train/Val 격차 최소 유지
과적합 방지 효과 확인: 최종 Epoch 30에서 Train 89.3% / Val 89.3%으로 격차 0.0%p — Dropout + label_smoothing + weight_decay 조합이 효과적으로 작동
E1에서 Val이 Train을 앞선 이유: 초기 Train에는 Augmentation이 적용되어 노이즈가 추가되지만, Val은 원본 이미지 → 사전학습 특징이 Val 이미지에 즉각 적용됨
5 / 11
WEEK 2 — GRAD-CAM

Grad-CAM 구현 원리 및 레이어 선택 근거

Grad-CAM 수식 및 구현
αck = GAP(∂yc / ∂Ak)   ← 채널 k의 중요도 가중치
Lc = ReLU( Σk αck · Ak )   ← 최종 히트맵 (7×7)
# 후크로 특징맵·기울기 동시 추출
layer.register_forward_hook(save_feature_maps)
layer.register_full_backward_hook(save_gradients)

# 채널 중요도: 전역 평균 풀링
weights = gradients.mean(dim=[2, 3], keepdim=True)
# 가중합 + ReLU (양의 기여만 시각화)
heatmap = F.relu((weights * feature_maps).sum(dim=1))
타겟 레이어: layer4[-1] (마지막 Bottleneck 블록)
해상도 7×7 / 채널 2048 — 공간 정보를 유지하면서 가장 높은 수준의 의미 정보 보유. layer1~3은 공간 정보는 풍부하나 의미가 낮고, GAP 이후는 공간 정보 소실
클래스별 대표 이미지 Grad-CAM 결과
클래스예측신뢰도판정
MartensiteMartensite90.2%✓ 침상 구조 장축 집중 확인
PearlitePearlite92.2%✓ 층상 경계면 집중 확인
networknetwork82.7%✓ 망상 삼중점 집중 확인
sph.+widmanstattensph.+widmanstatten90.8%✓ 올바른 부위 집중
pearlite+spheroiditepearlite+spheroidite57.3%⚠ 낮은 신뢰도 — 혼합 조직 특성
spheroiditeMartensite66.4%✗ 오분류 — 해당 샘플 비대표성
pearlite+widmanstattenPearlite36.7%✗ 오분류 — 학습 데이터 27장(최소)
핵심 발견: pearlite+widmanstatten은 F1=0.44의 원인이 데이터 부족(27장)에 있음을 Grad-CAM이 간접 확인 — 대표 이미지 자체를 Pearlite로 분류, 모델이 두 조직의 경계를 학습하지 못함
6 / 11
WEEK 2 — PERFORMANCE ANALYSIS

클래스별 성능 심층 분석

전체 Val Acc 90.3%의 이면 — 클래스별 Precision / Recall / F1 분해
분류 리포트 (val 196장)
클래스PRF1N
network1.001.001.0043
spheroidite0.990.920.9575
Pearlite0.790.920.8525
pearlite+spheroidite0.890.770.8322
Martensite0.780.880.828
sph.+widmanstatten0.700.940.8017
pearlite+widmanstatten0.670.330.446
Macro Avg0.830.820.81196
성능 패턴 해석

network — F1 1.00 (완벽)

망상(網狀) 시멘타이트의 입계 망상 패턴은 다른 조직과 시각적으로 명확히 구분 → 43장의 적당한 샘플로도 충분한 특징 학습. Grad-CAM에서 삼중점(triple junction)에 활성화 집중 확인

sph.+widmanstatten — Recall 0.94 vs Precision 0.70

실제 해당 클래스를 잘 잡아내지만(R=0.94), 다른 클래스를 이 클래스로 잘못 분류하는 경우 있음 → Widmanstätten 구조가 spheroidite와 pearlite 경계에 걸쳐 있어 인접 클래스와 혼동

pearlite+widmanstatten — F1 0.44 (최하위)

전체 데이터 27장, val 6장 — 통계적으로 신뢰하기 어려운 샘플 수. Recall 0.33은 실제 6장 중 2장만 올바르게 분류했음을 의미. pearlite(층상)와 widmanstatten(침상)의 혼합 조직을 구별할 충분한 패턴을 학습하지 못함

전체 정확도 90.3%가 가진 편향: spheroidite(374장)·network(212장) 두 클래스가 전체의 60%를 차지 — weighted avg F1 0.90은 데이터가 많은 클래스에 의해 높게 형성됨. macro avg F1 0.81이 더 현실적인 성능 지표
7 / 11
WEEK 2 — XAI VISUAL RESULTS

Grad-CAM 시각화 결과 — 금속공학적 검증

Martensite
Martensite → 90.2%
✓ 침상 구조 장축
Pearlite
Pearlite → 92.2%
✓ 층상 경계면
Network
network → 82.7%
✓ 망상 삼중점
pearlite+widmanstatten
pearlite+widmanstatten
✗ Pearlite 36.7% 오분류
XAI 검증의 의미:
Martensite의 Grad-CAM 활성화가 침상(lath) 구조의 장축 방향에 집중 — 이는 금속공학적으로 마르텐사이트를 식별하는 핵심 형태학적 특징과 일치. 단순히 높은 정확도가 아니라, 올바른 특징을 보고 판단했음을 입증
pearlite+widmanstatten 오분류 원인 분석:
Widmanstätten 조직은 층상 페라이트 구조를 가지며, 이는 Pearlite의 페라이트+시멘타이트 층상 패턴과 거시적으로 유사함. 27장의 학습 데이터로는 두 조직의 미세한 차이(라멜라 간격, 방향성)를 구별하는 특징을 추출하기 어려움
Confusion Matrix
혼동행렬 (Val 196장)
혼동행렬에서 읽히는 패턴:
pearlite+widmanstatten(6장) → Pearlite로 주로 오분류 / spheroidite+widmanstatten → spheroidite와 혼동 / 공통적으로 혼합 조직(+로 연결된 클래스)이 단일 조직 클래스로 흡수되는 경향 — 두 조직의 중간 형태학적 특성이 원인
8 / 11
WEEK 3 — DEPLOYMENT

배포 아키텍처 설계 및 플랫폼 선택 고찰

플랫폼 선택 결정 과정
플랫폼무료282MB 모델Python 서버결과
Streamlit Cloud ✗ (100MB 제한) 탈락
Cloudflare Pages ✗ (정적 파일만) 탈락
Render.com부분 슬립모드 문제
Hugging Face Spaces ✓ (LFS 지원) ✓ (Docker) 채택
Cloudflare Pages 시도 후 포기 원인:
정적 파일(HTML/CSS/JS) 호스팅 전용 플랫폼 — Streamlit은 Python WSGI/ASGI 서버가 필요하므로 구조적으로 배포 불가. Cloudflare Tunnel 대안도 CLI 인증 복잡도로 인해 HF Spaces로 전환
Docker 컨테이너 구성
FROM python:3.11-slim

# OpenCV 런타임 의존성
RUN apt-get install -y \
    libglib2.0-0 libgl1 \
    libsm6 libxext6 libxrender-dev

COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

EXPOSE 7860  # HF Spaces 기본 포트
CMD ["streamlit", "run", "app.py",
     "--server.port=7860",
     "--server.address=0.0.0.0",
     "--server.headless=true"]
발견된 기술적 이슈
matplotlib 한글 폰트 누락 (DejaVu Sans):
Streamlit 앱 내 matplotlib 그래프에서 한글 레이블 렌더링 실패 (Glyph missing 경고 다수). Docker 컨테이너에 NanumGothic 등 한글 폰트를 별도 설치하거나, 클래스명을 ASCII로 유지하는 방향으로 해결 필요
9 / 11
CRITICAL ANALYSIS

한계 분석 및 개선 방향

데이터 한계

pearlite+widmanstatten: 27장
통계적 신뢰 구간 자체가 성립하기 어려운 샘플 수. val 6장 기준 F1=0.44는 1~2장 변동에도 크게 흔들림

Austenite·Ferrite 클래스 부재
NIST 데이터셋에 해당 클래스 이미지 없음 → 실제 현장 활용 범위 제한

단일 도메인(UHCS)
초고탄소강(Ultra High Carbon Steel)에 특화 — 일반 탄소강·합금강에 대한 일반화 성능 미검증

모델 설계 한계

클래스 불균형 미보정
weighted sampling 또는 class_weight 적용 없이 label_smoothing만으로 대응 — 희소 클래스에 불리

CPU 전용 학습
30 epoch에 약 3시간 소요 → 하이퍼파라미터 탐색(스케줄러 종류, Dropout율 등) 실험 횟수 제한

앙상블 미적용
단일 모델 추론 — Test-Time Augmentation(TTA)이나 앙상블로 혼합 조직 클래스 성능 개선 여지 있음

개선 방향

단기:
WeightedRandomSampler 도입으로 희소 클래스 오버샘플링
클래스별 Focal Loss 적용 (α, γ 조정)

중기:
GAN 기반 합성 데이터 생성 (마르텐사이트, widmanstatten 증강)
EfficientNet-B3 / Swin Transformer와 백본 비교 실험

장기:
GPU 환경에서 하이퍼파라미터 그리드 서치
UHCS 외 다른 강종 데이터셋으로 도메인 일반화 검증

설계 결정의 일관성: 이 프로젝트의 모든 한계는 사전에 인지하고 기록했다. 데이터 부족은 숨기지 않고 F1 지표로 명시했으며, Grad-CAM으로 오분류 원인을 추적해 "모델이 모르는 것이 무엇인지"를 명확히 했다. 이 투명성이 다음 개선 사이클의 시작점이 된다.
10 / 11
CONCLUSION
"정확도 90.3%,
그리고 까지 묻는다"
3주 단계별 설계가 준 것
한 번에 완성하는 대신, 매주 검증 가능한 결과물을 산출. Week 1 실패 시 Week 2·3을 낭비하지 않는 구조 — 예상 못한 이슈(배포 용량, 한글 폰트, 클래스 불균형)를 각 단계에서 분리해 원인 특정
설명가능 AI(XAI)가 발견한 것
Grad-CAM은 단순 시각화가 아니라 진단 도구. pearlite+widmanstatten F1=0.44의 원인이 "데이터 부족"임을 히트맵 오분류로 간접 확인 — 다음 개선 사이클(데이터 수집)의 근거를 제공
weighted avg F1 0.90 vs macro avg F1 0.81
두 지표의 격차 0.09p는 클래스 불균형의 정량적 증거. 전체 정확도 90.3%가 실제 성능을 과대평가하고 있음을 스스로 명시 — 정직한 성능 보고
기술 스택 선택의 근거
ResNet50: 961장·CPU 환경에서 실현 가능한 최적 균형점 / Grad-CAM layer4[-1]: 공간 정보와 의미 정보의 균형점 / HF Spaces: 282MB 모델을 수용하는 유일한 무료 Python 호스팅
모델 — ResNet50 IMAGENET1K_V2 · 24.6M params · Val Acc 90.3% (Epoch 24)  |  배포 — Docker + Hugging Face Spaces (Port 7860)  |  코드 — PyTorch 2.x · Streamlit · OpenCV
11 / 11