Grid-Searching Preprocessing Steps and Model Parameters
A grid search can tune preprocessing parameters and model parameters together when both are steps in one pipeline.
The Search Winner Is a Whole Pipeline
When you tune a model with GridSearchCV, the search tries many parameter combinations and keeps one winner. In many workflows that winner is not just a bare classifier. When the object being searched is a pipeline, the winner is a complete pipeline, with its preprocessing steps and its model, all fitted together. Once the search has finished, a practical question follows: how do you get inside that fitted pipeline and read the numbers the model has learned?
The answer is a short chain of attribute lookups. Each lookup moves you one level deeper into the structure: from the search object, to the best model it found, to one step of that model, to a learned quantity inside that step. Beginners most often go wrong when they try to walk this chain by themselves, because each link is a separate lookup and a wrong key or a skipped level breaks the whole expression.
Walking Down the Attribute Chain
Start with the search object, the one you created with GridSearchCV. The attribute best_estimator_ is found on this search object, not on the pipeline you originally handed to it. The search keeps the record of which configuration won, and it exposes that winning configuration as best_estimator_. The source describes this model as already refit on all of the training data, so it is the version that produced the winning result.
- GridSearchCV wraps the pipeline and the
param_grid; fit runs the whole search - best_estimator_ is an attribute of the search object
- Printing the result is the simplest way to confirm what you got back
The printed representation is your evidence. In the source example, printing best_estimator_ shows a Pipeline object whose steps are listed by name: a StandardScaler first and a LogisticRegression second. That printed text confirms that best_estimator_ is a pipeline with multiple steps, which is the fact everything later in this article depends on.
The next link is named_steps. A pipeline's named_steps attribute gives you each step by its name. You index it like a dictionary, passing the step name as a string, and you get back that step's fitted object. Read the whole expression from left to right: grid is the search object, best_estimator_ moves you to the winning pipeline, named_steps moves you to the dictionary of its steps, and the string key picks one step out of that dictionary.
- 'logreg' must match the name given in the steps list exactly
named_stepsis indexed like a dictionary with a string key- coef_ is read from the fitted LogisticRegression step
The coef_ attribute belongs to the fitted LogisticRegression step, so the lookup must pass through named_steps before reading it. The coefficients are the weights the model learned for each input feature, and because this step was trained as part of the best pipeline, these weights are the ones that actually produced the search's winning result. The printed output is nested in double brackets, which signals a two-dimensional array with one row of weights and one value per input feature. Reading that structure correctly is part of interpreting the model, not just retrieving it.
Writing Keys That Reach Each Step
A grid search can tune preprocessing parameters and model parameters together when both are steps in one pipeline. Consider a pipeline that adds polynomial features before a Ridge regression model. It has two settings that need a decision: the degree of the polynomial features, which controls how far each existing feature is raised to a power, and the alpha parameter of Ridge regression, which controls the regularization strength. Because the pipeline already contains both steps, a single search can vary the degree and the alpha value together and compare every combination on the same cross-validated scores.
The search must know which step each value belongs to. Hyperparameter values are passed to a step by prefixing the parameter name with the step name, separated by a double underscore. Each key in the param_grid is written as the step name followed by the parameter name, such as polynomialfeatures__degree and ridge__alpha. Each key therefore tells the search which step to change and which parameter to change inside that step.
- The step names polynomialfeatures and ridge must match the keys exactly
- The double underscore separates the step name from the parameter name
- The value lists here are illustrative choices, not values from the source
Counting the Candidates in a Grid
Before reading any search output, it helps to know how large the search is. A grid search tries every combination of the values listed in the param_grid, so the number of candidates is the product of the number of values for each key. Cross-validation then multiplies that count, because each candidate is evaluated once per fold.
Counting candidates for the degree and alpha grid
The param_grid has polynomialfeatures__degree with 3 values and ridge__alpha with 6 values. How many candidate combinations does the search evaluate?
- 1Count the values for each key
The degree key has 3 values, and the alpha key has 6 values.
- 2Multiply the counts
Every degree is paired with every alpha, so the number of candidates is $3 \times 6 = 18$.
- 3Check against the source's score count
The source reshapes 18 mean test scores into a grid with 3 rows, which leaves $18 / 3 = 6$ columns, matching the 6 alpha values.
The grid defines 18 candidates, and with 5 cross-validation folds the search performs 90 fits in total.
What do you think happens?
Before reading the answer, predict how many candidate combinations this param_grid defines: a degree key with 3 values and an alpha key with 6 values.
Reveal answer
18 candidates, because 3 times 6 equals 18.
Each candidate is one pairing of a degree value with an alpha value, so the total is the product of the two counts.
Reshaping Scores into a Grid
After the search runs, the results can be visualized as a heat map. The scores are read from the cv_results_ attribute, specifically the mean test score for each candidate. The 18 mean scores are reshaped into a grid with 3 rows and as many columns as needed, so each row corresponds to one polynomial degree and each column to one alpha value. Each cell is one candidate pairing, and the color reflects the averaged cross-validation score for that pairing.
The layout follows directly from the reshape call. The source places degrees on the vertical axis and alpha values on the horizontal axis. The heat map is a way of reading the search result, not a separate calculation, so its shape must always match the grid you defined. The flat array is in candidate order, and the reshape simply cuts it into rows.
Where Grid Search Results Diverge
When the heat map or the best score does not match what you expected, the cause is usually found in one of a few specific places. Work through them in order: first the keys of the param_grid, then the step names in the pipeline, then the reshape call, and last the axis labels. Each of these is visible in the code, so you can check them directly rather than guessing.
Common mistake Using a key without the step-name prefix
What it looks likeparam_grid = {'degree': [1, 2, 3]}Why it failsThe search needs to know which step owns the parameter, and the step name is the prefix before the double underscore.
Do this insteadUse the full key, such as 'polynomialfeatures__degree'.
Common mistake Misspelling a step name in the param_grid or in named_steps
What it looks likenamed_steps['Ridge'] when the step was named 'ridge'
Why it failsEach link in the chain is a separate lookup, and a key that does not match exactly fails.
Do this insteadCopy the step name from the pipeline's steps list and use it unchanged in every lookup.
Common mistake Reading coef_ from the search object or the pipeline
What it looks likegrid.coef_ or grid.best_estimator_.coef_Why it failsThe learned weights live on the model step, not on the wrapper.
Do this insteadGo through named_steps to the LogisticRegression or Ridge step, then read coef_.
Common mistake Reshaping the scores with the wrong number of rows
What it looks likeReshaping 18 scores into 4 rows
Why it failsThe row count must match the number of values in one key, and mismatched shapes produce a grid that does not correspond to the candidates.
Do this insteadSet the rows to the length of one key's value list and confirm that the total divides evenly.
Predicting the Lookup Before Running It
What do you think happens?
After grid.fit(...), what type is grid.best_estimator_ when the searched object is a pipeline with a scaler and a model?
Reveal answer
A Pipeline object, with its steps listed by name.
The source's printed output shows best_estimator_ as a Pipeline containing a StandardScaler and a LogisticRegression, so the winner is the complete pipeline, not a bare classifier.
A colleague writes grid.best_estimator_.coef_ and gets an error. Using the chain of lookups, rewrite the expression so it reads the coefficients of the logistic regression step named 'logreg' in the winning pipeline. Then predict the shape of the result: is it one-dimensional or two-dimensional, and how many values does each row hold?
Hints
- The wrapper does not hold the weights; the model step does.
- Pass through
named_stepswith the exact string key before reading coef_. - The printed output uses double brackets, which signals a two-dimensional array with one row.
Key Takeaways
- GridSearchCV exposes the winning configuration as grid.best_estimator_, which is a complete pipeline refit on all of the training data.
named_steps[step_name] reaches one step of the fitted pipeline by its exact name, and that step's coef_ attribute holds the learned weights.- A
param_gridkey is the step name followed by a double underscore and the parameter name, so one search can tune preprocessing and model settings together. - The number of candidates is the product of the value counts for each key, and cross-validation multiplies that count into the total number of fits.
- When results diverge, check the
param_gridkeys, the step names, the reshape call, and the axis labels, in that order.