CPU/NPU 환경에서 YOLO를 대체하는 경량 객체탐지 모델 개발 - VisionicNet 개발기

약 2달간 진행한 경량 객체탐지 모델 개발 프로젝트를 정리해보려 합니다.

 

결론부터 말씀드리자면 YOLO v8 Nano 대비 추론 속도 & 정확도 ± 10% 수준의 상용 라이선스 자유로운 모델을 만드는 데는 성공했고, INT8 배포는 아직 과제로 남아있습니다.

 

소요 시간 : 약 12개월
학습(GPU 가동) 시간 : 약 315시간(약 13일) 소요
개발 방식 : 1인 개발 + AI 툴(Claude, Gemini)

 

목표

  • YOLO v8 Nano 모델을 대체할 CPU환경에 최적화 된 객체 탐지 모델 개발.
  • 추론 속도 및 정확도(mAP50-95)를 ± 10% 안에 드는것이 1차 목표
  • 배포 대상 : CPU 기반 서버, 모바일 엣지 디바이스

 

왜 만들게 됐는가?

YOLO는 객체탐지 분야에서 사실상 표준처럼 쓰이는 모델이지만, Ultralytics YOLO는 AGPL-3.0 라이선스라는 큰 제약이 있습니다.

 

AGPL-3.0은 해당 모델을 서비스에 사용하면 서비스 코드 전체를 공개해야 할 수 있어, 상용 제품에 사용하기가 사실상 어렵습니다.

 

그래서 아래 목표를 세우고 직접 모델을 만들기로 했습니다.

  • 라이선스 자율성 : Ultralytics AGPL-3.0 영향 없는 구성요소만 사용
  • NPU/CPU 친화 아키텍처 : 모바일 NPU와 CPU 모두에서 효율적으로 동작
  • YOLO v8 대비 ± 10% 수준의 추론 속도 & 정확도 : 100회 추론 속도 비교 & COCO Dataset의 val 2017로 비교
  • 공정한 비교 유지 : COCO 데이터만, 외부 distillation·auxiliary head 금지

사용 가능한 컴포넌트 기준:

  • VoVNet OSA (BSD 3-Clause)
  • GFL/TOOD/CIoU/EIoU (논문 표준 구현)
  • Ultralytics C2f / C3 / C3k2 패턴 일절 금지

기본 백본 구상 (V1~V2)

  • MobileNet V4를 기반
  • PatNet의 1/4, 3/4 나눠서 연산 기법 적용

 

단계별 진행 상황

  1. 기반 아키텍처 확립
  2. 다양한 카드 시도와 실패
  3. 한번에 너무 많이 바꾸면 안된다
  4. Dynamic Schedule + P3에 OSAPATBlock 추가
  5. 똑같은 실수 반복
  6. 배포 변환 : ONNX / TFLite(LiteRT)
  7. INT 8 양자화 - 시도 및 실패
  8. INT 8 양자화 - QAT 도전
  9. Neck/Head 재설계 - V14로의 전환
  10. mAP 손실 진단과 EIoU 채택
  11. 남은 과제
  12. 마무리

1단계 - 기반 아키텍처 확립 (V3 ~ V7)

첫 번째 과제는 NPU에서 1st-class op만 사용하는 백본을 만드는 것이었습니다.

 

핵심 아이디어는 LKRep(Large Kernel Reparam) 입니다.

 

학습 시에는 여러 branch(1×k, k×1, k×k DW, identity)를 병렬로 사용해 풍부한 gradient를 받고, 추론 시에는 이 branch들을 수학적으로 단일 DW conv로 합쳐 속도 손해 없이 표현력을 확보하는 방식입니다.

 

RepVGG에서 영감을 받은 접근이지만, NPU 친화를 위해 DW conv + 1×1 PW conv 조합만 사용하도록 설계했습니다.

V3부터 V7까지의 핵심 변화를 정리하면 아래와 같습니다.


