Ultralytics YOLO27:

Cách xuất model PyTorch không phải YOLO bằng Ultralytics#

Ultralytics cung cấp các tiện ích export độc lập trong ultralytics.utils.export, giúp kết hợp nhiều backend thông qua một giao diện nhất quán. Bạn có thể export mọi torch.nn.Module, bao gồm model hình ảnh timm, model phân loại và phát hiện đối tượng torchvision hoặc kiến trúc tùy chỉnh của riêng bạn sang TorchScript, ONNX, OpenVINO, CoreML, Core AI, TensorFlow SavedModel, TensorFlow Frozen Graph, PaddlePaddle, MNN, NCNN và ExecuTorch mà không cần tìm hiểu riêng từng backend.

Việc đưa model PyTorch vào production thường đồng nghĩa với việc phải xử lý một exporter khác nhau cho từng đích: torch.onnx.export cho ONNX, coremltools cho thiết bị Apple, onnx2tf cho TensorFlow, pnnx cho NCNN, v.v. Mỗi công cụ có API, các vấn đề phụ thuộc và quy ước đầu ra riêng. Những tiện ích này gộp tất cả vào một cách gọi duy nhất.

Vì sao nên dùng Ultralytics để export model không phải YOLO?#

  • Một API cho 11 định dạng: chỉ cần học một quy ước gọi hàm thay vì cả tá quy ước.
  • Cùng luồng xử lý với export YOLO: các helper tương tự cũng được dùng cho mọi thao tác export YOLO của Ultralytics.
  • Lượng tử hóa FP16 và INT8 thông qua một đối số quantize duy nhất cho các định dạng hỗ trợ tính năng này.
  • Chạy trên CPU: không cần GPU cho riêng bước xuất model, vì vậy bạn có thể chạy cục bộ trên laptop; không hỗ trợ xuất CoreML trên Windows, còn xuất Core AI cần macOS 26 trở lên trên Apple silicon, hoặc Linux x86_64 với glibc 2.34 trở lên và Python 3.11 đến 3.14.

Bắt đầu nhanh#

Cách nhanh nhất là export sang [ONNX](https://ultralytics-translation-0.invalid chỉ với hai dòng lệnh, không cần code YOLO và không cần thiết lập gì ngoài pip install ultralytics onnx timm:

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")

Các định dạng export được hỗ trợ#

Các hàm torch2* nhận một torch.nn.Module chuẩn cùng tensor đầu vào mẫu. MNN, TF SavedModel và TF Frozen Graph sử dụng ONNX hoặc Keras làm artifact trung gian. Trong cả hai trường hợp, không cần thuộc tính dành riêng cho YOLO.

Định dạngHàmCài đặtĐầu ra
TorchScripttorch2torchscript()được tích hợp trong PyTorchtệp .torchscript
ONNXtorch2onnx()pip install onnxtệp .onnx
OpenVINOtorch2openvino()pip install openvinothư mục _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple silicon macOS 26 trở lên, Linux x86_64 glibc 2.34 trở lên; Python 3.11–3.14)thư mục .aimodel
TF SavedModelonnx2saved_model()xem yêu cầu chi tiết bên dướithư mục _saved_model/
TF Frozen Graphkeras2pb()xem yêu cầu chi tiết bên dướitệp .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddlethư mục _paddle_model/
MNNonnx2mnn()pip install MNNtệp .mnn
NCNNtorch2ncnn()pip install ncnn pnnxthư mục _ncnn_model/
ExecuTorchtorch2executorch()pip install executorchthư mục _executorch_model/
Nhúng metadata

Một số hàm export chấp nhận dictionary metadata tùy chọn (ví dụ: torch2torchscript(..., metadata={"author": "me"})) để nhúng các cặp key-value tùy chỉnh vào artifact đã export, nếu định dạng đó hỗ trợ.

Ví dụ từng bước#

Mọi ví dụ bên dưới đều dùng cùng một thiết lập: ResNet-18 pretrained từ timm ở chế độ đánh giá:

import timm
import torch

model = timm.create_model("resnet18", pretrained=True).eval()
im = torch.randn(1, 3, 224, 224)
Luôn gọi `model.eval()` trước khi export

Dropout, batch normalization và các layer chỉ dùng khi huấn luyện khác sẽ hoạt động khác trong quá trình inference. Bỏ qua .eval() sẽ tạo ra bản export có đầu ra không chính xác.

Xuất sang ONNX#

