الوحدة 9 — المراقبة: انحراف البيانات والمفهوم
نموذج التسرّب في الإنتاج منذ أسبوعين. كلّ شيء يعمل تقنيًّا: الخدمة تستجيب، لا أخطاء 500. لكنّ فريق التسويق يشتكي: التوقّعات تبدو خاطئة، الحملات لا تؤتي ثمارها. ما الذي فاتنا؟ الجواب في المراقبة: لا مراقبة، لا مشكلة مرئيّة، ثم فجأة كارثة صامتة.
نوعان مختلفان من الانحراف
نُميّز نوعين لا يجب الخلط بينهما:
- انحراف البيانات (data drift): توزيع مُدخلات النموذج تغيّر. مثال: 60 بالمائة من المشتركين الجدد في مارس تحت 30 سنة، مقابل 35 بالمائة وقت التدريب. الميزات نفسها، توزيعها مختلف.
- انحراف المفهوم (concept drift): العلاقة بين المدخلات والهدف تغيّرت. مثال: قبل ستّة أشهر، مشتركو الباقة الرخيصة كانوا الأكثر تسرّبًا. اليوم، منافس أطلق عرضًا يستهدف الباقة الغالية، فأصبح مشتركو الباقة الغالية هم الأكثر تسرّبًا. المتغيّرات نفسها بنفس القيم، لكنّ استنتاج النموذج عليها خاطئ الآن.
انحراف البيانات تُكشَف مبكّرًا وتلقائيًّا لأنّها تحدث في المدخلات، وهذه متوفّرة لحظيًّا. انحراف المفهوم تُكشَف متأخّرًا لأنّها تحتاج ملاحظة الهدف الحقيقي (هل تسرّب المشترك فعلًا؟)، وهذا لا يُعرف قبل أسابيع.
اختبارات إحصائية لكلّ متغيّر
الطريقة القياسية لكشف انحراف البيانات: نقارن توزيع كلّ متغيّر في نافذة حديثة مع مرجع (بيانات التدريب أو نافذة قديمة مستقرّة). أدوات الاختبار الشائعة:
- Kolmogorov-Smirnov للمتغيّرات المستمرّة: يقارن الدالّتين التراكميّتين ويُنتج قيمة p.
- Chi-squared للمتغيّرات الاسميّة: يقارن الترددات الفعلية بالمتوقّعة.
- PSI (Population Stability Index): مقياس عمليّ في الصناعة المصرفيّة. قيمة تحت 0,1 مستقرّ، بين 0,1 و0,25 تنبّه، فوق 0,25 انحراف صريح.
- Wasserstein distance: مسافة قابلة للتفسير («كم يجب أن أزحف كتلة» من توزيع إلى آخر).
مثال بسيط:
from scipy.stats import ks_2samp
for col in numeric_cols:
stat, p_value = ks_2samp(reference[col], recent[col])
if p_value < 0.01:
alert(f"انحراف على {col}: p = {p_value:.4f}")
القاعدة: لا تختبر إلّا المتغيّرات المهمّة. اختبار مئة متغيّر بمستوى ثقة 99 بالمائة يُنتج تنبيهًا خاطئًا كلّ نهار (نصف عشوائيًّا). ركّز على العشرين متغيّرًا الأكثر أهمّية في النموذج.
انحراف الهدف: مراقبة أعمق
المتغيّرات المُدخَلة ليست كلّ شيء. المتغيّر الأكثر إخبارًا هو الهدف نفسه، حين نستطيع ملاحظته. في التسرّب: نسبة المشتركين الذين تسرّبوا فعلًا في الشهر الماضي.
إن ارتفعت هذه النسبة من 3 بالمائة إلى 8 بالمائة، هذا لا يعني أنّ النموذج «سيّئ»، بل أنّ العالم تغيّر. مؤشّرات النموذج نفسه (AUC، Precision، Recall) على البيانات الحديثة تُقاس بمقارنة توقّعاته بالحقيقة ، وتَكشف انحراف المفهوم مباشرة.
مشكلة عملية: الحقيقة تصل متأخّرة. لا نعرف إن كان مشترك قد تسرّب فعلًا إلّا بعد شهر. المراقبة على مقاييس النموذج تعمل بتأخير، وقرارات الترقية بناءً عليها كذلك.
التنبيه المُفيد ضدّ الضوضاء
خطأ شائع: تنبيهات كثيرة تُصبح ضجيجًا. مبدأ توجيهي: كلّ تنبيه يجب أن يُنشئ عملًا واضحًا لدى مَن يستقبله. تنبيه «PSI الميزة X = 0,12» بلا سياق ولا خطوة تالية سيُتجاهل بعد أسبوع.
نصائح عملية:
- مستوى ثقة عالٍ قبل الإطلاق: p < 0,001، وليس 0,05. الأخير يعطي ضجيجًا.
- حجم الحدث المكتشف مهم: انحراف إحصائي مؤكّد لكنّه صغير جدًّا (0,2 بالمائة تغيّر في المتوسّط) ليس دليلًا كافيًا للتنبيه.
- نافذة مقارنة معقولة: مقارنة يوم واحد بيوم واحد تُنتج تنبيهات موسمية. مقارنة أسبوع مع أسبوع، أو استعمال يوم من الأسبوع نفسه، تُلغي هذا الضجيج.
- تصعيد متدرّج: إشعار خفيف أوّلًا، ثمّ تصعيد إذا استمرّ الانحراف عبر عدّة نوافذ.
تقارير Evidently
مكتبة Evidently (مفتوحة المصدر) تُقدّم اختبارات مسبَقة الصنع، تقارير HTML قابلة للتشارك، وإرسال المقاييس إلى Prometheus أو Grafana:
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset, TargetDriftPreset
report = Report(metrics=[DataDriftPreset(), TargetDriftPreset()])
report.run(reference_data=reference, current_data=current)
report.save_html("drift_report.html")