ضبط جودة مجموعة البيانات
دقّق الوسوم وتوازن الفئات والتكرارات وتغطية التقسيمات باستخدام برنامج نصي، ودع نموذجًا يشير إلى الوسوم المحتمل فقدانها، ثم أصلح ما تجده قبل التدريب.
عادةً ما يؤدي إصلاح الوسوم إلى دقة أعلى مما يحققه ضبط المعلمات الفائقة. فكثيرًا ما تقضي الفرق أسابيع في ضبط الإعدادات لكسب نقطة أو نقطتين من mAP، ثم تكتشف في مراجعة تدقيقية لا تستغرق سوى ظهيرة واحدة أن جزءًا من إحدى الفئات وُسِم خطأً طوال الوقت. يكشف ضبط الجودة ما أغفله دليل وضع الوسوم قبل أن يحوّل تشغيل التدريب أخطاء الوسوم إلى رقم لا ينبغي لأحد الوثوق به.
عملي
الفحوصات السبع#
أجرِ الفحوصات السبع كلها قبل أول عملية تدريب، ثم أعدها بعد كل دفعة كبيرة من الوسوم. يشرح دليل المعالجة المسبقة للبيانات المشروحة عددًا منها بالتفصيل.
| # | الفحص | ما الذي يكشفه | طريقة الكشف |
|---|---|---|---|
| 1 | الوسوم المفقودة | الكائنات التي أغفلها القائمون بالوسم | مراجعة العناصر المشتبه بها أدناه؛ وفحص عينة بصريًا |
| 2 | الفئات الخاطئة أو غير الصالحة | فئات متبادلة، ومعرّفات خارج قائمة الفئات | برنامج التدقيق النصي؛ ومصفوفة الالتباس بعد أول عملية تدريب |
| 3 | المربعات الفضفاضة | مربعات تتضمن هوامش أو حوافًا مقصوصة | فحص عينة بصريًا؛ ورصد المربعات الصغيرة جدًا في التدقيق |
| 4 | التكرارات | نسخ مطابقة وإطارات متطابقة تقريبًا | برنامج التدقيق النصي للنسخ المطابقة؛ وعرض Clustering في Ultralytics Platform، مع تلوين العناصر بحسب التقسيم، للكشف عن التكرارات شبه المطابقة |
| 5 | اختلال توازن الفئات | فئات تقل أعدادها كثيرًا عن المستهدف في المواصفات | برنامج التدقيق النصي؛ وعلامة التبويب Charts في Platform |
| 6 | تغطية التقسيمات وتسرب البيانات | فئات غائبة عن أحد التقسيمات؛ مقطع واحد موجود في تقسيمين | برنامج التدقيق النصي؛ والتقسيمات المجمعة (الدرس التالي) |
| 7 | نسبة صور الخلفية | عدد قليل جدًا أو كبير جدًا من الصور الفارغة | برنامج التدقيق النصي؛ والهدف 0–10% |
أتمتة الإحصاء#
الفحوصات 2 و4 و5 و6 و7 مسائل إحصاء، لذا أنشئ نصوصًا برمجية لتنفيذها. يقرأ هذا التدقيق ملف YAML لبيانات الكشف، حيث يشير train: وval: إلى مجلدي الصور. ويحصي النماذج لكل فئة وصور الخلفية في كل تقسيم، ويرصد معرّفات الفئات غير الصالحة، والمربعات الصغيرة جدًا، وصفوف الوسوم المكررة، والصور المتطابقة بايتًا ببايت، ويسرد الفئات الموجودة في val وغير الموجودة في train:
import hashlib
from collections import Counter
from pathlib import Path
from ultralytics.data.utils import IMG_FORMATS, check_det_dataset, img2label_paths
data = check_det_dataset("coco8.yaml") # downloads COCO8 on first run; swap in your data YAML
names, seen = data["names"], {}
for split in ("train", "val"):
images = sorted(p for p in Path(data[split]).rglob("*.*") if p.suffix[1:].lower() in IMG_FORMATS)
instances, hashes, problems, background = Counter(), Counter(), [], 0
for img, label in zip(images, img2label_paths([str(p) for p in images])):
hashes[hashlib.md5(img.read_bytes()).hexdigest()] += 1
rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
background += not rows
if len(rows) != len({tuple(r) for r in rows}):
problems.append(f"{img.name}: duplicate label rows")
for r in rows:
cls, w, h = int(r[0]), float(r[3]), float(r[4])
if cls not in names:
problems.append(f"{img.name}: class {cls} is not in names")
elif w * h < 1e-4:
problems.append(f"{img.name}: tiny {names[cls]} box")
instances[cls] += 1
seen[split] = set(instances)
duplicates = sum(n - 1 for n in hashes.values())
print(f"{split}: {len(images)} images, {background} background, {duplicates} duplicate images")
for cls, n in instances.most_common():
print(f" {names.get(cls, cls):>12} {n}")
for problem in problems:
print(" !", problem)
print("in val but never in train:", sorted(names[c] for c in seen["val"] - seen["train"] if c in names))train: 4 images, 0 background, 0 duplicate images
orange 4
bowl 3
giraffe 2
...
val: 4 images, 0 background, 0 duplicate images
person 10
horse 2
...
in val but never in train: ['dog', 'elephant', 'horse', 'person', 'umbrella']السطر الأخير هو النتيجة. يحتوي person على 10 نماذج في val ولا يحتوي على أي منها في train، لذا تقيّم val فئةً لم يسبق للنموذج أن تدرّب عليها. مجموعة COCO8 مخصصة لاختبار أولي من 8 صور، لذلك هذا متوقع فيها؛ أما في مجموعة بياناتك فيعني ذلك ضرورة إصلاح التقسيم أو جمع مزيد من البيانات قبل التدريب. عند وجود مشكلات، تظهر تحت اسم التقسيم الخاص بها، مثل ! 000000000025.jpg: class 99 is not in names أو ! 000000000009.jpg: duplicate label rows.
دع النموذج يشير إلى الوسوم المشتبه بها#
الوسوم المفقودة هي أكثر الأخطاء ضررًا وأصعبها اكتشافًا بمجرد تصفح الصور. يستطيع نموذج مدرّب مسبقًا الإشارة إليها: فالاكتشاف الواثق الذي لا يقابله أي وسم في جواره إما وسم مفقود وإما خطأ من النموذج، وكلاهما يستحق المراجعة. يعمل هذا على COCO128:
from pathlib import Path
import torch
from ultralytics import YOLO
from ultralytics.data.utils import check_det_dataset, img2label_paths
from ultralytics.utils.metrics import box_iou
from ultralytics.utils.ops import xywh2xyxy
data = check_det_dataset("coco128.yaml") # swap in your data YAML
model = YOLO("yolo26n.pt") # a pretrained model, or your own first fine-tune
images = sorted(Path(data["train"]).glob("*.jpg"))
suspects = []
for result, label in zip(model(images, conf=0.6, stream=True, verbose=False), img2label_paths(map(str, images))):
rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
boxes = xywh2xyxy(torch.tensor([[float(v) for v in r[1:5]] for r in rows]).reshape(-1, 4))
iou = box_iou(result.boxes.xyxyn, boxes) # model boxes vs labeled boxes
for j, (cls, conf) in enumerate(zip(result.boxes.cls.int().tolist(), result.boxes.conf.tolist())):
if not rows or iou[j].max() < 0.3: # confident detection with no label anywhere near it
suspects.append((conf, Path(result.path).name, model.names[cls]))
print(f"{len(suspects)} confident detections with no matching label; top 5:")
for conf, name, cls in sorted(suspects, reverse=True)[:5]:
print(f" {conf:.2f} {cls:<12} {name}")4 confident detections with no matching label; top 5:
0.77 donut 000000000438.jpg
0.75 person 000000000531.jpg
0.71 donut 000000000438.jpg
0.66 bottle 000000000436.jpgمن أصل 128 صورة، يضيّق البرنامج النصي نطاق المراجعة إلى ثلاث صور. ويُظهر فتحها نوعَي النتائج:
000000000438.jpgصندوق يضم اثنتي عشرة قطعة دونات، حيث لا يتداخل اكتشافان واثقان مع أي قطعة دونات موسومة: وهما مرشحان لوسوم مفقودة أو موضوعة في غير مكانها.000000000531.jpgيرصد «شخصًا» تبيّن أنه ظل المصوّر على ملعب تنس. الوسم صحيح والنموذج مخطئ، وهذا بالضبط سبب مراجعة شخص لكل عنصر مشتبه به بدلًا من إضافته تلقائيًا.
خفّض قيمة conf لتوسيع نطاق البحث، واستبدله بأول نموذج ضبط دقيق خاص بك عند توفره، لأنه يعرف فئاتك.
اقرأ مخططات مجموعة البيانات#

