첫 컴파일#

이 장에서는 원본 모델에서 NPU 바이너리(MXQ)를 만드는 가장 짧은 경로를 설명합니다.

원본 모델 -> MBLT -> MXQ

예시는 ResNet-50 ONNX 모델 기준이지만, 다른 모델에서도 흐름은 같습니다.

CLI로 컴파일#

가장 빠른 첫 실행#

모델과 target device만 지정하면 바로 compile 명령을 실행할 수 있습니다.

python -m qbcompiler compile \
  --model /workspace/resnet50.onnx \
  --backend onnx \
  --target-device regulus-rb \
  --output /workspace/resnet50.mxq

성공하면 MXQ 파일이 생성됩니다.

ls -lh /workspace/resnet50.mxq

캘리브레이션 데이터를 추가해 정확도 높이기#

위 결과는 캘리브레이션 없이 양자화되었기 때문에 정확도가 크게 떨어질 수 있습니다. 실제 배포용 빌드에서는 --calib-data-path로 대표성 있는 캘리브레이션 데이터를 제공해 양자화 정확도를 높입니다. 캘리브레이션 데이터를 아직 준비하지 않았다면 캘리브레이션 데이터 준비 섹션을 참고합니다.

python -m qbcompiler compile \
  --model /workspace/resnet50.onnx \
  --backend onnx \
  --calib-data-path /workspace/calibration/resnet50 \
  --target-device regulus-rb \
  --output /workspace/resnet50.mxq

CLI는 주요 파이프라인 단계와 대응됩니다. 개별 단계를 직접 실행할 수도 있습니다.

# 원본 모델 -> MBLT (parse만)
python -m qbcompiler parse --model model.onnx --backend onnx --output model.mblt

# MBLT -> MXQ (quantize만)
python -m qbcompiler quantize --mblt model.mblt --calib-data-path calib --output model.mxq

설치된 버전의 정확한 옵션은 python -m qbcompiler <command> --help로 확인합니다.

Python으로 컴파일#

동일한 작업을 Python에서도 수행할 수 있습니다.

from qbcompiler import mxq_compile

mxq_compile(
    model="/workspace/resnet50.onnx",
    backend="onnx",
    calib_data_path="/workspace/calibration/resnet50",
    target_device="regulus-rb",
    save_path="/workspace/resnet50.mxq",
)

PyTorch 모델에서는 모델의 forward parameter 이름과 key가 일치하는 feed_dict를 제공합니다.

import torch
import torchvision
from qbcompiler import mxq_compile

model = torchvision.models.resnet50(pretrained=True).eval().cpu()
feed_dict = {"x": torch.randn(1, 3, 224, 224)}

mxq_compile(
    model=model,
    backend="torch",
    feed_dict=feed_dict,
    calib_data_path="/workspace/calibration/resnet50",
    target_device="regulus-rb",
    save_path="/workspace/resnet50.mxq",
)

feed_dict에 들어 있는 텐서 형상은 원본 모델에서 사용할 수 있어야 합니다. HuggingFace/transformers LLM은 torch 백엔드와 트랜스포머 모델 장의 전용 설정을 사용합니다.

주요 argument 요약:

목적

Python argument

원본 모델

model

입력 프레임워크

backend

캘리브레이션 샘플

calib_data_path

대상 NPU

target_device

출력 MXQ 경로

save_path

PyTorch 예제 입력

feed_dict

동일한 컴파일 설정 개념은 CLI와 Python에서 모두 사용할 수 있습니다. 옵션이 많거나 정확히 재현해야 하는 빌드에는 설정 파일을 사용합니다.

qb Compiler는 classification, yolo_640, llm, vision_transformer 등 주요 모델 계열을 위한 내장 프리셋을 제공합니다. 사용 가능한 프리셋 목록은 python -m qbcompiler presets로 확인합니다. 모델 계열별 컴파일 가이드는 비전 모델트랜스포머 모델을 참고합니다.

캘리브레이션 데이터 준비#