from ultralytics.utils.export import torch2onnx

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

Để dùng kích thước batch động, hãy truyền một dictionary dynamic:

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

Opset mặc định là 14 và tên input mặc định là "images". Ghi đè bằng các tham số opset, input_names hoặc output_names.

Export sang TorchScript#

Không cần cài thêm dependency. Bên dưới sử dụng torch.jit.trace.

from ultralytics.utils.export import torch2torchscript

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

Export sang OpenVINO#

from ultralytics.utils.export import torch2openvino

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

Thư mục chứa một cặp model.xml và model.bin có tên cố định:

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

Truyền dynamic=True để dùng shape input động, quantize=16 cho FP16 hoặc quantize=8 để lượng tử hóa INT8. Ngoài ra, INT8 cần tham số calibration_dataset.

Cần openvino>=2024.0.0 (hoặc >=2025.2.0 trên macOS 15.4 trở lên) và torch>=2.1.

Export sang 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")

Với model phân loại, hãy truyền danh sách tên lớp vào classifier_names để thêm classification head vào model CoreML.

Cần coremltools>=9.0, torch>=1.11 và numpy<=2.3.5. Không hỗ trợ trên Windows.

Lỗi `BlobWriter not loaded`

coremltools>=9.0 cung cấp wheel cho Python 3.10–3.13 trên macOS và Linux. Với các phiên bản Python mới hơn, phần mở rộng C gốc không tải được. Hãy dùng Python 3.10–3.13 để export CoreML.

Export sang Core AI#

from ultralytics.utils.export import torch2coreai

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

Artifact .aimodel là một thư mục:

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

Quá trình xuất model chạy trên macOS 26 trở lên trên Apple silicon, hoặc Linux x86_64 với glibc 2.34 trở lên và Python 3.11 đến 3.14 (pip install coreai-torch), còn quantize=16 ghi một asset FP16 nhận đầu vào float16; asset này chạy trên iOS 27 và macOS 27. Xem tích hợp Core AI, bao gồm lưu ý về các asset FP16 bị dừng khi tải.

Export sang TensorFlow SavedModel#

Quá trình export TF SavedModel sử dụng ONNX làm bước trung gian:

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")

Hàm trả về một model Keras và đồng thời tạo các tệp LiteRT FP32 và FP16 (.tflite) bên trong thư mục đầu ra:

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

Truyền quantize=8 để thêm một .tflite INT8 bên cạnh các tệp đó.

Không thể chạy export TensorFlow trên macOS với Python 3.13 trở lên; hãy dùng Python 3.12 trở xuống trên macOS hoặc dùng Linux.

Yêu cầu với Python 3.12 trở xuống (trên Python 3.13 trở lên, quá trình export cần tensorflow>2.19.0, tf_keras>2.19.0, onnx2tf>=2.3.0,<2.3.16 và protobuf>=6.31.1,<7.0.0):

  • 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
  • ai-edge-litert>=1.2.0,<1.4.0 trên macOS (ai-edge-litert>=1.2.0 trên các nền tảng khác)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=5

Export sang TensorFlow Frozen Graph#

Tiếp tục từ bước export SavedModel ở trên, chuyển đổi keras_model được trả về thành graph .pb đóng băng:

from pathlib import Path

from ultralytics.utils.export import keras2pb

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

Export sang NCNN#

from ultralytics.utils.export import torch2ncnn

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

Thư mục chứa các tệp param và bin có tên cố định cùng với một Python wrapper:

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

Export sang MNN#

Export MNN cần tệp ONNX làm đầu vào. Trước tiên, export sang ONNX rồi chuyển đổi:

from ultralytics.utils.export import onnx2mnn, torch2onnx

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

Hỗ trợ quantize=16 cho FP16 và quantize=8 để lượng tử hóa INT8. Cần MNN>=2.9.6 và torch>=1.10.

Export sang PaddlePaddle#

from ultralytics.utils.export import torch2paddle

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

Thư mục chứa model PaddlePaddle và các tệp tham số:

resnet18_paddle_model/
├── inference_model/
│   ├── model.json
│   └── model.pdiparams
├── model.pdparams
└── x2paddle_code.py

Cần x2paddle và bản phân phối PaddlePaddle phù hợp với nền tảng của bạn:

  • paddlepaddle-gpu>=3.0.0,<3.3.0 trên CUDA
  • paddlepaddle==3.0.0 trên CPU ARM64
  • paddlepaddle>=3.0.0,<3.3.0 trên các CPU khác

