Concepts / Simple Grid Search

Simple Grid Search

A simple grid search is a set of nested for loops that trains and evaluates a classifier for every combination of candidate parameter values.

  • Programming

Why More Cores Does Not Settle the Question

A grid search is a method for finding good parameter values for a classifier. You list candidate values, try them, and keep the combination that scores best. Once you learn that a grid search can run in parallel, a natural belief follows: if one core makes the search slow, giving the search every core on the machine should make it as fast as possible. That belief is partly correct, but it is incomplete in a way that can cost time and memory. This article starts with a complete script, traces what it does, and then builds the reasoning needed to use parallelism well.

The article has three goals. First, you will follow a simple grid search step by step and count the models it trains. Second, you will see why grid search splits so neatly across cores, and where that splitting runs into limits. Third, you will examine why the reported best score can mislead you when the parameters are chosen using the test data.

Following the Nested Loops

A simple grid search is a set of nested for loops that trains and evaluates a classifier for every combination of candidate parameter values. The source introduces it using two parameters of a support vector classifier, SVC: gamma and C. The outer loop chooses a gamma value and holds it fixed. The inner loop then cycles through every C value. When the inner loop finishes, the outer loop advances to the next gamma, and the inner loop starts again from the first C value. Because of this structure, every pair of values is visited exactly once.

python
Notes on the code
  • The import lines and the train_test_split call must run before the loops, or the script fails
  • The outer for loop chooses gamma and holds it fixed
  • The inner for loop cycles through every C value for that gamma
  • A new SVC is built with the current gamma and C on every pass
  • The if statement is the only place where the stored answer can change

Inside the inner loop, four steps repeat for every combination. A new SVC is built with the current gamma and C. The model is fitted on the training data. The fitted model is scored on the test data. Finally, an if statement compares that score with best_score. If the new score is higher, both best_score and the best_parameters dictionary are overwritten. Every combination is trained and scored, but the stored result is replaced only when a new score beats the current best.

gamma 0.1, C 1fit and scoregamma 0.1, C 10fit and scoregamma 1, C 1fit and scoregamma 1, C 10fit and scoreinner loop advancesouter loop advances, inner restartsinner loop advances
Which gamma and C pairs get trained and scored, and in what order do the outer and inner loops visit them? This diagram shows a simplified 2 by 2 grid. The real script uses 6 by 6 values.

What do you think happens?

The script uses 6 gamma values and 6 C values. How many models does the nested loop train and evaluate in total?

Reveal answer
Answer:

36 models

The nested loops multiply the list lengths. Each of the 6 gamma values is paired with each of the 6 C values, which gives $6 \times 6 = 36$ combinations, and each combination produces one trained and scored model.

Worked example

Counting models when cross-validation is added

Suppose the same 36 parameter combinations are each evaluated with 5 cross-validation folds. How many models are trained in total? This extension is not part of the simple script, but it shows how the count multiplies.

  1. 1
    Count the parameter combinations

    The grid has 6 gamma values and 6 C values, so there are $6 \times 6 = 36$ combinations.

  2. 2
    Count the folds per combination

    Each combination is evaluated on 5 folds, so each combination trains 5 models.

  3. 3
    Multiply

    The total is $36 \times 5 = 180$ models.

Result

180 models, one per combination per fold

Tracking best_score and best_parameters

The stored answer starts empty. best_score begins at a value below any real score, and best_parameters is an empty dictionary. Only the if comparison can change either variable. The comparison replaces them only when a new score is higher than the current best_score. A lower score is evaluated and then ignored. The print statements run once, after every loop has finished, so they report the final stored state rather than any intermediate one.

best_score = 0start, emptybest_parametersbest_score = 0.90gamma 0.1, C 1 scored 0.90best_score = 0.90gamma 0.1, C 10 scored 0.85best_score = 0.95gamma 1, C 1 scored 0.95best_score = 0.95gamma 1, C 10 scored 0.93best_score = 0.97gamma 10, C 1 scored 0.970.90 beats 00.85 does not beat 0.900.95 beats 0.900.93 does not beat 0.950.97 beats 0.95
Hypothetical scores for six evaluations, used only to show the update rule. Each node is the value of best_score after an evaluation. Nodes marked changed are the evaluations where a higher score replaced the stored best.

