الوحدة 5 · ما بعد الأساسيات
تعلّم الآلة المسؤول: الانحياز والإنصاف والانجراف
20 دقراءة 4 دتمرين Python
الدرس في لمحة
افحص أخطاء النموذج مجموعةً مجموعة، واختر معيار إنصاف يطابق الضرر، وراقب المدخلات والتنبؤات والنتائج كي تلاحظ حين يبتعد العالم عن بيانات التدريب.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
بلغ نموذجك البطل ROC AUC على الاختبار قدره 0.81. هذا رقم واحد لـ 100 عميل من ثماني دول وثلاث شرائح. ومن الممكن تمامًا أن يكون النموذج جيدًا في المتوسط وضعيفًا لمجموعة واحدة، وأن تكون تلك المجموعة هي الأقل قدرة على الشكوى. ومن الممكن أيضًا أن يكون نموذج كان جيدًا في أكتوبر مخطئًا بصمت في مارس. يغطي هذا الدرس الأمرين: الإنصاف (fairness) قبل الإطلاق، والانجراف (drift) بعده.
من أين يأتي الانحياز
نادرًا ما يكون الانحياز (bias) في النموذج سطرًا من الكود. إنه يصل مع البيانات ومع طريقة استخدام التنبؤات:
- الانحياز التاريخي: الـ labels تسجّل قرارات سابقة، لا الحقيقة. والنموذج المدرَّب على من حصل على الموافقة يتعلّم من حصل على الموافقة، بما في ذلك أي تحيّز في تلك الموافقات.
- التمثيل: المجموعات الصغيرة تعطي النموذج ما يتعلّمه أقل. لدى Cartwheel 17 عميلًا أردنيًا في الجدول كله؛ وأي نموذج سيكون أقل موثوقية لهم.
- البدائل (proxies): حذف عمود حسّاس لا يحذف معلوماته. فرسوم الشحن وأزمنة التوصيل والعملة قد تحلّ محل الدولة.
- الاستخدام: الدرجة نفسها تعني أشياء مختلفة بحسب الإجراء. علامة انقطاع تطلق خصمًا تفيد العميل المُعلَّم؛ وعلامة تطلق تجميد الائتمان تضرّه.
فحص النموذج مجموعةً مجموعة
لتفحص بإنصاف تحتاج إلى تنبؤات لعملاء كثيرين لم يتدرّب عليهم النموذج. تعطي cross_val_predict كل صف تنبؤًا خارج الطيّة: فكل عميل يُقيَّم بنموذج تدرّب على الطيّات الأخرى.
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
"days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
model = make_pipeline(StandardScaler(), LogisticRegression())
prob = cross_val_predict(model, churn[features], churn["churned"], cv=cv, method="predict_proba")[:, 1]
audit = churn.assign(flagged=(prob >= 0.5).astype(int))
def group_report(g):
return pd.Series({
"customers": len(g),
"churn_rate": g["churned"].mean(),
"flag_rate": g["flagged"].mean(),
"recall": recall_score(g["churned"], g["flagged"]),
"precision": precision_score(g["churned"], g["flagged"]),
})
print(audit.groupby("segment")[["churned", "flagged"]].apply(group_report).round(2))
print(audit.groupby("country")[["churned", "flagged"]].apply(group_report).round(2))اقرأ عبر الأعمدة:
- نسبة العملاء المُعلَّمين (flag rate) أو نسبة الاختيار: كم مرة تحصل كل مجموعة على العرض.
- الـ Recall: من منقطعي كل مجموعة، كم منهم يلتقط النموذج. وهي هنا حصة العملاء المعرّضين للخطر الذين يحصلون على المساعدة.
- الـ Precision: من العملاء المُعلَّمين في كل مجموعة، كم منهم كان راحلًا فعلًا. والـ precision المنخفضة تعني خصومات تذهب إلى أشخاص كانوا سيبقون.
تتراوح الـ recall بين 0.71 (كندا والإمارات والمملكة المتحدة) و0.82 (مصر والأردن). والـ precision للشركات الصغيرة 0.66 مقابل 0.78 للمستهلكين الأفراد. بعض الفجوات ضجيج: فأرقام الأردن تقوم على 17 عميلًا، فيحرّكها عميل واحد بعدة نقاط. وبعضها يستحق نظرة أدقّ مع نمو البيانات. ليس الغرض من الفحص ختم نجاح/رسوب؛ بل معرفة أين يكون النموذج أضعف، كي تقرّر ما إذا كان ذلك مقبولًا.
اختيار معنى “المنصف”
هناك عدة تعريفات معقولة، وحين تختلف النِّسب الأساسية بين المجموعات (تنقطع مصر بنسبة 67%، وألمانيا بنسبة 45%) لا يمكن أن تتحقق كلها معًا:
- تساوي نِسب الاختيار (demographic parity): كل مجموعة تُعلَّم بالنسبة نفسها.
- تساوي الـ recall (equal opportunity): المنقطعون الحقيقيون في كل مجموعة يُلتقطون بالنسبة نفسها.
- تساوي الـ precision: العلامة تعني الخطر نفسه في كل مجموعة.
اختر بأن تسأل من يتضرّر من أيّ خطأ. في عروض الاسترجاع، الضرر هو منقطع لا يصله العرض أبدًا، فتساوي الـ recall هو الهدف الطبيعي. وفي نموذج يجمّد الحسابات، الضرر هو الإيجابي الكاذب، فستنظر إلى معدّلات الإيجابيات الكاذبة بدلًا من ذلك. دوّن اختيارك؛ فهو قرار تجاري وأخلاقي، لا خيار تقني افتراضي.
بعد الإطلاق: الانجراف
تعلّم النموذج من العملاء كما كانوا في 30 سبتمبر 2025. والعالم يتحرّك. انجراف البيانات (data drift) تغيّر في المدخلات: تطبيق جوّال جديد يحرّك mobile_share، وإضراب شركة شحن يمطّ days_since_last_order. وانجراف المفهوم (concept drift) تغيّر في العلاقة نفسها: برنامج ولاء لدى منافس يجعل حتى العملاء الحديثين يرحلون. وللانقطاع تأخّر في الـ labels: فلا تعرف ما إذا كان التنبؤ صحيحًا إلا بعد 90 يومًا.
يمكن فحص انجراف المدخلات كل يوم، دون labels، بمقارنة القيم الحديثة لكل feature ببيانات التدريب. واختبار Kolmogorov–Smirnov للعيّنتين من SciPy بداية بسيطة:
import numpy as np
import pandas as pd
from scipy.stats import ks_2samp
churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
"days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
training = churn[features]
# Simulate next quarter: a courier problem delays orders and a new app shifts shopping to mobile
rng = np.random.default_rng(7)
recent = training.sample(150, random_state=1).copy()
recent["days_since_last_order"] += rng.integers(20, 60, len(recent))
recent["mobile_share"] = (recent["mobile_share"] + 0.3).clip(0, 1)
for col in features:
stat, p = ks_2samp(training[col], recent[col])
flag = "DRIFT" if p < 0.01 else ""
print(f"{col:22} KS {stat:.2f} p={p:.3g} {flag}")يضيء العمودان اللذان تغيّرا؛ ولا يضيء الباقي. في بيئة الإنتاج ستشغّل هذا وفق جدول زمني، وتطلق تنبيهًا على الانجراف المستمر لا على يوم سيئ واحد، وتقرنه بنسبة العملاء المُعلَّمين في كل مجموعة، ثم بالـ recall والـ precision لكل مجموعة حين تصل النتائج. وحين ينخفض الأداء الحقيقي، أعد التدريب على بيانات حديثة وأعد إجراء فحص هذا الدرس قبل إعادة الإطلاق.
المراقبة تفترض أن النموذج يعمل في مكان ما. يحزم الدرس الأخير الـ pipeline البطل، ويضعه خلف API، ويغلق الحلقة.
أهم النقاط
- يدخل الانحياز عبر البيانات (labels تاريخية، ومجموعات ممثّلة تمثيلًا ناقصًا، وfeatures بديلة) وعبر طريقة استخدام التنبؤات؛ والنتيجة الإجمالية الجيدة قد تُخفي نتيجة سيئة لمجموعة واحدة.
- افحص بمقاييس لكل مجموعة: نسبة الاختيار، والـ recall، والـ precision، محسوبة على تنبؤات خارج الطيّة (out-of-fold) أو على بيانات الاختبار.
- تعريفات الإنصاف مثل تساوي نِسب الاختيار وتساوي الـ recall وتساوي الـ precision لا يمكن عادةً أن تتحقق كلها معًا؛ فاختر التعريف الذي يطابق من يتضرّر من أيّ خطأ.
- حذف العمود الحسّاس لا يزيل الانحياز، لأن features أخرى تعمل كبدائل عنه؛ فأبقِه متاحًا للفحص.
- راقب ثلاثة أشياء بعد الإطلاق: توزيعات المدخلات (data drift)، وتوزيع التنبؤات، والأداء الحقيقي حين تصل النتائج.