버전 주요 변경 내용 효과
V3 LKRep DW 도입 NPU 1st-class op, 학습-추론 분리
V4 P3·P5 depth +1 params/FLOPs 균형 조정
V6 LKRepDWFlexible (k=3/5/7) 도입 유연한 receptive field
V6.3 sp_attn 제거, 블록 4→3 params -32% (-1.10M)
V7 LKRep+PWConv 철학 복원, 310 ep 학습 mAP@50-95 ≈ 0.3422, 안정적 baseline 확정

V7이 첫 안정적인 baseline으로 자리를 잡았습니다.

 


 

2단계 - 다양한 카드 시도와 실패 (V8 ~ V10)

V7을 baseline으로 여러 개선을 시도했습니다. 결론부터 말씀드리면 전부 실패입니다.

 

V8 - SPPF 추가 (실패)

SPPF(Spatial Pyramid Pooling Fast)는 YOLO 계열에서 글로벌 context를 잡기 위해 자주 사용하는 블록입니다.

그런데 우리 V7은 이미 P5에 LK 7×7 DW conv를 사용하고 있어서, receptive field가 충분히 확보된 상태였습니다.

SPPF는 사실상 이것과 중복이었고, 결과적으로 mAP가 0.31 수준으로 오히려 후퇴했습니다.

 

V9 - ECA Attention 도입 (실패)

ECA(Efficient Channel Attention)는 채널 간 관계를 학습하는 가벼운 attention 모듈입니다. 넣어봤더니 mAP가 0.2497로 크게 떨어졌습니다.

원인을 분석해보니 ECA의 Conv1d → 0 → Hardsigmoid(0) = 0.5 흐름이 문제였습니다. 초기화 시 attention weight가 모두 0.5가 되어 전체 feature를 50% 감쇠시켜버린 것입니다.

NPU 친화 정책과 activation 초기화 문제가 맞물린 케이스였습니다.

 

V10 - V2 reparam (후퇴)

asymmetric branch(1×k, k×1)를 추가한 LKRepDWFlexibleV2와 AdamW 옵티마이저를 같이 적용했는데 mAP 0.3350으로 V7(0.3422) 대비 후퇴했습니다.

 

여기서 중요한 교훈을 얻었습니다. 아키텍처와 학습 레시피를 동시에 바꾸면 무엇이 원인인지 알 수 없다는 것입니다.

 


 

3단계 - 한 번에 너무 많이 바꾸면 안 된다 (V11)

가장 큰 실패 사례입니다.

V11에서는 아키텍처 변경(bilinear upsample, OSAPATBlock, DropPath)과 학습 레시피 변경(AdamW, MixUp, multi-scale, ema_tau 변경)을 동시에 적용했습니다.

결과는 epoch 47에서 mAP 0.2253으로 plateau, V7 동일 시점 0.2721 대비 -0.05 mAP 후퇴였습니다.

무엇이 문제인지 명확히 알 수 없었고, 학습을 중단했습니다.

 

V11-lite - 아키텍처 vs 레시피 분리 가설

V11 실패 이후 가설을 세웠습니다.

"아키텍처는 V11이지만, 학습 레시피는 V7 검증 방식으로 가면 어떨까?"

V11 architecture + V7 검증 레시피(SGD, lr=0.005, MixUp OFF, multi-scale OFF, ema_tau=10)로 다시 학습했고, mAP@50-95 0.3376을 달성했습니다.

그런데 학습 과정을 보면서 특정 패턴을 발견했습니다.

  • epoch 100~290: 사실상 plateau (+0.001 mAP 수준)
  • epoch 290에서 close_mosaic 진입 후 10 epoch 만에 +0.075 fitness 폭증

즉, 학습 시간의 95%가 mosaic plateau에 낭비되고 있었던 겁니다. 이 발견이 다음 단계의 핵심 아이디어가 됩니다.

