Concepts / Grid search for hyperparameter tuning

Grid search for hyperparameter tuning

Grid search is a way of trying out different settings and keeping the combination that works best.

  • Programming

The Object Grid Search Works On

Grid search is a way of trying out different settings and keeping the combination that works best. Before you can search over anything, though, you need an object to search over. In scikit-learn that object is often a Pipeline, which bundles several processing steps into one unit. This article starts with the pipeline itself, because grid search later changes the steps inside it.

python
Notes on the code
  • The list holds two (name, estimator) pairs, so the pipeline has two steps.
  • 'preprocessing' is the label for the scaling step; 'classifier' is the label for the SVC step.
  • The list order is the run order: preprocessing comes before the classifier.

Read the line that builds pipe as a recipe rather than as a computation. It creates a pipeline with two steps. Nothing has been learned from data yet. The rest of this article follows that pipe object through three stages: how it is defined, how its steps are named and ordered, and how a grid changes those steps before a search evaluates them.

Defining a Pipeline Without Fitting It

A Pipeline is built from a list of named steps, and the steps run in the order given. Defining the pipeline records those steps, but it does not fit anything yet. No scaling is computed and no classifier is trained at the moment the object is created. The pipeline is a single object that stores which steps exist and in what order.

What do you think happens?

After Pipeline([...]) is created but before fit is called, has the scaling step learned anything from data?

  • Yes, it has already computed the scaling statistics

  • No, defining the pipeline only records the steps

Reveal answer
Answer:

No, defining the pipeline only records the steps

Defining the pipeline only records which steps exist and their order. Learning happens later, when fit is called on the pipeline with data.

Training datapassed to fitpreprocessingStandardScalerclassifierSVCFitted pipelinesteps learned in orderraw datatransformed datafit
When fit is called on the pipeline, the data enters the preprocessing step first, and that step's output is handed to the classifier step.

The diagram shows the run order. Each named step receives the output of the step before it. The name is what identifies each step, so the same pipeline can later be referred to from outside by those labels.

Step Names and the Order They Run

The names matter as much as the estimators. Each name is a label for its step, and you use that label to refer to the step from outside the pipeline. In the pipeline above, 'preprocessing' and 'classifier' are the two labels. The order of the list is the order of the steps, so the preprocessing step comes before the classifier.

The label is also how a grid reaches inside the pipeline. When you want to change a setting on a particular step, you write a key that starts with that step's name. The key below is split into its three parts so you can see which part names the step and which part names the estimator's parameter.

classifierpipeline step name__separatorCparameter of the SVC
The first token names the pipeline step, the double underscore separates the step from the parameter, and the last token names the parameter on that step's estimator.

Searching Across Two Estimators

A common goal is to compare two different classifiers, such as RandomForestClassifier and SVC. Those two classifiers have different parameters to tune, and they may need different preprocessing. One fixed dictionary of parameters cannot describe both cases cleanly. So the search uses a list of search grids, and each grid in the list covers one combination of choices.

To put an estimator into a step, you use the step's name as the parameter name in the grid, and the value is the list of estimators to try for that step. The grid below has two entries. The first tries SVC with scaling, and the second tries a random forest with no scaling.

python
Notes on the code
  • 'classifier' lists the estimators to try for the classifier step.
  • 'preprocessing' lists the estimators to try for the preprocessing step.
  • 'classifier__C' is a parameter of the SVC; it is only meaningful when the SVC is the candidate.
  • The second dictionary sets 'preprocessing' to None, so its candidate runs without a scaling step.

Each dictionary in the list defines its own set of candidates, and the candidates are evaluated separately. The tree below shows how the grid expands into concrete candidate pipelines. Notice that the two branches never share parameters: the SVC branch has C values, and the random forest branch has tree-count values.

