{"id":"l-ml-1-4","courseId":"ml-crash-course","lang":"ar","kind":"lesson","summary":"ابنِ خطَّي الأساس اللذين يجب أن يتفوّق عليهما كل نموذج، أي نموذج وهمي يتنبأ بالفئة الأكثر شيوعًا وقاعدة عمل من سطر واحد، واحكم على النموذج بمقدار تحسّنه عليهما لا برقمه المجرّد.","html":"<p>شجرتك بعمق 3 تتنبأ بالانقطاع بدقة 73% على عملاء لم ترَهم. قبل أن تعرض هذا الرقم على أحد، اطرح السؤال الذي يطرحه كل عالم بيانات متمرّس أولًا: مقارنةً بماذا؟ بلا مقارنة، 73% ليست جيدة ولا سيئة. إنها مجرد رقم.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">أغبى نموذج ممكن</h2>\n<p>خط الأساس الأول يتجاهل الـ features تمامًا. في التصنيف يتنبأ بالفئة الأكثر شيوعًا للجميع. وفي الانحدار يتنبأ بالمتوسط. يوفّر scikit-learn كليهما كـ estimators حقيقية، فيندمجان في الكود نفسه الذي تكتبه لأي نموذج.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"0\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.model_selection </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.dummy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> DummyClassifier</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">churn </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churn.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">features </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tenure_days\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total_spent\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"avg_order_value\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">            \"days_since_last_order\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"used_coupon\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"support_tickets\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mobile_share\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">X_train, X_test, y_train, y_test </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split(</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    churn[features], churn[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churned\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#953800;--shiki-dark:#FFA657\">test_size</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.25</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">random_state</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">42</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">    stratify</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">churn[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churned\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">dummy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> DummyClassifier(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">strategy</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"most_frequent\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).fit(X_train, y_train)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"dummy predicts:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, dummy.predict(X_test)[:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"dummy accuracy:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, dummy.score(X_test, y_test))</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>التنبؤ بـ “منقطع” لكل عميل يصيب في 55% من الحالات، لأن 55% من العملاء انقطعوا فعلًا. هذا هو الحدّ الأدنى. ودقة شجرتك البالغة 73% تزيد عليه بـ 18 نقطة، وهذا تقدّم حقيقي، لكنك لم تنتهِ من الأسئلة بعد.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>المقارنة برمي عملة</span></p>\n<p>كثيرًا ما يعامل الناس نسبة 50% على أنها خط الأساس لمسألة نعم/لا. وهي ليست كذلك ما لم تكن الفئات متوازنة. فإذا كانت 95% من المعاملات سليمة، فإن “نموذجًا” يقول “سليمة” في كل مرة يحقق accuracy بنسبة 95% ولا يلتقط أي احتيال. احسب دائمًا نتيجة الفئة الأكثر شيوعًا على بيانات اختبارك أنت، وكن حذرًا من أي accuracy قريبة منها.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">القاعدة التي كان سيكتبها شخص ما</h2>\n<p>خط الأساس الثاني هو الذي يؤلم. قبل تعلّم الآلة، كان أحدهم في Cartwheel سيكتب قاعدة: “إذا لم يطلب العميل شيئًا منذ N يومًا، فهو في طريقه إلى الانقطاع”. تختار N بالطريقة التي تدرّب بها نموذجًا: على بيانات التدريب وحدها.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"1\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.model_selection </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">churn </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churn.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">features </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tenure_days\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total_spent\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"avg_order_value\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">            \"days_since_last_order\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"used_coupon\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"support_tickets\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mobile_share\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">X_train, X_test, y_train, y_test </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split(</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    churn[features], churn[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churned\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#953800;--shiki-dark:#FFA657\">test_size</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.25</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">random_state</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">42</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">    stratify</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">churn[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churned\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> rule_accuracy</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(days, X, y):</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    predicted </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (X[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"days_since_last_order\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> days).astype(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">int</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (predicted </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> y).mean()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">candidates </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> range</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">30</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">301</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">10</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">best_days </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> max</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(candidates, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">key</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=lambda</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> d: rule_accuracy(d, X_train, y_train))</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"best cut-off on training data:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, best_days, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"days\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"rule accuracy on test data:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, rule_accuracy(best_days, X_test, y_test))</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>تحقق القاعدة 72% على بيانات الاختبار. وتحقق الشجرة 73%. على 100 عميل اختبار، هذا فرق شخص واحد.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 640 230\" role=\"img\" aria-labelledby=\"l-ml-1-4-t1\"><defs><marker id=\"arrow-l-ml-1-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-ml-1-4-t1\">مخطط أعمدة لدقة الاختبار: النموذج الوهمي للفئة الأكثر شيوعًا 55%، وقاعدة الـ 80 يومًا 72%، والشجرة بعمق 3 بنسبة 73%.</title>\n  <line class=\"d-line\" x1=\"190\" y1=\"20\" x2=\"190\" y2=\"190\"/>\n  <text class=\"d-label\" x=\"180\" y=\"55\" text-anchor=\"end\">النموذج الوهمي</text>\n  <rect class=\"d-box\" x=\"190\" y=\"34\" width=\"231\" height=\"32\" rx=\"4\"/>\n  <text class=\"d-label-strong\" x=\"431\" y=\"56\">55%</text>\n  <text class=\"d-label\" x=\"180\" y=\"110\" text-anchor=\"end\">قاعدة 80 يومًا</text>\n  <rect class=\"d-box-warn\" x=\"190\" y=\"89\" width=\"302\" height=\"32\" rx=\"4\"/>\n  <text class=\"d-label-strong\" x=\"502\" y=\"111\">72%</text>\n  <text class=\"d-label\" x=\"180\" y=\"165\" text-anchor=\"end\">شجرة بعمق 3</text>\n  <rect class=\"d-box-primary\" x=\"190\" y=\"144\" width=\"307\" height=\"32\" rx=\"4\"/>\n  <text class=\"d-label-strong\" x=\"507\" y=\"166\">73%</text>\n  <text class=\"d-label-muted\" x=\"190\" y=\"215\">طول العمود = الدقة (0% عند المحور، و100% عند 610)</text>\n</svg><figcaption>الـ accuracy على عملاء الاختبار المئة أنفسهم</figcaption></figure>\n<p>هذا ليس فشلًا. بل هو أنفع ما تعلّمته حتى الآن. فهو يخبرك أن معظم الإشارة الدالّة على الانقطاع تكمن في عمود واحد، هو حداثة آخر طلب، وأن شجرة بثمانية features لم تضف عليه حتى الآن شيئًا يُذكر. ويعطيك أيضًا هدفًا: من الآن فصاعدًا، على النموذج أن يتفوّق على 72%، لا على 55%، كي يستحق الإطلاق والشرح والمراقبة.</p>\n<aside class=\"callout callout-why\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>لماذا تهمّ قاعدة خط الأساس</span></p>\n<p>للنموذج تكاليف تشغيل: pipeline يغذّيه، وإعادة تدريب، ومراقبة، واجتماع في كل مرة يتصرّف فيها بغرابة. أما القاعدة فتكلّف سطرًا واحدًا من SQL. وحين يسألك أحد أصحاب القرار “لماذا لا نستخدم القاعدة فحسب؟”، تريد أن تكون قد قست الإجابة قبل أن يسأل.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">خطوط الأساس في الانحدار</h2>\n<p>المقابل في الانحدار هو <code dir=\"ltr\">DummyRegressor</code>، الذي يتنبأ بمتوسط التدريب لكل صف. وقيمة <code dir=\"ltr\">score</code> (أي R²) على بيانات الاختبار قريبة من 0، لأن R² معرَّف على أنه التحسّن مقارنةً بالتنبؤ بالمتوسط. ستستخدمه في القسم 2.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"2\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.datasets </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> load_diabetes</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.dummy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> DummyRegressor</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.model_selection </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">X, y </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> load_diabetes(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">return_X_y</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">True</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">as_frame</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">True</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">X_train, X_test, y_train, y_test </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> train_test_split(X, y, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">test_size</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.25</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">random_state</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">42</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">dummy </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> DummyRegressor(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">strategy</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mean\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).fit(X_train, y_train)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"predicts\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(dummy.predict(X_test)[</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"for everyone\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"R² on test:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(dummy.score(X_test, y_test), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<h2 id=\"sec-4\" tabindex=\"-1\">التحسّن لا النتيجة</h2>\n<p>من الآن فصاعدًا، اعرض النموذج بمقدار تحسّنه على خطوط الأساس: “accuracy بنسبة 73%، مقابل 55% للفئة الأكثر شيوعًا و72% لقاعدة الـ 80 يومًا”. هذه الجملة الواحدة تخبر القارئ أكثر مما يخبره أي رقم منفرد، وتحميك من خداع نفسك.</p>\n<p>أبقِ خطوط الأساس في كودك، بجانب النماذج، وأعد تشغيلها كلما تغيّرت البيانات أو التقسيم. فخط الأساس المحسوب مرة واحدة على تقسيم قديم ثم المقتبس إلى الأبد أسوأ من عدم وجوده، لأنه يبدو كأنه دليل. عمليًا، يجب أن يضع جدول المقارنة في آخر الـ notebook النموذجَ الوهمي في الأعلى، وأفضل قاعدة تحته، ثم كل النماذج بعد ذلك، وكلها محسوبة على صفوف الاختبار نفسها تمامًا.</p>\n<p>وكن صادقًا بشأن الضجيج أيضًا. مع 100 عميل اختبار، العميل الواحد يساوي نقطة مئوية واحدة، لذا قد تظهر فروق من نقطتين أو ثلاث أو تختفي مع خلط مختلف. سيستبدل القسم 4 التقسيمَ الواحد بالتحقق المتقاطع، الذي يخبرك كم تتذبذب النتيجة. وحتى ذلك الحين، عامِل الفجوات الصغيرة على أنها تعادل.</p>\n<p>والـ accuracy ليست الطريقة الوحيدة للمقارنة أيضًا. فالنموذج الذي يرتّب العملاء بحسب خطر انقطاعهم يستطيع أن يسلّم فريق التسويق أكثر 30 عميلًا مرجّحًا للانقطاع، وهذا ما لا تستطيعه قاعدة نعم/لا. يقيس القسم 3 ذلك بالضبط. لكن أولًا، يفتح القسم 2 أبسط نموذج على الإطلاق، الخط المستقيم، لترى ما معنى “التعلّم” بالأرقام.</p>\n","toc":[{"id":"sec-1","text":"أغبى نموذج ممكن","level":2},{"id":"sec-2","text":"القاعدة التي كان سيكتبها شخص ما","level":2},{"id":"sec-3","text":"خطوط الأساس في الانحدار","level":2},{"id":"sec-4","text":"التحسّن لا النتيجة","level":2}],"takeaways":["النتيجة لا تعني شيئًا حتى تقارنها بخط أساس (baseline) مبني على التقسيم نفسه.","يتنبأ <code dir=\"ltr\">DummyClassifier</code> بالفئة الأكثر شيوعًا ويتنبأ <code dir=\"ltr\">DummyRegressor</code> بالمتوسط؛ وهما يحدّدان الحدّ الأدنى.","القاعدة البسيطة المضبوطة على بيانات التدريب هي خط الأساس الذي يُبقيك صادقًا بشأن ما إذا كان النموذج يستحق كلفته.","حين تهيمن فئة واحدة، تكافئ الـ accuracy تجاهلَ الفئة النادرة؛ فقد تعني نتيجة 95% أن النموذج لم يجد شيئًا."],"further":[{"title":"Dummy estimators","url":"https://scikit-learn.org/stable/modules/model_evaluation.html#dummy-estimators"},{"title":"DummyClassifier","url":"https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyClassifier.html"}],"quiz":[{"q":"<p>نموذج لكشف الاحتيال يحقق accuracy بنسبة 97%. ونسبة المعاملات الاحتيالية 3%. ما أول ما يجب أن تتحقق منه؟</p>","options":[{"text":"هل يتفوّق النموذج على نموذج وهمي يتنبأ بـ “ليس احتيالًا” لكل شيء.","why":"صحيح. هذا النموذج الوهمي يحقق 97% أيضًا، فربما لم يتعلّم النموذج شيئًا عن الاحتيال."},{"text":"هل 97% أعلى من متوسط القطاع لنماذج كشف الاحتيال.","why":"أرقام الشركات الأخرى تأتي من بيانات أخرى وتوازن فئات مختلف. المقارنة المهمة هي على تقسيمك أنت."},{"text":"هل دقة التدريب أيضًا في حدود 97%.","why":"مقارنة التدريب بالاختبار تكشف الـ overfitting، لكن قد تكون كلتاهما 97% بينما لا يلتقط النموذج حالة احتيال واحدة."}],"answer":0},{"q":"<p>تستخدم قاعدة خط الأساس لديك حدًّا فاصلًا عند 80 يومًا. كيف كان يجب اختيار الرقم 80؟</p>","options":[{"text":"بتجربة عدة حدود والاحتفاظ بصاحب أفضل accuracy على بيانات الاختبار.","why":"هذا يضبط خط الأساس على بيانات الاختبار، فيبدو أفضل مما سيكون عليه مع بيانات جديدة، وهو الخطأ نفسه الذي يقع حين تضبط نموذجًا عليها."},{"text":"بتجربة عدة حدود على بيانات التدريب والاحتفاظ بالأفضل هناك.","why":"صحيح. يُعامَل خط الأساس معاملة النموذج نفسها؛ وبيانات الاختبار لا تقيس إلا النتيجة النهائية."},{"text":"لا يهم ذلك في خط الأساس، فخطوط الأساس لا تُطلق.","why":"القواعد كثيرًا ما تُطلق فعلًا، وخط الأساس المضبوط بشكل غير عادل يعطيك حكمًا خاطئًا حول ما إذا كان النموذج يستحق العناء."}],"answer":1},{"q":"<p>يحقق <code dir=\"ltr\">DummyRegressor()</code> على بيانات السكري قيمة R² قرابة 0 على بيانات الاختبار. لماذا؟</p>","options":[{"text":"لأن النموذج الوهمي فشل في التقارب.","why":"لا يوجد ما يتقارب. النموذج الوهمي يحسب رقمًا واحدًا هو متوسط التدريب."},{"text":"لأن هدف بيانات السكري لا نمط فيه.","why":"النموذج الوهمي لا ينظر إلى الـ features أبدًا، فنتيجته لا تقول شيئًا عن وجود نمط من عدمه."},{"text":"لأن R² يقيس التحسّن مقارنةً بالتنبؤ بالمتوسط، والنموذج الوهمي يتنبأ بالمتوسط.","why":"صحيح. قيمة R² للمتنبئ بالمتوسط تساوي 0 بحكم التعريف؛ ويظهر رقم سالب صغير لأن متوسط التدريب يختلف قليلًا عن متوسط الاختبار."}],"answer":2},{"q":"<p>شجرة تتفوّق على النموذج الوهمي بـ 18 نقطة، لكنها تتفوّق على قاعدة من سطر واحد بنقطة واحدة. ما الخطوة التالية الأكثر قابلية للدفاع عنها؟</p>","options":[{"text":"أطلق الشجرة، فنتيجتها هي الأعلى.","why":"نقطة واحدة على 100 عميل اختبار تعني عميلًا واحدًا. هذا ليس دليلًا على أن الشجرة أفضل، والقاعدة أسهل في التشغيل والشرح."},{"text":"أعلن أن الشجرة أفضل من خط الأساس بـ 18 نقطة.","why":"ذكر أضعف خط أساس وحده يُخفي أن خيارًا أبسط بكثير يؤدي بالمستوى نفسه. وهذه هي المبالغة التي يحذّر منها هذا الدرس."},{"text":"أبقِ القاعدة معيارًا للمقارنة، ولا تنتقل إلا حين يتفوّق عليها نموذج بوضوح وبشكل متكرّر.","why":"صحيح. التحقق المتقاطع في القسم 4 يخبرك ما إذا كانت الفجوة حقيقية أم ضجيجًا."}],"answer":2}],"exercise":{"kind":"python","files":["churn.csv"],"starter":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.dummy import DummyClassifier\nfrom sklearn.neighbors import KNeighborsClassifier\n\nchurn = pd.read_csv(\"churn.csv\")\nfeatures = [\"tenure_days\", \"orders\", \"total_spent\", \"avg_order_value\",\n            \"days_since_last_order\", \"used_coupon\", \"support_tickets\", \"mobile_share\"]\nX_train, X_test, y_train, y_test = train_test_split(\n    churn[features], churn[\"churned\"], test_size=0.25, random_state=42,\n    stratify=churn[\"churned\"])\n\n# 1. Majority-class baseline: test accuracy\ndummy_acc = None\n\n# 2. Rule baseline: churned if days_since_last_order > best_days.\n#    Pick best_days from range(30, 301, 10) using the TRAINING data only.\nbest_days = None\nrule_acc = None   # the rule's accuracy on the test data\n\n# 3. A k-nearest-neighbours model (default settings): test accuracy\nknn_acc = None\n","solution":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.dummy import DummyClassifier\nfrom sklearn.neighbors import KNeighborsClassifier\n\nchurn = pd.read_csv(\"churn.csv\")\nfeatures = [\"tenure_days\", \"orders\", \"total_spent\", \"avg_order_value\",\n            \"days_since_last_order\", \"used_coupon\", \"support_tickets\", \"mobile_share\"]\nX_train, X_test, y_train, y_test = train_test_split(\n    churn[features], churn[\"churned\"], test_size=0.25, random_state=42,\n    stratify=churn[\"churned\"])\n\ndummy_acc = DummyClassifier(strategy=\"most_frequent\").fit(X_train, y_train).score(X_test, y_test)\n\ndef rule_accuracy(days, X, y):\n    return ((X[\"days_since_last_order\"] > days).astype(int) == y).mean()\n\nbest_days = max(range(30, 301, 10), key=lambda d: rule_accuracy(d, X_train, y_train))\nrule_acc = rule_accuracy(best_days, X_test, y_test)\n\nknn_acc = KNeighborsClassifier().fit(X_train, y_train).score(X_test, y_test)\nprint(dummy_acc, best_days, rule_acc, knn_acc)\n","checks":[{"id":"dummy","test":"dummy_acc is not None and abs(float(dummy_acc) - 0.55) < 0.005"},{"id":"best-days","test":"best_days in range(30, 301, 10) and abs(((X_train['days_since_last_order'] > best_days).astype(int) == y_train).mean() - max(((X_train['days_since_last_order'] > d).astype(int) == y_train).mean() for d in range(30, 301, 10))) < 1e-9"},{"id":"rule-acc","test":"rule_acc is not None and abs(float(rule_acc) - ((X_test['days_since_last_order'] > best_days).astype(int) == y_test).mean()) < 1e-9"},{"id":"knn","test":"knn_acc is not None and abs(float(knn_acc) - 0.72) < 0.005"}],"title":"تفوّق على خطوط الأساس، أو لا","prompt":"<p>ضع نموذج أقرب k جيران بجانب خطَّي الأساس على التقسيم نفسه.</p>\n<ol>\n<li><code dir=\"ltr\">dummy_acc</code>: دقة نموذج <code dir=\"ltr\">DummyClassifier</code> الذي يتنبأ بالفئة الأكثر شيوعًا على بيانات الاختبار.</li>\n<li><code dir=\"ltr\">best_days</code>: الحدّ الفاصل من <code dir=\"ltr\">range(30, 301, 10)</code> الذي يعطي أفضل accuracy <strong>على بيانات التدريب</strong> للقاعدة “منقطع إذا كان <code dir=\"ltr\">days_since_last_order</code> &gt; الحدّ”. ثم <code dir=\"ltr\">rule_acc</code>: دقة هذه القاعدة على بيانات الاختبار.</li>\n<li><code dir=\"ltr\">knn_acc</code>: دقة <code dir=\"ltr\">KNeighborsClassifier()</code> بإعداداته الافتراضية على بيانات الاختبار.</li>\n</ol>\n","hints":["<code dir=\"ltr\">DummyClassifier(strategy=&quot;most_frequent&quot;).fit(X_train, y_train).score(X_test, y_test)</code>","اكتب دالة مساعدة <code dir=\"ltr\">rule_accuracy(days, X, y)</code> تقارن <code dir=\"ltr\">(X[&quot;days_since_last_order&quot;] &gt; days).astype(int)</code> بـ <code dir=\"ltr\">y</code> وتأخذ المتوسط.","<code dir=\"ltr\">max(range(30, 301, 10), key=lambda d: rule_accuracy(d, X_train, y_train))</code> تختار الحدّ الفاصل باستخدام بيانات التدريب وحدها."],"explanation":"<p>يحقق النموذج الوهمي 0.55، وقاعدة الـ 80 يومًا 0.72، ونموذج k-NN قيمة 0.72. أقرب k جيران، بثمانية features، لا يؤدي أفضل من سطر منطقي واحد على عمود واحد. وهذه نتيجة حقيقية: إما أن الـ features الأخرى لا تحمل إشارة إضافية تُذكر، وإما أن هذا النموذج لا يستطيع الاستفادة منها (فـ k-NN يتشوّه بالـ features ذات المقاييس المتباعدة جدًا، وستصلح ذلك في القسم 2). واختيار الحدّ الفاصل على بيانات التدريب يُبقي المقارنة عادلة.</p>\n","checkLabels":{"dummy":"dummy_acc هي دقة الفئة الأكثر شيوعًا على بيانات الاختبار","best-days":"best_days هو أفضل حدّ فاصل على بيانات التدريب","rule-acc":"rule_acc هي دقة تلك القاعدة على بيانات الاختبار","knn":"knn_acc هي دقة نموذج k-NN على بيانات الاختبار"}},"words":601,"readMinutes":4}