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

تقسيمات تكشف الحقيقة

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

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

النتيجة

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

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

  2. تحقق من وجود صور متطابقة تقريبًا بين التقسيمات قبل الوثوق بأي نتيجة تحقق.

  3. حدد مفتاح المجموعة: المقطع أو الكاميرا أو اليوم أو الشخص.

  4. اخلط المجموعات لا الصور، ثم تحقق من ظهور كل فئة في كل تقسيم.

  5. ثبّت مجموعة الاختبار ولا تستخدمها إلا مرة واحدة لكل تجربة مكتملة.

عملي

ثلاثة تقسيمات، وثلاث مهام#

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

كل قرار تتخذه بالاطلاع على أحد الأقسام يُسرّب جزءًا صغيرًا منه إلى النموذج. هذه هي وظيفة مجموعة التحقق. إذا ضبطت الإعدادات باستخدام مجموعة الاختبار، فلن يعود رقمها تقديرًا نزيهًا — فقد أفرطت في التكيّف مع اختياراتك. في Ultralytics، يحسب model.val(split="test") نتائج قسم الاختبار المحدد في ملف YAML لمجموعة البيانات.

اكتشف التسرّب في مجموعة بيانات حقيقية#

سُمّيت صور أقراص الدواء Frame_0.jpg وFrame_4.jpg وFrame_8.jpg — وهي إطارات من مقطع فيديو. هذه إشارة تحذير: الإطارات المتجاورة متطابقة تقريبًا، ويؤدي التقسيم العشوائي إلى وضعها في كلا القسمين. تحقّق من ذلك بمقارنة بصمات رمادية مصغّرة لكل صورة تحقق مع كل صورة تدريب:

from pathlib import Path

import numpy as np
from PIL import Image
from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("medical-pills.yaml")

def thumbnail(path):
    """A 32x18 grayscale fingerprint: enough to spot near-identical frames."""
    return np.asarray(Image.open(path).convert("L").resize((32, 18)), dtype=float)

train = {p.name: thumbnail(p) for p in sorted(Path(data["train"]).glob("*.jpg"))}
pairs = []
for p in sorted(Path(data["val"]).glob("*.jpg")):
    v = thumbnail(p)
    name, diff = min(((n, np.abs(v - t).mean()) for n, t in train.items()), key=lambda x: x[1])
    pairs.append((diff, p.name, name))

pairs.sort()
print(f"{sum(d < 5 for d, _, _ in pairs)} of {len(pairs)} val images have a near-twin in train (difference < 5)")
for diff, val_name, train_name in pairs[:3]:
    print(f"  {val_name} ~ {train_name}  difference {diff:.1f}")
17 of 23 val images have a near-twin in train (difference < 5)
  Frame_344.jpg ~ Frame_56.jpg  difference 0.3
  Frame_28.jpg ~ Frame_312.jpg  difference 0.5
  Frame_24.jpg ~ Frame_308.jpg  difference 1.8

يعني فرق قدره 0.3 على مقياس من 0 إلى 255 أن الصورتين متطابقتان عمليًا. افتح val/Frame_24.jpg وtrain/Frame_308.jpg جنبًا إلى جنب، وسترى عبوات الأقراص نفسها من الزاوية نفسها. من بين صور التحقق البالغ عددها 23، توجد صورة مطابقة في التدريب لـ17 صورة، لذا فإن درجة التحقق هنا تقيس في معظمها مدى تذكّر النموذج لصور سبق أن رآها. لا بأس بذلك لمجموعة بيانات تجريبية سريعة — لكنه بالضبط ما يجب ألا تطلقه.

مربع حوار إعادة توزيع الأقسام في منصة Ultralytics لمجموعة أقراص الدواء، مضبوطًا على 80% للتدريب و20% للتحقق و0% للاختبار

يوجد الفخ نفسه في أي أداة. يذكر الوصف الخاص بمربع الحوار هذا أنه يعيد تعيين كل صورة عشوائيًا، لذا فهو مثالي عندما تكون الصور لقطات مستقلة — لكنه يسبب تسرّبًا عندما تكون إطارات فيديو. أجرِ أولًا فحصًا للصور شبه المتطابقة، واحتفظ بنسبة للاختبار أكبر من 0% إذا أردت رقمًا نهائيًا لم يُضبط بناءً عليه.

قسّم حسب المجموعة#

القاعدة: يجب أن تكون الأقسام مستقلة على مستوى الوحدة التي ستكون جديدة عند النشر.

البياناتالتقسيم حسب
الفيديومقطع أو جلسة تسجيل
أسطول كاميرات ثابتالكاميرا أو الموقع
المشاهد التي تتغير بمرور الوقتاليوم أو الأسبوع
البيانات الطبية أو الرياضية أو بيانات الأشخاصالمريض أو الرياضي أو الشخص المعني
دفعات الصورحدث الالتقاط

غالبًا ما تحمل أسماء الملفات مفتاح المجموعة. ينشئ هذا المثال 900 صورة من 6 كاميرات ويقارن بين الاستراتيجيتين:

import random
from collections import defaultdict

