مراقبة الصحة والدقة
اطّلع على مقاييس نقاط النهاية وإحصاءات التنبؤ في Platform، وأرسل تنبيهًا من فحص صحة مجدول، واكتشف انحراف الدقة من خلال مهمة اختبار مجموعة محجوبة.
قد تعيد نقطة النهاية الحالة 200 OK لكل طلب، بينما تنخفض دقتها بمقدار 5 نقاط لأن العاملين في النوبة الليلية خفّضوا الإضاءة. توضح Platform أداء كل نقطة نهاية، لكنها لا ترسل تنبيهات إلى أحد، ولا تستطيع قياس الدقة من دون تسميات. ينشئ هذا الدرس الفحصين المجدولين اللذين يسدان هذه الفجوة، ويجرّب تنبيه الدقة باستخدام محاكاة لنوبة ليلية.
عملي
ما تعرضه Platform#

| المؤشر | المكان | مدة الاحتفاظ |
|---|---|---|
| طلبات HTTP، ومعدل الأخطاء (4xx و5xx)، وزمن الاستجابة P95 | بطاقات في صفحة النشر وعلامة التبويب Deployments، تُحدّث كل دقيقة | تغطي البطاقات 24 ساعة؛ وتتيح API بيانات من ساعة واحدة إلى 30 يومًا |
| الصحة | بطاقة Endpoint في علامة التبويب Overview | مباشرة؛ وتُعاد المحاولة تلقائيًا ما دامت غير سليمة |
| السجلات | علامة التبويب Logs: أحدث 20 سجلًا، مع عامل التصفية Errors | تعيد API ما يصل إلى 200 سجل لكل طلب، مع دعم تقسيم النتائج إلى صفحات |
| إحصاءات التنبؤ وأمثلة مؤقتة (ما يصل إلى 100 صورة) | علامة التبويب Monitoring | ذاكرة مثيل الخدمة فقط |