또한 이 과정에서 중요한 변화도 있었습니다. OSAPATBlock을 위해 처음에는 C2f 기반 블록을 검토했는데, AGPL-3.0 오염 가능성이 있어 BSD 3-Clause인 VoVNet OSA 방식으로 전환했습니다.

AI가 정말 유용한 도구인건 맞지만 검증, 확인, 결정은 사람이 해야 한다는 것을 다시 한번 깨달은 경험이였습니다.

 


 

4단계 - V12: Dynamic Schedule + P3 OSAPATBlock 추가

V11-lite에서 얻은 인사이트를 바탕으로 두 가지를 적용했습니다.

 

Dynamic Schedule 구현

patience 기반 자동 phase 전환을 training_v6.py에 구현했습니다.

Phase 0 (mosaic ON)
  → 30 epoch 동안 best 갱신 없으면 → close_mosaic 자동 ON

Phase 1 (close_mosaic)
  → 최소 5 epoch 유지 후
  → 15 epoch 정체 시 early stop

DDP(분산 학습) 환경에서는 main process가 best_epoch를 결정한 후 dist.broadcast로 전체 rank에 동기화하도록 처리했습니다.

 

P3 OSAPATBlock 추가

V12에서 추가한 유일한 아키텍처 변경입니다.

후보 카드가 여러 개 있었는데, 추론 속도 - 정확도 트레이드오프 기준으로 엄격하게 선별했습니다.

후보 안건 mAP50-95 예상 latency 증가 예상 결정
1안 P3 OSAPATBlock 추가 +0.015 +0.6 ms 채택
2안 Head depth 2→3 +0.015 +3.7 ms (+21%) 폐기

2안은 mAP 향상 대비 latency 비용이 너무 커서 과감하게 버렸습니다.

그리고 하나 더, ONNX 그래프를 분석하다가 SiLU가 42개나 남아있는 것을 발견했습니다. SiLU는 sigmoid(x) * x 구조라 NPU에서 Sigmoid + Mul 두 연산 과정으로 분해되어 비효율적입니다. 모든 SiLU를 Hardswish로 교체했습니다.

 

V12 학습 결과

학습 완료: 273 epoch (예약 300 → -10% 자동 단축)
Phase 전환: epoch 229 (V11-lite의 290 대비 -60 epoch 단축)
close_mosaic boost: epoch 229~273, +0.087 fitness 폭증
학습 시간: 약 60시간

최종 mAP@50-95: 0.3522
TFLite FP32 latency: 16.53 ms (YOLO11n 17.7 ms 대비 -6.6%)

Dynamic Schedule 덕분에 학습 시간이 단축됐고, 1안과 Hardswish 전환이 맞물려 V7(0.3422) 대비 mAP +0.01, latency는 오차 범위 내 증가만 얻었습니다.


 

5단계 - V13 시도 (실패)

V12 확정 후 "latency 비용이 없는" 후보 군 5개를 동시에 적용해봤습니다.

  • 1안: EMA decay 0.9998 → 0.9999
    • 이유 : 0.9998에서 0.9999로 수치를 올리면 과거 가중치의 보존 비율이 더 높아집니다. 학습 후반부의 미세한 가중치 변화를 억제하고 파라미터를 안정화시켜 모델의 일반화 성능을 높이기 위해 도입 했습니다.
  • 2안: cls loss를 VarifocalLoss로 교체
    • 이유 : 기존 BCE(Binary Cross Entropy)가 객체의 존재 유무(1 또는 0)만을 분류 손실로 학습했다면, Varifocal Loss(VFL)는 예측한 바운딩 박스와 실제 박스 간의 IoU(교집합 비율)를 분류 정답(Target)으로 사용하여 학습합니다.
  • 3안: TAL topk 13 → 10
    • 이유 : 13에서 10으로 줄이면 모델에게 학습시킬 정답 샘플의 선정 기준이 더 타이트해집니다. 품질이 낮은 주변부 예측을 정답에서 배제하고, 객체의 중심부나 특징이 뚜렷한 고품질(High-quality) 샘플에 집중하도록 강제하기 위한 조치입니다.
  • 4안: affine_scale 0.5 → 0.9
    • 이유 : 수치를 0.5에서 0.9로 올리면 더 극단적인 이미지 크기 변화를 허용하게 됩니다. 모델이 다채로운 객체 크기에 강건해지게 만들어 과적합을 방지하려는 목적입니다.
  • 5안: warmup_ep 5 → 10
    • 이유 : 웜업 기간을 5 에폭에서 10 에폭으로 늘리는 것은 초기 학습의 안정성을 확보하기 위해서입니다. 특히 Varifocal Loss와 같은 새로운 손실 함수나 극단적인 데이터 증강이 동시에 도입되었을 때, 초기 가중치가 큰 기울기로 인해 파괴되거나 발산하는 것을 막기 위한 안전장치 역할을 합니다.

