اكتشاف الأجسام: YOLO وFaster-RCNN
“من تصنيف الصور إلى تحديد وتسمية كل كائن في المشهد”
من النوافذ المتحركة إلى YOLO — IoU وصناديق الإرساء وNMS وmAP. كيف يكتشف YOLO 80 فئة جسم في الوقت الفعلي بـ30 إطار في الثانية.
المتطلبات الأساسية
المفاهيم المغطاة
∑الصيغ الرئيسية
IoU
التقاطع على الاتحاد — مقياس جودة الصندوق الحدودي؛ IoU > 0.5 هو كشف صحيح اصطلاحاً
mAP
متوسط متوسط الدقة — المساحة تحت منحنى الدقة-الاستدعاء، متوسطة عبر جميع الفئات
خسارة YOLO
مجموع موزون: انحدار الصندوق + ثقة الموضوعية + احتمالات الفئة
NMS
الاحتفاظ فقط بالصندوق الأعلى ثقةً عندما تتداخل عدة صناديق بشكل كبير على نفس الجسم
▶محاكاة تفاعلية
ما وراء التصنيف: أين وماذا؟
تُجيب تصنيف الصور على «هل يوجد قط؟». الكشف يُجيب على «أين القطط، وهل يوجد أيضاً كلاب؟». هذا التحول من علامة واحدة إلى عدد متغير من المخرجات (فئة، صندوق حدود) هو ما يجعل الكشف عن الأجسام المهمة المحورية في القيادة الذاتية والتصوير الطبي وتسجيل التجزئة والمراقبة. كل سيارة ذاتية القيادة تشغّل كاشفاً في الوقت الفعلي يعالج أكثر من 30 إطاراً في الثانية. التطور من مصنّفات النافذة المنزلقة (DPM, 2010) → كاشفات مرحلتين (RCNN, 2014؛ Faster-RCNN, 2015) → كاشفات مرحلة واحدة (YOLO v1, 2016 → v8, 2023) هو أحد أسرع المجالات تطوراً في رؤية الحاسوب.
يمرر Tesla Autopilot 8 كاميرات عبر شبكة كشف مخصصة بـ36 إطاراً/ثانية على شريحة 72 TOPS. يجب أن يتناسب النموذج بأكمله في ميزانية زمن استجابة ضيقة مع الكشف عن أجسام على بعد 200 متر.
مرحلتان مقابل مرحلة واحدة: المقايضة الأساسية
**كاشفات مرحلتين (Faster-RCNN):** المرحلة 1 — شبكة اقتراح المناطق (RPN) تقترح ~300 منطقة مرشحة. المرحلة 2 — رأس تصنيف + انحدار يُحسّن كل اقتراح. الميزة: دقة عالية. العيب: بطيء. **كاشفات مرحلة واحدة (YOLO، SSD):** قسّم الصورة إلى شبكة. كل خلية تتنبأ مباشرةً بإزاحات الصندوق ودرجة الموضوعية واحتمالات الفئة في مرور أمامي واحد. الميزة: سريع. العيب: أصعب في التدريب ويفوّت الأجسام الصغيرة المتداخلة. **مبني على المراسي مقابل بدون مراسي:** YOLO v1-v3 استخدم صناديق مراسي. YOLO v8 / FCOS / CenterNet بدون مراسي — يتنبأ مباشرةً بمركز + عرض/ارتفاع الصندوق، أبسط وغالباً أفضل.
YOLO = 'You Only Look Once.' الفكرة: بدلاً من تشغيل مصنّف في آلاف مواضع النافذة المنزلقة، التنبؤ بجميع الصناديق في وقت واحد في مرور واحد للشبكة.
خط أنابيب استدلال YOLO
قسّم صورة الإدخال إلى شبكة S×S (مثل 13×13 لإدخال 416 بكسل في YOLO v2).
لكل خلية: تنبّأ بـ B صندوقاً حدودياً (كل منها: x, y, w, h نسبة إلى الخلية + درجة موضوعية) وC احتمالاً للفئة.
إحداثيات الصندوق: x, y هي إزاحات من مركز الخلية (0-1)، w/h هي إزاحات مقياس لوغاريتمي من أحجام المراسي.
الموضوعية × احتمال الفئة = درجة ثقة خاصة بالفئة لكل صندوق.
طبّق الإخماد غير الأقصى (NMS): لكل فئة رتّب الصناديق حسب الثقة، احتفظ بالأعلى ثقةً، اخمد الصناديق بـIoU > 0.5 مع الصندوق المحتفظ به، كرر.
المخرج النهائي: قائمة بطول متغير من الصفوف (فئة، ثقة، x1, y1, x2, y2).
اكتشاف الأجسام مع YOLOv8
class="tok-comment"># pip install ultralytics from ultralytics import YOLO import numpy as np import cv2 class="tok-comment"># ── class="tok-num">1. Load pretrained YOLO v8 ──────────────────────────────────────────────── model = YOLO(class="tok-str">"yolov8n.pt") class="tok-comment"># nano model (class="tok-num">3.2M params, fastest) class="tok-comment"># Other sizes: yolov8s.pt, yolov8m.pt, yolov8l.pt, yolov8x.pt class="tok-comment"># ── class="tok-num">2. Inference on a single image ──────────────────────────────────────────── results = model(class="tok-str">"path/to/image.jpg", conf=class="tok-num">0.25, iou=class="tok-num">0.5) for r in results: boxes = r.boxes class="tok-comment"># Boxes object for box in boxes: x1, y1, x2, y2 = box.xyxy[class="tok-num">0].tolist() class="tok-comment"># absolute pixel coords conf = box.conf[class="tok-num">0].item() class="tok-comment"># confidence score cls = int(box.cls[class="tok-num">0].item()) class="tok-comment"># class index label = model.names[cls] print(fclass="tok-str">"{label}: {conf:.2f} at ({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f})") class="tok-comment"># ── class="tok-num">3. Fine-tuning on custom dataset ───────────────────────────────────────── class="tok-comment"># Dataset format: YOLO txt format class="tok-comment"># data.yaml: class="tok-comment"># train: /path/to/train/images class="tok-comment"># val: /path/to/val/images class="tok-comment"># nc: class="tok-num">3 # number of classes class="tok-comment"># names: [class="tok-str">'cat', class="tok-str">'dog', class="tok-str">'car'] model = YOLO(class="tok-str">"yolov8s.pt") class="tok-comment"># start from ImageNet pretrained results = model.train( data=class="tok-str">"data.yaml", epochs=class="tok-num">50, imgsz=class="tok-num">640, batch=class="tok-num">16, lr0=class="tok-num">0.01, class="tok-comment"># initial learning rate lrf=class="tok-num">0.01, class="tok-comment"># final lr fraction augment=True, class="tok-comment"># mosaic, flip, scale augmentation device=class="tok-num">0, class="tok-comment"># GPU class="tok-num">0 ) print(fclass="tok-str">"mAP50: {results.metrics.mAP50:.4f}") class="tok-comment"># ── class="tok-num">4. IoU calculation from scratch ────────────────────────────────────────── def iou(box1, box2): class="tok-str">"""box = [x1, y1, x2, y2]""" x1 = max(box1[class="tok-num">0], box2[class="tok-num">0]); y1 = max(box1[class="tok-num">1], box2[class="tok-num">1]) x2 = min(box1[class="tok-num">2], box2[class="tok-num">2]); y2 = min(box1[class="tok-num">3], box2[class="tok-num">3]) inter = max(class="tok-num">0, x2-x1) * max(class="tok-num">0, y2-y1) area1 = (box1[class="tok-num">2]-box1[class="tok-num">0]) * (box1[class="tok-num">3]-box1[class="tok-num">1]) area2 = (box2[class="tok-num">2]-box2[class="tok-num">0]) * (box2[class="tok-num">3]-box2[class="tok-num">1]) return inter / (area1 + area2 - inter + class="tok-num">1e-6) gt = [class="tok-num">100, class="tok-num">50, class="tok-num">250, class="tok-num">200] pred = [class="tok-num">110, class="tok-num">60, class="tok-num">260, class="tok-num">210] print(fclass="tok-str">"\nIoU = {iou(gt, pred):.4f}") class="tok-comment"># ── class="tok-num">5. Manual NMS ───────────────────────────────────────────────────────────── def nms(boxes, scores, iou_threshold=class="tok-num">0.5): class="tok-str">"""Boxes: (N,class="tok-num">4) xyxy, Scores: (N,)""" order = np.argsort(scores)[::-class="tok-num">1] keep = [] while len(order) > class="tok-num">0: i = order[class="tok-num">0] keep.append(i) ious = np.array([iou(boxes[i], boxes[j]) for j in order[class="tok-num">1:]]) order = order[class="tok-num">1:][ious < iou_threshold] return keep boxes = np.array([[class="tok-num">100,class="tok-num">50,class="tok-num">250,class="tok-num">200],[class="tok-num">105,class="tok-num">55,class="tok-num">255,class="tok-num">205],[class="tok-num">200,class="tok-num">100,class="tok-num">350,class="tok-num">250]]) scores = np.array([class="tok-num">0.95, class="tok-num">0.87, class="tok-num">0.72]) kept = nms(boxes, scores) print(fclass="tok-str">"Kept boxes: {kept}") class="tok-comment"># [class="tok-num">0, class="tok-num">2] — box class="tok-num">1 suppressed (overlaps with class="tok-num">0)
mAP وفخ عتبة IoU
mAP@0.5 وmAP@0.5:0.95 تحكيان قصصاً مختلفة جداً. نموذج بـmAP@0.5 ممتاز لكن mAP@0.5:0.95 ضعيف يُحدّد مواقع الأجسام بشكل فضفاض — مقبول للمهام الخشنة، سيئ للإمساك الروبوتي. أيضاً تعامل mAP جميع الفئات بالتساوي مما يُخفي الأداء السيئ على الفئات النادرة. للمجموعات غير المتوازنة أبلغ عن AP لكل فئة منفصلاً. مخاطر شائعة: (1) نسيان تطبيع إحداثيات الصندوق بحجم الصورة. (2) استخدام عتبة ثقة منخفضة جداً أثناء NMS — احتفظ بـconf_threshold ≈ 0.25 أثناء الاستدلال. (3) الإفراط في التخصيص على المجموعات الصغيرة — استخدم دائماً تعزيزاً قوياً.
تحسين بنسبة 1% في mAP على معيار COCO (80 فئة، 330 ألف صورة) يمثل أشهراً من البحث — السياق مهم عند مقارنة النماذج في مجالك.
?اختبار المعرفة
يتم حفظ التقدم في متصفحك — لا حاجة لحساب.