YOLO Vision 2026:

Ultralytics로 Non-YOLO PyTorch 모델을 Export하는 방법#

Ultralytics는 여러 백엔드를 하나의 일관된 인터페이스 뒤에서 래핑하는 ultralytics.utils.export을 통해 독립형 내보내기 유틸리티를 제공합니다. timm 이미지 모델, torchvision 분류기 및 감지기, 또는 사용자 정의 아키텍처를 포함하여 모든 torch.nn.ModuleONNX, TorchScript, OpenVINO, CoreML, NCNN, PaddlePaddle, MNN, ExecuTorch, Core AI, TensorFlow SavedModel, 및 TensorFlow Frozen Graph로 각 백엔드를 따로 학습할 필요 없이 내보낼 수 있습니다.

PyTorch 모델을 production 환경에 배포하려면 일반적으로 target마다 다른 exporter를 사용해야 합니다. 예를 들어 ONNX에는 torch.onnx.export, Apple 디바이스에는 coremltools, TensorFlow에는 onnx2tf, NCNN에는 pnnx 등을 사용합니다. 각 도구에는 고유한 API, dependency 관련 특이사항, output convention이 있습니다. 이러한 유틸리티는 이 과정을 하나의 호출 패턴으로 통합합니다.

Non-YOLO Export에 Ultralytics를 사용하는 이유#

  • 11개 포맷에 걸친 단일 API: 수십 개 대신 단일 호출 규칙을 학습합니다.
  • 공유 utility surface: export helper는 ultralytics.utils.export 아래에 있으므로 backend package를 설치한 후 format 전반에서 동일한 호출 패턴을 유지할 수 있습니다.
  • YOLO export와 동일한 code path: 모든 Ultralytics YOLO export가 동일한 helper를 사용합니다.
  • FP16 및 INT8 양자화는 이를 지원하는 포맷(OpenVINO, CoreML, MNN; NCNN 및 Core AI는 FP16만 지원)에 대해 내장되어 있습니다.
  • CPU에서 작동: 내보내기 단계 자체에 GPU가 필요하지 않으므로 노트북에서 로컬로 실행할 수 있습니다. CoreML 내보내기는 Windows에서 지원되지 않으며, Core AI 내보내기는 Apple silicon에서 macOS 26 이상이 필요합니다.

빠른 시작#

가장 빠른 방법은 YOLO code나 pip install ultralytics onnx timm 외의 별도 setup 없이 ONNX로 두 줄만 export하는 것입니다:

import timm
import torch

from ultralytics.utils.export import torch2onnx

model = timm.create_model("resnet18", pretrained=True).eval()
torch2onnx(model, torch.randn(1, 3, 224, 224), output_file="resnet18.onnx")

지원되는 Export Format#

torch2* 함수는 표준 torch.nn.Module과 example input tensor를 받습니다. MNN, TF SavedModel, TF Frozen Graph는 중간 ONNX 또는 Keras artifact를 거칩니다. 어느 경우에도 YOLO 전용 attribute는 필요하지 않습니다.

형식함수설치출력
ONNXtorch2onnx()pip install onnx.onnx 파일
TorchScripttorch2torchscript()PyTorch에 포함됨.torchscript 파일
OpenVINOtorch2openvino()pip install openvino_openvino_model/ directory
CoreMLtorch2coreml()pip install coremltools.mlpackage
TF SavedModelonnx2saved_model()아래에서 자세한 요구 사항 확인_saved_model/ directory
TF Frozen Graphkeras2pb()아래에서 자세한 요구 사항 확인.pb 파일
NCNNtorch2ncnn()pip install ncnn pnnx_ncnn_model/ directory
MNNonnx2mnn()pip install MNN.mnn 파일
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddle_paddle_model/ directory
ExecuTorchtorch2executorch()pip install executorch_executorch_model/ directory
Core AItorch2coreai()pip install coreai-torch (Apple silicon의 macOS 26 이상).aimodel directory
중간 format으로서의 ONNX

MNN, TF SavedModel, TF Frozen Graph export는 중간 단계로 ONNX를 거칩니다. 먼저 ONNX로 export한 다음 변환합니다.

Metadata 삽입

여러 export 함수는 선택적 metadata dictionary(예: torch2torchscript(..., metadata={"author": "me"}))를 받아 format에서 지원하는 경우 export된 artifact에 custom key-value pair를 삽입합니다.

단계별 예제#

아래의 모든 예제는 evaluation mode로 실행되는 timm의 pretrained ResNet-18을 사용하는 동일한 setup을 따릅니다:

import timm
import torch

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
Export 전에 항상 `model.eval()`을 호출합니다

Dropout, batch normalization, 기타 training 전용 layer는 inference 중에 다르게 작동합니다. .eval()을 생략하면 output이 올바르지 않은 export가 생성됩니다.

ONNX로 Export#

from ultralytics.utils.export import torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")

dynamic batch size를 사용하려면 dynamic dictionary를 전달합니다:

torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})

기본 opset은 14이고 기본 input name은 "images"입니다. opset, input_names 또는 output_names argument로 이를 재정의할 수 있습니다.

TorchScript로 Export#

추가 dependency가 필요하지 않습니다. 내부적으로 torch.jit.trace을 사용합니다.

from ultralytics.utils.export import torch2torchscript

torch2torchscript(model, im, output_file="resnet18.torchscript")

OpenVINO로 Export#

from ultralytics.utils.export import torch2openvino

ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")

directory에는 고정된 이름의 model.xmlmodel.bin pair가 포함됩니다:

resnet18_openvino_model/
├── model.xml
└── model.bin

dynamic input shape에는 dynamic=True, FP16에는 quantize=16, INT8 quantization에는 quantize=8를 전달합니다. INT8에는 추가로 calibration_dataset argument가 필요합니다.

openvino>=2024.0.0(또는 macOS 15.4 이상에서는 >=2025.2.0) 및 torch>=2.1가 필요합니다.

CoreML로 Export#

import coremltools as ct

from ultralytics.utils.export import torch2coreml

inputs = [ct.TensorType("input", shape=(1, 3, 224, 224))]
ct_model = torch2coreml(model, inputs, im, classifier_names=None, output_file="resnet18.mlpackage")

classification 모델의 경우 class name 목록을 classifier_names에 전달하여 CoreML 모델에 classification head를 추가합니다.

coremltools>=9.0, torch>=1.11, numpy<=2.3.5가 필요합니다. Windows에서는 지원되지 않습니다.

`BlobWriter not loaded` error

coremltools>=9.0은 macOS 및 Linux에서 Python 3.10–3.13용 wheel을 제공합니다. 최신 Python version에서는 native C extension을 load하지 못합니다. CoreML export에는 Python 3.10–3.13을 사용합니다.

TensorFlow SavedModel로 Export#

TF SavedModel export는 중간 단계로 ONNX를 거칩니다:

from ultralytics.utils.export import onnx2saved_model, torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")
keras_model = onnx2saved_model("resnet18.onnx", output_dir="resnet18_saved_model")

이 함수는 Keras model을 반환하고 FP32 및 FP16 LiteRT file(.tflite)도 output directory 내부에 생성합니다:

resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tflite

이들과 함께 INT8 .tflite을 추가하려면 quantize=8을 전달합니다.

요구 사항:

  • tensorflow>=2.0.0,<=2.19.0
  • onnx2tf>=1.26.3,<1.29.0
  • tf_keras<=2.19.0
  • sng4onnx>=1.0.1
  • onnx_graphsurgeon>=0.3.26
  • macOS에서는 ai-edge-litert>=1.2.0,<1.4.0(기타 platform에서는 ai-edge-litert>=1.2.0)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

TensorFlow Frozen Graph로 Export#

위의 SavedModel export에 이어 반환된 keras_model을 frozen .pb graph로 변환합니다:

from pathlib import Path

from ultralytics.utils.export import keras2pb

keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))

NCNN으로 Export#

from ultralytics.utils.export import torch2ncnn

torch2ncnn(model, im, output_dir="resnet18_ncnn_model")

directory에는 Python wrapper와 함께 고정된 이름의 param 및 bin file이 포함됩니다:

resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.py

torch2ncnn()은 처음 사용할 때 ncnnpnnx를 확인합니다.

MNN으로 Export#

MNN export에는 input으로 ONNX file이 필요합니다. 먼저 ONNX로 export한 다음 변환합니다:

from ultralytics.utils.export import onnx2mnn, torch2onnx

torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")

FP16에는 quantize=16, INT8 quantization에는 quantize=8을 지원합니다. MNN>=2.9.6torch>=1.10이 필요합니다.

PaddlePaddle로 Export#

from ultralytics.utils.export import torch2paddle

torch2paddle(model, im, output_dir="resnet18_paddle_model")

directory에는 PaddlePaddle model 및 parameter file이 포함됩니다:

resnet18_paddle_model/
├── model.pdmodel
└── model.pdiparams

x2paddle 및 platform에 맞는 올바른 PaddlePaddle distribution이 필요합니다:

  • CUDA에서 paddlepaddle-gpu>=3.0.0,<3.3.0
  • ARM64 CPU에서 paddlepaddle==3.0.0
  • 기타 CPU에서 paddlepaddle>=3.0.0,<3.3.0

NVIDIA Jetson에서는 지원되지 않습니다.

ExecuTorch로 Export#

from ultralytics.utils.export import torch2executorch

torch2executorch(model, im, output_dir="resnet18_executorch_model")

export된 .pte file은 output directory 내부에 저장됩니다:

resnet18_executorch_model/
└── model.pte

torch>=2.9.0 및 호환되는 ExecuTorch runtime(pip install executorch)이 필요합니다. runtime 사용법은 ExecuTorch integration을 참조하세요.

Core AI로 내보내기#

from ultralytics.utils.export import torch2coreai

torch2coreai(model, im, output_file="resnet18.aimodel")

.aimodel 에셋은 디렉터리입니다:

resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.json

내보내기는 Apple silicon의 macOS 26 이상(pip install coreai-torch)에서 실행되며, quantize=16은 float16 입력을 받는 FP16 에셋을 기록하고, 이 에셋은 iOS 27 및 macOS 27에서 실행됩니다. 로드 시 중단되는 FP16 에셋에 대한 참고 사항을 포함하여 Core AI 통합을 참조하십시오.

Export된 모델 검증#

Export한 후 production에 배포하기 전에 원본 PyTorch model과의 numerical parity를 확인합니다. ultralytics.nn.backendsONNXBackend을 사용하는 간단한 smoke test는 output을 비교하고 tracing 또는 quantization error를 조기에 표시합니다:

import numpy as np
import timm
import torch

from ultralytics.nn.backends import ONNXBackend

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
with torch.no_grad():
    pytorch_output = model(im).numpy()

onnx_model = ONNXBackend("resnet18.onnx", device=torch.device("cpu"))
onnx_output = onnx_model(im)[0]

diff = np.abs(pytorch_output - onnx_output).max()
print(f"Max difference: {diff:.6f}")  # ~1e-6 for an FP32 ONNX export
예상 차이

tolerance는 global이 아니라 format별로 적용됩니다. ResNet-18에서 FP32 export는 ONNX, TF SavedModel 및 LiteRT의 경우 1e-6 근처에 도달하고, TorchScript의 경우 정확히 0에 도달합니다. NCNN은 대략 1e-2로 차이가 나는 예외적인 경우입니다. CPU runtime이 기본적으로 FP16 packing 및 arithmetic을 활성화하므로 FP32 export도 half precision으로 실행됩니다. format 자체의 baseline보다 훨씬 큰 차이는 지원되지 않는 op, 잘못된 input shape 또는 eval mode가 아닌 model을 의미합니다. FP16 및 INT8 export에는 더 느슨한 tolerance가 적용됩니다. random tensor 대신 실제 data로 검증하세요.