Read the diagram as a trace. Only three evaluations change the stored answer. The scores 0.85 and 0.93 are computed and then discarded, because each is lower than the best seen so far. This is the core rule of the script: the search keeps every score in its head only long enough to compare it, and it keeps just the winning combination.

Why Grid Search Splits Across Cores

Grid search and cross-validation are embarrassingly parallel. The term means that the work divides into pieces that never need to talk to each other. Consider one combination of parameter settings evaluated on one cross-validation split. Building that model needs only the training portion of that split and the chosen parameter values. It does not need the result of any other combination or any other split. Each piece can therefore run on its own, in any order, on any core.

Parameter gridevery combination and splitCore 1one fit and score jobGathered scorescompared once jobs finishBest combinationhighest score keptCore 2one fit and score jobCore 3one fit and score jobindependent jobindependent jobindependent jobscore returnsscore returnsscore returnscompare
How the independent model fits are split across worker cores, and how their scores come back together into one result. No core passes its model to another core. Only scores flow back to be compared.

Locating Failures in the Script

When a grid search script fails or gives a strange result, the cause usually sits in one of a few places. Trace the script in order and check each place before blaming the model.

  • Common mistake

    Running the loops without the setup that defines the data

    What it looks like

    The loops call train_test_split and refer to iris, but the import and load lines were never run, so the script stops with a NameError before any model is trained.

    Why it fails

    The snippet depends on earlier setup for train_test_split and iris. The loop body cannot run if the names it uses do not exist yet.

    Do this instead

    Run the import and data-loading lines first, then the split, then the loops.

  • Common mistake

    Expecting the best score to come from an unexpected place

    What it looks like

    A reader assumes the reported best combination is the last one the loops visited.

    Why it fails

    Only the if comparison changes best_score and best_parameters, and it replaces them only when a new score is higher. The last combination may score lower than an earlier one.

    Do this instead

    Trace the if statement for each evaluation, as in the best_score trace above.

  • Common mistake

    Assuming all 36 models share one training set

    What it looks like

    A reader assumes each SVC is trained on different data for each pair.

    Why it fails

    In the script, every model is fitted on the same training data and scored on the same test data. Only gamma and C change between models.

    Do this instead

    Keep the data fixed and vary only the parameters, so the comparison between combinations is fair.

  • Common mistake

    Nesting the loops in the wrong order changes the visit order but not the total count, so the number of models stays at 36 either way.

Overfitting the Parameters and the Validation Set

The source section on this implementation is titled to flag a danger. In the script, every combination is scored on the test set, and the combination with the highest test score is reported as best. The parameters were therefore picked to fit the test data. The reported best score is no longer an unbiased estimate of how the model will perform on new data. The selection process has leaked information from the test set into model choice.

Test setheld out for finalevaluationScore each pairgamma and C combinationsKeep highest scoreparameters chosen by testresultReported best scoreoptimistic estimatescores usedselectionreportedfeeds back into the test data
How repeatedly choosing parameters by test-set score feeds information from the test set back into model selection, so the test set stops being independent.

Practice: Predicting and Tracing a Grid

EASY

Change the script so that gamma takes 3 values and C takes 4 values. Before running anything, predict the number of models trained. Then state which combination the outer loop visits first, and which combination it visits last.

Hints
  • The total is the number of gamma values multiplied by the number of C values.
  • The outer loop uses the first gamma value, and the inner loop runs through all 4 C values before the outer loop advances.
  • The expected total is $3 \times 4 = 12$ models.

Key Takeaways

  • A simple grid search is a set of nested for loops that trains and evaluates a classifier for every combination of candidate parameter values. Two lists of 6 values each produce 36 models.
  • Only the if comparison changes best_score and best_parameters, and it replaces them only when a new score is higher. Lower scores are computed and then discarded.
  • Grid search and cross-validation are embarrassingly parallel because each fit is independent. Use n_jobs to set the core count, with -1 requesting all cores, but keep parallelism at the search level and monitor memory.
  • Choosing parameters by test-set score makes the reported best score optimistic. Keep a separate validation set for selection and a test set for the final, one-time evaluation.

Related concepts

More concepts starting with S