epoch 153 시점 비교:

  • V12: fitness 0.2777
  • V13: fitness 0.1968 (-8.1pt 후퇴)

분석해보니 box/dfl loss는 V12와 비슷한데 cls loss만 V12의 절반이었습니다. VarifocalLoss의 alpha=0.75가 negative class weight을 과도하게 억제한 것으로 의심되지만, 카드를 5개 동시에 바꿔서 정확한 원인 분리가 불가능했습니다.

V11 때와 같은 실수를 반복했습니다. V12 확정, V13 폐기.

 

그리고 정말 아쉽게도 모델 개발 및 학습할 시간이 없어서 고도화는 여기서 멈추고 배포를 위한 단계로 넘어가기로 했습니다.


 

6단계 - 배포 변환: ONNX / TFLite

V12 best.pt를 실제 배포 포맷으로 변환했습니다.

 

ONNX 변환 및 검증

PyTorch ↔ ORT 수치 일치: max_abs 1.8e-5 
ONNX FP32 latency (8-thread): 18.37 ms

 

TFLite (LiteRT) 변환

onnx2tf를 통해 ONNX → TFLite로 변환했습니다.

놀라운 점은 TFLite FP32 + XNNPACK이 ONNX FP32 + ORT보다 8% 빠르다는 것입니다.

TFLite FP32 (XNNPACK 8-thread): 16.53 ms
ONNX FP32 (ORT):               18.37 ms
YOLO11n (참조):                 17.70 ms

XNNPACK의 CPU 최적화가 ORT보다 우수하다는 결론입니다.


 

7단계 - INT8 양자화 시도 (실패)

📌 [2026.07.10 업데이트] 이 글에서 "근본 원인은 cls_pred 출력 분포 문제"라고 
결론 내렸는데, 이후 추가 소거 실험(cls_pred/reg_pred 제외, entropy 캘리브레이션, 
depthwise conv 제외)으로 재검증한 결과 이 가설은 옳지 않았다는 사실이 증명되었습니다. 
실제 원인과 재진단 과정은 후속 포스팅에서 다룹니다. 
아래는 당시 시점의 분석 과정을 그대로 남겨둔 기록입니다.

 

배포 크기와 NPU 활용을 위해 INT8 양자화를 시도했습니다. 결과는 전면 실패였습니다.

 

PTQ (Post-Training Quantization) 시도

방식 mAP 50-95 비고
ORT MinMax calib 0.1243 -64.7%
ORT Entropy calib 0.1268 -64.0%
LiteRT integer_quant 0 output dequant 노드 손상
LiteRT full_integer_quant 0 cls saturate
LiteRT DRQ 0 real image mAP=0

총 6가지 변종 모두 실패입니다.
당시 분석으로는 원인이 cls_pred 출력 분포 문제라고 판단했습니다. 
(→ 이후 소거 실험으로 기각됨, 후속 포스팅 참고)

 

※ 당시 세웠던 가설 : 왜 cls_pred 출력 분포 문제인가?

해당 내용은 AI 툴을 사용하여 분석하도록 하였고 그 결과를 판단하였습니다.

