Grid search for hyperparameter tuning
Grid search is a way of trying out different settings and keeping the combination that works best.
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.
- 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?
Reveal 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.
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.
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.
- '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.
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 likeA grid key spelled 'Classifier__C' when the pipeline step is named 'classifier'.
Why it failsThe grid finds steps by name, and a mismatch means it has no step to change.
Do this insteadCompare 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 likeWriting the SVC class name as the key instead of the step name.
Why it failsThe grid refers to the step, and its value is the list of estimators to try for that step.
Do this insteadUse 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 likeGiving a classifier__C value to a candidate whose classifier is a random forest.
Why it failsA random forest does not have a C parameter, so that candidate has nothing to receive the value.
Do this insteadPut 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 likePutting SVC and random forest options into a single dictionary that also needs different preprocessing.
Why it failsDifferent candidates need different parameters and preprocessing, which one fixed dictionary cannot describe cleanly.
Do this insteadWrite 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.
- 1Compare every key in the grid with the step names in the pipeline, and fix any mismatch first.
- 2Check 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.
- 3Check that each dictionary in the list contains only parameters that belong to the estimators it lists.
- 4Check that the list contains one dictionary for each separate set of candidates.
Try It on a Broken Grid
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.