طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 303 · من الفكرة إلى المنتج (٣): بيانات وتنبّؤ
الدرس 5 · الوحدة 3: التقييم والقرار

المحطّة ٥: تقييم النموذج — الدقّة ومصفوفة الالتباس

قِس لا تظنّ: الدقّة ومصفوفة الالتباس، وأيّ خطأٍ يؤذي قرارك أكثر. ⏱ نحو 28 دقيقة.

صحيح-إيجابي2خاطئ-إيجابي0خاطئ-سلبي1صحيح-سلبي2تنبّؤ: متعثّرتنبّؤ: سليم
بعد هذا الدرس ستستطيع أن:
  • تقيس جودة النموذج بالمقارنة بالحقيقة لا بالظنّ.
  • تبني مصفوفة الالتباس وتميّز نوعي الخطأ.
  • تحسب الاستدعاء وتفهم متى يهمّ.
  • تربط اختيار المقياس بتبعات القرار.

١هل نموذجك جيّد؟ لا تصدّق، قِس

بنينا نموذجًا — لكن هل هو جيّد؟ السؤال لا يُجاب بالظنّ بل بالقياس: نقارن تنبّؤاته بالنتائج الحقيقيّة التي نعرفها (at_risk). أبسط مقياسٍ هو الدقّة: نسبة التنبّؤات الصحيحة.

main.py
students = [    {"name": "سعود", "grades": [70, 65, 80], "attendance": 0.9,  "at_risk": False},    {"name": "نورة", "grades": [55, 40, 60], "attendance": 0.6,  "at_risk": True},    {"name": "خالد", "grades": [90, 85, 88], "attendance": 0.95, "at_risk": False},    {"name": "ريم",  "grades": [50, 45],     "attendance": 0.5,  "at_risk": True},    {"name": "فهد",  "grades": [60, 62],     "attendance": 0.8,  "at_risk": True},] def predict(avg, attendance):    return avg < 60 or attendance < 0.70 for s in students:    avg = round(sum(s["grades"]) / len(s["grades"]), 1)    s["pred"] = predict(avg, s["attendance"]) correct = sum(1 for s in students if s["pred"] == s["at_risk"])print("صحيحة:", correct, "من", len(students))print("الدقّة:", round(correct / len(students), 2))
Shell×
>>> %Run main.py
صحيحة: 4 من 5
الدقّة: 0.8
>>>

٢لماذا لا تكفي الدقّة؟ مصفوفة الالتباس

الدقّة وحدها تخدع: قد تخطئ في النوع المهمّ من الخطأ. نفصّلها بأربع خاناتٍ — مصفوفة الالتباس — نعدّ فيها نوعي الصواب ونوعي الخطأ. «الإيجابيّ» هنا يعني «متعثّر»:

main.py
students = [    {"name": "سعود", "grades": [70, 65, 80], "attendance": 0.9,  "at_risk": False},    {"name": "نورة", "grades": [55, 40, 60], "attendance": 0.6,  "at_risk": True},    {"name": "خالد", "grades": [90, 85, 88], "attendance": 0.95, "at_risk": False},    {"name": "ريم",  "grades": [50, 45],     "attendance": 0.5,  "at_risk": True},    {"name": "فهد",  "grades": [60, 62],     "attendance": 0.8,  "at_risk": True},] def predict(avg, attendance):    return avg < 60 or attendance < 0.70 for s in students:    avg = round(sum(s["grades"]) / len(s["grades"]), 1)    s["pred"] = predict(avg, s["attendance"]) tp = fp = fn = tn = 0for s in students:    if s["pred"] and s["at_risk"]:        tp += 1   # تنبّأنا متعثّر وهو متعثّر    elif s["pred"] and not s["at_risk"]:  fp += 1   # تنبّأنا متعثّر وهو سليم (إنذار كاذب)    elif not s["pred"] and s["at_risk"]:  fn += 1   # قلنا سليم وهو متعثّر (فاتنا!)    else:                                 tn += 1   # تنبّأنا سليم وهو سليم print("صحيح-إيجابي TP:", tp)print("خاطئ-إيجابي FP:", fp)print("خاطئ-سلبي  FN:", fn)print("صحيح-سلبي  TN:", tn)
Shell×
>>> %Run main.py
صحيح-إيجابي TP: 2
خاطئ-إيجابي FP: 0
خاطئ-سلبي FN: 1
صحيح-سلبي TN: 2
>>>
صحيح-إيجابي2خاطئ-إيجابي0خاطئ-سلبي1صحيح-سلبي2تنبّؤ: متعثّرتنبّؤ: سليم