هناك ملاحظتان أساسيتان تؤثران في كل ما يلي. لا تُجمع المقاييس إلا عندما تكون نقطة النهاية Ready، وينتهي الوصول إلى السجل عند حذف عملية النشر. وتُفقد إحصاءات وأمثلة علامة التبويب Monitoring كلما توقفت نقطة النهاية أو أُعيد تشغيلها أو غُيّر حجمها أو استُبدل نموذجها: فهي عرض مباشر وليست أرشيفًا.
مثال عملي: فحص مجدول لنقطة النهاية#
تحوّل Platform بطاقة معدل الأخطاء إلى اللون الأحمر عندما يتجاوز 5%، ويمكن لـSlack الإعلان عن جاهزية عملية نشر أو إخفاقها في بدء التشغيل، لكن لا توجد تنبيهات عند تجاوز الحدود. شغّل هذا الفحص كل 5 دقائق باستخدام cron أو CI أو وكيل المراقبة، وأرسل تنبيهًا عند الخروج برمز غير صفري:
from ultralytics_platform import Platform
OWNER, DEPLOYMENT = "your-username", "yolo26n-iowa" # from the deployment page URL
P95_BUDGET_MS, MAX_ERROR_RATE = 300, 1.0 # errorRate is a percentage
with Platform() as client: # reads ULTRALYTICS_API_KEY
health = client.deployments.health(OWNER, DEPLOYMENT)
summary = client.deployments.metrics(OWNER, DEPLOYMENT, range="1h", view="overview")["summary"]
print(
f"healthy={health['healthy']} requests={summary['totalRequests']} "
f"p95={summary['p95LatencyMs']:.0f} ms errors={summary['errorRate']:.2f}%"
)
if not health["healthy"] or summary["p95LatencyMs"] > P95_BUDGET_MS or summary["errorRate"] > MAX_ERROR_RATE:
for entry in client.deployments.logs(OWNER, DEPLOYMENT, severity="ERROR,WARNING", limit=5)["entries"]:
print(entry["timestamp"], entry["severity"], entry["message"][:120])
raise SystemExit(1) # non-zero exit lets cron, CI, or your pager treat it as an alertعند تشغيله مقابل API بديلة تُرجع نقطة نهاية سليمة، يطبع الفحص سطرًا واحدًا وينتهي برمز الخروج 0؛ أما مع P95_BUDGET_MS = 100، فتنتهي البيانات نفسها برمز الخروج 1:
healthy=True requests=1200 p95=180 ms errors=0.40%healthيفحص نقطة النهاية ويعيدhealthyوstatusوlatencyMs. كما يوقظ نقطة نهاية متوسعة إلى الصفر، لذا يمكن أن يكون الجدول كل 5 دقائق بمثابة فحص إبقاء الخدمة دافئة.metrics(..., view="overview")يعيدtotalRequestsوerrorRate(نسبة مئوية) وp95LatencyMsللنطاق.logsيجلب أحدث الأخطاء والتحذيرات، لتصل التنبيهات ومعها أول مؤشر على المشكلة.
ثبّت SDK باستخدام pip install -U ultralytics-platform (يتطلب SDK إصدار Python 3.11 أو أحدث). أرسل التنبيهات عند استمرار تجاوز الحدود فقط، مثل فشل ثلاثة فحوص متتالية، وأرفق بكل تنبيه إدخالًا من سطر واحد في دليل التشغيل.
مثال عملي: تتبّع الدقة باستخدام مجموعة محجوبة#
لا تكشف مقاييس الطلبات الدقة، لكن المجموعة المحجوبة تكشفها: استخدم 200–500 صورة تحاكي بيانات الإنتاج، مع تسميات دقيقة، ضمن إصدار خاص بها من مجموعة بيانات منفصلة، ولم يسبق استخدامها في التدريب. قيّم نقطة التحقق المنشورة أسبوعيًا، وأضف النتائج إلى ملف سجل:
import json
import sys
from datetime import date
from pathlib import Path
from ultralytics import YOLO
DEPLOYED = "pills-v1.pt" # the exact best.pt behind the endpoint
HOLDOUT = sys.argv[1] if len(sys.argv) > 1 else "medical-pills.yaml" # labeled, never trained on
metrics = YOLO(DEPLOYED).val(data=HOLDOUT, split="val", plots=False, verbose=False)
history = Path("holdout_history.jsonl")
rows = [json.loads(line) for line in history.read_text().splitlines()] if history.exists() else []
row = {"date": date.today().isoformat(), "data": HOLDOUT, "map50_95": round(float(metrics.box.map), 4)}
history.write_text("".join(json.dumps(r) + "\n" for r in [*rows, row]))
baseline = rows[0]["map50_95"] if rows else row["map50_95"]
drop = baseline - row["map50_95"]
print(f"holdout mAP50-95 {row['map50_95']:.3f} (baseline {baseline:.3f}, drop {drop:+.3f})")
if drop > 0.01: # more than 1 mAP point below the deployment-day baseline
raise SystemExit("ALERT: holdout accuracy regressed - open the retraining runbook")يسجل التشغيل الأول خط الأساس ليوم النشر. ثم جرّب التنبيه قبل الحاجة إليه: ينشئ هذا البرنامج النصي نسخة «نوبة ليلية» من المجموعة المحجوبة، بالتسميات والصور نفسها لكن بسطوع قدره 15%:
import shutil
from pathlib import Path
import cv2
from ultralytics.data.utils import check_det_dataset
src = Path(check_det_dataset("medical-pills.yaml")["path"])
dst = Path("pills-night")
(dst / "images/val").mkdir(parents=True, exist_ok=True)
for img in (src / "images/val").glob("*.jpg"): # darken every holdout image to mimic a dim night shift
cv2.imwrite(str(dst / "images/val" / img.name), (cv2.imread(str(img)) * 0.15).astype("uint8"))
shutil.copytree(src / "labels/val", dst / "labels/val", dirs_exist_ok=True) # same labels, darker pixels
Path("pills-night.yaml").write_text(f"path: {dst.resolve()}\ntrain: images/val\nval: images/val\nnames:\n 0: pill\n")
print("wrote pills-night.yaml")$ python holdout_check.py
holdout mAP50-95 0.659 (baseline 0.659, drop +0.000)
$ python simulate_night_shift.py
wrote pills-night.yaml
$ python holdout_check.py pills-night.yaml
holdout mAP50-95 0.609 (baseline 0.659, drop +0.050)
ALERT: holdout accuracy regressed - open the retraining runbookستظل كل الطلبات أثناء النوبة الليلية تُرجع 200 خلال أجزاء من الثانية، رغم أن النموذج فقد 5 نقاط mAP. وحدها المجموعة المحجوبة تكشف ذلك. حدّث المجموعة المحجوبة كل ربع سنة بصور إنتاج جديدة ذات تسميات، وأعد ضبط خط الأساس كلما نشرت نموذجًا جديدًا. يغطي دليل مراقبة النماذج ودرس قابلية المراقبة والانحراف اختبارات الانحراف الإحصائي.
قراءة علامة التبويب Monitoring لاكتشاف الانحراف#

