انتقل إلى المحتوى الرئيسي

الوحدة 2 — الكشف ذو المرحلتين: عائلة R-CNN

انتهت الوحدة السابقة إلى نتيجة عمليّة: مشروع عدّ السيّارات على التقاطع يتطلّب كشفًا، لا تصنيفًا. تبقى مسألة كيف ننفّذ الكشف. تعرض هذه الوحدة أوّل مقاربة نجحت فعلًا — عائلة R-CNN — التي تفصل المشكلة إلى مرحلتين: أوّلًا نقترح مناطق محتملة، ثم نصنّفها ونصحّح موضعها.

من النافذة المنزلقة إلى الاقتراحات

المقاربة الساذجة قبل R-CNN كانت النافذة المنزلقة: نمرّر مصنّف صور على كلّ موضع وكلّ مقياس وكلّ نسبة عرض/ارتفاع ممكنة، ونحتفظ بالمواضع التي يقول عنها المصنّف «سيّارة». لكنّ هذا كارثي حسابيًا: على صورة 500 في 500 بكسل، بعشرات الأحجام والنسب، تصير النوافذ المُقيَّمة بمئات الآلاف. وعلى كامل مجموعة تدريب، النتيجة هي أسابيع من الحساب لصورة واحدة.

طرح R-CNN الأصلي (Girshick، 2014) خطوة وسيطة: خوارزمية Selective Search تُنتج قرابة 2000 «اقتراح» فقط لكلّ صورة، وهي نوافذ من المرجَّح أن تحتوي على جسم. ثم تُمرَّر كلّ نافذة عبر شبكة CNN لاستخراج متّجه سماتها، ويُصنَّف بمصنّف SVM لكلّ صنف. مكسب هائل مقارنة بالنافذة المنزلقة، لكن السرعة تبقى بضع دقائق للصورة الواحدة، لأنّ كلّ اقتراح يتطلّب مرورًا كاملًا في CNN.

Fast R-CNN: مشاركة الحساب

جاء Fast R-CNN (Girshick، 2015) بفكرة بسيطة وعميقة: لا نُمرّر الشبكة على كلّ اقتراح على حدة. بل نُمرّرها مرّة واحدة على الصورة كلّها لإنتاج خريطة سمات، ثم نستخرج من هذه الخريطة الجزء المقابل لكلّ اقتراح.

المشكلة التقنية: الاقتراحات لها أحجام مختلفة، لكنّ الطبقات الكاملة الاتّصال في نهاية الشبكة تحتاج إلى دخل ثابت الحجم. حلّ Fast R-CNN هو RoI Pooling (Region of Interest Pooling): تقسيم منطقة الاقتراح داخل خريطة السمات إلى شبكة ثابتة مثل 7×7، وأخذ الحدّ الأقصى داخل كلّ خليّة.

هذا يُعطي متّجه سمات بحجم موحّد لأيّ اقتراح، ويسمح بالتدريب المشترك للمصنّف وللانحدار الذي يُصحّح إحداثيات الصندوق. ورأس الشبكة يُنتج مُخرَجين متوازيين: احتمالات الأصناف، وإزاحة الصندوق نسبةً إلى الاقتراح. الخسارة الكلّية هي مجموع خسارة إنتروبيا متقاطعة للتصنيف وخسارة L1 مُنعّمة لتصحيح الصندوق.

Faster R-CNN: شبكة اقتراح المناطق

بقي Fast R-CNN معتمدًا على Selective Search الخارجية والبطيئة. جاء Faster R-CNN (Ren et al.، 2015) ليغلق الحلقة: شبكة اقتراح المناطق (Region Proposal Network، اختصارًا RPN) تعمل على خريطة السمات نفسها وتُنتج الاقتراحات مباشرة.

الفكرة: على كلّ نقطة من خريطة السمات، نُعرِّف مجموعة من المراسي (anchors) بأحجام ونسب مختلفة (مثلًا 3 مقاييس × 3 نسب = 9 مراسي لكلّ نقطة). ثم RPN تُنتج لكلّ مرساة تنبّؤين: احتمال أنّها تحتوي على جسم (بلا تحديد الصنف)، وتصحيحات لإحداثياتها. نحتفظ بأعلى NN مرساة موصى بها، ونمرّرها إلى المرحلة الثانية لتصنيفها الفعلي.

النتيجة معمارية مُتّحدة كليًّا، قابلة للتدريب من طرف إلى طرف، وأسرع بأمر مقدار من Fast R-CNN.

RoI Align: تصحيح انحياز عدم المحاذاة