다만 이후 확인하겠지만, 이 가설을 직접 소거 실험으로 검증하지 않은 채 결론으로 채택한 것이 이번 원인 오판의 핵심 원인이었습니다.

PTQ 실패의 주된 원인이 cls_pred 때문이라는 것은 출력 텐서의 데이터 분포와 양자화 도구의 스케일 할당 방식을 분석하여 밝혀낸 것입니다.

초기화 값(Bias Prior)의 특성: DecoupledHeadDFLv2.cls_pred의 편향(Bias) 값은 학습 초기부터 확률 0.01을 가정하여 초기화되었습니다. 이는 수식 상 -log((1-0.01)/0.01) ≈ -4.6에 해당합니다.
음수 영역에 편중된 로짓 분포: 객체 탐지 모델 특성상 정답 객체보다 배경(Negative class)의 수가 압도적으로 많습니다. 이 때문에 학습이 완료된 후에도 cls_pred가 출력하는 가공되지 않은 로짓(Logit) 값의 대부분이 [-20, -1]이라는 넓은 음수 영역에 머물게 됩니다.
자동 PTQ 도구의 잘못된 가정: LiteRT(onnx2tf 등)의 자동 양자화 도구들은 분류 헤드의 출력이 최종적으로 Sigmoid를 거쳐 [0, 1] 사이의 값이 될 것이라고 기계적으로 가정합니다. 이에 따라 INT8 변환 시 해당 노드의 출력 스케일(Scale)을 1/128로 일괄 고정해 버립니다.
값의 포화(Saturation) 발생: 실제 로짓은 [-20, -1]로 넓게 퍼져 있는데, 도구가 1/128이라는 극단적으로 미세한 해상도의 스케일을 강제 적용하면서 모델의 모든 분류 예측값이 INT8의 최솟값이나 최댓값으로 뭉뚱그려지는 '포화(Saturation)' 현상이 발생했습니다. 

이로 인해 어떤 객체인지 구분하는 능력이 완전히 상실되어 전체 정확도(mAP)가 0으로 떨어졌습니다.

 

cls_pred.bias가 prior 0.01 기준으로 -log(99) ≈ -4.6으로 초기화되어, 학습 후에도 cls logit이 대부분 [-20, -1] 영역에 몰려있습니다.

 

자동 PTQ 도구들은 sigmoid 후 [0, 1] 범위를 가정하고 output scale을 1/128로 결정하는데, 이 scale에서는 cls logit이 모두 saturation되어 버립니다.


 

8단계 - QAT (Quantization-Aware Training): 부분 성공

PTQ가 전부 실패하자 마지막 카드로 QAT를 시도했습니다.

QAT는 학습 과정에서 fake quantization 노드를 삽입해 모델이 INT8 환경에 적응하도록 만드는 방식입니다. PTQ에 비해 시간이 훨씬 많이 들지만, 정확도 회복률이 높습니다.

 

Hybrid QAT 선택

시간 제약상 세 가지 옵션 중 Hybrid QAT를 선택했습니다.

  • Full QAT: 12h, 결과 불확실
  • Hybrid QAT: 10h, cls_pred + reg_pred 6개만 FP32 유지 
  • Surgical QAT: 4h, 효과 미미

학습 중 발생한 이슈 3가지

QAT 학습 과정에서 예상치 못한 이슈가 연달아 터졌습니다.

  1. bf16 vs fake_quant 충돌 — fake quant 노드가 BF16을 지원하지 않아 mixed precision을 완전히 꺼야 했습니다. (_qat suffix 분기 처리)
  2. EMA shape mismatch — fake quant가 삽입되면서 생긴 scale 파라미터([1])가 가중치 shape([32])와 충돌, ema.update()에서 shape 불일치 시 skip하도록 수정했습니다.
  3. GPU memory 부족 — batch 32 → 16으로 줄여 해결했습니다.

QAT 학습 결과