في Ultralytics Platform، تعرض علامة التبويب Charts لمجموعة البيانات توزيع التقسيمات وأعداد الفئات وإحصاءات المربعات (وثائق مجموعة البيانات). تكشف لقطة الشاشة هذه بالفعل نتيجةً لضبط الجودة: تضم قائمة الفئات فئتين، لكن tablet لا يحتوي على أي وسم من أصل 1,755. لا يمكن تعلّم فئة بلا وسوم. إما أن تضع لها وسومًا، أو تحذفها من قائمة الفئات قبل التدريب.
عالج اختلال توازن الفئات بالترتيب الصحيح#
forklift ████████████████████████ 8000 instances
person ████████████████ 5400
pallet ██ 700 ← red flagيؤدي تفاوت كهذا إلى نموذج يثق باكتشاف الرافعات الشوكية، لكنه لا يرصد المنصات. هناك ثلاثة حلول، بالترتيب:
- اجمع مزيدًا من بيانات الفئة الأقل تمثيلًا وضع وسومًا لها. هذا هو الحل الأفضل دائمًا: استهدف ≥ 1,500 صورة و≥ 10,000 نموذج لكل فئة.
- زِد أخذ العينات منها في train فقط. أدرج الصور التي تحتوي على الفئة الأقل تمثيلًا أكثر من مرة في قائمة ملفات التدريب، لكي يراها النموذج بوتيرة أعلى في كل حقبة. لا تضع النسخ في val أو test.
- زِن الخسارة. ترفع وسيطة التدريب
cls_pw(تكون 0.0 افتراضيًا، وتصل إلى 1.0 عند تطبيق الترجيح الكامل عكسيًا بحسب التكرار) وزن خسارة التصنيف للفئات النادرة. لا تلجأ إليها إلا بعد تنفيذ الحلين 1 و2.
إزالة الصور الضبابية أو المزدحمة أو المربكة من مجموعة التحقق ترفع mAP ولا تحسّن النموذج، بل تجعله أقل نزاهة. إذا كانت الصورة إطارًا حقيقيًا من النشر، فأبقها؛ وأصلح تسمياتها بدلًا من ذلك. أزل فقط القيم الشاذة الحقيقية (الخطأ 9) والتكرارات التامة.
دوّن ملاحظات ضبط الجودة#
تكفي بضعة أسطر للإجابة عن سؤال «ما الذي تحققت منه؟» عندما يطرحه عميل أو مدقق بعد أشهر:
Dataset: forklift-v1 Audit date: 2026-05-09
Images: 4,820 Labels: 18,442 Background: 380 (7.9%)
Audit script: 3 invalid class IDs fixed, 41 duplicate label rows removed
Suspect review: 62 flagged, 19 missing labels added, 43 model errors ignored
Visual sample: 120 images, 4 mislabeled (3.3%), fixed
Split check: grouped by camera-day, every class in every split
Imbalance: pallet 2.5x below person; collecting more next round
Ready to train: yesعرض الحل
ملاحظات ضبط الجودة لعمليتي التشغيل التجريبيتين في هذا الدرس:
Dataset: COCO8 + COCO128 demo Audit date: 2026-05-09
qc_audit.py (COCO8): 0 invalid classes, 0 duplicate rows, 0 duplicate images
FINDING: dog, elephant, horse, person, umbrella appear in val only
ACTION: re-split by group or collect these classes into train before training
suspect_labels.py (COCO128, conf 0.6): 4 suspects in 3 images
000000000438.jpg: 2 donuts without matching labels -> relabel the donut box
000000000531.jpg: "person" is the photographer's shadow -> model error, label kept
000000000436.jpg: bottle -> open and decide
Ready to train: no, fix the val-only classes firstانتبه إلى السطر الأخير. تفشل مجموعة البيانات في ضبط الجودة إذا كانت إحدى الفئات موجودة في val فقط، حتى لو كان كل وسم منفرد صحيحًا؛ لأن المقياس الخاص بهذه الفئة يقيس شيئًا لم تتح للنموذج فرصة تعلّمه.