دوّن شكل المخططات خلال أسبوع النشر، ثم قارنها لاحقًا. يقيس مستوى الثقة اليقين لا الصحة، لذا ادرس هذه المخططات إلى جانب المجموعة المحجوبة:
| النمط | السبب المرجح | الإجراء |
|---|---|---|
| ينخفض مستوى الثقة بينما تحافظ المجموعة المحجوبة على أدائها | تحول طبيعي في المجال | راقب الوضع؛ واحفظ الأمثلة لإصدار مجموعة البيانات التالي |
| ينخفض مستوى الثقة وأداء المجموعة المحجوبة | تراجع حقيقي في الدقة | أعِد التدريب (الدرس 10) |
| تغيّر مفاجئ في أحجام الصناديق أو مواقعها | تحركت الكاميرا، أو تغيّرت العدسة أو الموقع | تحقق من الكاميرا قبل المساس بالنموذج |
| انخفضت التنبؤات لكل صورة مع ثبات مستوى الثقة | الكاميرا غير متصلة، أو المشهد أقل نشاطًا، أو يوجد خلل في العميل | تحرَّ عن المشكلة في المراحل السابقة |
| ارتفع زمن الاستجابة P95 مع ثبات زمن الاستدلال | معالجة الطلبات أو حجم الحمولة أو السعة | المشكلة في البنية التحتية، لا في النموذج |
إعادة صور الإنتاج إلى النظام#
في علامة التبويب Monitoring، حدّد الأمثلة وانقر على Save to dataset؛ فستمر عبر مسار الاستيعاب المعتاد مع تنبؤاتها، وستحتفظ في بياناتها الوصفية بـdeploymentId والمنطقة. وينفذ قالب Collect Uncertain Detections في Agents الأمر نفسه لمجموعة بيانات كاملة، إذ يجري التنبؤ باستخدام عملية النشر وينسخ الصور التي تتضمن اكتشافات ضمن نطاق ثقة محدد إلى مجموعة مراجعة.

مواطن الخلل#
العَرَض: تختفي الأمثلة التي كنت تنوي تسميتها بعد تغيير الحجم أو تبديل النموذج. الحل: تُخزَّن هذه الأمثلة في ذاكرة نسخة الخدمة. احفظها في مجموعة بيانات وانتظر اكتمال استيعابها قبل إيقاف النموذج أو تغيير حجمه أو استبداله.
العَرَض: يظهر تنبيه مجموعة التحقق المستبعدة مباشرةً بعد نشر نموذج أفضل، أو لا يظهر مجددًا أبدًا. الحل: خط الأساس هو الصف الأول في السجل. ابدأ holdout_history.jsonl جديدًا (أو سجّل صفًا جديدًا لخط الأساس) في كل مرة تستبدل فيها النموذج.
العَرَض: يرتفع معدل الأخطاء أثناء تدفق مفاجئ لحركة المرور، بينما تظل حالة الصحة خضراء. الحل: تُحتسب استجابات 429 أخطاءً، لكنها تعني «بلغت السعة القصوى». تحقّق من السجلات؛ فإذا كانت الاستجابات 429، وزّع حركة المرور على مزيد من الأطراف النهائية أو استخدم حجمًا مخصصًا أكبر.
عرض الحل
الإجابة العملية لنقطة نهاية الأقراص: إدخال cron هو */5 * * * * python endpoint_check.py || ./page-oncall.sh مع P95_BUDGET_MS = 300 وMAX_ERROR_RATE = 1.0؛ يؤدي ضبط الميزانية على 100 ms مقابل P95 فعلي يبلغ 180 ms إلى الخروج برمز 1 وإرسال تنبيه، لذا يعمل الربط كما ينبغي. تُشغّل مهمة CI الأسبوعية holdout_check.py باستخدام DEPLOYED = "pills-v2.pt" على إصدار مجموعة التحقق الخاصة المحجوزة المكوّنة من 300 صورة: خط الأساس 0.69. يخفض اختبار المناوبة الليلية النتيجة بمقدار 0.05 ويطبع سطر ALERT مع رمز الخروج 1، ما يؤدي إلى فشل مهمة CI وإشعار القناة. تُرسل اثنتا عشرة عينة من المراقبة خلال المناوبة المسائية إلى pill-review عبر Save to dataset، وتُوسم كل عينة بـ deploymentId وeurope-west1.