عيب RoI Pooling أنّها تقرّب إحداثيات الاقتراح إلى أقرب خليّة، ثم تقرّب حدود كلّ خليّة داخلية. هذان التقريبان يزيحان الميزة عن موضعها الفعلي بنصف بكسل تقريبًا في كلّ اتّجاه، وهو ما يُدهور التقطيع الدقيق (سنعود إليه في الوحدة 7).

جاء RoI Align (He et al.، 2017، في ورقة Mask R-CNN) لحلّ هذا: لا تقريب، بل استيفاء ثنائي الخطّية لقيم البكسلات في مواضع حقيقية عشرية. يُحسّن دقّة الصناديق قليلًا، ويُحسّن دقّة أقنعة النُسَخ بأمر مقدار.

استخدام Faster R-CNN بـtorchvision

torchvision يُقدّم Faster R-CNN مُدرَّبًا مسبقًا على COCO. لتطبيقه على صورة من كاميرا التقاطع:

import torch
import torchvision
from torchvision.transforms import functional as F
from PIL import Image

# النموذج المُدرَّب مسبقا على COCO
weights = torchvision.models.detection.FasterRCNN_ResNet50_FPN_Weights.DEFAULT
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(weights=weights)
model.eval()

# صورة من كاميرا التقاطع
image = Image.open("carrefour_10h32.jpg").convert("RGB")
tensor = F.to_tensor(image)

with torch.no_grad():
outputs = model([tensor])

# مخرج لصورة واحدة
predictions = outputs[0]
boxes = predictions["boxes"] # (N, 4) بصيغة (x1, y1, x2, y2)
labels = predictions["labels"] # (N,) معرفات اصناف COCO
scores = predictions["scores"] # (N,) بين 0 و 1

# نحتفظ بالسيارة (3) والمشاة (1) والحافلة (6) فقط
categories_of_interest = {1: "مشاة", 3: "سيارة", 6: "حافلة"}
mask = torch.isin(labels, torch.tensor(list(categories_of_interest.keys())))
mask &= scores > 0.5

filtered_boxes = boxes[mask]
filtered_labels = [categories_of_interest[int(l)] for l in labels[mask]]
print(f"عدد الاجسام: {len(filtered_boxes)}")

المفاضلة بين الدقّة والسرعة

الكواشف بمرحلتين تُقدّم الدقّة الأعلى بين العائلات، خصوصًا للأجسام الصغيرة والمزدحمة. سبب ذلك أنّ RPN تُقلّص عدد المرشّحين من عشرات الآلاف إلى بضعة آلاف، فيُصبح المصنّف قادرًا على العمل بعناية أكبر لكلّ منها.

الثمن هو السرعة. Faster R-CNN بـResNet-50 على وحدة GPU حديثة يُقدّم قرابة 15 إلى 20 إطارًا في الثانية، وهو يكفي بالكاد لتحليل فيديو 30 إطارًا في الثانية بعد التخفيض. أمّا الكشف في الزمن الحقيقي على معالج بلا GPU فبعيد المنال. لهذا نحتاج إلى العائلة الثانية — الكواشف ذوات المرحلة الواحدة — وهو موضوع الوحدة التالية.

احذر مسمّى «الاقتراحات» في الوثائق

يُستخدم مصطلح «proposals» بمعنيين متمايزين: الاقتراحات الأوّلية التي تُنتجها Selective Search أو RPN قبل التصفية، وهي بعشرات الآلاف؛ والاقتراحات المحفوظة بعد تطبيق NMS وعتبة الثقة، وهي بضعة آلاف فقط. اقرأ السياق دائمًا: قراءة «2000 اقتراح» لا تعني الشيء نفسه قبل NMS وبعده، ويمكن أن تُخفي مشكلات ذاكرة حقيقية إن خُلط بينهما.

في الخلاصة

  • عائلة R-CNN تُفصل الكشف إلى مرحلتين: اقتراح مناطق محتملة، ثم تصنيفها وتصحيح صناديقها.
  • Fast R-CNN يمرّر CNN مرّة واحدة على الصورة، وFaster R-CNN يُدمج شبكة اقتراح المناطق (RPN) لسرعة كاملة من طرف إلى طرف.
  • RoI Align يُصحّح انحياز عدم المحاذاة في RoI Pooling؛ تحسّن طفيف للكشف وكبير لأقنعة النُسَخ.
  • الكواشف بمرحلتين تُقدّم الدقّة الأعلى لكن بسرعة 15 إلى 20 إطارًا في الثانية على GPU، وهو حدّ يمنعها من التطبيقات الزمنية الحقيقية على أجهزة أضعف.

الوحدة التالية: العائلة الثانية من الكواشف، YOLO وSSD، التي تُلغي المرحلة الوسيطة وتُحقّق سرعة حقيقية في الزمن الحقيقي.