٣أيّ خطأٍ أخطر؟ يعتمد على القرار

في مشروعنا الخطآن ليسا سواءً:

الخطأمعناهأثره
خاطئ-سلبي FNطالبٌ متعثّرٌ ظنّه النموذج سليمًاالأخطر: فاتنا طالبٌ يحتاج دعمًا
خاطئ-إيجابي FPطالبٌ سليمٌ ظنّه متعثّرًاأخفّ: دعمٌ زائدٌ لمن لا يحتاجه

لذلك نهتمّ بـ الاستدعاء (Recall = TP/(TP+FN)): كم نسبة المتعثّرين الحقيقيّين التقطهم النموذج. نموذجنا فوّت «فهد» (FN=1)، فاستدعاؤه ليس تامًّا — إشارةٌ إلى أنّ قاعدتنا قد تحتاج تحسينًا (خفض عتبة الدرجة مثلًا).

توقّع وتحقّق

ما الدقّة (Accuracy)؟

«قلنا سليم وهو متعثّر» هو:

في التنبّؤ بالتعثّر، أيّ خطأٍ أخطر عادةً؟

الاستدعاء Recall يقيس:

TP/(TP+FN).

الخلاصة

تحدٍّ برمجي: تقييم النموذج

متوسّط

قيّم نموذجك: احسب الدقّة ومصفوفة الالتباس الأربع، ثمّ الاستدعاء. قارن التنبّؤ بالنتيجة الحقيقيّة at_risk.

المطلوب:
  • احسب الدقّة = الصحيحة / الكلّ (مدوّرةً لخانتين).
  • احسب tp, fp, fn, tn بمقارنة pred بـ at_risk.
  • اطبع الدقّة: ... ثم الخانات الأربع.
  • اطبع الاستدعاء: ... = tp / (tp + fn) مدوّرًا لخانتين.

ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:

main.py
students = [    {"name": "سعود", "grades": [70, 65, 80], "attendance": 0.9,  "at_risk": False},    {"name": "نورة", "grades": [55, 40, 60], "attendance": 0.6,  "at_risk": True},    {"name": "خالد", "grades": [90, 85, 88], "attendance": 0.95, "at_risk": False},    {"name": "ريم",  "grades": [50, 45],     "attendance": 0.5,  "at_risk": True},    {"name": "فهد",  "grades": [60, 62],     "attendance": 0.8,  "at_risk": True},] def predict(avg, attendance):    return avg < 60 or attendance < 0.70 for s in students:    avg = round(sum(s["grades"]) / len(s["grades"]), 1)    s["pred"] = predict(avg, s["attendance"]) # أكمل: احسب الدقّة والمصفوفة والاستدعاء واطبعها
المطلوب×
الدقّة: 0.8
TP: 2 FP: 0 FN: 1 TN: 2
الاستدعاء: 0.67
💡 تلميح 1
الدقّة: sum(1 for s in students if s["pred"] == s["at_risk"]) / len(students).
💡 تلميح 2
صنّف كل طالبٍ في خانةٍ واحدةٍ بشرطي pred وat_risk، ثم Recall = tp/(tp+fn).

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

main.py
students = [    {"name": "سعود", "grades": [70, 65, 80], "attendance": 0.9,  "at_risk": False},    {"name": "نورة", "grades": [55, 40, 60], "attendance": 0.6,  "at_risk": True},    {"name": "خالد", "grades": [90, 85, 88], "attendance": 0.95, "at_risk": False},    {"name": "ريم",  "grades": [50, 45],     "attendance": 0.5,  "at_risk": True},    {"name": "فهد",  "grades": [60, 62],     "attendance": 0.8,  "at_risk": True},] def predict(avg, attendance):    return avg < 60 or attendance < 0.70 for s in students:    avg = round(sum(s["grades"]) / len(s["grades"]), 1)    s["pred"] = predict(avg, s["attendance"]) correct = sum(1 for s in students if s["pred"] == s["at_risk"])print("الدقّة:", round(correct / len(students), 2)) tp = fp = fn = tn = 0for s in students:    if s["pred"] and s["at_risk"]:        tp += 1    elif s["pred"] and not s["at_risk"]:  fp += 1    elif not s["pred"] and s["at_risk"]:  fn += 1    else:                                 tn += 1 print("TP:", tp, "FP:", fp, "FN:", fn, "TN:", tn)print("الاستدعاء:", round(tp / (tp + fn), 2))

🐞 لاحظت خطأً في هذا الدرس؟ أبلغني بالبريد

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس