Ultralytics YOLO27:

كيفية تصدير نماذج 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](https://ultralytics-translation-0.invalid في سطرين، دون استخدام شيفرة 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.

التنسيقالدالةالتثبيتالمخرجات
TorchScripttorch2torchscript()مضمّنة مع PyTorchملف .torchscript
ONNXtorch2onnx()pip install onnxملف .onnx
OpenVINOtorch2openvino()pip install openvinoدليل _openvino_model/
CoreMLtorch2coreml()pip install coremltools.mlpackage
Core AItorch2coreai()pip install coreai-torch (Apple silicon macOS 26+، أو Linux بمعمارية x86_64 مع glibc 2.34+؛ Python 3.11-3.14)دليل .aimodel
TF SavedModelonnx2saved_model()راجع المتطلبات التفصيلية أدناهدليل _saved_model/
TF Frozen Graphkeras2pb()راجع المتطلبات التفصيلية أدناهملف .pb
PaddlePaddletorch2paddle()pip install paddlepaddle x2paddleدليل _paddle_model/
MNNonnx2mnn()pip install MNNملف .mnn
NCNNtorch2ncnn()pip install ncnn pnnxدليل _ncnn_model/
ExecuTorchtorch2executorch()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)
استدعِ `model.eval()` دائمًا قبل التصدير

يتصرف 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.

خطأ `BlobWriter not loaded`

توفر 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 أصلًا يستخدم مدخلات 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.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 على macOS (ai-edge-litert>=1.2.0 على الأنظمة الأساسية الأخرى)
  • onnxslim>=0.1.82
  • onnx>=1.12.0,<2.0.0
  • protobuf>=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 على CUDA
  • paddlepaddle==3.0.0 على CPU بمعمارية ARM64
  • paddlepaddle>=3.0.0,<3.3.0 على وحدات CPU الأخرى

غير مدعوم على 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-6 for an FP32 ONNX export
الفرق المتوقع

في ResNet-18، تقع معظم عمليات التصدير بصيغة FP32 ضمن نحو 1e-5 من PyTorch، وتطابق TorchScript النتائج تمامًا. ومع ذلك، يمكن لثلاثة محركات تشغيل حساب تصدير FP32 بدقة مخفّضة، وتحقيق نتائج قريبة من 1e-2 إلى 1e-1: يفعّل NCNN العمليات الحسابية FP16 على وحدات CPU التي تدعمها، وتحمل MNNBackend النماذج باستخدام precision="low"، بينما تعمل إضافة OpenVINO CPU تلقائيًا بدقة FP16 ضمن وضع التنفيذ الافتراضي PERFORMANCE على بعض الأجهزة، مثل Apple silicon. يشير اختلاف أكبر بكثير من خط الأساس الخاص بالتنسيق إلى عمليات غير مدعومة، أو شكل إدخال غير صحيح، أو أن النموذج ليس في وضع التقييم. وتسمح عمليات التصدير FP16 وINT8 بهوامش تفاوت أوسع. تحقّق باستخدام بيانات حقيقية بدلًا من الموترات العشوائية.

قد يختلف اسم موتر الإدخال في بيئات التشغيل الأخرى. فعلى سبيل المثال، يستخدم OpenVINO اسم وسيطة forward الخاصة بالنموذج (عادةً x للنماذج العامة)، بينما تكون القيمة الافتراضية لـ torch2onnx هي "images".

تشغيل النموذج المُصدَّر#

يمكن تحميل النماذج غير المعتمدة على YOLO التي تم تصديرها باستخدام واجهة API المعتادة 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 عند عدم وجود بيانات وصفية. لذلك، يُمرَّر حجم 224x224 إلى نموذج مُصدَّر ذي شكل ثابت بحجم 200x200، فيُرفض رغم تطابق imgsz=200 معه. للأحجام التي لا تقبل القسمة على 32، استدعِ الواجهة الخلفية مباشرةً.

الاستدعاء المباشر لواجهة خلفية#

للتعامل مع الموترات الخام دون [المعالجة المسبقة](https://ultralytics-translation-0.invalid والمعالجة اللاحقة من Ultralytics، استخدم الفئات الخاصة بكل تنسيق في ultralytics.nn.backends، كما في مثال التحقق أعلاه. تأخذ كل فئة الملف المُصدَّر والجهاز، ويمكن استدعاؤها مباشرةً:

التنسيقالواجهة الخلفيةتخطيط الإدخال
TorchScriptTorchScriptBackendBCHW
ONNXONNXBackendBCHW
OpenVINOOpenVINOBackendBCHW
CoreMLCoreMLBackendBHWC
Core AICoreAIBackendBCHW
TF SavedModel وFrozen GraphTensorFlowBackendBHWC
LiteRTLiteRTBackendBCHW
PaddlePaddlePaddleBackendBCHW
MNNMNNBackendBCHW
NCNNNCNNBackendBCHW
ExecuTorchExecuTorchBackendBCHW

يتعامل 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 وحدة GPU من NVIDIA. ويتطلب RKNN حزمة SDK الخاصة بـ rknn-toolkit2 (على Linux فقط). كما يتطلب Edge TPU الملف التنفيذي edgetpu_compiler (على Linux فقط).

الخلاصة#

تحوّل هذه الأدوات أي نموذج 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) باستخدام CPU. لا يلزم وجود GPU لتنفيذ عملية التصدير نفسها. أما TensorRT فهو التنسيق الوحيد الذي يتطلب وحدة GPU من 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 على بعض وحدات CPU على تشغيل عمليات التصدير FP32 بدقة مخفّضة، وتحقق نتائج قريبة من 1e-2 إلى 1e-1، بينما تحقق معظم التنسيقات الأخرى نتائج قريبة من 1e-5. يشير فرق أكبر بكثير إلى عمليات غير مدعومة، أو شكل إدخال غير صحيح، أو أن النموذج ليس في وضع التقييم. راجع التحقق من النموذج المُصدّر للاطلاع على مثال قابل للتشغيل.

التعليقات