Concepts / Building Pipelines with Preprocessing Steps

Building Pipelines with Preprocessing Steps

Keys such as classifier__C use the double underscore to point at a parameter inside a named pipeline step, while keys such as preprocessing list the step's possible objects directly.

  • Programming

Searching Models and Preprocessing Together

Earlier in a machine learning workflow, you often tune the settings of one model and compare the results by hand. You try a few values, note which one scores best, and move on. That approach answers a narrow question: which hyperparameter values work best for this one model?

This article covers a more complete question. Instead of asking only which hyperparameter values work best, you ask which combination of model, preprocessing step, and hyperparameter values works best. The source works through this with the cancer dataset. It compares a Support Vector Classifier (SVC) against a RandomForestClassifier, and it also chooses between scaling the data with StandardScaler or leaving it unchanged.

The key idea is that GridSearchCV does not care what kind of thing it is searching over. A number, a model object, and a preprocessing step are all treated the same way. The search tries each candidate in the grid, evaluates it, and reports which one scored best. Once you understand how the grid is written, the rest of the workflow follows from it.

Reading the Parameter Grid

Definition

A parameter grid is a Python list in which each entry is a dictionary describing one family of candidates. Each dictionary maps a key to a list of values to try. Because the grid is a list of dictionaries, GridSearchCV can search over models and preprocessing steps at the same time.

The central object in this workflow is the parameter grid. The source defines a grid with two entries. The first entry covers the SVC branch, and the second covers the RandomForestClassifier branch. Within each entry, the key 'classifier' holds the model objects to try, and the key 'preprocessing' holds the preprocessing choices. Other keys hold hyperparameter values for that model.

python
Notes on the code
  • The first dictionary is the SVC family: one model, two preprocessing choices, and two hyperparameters with three values each.
  • The second dictionary is the RandomForestClassifier family. It only lists keys that apply to its own model.
  • The numeric values in this grid are an illustration written for this article, not the source's exact values.

Notice that the two entries are independent. The search does not force the RandomForestClassifier to be combined with the SVC's gamma and C values. Each entry defines only the keys that apply to its own model. This is why a single grid can hold several different model families without mixing their hyperparameters together.

Double Underscore Keys

Keys such as classifier__gamma and classifier__C use a double underscore to point at a setting inside a pipeline step. The part before the double underscore names the step. The part after it names the parameter of that step. So classifier__C means the C parameter of whatever object is stored under the 'classifier' step.

classifiername of the pipeline step__separator between step andparameterCparameter of that step
How the key classifier__C maps to the C parameter inside the classifier step.

The preprocessing key works differently. It is not a parameter of a model at all. It names a step, and its possible objects are listed directly. In the grid above, those objects are StandardScaler() and None. So a key with no double underscore lists whole step objects, while a key with a double underscore reaches into a step and names one of its parameters.

Counting Candidates Before Running

Before you run a grid search, it helps to know how much work it will do. Count the candidates in each entry of the grid, then add the entries together. The number of candidates in one entry is the product of the lengths of its value lists. Each key contributes one factor, and a key with a single value contributes a factor of one.

Formula

In this formula, N is the total number of candidates, e runs over the entries of the grid, k runs over the keys inside one entry, and |V_{e,k}| is the number of values listed under key k in entry e. Multiplication happens within an entry, because every combination of values inside that family is a candidate. Addition happens between entries, because the families do not combine with each other.

What do you think happens?

Using the illustrative grid in the previous section, how many candidate combinations will GridSearchCV evaluate in total? Count the SVC entry and the RandomForest entry separately before adding them.

Reveal answer
Answer:

24 candidates: the SVC entry gives 1 x 2 x 3 x 3 = 18, and the RandomForest entry gives 1 x 2 x 3 = 6, so 18 + 6 = 24.

Within the SVC entry, the model has one value, preprocessing has two, gamma has three, and C has three. Those lengths multiply. The RandomForest entry has one model, two preprocessing choices, and three max_features values. The two entries are then added.

Parameter gridtwo entriesSVC entry1 x 2 x 3 x 3 = 18candidatesTotal candidates18 + 6 = 24RandomForest entry1 x 2 x 3 = 6 candidatesentry 1entry 2186
How the two grid entries multiply within themselves and then add together to give the total number of candidates.
Worked example

Counting the fits for a five-fold search

The grid has 24 candidates, as counted above. GridSearchCV is run with cv=5. How many model fits does the search perform?

  1. 1
    Count the candidates

    The grid holds 24 candidate combinations across both entries.

  2. 2
    Apply the fold count

    The source notes that cv=5 multiplies the number of fits. Each candidate is fitted once per fold, so each candidate is fitted five times.

  3. 3
    Multiply

    24 candidates multiplied by 5 folds gives 120 fits.

Result

The search performs 120 fits. Adding more values to any list in the grid increases this number, and so does increasing cv.

Running the Search with a Pipeline

Once the grid is defined, the source runs the search in the usual way. The data is first split into training and test sets with train_test_split, using random_state=0 so the split is repeatable. The grid is then built around a pipeline object, which the source refers to as pipe. GridSearchCV is asked to use five cross-validation folds. Calling fit runs the whole search on the training data only. The test set is held back until the end.

