Keras
GridSearchCV
Model Tuning
Machine Learning
Python Errors

'Sequential' object has no attribute 'loss' - When I used GridSearchCV to tuning my Keras model

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

The error 'Sequential' object has no attribute 'loss' during GridSearchCV usually means scikit-learn is interacting with the wrong kind of object. GridSearchCV expects an estimator that follows the scikit-learn API, but a raw Keras Sequential model is only part of that contract. The reliable fix is to wrap model creation in a scikit-learn-compatible estimator and make sure the model is compiled before each fit.

Why the Error Happens

A plain Keras Sequential model is not a native scikit-learn estimator. GridSearchCV expects methods such as get_params, set_params, and predictable cloning behavior. On top of that, the model must be compiled before training so attributes such as loss and optimizer are initialized correctly.

A common broken pattern looks like this:

python
1from sklearn.model_selection import GridSearchCV
2from tensorflow import keras
3
4model = keras.Sequential([
5    keras.layers.Dense(32, activation="relu", input_shape=(10,)),
6    keras.layers.Dense(1, activation="sigmoid")
7])
8
9search = GridSearchCV(model, param_grid={"epochs": [5, 10]})

This fails because model is not a scikit-learn estimator designed for cloning and tuning.

Use a Wrapper That Builds and Compiles the Model

The cleanest modern approach is to use SciKeras, which provides scikit-learn wrappers for Keras models.

python
1from scikeras.wrappers import KerasClassifier
2from sklearn.model_selection import GridSearchCV
3from tensorflow import keras
4
5
6def build_model(units=32, learning_rate=0.001):
7    model = keras.Sequential([
8        keras.layers.Input(shape=(10,)),
9        keras.layers.Dense(units, activation="relu"),
10        keras.layers.Dense(1, activation="sigmoid")
11    ])
12    model.compile(
13        optimizer=keras.optimizers.Adam(learning_rate=learning_rate),
14        loss="binary_crossentropy",
15        metrics=["accuracy"]
16    )
17    return model
18
19clf = KerasClassifier(model=build_model, verbose=0)
20
21param_grid = {
22    "model__units": [16, 32, 64],
23    "model__learning_rate": [0.001, 0.01],
24    "epochs": [5, 10],
25    "batch_size": [16, 32]
26}
27
28search = GridSearchCV(clf, param_grid=param_grid, cv=3)
29search.fit(X_train, y_train)
30
31print(search.best_params_)
32print(search.best_score_)

The important points are:

  • 'GridSearchCV receives a wrapper estimator, not a raw Sequential object'
  • the build function compiles the model every time a new trial is created
  • hyperparameters for the build function are passed through the wrapper

Why Compilation Must Happen in the Build Function

Each parameter combination in grid search creates a fresh estimator. That means the model needs to be fully constructed and compiled inside the callable that produces it. If you build a model once outside the search and try to reuse it, the estimator cloning process becomes unreliable and state can leak between folds.

Compiling inside the build function also ensures the loss, optimizer, and metrics match the current hyperparameter combination.

Older Wrappers Versus Current Practice

You may still see examples using tensorflow.keras.wrappers.scikit_learn.KerasClassifier. Those older wrappers existed for years, but the SciKeras approach is generally more predictable and aligns better with scikit-learn behavior.

If you are maintaining legacy code, the same conceptual rule still applies: the wrapper must create a fresh compiled model for each trial.

Common Pitfalls

The most common mistake is passing a model instance instead of a model-building function through a proper wrapper. Grid search needs to clone estimators repeatedly, so a single prebuilt network is the wrong shape for the API.

Another mistake is forgetting to compile the model inside the build function. A model without a loss function cannot train correctly, and many errors around missing loss or broken fit behavior trace back to that omission.

Developers also often misname parameters in the grid. With SciKeras, build-function parameters are usually prefixed with model__. If the grid keys do not match the estimator's exposed parameters, tuning fails or silently does not test what you expected.

Finally, be realistic about compute cost. Running deep-learning models inside GridSearchCV multiplies training time by the number of parameter combinations and folds. Start with a small grid before scaling the search.

Summary

  • 'GridSearchCV should receive a scikit-learn-compatible wrapper, not a raw Keras Sequential model.'
  • The wrapper must build and compile a fresh model for each parameter combination.
  • SciKeras is a practical modern solution for tuning Keras models with scikit-learn tools.
  • Loss, optimizer, and metrics should be defined inside the model-building function.
  • Start with a small parameter grid because deep-learning grid search becomes expensive quickly.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design