학습 완료: 20 epoch, 4시간 30분
best epoch 13: mAP@50-95 = 0.3306
FP32 V12(0.3522) 대비 -0.0216 (-6.1%)
회복률: 93.9%

 

PTQ 대비 비교:

방식 mAP 50-95 FP32 대비 회복률 base 대비 성능
FP32 baseline 0.3522 - 100%
Hybrid QAT 0.3306 -2.2% 93.9% 
ORT PTQ Entropy 0.1268 -64.0% 36.0% 
LiteRT PTQ (모든 변종) 0 -100% 0% 

QAT가 PTQ 대비 압도적으로 우위임을 입증했습니다.

 

변환 단계에서 또 실패

문제는 QAT 학습 결과를 실제 INT8 TFLite로 변환하는 것이 불가능했다는 점입니다.

시도결과
PyTorch QAT → ONNX QDQ (dynamo) aten._fused_moving_avg_obs_fq_helper 미지원 
PyTorch QAT → ONNX QDQ (TorchScript) fused_moving_avg_obs_fake_quant opset 미지원 
taq.convert() → ONNX PATConv residual의 aten::add.out 충돌 
QAT 가중치 → FP32 ONNX → onnx2tf PTQ Hybrid 보호로 cls bias 변화 미미 → 동일 saturation 

Hybrid QAT는 cls_pred를 FP32로 보호하기 때문에 학습 중 cls logit이 INT8에 맞게 압축될 기회가 없었고, 변환 후에도 동일한 saturation 문제가 발생합니다.

 

현재까지의 결론:

PyTorch QAT → LiteRT INT8의 end-to-end path는 PATConv residual과 연산 로직 호환성 문제로 아직 불완전합니다.

 


9단계 - Neck/Head 재설계 — V14로의 전환

V12 이후 정확도 개선을 목표로 진행한 실험입니다.

V12까지 완료 후, 한 가지 문제가 눈에 들어왔습니다.

 

Neck과 Head에 dense 3×3 conv가 그대로 남아있었습니다.

프로파일링해보니 Head의 dense conv가 전체 latency의 약 53%를 차지하고 있었습니다.

최적화 대상이었던 Backbone보다 Head가 더 무거웠던 것입니다.

 

DW-separable로 교체하면 어떨까? 절약된 latency를 용량 확장에 재투자하는 방식으로 접근했습니다.

변경 내용:
  Head: dense 3×3 conv → DW 3×3 + PW 1×1
  Neck: RepCSP inner 3×3 → reparam DW + PW

 

ONNX latency 결과 (2-Threads):

"※ 이 구간(9단계)의 latency는 6단계와 다른 측정 조건입니다. 
6단계는 TFLite+XNNPACK 8-thread 기준이고, 이 구간은 ONNX 게이트 
2-thread 기준으로, 런타임과 스레드 수가 모두 다릅니다."

V12       : 52.4 ms (baseline)
V14-full  : 48.3 ms (-8%)   ← DW화 + 용량 확장 재투자
V14-lite  : 35.7 ms (-32%)  ← 용량 확장 롤백 (DW 구조만 유지)

그런데 V14-full에서 용량 확장(채널 수 증가, 레이어 깊이 증가)이 mAP에 기여를 전혀 하지 못하는 것으로 확인됐습니다.

재투자가 효과를 내지 못한 것입니다.

 

그래서 용량 확장만 롤백한 V14-lite를 별도로 만들었습니다.

DW 구조는 유지하면서 채널 수와 레이어 깊이를 V12 수준으로 되돌린 버전입니다.

현재 최고 실측 기준선:

run: train_v6_20260613_001050 (V14-full, single-scale, CIoU)
mAP@50-95: 0.3543  ← 절대 최고

V12(0.3522) 대비 +0.0021 개선됐습니다.

작은 차이지만 DW-separable 전환이 정확도를 희생시키지 않았다는 것을 확인했습니다.


10단계 - mAP 손실 진단과 EIoU 채택

