Skip to content

Vineyard Wildlife INT8 Quantization Diagnosis

ehdwo0427 edited this page May 7, 2026 · 2 revisions

INT8 양자화 진단 — "왜 망가졌나, 어디서 무엇이 무너졌나"

이 문서를 보는 이유

처음에는 "ncnn FP16으로 10 FPS 나왔으니 INT8로 가면 더 빨라지겠지" 정도로 가볍게 시작했습니다. 그러나 결과는:

  • imgsz=320: mAP50-95 0.684 → 0.473 (-31%p)
  • imgsz=640: mAP50-95 0.787 → 0.000 (detection 0건, 완전 실패)

이 문서는 단순히 "INT8이 안 됐다"가 아니라, 어디서 무엇이 무너졌고 어떻게 진단했는지 정리합니다. 양자화가 한 줄 옵션이 아니라 여러 단계의 의사결정 사슬이라는 점을 가장 잘 보여주는 트랙입니다.

시도 1 — Naive quantize_static

처음 시도한 코드:

quantize_static(
    "yolo11n.onnx", "yolo11n_int8.onnx",
    calibration_data_reader=calib_reader,
    quant_format=QDQ,
    weight_type=QInt8,
    activation_type=QInt8,
    calibrate_method=MinMax,
)
  • 양자화 자체는 성공 (파일 정상 생성)
  • RPi에서 추론도 됨 (속도는 빨라짐)
  • 그러나 mAP가 무너짐

진단 — 4가지 원인 후보

INT8 failure — 4 root causes

1. Calibration 전처리 ≠ 추론 전처리

단계 전처리
Calibration (내가 짠 reader) PIL → resize(320, 320) → /255 (squash)
추론 (ultralytics 기본) letterbox padding → /255 (aspect ratio 보존)

squash로 만든 통계로 letterbox 입력을 양자화하니 activation 분포가 어긋나서 quantization scale이 잘못 잡힘. 이것만으로도 큰 정확도 손실 발생.

2. YOLO detection head의 INT8 민감도

YOLO의 마지막 detection head는:

  • sigmoid (confidence)
  • exp (anchor decoding)
  • 큰 dynamic range (objectness 0~1, box coordinate는 픽셀 단위)

INT8 256단계로 표현하면 confidence 0.55 ~ 0.65 영역 같은 미세한 값들이 같은 정수로 뭉개져서 NMS 통과 객체가 사라집니다.

3. Activation per-tensor (per-channel 아님)

onnxruntime의 quantize_static은:

  • weights: per_channel 지원
  • activations: per_tensor만

YOLO의 multi-scale feature pyramid는 layer마다 분포 편차가 커서 per_tensor 하나의 scale로 묶으면 손실이 큽니다.

4. MinMax calibration의 outlier 민감성

MinMax는 calibration 데이터의 max/min을 그대로 quantization range로 씁니다. 한 장의 outlier 이미지가 max를 크게 끌어올리면 나머지 정상 분포가 다 좁은 범위로 압축됩니다.

시도 2 — v2 레시피 (4가지 동시 수정)

# 1) calibration reader도 letterbox로 통일
def preprocess(img):
    return letterbox(img, new_shape=320, stride=32, auto=False)

# 2) detection head 마지막 6개 conv를 양자화에서 제외
nodes_to_exclude = get_last_n_conv_nodes(onnx_model, n=6)

# 3) calibrate_method 변경
quantize_static(
    ...,
    calibrate_method=CalibrationMethod.Percentile,  # outlier robust
    nodes_to_exclude=nodes_to_exclude,
    per_channel=True,
    quant_format=QDQ,
    weight_type=QInt8,
    activation_type=QInt8,
)

결과

INT8 Quantization v1 vs v2 (320 / 640)

imgsz v1 v2 변화
320 0.473 0.545 +7%p (부분 개선)
640 0.000 0.000 변화 없음 (완전 실패)

결론 — onnxruntime ARM EP의 큰 feature map INT8 한계

640 imgsz의 더 큰 activation map에서 onnxruntime ARM EP의 INT8 처리에 구조적 한계가 있는 것으로 추정. 우리 환경(RPi 4 + Python 3.13 + onnxruntime 1.24)에서는 현 시점 INT8을 deployment에 사용 불가.

왜 320은 부분적으로라도 살아났는데 640은 0인가

활성화 텐서 크기가 4배가 되면서:

  • 분포 추정에 들어가는 outlier 영향이 커짐
  • 작은 confidence 값이 INT8 quantization 후 0으로 뭉개지는 비율이 늘어남
  • ARM EP 내부 INT8 커널이 큰 feature map에서 누적 오차를 더 키움

확정적인 단정은 어렵지만, "정확히 어디가 망가졌는지" 는 320 vs 640 비교로 분명해졌습니다.

대안 트랙 (보류 중)

트랙 가능성 비용 현재 상태
ncnn INT8 높음 ncnn2table + ncnn2int8 시스템 바이너리 컴파일, 1~2일 셋업 일정 압박으로 보류
TFLite INT8 낮음 tflite-runtime이 RPi Python 3.13 wheel 미제공 사실상 차단
양자화 인지 학습(QAT) 중간 처음부터 fake-quant 노드로 재학습 필요 일정 무리, 다음 분기
더 작은 모델로 교체 가능 정확도 손실 현재는 ncnn FP16이 충분해서 불필요

현재 결정

ncnn FP16을 deployment 베이스라인으로 확정.

이유:

  • ncnn FP16이 이미 5 FPS 목표를 +96% 헤드룸으로 통과 (10.03 FPS)
  • 정확도 손실 0%
  • INT8을 굳이 갈 이유가 없음 (속도 +85% 추가, 정확도 -31%p~100% 손실)

INT8은 향후 RPi 5 / Hailo-8L / Jetson 같은 디바이스 비교 트랙이나, 모델이 더 무거워졌을 때 다시 검토할 카드로 보존.

회고

1. "한 줄 변환"의 환상

처음엔 int8=True 같은 옵션 한 줄이면 끝일 줄 알았습니다. 실제로는 calibration 전처리 일치, head exclusion, calibration method, per_channel/per_tensor까지 챙겨야 작동하고, 그마저도 백엔드 한계에 막힐 수 있습니다.

2. "안 됐다"와 "왜 안 됐다"의 차이

시도 1만 보고 "INT8 안 됨" 으로 끝냈으면 시도 2의 +7%p 개선과 320 vs 640의 다른 양상은 보지 못했을 것입니다. 실패의 원인을 4가지 후보로 분리하는 것이 다음 결정의 근거가 됩니다.

3. trade-off의 기준은 결국 "목표 vs 헤드룸"

INT8을 안 가도 되는 이유가 "FP16이 이미 5 FPS 목표를 두 배로 넘긴다" 는 점이었습니다. 양자화는 "할 수 있어서" 가 아니라 "필요해서" 가는 것.

4. 양자화 도구의 성숙도는 백엔드별로 다르다

ultralytics CLI / onnxruntime / ncnn / TFLite 모두 INT8을 지원한다고 표면상 말하지만, 실제 deployment 환경에서 작동하는 조합은 좁습니다. "프로덕션 양자화는 framework 레벨이 아니라 runtime 레벨에서 정해진다" 가 가장 큰 학습이었습니다.

관련 자료

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