Ultralytics YOLO27: انضم إلى قائمة الانتظار.
Get Started
أساسيات الرؤية الحاسوبية·مجموعات البيانات والتسميات·الدرس 7/10
الدرسمبتدئ

جودة التعليقات التوضيحية

تعرّف على محتوى تسمية YOLO، وافحص ملفات التسميات لاكتشاف الصفوف التالفة، وحافظ على اتساق المعلّقين باستخدام إرشادات وجولة معايرة مقيسة.

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

النتيجة

افحص ملفات التسميات في مجموعة بيانات، واكتب إرشادات من صفحة واحدة لفئة واحدة، وقِس الاتفاق بين معلّقين اثنين.

المسار السريع
إذا كنت تعرف الأساسيات بالفعل، فإليك النسخة المختصرة.
  1. تعرّف على صف التسمية الملائم لمهمتك: مربع أو مضلع أو نقاط مفتاحية أو زوايا.

  2. افحص كل ملف تسمية: عدد الأعمدة، ومعرّفات الفئات، والإحداثيات ضمن النطاق 0–1، والمربعات المكررة.

  3. إرشادات من صفحة واحدة تتضمن صورة مثال لكل قاعدة وقائمة بما لا ينبغي وضع تسميات له.

  4. ابدأ بجولة معايرة: يضع الجميع تسميات للصور الخمسين نفسها؛ ولا توسّع النطاق إلا بعد اجتياز اختبار الاتفاق.

  5. يرفض المراجعون العناصر مع ذكر السبب؛ ولا يصححونها بصمت أبدًا.

عملي

ما التسمية فعليًا؟#

في تنسيق YOLO، لكل صورة ملف مطابق من نوع .txt يتضمن صفًا واحدًا لكل عنصر. تُطبّع الإحداثيات إلى النطاق 0–1 نسبةً إلى عرض الصورة وارتفاعها، لذا تظل التسميات صالحة بعد تغيير الحجم. يعتمد تخطيط الصف على المهمة:

المهمةتسمية واحدةملاحظات
الكشفclass x_center y_center width heightمركز المربع وحجمه
تجزئة الكائناتclass x1 y1 x2 y2 ... xn ynمحيط المضلع
تقدير الوضعيةclass x y w h px1 py1 v1 ... pxn pyn vnمربع، ثم نقاط مفتاحية مع إمكانية تحديد مدى ظهورها
OBBclass x1 y1 x2 y2 x3 y3 x4 y4الزوايا الأربع للمربع المدور
التصنيفلا يوجد ملف تسمياتصور مصنفة في مجلد واحد لكل فئة
التجزئة الدلاليةقناع PNG لكل صورةقيمة كل بكسل هي فهرس فئة
تقدير العمقملف عمق PNG أو NPY لكل صورةقيمة كل بكسل هي مسافة

تبدأ فهارس الفئات من 0 وتُربط بأسماء في ملف YAML الخاص بمجموعة البيانات. الصورة التي لا تحتوي على عناصر لا تحتاج إلى ملف تسميات — فهذه هي طريقة عمل صور الخلفية. تتضمن صفحات مجموعات بيانات تنسيق الكشف والتجزئة الدلالية والعمق أمثلة عملية.

افحص ملفات التسميات#

قد تمر الصفوف التالفة من دون اكتشاف: إحداثي أكبر من 1، أو معرّف فئة غير معرّف في YAML، أو تحديد الكائن نفسه بمربعين؛ وكلها تتحول إلى ضوضاء يحاول النموذج تعلمها. تكشف أداة فحص قصيرة هذه المشكلات قبل التدريب:

from itertools import combinations
from pathlib import Path

from ultralytics.data.utils import check_det_dataset

def iou(a, b):
    """IoU of two normalized (x_center, y_center, width, height) boxes."""
    ax1, ay1, ax2, ay2 = a[0] - a[2] / 2, a[1] - a[3] / 2, a[0] + a[2] / 2, a[1] + a[3] / 2
    bx1, by1, bx2, by2 = b[0] - b[2] / 2, b[1] - b[3] / 2, b[0] + b[2] / 2, b[1] + b[3] / 2
    inter = max(0, min(ax2, bx2) - max(ax1, bx1)) * max(0, min(ay2, by2) - max(ay1, by1))
    return inter / (a[2] * a[3] + b[2] * b[3] - inter)

def check_labels(text, names):
    """Return a list of problems in one YOLO detection label file."""
    problems, boxes = [], []
    for i, row in enumerate(text.split("\n"), 1):
        values = row.split()
        if not values:
            continue
        if len(values) != 5:
            problems.append(f"row {i}: {len(values)} values, expected 5")
        elif int(values[0]) not in names:
            problems.append(f"row {i}: unknown class {values[0]}")
        elif not all(0 <= float(v) <= 1 for v in values[1:]):
            problems.append(f"row {i}: coordinates outside 0-1")
        else:
            boxes.append((i, int(values[0]), [float(v) for v in values[1:]]))
    for (i, ca, a), (j, cb, b) in combinations(boxes, 2):
        if ca == cb and iou(a, b) > 0.8:
            problems.append(f"row {j}: duplicate of row {i} (IoU {iou(a, b):.2f})")
    return problems