param_gridlist of two gridsGrid 1SVC with scalingSVC, C = 0.1candidate pipelineGrid 2random forest, no scalingSVC, C = 1candidate pipelineSVC, C = 10candidate pipelineRandomForest, 100treescandidate pipelineRandomForest, 200treescandidate pipelinedictionary 1dictionary 2combinationcombinationcombinationcombinationcombination
Each dictionary in the list becomes a branch, and each combination of listed values under that branch becomes one candidate pipeline to evaluate.

Skipping a Step with None

Sometimes one candidate needs a step that another candidate does not. The random forest in the example does not need preprocessing. To skip a step for a particular candidate, you set that step to None. In the grid, the second entry sets the preprocessing step to None, so the search tries the random forest without a scaling step, while the first entry still scales the data before the SVC.

Skipping is a per-candidate choice. Setting a step to None does not remove the step from the pipeline definition for every candidate. It only skips that step for the candidate in which the value appears. Other candidates in the search can still include the step.

Mistakes That Break a Grid Search

  • Common mistake

    Using a key that does not match any step name

    What it looks like

    A grid key spelled 'Classifier__C' when the pipeline step is named 'classifier'.

    Why it fails

    The grid finds steps by name, and a mismatch means it has no step to change.

    Do this instead

    Compare every key in the grid with the step labels in the pipeline, character by character.

  • Common mistake

    Treating the grid as if it refers to the estimator class

    What it looks like

    Writing the SVC class name as the key instead of the step name.

    Why it fails

    The grid refers to the step, and its value is the list of estimators to try for that step.

    Do this instead

    Use the step name as the key, and put the estimators in a list as its value.

  • Common mistake

    Putting parameters of one estimator into a grid that uses a different estimator

    What it looks like

    Giving a classifier__C value to a candidate whose classifier is a random forest.

    Why it fails

    A random forest does not have a C parameter, so that candidate has nothing to receive the value.

    Do this instead

    Put each estimator's parameters in the dictionary that describes that estimator's candidates.

  • Common mistake

    Forcing one dictionary to describe incompatible candidates

    What it looks like

    Putting SVC and random forest options into a single dictionary that also needs different preprocessing.

    Why it fails

    Different candidates need different parameters and preprocessing, which one fixed dictionary cannot describe cleanly.

    Do this instead

    Write a list of grids, with one dictionary per combination of choices.

  • Common mistake

    Assuming the pipeline has already been fitted when it is only defined

Finding Where a Grid Went Wrong

When a grid search does not behave as you expected, the cause is usually one of three things: a step's name, the value you assigned to a step, or the structure of the list. Narrow the problem down in that order. Start with the names, because the name is how the grid finds the step it is changing. Only after the names match should you check the values, and then the list structure.

  1. 1
    Compare every key in the grid with the step names in the pipeline, and fix any mismatch first.
  2. 2
    Check the value assigned to each step: it should be a list of estimators to try, or None where a step is meant to be skipped.
  3. 3
    Check that each dictionary in the list contains only parameters that belong to the estimators it lists.
  4. 4
    Check that the list contains one dictionary for each separate set of candidates.

Try It on a Broken Grid

EASY

The pipeline has steps named 'preprocessing' and 'classifier'. A colleague wrote the grid entry {'Classifier__C': [1, 10]} for an SVC candidate. Explain what the grid will fail to do and how you would correct the key.

Hints
  • Compare the key's spelling and capitalization with the step name in the pipeline.
  • The parameter C belongs to the SVC, so the key needs the step name, the double underscore, and then C.

Key Takeaways

  • A Pipeline is built from a list of named steps that run in the order given, and defining it records the steps without fitting anything.
  • To search over different estimators, assign each estimator to a step by using the step's name as the parameter name, with a list of estimators as the value.
  • Use a list of search grids when candidates need different parameters and preprocessing, so each grid covers one combination of choices.
  • Setting a step to None skips that step for a particular candidate, which is useful when an estimator does not need preprocessing.
  • When a grid misbehaves, check step names first, then the values assigned to steps, then the structure of the list.

Related concepts

More concepts starting with G