캘리브레이션 데이터는 컴파일된 모델이 실제 추론에서 받을 이미지 또는 텐서와 일치해야 합니다. 이미지 모델에서는 원본 이미지 파일 디렉터리를 calib_data_path에 직접 전달하고 PreprocessingConfig로 모델 전처리를 정의하거나, 준비된 NumPy 텐서를 사용할 수 있습니다.

전처리 파이프라인으로 원본 이미지 사용#

표준 TorchVision ResNet-50 recipe에서는 대표 JPEG 또는 PNG 이미지를 /workspace/calibration/imagenet-1k-selected 같은 디렉터리에 둡니다. .npy 파일로 변환하지 않고, 해당 디렉터리를 calib_data_path에 전달한 뒤 Python 컴파일 호출에서 전처리 파이프라인을 설정합니다.

from qbcompiler import (
    CalibrationConfig,
    PreprocessingConfig,
    Uint8InputConfig,
    mxq_compile,
)

preprocess_pipeline = [
    {"op": "resize", "height": 256, "width": 256, "mode": "bilinear"},
    {"op": "centerCrop", "height": 224, "width": 224},
    {
        "op": "normalize",
        "scaleToUint8": True,  # [0, 255] -> [0.0, 1.0]
        "mean": [0.485, 0.456, 0.406],
        "std": [0.229, 0.224, 0.225],
        "fuseIntoFirstLayer": True,
    },
]

preprocessing_config = PreprocessingConfig(
    apply=True,
    auto_convert_format=True,
    pipeline=preprocess_pipeline,
    input_configs={},
)

calibration_config = CalibrationConfig(
    method=1,
    output=0,
    mode=1,
    max_percentile={"percentile": 0.9999, "topk_ratio": 0.01},
)

mxq_compile(
    model="/workspace/resnet50.onnx",
    backend="onnx",
    calib_data_path="/workspace/calibration/imagenet-1k-selected",
    save_path="/workspace/resnet50.mxq",
    target_device="regulus-rb",
    device="cpu",
    inference_scheme="single",
    image_channels=3,
    preprocessing_config=preprocessing_config,
    uint8_input_config=Uint8InputConfig(apply=True, inputs=[]),
    calibration_config=calibration_config,
)

image_channels=3은 필요한 경우 흑백 캘리브레이션 이미지를 RGB로 변환합니다. auto_convert_format=True는 입력 형식 변환을 처리합니다. 파이프라인은 bilinear 크기 변경, 가운데 자르기, ImageNet 스케일링 및 정규화를 사용합니다. fuseIntoFirstLayer=TrueUint8InputConfig를 사용하면 정규화를 첫 레이어에 결합하면서 MXQ 모델이 uint8 이미지 입력을 받을 수 있습니다.

inference_scheme="single"은 MXQ를 Single 코어 모드용으로 컴파일합니다. multi, global4, global8 같은 다른 값은 ARIES 런타임의 다른 코어 모드를 선택하므로, 값을 바꾸기 전에 참조 — inferenceScheme을 확인하세요.

이 방법은 파이프라인이 모델의 학습/추론 레시피와 일치할 때만 사용합니다. 모델의 이미지 크기, 색상 순서, 크기 변경 규칙, 자르기 규칙, 스케일링, 정규화가 다르면 파이프라인도 그에 맞게 변경해야 합니다.

준비된 NumPy 텐서 사용#

qb Compiler는 YAML 전처리 설명 또는 Python 전처리 함수를 사용해 전처리된 NumPy 캘리브레이션 샘플도 만들 수 있습니다.

YAML 기반 전처리 예시:

from qbcompiler.calibration import make_calib

make_calib(
    args_pre="/workspace/resnet50.yaml",
    data_dir="/workspace/calibration/cali_1000",
    save_dir="/workspace/calibration",
    save_name="resnet50",
    max_size=100,
)

전처리 YAML 예시:

Datatype: Image
GetImage:
    to_float32: false
    channel_order: RGB

Pre-Order: [ResizeTorch, CenterCrop, Normalize, SetOrder]
Pre-processing:
    ResizeTorch:
        size: 256
        interpolation: bilinear
    CenterCrop:
        size: [224, 224]
    Normalize:
        mean: [0.485, 0.456, 0.406]
        std: [0.229, 0.224, 0.225]
        to_float_div255: true
    SetOrder:
        shape: HWC

직접 작성한 전처리 함수 예시:

import numpy as np
import torch
import torchvision.transforms.functional as F
from PIL import Image
from torchvision.transforms import InterpolationMode
from qbcompiler.calibration import make_calib_man

def preprocess_resnet50(img_path: str):
    img = Image.open(img_path)
    out = F.pil_to_tensor(img)
    out = F.resize(out, size=256, interpolation=InterpolationMode.BILINEAR)
    out = F.center_crop(out, output_size=(224, 224))
    out = out.to(torch.float, copy=False) / 255.0
    out = F.normalize(out, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    return np.transpose(out.numpy(), axes=[1, 2, 0])

make_calib_man(
    pre_ftn=preprocess_resnet50,
    data_dir="/workspace/calibration/cali_1000",
    save_dir="/workspace/calibration",
    save_name="resnet50",
    max_size=100,
)

두 예시는 다음 산출물을 생성합니다.

  • /workspace/calibration/resnet50: 전처리된 .npy 샘플 디렉터리

  • /workspace/calibration/resnet50.txt: 샘플 목록을 담은 메타데이터 파일

두 경로 모두 캘리브레이션 입력으로 사용할 수 있습니다.

컴파일 결과 검증#

MXQ 파일이 오류 없이 생성되었다고 올바른 추론이 보장되지는 않습니다. 컴파일된 모델을 NPU에서 실행하고 원본 floating-point 모델의 결과와 비교해야 합니다.

qbruntime으로 추론 실행#

Mobilint 런타임 라이브러리(pip install mobilint-qb-runtime)를 설치하고 target device에서 MXQ를 실행합니다. 설치 방법은 Runtime 설치 가이드를 참고합니다.

import qbruntime
import numpy as np
import torch
from PIL import Image
from torchvision.transforms import functional as F, InterpolationMode

acc = qbruntime.Accelerator(0)
mc = qbruntime.ModelConfig()
mxq_model = qbruntime.Model("resnet50.mxq", mc)
mxq_model.launch(acc)

img = Image.open("test.jpg").convert("RGB")
out = F.pil_to_tensor(img)
out = F.resize(out, size=256, interpolation=InterpolationMode.BILINEAR)
out = F.center_crop(out, output_size=(224, 224))
out = out.to(torch.float) / 255.0
out = F.normalize(out, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
image = np.transpose(out.numpy(), axes=[1, 2, 0])  # HWC float32

output = mxq_model.infer(image)

전처리는 모델을 캘리브레이션할 때와 동일해야 합니다. 위 예제는 기본 컴파일 경로가 float32 캘리브레이션 데이터를 사용하므로 정규화된 float32 입력을 보냅니다. 정규화를 모델에 결합하여 uint8 입력을 사용하는 작업 흐름은 SDK Tutorial — Image Classification을 참고합니다.

모델 로딩, 후처리, top-k 출력을 포함한 전체 런타임 예제는 SDK Tutorial — Runtime을 참고합니다.

출력 확인#

대표적인 입력 몇 개를 넣고 출력이 합리적인지 확인합니다.

  • Classification: top-5 예측에 기대하는 클래스가 포함되어야 합니다

  • Detection: bounding box가 합리적인 위치와 크기에 나타나야 합니다

  • LLM: 생성된 텍스트가 프롬프트에 맞게 자연스러워야 합니다

출력이 이상하면, 예를 들어 이미지를 잘못 분류하거나 탐지 결과가 비어 있거나 텍스트가 깨지는 경우, 캘리브레이션 데이터 품질을 점검하거나 다른 프리셋을 사용하거나 양자화 옵션을 조정합니다. 튜닝 방법은 모델 양자화를 참고합니다.

컴파일 결과 보관#

반복 가능한 빌드를 위해 다음 파일을 함께 보관합니다.

  • 원본 모델 또는 모델 리비전

  • 캘리브레이션 데이터 디렉터리 또는 .txt 메타데이터 파일

  • 컴파일 설정 또는 프리셋 이름

  • target device 문자열

  • 생성된 .mxq