قابلية الملاحظة والانحراف
سجّل المقاييس التي تكشف التراجع الصامت في الدقة، واختبر توزيعات الثقة للكشف عن الانحراف، ولا تطلق التنبيهات إلا عند ظهور مؤشرات تستدعي إجراءً.
قد تكون خدمة الرؤية عاملة وسريعة، لكنها تعطي نتائج خاطئة. قد تتحرك الكاميرا من موضعها، أو تتغير الإضاءة، أو يصل خط منتجات جديد، بينما يواصل النموذج إرجاع الصناديق وتظل جميع لوحات معلومات البنية التحتية خضراء. في هذا الدرس، أدى تعتيم مقطع لمحاكاة وقت الغسق إلى تغيير متوسط الثقة بمقدار 0.01 فقط، وهو تغير لا يظهر على الرسم البياني، لكن اختبار KS ثنائي العينة كشفه عند p = 0.007. ستبني هذا الاختبار وآلية التسجيل التي تمدّه بالبيانات.
عملي
ثلاث طبقات من المؤشرات#
| الطبقة | السؤال | المؤشرات |
|---|---|---|
| البنية التحتية | هل الخدمة تعمل وتواكب سرعة الإدخال؟ | زمن الاستجابة، ومعدل المعالجة، والإطارات المُسقطة، واستخدام CPU وGPU |
| سلوك النموذج | هل يتصرف النموذج كما كان الأسبوع الماضي؟ | عمليات الكشف لكل إطار، ومتوسط الثقة، ومزيج الفئات، وأحجام الصناديق |
| دقة النموذج | هل ما زال النموذج يعطي نتائج صحيحة؟ | mAP لمجموعة التحقق (مقاييس الأداء)، ومراجعة بشرية لعينات، ومعدل الإنذارات الكاذبة |
تغطي معظم الفرق الطبقة الأولى بأدوات قياسية وتتوقف عندها. أما الطبقتان الثانية والثالثة فهما موضع الإخفاق الصامت لأنظمة الرؤية؛ ويغطي دليل مراقبة النماذج دورة الصيانة كاملة.