broken = "0 0.50 0.50 0.20 0.30\n0 0.51 0.50 0.20 0.30\n3 0.40 0.40 0.10 0.10\n0 1.20 0.50 0.10 0.10\n0 0.50 0.50 0.10"
print("\n".join(check_labels(broken, {0: "pill"})))

data = check_det_dataset("medical-pills.yaml")
files = sorted(Path(data["path"], "labels").rglob("*.txt"))
found = sum(len(check_labels(f.read_text(), data["names"])) for f in files)
print(f"medical-pills: {found} problems in {len(files)} label files")
row 3: unknown class 3
row 4: coordinates outside 0-1
row 5: 4 values, expected 5
row 2: duplicate of row 1 (IoU 0.90)
medical-pills: 0 problems in 115 label files

يعرض المثال المصنوع يدويًا كل خطأ تكتشفه أداة الفحص؛ أما مجموعة البيانات الحقيقية فتجتاز الفحص من دون مشكلات. شغّلها بعد كل دفعة من أعمال وضع التسميات وقبل كل تدريب — فالأمر يستغرق ثوانٍ.

لماذا تنحرف التسميات؟#

محرر التعليقات التوضيحية في Ultralytics Platform بملء الشاشة: صناديق كرتونية على روبوتات مستودع محددة بمضلعات، وقائمة الفئات مفتوحة بجوارها

انظر إلى لوحة الفئات في هذا المحرر: حُفظت مضلعات الصناديق الكرتونية الأربعة عشر ضمن Grapes، بينما تقع فئة Box أسفلها في القائمة — كما توجد الفئتان plane وPlane منفصلتين. لا تستطيع أداة الفحص اكتشاف أي من المشكلتين، لأن كل صف صحيح، لكن مراجعًا يطابق التسميات مع الإرشادات سيلاحظ المشكلتين في ثوانٍ. معظم أخطاء التسميات من هذا النوع: ملفات صحيحة، وقرارات خاطئة.

نادرًا ما يضع المعلّقون تسميات رديئة بسبب الإهمال. بل يفعلون ذلك بصورة غير متسقة لأن القواعد ملتبسة، فيجتهد كل منهم بتخمين معقول لكنه مختلف:

  • أين ينتهي المربع؟ هل يشمل الظل، أو حقيبة محمولة، أو الحمولة على شوكتي رافعة شوكية؟
  • الاقتطاع. هل يُصنّف شخص يقع نصفه خارج الإطار على أنه person؟ وما النسبة الظاهرة التي تبرر ذلك؟
  • الحجب. هل يُصنّف ظهور عجلة واحدة فقط من سيارة، أم تُهمل السيارة؟
  • حدود الفئات. هل تُصنّف شاحنة صغيرة على أنها car أم truck؟ وهل تُصنّف دراجة نارية صغيرة على أنها bicycle أم other؟
  • حالات لا ينبغي وضع تسميات لها. الانعكاسات والملصقات والشاشات التي تعرض العنصر.

كل خيار مقبول ما دام الجميع يختار الخيار نفسه.

إرشادات من صفحة واحدة#

ينبغي أن تكون الإرشادات في صفحة واحدة، وتتضمن صورة مثال لكل قاعدة، وتعرض الحالات الحدية أولًا:

## Class: forklift

Include:
- Forklifts with at least 30% of the body visible, moving or parked, any size or color.

Do not label:
- Pallet jacks (no overhead mast) → class `pallet_jack`.
- Reflections in glass or polished floors, and forklifts on posters or screens.

Box rules:
- Include the mast and the full visible body.
- Exclude the load (pallets, boxes) — those have their own classes.
- For partly occluded forklifts, box the visible part only.

قِس الاتفاق في جولة معايرة#

قبل التوسع من معلّق واحد إلى عشرين، يضع الجميع تسميات للصور الخمسين نفسها ثم تقارن النتائج:

الاتفاق هو نسبة العناصر التي رسمها المعلّقان معًا بالفئة نفسها وبـ IoU لا يقل عن 0.5 — وهو المقياس العملي لـموثوقية الاتفاق بين المقيّمين:

def iou(a, b):
    """IoU of two (x1, y1, x2, y2) pixel boxes."""
    w = max(0, min(a[2], b[2]) - max(a[0], b[0]))
    h = max(0, min(a[3], b[3]) - max(a[1], b[1]))
    inter = w * h
    return inter / ((a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter)

def agreement(boxes_a, boxes_b, threshold=0.5):
    """Share of objects both annotators drew with the same class and IoU >= threshold."""
    unmatched, matched = list(boxes_b), 0
    for cls_a, box_a in boxes_a:
        best = max(unmatched, key=lambda b: iou(box_a, b[1]) if b[0] == cls_a else -1, default=None)
        if best and best[0] == cls_a and iou(box_a, best[1]) >= threshold:
            unmatched.remove(best)
            matched += 1
    return matched / max(len(boxes_a), len(boxes_b))

# Two annotators label the same calibration image
alice = [("forklift", (100, 200, 400, 600)), ("person", (450, 220, 520, 480)), ("person", (600, 230, 660, 470))]
bob = [("forklift", (100, 120, 400, 600)), ("person", (452, 218, 522, 478)), ("pallet_jack", (600, 230, 660, 470))]
print(f"agreement: {agreement(alice, bob):.0%}")
agreement: 67%

يخفي هذا الرقم، 67%، نوعين من الاختلاف. يبدأ مربع الرافعة الشوكية الذي رسمه Bob أعلى بـ80 بكسل — إذ أدرج طرف السارية — لكنه يطابق مع ذلك عند IoU بقيمة 0.83، لذا يُحتسب اتفاقًا؛ شدّد قاعدة تحديد المربع إذا كان منتجك يقيس الارتفاع. العنصر الثالث يمثل تعارضًا حقيقيًا: تقول Alice إنه person، بينما يقول Bob إنه pallet_jack. يستلزم ذلك قاعدة أوضح وصورة مثال، ثم جولة أخرى. احتفظ بصور المعايرة الخمسين: ستصبح اختبار التهيئة لكل معلّق جديد.

المراجعون يرفضون التسميات ولا يصلحونها سرًا

المراجع الذي يصحح التسميات بهدوء يزيل حلقة التغذية الراجعة، ولا يتعلم العاملون على التعليقات التوضيحية أبدًا ما أخطؤوا فيه. ارفض التسميات مع ذكر السبب ودع العامل المسؤول عن التعليقات التوضيحية يصلحها. سيكون العمل أبطأ في أول مئة صورة، لكنه أسرع بكثير بعدها.

ما الذي ينبغي تتبعه؟#

المقياسما الذي يوضحه؟
الإنتاجية (عدد العناصر في الساعة)القدرة والتكلفة
معدل الرفضما إذا كانت الجودة تتراجع
الاتفاق على مجموعة تداخل مشتركةما إذا كان المعلّقون لا يزالون يطبقون القواعد نفسها

عندما ينخفض الاتفاق، فهذا يعني أن شيئًا ما قد تغير: معلّق جديد، أو كاميرا جديدة، أو دفعة ملتبسة، أو إرهاق. وعندما يخفق النموذج لاحقًا في حالة محددة، أعد إرسال الصور نفسها لوضع التسميات — وهي الحلقة التي تؤتمتها عملية التعلم النشط، والتي يأتي منها معظم التحسن الفعلي في الدقة الواقعية. استعرض حالات الإخفاق وصفِّها في مجموعات البيانات على Ultralytics Platform، وراجع ضبط جودة مجموعات البيانات وأفضل ممارسات التعليقات التوضيحية لمعرفة كيفية تطبيق ذلك على نطاق واسع.

جرّبه

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

متى يُعدّ مكتملًا
تكون قد أتممت الدرس عند تحقق جميع الشروط التالية.
  • لا تُبلغ أداة الفحص عن أي مشكلات في تسمياتك، أو أنك أصلحت كل ما اكتشفته.

  • لديك إرشادات من صفحة واحدة تتضمن مثالًا لكل قاعدة وقائمة بما لا ينبغي وضع تسميات له.

  • لديك درجة اتفاق لمعلّقين اثنين وقائمة بالقواعد التي غيرتها استنادًا إلى هذه الدرجة.

عرض الحل

إرشادات لفئة pill الخاصة بـ medical-pills، كُتبت بعد جولة معايرة على 10 صور:

## Class: pill

Include:
- Every tablet inside a blister cavity, including half-visible ones at the image edge.
- Tablets seen through clear plastic, even when glare covers part of them.

Do not label:
- Empty cavities, torn foil, or tablets outside the pack.
- Reflections of tablets in the foil.

Box rules:
- Box the tablet itself, not the plastic bubble around it.
- If two tablets touch, draw two boxes.

كيفية قراءة درجة الاتفاق: لنفترض أن نتيجة الجولة الأولى 82%. صنّف العناصر غير المتطابقة حسب السبب. في عبوات الفقاعات، يتمثل السببان المعتادان في رسم المربعات حول الفقاعة البلاستيكية بدلًا من القرص (يُعالج ذلك بقاعدة المربع الأولى وإضافة صورة مثال) وتجاوز الأقراص التي يحجبها الوهج من قِبل أحد المعلّقين (يُعالج ذلك بقاعدة التضمين الثانية). أعد الاختبار على 10 صور جديدة، ولا تتوسع إلا بعد تجاوز النتيجة حد 90%.

ما الخطوة التالية

التالي: تقسيم البيانات المصنفة بحيث تظل مقاييسك موثوقة — بدءًا بتسرّب خفي في مجموعة البيانات هذه نفسها.

سجّل الدخول لحفظ التقدم