{"id":"l-ml-3-3","courseId":"ml-crash-course","lang":"ar","kind":"lesson","summary":"اجمع أشجارًا كثيرة باستخدام الغابات العشوائية والـ gradient boosting، واستفد من تعامل HistGradientBoostingClassifier المدمج مع القيم المفقودة، وقِس أهمية الـ features بالـ permutation importance.","html":"<p>شجرة القرار الواحدة سهلة القراءة وسهلة الكسر: غيّر بضعة صفوف تدريب وستتبدّل الفروع. والعلاج حيلة إحصائية قديمة: اسأل مقدِّرات كثيرة مستقلة ومختلفة قليلًا، واجمع إجاباتها. أخطاء كلٍّ منها تتلاشى جزئيًا، وما يتبقى أكثر موثوقية من أي عضو منفرد. النماذج المبنية بهذه الطريقة هي <strong>النماذج المجمّعة (ensembles)</strong>، وفي البيانات الجدولية مثل بيانات الانقطاع، هي ما يلجأ إليه معظم الممارسين بعد خطوط الأساس والنموذج الخطي.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الغابات العشوائية: أشجار كثيرة، ومتوسط</h2>\n<p>تنمّي الغابة العشوائية بضع مئات من الأشجار العميقة، كلٌّ منها على <strong>عيّنة bootstrap</strong> مختلفة (صفوف التدريب مسحوبة مع الإرجاع، فترى كل شجرة مجموعة بيانات مختلفة قليلًا)، ولا ترى عند كل تقسيم إلا مجموعة فرعية عشوائية من الـ features. هذه الحيلة الثانية تمنع كل الأشجار من أن تبدأ بالـ feature المهيمن نفسه، فتصبح الأشجار أكثر اختلافًا عن بعضها، وتصبح أخطاؤها أقل ترابطًا. وللتنبؤ، تأخذ الغابة متوسط احتمالات الأشجار.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ Gradient Boosting: أشجار يصلح بعضها بعضًا</h2>\n<p>يسلك الـ boosting النهج المعاكس. فهو يبني أشجارًا ضحلة واحدة في كل مرة. الأولى تعطي تنبؤات تقريبية؛ والثانية تُدرَّب على الأخطاء التي تركتها الأولى؛ والثالثة على ما لا تزال الأوليان تخطئان فيه، وهكذا. ومساهمة كل شجرة تُصغَّر بمقدار <code dir=\"ltr\">learning_rate</code>، فتزحف المجموعة نحو إجابة جيدة بدل أن تترنّح. والمزيد من الأشجار (<code dir=\"ltr\">max_iter</code>) ومعدّل تعلّم أعلى يلائمان بيانات التدريب بدقة أكبر، ثم يقعان في النهاية في الـ overfitting.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 250\" role=\"img\" aria-labelledby=\"l-ml-3-3-t1\"><defs><marker id=\"arrow-l-ml-3-3-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-3-3-t1\">الصف العلوي، الغابة العشوائية: ثلاث أشجار مدرَّبة باستقلال على عيّنات مختلفة تصبّ في متوسط. الصف السفلي، الـ gradient boosting: الشجرة 1 تمرّر أخطاءها إلى الشجرة 2، التي تمرّر أخطاءها المتبقية إلى الشجرة 3، وتُجمع مخرجاتها بعد تصغيرها.</title>\n  <text class=\"d-label-strong\" x=\"10\" y=\"24\">الغابة العشوائية (bagging)</text>\n  <rect class=\"d-box\" x=\"20\" y=\"40\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"75\" y=\"65\" text-anchor=\"middle\">شجرة على العيّنة 1</text>\n  <rect class=\"d-box\" x=\"150\" y=\"40\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"205\" y=\"65\" text-anchor=\"middle\">شجرة على العيّنة 2</text>\n  <rect class=\"d-box\" x=\"280\" y=\"40\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"335\" y=\"65\" text-anchor=\"middle\">شجرة على العيّنة 3</text>\n  <path class=\"d-arrow\" d=\"M390 60 L480 60\" marker-end=\"url(#arrow-l-ml-3-3-1)\"/>\n  <rect class=\"d-box-primary\" x=\"485\" y=\"40\" width=\"190\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"580\" y=\"65\" text-anchor=\"middle\">متوسط الأصوات</text>\n  <text class=\"d-label-strong\" x=\"10\" y=\"140\">Gradient boosting</text>\n  <rect class=\"d-box\" x=\"20\" y=\"160\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"75\" y=\"185\" text-anchor=\"middle\">الشجرة 1</text>\n  <path class=\"d-arrow\" d=\"M130 180 L150 180\" marker-end=\"url(#arrow-l-ml-3-3-1)\"/>\n  <rect class=\"d-box\" x=\"155\" y=\"160\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"210\" y=\"185\" text-anchor=\"middle\">الشجرة 2 على الأخطاء</text>\n  <path class=\"d-arrow\" d=\"M265 180 L285 180\" marker-end=\"url(#arrow-l-ml-3-3-1)\"/>\n  <rect class=\"d-box\" x=\"290\" y=\"160\" width=\"110\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"345\" y=\"185\" text-anchor=\"middle\">الشجرة 3 على الأخطاء</text>\n  <path class=\"d-arrow\" d=\"M400 180 L480 180\" marker-end=\"url(#arrow-l-ml-3-3-1)\"/>\n  <rect class=\"d-box-accent\" x=\"485\" y=\"160\" width=\"190\" height=\"40\" rx=\"8\"/><text class=\"d-label\" x=\"580\" y=\"185\" text-anchor=\"middle\">مجموع مصغَّر بمعدّل التعلّم</text>\n  <text class=\"d-label-muted\" x=\"210\" y=\"228\" text-anchor=\"middle\">كل شجرة تعتمد على ما سبقها</text>\n</svg><figcaption>الـ bagging يأخذ المتوسط بالتوازي؛ والـ boosting يصحّح بالتتابع</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">كلاهما على بيانات الانقطاع، مع عودة العمود الناقص</h2>\n<p>حتى الآن استبعدت <code dir=\"ltr\">avg_satisfaction</code> لأن نصف قيمه فارغة. أما <code dir=\"ltr\">HistGradientBoostingClassifier</code>، وهو تطبيق الـ boosting السريع في scikit-learn، فيتعامل مع القيم المفقودة بشكل أصيل: عند كل تقسيم يتعلّم إلى أيّ جهة يجب أن تذهب الفراغات. والإصدارات الحديثة من <code dir=\"ltr\">DecisionTreeClassifier</code> و<code dir=\"ltr\">RandomForestClassifier</code> تقبل NaN أيضًا. أما <code dir=\"ltr\">LogisticRegression</code> وk-NN فما زالا لا يقبلانها؛ ويرفعان <code dir=\"ltr\">ValueError: Input X contains NaN</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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.ensemble </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> HistGradientBoostingClassifier, RandomForestClassifier</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.linear_model </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> LogisticRegression</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.pipeline </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> make_pipeline</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.preprocessing </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StandardScaler</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:#0A3069;--shiki-dark:#A5D6FF\">            \"avg_satisfaction\"</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\">no_gaps </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> features[:</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># logistic regression can't take the NaNs in avg_satisfaction</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">models </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"logistic (no satisfaction)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: (make_pipeline(StandardScaler(), LogisticRegression()), no_gaps),</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"random forest\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: (RandomForestClassifier(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_estimators</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">300</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">min_samples_leaf</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</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:#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\">), features),</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"boosting, defaults\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: (HistGradientBoostingClassifier(</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\">), features),</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"boosting, gentle\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: (HistGradientBoostingClassifier(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">max_depth</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">learning_rate</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.05</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">                                                        max_iter</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">200</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\">), features),</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> name, (model, cols) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> models.items():</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    model.fit(X_train[cols], 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:#CF222E;--shiki-dark:#FF7B72\">f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:27}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> train </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">model.score(X_train[cols], y_train)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:.2f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  \"</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">          f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"test </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">model.score(X_test[cols], y_test)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:.2f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>تبلغ الغابة 0.76 والـ boosting المضبوط بلطف 0.78، مقابل 0.72 للانحدار اللوجستي وللقاعدة. أما الـ boosting بإعداداته الافتراضية فيلائم بيانات التدريب بشكل شبه مثالي (0.99) ويؤدي أسوأ من النسخة اللطيفة: فعلى 298 صفًا، الإعدادات الافتراضية متحمّسة أكثر من اللازم. والأشجار الأضحل مع معدّل تعلّم أصغر تُبقيه تحت السيطرة.</p>\n<p>لا تتمسّك بهذه الأرقام بشدة. فعلى بيانات اختبار من 100 عميل، النقطتان تعنيان عميلين. يضيف الدرس القادم مقياسًا لجودة الترتيب، وسيخبرك التحقق المتقاطع في القسم 4 ما إذا كانت أفضلية النماذج المجمّعة تصمد أمام خلط مختلف.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">على ماذا تعتمد الأشجار؟</h2>\n<p>توفّر الغابات <code dir=\"ltr\">feature_importances_</code>، لكن هذه الأرقام تُحسب على بيانات التدريب من مقدار ما خفّضه كل feature من عدم النقاوة، وهي منحازة لصالح الـ features المتصلة ذات العتبات المحتملة الكثيرة. أما <strong>الـ permutation importance (أهمية التبديل)</strong> فتطرح سؤالًا أدقّ على بيانات محجوزة: إذا خلطت هذا العمود وحده، فقطعت صلته بالهدف، فكم تنخفض النتيجة؟</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.ensemble </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> RandomForestClassifier</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sklearn.inspection </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> permutation_importance</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:#0A3069;--shiki-dark:#A5D6FF\">            \"avg_satisfaction\"</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 style=\"color:#1F2328;--shiki-dark:#E6EDF3\">forest </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> RandomForestClassifier(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_estimators</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">300</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">min_samples_leaf</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</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\">).fit(X_train, y_train)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">result </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> permutation_importance(forest, X_test, y_test, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_repeats</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">10</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\">table </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.DataFrame({</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"impurity (train)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: forest.feature_importances_,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">                      \"permutation (test)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: result.importances_mean}, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">features)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(table.sort_values(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"permutation (test)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">ascending</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">False</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).round(</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>تضع أهمية عدم النقاوة <code dir=\"ltr\">tenure_days</code> في المرتبة الثانية. أما الـ permutation importance على بيانات الاختبار فتقول إنه لا يكاد يهمّ: لقد استخدمته الغابة لملاءمة الضجيج في بيانات التدريب. خلط حداثة آخر طلب يكلّف نحو 13 نقطة من الـ accuracy؛ وخلط الرضا لا يكلّف شيئًا (القيم السالبة الصغيرة ضجيج). والخلاصة الأمينة لـ Cartwheel: حداثة آخر طلب وعدد الطلبات هما ما يحرّك التنبؤات، واستبيان الرضا بحالته الراهنة نصف الفارغة لا يضيف شيئًا.</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>أهمية عدم النقاوة تصف كيف نمّى نموذج واحد أشجاره على بيانات التدريب. وهي تضخّم الـ features المتصلة كثيرة القيم، وتوزّع الفضل اعتباطيًا بين الـ features المترابطة. حين يسألك أحد أصحاب القرار عمّا يهمّ، استخدم الـ permutation importance على بيانات محجوزة، وصُغ الإجابة مع ذلك على أنها “ما يعتمد عليه النموذج”، لا “ما يسبّب الانقطاع”.</p>\n</aside>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>خيارات افتراضية للبيانات الجدولية</span></p>\n<p>بعد خطوط الأساس والانحدار اللوجستي، جرّب <code dir=\"ltr\">HistGradientBoostingClassifier</code> مع <code dir=\"ltr\">learning_rate</code> معتدل (0.05–0.1) وأشجار ضحلة. إنه سريع، ويتعامل مع القيم المفقودة، ويصعب التفوّق عليه في الجداول. واستخدم <code dir=\"ltr\">RandomForestClassifier</code> حين تريد نموذجًا قويًا بلا ضبط يُذكر.</p>\n</aside>\n<p>حملتك الـ accuracy حتى هنا، لكنها تعامل العميل المنقطع الذي فاتك والإنذار الكاذب على أنهما سيئان بالقدر نفسه، وتتجاهل الترتيب الذي بنيته لفريق التسويق. الدرس القادم يستبدلها بمقاييس تنظر في كل نوع من الأخطاء على حدة.</p>\n","toc":[{"id":"sec-1","text":"الغابات العشوائية: أشجار كثيرة، ومتوسط","level":2},{"id":"sec-2","text":"الـ Gradient Boosting: أشجار يصلح بعضها بعضًا","level":2},{"id":"sec-3","text":"كلاهما على بيانات الانقطاع، مع عودة العمود الناقص","level":2},{"id":"sec-4","text":"على ماذا تعتمد الأشجار؟","level":2}],"takeaways":["تدرّب الغابة العشوائية (random forest) أشجارًا عميقة كثيرة على عيّنات bootstrap مع مجموعات فرعية عشوائية من الـ features، ثم تأخذ متوسط أصواتها، فيُلغى جزء كبير من تباين الشجرة الواحدة.","يبني الـ gradient boosting أشجارًا ضحلة واحدة تلو الأخرى، كلٌّ منها مدرَّبة على أخطاء المجموعة حتى تلك اللحظة؛ ويوازن <code dir=\"ltr\">learning_rate</code> و<code dir=\"ltr\">max_iter</code> بين السرعة والـ overfitting.","<code dir=\"ltr\">HistGradientBoostingClassifier</code> هو نموذج الـ boosting السريع في scikit-learn، ويقبل القيم المفقودة (NaN) مباشرة.","قيم <code dir=\"ltr\">feature_importances_</code> المبنية على عدم النقاوة تحابي الـ features ذات القيم المختلفة الكثيرة؛ أما الـ permutation importance على بيانات محجوزة فتقيس ما يعتمد عليه النموذج فعلًا.","في مجموعات البيانات الجدولية الصغيرة، كثيرًا ما لا تضيف النماذج المجمّعة إلا بضع نقاط فوق نموذج خطي جيد، فقارنها به بدل أن تفترض أنها ستفوز."],"further":[{"title":"Ensembles: Gradient boosting, random forests, bagging","url":"https://scikit-learn.org/stable/modules/ensemble.html"},{"title":"HistGradientBoostingClassifier","url":"https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingClassifier.html"},{"title":"Permutation feature importance","url":"https://scikit-learn.org/stable/modules/permutation_importance.html"}],"quiz":[{"q":"<p>لماذا تتفوّق الغابة العشوائية عادةً على شجرة واحدة غير مقيّدة مع البيانات الجديدة؟</p>","options":[{"text":"لأن كل شجرة فيها أضحل، فلا تستطيع الوقوع في الـ overfitting.","why":"أشجار الغابة تُنمّى عادةً عميقة. والفائدة تأتي من أخذ المتوسط، لا من تقييد كل شجرة."},{"text":"لأنها تستخدم الـ gradient descent لضبط الأشجار معًا.","why":"أشجار الغابة تُدرَّب مستقلة؛ ولا شيء يربطها ببعضها أثناء التدريب."},{"text":"لأنها تتدرّب على بيانات أكثر من الشجرة الواحدة.","why":"كل شجرة ترى عيّنة bootstrap من بيانات التدريب نفسها، فلا بيانات إضافية هنا."},{"text":"لأن أخذ متوسط أشجار كثيرة مدرَّبة على عيّنات ومجموعات features مختلفة يُلغي أخطاءها الفردية.","why":"صحيح. كل شجرة تقع في الـ overfitting بطريقتها الخاصة؛ والأخطاء مستقلة جزئيًا، فيكون المتوسط أكثر استقرارًا."}],"answer":3},{"q":"<p>في الـ gradient boosting، ماذا تتعلّم كل شجرة جديدة؟</p>","options":[{"text":"تصحيح الأخطاء التي ما زالت ترتكبها المجموعة المبنية حتى الآن.","why":"صحيح. الـ boosting تسلسلي؛ وكل شجرة تستهدف ما تبقّى."},{"text":"الهدف نفسه الذي تعلّمته الشجرة الأولى، على عيّنة bootstrap جديدة.","why":"هذا وصف الـ bagging، الفكرة التي تقوم عليها الغابات العشوائية."},{"text":"مجموعة فرعية عشوائية من الـ features، متجاهلة الأشجار السابقة.","why":"المجموعات الفرعية العشوائية من الـ features حيلة الغابات. أما أشجار الـ boosting فتعتمد على الأشجار التي قبلها."}],"answer":0},{"q":"<p>العمود <code dir=\"ltr\">avg_satisfaction</code> فارغ لنصف العملاء. أيّ نموذج يمكنك تدريبه عليه دون معالجة مسبقة إضافية؟</p>","options":[{"text":"<code dir=\"ltr\">LogisticRegression</code>، لأنه يتجاهل القيم المفقودة.","why":"يرفع <code dir=\"ltr\">ValueError: Input X contains NaN</code>. ستحتاج إلى imputer أولًا."},{"text":"<code dir=\"ltr\">HistGradientBoostingClassifier</code>، الذي يتعلّم عند كل تقسيم الفرعَ الذي يجب أن تسلكه القيم المفقودة.","why":"صحيح. الدعم الأصيل لقيم NaN من أهم مزاياه العملية."},{"text":"<code dir=\"ltr\">KNeighborsClassifier</code>، لأنه يتخطّى الأعمدة المفقودة عند قياس المسافة.","why":"يرفع k-NN خطأً مع NaN أيضًا؛ فلا يمكن حساب المسافات مع وجود فجوات."}],"answer":1},{"q":"<p>يضع <code dir=\"ltr\">feature_importances_</code> الخاص بالغابة <code dir=\"ltr\">tenure_days</code> في المرتبة الثانية، لكن الـ permutation importance على بيانات الاختبار تعطيه ما يقارب الصفر. ما التفسير الأرجح؟</p>","options":[{"text":"الـ permutation importance معطوبة في الغابات العشوائية.","why":"تعمل مع أي نموذج؛ فهي لا تحتاج إلا إلى تنبؤات ونتيجة."},{"text":"بيانات الاختبار أصغر من أن تحتوي <code dir=\"ltr\">tenure_days</code>.","why":"كل صف فيه هذا الـ feature. بيانات الاختبار الصغيرة تضيف ضجيجًا، لكن لهذا النمط سببًا معروفًا."},{"text":"استخدمت الغابة <code dir=\"ltr\">tenure_days</code> في تقسيمات كثيرة لاءمت ضجيج التدريب، فتضخّمت أهمية عدم النقاوة دون أن يفيد ذلك مع البيانات الجديدة.","why":"صحيح. أهمية عدم النقاوة تُحسب على بيانات التدريب وتحابي الـ features المتصلة ذات العتبات المحتملة الكثيرة."},{"text":"خلط <code dir=\"ltr\">tenure_days</code> جعل النموذج أدق، وهذا يُثبت أن الـ feature ضار.","why":"النتيجة القريبة من الصفر تعني أن الخلط غيّر القليل، لا أن النموذج تحسّن."}],"answer":2}],"exercise":{"kind":"python","files":["churn.csv"],"starter":"import pandas as pd\nfrom sklearn.ensemble import HistGradientBoostingClassifier\nfrom sklearn.inspection import permutation_importance\nfrom sklearn.model_selection import train_test_split\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\",\n            \"avg_satisfaction\"]\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. Gradient boosting on all nine features (missing values included):\n#    max_depth=3, learning_rate=0.05, max_iter=200, random_state=42\nbooster = HistGradientBoostingClassifier(random_state=42)\nbooster.fit(X_train.drop(columns=\"avg_satisfaction\"), y_train)\n\n# 2. Test accuracy\ntest_acc = None\n\n# 3. Permutation importance on the TEST set (n_repeats=5, random_state=0),\n#    then the feature names sorted from most to least important\nranking = []\n","solution":"import pandas as pd\nfrom sklearn.ensemble import HistGradientBoostingClassifier\nfrom sklearn.inspection import permutation_importance\nfrom sklearn.model_selection import train_test_split\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\",\n            \"avg_satisfaction\"]\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\nbooster = HistGradientBoostingClassifier(max_depth=3, learning_rate=0.05, max_iter=200,\n                                         random_state=42)\nbooster.fit(X_train, y_train)\n\ntest_acc = booster.score(X_test, y_test)\n\nresult = permutation_importance(booster, X_test, y_test, n_repeats=5, random_state=0)\nranking = pd.Series(result.importances_mean, index=features).sort_values(ascending=False).index.tolist()\nprint(test_acc, ranking[:3])\n","checks":[{"id":"nine-features","test":"booster.n_features_in_ == 9"},{"id":"settings","test":"booster.max_depth == 3 and booster.learning_rate == 0.05 and booster.max_iter == 200"},{"id":"accuracy","test":"test_acc is not None and 0.74 <= float(test_acc) <= 0.82 and abs(float(test_acc) - booster.score(X_test, y_test)) < 1e-9"},{"id":"ranking","test":"len(ranking) == 9 and set(ranking) == set(features) and ranking[0] == 'days_since_last_order'"}],"title":"طبّق الـ boosting مع إبقاء الفجوات","prompt":"<p>درّب نموذج gradient boosting على الـ features التسعة كلها، بما فيها <code dir=\"ltr\">avg_satisfaction</code> نصف الفارغ، واكتشف على ماذا يعتمد.</p>\n<ol>\n<li><code dir=\"ltr\">booster</code>: <code dir=\"ltr\">HistGradientBoostingClassifier(max_depth=3, learning_rate=0.05, max_iter=200, random_state=42)</code> مدرَّبًا على <code dir=\"ltr\">X_train</code> كاملة (دون حذف أي عمود).</li>\n<li><code dir=\"ltr\">test_acc</code>: دقته على بيانات الاختبار.</li>\n<li><code dir=\"ltr\">ranking</code>: قائمة بأسماء الـ features التسعة مرتّبة بحسب الـ permutation importance على بيانات الاختبار (<code dir=\"ltr\">n_repeats=5, random_state=0</code>)، الأهم أولًا.</li>\n</ol>\n","hints":["لا حاجة إلى ملء القيم المفقودة: <code dir=\"ltr\">HistGradientBoostingClassifier</code> يوجّهها بنفسه.","تُعيد <code dir=\"ltr\">permutation_importance(booster, X_test, y_test, n_repeats=5, random_state=0)</code> كائنًا فيه <code dir=\"ltr\">importances_mean</code>.","ضع المتوسطات في <code dir=\"ltr\">pd.Series</code> فهرسه <code dir=\"ltr\">features</code>، ورتّبها تنازليًا، وخذ <code dir=\"ltr\">.index.tolist()</code>."],"explanation":"<p>يصل الـ boosting اللطيف إلى نحو 0.78 على بيانات الاختبار، أي أعلى من الانحدار اللوجستي ببضع نقاط. وتضع الـ permutation importance العمود <code dir=\"ltr\">days_since_last_order</code> متقدّمًا بفارق كبير على كل ما عداه، و<code dir=\"ltr\">avg_satisfaction</code> قرب الصفر أو دونه: النموذج يستطيع استخدام العمود، لكن الاستبيان بالشكل الذي جُمع به لا يساعد. وحساب الأهمية على بيانات الاختبار يقيس ما يعمّم، لا ما حفظته الأشجار.</p>\n","checkLabels":{"nine-features":"النموذج مدرَّب على الـ features التسعة كلها","settings":"يستخدم الـ booster الإعدادات اللطيفة","accuracy":"test_acc هي الدقة على بيانات الاختبار","ranking":"يسرد ranking الـ features التسعة بحسب الـ permutation importance، وحداثة آخر طلب أولًا"}},"words":636,"readMinutes":4}