다른 runtime에서는 input tensor name이 다를 수 있습니다. 예를 들어 OpenVINO는 model의 forward-argument name을 사용하며 generic model에서는 일반적으로 x입니다. 반면 torch2onnx의 기본값은 "images"입니다.

Export한 모델 실행#

Export한 non-YOLO model은 일반 YOLO() API를 통해 다시 load합니다. 위의 export에는 Ultralytics task 또는 input-size metadata가 포함되어 있지 않으므로 task을 명시적으로 전달하고, export에 사용한 example tensor와 일치하는 imgsz를 전달합니다:

from ultralytics import YOLO

results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)

export에 고정 input shape가 있는 경우 imgsz이 중요합니다. 위의 ONNX 및 TF SavedModel export는 기본값 640을 거부합니다. 위의 TorchScript 및 NCNN export는 다른 size를 허용하지만 어느 exporter도 이를 보장하지 않습니다. 두 exporter 모두 example tensor에서 tracing하므로 Linear layer로 flatten되는 model은 고정된 상태로 유지됩니다. 자체 export를 확인하세요.

그런 다음 value는 model stride의 배수로 올림됩니다. metadata가 없을 때 stride는 32이므로 200x200의 고정 shape export에는 224x224가 입력되며, imgsz=200이 일치하더라도 거부됩니다. 32의 배수가 아닌 input size에는 backend를 직접 호출하세요.

Backend 직접 호출#

Ultralytics preprocessing 및 post-processing이 없는 raw tensor에는 ultralytics.nn.backends의 format별 class를 사용합니다. 위의 verification example이 이를 보여 줍니다. 각 class는 export된 artifact와 device를 받고 callable합니다:

형식BackendInput layout
ONNXONNXBackendBCHW
TorchScriptTorchScriptBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
TF SavedModel, Frozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
NCNNNCNNBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW
Core AICoreAIBackendBCHW

TensorFlowBackend은 두 format을 처리하고 기본값으로 format="saved_model"을 사용하므로 frozen graph에는 format="pb"를 전달합니다.

YOLO() 경로가 처리하지만 직접 호출에서는 처리하지 않는 세 가지 항목:

  • Input layout: CoreMLBackendTensorFlowBackend은 BHWC를 예상합니다. 먼저 im.permute(0, 2, 3, 1)로 transpose하세요. BCHW tensor를 사용하면 shape mismatch가 발생합니다.
  • Autograd: 호출을 torch.inference_mode()으로 감싸세요. TorchScriptBackendgradient graph가 여전히 연결된 tensor를 반환합니다.
  • Post-processing: metadata가 없으면 backend는 taskNone로 남기고 names는 비워 둡니다. LiteRTBackend은 여전히 3-D output을 YOLO box를 포함한다고 가정하여 image size로 denormalize합니다. 이는 3-D output을 가진 non-YOLO model에서는 잘못된 동작입니다. classifier logit과 같은 2차원 output에는 영향을 주지 않습니다.

알려진 제한 사항#

  • 다중 입력 지원이 균등하지 않음: torch2onnxtorch2openvino은 다중 입력이 있는 모델에 대해 예제 텐서의 튜플이나 리스트를 허용합니다. torch2torchscript, torch2coreml, torch2ncnn, torch2paddle, torch2executorch, torch2coreai은 단일 입력 텐서를 가정합니다.
  • ExecuTorch에는 flatc이 필요합니다: ExecuTorch runtime에는 FlatBuffers compiler가 필요합니다. macOS에서는 brew install flatbuffers, Ubuntu에서는 apt install flatbuffers-compiler로 설치합니다.
  • 삽입된 metadata 없음: 위의 export에는 Ultralytics task 또는 input-size metadata가 포함되어 있지 않으므로 YOLO()은 어느 것도 추론할 수 없으며 두 값을 모두 명시적으로 전달해야 합니다. Export한 모델 실행을 참조하세요.
  • YOLO 전용 format: AxeleraSony IMX500 export에는 YOLO 전용 model attribute가 필요하므로 generic model에서는 사용할 수 없습니다.
  • Platform별 format: TensorRT에는 NVIDIA GPU가 필요합니다. RKNN에는 rknn-toolkit2 SDK가 필요합니다(Linux 전용). Edge TPU에는 edgetpu_compiler binary가 필요합니다(Linux 전용).

