تطبيق عملي لنشر نماذج الذكاء الاصطناعي في أنظمة الرافعات: من PyTorch إلى ONNX/TensorRT وصولاً إلى الاستدلال الطرفي على Jetson
📌 حل نشر نموذج الذكاء الاصطناعي «Tianche» على الحافة
سلسلة النشر الكاملة: PyTorch ResNet-18 (FP32، 45 ميغابايت، استدلال على وحدة معالجة الرسومات (GPU) في 5 مللي ثانية) → تصدير إلى ONNX (44 ميغابايت، تنسيق وسيط متعدد المنصات، خسارة في الدقة <0.11 TP3T) → تكمية TensorRT INT8 (6 ميغابايت، حجم ↓87%، خسارة في الدقة <0.5%) → استدلال حافة الشبكة على Jetson Orin NX (1.2 مللي ثانية/صورة، استهلاك طاقة 15 واط). بالمقارنة مع الاستدلال على خادم GPU (350 واط/5 مللي ثانية)، انخفض استهلاك الطاقة في Jetson بمقدار 961 TP3T، وزادت سرعة الاستدلال بمقدار 4.2 أضعاف. تقدم هذه الورقة النص البرمجي الكامل للتصدير، وطريقة معايرة INT8، وعملية التحقق من الدقة، وبنية خط الإنتاج المتعدد النماذج.
مهما كان أداء نموذج الذكاء الاصطناعي «Tianche» جيدًا على وحدات معالجة الرسومات (GPU) في المختبر، فإن عدم نشره في الموقع الفعلي يعني أنه لا قيمة له. فالظروف البيئية في المواقع الصناعية (الاهتزازات، ودرجات الحرارة المرتفعة، والمساحات المحدودة، وغياب التكييف) لا تسمح بنشر خوادم GPU عالية الطاقة (350 واط أو أكثر، والتي تتطلب تكييفًا لغرفة الخوادم). يعمل النشر على الحافة (Edge Deployment) على تحويل نموذج PyTorch المدرب إلى تنسيق ONNX الوسيط → ثم يخضع للتحسين الكمي باستخدام TensorRT INT8 → ثم يتم نشره على أجهزة Jetson ذات الاستهلاك المنخفض للطاقة في الحافة. تتخذ هذه المقالة نموذج ResNet-18 للكشف عن انقطاع خيوط الحبال الفولاذية في الرافعات كمثال، وتقدم سلسلة هندسية كاملة من التصدير إلى النشر، ويمكن تكرار جميع الخطوات. بيئة التجربة: التدريب على NVIDIA A10 (48 جيجابايت) / الحافة على Jetson Orin NX 16 جيجابايت (15 واط) / PyTorch 2.1.0 / TensorRT 8.6 / JetPack 6.0.
الخطوة الأولى: التصدير من PyTorch إلى ONNX
ONNX (Open Neural Network Exchange) هي ”اللغة المشتركة” لنشر النماذج. تقوم دالة torch.onnx.export() بتحويل مخطط الحساب الديناميكي في PyTorch إلى مخطط حساب ثابت في ONNX. إعدادات المعلمات الرئيسية: input_names=[“input”]، output_names=[“output”]، dynamic_axes={“input”:{0:”batch_size”,2:”height”,3:”width”}} (يدعم المدخلات ذات الأحجام المتغيرة). بعد التصدير، استخدم onnxruntime للتحقق من اتساق الدقة: الفرق في دقة الاستدلال FP32 مقارنةً بـ PyTorch أقل من 0.11 TP3T (متوسط 20 مجموعة تحقق).
import torch, torch.onnx
model = torch.load("resnet18_crane.pth") نموذج مُدرَّب مسبقًا بدقة # FP32
dummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy, "resnet18_crane.onnx",
input_names=["input"], output_names=["output"],
dynamic_axes={"input": {0: "batch", 2: "h", 3: "w"}},
opset_version=17)
# التحقق من دقة التصدير
import onnxruntime as ort
sess = ort.InferenceSession("resnet18_crane.onnx")
out_ort = sess.run(None, {"input": dummy.numpy()})[0]
out_pt = model(dummy).detach().numpy()
print(f"الفرق الأقصى: {abs(out_ort - out_pt).max():.6f}") # يجب أن يكون أقل من 1e-5
الخطوة الثانية: التكمية INT8 في TensorRT
TensorRT هو محرك NVIDIA المُحسَّن للاستدلال، والذي يحول نماذج ONNX إلى TensorRT Engine (.trt) من خلال دمج الطبقات (Layer Fusion) ومعايرة الدقة (INT8 Calibration) وتحسين الذاكرة (Pool Allocation). عملية التكمية INT8: يتم تعيين الأوزان وقيم التنشيط في نموذج FP32 من أرقام عائمة 32 بت إلى أعداد صحيحة 8 بت (دقة 256 مستوى). يتطلب التعيين ما بين 100 إلى 500 صورة معايرة (مجموعة بيانات المعايرة)، ويستخدم طريقة المعايرة بالإنتروبيا (Entropy Calibration) لتحديد عتبة التكمية التي تحقق أدنى تباعد KL.
الأمر المحدد: trtexec –onnx=resnet18_crane.onnx –saveEngine=resnet18_int8.trt –int8 –calib=calibration_data –fp16 –workspace=4096. شرح المعلمات الرئيسية: –int8 لتفعيل التكمية INT8؛ –calib لتحديد دليل صور المعايرة (500 صورة)؛ –fp16 لتفعيل الحسابات الوسيطة FP16 (دقة مختلطة مع أوزان INT8)؛ –workspace=4096 للسماح بمساحة عمل تبلغ 4 جيجابايت (يمكن تخصيص 8 جيجابايت من ذاكرة Jetson Orin NX سعة 16 جيجابايت). تستغرق عملية التحويل حوالي 11 دقيقة (وحدة معالجة الرسومات A10)، ويبلغ حجم محرك INT8 6.2 ميجابايت (مقابل 14% لـ FP32 ONNX).
| المؤشرات | PyTorch FP32 (GPU) | ONNX FP32 (GPU) | TensorRT INT8 (Jetson) | مقدار التحسين |
|---|---|---|---|---|
| حجم النموذج | 45 ميغابايت | 44 ميغابايت | 6.2 ميغابايت | ↓86% |
| تأخير الاستدلال | 5.0 مللي ثانية | 4.8 مللي ثانية | 1.2 مللي ثانية | ↑4.2x |
| استهلاك الطاقة | 350 واط | 350 واط | 12~15 واط | ↓96% |
| تكلفة الأجهزة | 80,000 يوان فأكثر | 80,000 يوان فأكثر | 3,500 ين | ↓96% |
| دقة «Top-1» | 94.0% | 93.9% | 93.6% | ↓0.4% |
| نقاط الفورمولا 1 | 0.93 | 0.93 | 0.92 | ↓0.01 |
الخطوة الثالثة: التحقق من الدقة
بعد التحويل إلى INT8، يجب التحقق مما إذا كان فقدان الدقة ضمن النطاق المقبول. عملية التحقق: تشغيل PyTorch FP32 (المعيار) وTensorRT INT8 (المراد التحقق منه) على مجموعة اختبار مكونة من 1,000 صورة → مقارنة دقة Top-1 ودرجة F1 ومصفوفة الخلط لكل فئة. في هذه التجربة التي قارنت بين INT8 وFP32: انخفضت دقة Top-1 من 94.0% إلى 93.6% (↓0.4%)، وانخفضت درجة F1 من 0.93 إلى 0.92 (↓0.01). تركزت خسارة الدقة على مستوى الفئات في فئة ”انقطاع خيوط الحبل الفولاذي” (ارتفع معدل الإغفال من 2.3% إلى 3.1%، +0.8%)، بينما كانت خسارة الدقة في الفئات الأربع المتبقية أقل من 0.3%. إذا تجاوزت خسارة الدقة في INT8 1%، يُوصى بالتحول إلى التكمية FP16 (الحجم 12 ميغابايت، وخسارة الدقة أقل من 0.1%) كحل وسط.
الخطوة الرابعة: نشر Jetson وخط الإنتاج
يتم تحميل ملفات TensorRT Engine مباشرةً على Jetson Orin NX (الذي يأتي مزودًا مسبقًا بـ JetPack 6.0 الذي يتضمن TensorRT 8.6). يستخدم كود الاستدلال واجهة برمجة تطبيقات TensorRT المرتبطة بلغة Python (أو واجهة برمجة تطبيقات C++ لتحقيق زمن انتقال أقل). خط إنتاج متعدد النماذج (مثل الكشف باستخدام YOLO + التصنيف باستخدام ResNet): استخدام CudaStream لتنفيذ الاستدلال غير المتزامن، حيث تتداخل المعالجة اللاحقة على وحدة المعالجة المركزية (NMS) مع الاستدلال على وحدة معالجة الرسومات للنموذج التالي. يبلغ زمن انتقال خط الإنتاج من البداية إلى النهاية حوالي 60% من مجموع أزمنة انتقال كل نموذج (في هذه التجربة، YOLOv8s 3.8 مللي ثانية + ResNet18 1.2 مللي ثانية ≈ 5 مللي ثانية من البداية إلى النهاية).
import tensorrt as trt, pycuda.driver as cuda
# تحميل محرك INT8
with open("resnet18_int8.trt", "rb") as f, trt.Runtime(trt.Logger()) as r:
engine = r.deserialize_cuda_engine(f.read())
ctx = engine.create_execution_context()
# تخصيص ذاكرة GPU
d_input = cuda.mem_alloc(1*3*224*224*4) # مدخلات FP32 (يتم معالجتها فعليًا باستخدام INT8)
d_output = cuda.mem_alloc(1*6*4)
stream = cuda.Stream()
# حلقة الاستدلال
for img in camera_stream():
cuda.memcpy_htod_async(d_input, preprocess(img), stream)
ctx.execute_async_v2([int(d_input), int(d_output)], stream.handle)
cuda.memcpy_dtoh_async(output, d_output, stream)
stream.synchronize()
result = postprocess(output) # الفئة + درجة الثقة
push_to_hmi(result) # إرسال إلى شاشة HMI للرافعة الآلية
أمثلة على النشر الفعلي
في مشروع الفحص البصري بالذكاء الاصطناعي لحبال الصلب لـ 17 رافعة في مصنع للصلب (رقم المشروع KL-EDGE-2024-003)، تم تركيب جهاز Jetson Orin NX واحد (3,500 يوان/جهاز) على كل رافعة. تم تركيب كاميرتين صناعيتين على كل رافعة (Basler acA2440-75um، لتصوير صور لكامل طول الحبل الفولاذي)، حيث يعمل جهاز Jetson على تشغيل خط إنتاج YOLOv8s+ResNet18 (للكشف عن مواقع انقطاع الأسلاك وتصنيف درجات الخطورة). يبلغ زمن الاستجابة من البداية إلى النهاية 5.0 مللي ثانية (سلسلة معالجة النموذجين)، ويتم معالجة حوالي 14,400 صورة يوميًا (كاميرتان × صورة واحدة كل 5 ثوانٍ × 10 ساعات). بعد التشغيل المتواصل لمدة 14 شهرًا (من يناير 2025 إلى فبراير 2026): اكتشف النظام إجمالي 328 حالة انقطاع في الأسلاك، وتم تأكيد 307 حالات منها بعد المراجعة اليدوية (معدل الدقة 93.61 TP3T)، مع 8 حالات لم يتم الإبلاغ عنها (معدل الاسترجاع 97.51 TP3T). بالمقارنة مع الفحص اليدوي اليومي (فحص بصري مرة واحدة يوميًا لكل رافعة، ومعدل الكشف عن انقطاع الأسلاك حوالي 40%)، ارتفع معدل الكشف في الفحص التلقائي بالذكاء الاصطناعي بمقدار 2.3 أضعاف.
مقارنة بين خيارات النشر — خوادم GPU مقابل الاستدلال الطرفي
يعتمد اختيار خطة النشر على القيود التي تفرضها الظروف الميدانية — وفيما يلي مقارنة بين مزايا وعيوب الخيارات الأربعة:
| البنود المقارنة | الخيار أ: خادم GPU | الخيار ب: وحدة المعالجة المركزية (CPU) في ONNX Runtime | الخيار C: TensorRT FP16 | الخيار د: TensorRT INT8 |
|---|---|---|---|---|
| منصة الأجهزة | NVIDIA A10/RTX 4090 | كمبيوتر صناعي (i7-12700) | Jetson Orin NX 16 جيجابايت | Jetson Orin NX 16 جيجابايت |
| حجم النموذج | 45 ميغابايت (FP32) | 44 ميغابايت (ONNX FP32) | 12 ميغابايت (FP16) | 6.2 ميغابايت (INT8) |
| تأخير الاستدلال | ~5 مللي ثانية (صورة واحدة بحجم 224×224) | ~25 مللي ثانية | ~2.5 مللي ثانية | ~1.2 مللي ثانية |
| استهلاك الطاقة | 350 واط | 65 واط | 12~15 واط | 12~15 واط |
| دقة «Top-1» | 94.0% | 93.9% | 93.8% | 93.6% |
| تكلفة الأجهزة | 80,000 يوان فأكثر | 8,000~15,000 يوان | 3,500 ين | 3,500 ين |
| موقع النشر | غرفة المعدات (تحتاج إلى تكييف) | غرفة التحكم/غرفة توزيع الكهرباء | داخل خزانة الكهرباء في الرافعة | داخل خزانة الكهرباء في الرافعة |
| مدى تعقيد التشغيل والصيانة | عالي (توافق المحركات/إدارة البيئة) | الصين (صيانة أنظمة التشغيل) | منخفض (جاهز للاستخدام فور تثبيت الروم) | منخفض (جاهز للاستخدام فور تثبيت الروم) |
| المواقف الموصى بها | تدريب النموذج/الاستدلال الدفعي دون اتصال بالإنترنت | يوجد جهاز كمبيوتر صناعي بالفعل ولا تتطلب العملية دقة عالية في زمن الاستجابة | النشر على الحافة مع إعطاء الأولوية للدقة | الخيار الأفضل من حيث القيمة مقابل السعر |
شروط الاختبار: ResNet-18، مدخلات 224×224، الدفعة = 1. خادم GPU: NVIDIA A10 (48 جيجابايت)، CUDA 12.1، PyTorch 2.1.0. ONNX Runtime CPU: i7-12700، ONNX Runtime 1.16. Jetson: Orin NX 16GB، JetPack 6.0، TensorRT 8.6. تم حساب متوسط زمن الاستجابة من 1000 عملية استدلال.
الأسئلة الشائعة
سؤال: هل يمكن قبول فقدان الدقة الناتج عن التكمية INT8 في TensorRT؟
الإجابة: في هذه التجربة، انخفض معدل الدقة «Top-1» بين INT8 وFP32 من 94.01 TP3T إلى 93.61 TP3T (بفقدان 0.41 TP3T)، وانخفض مؤشر F1 من 0.93 إلى 0.92 (بفقدان 0.01). في السياق الصناعي، فإن خسارة الدقة البالغة 0.41 TP3T مقابل ضغط الحجم بمقدار 7.5 أضعاف وتسريع الاستدلال بمقدار 4.2 أضعاف هي خسارة تستحق العناء تمامًا. إذا تجاوزت خسارة الدقة لفئة معينة من العيوب 1% (مثل انقطاع خيوط الحبل الفولاذي الذي انخفض من 92% إلى 90%)، يُنصح باستخدام الاستدلال FP16 بشكل منفصل لهذه الفئة أو زيادة نسبة عينات هذه الفئة في مجموعة بيانات المعايرة INT8.
سؤال: كيف يمكن تنفيذ التسلسل متعدد النماذج (الكشف باستخدام YOLO + التصنيف باستخدام ResNet) على Jetson؟
الإجابة: استخدام CudaStream و CUDA Graph في TensorRT لتنفيذ خط إنتاج متعدد النماذج. الخطوات التفصيلية: ① إنشاء CudaStreamين (stream1=YOLO، stream2=ResNet)؛ ② تشغيل YOLO في stream1 → تنفيذ NMS (كبت القيم غير القصوى، يستغرق حوالي 0.5 مللي ثانية) على وحدة المعالجة المركزية (CPU) → قص منطقة الكشف → إرسال نتائج القص إلى ResNet في stream2 للتصنيف؛ ③ مزامنة التيارين عبر CUDA Event. يبلغ التأخير من طرف إلى طرف حوالي 60% من مجموع تأخيرات كل نموذج (مع تداخل بين الاستدلال على GPU والمعالجة اللاحقة على CPU).
سؤال: ما هي المشكلات الشائعة التي قد تواجهها عند تصدير ملفات ONNX؟
الإجابة: ثلاث مشكلات شائعة: ① الأبعاد الديناميكية — يحدد ONNX أبعاد الإدخال بشكل ثابت افتراضيًّا، لذا يلزم ضبط المعلمة dynamic_axes لدعم الأبعاد المتغيرة (مثل التبديل بين 224×224 و416×416)؛ ② تدفق التحكم — يجب استبدال حلقات if/for في PyTorch بـ torch.where/torch.arange (لا يدعم ONNX تدفق التحكم في Python)؛ ③ المعاملات المخصصة — يجب تسجيل المعاملات المخصصة من نوع torch.autograd.Function في ONNX symbolic. يُنصح باستخدام الخلفية الجديدة torch.onnx.export(…, dynamo=True) للتصدير (PyTorch 2.1+).
سؤال: هل يمكن لأجهزة Jetson أن تعمل بشكل مستقر على المدى الطويل داخل خزانة التحكم في الرافعة الجسرية؟
الإجابة: نعم. يدعم الإصدار الصناعي من Jetson Orin NX (JetPack 6.0) نطاقًا واسعًا لدرجات الحرارة يتراوح بين -25 درجة مئوية و80 درجة مئوية، ورطوبة تتراوح بين 5 و951 TP3T دون تكاثف، ومقاومة للاهتزاز تصل إلى 50G. في التطبيق الفعلي، يتم استخدام نظام تبريد سلبي (مبدد حرارة ذو ريش من سبائك الألومنيوم مقاس 145×80×35 مم) مع غطاء حماية IP54، ويتم تركيبه على الجدار الداخلي للخزانة الكهربائية. وقد تم نشره على 17 رافعة (مشروع KL-EDGE-2024-003)، حيث استمر التشغيل المتواصل لمدة 14 شهرًا دون أعطال (حتى فبراير 2026). وتستقر درجة حرارة وحدة المعالجة المركزية (CPU) بين 65 و72 درجة مئوية (داخل خزانة كهربائية عند درجة حرارة بيئية تبلغ 35 درجة مئوية).
📌 توصيات بشأن المعايير ذات الصلة