Không hỗ trợ trên NVIDIA Jetson.

Export sang ExecuTorch#

from ultralytics.utils.export import torch2executorch

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

Tệp .pte đã export được lưu trong thư mục đầu ra:

resnet18_executorch_model/
└── model.pte

Cần torch>=2.9.0 và runtime ExecuTorch tương ứng (pip install executorch). Để biết cách sử dụng runtime, xem tích hợp ExecuTorch.

Xác minh model đã export#

Sau khi export, hãy xác minh độ tương đương số học với model PyTorch gốc trước khi phát hành. Bài kiểm tra nhanh bằng ONNXBackend từ ultralytics.nn.backends sẽ so sánh đầu ra và sớm phát hiện lỗi tracing hoặc lượng tử hóa:

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
Sai số kỳ vọng

Với ResNet-18, hầu hết các bản xuất FP32 có sai số xấp xỉ 1e-5 so với PyTorch, còn TorchScript khớp chính xác. Tuy nhiên, ba runtime vẫn có thể tính toán bản xuất FP32 ở độ chính xác thấp hơn và có sai số gần 1e-2 đến 1e-1: NCNN bật phép tính FP16 trên CPU hỗ trợ tính năng này, MNNBackend tải model bằng precision="low", còn plugin CPU của OpenVINO tự động chạy ở FP16 theo chế độ thực thi PERFORMANCE mặc định trên một số phần cứng, chẳng hạn như Apple silicon. Sai lệch lớn hơn nhiều so với mức cơ sở của chính định dạng thường cho thấy có toán tử không được hỗ trợ, hình dạng đầu vào không đúng hoặc model chưa ở chế độ đánh giá. Các bản xuất FP16 và INT8 có dung sai rộng hơn. Hãy xác thực bằng dữ liệu thực thay vì tensor ngẫu nhiên.

Tên tensor đầu vào có thể khác nhau ở các runtime khác. Ví dụ, OpenVINO dùng tên tham số forward của model (thường là x với model tổng quát), trong khi torch2onnx mặc định là "images".

Chạy model đã export#

Có thể tải lại model không phải YOLO đã export thông qua API YOLO() thông thường. Các bản export ở trên không chứa metadata về task hoặc kích thước đầu vào của Ultralytics, vì vậy hãy truyền rõ task và imgsz khớp với tensor mẫu đã dùng để export:

from ultralytics import YOLO

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

imgsz rất quan trọng khi bản export có shape đầu vào cố định: các bản export ONNX và TF SavedModel ở trên từ chối giá trị mặc định 640. Các bản export TorchScript và NCNN ở trên chấp nhận kích thước khác, nhưng không exporter nào đảm bảo điều đó: cả hai đều tracing dựa trên tensor mẫu, vì vậy model flatten thành layer Linear sẽ giữ kích thước cố định. Hãy kiểm tra bản export của bạn.

Sau đó, giá trị được làm tròn lên bội số của stride model, bằng 32 khi không có metadata. Vì vậy, bản export có shape cố định 200x200 sẽ nhận đầu vào 224x224 và bị từ chối, dù imgsz=200 khớp với nó. Với kích thước đầu vào không phải bội số của 32, hãy gọi trực tiếp backend.

Gọi trực tiếp backend#

Để dùng tensor thô không qua tiền xử lý và hậu xử lý của Ultralytics, hãy dùng các class theo từng định dạng trong ultralytics.nn.backends, như trong ví dụ xác minh ở trên. Mỗi class nhận artifact đã export và một thiết bị, đồng thời có thể được gọi trực tiếp:

Định dạngBackendBố cục đầu vào
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel, Frozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

TensorFlowBackend hỗ trợ hai định dạng và mặc định dùng format="saved_model", vì vậy hãy truyền format="pb" cho frozen graph.

Ba việc mà luồng YOLO() xử lý giúp bạn nhưng khi gọi trực tiếp thì không:

  • Bố cục đầu vào: CoreMLBackend và TensorFlowBackend yêu cầu BHWC. Hãy chuyển vị trước bằng im.permute(0, 2, 3, 1); tensor BCHW sẽ gây lỗi không khớp shape.
  • Autograd: bọc lời gọi trong torch.inference_mode(). TorchScriptBackend trả về tensor vẫn chứa graph gradient.
  • Hậu xử lý: khi không có metadata, backend để task ở dạng None và names rỗng. LiteRTBackend vẫn giải chuẩn hóa mọi đầu ra 3-D theo kích thước ảnh, giả định rằng đầu ra chứa các box YOLO; điều này không đúng với model không phải YOLO có đầu ra 3-D. Các đầu ra 2-D như logits của model phân loại không bị ảnh hưởng.

