DEV Community

Cover image for I Was Tired of Waiting for GridSearchCV. So I Built Something Smarter. 🚀
Anik Chand
Anik Chand

Posted on

I Was Tired of Waiting for GridSearchCV. So I Built Something Smarter. 🚀

Have you ever set up a GridSearchCV, pressed run, watched the little spinner go... and then just left the room? Maybe made tea. Maybe made dinner. Came back — and it was still running?

I hit that wall one too many times. Instead of waiting, I started thinking — why does this have to be this slow? That frustration turned into a late-night coding session, which became LazyTune — a smarter hyperparameter tuner for scikit-learn that I turned into a proper Python package with a live web app.


The Problem with GridSearchCV

Here's what GridSearchCV does under the hood:

You give it a parameter grid. Say 4 values for n_estimators, 4 for max_depth, 4 for min_samples_split. That's 64 combinations. With 5-fold CV, that's 320 full training runs. On your entire dataset. Every single one.

RandomizedSearchCV helps a little — it just picks random combos instead of all of them. But random is dumb. It has no idea which combinations are promising. Tools like Optuna and Hyperopt are genuinely clever, but they come with their own vocabulary and APIs, and honestly feel like overkill when you just want to tune a Random Forest on a Friday afternoon.


The Insight That Started Everything

Here's the thought that clicked at 2am:

Most hyperparameter combinations are obviously bad within the first few training rounds. You don't need to fully train them to know they're losers.

Think of it like a talent show audition. You don't give every contestant an hour-long slot. You do a quick 2-minute round first, figure out who's genuinely talented, then give the finalists the full slot.

LazyTune does exactly this:

  1. Generate all combinations
  2. Do a quick screening on a small data subset
  3. Rank every combination by early performance
  4. Prune the obvious losers (the bottom X%)
  5. Fully train only the survivors
  6. Return the winner

Main diagram

The whole thing lives in one class — SmartSearch — that you use almost identically to GridSearchCV. No new mental model. No new vocabulary. Just smarter.


Under the Hood — The Full Data Flow

Dataflow

Step 1 — Split your data: 80% training, 20% held-out test set.

Step 2 — Split the training set further: 30% becomes a "small screening subset," 70% becomes a validation pool.

Step 3 — Screen ALL combinations quickly on that 30% subset using 3-fold CV.

Step 4 — Rank all combinations by validation score — you get a full leaderboard.

Step 5 — With prune_ratio=0.1, keep the top ~10%. The other 90%? Gone. Never fully trained. ✂️

Step 6 — Fully train the surviving configs on the entire training set with proper CV.

Step 7 — Evaluate survivors on the held-out 20%. The winner comes back as best_estimator_ with best_params_ and best_score_.

The result: serious compute on 3 configs instead of 27 — but because the screening was smart, the winner is almost always the same as exhaustive GridSearchCV.


GridSearchCV vs LazyTune — The Visual Difference

visual difference

On the left, every combination gets the full expensive treatment. On the right, LazyTune screens all 9 cheaply, crosses out the clear losers, and only invests in the survivors. Same result. Fraction of the compute.


The Benchmarks

Does LazyTune match GridSearchCV's accuracy?

gridsearchcv vs. lazytune

For all three classifiers — RandomForest, SVC, LogisticRegression — the accuracy bars are practically indistinguishable. Both hitting 95–97%. LazyTune matches GridSearchCV's accuracy almost perfectly.

LazyTune vs Every Major Tuner

benchmark

Method Accuracy Runtime
🥇 LazyTune 0.940 6.23s
GridSearchCV 0.909 5.02s
RandomizedSearchCV 0.909 4.83s
Optuna 0.912 5.43s
Hyperopt 0.913 10.37s 😬

LazyTune gets the highest accuracy of all five. Hyperopt — often touted as the smart Bayesian choice — takes 10.37s and still scores lower.

Large Dataset Benchmark

large dataset comparison

Method Accuracy Runtime
🥇 LazyTune 0.982 122.3s
GridSearchCV 0.978 143.5s
RandomizedSearchCV 0.978 24.5s
Optuna 0.978 76.2s
Hyperopt 0.977 86.6s

LazyTune still leads on accuracy. GridSearchCV is 17% slower and still loses. LazyTune consistently gives you the best accuracy-per-second of anything tested.


Let's Write Some Code

pip install lazytune
Enter fullscreen mode Exit fullscreen mode

Basic usage — Random Forest

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from lazytune import SmartSearch

X, y = load_breast_cancer(return_X_y=True)

param_grid = {
    "n_estimators": [50, 100, 150, 200],
    "max_depth": [5, 10, 15, None],
    "min_samples_split": [2, 3, 4, 5]
}

search = SmartSearch(
    estimator=RandomForestClassifier(random_state=42),
    param_grid=param_grid,
    metric="accuracy",
    cv_folds=3,
    prune_ratio=0.5,
    n_jobs=-1
)

search.fit(X, y)

print("Best parameters:", search.best_params_)
print("Best CV score:  ", search.best_score_)
print("Best model:     ", search.best_estimator_)
Enter fullscreen mode Exit fullscreen mode

api comparison diagram

One new parameter — prune_ratio. One renamed parameter — metric instead of scoring. Everything else is identical to sklearn.

SVM with F1 score

search = SmartSearch(
    estimator=SVC(random_state=42),
    param_grid={
        "C": [0.1, 1, 10, 50, 100],
        "kernel": ["linear", "rbf"],
        "gamma": ["scale", "auto", 0.001, 0.0001]
    },
    metric="f1_macro",
    cv_folds=5,
    prune_ratio=0.6
)
search.fit(X, y)
Enter fullscreen mode Exit fullscreen mode

Understanding prune_ratio

prune ratio

prune_ratio What happens Best for
0.1 Keep only top 10% Huge grids where you trust fast screening
0.3 Keep top 30% Good balance, slightly conservative
0.5 Keep top 50% Start here — recommended default
1.0 Keep everything Same as GridSearchCV — comparison only

Always start at 0.5. Once you trust the screening on your dataset, try going lower.


After .fit() — Everything You Get Back

search.best_params_      # dict      — the winning hyperparameter combo
search.best_score_       # float     — best cross-validated score
search.best_estimator_   # model     — fully fitted, ready to use
search.summary_          # DataFrame — every trial ranked by score
search.cv_results_       # dict      — full CV results per candidate
Enter fullscreen mode Exit fullscreen mode

Since best_estimator_ is a normal sklearn model:

predictions = search.predict(X_new)
accuracy    = search.score(X_test, y_test)
Enter fullscreen mode Exit fullscreen mode

No adapters. No wrappers. Just works.


There's Also a Web App

lazytune.vercel.app — upload your CSV, pick a model, enter your parameter ranges, hit run. The same SmartSearch engine runs on the backend. No local setup required.


What's On the Roadmap

  • Auto prune_ratio — calibrate pruning based on grid size and a time budget
  • XGBoost / LightGBM native support
  • Early stopping within screening — kill candidates mid-CV the moment they're failing
  • Visual trial landscape — a heatmap of the hyperparameter space in the web UI
  • Timing breakdown in summary_

Open an issue on GitHub if any of these excite you — or if you have an idea I haven't thought of.


Try It Right Now

pip install lazytune
Enter fullscreen mode Exit fullscreen mode

This started as frustration at 2am and is now a real thing people can pip install. If you found it useful — a ❤️ on the post or a ⭐ on GitHub keeps me motivated to keep building.

Happy tuning! 🚀

Top comments (0)