python
Notes on the code
  • X and y are assumed to hold the cancer dataset's features and labels.
  • The pipe object is assumed to already exist, with a step named 'preprocessing' and a step named 'classifier'. The source uses pipe without defining it here.
  • fit uses only the training split. The test split is used only by the final score call.

It helps to trace what happens to the data when a candidate is evaluated. For each candidate, the data first passes through the preprocessing step, which is either StandardScaler or no change at all. The output of that step is what the classifier step receives. The classifier is then fitted and scored. This sequence is repeated for each fold, so every candidate passes through the same pipeline structure five times in this setup.

Training dataone fold's training partPreprocessing stepStandardScaler or NoneClassifier stepSVC orRandomForestClassifierCross-validationscorefor this candidate and foldpasses intooutput received byproduces
What the data passes through first, what receives its output, and where the score comes from for one candidate on one fold.

Reading the Winning Combination

The printed output reports three different things, and each one answers a different question. The best_params_ attribute reports the winning combination across both models and the preprocessing step, not just the best hyperparameter value. In the source's run, the selected classifier is an SVC, the selected preprocessing is a StandardScaler, and the selected values are the ones stored under classifier__C and classifier__gamma. Reading this dictionary tells you which branch of the grid won, not only which number was best.

The best_score_ attribute reports the best cross-validation score from the training data. This is the score the search used to pick the winner. The score method, called on the held-out test set, reports how the chosen model performs on data the search never saw.

QuestionScore usedData it is computed onRole in the workflow
Which candidate was selected?best_score_Training data, across foldsUsed by the search to choose the winner, so it is somewhat optimistic about the chosen model
How does the chosen model perform on unseen data?score on the test setTest data, never used during the searchThe final performance check

The two scores answer different questions and come from different data.

The two scores should be read together. When the cross-validation score and the test-set score are close, as they are in the source's results, that suggests the search did not overfit the cross-validation folds. A large gap between them would be a reason to be cautious about the reported performance, because the chosen model would look better on the data used to select it than on new data.

Mistakes Beginners Make with Grids

  • Common mistake

    Treating the whole grid as one family of combinations

    What it looks like

    Putting SVC's gamma and C values and RandomForestClassifier's settings into one dictionary, so the search tries mixed combinations.

    Why it fails

    Each entry in the list is a separate family. Keys that do not apply to a model have no place in that model's entry.

    Do this instead

    Give each model its own dictionary, and list only the keys that apply to that model.

  • Common mistake

    Writing a hyperparameter key without the step name

    What it looks like

    Using a key such as C where the pipeline expects classifier__C.

    Why it fails

    The double underscore is how the pipeline finds the step that owns the parameter. Without the step name, the pipeline does not know where to look.

    Do this instead

    Write the key as step name, double underscore, then parameter name, such as classifier__C.

  • Common mistake

    Expecting best_params_ to contain only a number

    What it looks like

    Reading best_params_ and assuming it only tells you the best C value.

    Why it fails

    best_params_ reports the winning combination across models and preprocessing, so it can name a different classifier entirely.

    Do this instead

    Read the whole dictionary to see which branch of the grid won, then read the hyperparameter values inside it.

  • Common mistake

    Reporting the best cross-validation score as the final result

    What it looks like

    Claiming the chosen model reaches the best_score_ value on new data.

    Why it fails

    The cross-validation score was used to select the winner, so it is somewhat optimistic. Only the test-set score uses data the search never saw.

    Do this instead

    Report the test-set score as the final performance check, and compare it with the cross-validation score.

  • Common mistake

    Forgetting that the candidate count multiplies with cv, so a grid that looks small can produce many fits

Practice Predicting the Search Size

MEDIUM

A grid has two entries. The first entry lists one classifier, two preprocessing choices, four values for classifier__C, and three values for classifier__gamma. The second entry lists one classifier, two preprocessing choices, and five values for classifier__max_features. Before running anything, predict how many candidates GridSearchCV evaluates, and then how many fits it performs with cv=5.

Hints
  • Count the candidates in the first entry by multiplying the lengths of its value lists: one classifier, two preprocessing choices, four C values, and three gamma values.
  • Count the second entry the same way, then add the two entries together.
  • Multiply the total number of candidates by 5 to get the number of fits.
EASY

Explain in one or two sentences why best_score_ and the test-set score should be compared, and what a large gap between them would suggest.

Hints
  • Think about which data was used to choose the winning candidate.
  • Think about what the test set was held back for.

Key Takeaways

  • A parameter grid is a list of dictionaries, and each dictionary describes one family of candidates that GridSearchCV explores.
  • Keys with a double underscore, such as classifier__C, point at a parameter inside a named pipeline step, while keys such as preprocessing list that step's possible objects directly.
  • The number of candidates is found by multiplying the value-list lengths inside each entry and then adding the entries together, and cv multiplies the number of fits.
  • best_params_ names the winning combination across models and preprocessing, best_score_ is the cross-validation score used for selection, and the test-set score is the final check on data the search never saw.
  • When the cross-validation score and the test-set score are close, the search is less likely to have overfit the folds; a large gap calls for caution.

Related concepts

More concepts starting with B