Các giới hạn đã biết#

  • Khả năng hỗ trợ nhiều đầu vào không đồng đều: torch2onnx, torch2openvino và torch2torchscript chấp nhận tuple gồm các tensor mẫu cho model có nhiều đầu vào. torch2coreml, torch2coreai, torch2ncnn, torch2paddle và torch2executorch giả định chỉ có một tensor đầu vào.
  • Định dạng chỉ dành cho YOLO: thao tác export Axelera và Sony IMX500 yêu cầu thuộc tính dành riêng cho model YOLO và không khả dụng với model tổng quát.
  • Định dạng phụ thuộc nền tảng: TensorRT cần GPU NVIDIA. RKNN cần SDK rknn-toolkit2 (chỉ Linux). Edge TPU cần binary edgetpu_compiler (chỉ Linux).

Kết luận#

Các tiện ích này chuyển đổi mọi model PyTorch, từ torch.nn.Module thông thường sang artifact ONNX, OpenVINO, CoreML, TensorFlow hoặc runtime di động sẵn sàng triển khai thông qua một API nhất quán. Chọn định dạng phù hợp với phần cứng đích, xác minh độ tương đương số học với model gốc, sau đó làm theo hướng dẫn tích hợp tương ứng để triển khai theo runtime.

Câu hỏi thường gặp#

  • Bất kỳ torch.nn.Module nào. Bao gồm các model từ timm, torchvision hoặc bất kỳ model PyTorch tùy chỉnh nào. Model phải ở chế độ đánh giá (model.eval()) trước khi xuất. ONNX, OpenVINO và TorchScript cũng chấp nhận tuple gồm các tensor mẫu cho model nhiều đầu vào.

  • Mọi định dạng được hỗ trợ (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) đều có thể export trên CPU. Bản thân quá trình export không cần GPU. TensorRT là định dạng duy nhất yêu cầu GPU NVIDIA.

  • Hãy dùng bản phát hành mới nhất. Đối số quantize yêu cầu Ultralytics >=8.4.81, còn xuất Core AI cần >=8.4.131 (>=8.4.163 trên Linux hoặc với coreai-torch>=0.4.3).

  • Có. Các model phân loại, phát hiện và phân đoạn của torchvision có thể được xuất sang .mlpackage thông qua torch2coreml. Với các model phân loại ảnh, hãy truyền danh sách tên lớp vào classifier_names để tích hợp sẵn đầu phân loại. Thực hiện xuất trên macOS hoặc Linux. CoreML không được hỗ trợ trên Windows. Xem tích hợp CoreML để biết chi tiết triển khai trên iOS.

  • Có, với một số định dạng. Truyền quantize=16 cho FP16 hoặc quantize=8 cho INT8 khi xuất sang OpenVINO, CoreML hoặc MNN; onnx2saved_model nhận quantize=8 để tạo tệp LiteRT INT8, còn NCNN và Core AI xuất FP32 theo mặc định, nhận quantize=16 cho FP16 và không có quy trình INT8. INT8 trong OpenVINO cũng yêu cầu đối số calibration_dataset để thực hiện lượng tử hóa sau huấn luyện. Xem trang tích hợp của từng định dạng để biết các đánh đổi khi lượng tử hóa.

  • Chạy model PyTorch gốc và model đã xuất trên cùng một đầu vào, sau đó so sánh đầu ra. Tải tệp đã xuất bằng backend tương ứng (ví dụ: ONNXBackend cho ONNX) và kiểm tra sai khác tuyệt đối lớn nhất. Đánh giá sai lệch dựa trên mức cơ sở của chính định dạng: NCNN, MNNBackend và OpenVINO trên một số CPU có thể chạy bản xuất FP32 ở độ chính xác thấp hơn và có sai lệch gần 1e-2 đến 1e-1, trong khi hầu hết định dạng khác có sai lệch gần 1e-5. Sai lệch lớn hơn nhiều thường cho thấy có toán tử không được hỗ trợ, hình dạng đầu vào không đúng hoặc model chưa ở chế độ đánh giá. Xem Xác minh model đã xuất để biết ví dụ có thể chạy.

Bình luận