"어디서 점수를 잃는가"를 데이터로 확인한 과정입니다.

V14까지 왔는데도 목표(YOLO v8n 0.373)와의 gap이 여전했습니다.

개선하려면 먼저 어디서 점수를 잃는지 알아야 했습니다.

V14-full best.pt로 COCO val2017 AP를 크기별로 분해했습니다.

AP_small:  0.187  ← 전체 평균(0.354)의 53%
AP_medium: 0.383
AP_large:  0.473

AR_small:  0.309  ← 소형 객체의 69%를 아예 못 찾음
AR_medium: 0.585

소형 객체가 최대 약점이지만, AR_small 0.309는 구조적 한계입니다.

P2 헤드(stride-4)나 해상도 증가 없이는 recall 자체를 올리기 어렵고, 이는 Phase 2에서 확인했듯 속도를 2배 이상 희생해야 합니다.

 

반면 중형 객체는 recall(0.585)이 충분합니다.

박스를 찾기는 하는데 정밀하게 맞추지 못하는 것이 문제입니다.

mAP@50-95의 고-IoU 부분은 거의 중대형에서 나오기 때문에, 중형 localization 정밀도를 올리는 것이 가성비가 훨씬 좋습니다.

 

EIoU 채택

box loss를 CIoU → EIoU로 교체하는 A/B 테스트를 진행했습니다.

조건: 동일 모델(V14-full) + 동일 레시피 + 동일 multi-scale 설정
변경: box loss만 차이

 


 

지표 CIoU EIoU 변화
mAP@50-95 0.3525 0.3544 +0.0019
AP_medium 0.383 0.390 +0.71pt
AP_large - - +0.39pt
mAP@75 - - +0.26pt
AP_small 0.187 0.179 -0.71pt ⚠

예상한 방향과 정확히 일치합니다.

중대형 정밀도가 올라가고 소형이 내려갔습니다.

 

순증은 +0.0019로 노이즈 경계에 걸쳐있지만, 방향이 일관되고 추론 비용이 0이기 때문에 EIoU를 채택했습니다.


11. 남은 과제

  • 아직 정확도(mAP50-95)가 많이 낮은 상황(YOLO 11n 대비 4% 이상 차이)이므로 이를 개선
  • ONNX 환경에서 아직 YOLO보다 추론속도가 느린 점을 개선
  • INT 8으로 양자화가 될 수 있도록 모델의 구조 개선
    •  [업데이트] 이 중 "INT8 양자화 구조 개선" 과제는 후속 실험으로 진행되었으며, 결과는 아래 포스팅에서 확인하실 수 있습니다.
 

INT8 양자화, PTQ로는 mAP가 61% 붕괴했다 - 원인 추적과 QAT 실험기

"INT8로 배포하면 mAP가 얼마나 떨어질까?"단순한 궁금증으로 시작했는데, 결과는 예상보다 훨씬 나빴습니다.그리고 원인을 추적하는 과정에서 "어느 레이어 때문이다"는 가설이 하나씩 기각되면

sonjuhy.tistory.com

 

 


 

12. 최종 성과 요약

항목 Visionic Net v14 YOLO 11n
mAP@50-95 0.3544 0.395
TFLite FP32 latency 16.53 ms 17.7 ms
Params (fused) 2.1 M
라이선스 상용 자유 AGPL-3.0
NPU 친화 DW + 1×1 + Hardswish only Conv + SiLU

YOLO11n 대비 mAP는 -4.1pt 차이가 있지만, latency는 오히려 -6.6% 빠르고, 라이선스는 자유롭습니다.

 

이 과정까지 오는데 순수 학습 시간으로

총 GPU 학습 시간: 약 315시간 (13일) 소요됐습니다.

 

시간이 조금 더 있었으면 좋은 결과를 낼 수 있지 않을까라는 아쉬움과 모델 구조, 학습 레시피의 조합에 따른 결과 차이도 알 수 있는 좋은 경험이였습니다.