Cách xuất model PyTorch không thuộc YOLO bằng Ultralytics#
Ultralytics cung cấp các tiện ích xuất độc lập trong ultralytics.utils.export, đóng gói nhiều backend sau một giao diện nhất quán. Bạn có thể xuất mọi torch.nn.Module, bao gồm model ảnh timm, bộ phân loại và bộ 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.
Triển khai model PyTorch vào môi trường production thường đồng nghĩa với việc phải dùng 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. Các tiện ích này gộp tất cả thành một mẫu gọi duy nhất.
Vì sao nên dùng Ultralytics để xuất model không thuộc YOLO?#
- Một API dùng cho 11 định dạng: chỉ cần học một quy ước gọi thay vì hàng tá quy ước.
- Cùng luồng mã với thao tác xuất YOLO: các helper giống nhau hỗ trợ mọi thao tác xuất YOLO của Ultralytics.
- Lượng tử hóa FP16 và INT8 thông qua một đối số
quantizeduy nhất cho các định dạng hỗ trợ. - Chạy trên CPU: bản thân bước xuất không cần GPU, nên bạn có thể chạy cục bộ trên laptop; xuất CoreML không được hỗ trợ 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à xuất sang ONNX chỉ với hai dòng, không cần mã 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 tiêu chuẩn và tensor đầu vào mẫu. MNN, TF SavedModel và TF Frozen Graph sử dụng ONNX hoặc hiện vật Keras trung gian. Không cần thuộc tính dành riêng cho YOLO trong cả hai trường hợp.
| Định dạng | Hàm | Cài đặt | Đầu ra |
|---|---|---|---|
| TorchScript | torch2torchscript() | được tích hợp trong PyTorch | tệp .torchscript |
| ONNX | torch2onnx() | pip install onnx | tệp .onnx |
| OpenVINO | torch2openvino() | pip install openvino | thư mục _openvino_model/ |
| CoreML | torch2coreml() | pip install coremltools | .mlpackage |
| Core AI | torch2coreai() | pip install coreai-torch (Apple silicon macOS 26+, Linux x86_64 glibc 2.34+; Python 3.11-3.14) | thư mục .aimodel |
| TF SavedModel | onnx2saved_model() | xem yêu cầu chi tiết bên dưới | thư mục _saved_model/ |
| TF Frozen Graph | keras2pb() | xem yêu cầu chi tiết bên dưới | tệp .pb |
| PaddlePaddle | torch2paddle() | pip install paddlepaddle x2paddle | thư mục _paddle_model/ |
| MNN | onnx2mnn() | pip install MNN | tệp .mnn |
| NCNN | torch2ncnn() | pip install ncnn pnnx | thư mục _ncnn_model/ |
| ExecuTorch | torch2executorch() | pip install executorch | thư mục _executorch_model/ |
Một số hàm xuất chấp nhận từ điển metadata tùy chọn (ví dụ: torch2torchscript(..., metadata={"author": "me"})) để nhúng các cặp khóa-giá trị tùy chỉnh vào hiện vật đã xuất, 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)Dropout, batch normalization và các layer chỉ dùng khi huấn luyện khác hoạt động khác nhau trong quá trình suy luận. Bỏ qua .eval() sẽ tạo ra các bản xuất 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 từ điển dynamic:
torch2onnx(model, im, output_file="resnet18_dyn.onnx", dynamic={"images": {0: "batch_size"}})Opset mặc định là 14 và tên đầu vào mặc định là "images". Ghi đè các giá trị này bằng các đối số opset, input_names hoặc output_names.
Xuất sang TorchScript#
Không cần 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")Xuất 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.binTruyền dynamic=True để dùng shape đầu vào động, quantize=16 cho FP16 hoặc quantize=8 để lượng tử hóa INT8. Ngoài ra, INT8 cần đối 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.
Xuất 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 được hỗ trợ trên Windows.
coremltools>=9.0 cung cấp wheel cho Python 3.10–3.13 trên macOS và Linux. Trên các phiên bản Python mới hơn, C extension gốc không tải được. Hãy dùng Python 3.10–3.13 để xuất CoreML.
Xuất sang Core AI#
from ultralytics.utils.export import torch2coreai
torch2coreai(model, im, output_file="resnet18.aimodel")Tệp .aimodel là một thư mục:
resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.jsonQuá trình xuất chạy trên macOS 26 trở lên với Apple silicon hoặc Linux x86_64 có glibc 2.34 trở lên, cùng Python 3.11 đến 3.14 (pip install coreai-torch); quantize=16 ghi một hiện vật FP16 nhận đầu vào float16; hiện vật 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 hiện vật FP16 bị dừng khi tải.
Xuất sang TensorFlow SavedModel#
Quá trình xuất 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, đồ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.tfliteTruyền quantize=8 để thêm một .tflite INT8 bên cạnh các tệp đó.
Không thể chạy xuất 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, thao tác xuất 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 thay thế):
tensorflow>=2.0.0,<=2.19.0onnx2tf>=1.26.3,<1.29.0tf_keras<=2.19.0sng4onnx>=1.0.1onnx_graphsurgeon>=0.3.26ai-edge-litert>=1.2.0,<1.4.0trên macOS (ai-edge-litert>=1.2.0trên các nền tảng khác)onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
Xuất sang TensorFlow Frozen Graph#
Tiếp tục từ thao tác xuất SavedModel ở trên, chuyển đổi keras_model được trả về thành đồ thị .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"))Xuất 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.pyXuất sang MNN#
Để xuất MNN, đầu vào phải là tệp ONNX. Trước tiên hãy xuất sang ONNX, sau đó 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 lượng tử hóa FP16 và quantize=8 cho lượng tử hóa INT8. Yêu cầu MNN>=2.9.6 và torch>=1.10.
Xuất 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.pyYêu cầu 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.0trên CUDApaddlepaddle==3.0.0trên CPU ARM64paddlepaddle>=3.0.0,<3.3.0trên các CPU khác
Không được hỗ trợ trên NVIDIA Jetson.
Xuất sang ExecuTorch#
from ultralytics.utils.export import torch2executorch
torch2executorch(model, im, output_dir="resnet18_executorch_model")Tệp .pte đã xuất được lưu trong thư mục đầu ra:
resnet18_executorch_model/
└── model.pteYêu cầu torch>=2.9.0 và runtime ExecuTorch tương thích (pip install executorch). Để biết cách sử dụng runtime, hãy xem tích hợp ExecuTorch.
Xác minh model đã xuất#
Sau khi xuất, hãy xác minh độ tương đương số học với model PyTorch gốc trước khi triển khai. Chạy kiểm tra nhanh bằng ONNXBackend từ ultralytics.nn.backends để 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-5 for an FP32 ONNX exportVới ResNet-18, hầu hết bản xuất FP32 có sai khác khoảng 1e-5 so với PyTorch, còn TorchScript khớp chính xác. Tuy vậy, 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 khác gần 1e-2 đến 1e-1: NCNN bật phép tính FP16 trên các CPU hỗ trợ, MNNBackend tải model bằng precision="low", còn plugin CPU OpenVINO tự động chạy FP16 ở chế độ thực thi mặc định PERFORMANCE trên một số phần cứng, chẳng hạn như chip Apple. Sai khác cao hơn nhiều so với mức cơ sở riêng của định dạng thường cho thấy có toán tử không được hỗ trợ, hình dạng đầu vào sai hoặc model chưa ở chế độ eval. 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.
Với các runtime khác, tên tensor đầu vào có thể khác. Chẳng hạn, OpenVINO sử dụng tên đối số forward của model (thường là x với các model thông dụng), trong khi torch2onnx mặc định dùng "images".
Chạy model đã xuất#
Các model không phải YOLO đã xuất có thể được tải lại thông qua API YOLO() thông thường. Các bản xuất ở trên không có metadata về tác vụ 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 ví dụ đã dùng khi xuất:
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 xuất có hình dạng đầu vào cố định: các bản xuất ONNX và TF SavedModel ở trên từ chối giá trị mặc định 640. Các bản xuất 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 từ tensor ví dụ, nên model được làm phẳng thành lớp Linear sẽ giữ kích thước cố định. Hãy kiểm tra bản xuất của bạn.
Sau đó, giá trị được làm tròn lên thành bội số của stride model, mặc định là 32 khi không có metadata. Do đó, bản xuất có hình dạng cố định 200x200 sẽ nhận đầu vào 224x224 và bị từ chối, dù imgsz=200 khớp với kích thước đó. 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#
Để làm việc với tensor thô không qua tiền xử lý và hậu xử lý của Ultralytics, hãy dùng các lớp theo từng định dạng trong ultralytics.nn.backends, như ví dụ xác minh ở trên. Mỗi lớp nhận artifact đã xuất cùng thiết bị và có thể được gọi trực tiếp:
| Định dạng | Backend | Bố cục đầu vào |
|---|---|---|
| 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 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à quy trình YOLO() xử lý thay bạn nhưng lệnh gọi trực tiếp thì không:
- Bố cục đầu vào:
CoreMLBackendvàTensorFlowBackendyêu cầu BHWC. Trước tiên, hãy chuyển vị bằngim.permute(0, 2, 3, 1); tensor BCHW sẽ gây lỗi không khớp hình dạng. - Autograd: bọc các lệnh gọi trong
torch.inference_mode().TorchScriptBackendtrả về tensor vẫn mang đồ thị gradient. - Hậu xử lý: khi không có metadata, backend để
taskở dạngNonevànamesở trạng thái rỗng.LiteRTBackendvẫn giải chuẩn hóa mọi đầu ra 3-D theo kích thước ảnh, giả định đầu ra chứa các hộp YOLO; điều này không đúng với model không phải YOLO có đầu ra 3-D. Đầu ra hai chiều như logits phân loại không bị ảnh hưởng.
Các giới hạn đã biết#
- Hỗ trợ nhiều đầu vào không đồng đều:
torch2onnx,torch2openvinovàtorch2torchscriptchấp nhận tuple gồm các tensor ví dụ cho model có nhiều đầu vào.torch2coreml,torch2coreai,torch2ncnn,torch2paddlevàtorch2executorchgiả định chỉ có một tensor đầu vào. - Định dạng chỉ dành cho YOLO: các bản xuất Axelera và Sony IMX500 yêu cầu các thuộc tính model dành riêng cho YOLO và không khả dụng với model thông dụng.
- Định dạng phụ thuộc nền tảng: TensorRT yêu cầu GPU NVIDIA. RKNN yêu cầu SDK
rknn-toolkit2(chỉ Linux). Edge TPU yêu cầu tệp nhị phânedgetpu_compiler(chỉ Linux x86_64).
Kết luận#
Các tiện ích này chuyển đổi mọi model PyTorch, từ torch.nn.Module đơn giản đến 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 từng runtime.
Câu hỏi thường gặp#
Mọi
torch.nn.Module. Bao gồm 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 tensor ví dụ cho model có nhiều đầu vào.Tất cả định dạng được hỗ trợ (TorchScript, ONNX, OpenVINO, CoreML, Core AI, TF SavedModel, TF Frozen Graph, PaddlePaddle, MNN, NCNN, ExecuTorch) đều có thể xuất trên CPU. Bản thân quy trình xuất không yêu cầu GPU. TensorRT là định dạng duy nhất cần GPU NVIDIA.
Hãy dùng bản phát hành mới nhất. Đối số
quantizeyêu cầu Ultralytics>=8.4.81, còn xuất Core AI cần>=8.4.131(>=8.4.163trên Linux hoặc khi dùngcoreai-torch>=0.4.3).Có. Bộ phân loại, detector và model phân đoạn của torchvision có thể được xuất sang
.mlpackagethông quatorch2coreml. Với model phân loại ảnh, hãy truyền danh sách tên lớp vàoclassifier_namesđể tích hợp sẵn đầu phân loại. Chạy quá trình 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=16cho FP16 hoặcquantize=8cho INT8 khi xuất sang OpenVINO, CoreML hoặc MNN;onnx2saved_modelnhậnquantize=8để tạo tệp LiteRT INT8, còn NCNN và Core AI mặc định xuất FP32, nhậnquantize=16cho FP16 và không có quy trình INT8. INT8 trong OpenVINO còn yêu cầu đối sốcalibration_datasetđể 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 với cùng một đầu vào, sau đó so sánh đầu ra. Tải tệp đã xuất bằng backend phù hợp (ví dụ,
ONNXBackendcho ONNX) và kiểm tra sai khác tuyệt đối lớn nhất. Đánh giá sai khác so với mức cơ sở riêng của định dạng: NCNN,MNNBackendvà 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ới sai khác gần1e-2đến1e-1, trong khi hầu hết định dạng khác có sai khác gần1e-5. Sai khác 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 sai hoặc model chưa ở chế độ eval. Xem Xác minh model đã xuất để biết ví dụ có thể chạy.