결론#

이러한 유틸리티는 일반 torch.nn.Module부터 deployment-ready ONNX, OpenVINO, CoreML, TensorFlow 또는 mobile-runtime artifact까지 모든 PyTorch model을 하나의 일관된 API로 변환합니다. target hardware에 맞는 format을 선택하고, 원본 model과 numerical parity를 검증한 다음, runtime별 deployment 단계에 해당하는 integration guide를 따르세요.

FAQ#

  • 모든 torch.nn.Module입니다. 여기에는 timm, torchvision의 model 또는 모든 custom PyTorch model이 포함됩니다. export 전에 model은 evaluation mode(model.eval())여야 합니다. ONNX 및 OpenVINO는 multi-input model에 대해 example tensor의 tuple도 허용합니다.

  • 지원되는 모든 포맷(TorchScript, ONNX, OpenVINO, CoreML, TF SavedModel, TF Frozen Graph, NCNN, PaddlePaddle, MNN, ExecuTorch, Core AI)은 CPU에서 내보낼 수 있습니다. 내보내기 프로세스 자체에 GPU는 필요하지 않습니다. TensorRT는 NVIDIA GPU가 필요한 유일한 포맷입니다.

  • ultralytics.utils.export 모듈과 표준화된 output_file/output_dir 인자를 포함하는 Ultralytics >=8.4.38을 사용합니다.

  • 예. torchvision 분류, 탐지 및 세그멘테이션 모델은 torch2coreml을 통해 .mlpackage으로 Export할 수 있습니다. 이미지 분류 모델의 경우 클래스 이름 목록을 classifier_names에 전달하여 분류 헤드를 내장합니다. macOS 또는 Linux에서 Export를 실행합니다. CoreML은 Windows에서 지원되지 않습니다. iOS 배포에 대한 자세한 내용은 CoreML 통합을 참조하세요.

  • 예, 여러 포맷에서 가능합니다. OpenVINO, CoreML 또는 MNN으로 내보낼 때 FP16의 경우 quantize=16을 전달하고 INT8의 경우 quantize=8을 전달합니다. NCNN 및 Core AI는 기본적으로 FP32로 내보내고, FP16의 경우 quantize=16를 취하며, INT8 경로는 없습니다. OpenVINO의 INT8은 추가로 후학습 양자화를 위한 calibration_dataset 인수가 필요합니다. 양자화 트레이드오프에 대해서는 각 포맷의 통합 페이지를 참조하십시오.

  • 동일한 입력에서 원본 PyTorch 모델과 Export한 모델을 실행한 다음 출력을 비교합니다. 일치하는 백엔드를 사용하여 Export한 파일을 로드하고(예: ONNX에는 ONNXBackend) 최대 절대 차이를 확인합니다. 차이를 해당 형식 자체의 기준값과 비교하여 판단합니다. 위의 ResNet-18 예제에서는 FP32 ONNX, TF SavedModel 및 LiteRT가 1e-6 근처에 있고, TorchScript는 0이며, NCNN은 CPU 런타임이 기본적으로 FP16을 사용하므로 1e-2 근처에 있습니다. 차이가 훨씬 크다면 지원되지 않는 연산, 잘못된 입력 형태 또는 eval 모드가 아닌 모델이 원인일 수 있습니다. 실행 가능한 예제는 Export한 모델 검증을 참조하세요.

댓글