كيفية تصدير نماذج PyTorch غير التابعة لـ YOLO باستخدام Ultralytics#
توفر Ultralytics أدوات مساعدة مستقلة للتصدير ضمن ultralytics.utils.export، وتغلّف عدة واجهات خلفية ضمن واجهة متسقة واحدة. يمكنك تصدير أي torch.nn.Module، بما في ذلك نماذج الصور من timm، ومصنفات وكواشف torchvision، أو البنى المخصصة الخاصة بك، إلى TorchScript، وONNX، وOpenVINO، وCoreML، وCore AI، وTensorFlow SavedModel، وTensorFlow Frozen Graph، وPaddlePaddle، وMNN، وNCNN، وExecuTorch، من دون الحاجة إلى تعلّم كل واجهة خلفية على حدة.
يتطلب نشر نماذج PyTorch في بيئة الإنتاج عادةً التعامل مع أداة تصدير مختلفة لكل وجهة: torch.onnx.export لـ ONNX، وcoremltools لأجهزة Apple، وonnx2tf لـ TensorFlow، وpnnx لـ NCNN، وغيرها. ولكل أداة واجهة API خاصة بها، وتبعيات قد تتسبب في مشكلات، واصطلاحات إخراج مختلفة. وتوحّد هذه الأدوات كل ذلك ضمن نمط استدعاء واحد.
لماذا تستخدم Ultralytics لتصدير النماذج غير التابعة لـ YOLO؟#
- واجهة API واحدة عبر 11 تنسيقًا: تعلّم صيغة استدعاء واحدة بدلًا من اثنتي عشرة صيغة.
- مسار الشيفرة نفسه لتصدير YOLO: تستخدم جميع عمليات تصدير Ultralytics YOLO الأدوات المساعدة نفسها.
- تكميم FP16 وINT8 عبر وسيطة واحدة هي
quantizeللتنسيقات التي تدعم ذلك. - يعمل على CPU: لا حاجة إلى GPU لتنفيذ خطوة التصدير نفسها، لذا يمكنك تشغيلها محليًا على حاسوب محمول؛ ولا يُدعم تصدير CoreML على Windows، ويتطلب تصدير Core AI نظام macOS 26 أو أحدث على أجهزة Apple silicon، أو Linux بمعمارية x86_64 وglibc 2.34 أو أحدث، مع Python من الإصدار 3.11 إلى 3.14.
البدء السريع#
أسرع طريقة هي التصدير إلى ONNX بسطرين، من دون شيفرة YOLO أو إعدادات تتجاوز 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")تنسيقات التصدير المدعومة#
تستقبل الدوال 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 أو أحدث، وLinux بمعمارية x86_64 و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"})) لتضمين أزواج مفاتيح وقيم مخصصة في العنصر المُصدّر، متى كان التنسيق يدعم ذلك.
أمثلة خطوة بخطوة#
تستخدم جميع الأمثلة أدناه الإعداد نفسه: ResNet-18 مُدرّب مسبقًا من timm وفي وضع التقييم:
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 لأشكال الإدخال الديناميكية، أو quantize=16 لـ FP16، أو quantize=8 لتكميم INT8. ويتطلب INT8 أيضًا الوسيطة calibration_dataset.
يتطلب openvino>=2024.0.0 (أو >=2025.2.0 على macOS 15.4 أو أحدث) و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 حزم wheels للإصدارات من Python 3.10 إلى 3.13 على macOS وLinux. وفي إصدارات Python الأحدث، يفشل تحميل امتداد C الأصلي. استخدم Python من الإصدار 3.10 إلى 3.13 لتصدير CoreML.
التصدير إلى Core AI#
from ultralytics.utils.export import torch2coreai
torch2coreai(model, im, output_file="resnet18.aimodel")عنصر .aimodel عبارة عن دليل:
resnet18.aimodel/
├── main.mlirb
├── main.hash
└── metadata.jsonيُجرى التصدير على macOS 26 أو أحدث على أجهزة Apple silicon، أو على Linux بمعمارية x86_64 وglibc 2.34 أو أحدث، باستخدام Python من الإصدار 3.11 إلى 3.14 (pip install coreai-torch)، وتكتب quantize=16 عنصرًا بصيغة FP16 يستقبل مدخلات float16؛ ويعمل العنصر على iOS 27 وmacOS 27. راجع تكامل Core AI، بما في ذلك الملاحظة بشأن عناصر FP16 التي تتوقف عند التحميل.
التصدير إلى 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، وتُنشئ أيضًا ملفات LiteRT بصيغتي FP32 وFP16 (.tflite) داخل دليل الإخراج:
resnet18_saved_model/
├── saved_model.pb
├── variables/
├── assets/
├── fingerprint.pb
├── resnet18_float32.tflite
└── resnet18_float16.tfliteمرّر quantize=8 لإضافة عنصر .tflite بصيغة INT8 إلى جانبها.
لا يعمل تصدير TensorFlow على macOS مع Python 3.13 أو أحدث؛ استخدم Python 3.12 أو إصدارًا أقدم على macOS، أو استخدم 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.26ai-edge-litert>=1.2.0,<1.4.0على macOS (ai-edge-litert>=1.2.0على الأنظمة الأساسية الأخرى)onnxslim>=0.1.82onnx>=1.12.0,<2.0.0protobuf>=5
التصدير إلى TensorFlow Frozen Graph#
استكمالًا لعملية تصدير SavedModel أعلاه، حوّل keras_model المُعاد إلى رسم .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.pyالتصدير إلى MNN#
يتطلب تصدير MNN ملف ONNX كمدخل. صدّر النموذج إلى ONNX أولًا، ثم حوّله:
from ultralytics.utils.export import onnx2mnn, torch2onnx
torch2onnx(model, im, output_file="resnet18.onnx")
onnx2mnn("resnet18.onnx", output_file="resnet18.mnn")يدعم quantize=16 للتكميم FP16 وquantize=8 للتكميم INT8. يتطلب 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.pyيتطلب x2paddle وتوزيعة PaddlePaddle المناسبة لمنصتك:
paddlepaddle-gpu>=3.0.0,<3.3.0على CUDApaddlepaddle==3.0.0على وحدة المعالجة المركزية ARM64paddlepaddle>=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.pteيتطلب torch>=2.9.0 وبيئة تشغيل ExecuTorch مطابقة (pip install executorch). لاستخدام بيئة التشغيل، راجع تكامل ExecuTorch.
التحقق من النموذج المُصدَّر#
بعد التصدير، تحقّق من التطابق العددي مع نموذج PyTorch الأصلي قبل النشر. يقارن اختبار دخان سريع باستخدام ONNXBackend من ultralytics.nn.backends المخرجات، ويكشف مبكرًا أخطاء التتبّع أو التكميم:
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 exportفي ResNet-18، تقع معظم عمليات التصدير FP32 ضمن نحو 1e-5 من PyTorch، ويطابق TorchScript النتائج تمامًا. مع ذلك، قد تحسب ثلاث بيئات تشغيل تصدير FP32 بدقة مخفّضة، فتكون النتائج قريبة من 1e-2 إلى 1e-1: يفعّل NCNN حساب FP16 على وحدات المعالجة المركزية التي تدعمه، وتحمل MNNBackend النماذج باستخدام precision="low"، بينما تعمل إضافة OpenVINO لوحدة المعالجة المركزية تلقائيًا بدقة FP16 في وضع التنفيذ الافتراضي PERFORMANCE على بعض الأجهزة، مثل شرائح Apple. يشير الفرق الأكبر بكثير من خط الأساس الخاص بالتنسيق إلى عمليات غير مدعومة، أو شكل إدخال خاطئ، أو أن النموذج ليس في وضع التقييم. تتطلب عمليات التصدير FP16 وINT8 هوامش تسامح أوسع. تحقّق باستخدام بيانات حقيقية بدلًا من الموترات العشوائية.
قد يختلف اسم موتر الإدخال في بيئات التشغيل الأخرى. يستخدم OpenVINO، مثلًا، اسم وسيط التمرير الأمامي للنموذج (عادةً x للنماذج العامة)، بينما تكون القيمة الافتراضية لـtorch2onnx هي "images".
تشغيل النموذج المُصدَّر#
يمكن تحميل النماذج غير التابعة إلى YOLO التي تم تصديرها مجددًا عبر واجهة YOLO() المعتادة. لا تتضمن عمليات التصدير أعلاه بيانات وصفية لمهام 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، الرسم البياني المجمّد | TensorFlowBackend | BHWC |
| LiteRT | LiteRTBackend | BCHW |
| PaddlePaddle | PaddleBackend | BCHW |
| MNN | MNNBackend | BCHW |
| NCNN | NCNNBackend | BCHW |
| ExecuTorch | ExecuTorchBackend | BCHW |
يدعم TensorFlowBackend تنسيقين، ويكون الإعداد الافتراضي format="saved_model"؛ لذا مرّر format="pb" للرسم البياني المجمّد.
هناك ثلاثة أمور تتولاها آلية YOLO() ولا يتولاها الاستدعاء المباشر:
- تخطيط الإدخال: يتوقع كل من
CoreMLBackendوTensorFlowBackendتخطيط BHWC. أعد ترتيب المحاور أولًا باستخدامim.permute(0, 2, 3, 1)؛ إذ يؤدي موتر BCHW إلى خطأ عدم تطابق الشكل. - التدرّج التلقائي: أحط الاستدعاءات بـ
torch.inference_mode(). يعيدTorchScriptBackendموترًا لا يزال يحمل رسمًا بيانيًا لـالتدرّج. - المعالجة اللاحقة: في غياب البيانات الوصفية، تترك الواجهة الخلفية
taskعلى هيئةNoneوتتركnamesفارغًا. مع ذلك، تزيلLiteRTBackendالتطبيع من أي مخرج ثلاثي الأبعاد باستخدام حجم الصورة، على افتراض أنه يحتوي على مربعات YOLO؛ وهذا غير صحيح لنموذج غير تابع إلى YOLO ذي مخرج ثلاثي الأبعاد. لا تتأثر المخرجات ثنائية الأبعاد، مثل قيم الإسناد اللوجستي للمُصنِّف.
القيود المعروفة#
- دعم المدخلات المتعددة متفاوت: تقبل
torch2onnxوtorch2openvinoوtorch2torchscriptصفًا من موترات الأمثلة للنماذج متعددة المدخلات. أماtorch2coremlوtorch2coreaiوtorch2ncnnوtorch2paddleوtorch2executorch، فتفترض وجود موتر إدخال واحد. - تنسيقات خاصة بـYOLO: يتطلب التصدير إلى Axelera وSony IMX500 سمات خاصة بنماذج YOLO، ولا يتوفر للنماذج العامة.
- تنسيقات خاصة بمنصات محددة: يتطلب TensorRT وحدة معالجة رسومات NVIDIA. ويتطلب RKNN حزمة SDK
rknn-toolkit2(لنظام Linux فقط). ويتطلب Edge TPU الملف التنفيذيedgetpu_compiler(لنظام Linux بمعمارية x86_64 فقط).
الخلاصة#
تنقل هذه الأدوات أي نموذج PyTorch، بدءًا من torch.nn.Module البسيط وصولًا إلى ملف جاهز للنشر بتنسيق ONNX أو OpenVINO أو CoreML أو TensorFlow أو بيئة تشغيل للأجهزة المحمولة، عبر واجهة API موحّدة. اختر التنسيق الملائم لأجهزتك المستهدفة، وتحقّق من التطابق العددي مع النموذج الأصلي، ثم اتبع دليل التكامل المطابق لخطوات النشر الخاصة ببيئة التشغيل.
الأسئلة الشائعة#
أي
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) على وحدة المعالجة المركزية. لا تحتاج عملية التصدير نفسها إلى وحدة معالجة رسومات. TensorRT هو التنسيق الوحيد الذي يتطلب وحدة معالجة رسومات NVIDIA.
استخدم أحدث إصدار. تتطلب الوسيطة
quantizeإصدار Ultralytics>=8.4.81، ويتطلب التصدير إلى Core AI الإصدار>=8.4.131(>=8.4.163على Linux أو معcoreai-torch>=0.4.3).نعم. يمكن تصدير نماذج torchvision للتصنيف والكشف والتقسيم إلى
.mlpackageعبرtorch2coreml. لنماذج تصنيف الصور، مرّر قائمة بأسماء الفئات إلىclassifier_namesلتضمين رأس تصنيف. نفّذ التصدير على macOS أو Linux. لا يُدعم CoreML على Windows. راجع تكامل CoreML لمعرفة تفاصيل النشر على iOS.نعم، لعدة تنسيقات. مرّر
quantize=16لـFP16 أوquantize=8لـINT8 عند التصدير إلى OpenVINO أو CoreML أو MNN؛ ويقبلonnx2saved_modelالقيمةquantize=8لملف LiteRT بتنسيق INT8. ويُصدّر كل من NCNN وCore AI بتنسيق FP32 افتراضيًا، ويقبلانquantize=16لـFP16، ولا يدعمان مسارًا لـINT8. يتطلب INT8 في OpenVINO أيضًا وسيطةcalibration_datasetلإجراء التكميم بعد التدريب. راجع صفحة التكامل الخاصة بكل تنسيق للاطلاع على المفاضلات المرتبطة بالتكميم.شغّل نموذج PyTorch الأصلي والنموذج المُصدَّر على الإدخال نفسه، ثم قارن المخرجات. حمّل الملف المُصدَّر باستخدام الواجهة الخلفية المطابقة (مثل
ONNXBackendلـONNX) وافحص أكبر فرق مطلق. قيّم الفارق مقارنةً بخط الأساس الخاص بالتنسيق: إذ يمكن لـNCNN وMNNBackendوOpenVINO على بعض وحدات المعالجة المركزية تشغيل عمليات التصدير FP32 بدقة مخفّضة، فتكون النتائج قريبة من1e-2إلى1e-1، بينما تكون نتائج معظم التنسيقات الأخرى قريبة من1e-5. يشير الفارق الأكبر بكثير إلى عمليات غير مدعومة، أو شكل إدخال خاطئ، أو أن النموذج ليس في وضع التقييم. راجع التحقق من النموذج المُصدَّر للاطلاع على مثال قابل للتشغيل.