# File names carry the unit that must never straddle splits: camera_day_frame.jpg
images = [f"cam{c:02d}_day{d}_{f:04d}.jpg" for c in range(1, 7) for d in range(1, 4) for f in range(50)]

# WRONG: shuffle individual frames, so near-identical neighbors land in both splits
random.seed(0)
shuffled = random.sample(images, len(images))
train, val = shuffled[: int(0.8 * len(images))], shuffled[int(0.8 * len(images)) :]
shared = {i.split("_")[0] for i in train} & {i.split("_")[0] for i in val}
print(f"per-image split: {len(shared)} of 6 cameras appear in both train and val")

# RIGHT: shuffle whole cameras, then expand them back into images
groups = defaultdict(list)
for image in images:
    groups[image.split("_")[0]].append(image)
cameras = random.sample(sorted(groups), len(groups))
train = [i for cam in cameras[:5] for i in groups[cam]]
val = [i for cam in cameras[5:] for i in groups[cam]]
print(f"per-camera split: train {len(train)} images, val {len(val)} images, val cameras {cameras[5:]}")
per-image split: 6 of 6 cameras appear in both train and val
per-camera split: train 750 images, val 150 images, val cameras ['cam05']

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

مواطن الخلل#

الخلط العشوائي للبيانات المترابطة

العَرَض: أداء التحقق أفضل بكثير من أول اختبار ميداني. الحل: اعثر على مفتاح المجموعة — وغالبًا ما يكون مخفيًا في أسماء الملفات أو بيانات التسجيل الوصفية — وقسّم البيانات بناءً عليه، ثم شغّل فحص التكرارات شبه المتطابقة للتأكد.

فئة غائبة عن التحقق

العَرَض: فئة نادرة بلا AP، أو قيمة AP مستندة إلى حالتين فقط. الحل: بعد التقسيم بحسب المجموعات، احسب عدد كل فئة في كل جزء، وبدّل مجموعات كاملة حتى تظهر كل فئة بنسب متقاربة. تؤتمت sklearn.model_selection.StratifiedGroupKFold هذه العملية.

الضبط على مجموعة الاختبار

العَرَض: ترتفع قيمة الاختبار تدريجيًا عبر التجارب. الحل: جمّد مجموعة الاختبار، واستخرجها من ظروف شبيهة بالإنتاج، وأبلغ عن نتائجها مرة واحدة لكل تجربة مكتملة. في مجموعات البيانات الصغيرة، احسب المتوسط عبر عدة تقسيمات باستخدام التحقق المتقاطع بـ k طيات بدلًا من إعادة استخدام تقسيم واحد.

يتناول تقسيم مجموعة البيانات على نحو صحيح النسب وفرض كل ذلك على مجموعة بيانات كاملة.

جرّبه

أجرِ فحص الصور شبه المتطابقة على مجموعة بياناتك (أو مجموعة أقراص الدواء). سجّل عدد صور التحقق التي لها صورة شبه مطابقة في التدريب. ثم حدّد مفتاح المجموعة، وأعِد التقسيم وفقًا له، وشغّل فحص التسرّب من الحل على قوائم الملفات الجديدة. والمطلوب هو عدد الصور شبه المطابقة قبل التقسيم وبعده، وأن يُظهر فحص التسرّب قائمة فارغة.

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

  • حدّدت مفتاح المجموعة وأعدت التقسيم وفقًا له.

  • يعرض فحص التسرّب قائمة فارغة للأقسام الجديدة.

عرض الحل

فحص تسرّب يعمل على أي قائمة بأسماء الملفات، إذا زُوّد بدالة تستخرج مفتاح المجموعة:

def leaked_groups(splits, key):
    """Return every group key that appears in more than one split."""
    owner, leaks = {}, set()
    for split, files in splits.items():
        for name in files:
            group = key(name)
            if owner.setdefault(group, split) != split:
                leaks.add(group)
    return sorted(leaks)

# ⁨استبدل هذه القوائم بأسماء ملفاتك، مثلاً [p.name for p in Path("images/train").glob("*.jpg")]⁩
splits = {
    "train": ["cam01_day1_0001.jpg", "cam01_day1_0002.jpg", "cam02_day1_0001.jpg"],
    "val": ["cam03_day2_0001.jpg", "cam01_day1_0003.jpg"],
    "test": ["cam04_day3_0001.jpg"],
}
print(leaked_groups(splits, key=lambda name: name.split("_")[0]))  # ⁨التجميع حسب الكاميرا⁩
['cam01']

يظهر cam01 في كل من التدريب والتحقق، لذا يسرّب هذا التقسيم البيانات. انقل كل صورة cam01 إلى قسم واحد، وأعِد التشغيل حتى يطبع []. أما في مجموعة أقراص الدواء، حيث تعود الإطارات من الفيديو الواحد إلى المنظر نفسه، فلا يوجد مفتاح مجموعة واضح في أسماء الملفات: إما أن تسجل مقاطع فيديو تحقق جديدة لعبوات مختلفة، أو أن تجمع الصور حسب بصمة التشابه الشديد وتخصص كل مجموعة كاملة لقسم واحد.

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

بعد إعداد تقسيمات نزيهة، ستقرأ لاحقًا المقاييس الناتجة عنها.

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