비전 모델#

비전 모델의 정확도는 컴파일 시점에 정의한 입력 형식에 크게 좌우됩니다. 모델은 정규화된 부동소수점 텐서로 학습되었을 수 있지만, 배포 애플리케이션은 보통 원본 RGB uint8 이미지를 생성합니다. qb Compiler는 컴파일 설정, 캘리브레이션 데이터, 추론 경로가 같은 입력 형식을 사용할 때 두 방식 모두 지원할 수 있습니다.

이미지 입력 형식#

먼저 내보낸 원본 모델이 기대하는 입력을 확인합니다.

  • 색상 순서: RGB 또는 BGR

  • 데이터 타입: 보통 uint8, float32, 또는 프레임워크별 텐서 데이터 타입

  • 수치 범위: 0..255, 0..1, 또는 정규화된 값

  • 레이아웃: HWC, NHWC, NCHW

  • 공간 크기: 고정, 동적, 또는 640 x 640 같은 프리셋별 크기

  • 배치 차원: 샘플별 캘리브레이션 파일에 포함되는지 여부

이 값들을 모델 계열만 보고 추정하지 않습니다. ResNet, YOLO, ViT 내보내기 결과는 내보내기 스크립트와 전처리를 연산 그래프에 포함했는지에 따라 서로 달라질 수 있습니다.

HWC, NHWC, NCHW#

HWC는 배치 차원이 없는 단일 이미지로 높이, 너비, 채널 순서입니다. NHWC는 앞에 배치를 추가합니다. NCHW는 PyTorch에서 흔한 채널 우선 레이아웃으로 배치, 채널, 높이, 너비 순서입니다.

기존 이미지 캘리브레이션 예제는 CPU 오프로딩 없이 컴파일할 때 전처리된 샘플을 HWC NumPy 배열로 저장하는 경우가 많습니다. CPU로 일부 연산을 옮기는 흐름이나 프레임워크에 노출되는 입력 형식을 유지하는 흐름에서는 원본 모델 입력 형상인 NCHW가 필요할 수 있습니다. 컴파일러가 기대하는 형상을 사용합니다.

정확도가 예상보다 낮으면 레이아웃을 먼저 확인합니다. 전체 원소 수가 같아도 채널 순서나 축 순서가 틀린 텐서는 컴파일에 성공하면서 잘못된 결과를 만들 수 있습니다.

uint8 입력#

uint8 입력은 컴파일된 모델의 입력 경계가 0..255 범위의 정수 이미지 픽셀을 받는다는 뜻입니다. 런타임 애플리케이션이 이미지를 캡처하거나 디코딩한 뒤 NPU 호출 전에 부동소수점 전처리를 피하고 싶을 때 유용합니다.

컴파일된 모델이 uint8을 받는다면, 컴파일러 설정이 변환과 정규화를 명시적으로 결합하지 않는 한 캘리브레이션 샘플도 같은 경계의 uint8 입력을 나타내야 합니다. 정규화된 float32 샘플로 캘리브레이션하고 같은 MXQ 입력에 원본 uint8 샘플을 추론으로 보내면 안 됩니다.

원본 RGB 입력#

원본 RGB(raw RGB) 입력은 애플리케이션이 모델별 정규화 이전의 디코딩된 RGB 픽셀을 제공한다는 뜻입니다. 일반적인 원본 RGB 이미지는 형상 (H, W, 3), 데이터 타입 uint8입니다.

원본 RGB는 많은 모델이 학습에 사용한 텐서와 같지 않습니다. 예를 들어 TorchVision ImageNet 모델은 RGB 이미지를 부동소수점으로 변환하고 255로 나눈 뒤 ImageNet 평균과 표준편차로 정규화하여 학습하는 경우가 많습니다. 런타임 입력 경계가 원본 RGB라면 컴파일 설정이 이 전처리를 반영해야 학습된 모델이 기대한 값을 NPU 내부에서 볼 수 있습니다.

정규화 결합#

