Ultralytics로 Non-YOLO PyTorch 모델을 Export하는 방법#
Ultralytics는 여러 백엔드를 하나의 일관된 인터페이스 뒤에서 래핑하는 ultralytics.utils.export을 통해 독립형 내보내기 유틸리티를 제공합니다. timm 이미지 모델, torchvision 분류기 및 감지기, 또는 사용자 정의 아키텍처를 포함하여 모든 torch.nn.Module를 ONNX, 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는 필요하지 않습니다.
| 형식 | 함수 | 설치 | 출력 |
|---|---|---|---|
| ONNX | torch2onnx() | pip install onnx | .onnx 파일 |
| TorchScript | torch2torchscript() | PyTorch에 포함됨 | .torchscript 파일 |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/ directory |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| TF SavedModel | onnx2saved_model() | 아래에서 자세한 요구 사항 확인 | _saved_model/ directory |
| TF Frozen Graph | keras2pb() | 아래에서 자세한 요구 사항 확인 | .pb 파일 |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/ directory |
| MNN | onnx2mnn() | pip install MNN | .mnn 파일 |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/ directory |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/ directory |
| Core AI | torch2coreai() | pip install coreai-torch (Apple silicon의 macOS 26 이상) | .aimodel directory |
MNN, TF SavedModel, TF Frozen Graph export는 중간 단계로 ONNX를 거칩니다. 먼저 ONNX로 export한 다음 변환합니다.
여러 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)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.xml 및 model.bin pair가 포함됩니다:
resnet18_openvino_model/
├── model.xml
└── model.bindynamic 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에서는 지원되지 않습니다.
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.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26- macOS에서는
ai-edge-litert>=1.2.0,<1.4.0(기타 platform에서는ai-edge-litert>=1.2.0) onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=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.pytorch2ncnn()은 처음 사용할 때 ncnn 및 pnnx를 확인합니다.
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.6 및 torch>=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.pdiparamsx2paddle 및 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.ptetorch>=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.backends의 ONNXBackend을 사용하는 간단한 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 exporttolerance는 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합니다:
| 형식 | Backend | Input layout |
|---|---|---|
| ONNX | ONNXBackend | BCHW |
| TorchScript | TorchScriptBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| TF SavedModel, Frozen Graph | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
| Core AI | CoreAIBackend | BCHW |
TensorFlowBackend은 두 format을 처리하고 기본값으로 format="saved_model"을 사용하므로 frozen graph에는 format="pb"를 전달합니다.
YOLO() 경로가 처리하지만 직접 호출에서는 처리하지 않는 세 가지 항목:
- Input layout:
CoreMLBackend및TensorFlowBackend은 BHWC를 예상합니다. 먼저im.permute(0, 2, 3, 1)로 transpose하세요. BCHW tensor를 사용하면 shape mismatch가 발생합니다. - Autograd: 호출을
torch.inference_mode()으로 감싸세요.TorchScriptBackend은 gradient graph가 여전히 연결된 tensor를 반환합니다. - Post-processing: metadata가 없으면 backend는
task을None로 남기고names는 비워 둡니다.LiteRTBackend은 여전히 3-D output을 YOLO box를 포함한다고 가정하여 image size로 denormalize합니다. 이는 3-D output을 가진 non-YOLO model에서는 잘못된 동작입니다. classifier logit과 같은 2차원 output에는 영향을 주지 않습니다.
알려진 제한 사항#
- 다중 입력 지원이 균등하지 않음:
torch2onnx및torch2openvino은 다중 입력이 있는 모델에 대해 예제 텐서의 튜플이나 리스트를 허용합니다.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: Axelera 및 Sony IMX500 export에는 YOLO 전용 model attribute가 필요하므로 generic model에서는 사용할 수 없습니다.
- Platform별 format: TensorRT에는 NVIDIA GPU가 필요합니다. RKNN에는
rknn-toolkit2SDK가 필요합니다(Linux 전용). Edge TPU에는edgetpu_compilerbinary가 필요합니다(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한 모델 검증을 참조하세요.