Ultralytics로 YOLO가 아닌 PyTorch 모델 내보내기#
Ultralytics는 여러 백엔드를 일관된 단일 인터페이스로 감싸는 독립형 내보내기 유틸리티를 ultralytics.utils.export에 제공합니다. timm 이미지 모델, torchvision 분류기 및 탐지기, 사용자 지정 아키텍처 등 모든 torch.nn.Module를 개별 백엔드 사용법을 익히지 않고도 TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch로 내보낼 수 있습니다.
PyTorch 모델을 프로덕션에 배포하려면 보통 대상별로 다른 내보내기 도구를 사용해야 합니다. ONNX에는 torch.onnx.export, Apple 기기에는 coremltools, TensorFlow에는 onnx2tf, NCNN에는 pnnx 등을 사용합니다. 도구마다 고유한 API, 의존성 관련 차이, 출력 규칙이 있습니다. 이 유틸리티는 이러한 과정을 하나의 호출 패턴으로 통합합니다.
YOLO가 아닌 모델을 내보낼 때 Ultralytics를 사용하는 이유#
- 11개 형식에서 하나의 API 사용: 여러 가지 호출 규칙을 익히는 대신 하나만 익히면 됩니다.
- YOLO 내보내기와 동일한 코드 경로: 모든 Ultralytics YOLO 내보내기에서 동일한 헬퍼를 사용합니다.
- 지원되는 형식에서는 단일
quantize인수를 통해 FP16 및 INT8 양자화를 수행합니다. - CPU에서 작동합니다: 내보내기 단계 자체에는 GPU가 필요하지 않으므로 노트북에서 로컬로 실행할 수 있습니다. CoreML 내보내기는 Windows에서 지원되지 않으며, Core AI 내보내기는 Apple silicon의 macOS 26 이상 또는 glibc 2.34 이상을 사용하는 x86_64 Linux와 Python 3.11~3.14가 필요합니다.
빠른 시작#
가장 빠른 방법은 YOLO 코드나 pip install ultralytics onnx timm 이외의 별도 설정 없이 ONNX로 내보내는 두 줄짜리 코드입니다:
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")지원되는 내보내기 형식#
torch2* 함수는 표준 torch.nn.Module과 예시 입력 텐서를 받습니다. MNN, TF SavedModel, TF Frozen Graph는 중간 ONNX 또는 Keras 아티팩트를 거칩니다. 어느 경우에도 YOLO 전용 속성은 필요하지 않습니다.
| 형식 | 함수 | 설치 | 출력 |
|---|---|---|---|
| TorchScript | torch2torchscript() | PyTorch에 포함됨 | .torchscript 파일 |
| ONNX | torch2onnx() | pip install onnx | .onnx 파일 |
| OpenVINO | torch2openvino() | pip install openvino | _openvino_model/ 디렉터리 |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| Core AI | torch2coreai() | pip install coreai-torch(Apple silicon macOS 26 이상, x86_64 Linux glibc 2.34 이상, Python 3.11~3.14) | .aimodel 디렉터리 |
| TF SavedModel | onnx2saved_model() | 자세한 요구 사항은 아래를 참조하세요 | _saved_model/ 디렉터리 |
| TF Frozen Graph | keras2pb() | 자세한 요구 사항은 아래를 참조하세요 | .pb 파일 |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | _paddle_model/ 디렉터리 |
| MNN | onnx2mnn() | pip install MNN | .mnn 파일 |
| NCNN | torch2ncnn() | pip install ncnn pnnx | _ncnn_model/ 디렉터리 |
| ExecuTorch | torch2executorch() | pip install executorch | _executorch_model/ 디렉터리 |
일부 내보내기 함수는 선택적 metadata 딕셔너리(예: torch2torchscript(..., metadata={"author": "me"}))를 받아, 해당 형식에서 지원하는 경우 내보낸 아티팩트에 사용자 지정 키-값 쌍을 삽입합니다.
단계별 예제#
아래의 모든 예제는 동일한 설정을 사용하며, 평가 모드의 사전 학습된 timm ResNet-18을 사용합니다:
import timm
import torch
model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)Dropout, 배치 정규화 및 기타 학습 전용 계층은 추론 중에 다르게 작동합니다. .eval()을 건너뛰면 출력이 잘못된 모델이 내보내집니다.
ONNX로 내보내기#
from ultralytics.utils.export import torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")동적 배치 크기를 사용하려면 dynamic 딕셔너리를 전달하세요:
torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})기본 opset은 14이며 기본 입력 이름은 "images"입니다. opset, input_names 또는 output_names 인수로 재정의할 수 있습니다.
TorchScript로 내보내기#
추가 의존성이 필요하지 않습니다. 내부적으로 torch.jit.trace을 사용합니다.
from ultralytics.utils.export import torch2torchscript
torch2torchscript(model, im, output_file="resnet18.torchscript")OpenVINO로 내보내기#
from ultralytics.utils.export import torch2openvino
ov_model = torch2openvino(model, im, output_dir="resnet18_openvino_model")디렉터리에는 이름이 고정된 model.xml 및 model.bin 파일 쌍이 들어 있습니다:
resnet18_openvino_model/
├── model.xml
└── model.bin동적 입력 형태에는 dynamic=True, FP16에는 quantize=16, INT8 양자화에는 quantize=8를 전달하세요. INT8에는 calibration_dataset 인수도 필요합니다.
openvino>=2024.0.0(또는 macOS 15.4 이상에서는 >=2025.2.0) 및 torch>=2.1가 필요합니다.
CoreML로 내보내기#
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")분류 모델의 경우 클래스 이름 목록을 classifier_names에 전달하면 CoreML 모델에 분류 헤드를 추가할 수 있습니다.
coremltools>=9.0, torch>=1.11, numpy<=2.3.5가 필요합니다. Windows에서는 지원되지 않습니다.
coremltools>=9.0은 macOS 및 Linux에서 Python 3.103.13용 휠을 제공합니다. 이후 Python 버전에서는 네이티브 C 확장 모듈을 로드할 수 없습니다. CoreML 내보내기에는 Python 3.103.13을 사용하세요.
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 이상 또는 glibc 2.34 이상을 사용하는 x86_64 Linux와 Python 3.11~3.14에서 실행됩니다(pip install coreai-torch). quantize=16은 float16 입력을 받는 FP16 에셋을 생성하며, 이 에셋은 iOS 27 및 macOS 27에서 실행됩니다. 로드 시 중단되는 FP16 에셋에 대한 참고 사항을 포함한 Core AI 통합을 참조하세요.
TensorFlow SavedModel로 내보내기#
TF SavedModel 내보내기는 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 모델을 반환하며 출력 디렉터리에 FP32 및 FP16 LiteRT 파일(.tflite)도 생성합니다:
resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tflitequantize=8을 전달하면 여기에 INT8 .tflite을 추가할 수 있습니다.
TensorFlow 내보내기는 Python 3.13 이상을 사용하는 macOS에서 실행되지 않습니다. macOS에서는 Python 3.12 이하를 사용하거나 Linux를 사용하세요.
Python 3.12 이하에서의 요구 사항입니다(Python 3.13 이상에서는 내보내기에 대신 tensorflow>2.19.0, tf_keras>2.19.0,
onnx2tf>=2.3.0,<2.3.16, protobuf>=6.31.1,<7.0.0이 필요합니다):
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(다른 플랫폼에서는ai-edge-litert>=1.2.0) onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
TensorFlow Frozen Graph로 내보내기#
위의 SavedModel 내보내기에 이어, 반환된 keras_model을 frozen .pb 그래프로 변환합니다:
from pathlib import Path
from ultralytics.utils.export import keras2pb
keras2pb(keras_model, output_file=Path("resnet18_saved_model/resnet18.pb"))NCNN으로 내보내기#
from ultralytics.utils.export import torch2ncnn
torch2ncnn(model, im, output_dir="resnet18_ncnn_model")디렉터리에는 이름이 고정된 param 및 bin 파일과 Python 래퍼가 들어 있습니다:
resnet18_ncnn_model/
├── model.ncnn.param
├── model.ncnn.bin
└── model_ncnn.pyMNN으로 내보내기#
MNN 내보내기에는 입력으로 ONNX 파일이 필요합니다. 먼저 ONNX로 내보낸 다음 변환하세요:
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 양자화에는 quantize=8을 지원합니다. MNN>=2.9.6 및 torch>=1.10이 필요합니다.
PaddlePaddle로 내보내기#
from ultralytics.utils.export import torch2paddle
torch2paddle(model, im, output_dir="resnet18_paddle_model")디렉터리에는 PaddlePaddle 모델 및 파라미터 파일이 들어 있습니다:
resnet18_paddle_model/
├── inference_model/
│ ├── model.json
│ └── model.pdiparams
├── model.pdparams
└── x2paddle_code.pyx2paddle과 플랫폼에 맞는 PaddlePaddle 배포판이 필요합니다:
- 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로 내보내기#
from ultralytics.utils.export import torch2executorch
torch2executorch(model, im, output_dir="resnet18_executorch_model")내보낸 .pte 파일은 출력 디렉터리 안에 저장됩니다:
resnet18_executorch_model/
└── model.ptetorch>=2.9.0과 호환되는 ExecuTorch 런타임(pip install executorch)이 필요합니다. 런타임 사용법은 ExecuTorch 통합 가이드를 참조하세요.
내보낸 모델 검증하기#
내보낸 후 배포하기 전에 원본 PyTorch 모델과 수치적 일치성을 검증하세요. ultralytics.nn.backends에서 ONNXBackend을 사용하는 간단한 스모크 테스트로 출력을 비교하고 추적 또는 양자화 오류를 조기에 확인할 수 있습니다:
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 exportResNet-18에서 대부분의 FP32 내보내기는 PyTorch와 약 1e-5 이내의 차이를 보이며, TorchScript는 정확히 일치합니다. 다만 세 가지 런타임은 FP32 내보내기를 낮은 정밀도로 계산해 1e-2~1e-1 정도의 차이를 보일 수 있습니다. NCNN은 지원되는 CPU에서 FP16 연산을 활성화하고, MNNBackend은 precision="low"를 사용해 모델을 로드하며, 일부 하드웨어(예: Apple silicon)에서는 OpenVINO CPU 플러그인이 기본 PERFORMANCE 실행 모드에서 자동으로 FP16을 사용합니다. 형식 자체의 기준값보다 차이가 훨씬 크다면 지원되지 않는 연산, 잘못된 입력 형태 또는 평가 모드가 아닌 모델이 원인일 수 있습니다. FP16 및 INT8 내보내기에는 더 느슨한 허용 오차가 적용됩니다. 무작위 텐서 대신 실제 데이터로 검증하세요.
다른 런타임에서는 입력 텐서 이름이 다를 수 있습니다. 예를 들어 OpenVINO는 모델의 forward 인수 이름(일반 모델에서는 보통 x)을 사용하는 반면, torch2onnx의 기본값은 "images"입니다.
내보낸 모델 실행하기#
YOLO가 아닌 모델을 내보낸 파일은 일반 YOLO() API를 통해 다시 로드할 수 있습니다. 위에서 내보낸 파일에는 Ultralytics 작업 또는 입력 크기 메타데이터가 없으므로 task을 명시적으로 전달하고, 내보내기에 사용한 예시 텐서와 일치하는 imgsz도 전달해야 합니다:
from ultralytics import YOLO
results = YOLO("resnet18.onnx", task="classify")("path/to/image.jpg", imgsz=224)
print(results[0].probs.top1)내보내기에 고정 입력 형태가 있는 경우 imgsz 값이 중요합니다. 위의 ONNX 및 TF SavedModel 내보내기는 기본값 640을 거부합니다. 위의 TorchScript 및 NCNN 내보내기는 다른 크기도 허용하지만, 두 내보내기 방식 모두 이를 보장하지는 않습니다. 두 방식 모두 예시 텐서로 추적하므로, Linear 계층으로 펼치는 모델은 고정된 형태를 유지합니다. 직접 내보낸 모델을 확인하세요.
그런 다음 값은 모델 스트라이드의 배수로 올림 처리되며, 메타데이터가 없으면 스트라이드는 32입니다. 따라서 200x200의 고정 형태 내보내기에는 224x224가 입력되어 imgsz=200과 일치하더라도 거부됩니다. 32의 배수가 아닌 입력 크기를 사용하려면 백엔드를 직접 호출하세요.
백엔드 직접 호출하기#
Ultralytics 전처리 및 후처리를 거치지 않고 원시 텐서를 사용하려면, 위의 검증 예제와 같이 ultralytics.nn.backends에 있는 형식별 클래스를 사용하세요. 각 클래스는 내보낸 아티팩트와 디바이스를 입력으로 받고 호출할 수 있습니다:
| 형식 | 백엔드 | 입력 레이아웃 |
|---|---|---|
| TorchScript | TorchScriptBackend | BCHW |
| ONNX | ONNXBackend | BCHW |
| OpenVINO | OpenVINOBackend | BCHW |
| CoreML | CoreMLBackend | BHWC |
| Core AI | CoreAIBackend | BCHW |
| TF SavedModel, Frozen Graph | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
TensorFlowBackend은 두 가지 형식을 지원하며 기본값은 format="saved_model"이므로, frozen graph에는 format="pb"를 전달하세요.
YOLO() 경로가 처리하지만 직접 호출할 경우에는 처리하지 않는 세 가지 항목:
- 입력 레이아웃:
CoreMLBackend및TensorFlowBackend은 BHWC를 기대합니다. 먼저im.permute(0, 2, 3, 1)로 전치하세요. BCHW 텐서를 사용하면 형태 불일치 오류가 발생합니다. - Autograd: 호출을
torch.inference_mode()으로 감싸세요.TorchScriptBackend은 그래디언트 그래프가 여전히 연결된 텐서를 반환합니다. - 후처리: 메타데이터가 없으면 백엔드는
task을None로 남기고names는 비워 둡니다.LiteRTBackend은 3차원 출력이 YOLO 박스를 담고 있다고 가정하여 이미지 크기에 맞게 역정규화합니다. 이는 3차원 출력을 가진 YOLO가 아닌 모델에서는 잘못된 처리입니다. 분류기 로짓과 같은 2차원 출력에는 영향을 주지 않습니다.
알려진 제한 사항#
- 다중 입력 지원은 형식마다 다릅니다:
torch2onnx,torch2openvino,torch2torchscript는 입력이 여러 개인 모델에 대해 예제 텐서의 튜플을 받습니다.torch2coreml,torch2coreai,torch2ncnn,torch2paddle,torch2executorch은 단일 입력 텐서를 전제로 합니다. - YOLO 전용 형식: Axelera 및 Sony IMX500 내보내기는 YOLO 전용 모델 속성이 필요하므로 일반 모델에서는 사용할 수 없습니다.
- 플랫폼별 형식: TensorRT에는 NVIDIA GPU가 필요합니다. RKNN에는
rknn-toolkit2SDK가 필요합니다(Linux 전용). Edge TPU에는edgetpu_compiler바이너리가 필요합니다(Linux 전용).
결론#
이 유틸리티를 사용하면 일반 torch.nn.Module에서 배포 준비가 완료된 ONNX, OpenVINO, CoreML, TensorFlow 또는 모바일 런타임 아티팩트까지 일관된 단일 API로 모든 PyTorch 모델을 변환할 수 있습니다. 대상 하드웨어에 적합한 형식을 선택하고, 원본 모델과 수치적 일치성을 검증한 다음, 런타임별 배포 절차에 맞는 통합 가이드를 따르세요.
자주 묻는 질문#
모든
torch.nn.Module에 해당합니다. timm, torchvision 또는 사용자 지정 PyTorch 모델을 포함합니다. 내보내기 전에 모델을 평가 모드(model.eval())로 설정해야 합니다. ONNX, OpenVINO, TorchScript는 다중 입력 모델에 대해 예제 텐서의 튜플도 받습니다.지원되는 모든 형식(TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch)은 CPU에서 내보낼 수 있습니다. 내보내기 프로세스 자체에는 GPU가 필요하지 않습니다. NVIDIA GPU가 필요한 형식은 TensorRT뿐입니다.
최신 릴리스를 사용하세요.
quantize인수에는 Ultralytics>=8.4.81이 필요하며, Core AI 내보내기에는>=8.4.131가 필요합니다(Linux에서는 또는coreai-torch>=0.4.3와 함께>=8.4.163).예. torchvision 분류, 탐지 및 세그멘테이션 모델은
torch2coreml을 통해.mlpackage으로 내보낼 수 있습니다. 이미지 분류 모델의 경우,classifier_names에 클래스 이름 목록을 전달하여 분류 헤드를 포함하세요. macOS 또는 Linux에서 내보내기를 실행하세요. CoreML은 Windows에서 지원되지 않습니다. iOS 배포에 관한 자세한 내용은 CoreML 통합을 참조하세요.여러 형식에서 가능합니다. OpenVINO, CoreML 또는 MNN으로 내보낼 때 FP16에는
quantize=16, INT8에는quantize=8을 전달하세요. INT8 LiteRT 파일을 만들려면onnx2saved_model에quantize=8을 지정해야 합니다. NCNN과 Core AI는 기본적으로 FP32로 내보내며, FP16에는quantize=16를 지정할 수 있지만 INT8 경로는 지원하지 않습니다. OpenVINO에서 INT8을 사용하려면 학습 후 양자화를 위한calibration_dataset인수도 필요합니다. 양자화의 장단점은 각 형식의 통합 페이지를 참조하세요.동일한 입력으로 원본 PyTorch 모델과 내보낸 모델을 실행한 다음 출력을 비교하세요. 해당 백엔드로 내보낸 파일을 로드하고(예: ONNX의 경우
ONNXBackend) 최대 절대 차이를 확인하세요. 차이를 해당 형식 자체의 기준값과 비교해 판단하세요. NCNN,MNNBackend, 그리고 일부 CPU에서 실행되는 OpenVINO는 FP32 내보내기를 낮은 정밀도로 처리해1e-2~1e-1정도의 차이를 보일 수 있지만, 대부분의 다른 형식은1e-5정도의 차이를 보입니다. 차이가 훨씬 크다면 지원되지 않는 연산, 잘못된 입력 형태 또는 평가 모드가 아닌 모델이 원인일 수 있습니다. 실행 가능한 예제는 내보낸 모델 검증하기를 참조하세요.