انتقل إلى المحتوى الرئيسي

الوحدة 8 — المقاييس: الاستدعاء وNDCG والتغطية والتنوّع

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

RMSE: المقياس الذي لا يقيس ما يهمّ

الجذر التربيعيّ لمتوسّط مربّع الخطأ RMSE كان مقياس جائزة Netflix. طوّرت الصناعة كلّها عليه بين 2006 و2009. ثمّ اكتشف الجميع في الإنتاج أنّه ينحرف عن التجربة الفعليّة.

المشكلة: RMSE يزن كلّ خطأ متساويًا. خطأ من 3.2 إلى 3.5 يعادل خطأ من 4.7 إلى 5.0. لكنّ الأخير أهمّ بكثير للمستخدم: الفرق بين توصية «رابعة أفضل خمسة» و«خامسة أفضل خمسة» يُغيّر ترتيب القائمة، وقد يُغيّر النقرة أو الشراء. الفرق بين تقييم 3.2 و3.5 لا يُغيّر شيئًا: العنصر لن يظهر في العشرة الأوائل في الحالتَين.

النتيجة: نموذج يخفض RMSE بشكل ملحوظ قد لا يُحسّن تجربة المستخدم على الإطلاق، وقد يسوءها. الصناعة تخلّت عن RMSE في التقييم النهائيّ منذ 2015 تقريبًا.

الدقّة والاستدعاء عند k

نبدأ من مقاييس التصنيف الكلاسيكيّة، مكيَّفة للترتيب على قائمة من الأعلى k. لكلّ مستخدم:

Precision@k=ذات صلةأعلى kk\text{Precision@k} = \frac{|\text{ذات صلة} \cap \text{أعلى k}|}{k} Recall@k=ذات صلةأعلى kذات صلة\text{Recall@k} = \frac{|\text{ذات صلة} \cap \text{أعلى k}|}{|\text{ذات صلة}|}

«ذات صلة» يعرّف عمليًّا بحسب المهمّة: العناصر التي قيّمها المستخدم بأربع نجوم أو خمس، أو أتمّها، أو نقر عليها لاحقًا.

الاستدعاء عند k هو المقياس الأكثر استعمالًا في التوصية. K عمليّة عادةً 10 (ما يظهر في الصفحة الرئيسيّة).

MAP: متوسّط الدقّة المتوسّطة

Precision@k لا تُميّز بين العنصر ذي الصلة في الترتيب الأوّل والعنصر ذي الصلة في الترتيب العاشر. من الناحية الفعليّة، الأوّل أهمّ بكثير: المستخدم لا يمرّر إلى الأسفل بالضرورة.

AP (Average Precision) يُصلح هذا:

AP(u)=1Iu+k=1KPrecision@k(u)1[ik ذو صلة]\text{AP}(u) = \frac{1}{|I_u^+|}\sum_{k=1}^{K} \text{Precision@k}(u) \cdot \mathbb{1}[i_k \text{ ذو صلة}]

نُجمع Precision@k عند كلّ موضع فيه عنصر ذو صلة، ونُقسّم على عدد العناصر ذات الصلة. عنصر ذو صلة في الموضع 1 يُعطي Precision عالية (1.0)، وعنصر ذو صلة في الموضع 10 يُعطي Precision أضعف. MAP متوسّط AP على كلّ المستخدمين.

NDCG: القياس المرجعيّ

Discounted Cumulative Gain يُعمّم AP إلى تقييمات ذات درجات (لا مجرّد ثنائيّة صلة / لا صلة). لكلّ موضع k في القائمة:

DCG@k=i=1k2reli1log2(i+1)\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{\text{rel}_i} - 1}{\log_2(i+1)}

reli\text{rel}_i هي درجة الصلة (0 للا شيء، 1 لنجرة، 2 لنقرة، 3 لاشتراك، مثلًا). المقام log2(i+1)\log_2(i+1) يعاقب المواضع البعيدة: عنصر في الموضع الأوّل يُعطي 1.0 على مقام log2(2)=1، وفي الموضع العاشر log2(11)≈3.46، فتنخفض مساهمته إلى ثلثه.

NDCG@k يُعيّر بقسمة على الترتيب المثاليّ:

NDCG@k=DCG@kIDCG@k\text{NDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

IDCG@k\text{IDCG@k} قيمة DCG لو رتّبنا الأمثل. النتيجة بين 0 و1: 1 يعني أنّ نموذجنا يعطي الترتيب المثاليّ للعناصر ذات الصلة.

حساب NDCG يدويًّا

الشرح النظريّ لا يُغني عن الحساب اليدويّ في مثال ملموس.

القائمة التي أعطى النموذج: A B C D E. درجات الصلة الحقيقيّة: A=3, B=0, C=2, D=1, E=0.

الحساب:

  • الموضع 1 (A، rel=3): (2^3 - 1) / log2(2) = 7 / 1 = 7.
  • الموضع 2 (B، rel=0): 0.
  • الموضع 3 (C، rel=2): (2^2 - 1) / log2(4) = 3 / 2 = 1.5.
  • الموضع 4 (D، rel=1): (2^1 - 1) / log2(5) ≈ 1 / 2.32 ≈ 0.431.
  • الموضع 5 (E، rel=0): 0.
  • DCG@5 ≈ 8.931.

الترتيب المثاليّ: A(3) C(2) D(1) B(0) E(0).

  • 7 + 3/log2(3) + 1/log2(4) = 7 + 1.893 + 0.5 = 9.393.
  • IDCG@5 ≈ 9.393.

NDCG@5 = 8.931 / 9.393 ≈ 0.951.

الحساب اليدويّ يُظهر بوضوح لماذا NDCG يُعاقب سيّئ الترتيب فقط: كلّ نقلة عنصر ذي صلة نحو الأسفل تُخفض القيمة.

import numpy as np

def dcg_at_k(reponses_relevance, k):
reponses = reponses_relevance[:k]
gains = (2.0 ** np.asarray(reponses)) - 1.0
discounts = np.log2(np.arange(2, len(gains) + 2))
return float((gains / discounts).sum())

def ndcg_at_k(reponses_relevance, k):
ideal = sorted(reponses_relevance, reverse=True)
idcg = dcg_at_k(ideal, k)
if idcg == 0:
return 0.0
return dcg_at_k(reponses_relevance, k) / idcg

التغطية والتنوّع والجدّة

مقاييس الترتيب لا تكفي. نموذج يُدنّي تركيزه على عشرة عناصر شعبيّة قد يفوز في الاستدعاء وNDCG لكنّه يخنق كاتالوغ المنصّة.

تغطية الكاتالوغ (catalog coverage): نسبة العناصر التي ظهرت في توصية واحدة على الأقلّ عبر كلّ المستخدمين.

Coverage=uR(u)I\text{Coverage} = \frac{|\bigcup_u R(u)|}{|I|}

قيمة صحّيّة تفوق 30% عادةً. أقلّ من 10% يعني أنّ الكاتالوغ نصف مشلول.

التنوّع داخل قائمة: متوسّط تباعد العناصر داخل توصية واحدة، عادةً 1 ناقص متوسّط التشابه الكوسينوسي.

Diversity(R)=12R(R1)i<jsim(i,j)\text{Diversity}(R) = 1 - \frac{2}{|R|(|R|-1)}\sum_{i<j} \text{sim}(i,j)

قائمة كلّها دورات Docker عليها تنوّع منخفض؛ قائمة تخلط Docker وSQL وTensorFlow تنوّع أعلى.

الجدّة (novelty): متوسّط سالب لوغاريتم شعبيّة كلّ عنصر مقترح. جدّة عالية تعني أنّ النظام يعرض عناصر غير شعبيّة عادةً، أي يفتح آفاق الاكتشاف.

Serendipity: المفاجأة المرغوبة

المقياس الأصعب في القياس والأعقد في المفهوم. Serendipity تعني: عنصر يُوصَى به، لم يكن المستخدم يتوقّع أن يُعجب به، ثمّ أعجبه فعلًا. عمليًّا، تُعرَّف بأنّها العناصر التي:

  1. ليست في العناصر الشعبيّة (بديهيّة).
  2. ليست الأقرب إلى بروفيل المستخدم (متوقّعة).
  3. تلقّت تفاعلًا إيجابيًّا.

قياسها يتطلّب اختبارًا حيًّا. لا يوجد مقياس هوفلاين متّفق عليه. Netflix أعلنت في 2018 أنّها تُدرِج Serendipity ضمن مؤشّرات الجودة الرئيسيّة.

التقييم الهوفلاين: القاعدة الذهبيّة

عند حساب أيّ من هذه المقاييس، القاعدة القاطعة: التقسيم الزمنيّ. تدريب على البيانات قبل تاريخ T، تقييم على البيانات بعده. تقسيم عشوائيّ يُنتج تسريبًا: النموذج يعرف تفاعلات مستقبل «المستخدم أ» ليتوقّع ماضيه. الأرقام تتضخّم بشكل ملحوظ، ولا تنعكس في الإنتاج.

نعود إلى التقسيم الزمنيّ بالتفصيل في الوحدة العاشرة.

الخلاصة

  • RMSE مقياس مضلّل في التوصية لأنّه يزن كلّ الأخطاء متساوية، بينما ما يهمّ تجربة المستخدم هو ترتيب أعلى العناصر.
  • الاستدعاء@k وMAP وNDCG@k مقاييس الترتيب المعياريّة؛ NDCG الأدقّ لأنّه يعاقب المواضع البعيدة ويقبل درجات صلة مدرَّجة.
  • التغطية والتنوّع والجدّة ضروريّة لكشف انحياز النظام نحو الشعبيّ، ونقص التنوّع الذي يبتلع الكاتالوغ.
  • التقسيم الزمنيّ شرط لا تنازل عنه لحساب هذه المقاييس؛ التقسيم العشوائيّ يُنتج أرقامًا وهميّة.

الوحدة التالية: التغذية الراجعة الضمنيّة وتحيّز الموضع، وكيف نصحّح خطأ ينمو مع كلّ نقرة.