إذا نشرت عبر Ultralytics Platform، فستوفر لك صفحة النشر طبقة البنية التحتية (الطلبات، ومعدل الأخطاء، وزمن الاستجابة P95) وسجلات الطلبات التي يمكنك تصفيتها لإظهار الأخطاء، كما هو موضح أعلاه؛ ويتناول درس مراقبة الصحة والدقة هذه الجوانب. أما طبقتا السلوك والدقة فعليك بناؤهما بنفسك، وهذا ما يفعله بقية هذا الدرس.
إصدار المقاييس لكل إطار#
يحتوي كل كائن Results على توقيتات وصناديق. اكتب سطر JSON واحدًا لكل إطار وأرسله إلى نظام المقاييس الذي تستخدمه:
import json
import time
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
with open("metrics.jsonl", "w") as log:
for r in model.predict(video, stream=True, verbose=False):
conf = r.boxes.conf
record = {
"ts": time.time(),
"camera": "lobby-1",
"inference_ms": round(r.speed["inference"], 1),
"detections": len(r.boxes),
"mean_conf": round(conf.mean().item(), 3) if len(conf) else None,
"confs": [round(c, 3) for c in conf.tolist()], # القيم الخام لاختبار الانحراف
}
log.write(json.dumps(record) + "\n")
print({k: v for k, v in record.items() if k != "confs"}){'ts': 1792003254.33, 'camera': 'lobby-1', 'inference_ms': 34.7, 'detections': 22, 'mean_conf': 0.543}الغرض من كل حقل:
- inference_ms: أطلق تنبيهًا عندما تتجاوز قيمة p95 الحد المسموح به.
- detections: يشير الانخفاض المفاجئ إلى ما يقارب الصفر عادةً إلى وجود كاميرا محجوبة أو معطّلة؛ أما الارتفاع الحاد فيشير إلى تغيّر المشهد أو إيجابيات كاذبة.
- mean_conf: خط اتجاه سريع، لكنه تقريبي أكثر من اللازم لاكتشاف الانحراف بمفرده.
- confs: التوزيع الكامل الذي يحتاج إليه اختبار الانحراف أدناه.
اختبر انحراف توزيعات الثقة#
يظهر انحراف البيانات في قيم الثقة قبل وقت طويل من تصنيف أي شخص لبيانات جديدة. اجمع قيم الثقة عند الإطلاق لتكون خط أساس، واجمع نافذة حديثة، ثم قارن التوزيعين باختبار كولموغوروف-سميرنوف ثنائي العينة (pip install scipy). يحاكي هذا البرنامج النصي وقت الغسق بتعتيم المقطع نفسه:
import cv2
import numpy as np
from scipy.stats import ks_2samp
from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download
model = YOLO("yolo26n.pt")
video = str(safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"))
def confidences(darken=1.0):
"""Collect every detection confidence from the clip, optionally darkened to simulate dusk."""
cap, out = cv2.VideoCapture(video), []
while True:
ok, frame = cap.read()
if not ok:
break
out += model((frame * darken).astype(np.uint8), verbose=False)[0].boxes.conf.tolist()
return np.array(out)
baseline = confidences() # what "normal" looked like at launch
for name, recent in (("same scene", confidences()), ("dusk (35% brightness)", confidences(darken=0.35))):
stat, p = ks_2samp(baseline, recent)
print(f"{name:22s} n={len(recent):4d} mean_conf={recent.mean():.3f} KS={stat:.3f} p={p:.2g}")same scene n=1376 mean_conf=0.519 KS=0.000 p=1
dusk (35% brightness) n=1337 mean_conf=0.508 KS=0.064 p=0.0068اقرأها سطرًا سطرًا:
- المشهد نفسه: تعطي التوزيعات المتطابقة إحصائية KS تساوي 0 وp = 1، وهذا هو الاختبار الضابط.
- الغسق: 39 اكتشافًا أقل ومتوسط أقل بمقدار 0.011 فقط، وهو فرق لن يلاحظه أحد على رسم بياني. تبلغ إحصائية KS (أكبر فجوة بين التوزيعين التراكميين) 0.064، وتشير قيمة p = 0.0068 < 0.01 إلى أن التحول حقيقي.
تنبيه الانحراف سببٌ لفحص إطارات عيّنة، وليس دليلًا على الفشل. عندما يكون الانحراف حقيقيًا، اجمع إطارات من الكاميرات المتأثرة وصنّفها: يوضح دليل جمع البيانات وتعليقها ما ينبغي جمعه، ويوضح دليل الضبط الدقيق كيفية إعادة التدريب. وتُعد أحجام الصناديق (العرض × الارتفاع) مؤشرًا ثانيًا بسيطًا للانحراف: إذ تظهر تغييرات الحوامل والعدسات فيها أولًا.
تحقّق من مجموعة اختبار محجوبة أسبوعيًا#
تشير مؤشرات السلوك إلى حدوث تغيير؛ لكن التسميات وحدها تكشف ما إذا كانت الدقة قد تراجعت. احتفظ بمجموعة اختبار محجوبة تضم 200–500 صورة إنتاجية مصنفة، وتحقق من كل إصدار للنموذج، وكذلك كل أسبوع:
yolo val model=yolo26n.pt data=coco8.yaml > weekly_metrics.txt # use your production model and holdout YAMLسجّل mAP50-95 وAP لكل فئة بمرور الوقت، وأطلق تنبيهًا عندما ينخفض mAP بأكثر من نقطة واحدة مقارنةً بالأسبوع السابق. السبب الأكثر شيوعًا هو استبدال نموذج كان من المفترض ألا يغيّر شيئًا؛ والسبب الثاني هو الانحراف. ويُظهر مصفوفة الالتباس على مجموعة الاختبار المحجوبة نفسها حالات التراجع لكل فئة بوضوح.

تجيب مجموعة الاختبار المحجوبة عن السؤال المطروح في هذا الرسم البياني. أعد التدريب عندما تُظهر المجموعة بدء تراجع الجودة، أو عندما تتوفر لديك بيانات جديدة مصنفة كافية من الظروف التي شهدت انحرافًا، لا وفق جدول زمني ثابت.
أطلق التنبيهات بناءً على الإجراءات، لا الأرقام#
| المؤشر | متى يُطلق التنبيه | الإجراء |
|---|---|---|
p95 inference_ms | يتجاوز الحد المسموح به لأكثر من 3 دقائق | وسّع نطاق الموارد أو انتقل إلى التصدير المحسّن |
| عدد الاكتشافات لكل إطار | انخفاض بنسبة 50% أو أكثر لمدة 5 دقائق | تحقق من بث الكاميرا، ثم من إصدار النموذج |
| الإطارات المفقودة | تتجاوز نسبتها 5% لمدة 10 دقائق | أضف سعة أو خفّض FPS لكل بث |
| اختبار KS للثقة | p < 0.01 في النوافذ اليومية | راجع الإطارات، ثم شغّل التحقق على مجموعة الاختبار المحجوبة |
| mAP50-95 لمجموعة الاختبار المحجوبة | انخفاض بأكثر من نقطة واحدة مقارنةً بالأسبوع السابق | اجمع البيانات وصنّفها واضبط النموذج بدقة |
مواطن الخلل#
العَرَض: يُطلق اختبار KS إنذارًا كل مساء وكل يوم اثنين. الحل: الدورات اليومية والأسبوعية تغيرات حقيقية في التوزيع. قارن حالات متماثلة (الكاميرات نفسها، والساعات نفسها، ويوم الأسبوع نفسه)، واستخدم نوافذ تضم آلاف عمليات الكشف.
العَرَض: تبدو قيمة mAP لمجموعة الاختبار المحجوبة جيدة، بينما يبلغ المستخدمون عن حالات إخفاق. الحل: يجب أن تماثل مجموعة الاختبار المحجوبة بيئة الإنتاج الحالية. حدّثها كل ربع سنة بإطارات موسومة حديثًا من الكاميرات المهمة.
يجب أن يتضمن كل تنبيه إجراءً تاليًا مكتوبًا. أما الإشارة التي تريد مراقبتها فحسب، فمكانها لوحة المعلومات لا نظام التنبيه.
عرض الحل
احتفظ بأول نافذة سليمة من مخرجات monitor.py بوصفها baseline.jsonl، ثم قارن بها كل نافذة جديدة:
import json
import numpy as np
from scipy.stats import ks_2samp
def load(path):
"""Flatten the per-frame 'confs' lists written by monitor.py."""
with open(path) as f:
return np.array([c for line in f for c in json.loads(line)["confs"]])
baseline, recent = load("baseline.jsonl"), load("metrics.jsonl")
stat, p = ks_2samp(baseline, recent)
print(f"KS={stat:.3f} p={p:.2g} -> {'review frames' if p < 0.01 else 'ok'}")KS=0.000 p=1 -> okهذه هي النتيجة المتوقعة عندما يأتي الملفان من اللقطات نفسها. شغّل الاختبار يوميًا باستخدام مجدول، وأرسل الإحصائية وقيمة p إلى نظام المقاييس نفسه المستخدم لسجلات كل إطار، ووجّه review frames إلى شخص، لا إلى نظام النداء.