{"id":"l-ml-1-2","courseId":"ml-crash-course","lang":"ar","kind":"lesson","summary":"حوّل جدول انقطاع العملاء إلى مصفوفة features اسمها X وهدف اسمه y، ثم قسّمه بـ train_test_split لتقيس النموذج على عملاء لم يرَهم قط.","html":"<p>النموذج الذي يحقق 99% إما جيد جدًا وإما رأى الإجابات. ولا يمكنك معرفة أيّهما من الرقم وحده. أنفع عادة في تعلّم الآلة هي أن تضع جانبًا بيانات لا يراها النموذج أثناء التدريب، كي يكون لنتيجته عليها معنى.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">سير العمل من البداية إلى النهاية</h2>\n<p>كل مشروع تعلّم موجَّه ستعمل عليه يتبع الحلقة نفسها بشكل عام. الخطوات مملّة عن قصد؛ وتخطّي إحداها هو الطريقة التي تفشل بها المشاريع.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 200\" role=\"img\" aria-labelledby=\"l-ml-1-2-t1\"><defs><marker id=\"arrow-l-ml-1-2-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-2-t1\">سبع خطوات في حلقة: صياغة السؤال، وجمع بيانات معروفة الإجابة، والتقسيم، وخط الأساس، والتدريب، والتقييم، ثم الإطلاق والمراقبة، مع سهم من التقييم عائد إلى التدريب للتكرار.</title>\n  <rect class=\"d-box-primary\" x=\"10\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"53\" y=\"63\" text-anchor=\"middle\">الصياغة</text>\n  <rect class=\"d-box\" x=\"108\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"151\" y=\"63\" text-anchor=\"middle\">الجمع</text>\n  <rect class=\"d-box-warn\" x=\"206\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"249\" y=\"63\" text-anchor=\"middle\">التقسيم</text>\n  <rect class=\"d-box\" x=\"304\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"347\" y=\"63\" text-anchor=\"middle\">Baseline</text>\n  <rect class=\"d-box\" x=\"402\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"445\" y=\"63\" text-anchor=\"middle\">التدريب</text>\n  <rect class=\"d-box\" x=\"500\" y=\"30\" width=\"86\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"543\" y=\"63\" text-anchor=\"middle\">التقييم</text>\n  <rect class=\"d-box-success\" x=\"598\" y=\"30\" width=\"92\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"644\" y=\"63\" text-anchor=\"middle\">الإطلاق</text>\n  <path class=\"d-arrow\" d=\"M96 58 L106 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M194 58 L204 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M292 58 L302 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M390 58 L400 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M488 58 L498 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M586 58 L596 58\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M543 90 C543 150 445 150 445 92\" marker-end=\"url(#arrow-l-ml-1-2-1)\"/>\n  <text class=\"d-label-muted\" x=\"494\" y=\"165\" text-anchor=\"middle\">كرّر</text>\n</svg><figcaption>سير عمل التعلّم الموجَّه</figcaption></figure>\n<p><strong>صُغ</strong> السؤال بدقة: ما الذي نتنبأ به، ولمن، وفي أيّ لحظة، وما الإجراء الذي سيتبع التنبؤ. بالنسبة إلى Cartwheel: “في 30 سبتمبر، لكل عميل طلب من قبل، تنبّأ بما إذا كان سيطلب خلال الـ 90 يومًا القادمة، كي يوجّه فريق التسويق عروض الاسترجاع”. <strong>اجمع</strong> أمثلة معروفة الإجابة: وهذا هو <code dir=\"ltr\">churn.csv</code>. <strong>اقتطع</strong> جزءًا للاختبار. ابنِ <strong>خط أساس (baseline)</strong> (بعد درسين). <strong>درّب</strong> النماذج، و<strong>قيّمها</strong>، وكرّر، وعندها فقط <strong>أطلق</strong> النموذج وواصل مراقبته.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ Features والهدف</h2>\n<p>ينتظر scikit-learn شيئين: مصفوفة features اسمها <code dir=\"ltr\">X</code>، فيها صف لكل مثال وعمود لكل feature، وهدفًا <code dir=\"ltr\">y</code> فيه إجابة واحدة لكل صف. الـ DataFrame من pandas يصلح ليكون <code dir=\"ltr\">X</code>، والـ Series يصلح ليكون <code dir=\"ltr\">y</code>.</p>\n<p>ستستخدم الآن الأعمدة الرقمية الثمانية. أما الأعمدة النصية مثل <code dir=\"ltr\">country</code> فتحتاج إلى ترميز أولًا (القسم 4 يتولّى ذلك)، و<code dir=\"ltr\">avg_satisfaction</code> فيه فجوات لا تقبلها بعض النماذج بعد.</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>\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 </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> churn[features]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">y </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 style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(X.shape, y.shape)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(y.value_counts())</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>يبقى <code dir=\"ltr\">customer_id</code> خارجًا. فالمعرّف اسم اعتباطي، ومع ذلك قد يجد نموذج مرن تقسيمات عليه تطابق صفوف التدريب بالمصادفة. وأي شيء لن يكون له معنى عند شخص يشرح سبب انقطاع العملاء لا ينبغي أن يكون feature.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">حجز بيانات الاختبار</h2>\n<p>تخلط <code dir=\"ltr\">train_test_split</code> الصفوف ثم تقسمها إلى جزأين. تدرّب على أحدهما وتقيس على الآخر.</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, y </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><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>\n<span class=\"line\"></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\">    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 style=\"color:#953800;--shiki-dark:#FFA657\">stratify</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">y</span></span>\n<span class=\"line\"><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\">\"train:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, X_train.shape, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churn rate\"</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\">(y_train.mean(), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</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\">\"test: \"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, X_test.shape, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"churn rate\"</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\">(y_test.mean(), </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=\"1\" hidden></div></div>\n<p>ثلاثة معاملات تقوم بالعمل:</p>\n<ul>\n<li><code dir=\"ltr\">test_size=0.25</code> يحجز ربع الصفوف: 100 عميل للاختبار و298 للتدريب. مع بضع مئات من الصفوف، تُعدّ نسبة 20–30% للاختبار معقولة؛ ومع الملايين تكفي 1–5%.</li>\n<li><code dir=\"ltr\">random_state=42</code> يثبّت الخلط فتحصل على التقسيم نفسه في كل تشغيل. وبدونه يُعاد الخلط مع كل تشغيل، فتتأرجح نتيجتك لأسباب لا علاقة لها بنموذجك.</li>\n<li><code dir=\"ltr\">stratify=y</code> يُبقي نسبة الانقطاع متساوية في الجزأين (55%). فمع 100 صف للاختبار، قد يعطيك خلط سيئ الحظ بيانات اختبار نسبة الانقطاع فيها 45% أو 62%، وعندها يتغيّر كل رقم تحسبه عليها.</li>\n</ul>\n<p>ترتيب القيم الأربع المُعادة ثابت: <code dir=\"ltr\">X_train, X_test, y_train, y_test</code>. والخلط بينها مصدر شائع لنتائج لا معنى لها.</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>تبقى بيانات الاختبار صادقة فقط في أول مرة تستخدمها لاتخاذ قرار. إذا جرّبت عشر أفكار واحتفظت بأيّها حقق أفضل نتيجة على بيانات الاختبار، فقد ضبطت نموذجك على أولئك العملاء المئة تحديدًا، وستكون النتيجة التي تنشرها متفائلة أكثر من اللازم. اتخذ قراراتك ببيانات التدريب (يبيّن القسم 4 كيف بالتحقق المتقاطع)، وانظر إلى نتيجة الاختبار مرة واحدة، في النهاية.</p>\n</aside>\n<aside class=\"callout callout-note\" 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\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">ملاحظة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>التقسيم العشوائي يفترض أن المستقبل يشبه الماضي</span></p>\n<p>التقسيم العشوائي يخلط الصفوف القديمة بالجديدة. فإذا كانت بياناتك تتغيّر مع الزمن، سمح التقسيم العشوائي للنموذج بأن يلمح أنماطًا من “المستقبل”. في المسائل الشبيهة بالتنبؤ الزمني، قسّم بحسب التاريخ: درّب على الفترات الأقدم واختبر على الأحدث. وسيعود درس تسرّب البيانات في القسم 4 إلى هذه النقطة.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">ما الذي يمنحك إياه التقسيم</h2>\n<p>فكّر في التقسيم على أنه عقد مع نفسك في المستقبل. كل ما تفعله من الآن فصاعدًا (اختيار الـ features، واختيار النماذج، وضبط الإعدادات) يحدث على جزء التدريب. ويبقى جزء الاختبار مختومًا حتى تصبح جاهزًا لنشر نتيجة. بـ <code dir=\"ltr\">X_train</code> و<code dir=\"ltr\">y_train</code> يمكنك تدريب أي نموذج تريده. وبـ <code dir=\"ltr\">X_test</code> و<code dir=\"ltr\">y_test</code> تحصل على إجابة صادقة واحدة عن سؤال “ما مدى نجاح هذا مع عملاء لم يرَهم؟”. هذا هو الرقم الذي يتنبأ بما سيحدث حين يستخدم فريق التسويق نموذجك في ديسمبر. في الدرس القادم ستدرّب أول نموذج وترى كم يمكن أن تكون الفجوة كبيرة بين هذين الرقمين.</p>\n","toc":[{"id":"sec-1","text":"سير العمل من البداية إلى النهاية","level":2},{"id":"sec-2","text":"الـ Features والهدف","level":2},{"id":"sec-3","text":"حجز بيانات الاختبار","level":2},{"id":"sec-4","text":"ما الذي يمنحك إياه التقسيم","level":2}],"takeaways":["كل مشروع تعلّم موجَّه يسير في الحلقة نفسها: صياغة السؤال، وجمع بيانات معروفة الإجابة، والتقسيم، وخط الأساس، والتدريب، والتقييم، ثم الإطلاق والمراقبة.","X جدول من الـ features فيه صف لكل مثال؛ وy هو عمود الإجابات المقابل له.","نتيجة النموذج على بيانات تدريبه تقول كم حفظ، لا كم يُحسن التنبؤ.","استخدم <code dir=\"ltr\">stratify=y</code> في التصنيف ليحافظ جزءا التدريب والاختبار على توازن الفئات نفسه، وحدّد <code dir=\"ltr\">random_state</code> ليكون التقسيم قابلًا للتكرار.","بيانات الاختبار مخصّصة للفحص الأخير؛ والنظر إليها أثناء اتخاذ القرارات يحوّلها إلى بيانات تدريب."],"further":[{"title":"train_test_split","url":"https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html"},{"title":"Common pitfalls and recommended practices","url":"https://scikit-learn.org/stable/common_pitfalls.html"}],"quiz":[{"q":"<p>درّبت نموذجًا على كل صفوف بيانات الانقطاع الـ 398، فحقق 0.99 على الصفوف نفسها. ماذا تستنتج؟</p>","options":[{"text":"سيكون النموذج دقيقًا بنحو 99% على عملاء الشهر القادم.","why":"رأى النموذج كل إجابة من تلك الإجابات. والنتيجة العالية على بيانات مرئية تتّسق تمامًا مع الحفظ المحض."},{"text":"القليل جدًا عن أدائه المستقبلي؛ تحتاج إلى صفوف لم يرَها النموذج قط.","why":"صحيح. البيانات المحجوزة وحدها تخبرك كيف يعمّم النموذج على عملاء جدد."},{"text":"النموذج يعاني من الـ overfitting، فاحذف features حتى تنخفض النتيجة.","why":"لا يمكنك تشخيص الـ overfitting دون نتيجة على بيانات محجوزة تقارن بها، وحذف الـ features عشوائيًا ليس حلًّا."}],"answer":1},{"q":"<p>ما الذي يغيّره <code dir=\"ltr\">stratify=y</code> في <code dir=\"ltr\">train_test_split(X, y, test_size=0.25, stratify=y)</code>؟</p>","options":[{"text":"يرتّب الصفوف بحسب الهدف قبل التقسيم.","why":"الترتيب سيضع فئة كاملة في جزء واحد. أما الـ stratify فيفعل العكس؛ يُبقي الفئات موزّعة بالتساوي."},{"text":"يوازن الفئات بحيث يكون كل جزء 50% منقطعين.","why":"يحافظ الـ stratify على النسبة الأصلية (55% هنا)؛ ولا يعيد الموازنة إلى 50/50."},{"text":"يُبقي حصة كل فئة متساوية في جزأي التدريب والاختبار.","why":"صحيح. في مجموعات البيانات الصغيرة قد يشوّه تقسيم سيئ الحظ بيانات الاختبار؛ والـ stratify يمنع ذلك."}],"answer":2},{"q":"<p>لماذا نحذف <code dir=\"ltr\">customer_id</code> من الـ features؟</p>","options":[{"text":"لأنه معرّف لا علاقة سببية له بالانقطاع، فلا يستطيع النموذج إلا أن يحفظه.","why":"صحيح. المعرّفات labels اعتباطية. ومع ذلك يستطيع نموذج مرن أن يقسم البيانات عليها ويتعلّم ضجيجًا لن يصمد مع عملاء جدد."},{"text":"لأن scikit-learn يرفض الأعمدة الرقمية المسمّاة <code dir=\"ltr\">id</code>.","why":"لا يفحص scikit-learn أسماء الأعمدة بحثًا عن معناها. بل سيستخدم المعرّف بلا تردّد، وهذه هي المشكلة."},{"text":"لأنه الهدف، فلا يجوز أن يكون في X.","why":"الهدف هو <code dir=\"ltr\">churned</code>. المعرّف مجرد اسم للصف، لا إجابة."},{"text":"لأن المعرّفات تبطّئ التدريب لكونها أرقامًا كبيرة.","why":"الأرقام الكبيرة لا تبطّئ التدريب بشكل يُذكر. السبب يتعلّق بما يستطيع النموذج تعلّمه، لا بالسرعة."}],"answer":0},{"q":"<p>جرّبت عشرين إعدادًا للنموذج، واحتفظت بصاحب أفضل نتيجة على بيانات الاختبار، ثم نشرت تلك النتيجة. ما الخطأ؟</p>","options":[{"text":"لا شيء؛ هذا هو الغرض من بيانات الاختبار.","why":"بيانات الاختبار مخصّصة لتقدير نهائي واحد غير منحاز. والاختيار بين عشرين خيارًا على أساسها يجعلها جزءًا من التدريب."},{"text":"عشرون إعدادًا أقل من أن تجد نموذجًا جيدًا.","why":"عدد الإعدادات ليس المشكلة. أي اختيار مبني على بيانات الاختبار يرفع النتيجة المنشورة بشكل منحاز."},{"text":"النتيجة المنشورة متفائلة، لأنك اخترت الإعداد الذي صادف أن يلائم هذه البيانات تحديدًا.","why":"صحيح. استخدم جزء تحقق (validation) أو التحقق المتقاطع للاختيار، ثم المس بيانات الاختبار مرة واحدة."}],"answer":2}],"exercise":{"kind":"python","files":["churn.csv"],"starter":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\n\nchurn = pd.read_csv(\"churn.csv\")\n\n# 1. Feature matrix: the eight numeric feature columns (no id, no target)\nX = churn.drop(columns=[\"country\", \"segment\", \"avg_satisfaction\"])\n# 2. Target\ny = churn[\"churned\"]\n\n# 3. Split: 25% test, random_state=42, keep the churn rate equal in both parts\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)\n","solution":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\n\nchurn = pd.read_csv(\"churn.csv\")\n\nX = churn.drop(columns=[\"customer_id\", \"country\", \"segment\", \"avg_satisfaction\", \"churned\"])\ny = churn[\"churned\"]\n\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y, test_size=0.25, random_state=42, stratify=y\n)\n","checks":[{"id":"features","test":"set(X.columns) == {'tenure_days', 'orders', 'total_spent', 'avg_order_value', 'days_since_last_order', 'used_coupon', 'support_tickets', 'mobile_share'}"},{"id":"target","test":"list(y.unique()) != [] and set(y.unique()) == {0, 1} and len(y) == 398"},{"id":"sizes","test":"X_train.shape == (298, 8) and X_test.shape == (100, 8) and len(y_test) == 100"},{"id":"stratified","test":"abs(float(y_test.mean()) - float(y.mean())) < 0.01 and abs(float(y_train.mean()) - float(y.mean())) < 0.01"}],"title":"ابنِ X وy ثم قسّم البيانات","prompt":"<p>جهّز بيانات انقطاع العملاء للنمذجة.</p>\n<ol>\n<li><code dir=\"ltr\">X</code>: أعمدة الـ features الرقمية الثمانية: كل شيء ما عدا <code dir=\"ltr\">customer_id</code> و<code dir=\"ltr\">country</code> و<code dir=\"ltr\">segment</code> و<code dir=\"ltr\">avg_satisfaction</code> والهدف.</li>\n<li><code dir=\"ltr\">y</code>: العمود <code dir=\"ltr\">churned</code>.</li>\n<li>قسّم البيانات إلى <code dir=\"ltr\">X_train, X_test, y_train, y_test</code> بحيث تكون بيانات الاختبار 25%، مع <code dir=\"ltr\">random_state=42</code>، ومع بقاء نسبة الانقطاع متساوية في الجزأين.</li>\n</ol>\n","hints":["ما زال <code dir=\"ltr\">customer_id</code> و<code dir=\"ltr\">churned</code> داخل <code dir=\"ltr\">X</code> في الكود المبدئي. احذفهما أيضًا.","<code dir=\"ltr\">test_size=0.25</code> يعطيك 100 صف للاختبار من أصل 398.","مرّر <code dir=\"ltr\">stratify=y</code> ليحافظ الجزآن على نسبة انقطاع 55%."],"explanation":"<p>حذف المعرّف والهدف يترك ثمانية features رقمية. يحجز <code dir=\"ltr\">test_size=0.25</code> مئة عميل، ويجعل <code dir=\"ltr\">random_state=42</code> التقسيم قابلًا للتكرار، ويُبقي <code dir=\"ltr\">stratify=y</code> نسبة الانقطاع عند 55% في الجزأين فلا تنحرف نتيجة الاختبار بسبب خلط سيئ الحظ. أما ترك <code dir=\"ltr\">churned</code> داخل <code dir=\"ltr\">X</code> فيتيح لأي نموذج قراءة الإجابة مباشرة من الـ features، وهذا أشدّ أشكال تسرّب البيانات.</p>\n","checkLabels":{"features":"يحتوي X على الـ features الرقمية الثمانية بالضبط","target":"y هو العمود churned (398 قيمة من 0 و1)","sizes":"298 صفًا للتدريب و100 صف للاختبار","stratified":"يحافظ جزءا التدريب والاختبار على نسبة الانقطاع الكلية"}},"words":575,"readMinutes":3}