{"id":"l-ml-4-3","courseId":"ml-crash-course","lang":"en","kind":"lesson","summary":"Tune hyperparameters with GridSearchCV and RandomizedSearchCV using pipeline step names, read cv_results_, and keep the final test score honest after the search.","html":"<p>Every model so far has had settings you picked by hand or left at defaults: <code dir=\"ltr\">C=1.0</code>, <code dir=\"ltr\">max_depth=3</code>, <code dir=\"ltr\">min_samples_leaf=5</code>. Some of those choices matter a lot, some barely at all, and the only way to know is to try. Trying by hand means loops, bookkeeping and the temptation to peek at the test set. scikit-learn’s search tools do it properly, with cross-validation built in.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Grid search</h2>\n<p><code dir=\"ltr\">GridSearchCV</code> takes an estimator, a dictionary of parameter values and a cross-validation scheme. It cross-validates every combination, picks the one with the best mean score, and refits it on all the training data. Afterwards it behaves like the winning model.</p>\n<p>Parameters inside a pipeline are addressed by step name, two underscores, and parameter name. That’s why naming your steps pays off.</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=\"Run this example\"><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>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><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>Copy</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.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\"> GridSearchCV, StratifiedKFold, 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\"> 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:#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\">cv </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StratifiedKFold(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_splits</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\">shuffle</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\">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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">pipe </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Pipeline([(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"scale\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, StandardScaler()), (</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"model\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, LogisticRegression())])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">search </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> GridSearchCV(pipe, {</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"model__C\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.01</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.03</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.3</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:#0550AE;--shiki-dark:#79C0FF\">3</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:#953800;--shiki-dark:#FFA657\">                      cv</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cv, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">scoring</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"roc_auc\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">search.fit(X_train, y_train)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">results </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.DataFrame(search.cv_results_)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(results[[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"param_model__C\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mean_test_score\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"std_test_score\"</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>\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:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, search.best_params_, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"CV AUC\"</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\">(search.best_score_, </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=\"0\" hidden></div></div>\n<p><code dir=\"ltr\">cv_results_</code> holds every combination’s fold scores; as a DataFrame it’s easy to scan. Here the scan is the finding: from <code dir=\"ltr\">C=0.03</code> to <code dir=\"ltr\">C=10</code> the mean AUC moves by less than half a point, far inside the fold-to-fold spread. Eight well-behaved features don’t give logistic regression room to overfit, so its penalty barely matters. Tuning can’t rescue a model that’s already at its ceiling; better features or a different model family might.</p>\n<p>Notice the values are spaced by factors of about three. For parameters that act multiplicatively (<code dir=\"ltr\">C</code>, <code dir=\"ltr\">alpha</code>, learning rates), search on a log scale; 0.01, 0.1, 1, 10 covers far more ground than 1, 2, 3, 4.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">A grid where it matters</h2>\n<p>Trees are another story. Depth and minimum leaf size control how much a tree can memorise, and the right values depend on the data.</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=\"Run this example\"><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>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><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>Copy</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\"> GridSearchCV, StratifiedKFold, 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.tree </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> DecisionTreeClassifier</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 style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cv </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StratifiedKFold(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_splits</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\">shuffle</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\">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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">grid </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"max_depth\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">None</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"min_samples_leaf\"</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:#0550AE;--shiki-dark:#79C0FF\">5</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 style=\"color:#0550AE;--shiki-dark:#79C0FF\">20</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">40</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">search </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> GridSearchCV(DecisionTreeClassifier(</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\">), grid, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">cv</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cv, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">scoring</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"roc_auc\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">search.fit(X_train, y_train)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">top </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.DataFrame(search.cv_results_).sort_values(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"rank_test_score\"</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\">(top[[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"param_max_depth\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"param_min_samples_leaf\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mean_test_score\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]].head(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</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>\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\">\"worst:\"</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\">(top[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"mean_test_score\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].min(), </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\">\"best:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, search.best_params_, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"| test AUC\"</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\">(search.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=\"1\" hidden></div></div>\n<p>25 combinations × 5 folds = 125 fits, plus one final refit. The spread is wide: the worst trees (unlimited depth, single-customer leaves) score far below the best. <code dir=\"ltr\">search.score</code> uses the same <code dir=\"ltr\">scoring</code> as the search, so it reports test ROC AUC here.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Random search</h2>\n<p>Grids grow multiplicatively. Four parameters with six values each is 1,296 combinations, times five folds. <strong><code dir=\"ltr\">RandomizedSearchCV</code></strong> samples a fixed number of combinations (<code dir=\"ltr\">n_iter</code>) instead, and it accepts distributions as well as lists.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 270\" role=\"img\" aria-labelledby=\"l-ml-4-3-t1\"><defs><marker id=\"arrow-l-ml-4-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-4-3-t1\">Two squares with an important parameter on the horizontal axis and an unimportant one on the vertical. The grid places nine points in a 3 by 3 lattice, testing only three distinct values of the important parameter. Random search scatters nine points, testing nine distinct values.</title>\n  <rect class=\"d-box\" x=\"40\" y=\"20\" width=\"240\" height=\"200\" rx=\"6\"/>\n  <rect class=\"d-box\" x=\"400\" y=\"20\" width=\"240\" height=\"200\" rx=\"6\"/>\n  <circle class=\"d-dot\" cx=\"80\" cy=\"60\" r=\"7\"/><circle class=\"d-dot\" cx=\"160\" cy=\"60\" r=\"7\"/><circle class=\"d-dot\" cx=\"240\" cy=\"60\" r=\"7\"/>\n  <circle class=\"d-dot\" cx=\"80\" cy=\"120\" r=\"7\"/><circle class=\"d-dot\" cx=\"160\" cy=\"120\" r=\"7\"/><circle class=\"d-dot\" cx=\"240\" cy=\"120\" r=\"7\"/>\n  <circle class=\"d-dot\" cx=\"80\" cy=\"180\" r=\"7\"/><circle class=\"d-dot\" cx=\"160\" cy=\"180\" r=\"7\"/><circle class=\"d-dot\" cx=\"240\" cy=\"180\" r=\"7\"/>\n  <circle class=\"d-dot\" cx=\"425\" cy=\"150\" r=\"7\"/><circle class=\"d-dot\" cx=\"452\" cy=\"70\" r=\"7\"/><circle class=\"d-dot\" cx=\"478\" cy=\"195\" r=\"7\"/>\n  <circle class=\"d-dot\" cx=\"505\" cy=\"105\" r=\"7\"/><circle class=\"d-dot\" cx=\"532\" cy=\"45\" r=\"7\"/><circle class=\"d-dot\" cx=\"560\" cy=\"170\" r=\"7\"/>\n  <circle class=\"d-dot\" cx=\"585\" cy=\"125\" r=\"7\"/><circle class=\"d-dot\" cx=\"608\" cy=\"60\" r=\"7\"/><circle class=\"d-dot\" cx=\"628\" cy=\"200\" r=\"7\"/>\n  <text class=\"d-label-strong\" x=\"160\" y=\"245\" text-anchor=\"middle\">Grid: 3 values tried</text>\n  <text class=\"d-label-strong\" x=\"520\" y=\"245\" text-anchor=\"middle\">Random: 9 values tried</text>\n  <text class=\"d-label-muted\" x=\"340\" y=\"125\" text-anchor=\"middle\">important →</text>\n</svg><figcaption>Grid search versus random search with the same nine trials</figcaption></figure>\n<p>When only one or two parameters really matter, which is the usual case, random search explores many more distinct values of them for the same budget.</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=\"Run this example\"><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>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><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>Copy</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\"> scipy.stats </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> randint</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.model_selection </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> RandomizedSearchCV, StratifiedKFold, 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 style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cv </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StratifiedKFold(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">n_splits</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\">shuffle</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\">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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">space </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"max_features\"</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:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"sqrt\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"min_samples_leaf\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: randint(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">search </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> RandomizedSearchCV(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\">60</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\">), space,</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">                            n_iter</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">cv</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">cv, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">scoring</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"roc_auc\"</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\">search.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\">\"best:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, search.best_params_)</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\">\"CV AUC\"</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\">(search.best_score_, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"| test AUC\"</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\">(search.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<p><code dir=\"ltr\">randint(1, 30)</code> from SciPy draws integers from 1 to 29; <code dir=\"ltr\">loguniform(0.01, 10)</code> is the usual choice for <code dir=\"ltr\">C</code> or learning rates. Set <code dir=\"ltr\">random_state</code> on the search so it samples the same candidates each run.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Searching more than model settings</h2>\n<p>Because the search tunes the whole pipeline, preprocessing choices are fair game too. A grid entry like <code dir=\"ltr\">&quot;prep__sat__simpleimputer__strategy&quot;: [&quot;mean&quot;, &quot;median&quot;]</code> compares imputation strategies with the same honest folds. You can even pass a list of grids, one per model family, and let the search swap the final step: <code dir=\"ltr\">[{&quot;model&quot;: [LogisticRegression()], &quot;model__C&quot;: [0.1, 1]}, {&quot;model&quot;: [RandomForestClassifier()], &quot;model__min_samples_leaf&quot;: [5, 20]}]</code>. That’s tidy, but it multiplies fits quickly, and it hides the comparison table you’d want to show a colleague. For a handful of candidate models, a plain loop of <code dir=\"ltr\">cross_validate</code> calls with the same splitter is often clearer.</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\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Believing best_score_</span></p>\n<p>The search reports the best of many cross-validated scores. Some of that “best” is luck: among enough noisy candidates, one will look good by chance. Expect the test score to come in a little lower, as it does here, and report the test score. If you need an unbiased estimate without a test set, nested cross-validation (a search inside each outer fold) gives one, at much higher cost.</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\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>A budget-friendly routine</span></p>\n<p>Start coarse and on a log scale, look at <code dir=\"ltr\">cv_results_</code>, then narrow around the best region. Tune the two or three parameters that control complexity (depth, leaf size, <code dir=\"ltr\">C</code>, learning rate) and leave the rest at defaults. If the top dozen configurations are within a standard deviation of each other, stop: you’re tuning noise.</p>\n</aside>\n<p>Searches multiply the number of models you fit, and with them the number of chances for information to leak from validation data into training. Next lesson is a field guide to leakage: how it happens, how to spot it, and why it produces the best scores you’ll ever see.</p>\n","toc":[{"id":"sec-1","text":"Grid search","level":2},{"id":"sec-2","text":"A grid where it matters","level":2},{"id":"sec-3","text":"Random search","level":2},{"id":"sec-4","text":"Searching more than model settings","level":2}],"takeaways":["<code dir=\"ltr\">GridSearchCV</code> tries every combination in a grid, cross-validating each; <code dir=\"ltr\">RandomizedSearchCV</code> samples a fixed number of combinations from lists or distributions.","Inside a pipeline, parameters are addressed as <code dir=\"ltr\">step__parameter</code>, for example <code dir=\"ltr\">model__C</code>.","After the search, <code dir=\"ltr\">best_params_</code> holds the winner and <code dir=\"ltr\">best_estimator_</code> is that model refit on all the training data.","<code dir=\"ltr\">best_score_</code> is slightly optimistic because it’s the best of many tries; the test set gives the unbiased number.","Search on a log scale for parameters like <code dir=\"ltr\">C</code>, <code dir=\"ltr\">alpha</code> and <code dir=\"ltr\">learning_rate</code>, and expect tuning to add points, not miracles."],"further":[{"title":"Tuning the hyper-parameters of an estimator","url":"https://scikit-learn.org/stable/modules/grid_search.html"},{"title":"GridSearchCV","url":"https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html"},{"title":"RandomizedSearchCV","url":"https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html"}],"quiz":[{"q":"<p>Your pipeline is <code dir=\"ltr\">Pipeline([(&quot;prep&quot;, preprocess), (&quot;clf&quot;, LogisticRegression())])</code>. Which grid tunes the regularisation strength?</p>","options":[{"text":"<code dir=\"ltr\">{&quot;C&quot;: [0.1, 1, 10]}</code>","why":"The pipeline has no parameter called <code dir=\"ltr\">C</code>; the search would raise an invalid-parameter error."},{"text":"<code dir=\"ltr\">{&quot;LogisticRegression__C&quot;: [0.1, 1, 10]}</code>","why":"The prefix is the step’s name, not the class name."},{"text":"<code dir=\"ltr\">{&quot;clf.C&quot;: [0.1, 1, 10]}</code>","why":"scikit-learn uses a double underscore, not a dot, to reach into steps."},{"text":"<code dir=\"ltr\">{&quot;clf__C&quot;: [0.1, 1, 10]}</code>","why":"Correct. Step name, two underscores, parameter name."}],"answer":3},{"q":"<p>A grid has 5 values for <code dir=\"ltr\">max_depth</code> and 5 for <code dir=\"ltr\">min_samples_leaf</code>, with <code dir=\"ltr\">cv=5</code>. How many models are fitted, including the final refit?</p>","options":[{"text":"126: 25 combinations × 5 folds, plus one refit on all the training data.","why":"Correct. With the default <code dir=\"ltr\">refit=True</code> the winner is retrained once more at the end."},{"text":"25: one per combination.","why":"Each combination is cross-validated, so it’s fitted once per fold."},{"text":"10: five values plus five values.","why":"A grid tries every combination, not each value on its own."}],"answer":0},{"q":"<p>Why might <code dir=\"ltr\">RandomizedSearchCV</code> with 20 iterations beat a 400-combination grid on the same budget of time?</p>","options":[{"text":"Random search uses a smarter optimiser that learns from previous trials.","why":"Plain random search doesn’t learn between trials; each sample is independent."},{"text":"Random search always finds the true optimum.","why":"It samples, so it can miss the optimum; the point is efficiency, not guarantees."},{"text":"When only a few parameters matter, random sampling tries many more distinct values of those parameters than a grid does.","why":"Correct. A grid wastes trials repeating the same values of the important parameter while varying unimportant ones."}],"answer":2},{"q":"<p>The search reports <code dir=\"ltr\">best_score_ = 0.813</code>, and the refit model scores 0.805 on the test set. What’s going on?</p>","options":[{"text":"The test set must be leaking, since scores should match exactly.","why":"Scores differ on different data; a leak would push the test score up, not down."},{"text":"The best of many cross-validated scores is a little optimistic; the test score is the honest estimate.","why":"Correct. Picking the maximum of noisy scores favours lucky configurations, a winner’s curse."},{"text":"The refit on all training data made the model worse.","why":"More training data rarely hurts; the gap comes from how the winner was selected."}],"answer":1}],"exercise":{"kind":"python","files":["churn.csv"],"starter":"import pandas as pd\nfrom sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.preprocessing import StandardScaler\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\"])\ncv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n\npipe = Pipeline([(\"scale\", StandardScaler()), (\"knn\", KNeighborsClassifier())])\n\n# 1. Grid: n_neighbors in [5, 15, 25, 35, 51] and weights in [\"uniform\", \"distance\"]\ngrid = {\"n_neighbors\": [5, 15, 25, 35, 51], \"weights\": [\"uniform\", \"distance\"]}\n\n# 2. Search with the provided cv, scoring ROC AUC, fitted on the training data\nsearch = None\n\n# 3. Results\nbest_params = None\nbest_cv_auc = None\ntest_auc = None   # ROC AUC of the refit best model on the test set\n","solution":"import pandas as pd\nfrom sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.preprocessing import StandardScaler\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\"])\ncv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n\npipe = Pipeline([(\"scale\", StandardScaler()), (\"knn\", KNeighborsClassifier())])\n\ngrid = {\"knn__n_neighbors\": [5, 15, 25, 35, 51], \"knn__weights\": [\"uniform\", \"distance\"]}\n\nsearch = GridSearchCV(pipe, grid, cv=cv, scoring=\"roc_auc\")\nsearch.fit(X_train, y_train)\n\nbest_params = search.best_params_\nbest_cv_auc = search.best_score_\ntest_auc = search.score(X_test, y_test)\nprint(best_params, round(best_cv_auc, 3), round(test_auc, 3))\n","checks":[{"id":"grid-names","test":"set(grid) == {'knn__n_neighbors', 'knn__weights'}"},{"id":"searched","test":"search is not None and len(search.cv_results_['params']) == 10 and search.scoring == 'roc_auc' and search.cv is cv and hasattr(search, 'best_estimator_')"},{"id":"best","test":"best_params is not None and best_params == search.best_params_ and best_params['knn__n_neighbors'] in [15, 25, 35, 51] and abs(float(best_cv_auc) - float(search.best_score_)) < 1e-9 and 0.77 < float(best_cv_auc) < 0.82"},{"id":"test-auc","test":"test_auc is not None and abs(float(test_auc) - float(search.score(X_test, y_test))) < 1e-9 and 0.75 < float(test_auc) < 0.82"}],"title":"Tune k-nearest neighbours","prompt":"<p>Find the best number of neighbours for a scaled k-NN pipeline.</p>\n<ol>\n<li>Fix <code dir=\"ltr\">grid</code> so its keys address the pipeline’s <code dir=\"ltr\">knn</code> step (the starter keys would raise an invalid-parameter error).</li>\n<li><code dir=\"ltr\">search</code>: a <code dir=\"ltr\">GridSearchCV</code> over <code dir=\"ltr\">pipe</code> with the provided <code dir=\"ltr\">cv</code> and <code dir=\"ltr\">scoring=&quot;roc_auc&quot;</code>, fitted on the training data.</li>\n<li><code dir=\"ltr\">best_params</code>, <code dir=\"ltr\">best_cv_auc</code>: the winning settings and their mean cross-validated AUC. <code dir=\"ltr\">test_auc</code>: the refit winner’s ROC AUC on the test set.</li>\n</ol>\n","hints":["Pipeline parameters are <code dir=\"ltr\">&lt;step name&gt;__&lt;parameter&gt;</code>: here <code dir=\"ltr\">knn__n_neighbors</code> and <code dir=\"ltr\">knn__weights</code>.","<code dir=\"ltr\">GridSearchCV(pipe, grid, cv=cv, scoring=&quot;roc_auc&quot;).fit(X_train, y_train)</code>","<code dir=\"ltr\">search.best_params_</code>, <code dir=\"ltr\">search.best_score_</code>, and <code dir=\"ltr\">search.score(X_test, y_test)</code> (which uses the search’s scoring)."],"explanation":"<p>Five neighbours is too few: each prediction rests on a handful of customers and the AUC is about 0.75. Around 25–35 neighbours the ranking improves to about 0.79, and the two weighting schemes barely differ. The winner (35, uniform) scores 0.795 in cross-validation and 0.785 on the test set: slightly lower, as expected for the best of ten tries. Still a touch behind logistic regression, so the simpler model keeps its place.</p>\n","checkLabels":{"grid-names":"grid keys use the knn__ prefix","searched":"search tried all 10 combinations with ROC AUC","best":"best_params and best_cv_auc come from the search","test-auc":"test_auc is the refit winner’s test ROC AUC"}},"words":697,"readMinutes":3}