정규화 결합은 스케일 적용, 평균 빼기, 표준편차 나누기 같은 단순 전처리를 컴파일된 연산 그래프 또는 컴파일 시점의 입력 처리로 이동하는 방식입니다. 이를 사용하면 애플리케이션은 원본 또는 가볍게 처리한 이미지 데이터를 전달하면서도 컴파일된 모델 내부에서는 정규화된 값을 사용할 수 있습니다.

정규화 결합은 결합된 연산이 학습 및 추론 전처리와 정확히 같을 때만 사용합니다.

  • 같은 색상 순서

  • 255 나누기 같은 스케일 계수

  • 같은 채널별 평균

  • 같은 채널별 표준편차

  • 평균과 표준편차 값에 대응하는 같은 채널 순서

정규화를 결합했다면 캘리브레이션은 컴파일된 모델로 전달하는 입력 형식에 맞춰 생성해야 합니다. 예를 들어 원본 RGB uint8 입력과 결합된 ImageNet 정규화를 사용한다면, 캘리브레이션 샘플은 크기 변경/자르기 이후 정규화 이전의 원본 RGB uint8 이미지를 나타내야 합니다.

전처리 파이프라인#

비전 전처리 파이프라인은 순서가 있는 명세로 작성해야 합니다. 분류 모델에서는 다음과 같을 수 있습니다.

decode image -> RGB -> resize -> center crop -> uint8 or float conversion -> normalize -> layout conversion

YOLO 계열 detection에서는 다음과 같을 수 있습니다.

decode image -> RGB -> aspect-ratio resize -> letterbox padding -> scale -> layout conversion

각 단계는 캘리브레이션에서 관찰되는 수치 분포를 바꿉니다. 크기 변경 보간법, 패딩 값, 자르기 정책, 색상 변환이 모두 중요합니다.

캘리브레이션과 추론 일치#

캘리브레이션 데이터 생성기와 추론 전처리는 코드를 공유하거나 하나의 명세를 공유해야 합니다. 둘이 달라지면 컴파일러는 한 분포를 캘리브레이션하고 배포된 MXQ는 다른 분포를 입력으로 받게 됩니다.

흔한 불일치는 다음과 같습니다.

  • 캘리브레이션은 RGB지만 추론은 BGR 사용

  • 캘리브레이션은 255로 나누지만 추론은 0..255를 그대로 전달

  • 캘리브레이션은 정규화하지만 추론은 활성화되지 않은 결합 설정에 의존

  • 캘리브레이션은 HWC지만 추론은 NCHW 전달

  • 캘리브레이션은 가운데 자르기를 쓰지만 추론은 자르기 없이 크기 변경만 수행

  • yolo_640 캘리브레이션을 yolo_1280 내보내기 결과에 재사용

디버깅할 때는 런타임 입력 텐서 하나를 저장하고 같은 이미지에서 생성한 캘리브레이션 텐서와 수치적으로 비교합니다.

분류 프리셋#

모델이 일반적인 분류 네트워크이지만 TorchVision 레시피와 정확히 같지 않다면 classification을 사용합니다. TorchVision 가중치에서 내보냈고 RGB 디코딩, 크기 변경, 자르기, 255 스케일링, 채널별 정규화로 구성된 ImageNet 전처리를 따른다면 classification_torchvision을 사용합니다. 컴파일 설정, 캘리브레이션 데이터, 런타임 전처리가 내보낸 모델과 같은 입력 형식을 사용하도록 맞춥니다.

이미지 분류 모델은 대개 가장 단순한 비전 모델입니다. ResNet, EfficientNet, MobileNet과 유사한 CNN 모델은 보통 하나의 이미지 입력과 하나의 logits 출력을 가집니다.

CLI 예제#

일반 classification 프리셋:

python -m qbcompiler compile \
  --model resnet50.onnx \
  --backend onnx \
  --target-device regulus-rb \
  --config-preset classification \
  --calib-data-path ./calib_resnet50 \
  --output resnet50.mxq

TorchVision ImageNet 가중치에는 일반 classification 프리셋 대신 classification_torchvision을 사용합니다.

qbcompiler compile \
  --model resnet50.onnx \
  --backend onnx \
  --target-device regulus-rb \
  --config-preset classification_torchvision \
  --calib-data-path ./calib_resnet50 \
  --output resnet50.mxq

전처리 파이프라인#

classification은 분류 캘리브레이션 기본값만 설정합니다. 이를 확장한 classification_torchvision은 3채널 uint8 이미지 입력과 다음 전처리 파이프라인을 활성화합니다.

단계

프리셋 연산

1

크기 변경

256 x 256, bilinear 보간

2

가운데 자르기

224 x 224

3

스케일 적용

uint8 픽셀 값을 [0, 255]에서 부동소수점 [0.0, 1.0] 범위로 변환

4

정규화

평균 [0.485, 0.456, 0.406], 표준편차 [0.229, 0.224, 0.225]

이 프리셋은 autoConvertFormat을 활성화하고 정규화에 scaleToUint8=true, fuseIntoFirstLayer=true를 설정합니다. 캘리브레이션 이미지와 런타임 입력도 같은 RGB, 크기 변경, 자르기, 정규화 명세에 맞춰 준비해야 합니다. 모델의 이미지 크기, 보간법, 색상 순서, 스케일링 규칙, 정규화 값이 다르면 일반 classification 프리셋 또는 사용자 설정을 사용합니다.

이 레시피에서는 캘리브레이션과 런타임 추론 전에 크기 변경과 가운데 자르기를 적용합니다. 결합되는 연산은 정규화이므로 캘리브레이션 샘플은 정규화 이전의 잘라낸 RGB uint8 이미지여야 합니다.

체크리스트#

  • 보통 1x3x224x224처럼 고정 입력 형상으로 내보냅니다.

  • 배포 데이터와 유사한 이미지로 캘리브레이션 텐서를 만듭니다.

  • 캘리브레이션 전처리와 추론 전처리를 일치시킵니다.

  • 컴파일 입력이 NCHW, NHWC, HWC 중 무엇을 기대하는지 확인합니다.

  • MXQ 생성 후 원본 모델의 top-k prediction과 비교합니다.

탐지 / YOLO 프리셋#

YOLO가 아닌 탐지 모델이나 입력 크기가 프리셋과 맞지 않는 모델에는 detection을 사용합니다. 탐지 모델 내보내기 결과는 디코딩, NMS, 기타 후처리가 연산 그래프 안에 포함되는지 여부가 모델마다 다를 수 있으므로 런타임 입력/출력 경계를 가정하기 전에 MBLT를 확인합니다. 내보낸 모델 입력 크기와 전처리 파이프라인이 정사각형 640 x 640이면 yolo_640, 1280 x 1280이면 yolo_1280을 사용합니다. 1280으로 내보낸 모델을 yolo_640로 컴파일하거나 640 모델을 yolo_1280로 컴파일하지 않습니다.

YOLO 모델은 대개 NPU 본문과 애플리케이션 측 또는 CPU 오프로딩 후처리 조합으로 컴파일합니다. 합성곱 특징 추출기는 주된 NPU 작업 부하이고, 디코딩, NMS, 슬라이싱, 연결, 출력 형식 변환에는 지원되지 않거나 NPU에 올릴 가치가 낮은 연산자가 포함될 수 있습니다.

CLI 예제#

python -m qbcompiler compile \
  --model yolov8n.onnx \
  --backend onnx \
  --target-device regulus-rb \
  --config-preset yolo_640 \
  --calib-data-path ./calib_yolo_640 \
  --output yolov8n_body.mxq

입력 크기가 1280 x 1280인 YOLO 모델에는 yolo_1280을 사용합니다.

프리셋 상세#

두 YOLO 프리셋은 calibration.mode=1, calibration.output=1을 설정하는 detection을 확장합니다. 3채널 uint8 입력과 autoConvertFormat을 활성화하고, 다음 letterbox 연산을 적용합니다.

프리셋

대상 크기

패딩 값

yolo_640

640 x 640

114

yolo_1280

1280 x 1280

114

프리셋, 내보낸 ONNX 또는 프레임워크 모델 형상, letterbox 전처리, 캘리브레이션 텐서, 런타임 입력 준비가 모두 같은 이미지 크기를 기준으로 해야 합니다. 캘리브레이션은 학습/추론 letterbox 경로를 재현해야 합니다. 색상 변환, 종횡비 유지 크기 변경, 패딩 값, 스케일링, 레이아웃 변환이 모두 일치해야 합니다. NPU 본문만 컴파일하면 캘리브레이션 데이터는 본문 입력과 일치해야 합니다. CPU 오프로딩을 활성화해 전처리 또는 후처리를 컴파일 그래프에 남기면 캘리브레이션 데이터는 원본 모델 입력과 일치해야 합니다.

Netron 확인#

최종 컴파일 전에 Netron을 사용합니다.

  • 파싱된 MBLT를 엽니다.

  • 주요 YOLO 본문이 파란색, 즉 NPU에서 지원되는지 확인합니다.

  • CPU head, NPU body, CPU tail을 식별합니다.

Vision Transformer#

Vision Transformer 모델은 패치 임베딩과 어텐션 블록 때문에 형상과 레이아웃에 민감합니다. Reshape, transpose, gather, 위치 임베딩 패턴이 많이 나타납니다. 패치 임베딩과 정규화 영향은 일반 CNN보다 캘리브레이션의 전처리 불일치에 더 민감하게 작용합니다. 시작점으로 vision_transformer 프리셋을 사용합니다.

CLI 예제#

python -m qbcompiler compile \
  --model vit.onnx \
  --backend onnx \
  --target-device regulus-rb \
  --config-preset vision_transformer \
  --calib-data-path ./calib_vit \
  --output vit.mxq

프리셋 상세#

vision_transformer 프리셋은 calibration.method=1, calibration.mode=0을 설정합니다. Transformer 활성값의 outputffn 정밀도도 모두 16 bit로 설정합니다. 이 프리셋에는 이미지 전처리가 정의되어 있지 않으므로, 내보낸 모델에 필요한 크기 변경, 자르기, 스케일링, 색상 변환, 정규화를 캘리브레이션과 런타임 경로에서 제공해야 합니다.

레이어별 재정의를 포함한 양자화 설정 옵션은 모델 양자화 — 양자화 설정을 참고합니다. 프리셋, 설정 파일, dump-config 사용법은 컴파일 설정을 참고합니다. 어텐션 projection과 FFN 등 transformer 아키텍처 고유의 양자화에 대해서는 트랜스포머 모델도 참고합니다.

체크리스트#

  • 고정 이미지 크기와 고정 시퀀스 길이로 내보냅니다.

  • 모델과 대상 실행 경로가 명시적으로 다른 값을 지원하지 않는 한 배치 크기를 고정합니다.

  • 패치 임베딩이 지원 연산자로 표현되는지 확인합니다.

  • 위치 임베딩 보간을 확인합니다. 동적 보간은 컴파일된 연산 그래프 밖으로 빼야 할 수 있습니다.

  • 추론과 같은 크기 변경, 자르기, 스케일링, 정규화를 적용한 대표 캘리브레이션 이미지를 사용합니다.

parse 실패 대응#

Patchify, reshape, 어텐션 블록 주변에서 parse가 실패하면 Netron에서 MBLT를 확인하고, 모델을 다시 내보낼지, 동적 형상 로직을 단순화할지, 추출한 본문을 컴파일할지 결정합니다.

멀티모달#

이미지 입력이 여러 입력 중 하나인 모델에는 multimodal을 사용합니다. 이 경우 이미지 캘리브레이션은 같은 샘플의 텍스트 또는 다른 모달리티 입력과 정렬되어 있어야 합니다.