Home › ML / AI › Sklearn NotFittedError — Fit Before Predict
Beginner 6 min · September 23, 2026

Sklearn NotFittedError — Fit Before Predict

Fix sklearn NotFittedError: fit before predict, fit whole pipelines, use transform on test data, and verify pickles with check_is_fitted..

N
Naren Founder & Principal Engineer

20+ years shipping production ML systems and the infrastructure behind them. Lessons pulled from things that broke in production.

Follow
✓ Production
production tested
September 24, 2026
last updated
1,942
articles · all by Naren
Before you start⏱ 8 min
  • ✓Python 3 with scikit-learn installed (pip install scikit-learn joblib)
  • ✓Basic ML flow: train/test split, fit, predict
  • ✓Comfort reading Python tracebacks
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • NotFittedError means predict, transform, or score ran before fit created learned attributes like coef_ or tree_
  • The usual cause isn't a missing fit call — it's predicting on the wrong object: a fresh instance, one pipeline step, or a pre-fit pickle
  • Fit the whole Pipeline once with pipe.fit(X_train, y_train); never fit steps individually outside it
  • Use fit_transform on training data only and transform on test data, or let a Pipeline enforce the split per fold
  • Verify with check_is_fitted after every load and at service startup so bad artifacts fail the deploy, not users
✦ Definition~90s read
What is Sklearn NotFittedError Fix?

NotFittedError (sklearn.exceptions.NotFittedError) is the exception scikit-learn raises when a method that needs learned parameters — predict, predict_proba, decision_function, score, or transform — runs on an estimator whose fit never completed. Scikit-learn splits every estimator's life into configuration (constructor parameters like C or max_iter) and learning (fit, which computes coef_, tree_, cluster_centers_, and friends).

★
Think of a scikit-learn model as a new hire: the constructor writes the job description, but only fit is the actual training.

Until fit runs, there is nothing to predict with, so the library raises instead of returning a meaningless number. The error message names the class and the fix: This X instance is not fitted yet. Call 'fit' with appropriate arguments before using this estimator.

Five situations produce it. Predicting with a fresh constructor is the simplest case from tutorials. Unfitted pipeline steps happen when pieces are fitted outside their pipeline or the pre-search object is used after GridSearchCV. The fit_transform mix-up puts fitting on test data or skips fitting on the serving path.

Never-fitted pickles ship a fresh twin of the trained object to production. Version skew across training and serving breaks unpickling of fitted state. All five share one message and one theme: the object asked to predict isn't the object that learned.

The fix is always about ordering and identity: fit the right object, on the right data, before predicting — then prove it with check_is_fitted at every handoff. NotFittedError is a friend: it's the library refusing to fabricate predictions, and every production appearance points at a real pipeline bug worth fixing.

Plain-English First

Think of a scikit-learn model as a new hire: the constructor writes the job description, but only fit is the actual training. Calling predict before fit is asking the new hire to do the job on day one before orientation — scikit-learn refuses with NotFittedError instead of letting them guess. Pipelines are team hand-offs where every station must be trained in order, and a saved model file is a trained employee's notes — useless if photocopied before training day.

Your training script ran clean, the model artifact is saved, and the API boots without complaint. Then the first real request lands and the traceback says it plainly: sklearn.exceptions.NotFittedError: This LogisticRegression instance is not fitted yet. NotFittedError is scikit-learn's guardrail against meaningless predictions. Every estimator separates configuration (the constructor) from learning (fit): until fit runs, there are no coefficients, no tree splits, no cluster centers — so predict has nothing to apply. The error fires the moment any method needing learned state — predict, predict_proba, score, transform — runs on an unfitted object. The loud failure beats a silent random prediction.

The confusion is that 'unfitted' rarely means 'you forgot fit entirely.' It usually means the wrong object got the predict call: a fresh instance shadowing the trained one, one pipeline step fitted outside its pipeline, a test set passed through fit_transform, or a pickle saved before fitting completed. Each variant names the same error but needs a different fix.

This guide covers all five: predicting before fitting, unfitted pipeline steps, the fit_transform-train versus transform-test split, never-fitted pickles, and check_is_fitted as your verification habit. You'll be able to read the error, name the ordering bug, and fix it in minutes.

What NotFittedError Guards Against

NotFittedError fires when a method needing learned state runs before fit creates it. Scikit-learn estimators store everything learned with a trailing underscore — coef_ on linear models, tree_ on trees, cluster_centers_ on KMeans, classes_ on classifiers. Until fit runs, those attributes don't exist, and predict raises instead of guessing. The message names the class and the remedy: This LogisticRegression instance is not fitted yet. Call 'fit' with appropriate arguments before using this estimator.

The design is deliberate. A prediction from an unfitted model isn't an error with a wrong answer — it's a random number wearing a model's uniform. Returning zeros or raising a warning would let garbage flow into dashboards, billing, and medical decisions silently. The loud exception forces the ordering bug into the open at the exact call site, with the exact class, every time.

Your first diagnostic is check_is_fitted from sklearn.utils.validation. It inspects the estimator for any fitted attribute and raises the same NotFittedError when none exists. Run it on the suspect object in isolation — not on the pipeline, not on a wrapper — to confirm unfitted state before theorizing. Then check vars(model): fitted estimators show their learned attributes plainly, while unfitted ones show only constructor parameters.

Note the scope: predict, predict_proba, predict_log_proba, decision_function, score, and transform all require fitted state. Only fit, fit_predict (on clustering), get_params, and set_params work unfitted — everything else is gated.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.utils.validation import check_is_fitted
from sklearn.exceptions import NotFittedError

X_train, X_test, y_train, y_test = train_test_split(
    *load_iris(return_X_y=True), test_size=0.2, random_state=0)

model = LogisticRegression(max_iter=1000)

try:
    check_is_fitted(model)
except NotFittedError as exc:
    print("unfitted:", exc)

# The fix: fit before predict.
model.fit(X_train, y_train)
check_is_fitted(model)  # now passes silently
print("test accuracy:", model.score(X_test, y_test))
📊 Production Insight
Log the estimator's class name and id() with every prediction error in serving. When a fresh instance shadows the trained one, identical class names hide the swap — differing object ids expose it instantly.
🎯 Key Takeaway
No trailing-underscore attributes means no learning happened — confirm with check_is_fitted, then find the fit that never ran.

Unfitted Steps Inside Pipelines and Grids

Pipelines chain preprocessing and modeling so one fit call trains every stage in order — but only when you fit the pipeline itself. Calling pipe.fit(X_train, y_train) runs each step's fit_transform in sequence and the final estimator's fit, threading outputs correctly. Predicting with pipe.predict then replays each step's transform. The moment you fit steps individually — scaler.fit here, classifier.fit there — you bypass the pipeline's routing and its fitted-state bookkeeping, and the pipeline still considers its steps unfitted.

ColumnTransformer has the same contract with an extra dimension: it fits one transformer per column group and concatenates the results. Fitting a sub-transformer manually and slotting it into a fresh ColumnTransformer leaves the parent unfitted, so transform raises even though the piece works standalone. The parent's fitted flag is what gates downstream calls, not the child's.

A subtler variant: cloning. GridSearchCV and cross_val_score clone the pipeline per fold and fit each clone — your original stays unfitted by design. Predicting with the pre-search object afterward throws, while best_estimator_ (refit on full data) works. This surprises teams who treat the searched object as trained.

Rule it simply: exactly one object gets fit — the outermost pipeline — and exactly that object predicts. Inspect pipe.named_steps after fitting to see every step carrying fitted attributes, and never reach inside to fit pieces.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    *load_iris(return_X_y=True), test_size=0.2, random_state=0)

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.utils.validation import check_is_fitted

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LogisticRegression(max_iter=1000)),
])

# Wrong: fitting steps individually leaves the pipeline unfitted.
# pipe.named_steps["scaler"].fit(X_train)

# Right: fit the pipeline end to end.
pipe.fit(X_train, y_train)
check_is_fitted(pipe)  # passes silently when fitted
print(pipe.predict(X_test[:5]))
📊 Production Insight
Verbose pipelines (verbose=True) log each step's fitting time — in production logs that timeline proves whether serving loaded a fitted pipeline or rebuilt fresh steps that never ran.
🎯 Key Takeaway
Fit the outermost pipeline once and predict with it — individually fitted steps don't count as a fitted pipeline.

fit_transform on Train, transform on Test

The fit_transform versus transform split is where NotFittedError meets data leakage. fit_transform learns parameters from its input and applies them; transform only applies previously learned ones. Training data gets fit_transform (learn the scaler's mean from train, then scale train). Test data gets transform only (apply train's mean to test). Reversing this — transforming train with a scaler fitted on test, or fitting on the test set at all — leaks test statistics into training and produces validation scores that production can never reproduce.

The failure mode teams actually hit: a script calls scaler.fit_transform(X_test) or reuses one scaler across both sets in the wrong order, then a later refactor serves predictions through a path where the scaler was never fitted on anything. The error surfaces far from the cause — at predict time in serving — while the bug sits in preprocessing order during training.

Pipelines eliminate the whole class. When scaling lives inside a Pipeline evaluated with cross_val_score, each fold clones the pipeline and fits its own scaler on that fold's training split only. There is no shared scaler to misuse, no order to remember, and no leakage. The pipeline object is the single source of fitted state.

If you must scale manually, enforce the order with code structure: split first, then exactly one fit_transform call on train and transform calls on everything else. Grep for fit_transform in review — every call site should name a training split.

📊 Production Insight
Leakage-inflated scores are the tell: when a manual-scale experiment beats its pipeline twin by points, the manual path fitted on test data. Trust the lower number — it's the honest one.
🎯 Key Takeaway
fit_transform learns and applies (train only); transform only applies (test and serving) — pipelines enforce this per fold automatically.

Never-Fitted Pickles and Version Skew

A fitted estimator only helps if the fitted object is what gets saved. The classic production NotFittedError comes from joblib.dump running on a fresh constructor — same class, same parameters, zero learned state — while fit ran on a different instance two cells up. Training metrics look great (computed from the fitted twin), the registry holds the unfitted twin, and serving throws on its first request.

Verify at both ends. After dumping, load the artifact in a fresh process and call check_is_fitted plus one golden prediction before publishing it. After loading in serving, call check_is_fitted again at startup and refuse to boot on failure. Two cheap checks bracket the entire export path and catch pre-fit saves, truncated files, and version-skewed unpickles alike.

Version skew deserves its own warning: pickles embed sklearn-version-specific structures, and unpickling across versions can warn, silently misbehave, or drop fitted state. Pin scikit-learn==x.y.z identically in training and serving requirements, record the version in sidecar metadata at export, and assert equality at load. Treat the pickle plus its version pin as one atomic artifact.

Prefer dumping whole pipelines over bare estimators. A pipeline pickle carries preprocessing and modeling state together, so serving can't reassemble mismatched pieces — the object that was validated in training is byte-for-byte the object that predicts.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import joblib
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.utils.validation import check_is_fitted

X_train, X_test, y_train, y_test = train_test_split(
    *load_iris(return_X_y=True), test_size=0.2, random_state=0)
pipe = Pipeline([("scaler", StandardScaler()),
                 ("clf", LogisticRegression(max_iter=1000))])

# Export AFTER fit, from the SAME object that was fitted.
pipe.fit(X_train, y_train)
joblib.dump(pipe, "/tmp/model.joblib")

# Verify in a fresh process before publishing the artifact.
loaded = joblib.load("/tmp/model.joblib")
check_is_fitted(loaded)  # raises if the wrong object was saved
print("golden predictions:", loaded.predict(X_test[:3]))
print("test accuracy:", loaded.score(X_test, y_test))
📊 Production Insight
Sidecar metadata (version, training date, feature list, data hash) turns artifact debugging from archaeology into arithmetic — every production model question starts with reading four fields.
🎯 Key Takeaway
Dump the fitted object, verify with check_is_fitted in a fresh process, and pin identical sklearn versions on both sides.

check_is_fitted as a Startup Gate

check_is_fitted is a one-line function that should appear in three places: unit tests, export jobs, and service startup. In tests, assert fitted state after every training helper returns — it catches refactors that accidentally return fresh instances. In export jobs, gate publication on it — no fitted state, no registry upload. In serving, call it before binding the port — a container that can't predict shouldn't accept traffic.

The function accepts any estimator, pipeline, or meta-estimator and checks for fitted attributes (anything ending in _ plus a fitted flag where defined). Pass a message argument to brand the error with context: check_is_fitted(model, msg="startup: production fraud model"). When it raises in logs, you know exactly which object and which stage failed instead of decoding a bare traceback.

Pair it with feature validation for a complete startup gate. Fitted state proves the model learned; column checks prove today's input matches training's schema. Together they catch the two dominant serving failures — unfitted artifacts and schema drift — before either reaches a user. Both checks run in milliseconds against an in-memory object.

Make unfitted a deploy-blocking state, not a retryable request error. Retrying a predict against an unfitted model can never succeed — the fix is a new artifact, not patience. Alert the model pipeline, not the request path.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import joblib

from sklearn.utils.validation import check_is_fitted

def load_production_model(path):
    model = joblib.load(path)
    # Fail the deploy here, not the first user request.
    check_is_fitted(model, msg=f"startup: artifact at {path} is not fitted")
    expected = joblib.load(path.replace("model.joblib", "features.joblib"))
    return model, expected


def predict(model, expected_columns, X):
    missing = set(expected_columns) - set(X.columns)
    if missing:
        raise ValueError(f"schema drift, missing columns: {sorted(missing)}")
    return model.predict(X[expected_columns])
📊 Production Insight
Startup gates change incident shape: instead of 100% request failure discovered by users, you get one CrashLoopBackOff discovered by deployment monitoring with the artifact version in the log.
🎯 Key Takeaway
Gate tests, exports, and startup on check_is_fitted — unfitted is a deploy blocker, never a retryable request error.

Prevent It Structurally: One Export Path

Long-term prevention is structural: make the unfitted state unrepresentable in your workflow. Keep exactly one training entrypoint that fits and exports the pipeline atomically — no notebook cells that can run out of order, no separate export scripts holding their own constructors. Parameterize it, version it, and run it in CI so the artifact is always built the same way.

Delete the patterns that breed wrong-object bugs. Ban bare estimator construction in serving code — serving loads artifacts, never constructors. Remove demo estimators and placeholder models from importable paths so no tired import can grab them. Review every fit_transform call site for the train-only rule, and prefer pipelines so the rule enforces itself.

Add contract tests around the artifact. A golden-sample test loads the production pickle and asserts exact predictions on a fixed input; it catches pre-fit saves, version skew, and feature-order changes in one assertion. A schema test asserts the serving feature list matches training's. Both run on every deploy, take seconds, and have saved more launches than any amount of careful notebook discipline.

Finally, pin and record everything: sklearn version, training date, data hash, feature list. Reproducibility turns the next NotFittedError from a mystery into a checklist — and checklists resolve in minutes.

⚠ Don't Swallow the Error
Never silence NotFittedError with try/except fallbacks that return zeros or cached predictions — a guessed prediction is worse than an error. Let it crash the deploy so the pipeline gets fixed.
📊 Production Insight
Teams that move training from notebooks to a single scripted entrypoint watch this error class vanish — ordering bugs are a notebook-execution artifact more than a modeling one.
🎯 Key Takeaway
One atomic train-and-export entrypoint, golden-sample contract tests, and full version metadata make unfitted artifacts nearly impossible.
● Production incidentPOST-MORTEMseverity: high

Fraud Model Refresh Exported an Unfitted Pipeline for 2 Hours

Symptom
After a routine model refresh, the fraud-scoring API returned 500 on every request for 2 hours. Logs showed NotFittedError naming the classifier step, while training dashboards showed the new model beating the old one. Rollback to the previous artifact recovered instantly, proving the serving code was fine and the new artifact was the problem.
Assumption
The team blamed the model registry. The training job had succeeded that morning, so everyone assumed the export was fine and the serving image was broken. An hour went into rebuilding the serving container with more memory and looser timeouts while the error persisted identically. Nobody questioned the artifact itself because the training logs showed fit completing — on a different object than the one saved.
Root cause
The training notebook constructed the pipeline twice: once for fitting (pipe.fit) and once in the export cell (a fresh Pipeline with identical parameters, never fitted). The export cell dumped the fresh instance. Training metrics were computed from the fitted object, so every dashboard looked green while the registry held an artifact with no learned state. Serving loaded it faithfully and threw NotFittedError on the first predict — 100% of scoring requests failed.
Fix
The hotfix was re-exporting the fitted pipeline object with joblib and redeploying the artifact in minutes. The durable fixes: the export cell now dumps the fitted pipeline (not the constructor), writes a sidecar JSON with sklearn version, training date, and feature list, and the serving job calls check_is_fitted at startup and refuses to boot on failure. A CI test loads the production artifact and predicts on a golden sample on every deploy.
Key lesson
  • The training log proves fit ran somewhere — only check_is_fitted on the loaded artifact proves the served object learned.
  • Save pipelines, not parts: exporting one step while serving another guarantees state mismatches.
  • Startup gates beat request-time errors — refuse to boot unfitted rather than failing the first user.
Production debug guideIsolate which object is unfitted, then trace why its fit never ran — in that order.5 entries
Symptom · 01
predict raises NotFittedError on a bare estimator
→
Fix
In a Python shell run from sklearn.utils.validation import check_is_fitted then check_is_fitted(model). If it raises NotFittedError, the object never completed fit in this process. Inspect vars(model) for trailing-underscore attributes like coef_ or tree_ — their absence confirms it. Fix: call model.fit(X_train, y_train) before predict, or load the fitted artifact with joblib instead of constructing a new estimator.
Symptom · 02
A Pipeline predicts fine in training but a step throws in serving
→
Fix
Run from sklearn.utils.validation import check_is_fitted then loop for name, step in pipe.named_steps.items(): check_is_fitted(step) and note which step raises. Print pipe.get_params() to confirm the failing step is the default fresh instance rather than your configured one. Fix: fit the pipeline object itself with pipe.fit(X_train, y_train) — never fit steps individually when a pipeline owns them.
Symptom · 03
Scores look great in dev, then NotFittedError or garbage accuracy appears elsewhere
→
Fix
Search the codebase with grep -rn fit_transform --include=*.py . and check whether any call site runs before train_test_split or on X_test. Confirm leakage by comparing cross-val scores against a pipeline-based run — inflated bare scores that collapse under a Pipeline prove it. Fix: split first, then scaler.fit_transform on train and scaler.transform on test, or wrap all steps in a Pipeline.
Symptom · 04
joblib.load succeeds but the first predict throws
→
Fix
Right after loading, run check_is_fitted(loaded) in the serving job and log sklearn.__version__ plus the artifact's saved version metadata. If it raises immediately, the pickle was dumped pre-fit — re-export from the training job after fit completes. If unpickling itself warns about versions, retrain or convert under matching sklearn versions; never serve across a major version gap.
Symptom · 05
Training works but serving throws on the same artifact
→
Fix
Run pip show scikit-learn in both training and serving environments and diff the versions. Then run python -c "import sklearn; print(sklearn.__version__)" inside the serving container to catch image drift. Fix: pin scikit-learn==x.y.z identically in training and serving requirements, rebuild both images, and re-export the artifact under the pinned version.
NotFittedError Causes — Confirm and Fix Each
Root CauseHow to ConfirmFixPrevention
predict called before fit on the same objectcheck_is_fitted raises, or estimator shows trailing-underscore attrs missingCall fit on training data before predict, or load a fitted artifactStructure code so fit always precedes predict in one obvious flow
Unfitted step inside a Pipeline or ColumnTransformerpipeline.named_steps shows a fresh step; verbose=True logs skipped fittingFit the whole pipeline once instead of fitting pieces separatelyNever call fit on individual steps — fit the pipeline end to end
fit_transform on test data / transform on train mix-upCross-val scores look too good, then production accuracy collapsesfit_transform train only, transform test only — ideally inside a PipelineBan bare fit_transform on full data; review every scaler call site
Loaded pickle that was never fitted (or wrong object)check_is_fitted on the loaded object raises immediately after loadRe-export a fitted pipeline with version + training date metadataSave pipelines, not parts; assert fitted state in the export job
⚙ Quick Reference
2 commands from this guide
FileCommand / CodePurpose
from sklearn.datasets import load_irisWhat NotFittedError Guards Against
from sklearn.utils.validation import check_is_fittedcheck_is_fitted as a Startup Gate

Key takeaways

1
NotFittedError means learned state is missing
some fit that should have run didn't, or predict hit the wrong object.
2
Fit the pipeline, never its steps
individual fitting breaks routing and feature alignment.
3
fit_transform is for train, transform is for test
crossing them leaks data and confuses state.
4
Persist fitted pipelines with version metadata, and verify with check_is_fitted after every load.
5
check_is_fitted is your startup gate
fail the deploy, not the first user request.
6
Match sklearn versions between training and serving
unpickling across versions corrupts or voids fitted state.

Common mistakes to avoid

5 patterns
×

Predicting with a freshly constructed estimator

Symptom
NotFittedError on the first predict in a notebook or script, naming the exact unfitted class like LogisticRegression or StandardScaler.
Fix
Call fit (or fit_transform on train) before any predict, score, or transform. If you need a fitted artifact across runs, persist it with joblib after fitting and load that file — never a fresh constructor.
×

Fitting pipeline steps individually instead of the pipeline

Symptom
The vectorizer is fitted but the classifier step throws NotFittedError, or steps disagree on feature counts because they saw different data.
Fix
Fit the pipeline object itself: pipe.fit(X_train, y_train), then pipe.predict(X_test). Never fit steps individually when a pipeline owns them — the pipeline routes each step's output correctly.
×

Calling fit_transform on the full dataset before splitting

Symptom
Suspiciously high validation scores in development followed by a production accuracy collapse — leakage plus a latent NotFittedError when the pattern is reused.
Fix
Split first, then fit_transform on train and transform on test — or put everything in a Pipeline so cross-validation clones and fits correctly per fold. Re-run the evaluation honestly and report the real number.
×

Unpickling a model that was saved before fitting

Symptom
NotFittedError immediately after joblib.load in production, while training notebooks work fine — the artifact was exported from the wrong cell or an unfitted clone.
Fix
Save the fitted pipeline (joblib.dump on the fitted object), record the sklearn version and training date beside it, and verify with check_is_fitted right after loading in the serving job.
×

Importing a demo or placeholder estimator into production code

Symptom
NotFittedError naming an estimator configured with toy parameters, thrown from a module path that belongs to examples or tests rather than the training job.
Fix
Keep one canonical predict path that loads the production artifact and validates it with check_is_fitted at startup. Delete or quarantine demo estimators so no import can accidentally grab them.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What does NotFittedError mean and what's the immediate fix?
Q02JUNIOR
What's the difference between fit, transform, and fit_transform?
Q03SENIOR
How do you prevent an unfitted model from reaching production?
Q04SENIOR
Why is fit_transform before train_test_split a bug, and how do pipelines...
Q05SENIOR
Production throws NotFittedError but training works. How do you debug it...
Q01 of 05JUNIOR

What does NotFittedError mean and what's the immediate fix?

ANSWER
It means predict (or transform/score) ran on an estimator whose fit never completed, so no learned attributes exist. The fix is calling fit with training data before predicting — or, in serving, loading the fitted artifact instead of constructing a fresh estimator.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Can I use check_is_fitted as a startup health check?
02
If a pipeline fails halfway, are earlier steps fitted?
03
Is the best estimator from GridSearchCV already fitted?
04
Should I set a parameter to tolerate constant input instead?
05
Why does my pipeline throw NotFittedError after GridSearchCV finishes?
06
Must StandardScaler really fit on training data only?
N
Naren Founder & Principal Engineer

20+ years shipping production ML systems and the infrastructure behind them. Lessons pulled from things that broke in production.

Follow
✓ Verified
production tested
September 24, 2026
last updated
1,942
articles · all by Naren
🔥

That's Scikit-Learn. Mark it forged?

6 min read · try the examples if you haven't

←
Previous
Spark Heap OutOfMemory Fix
9 / 9 · Scikit-Learn
Next
TensorFlow OOM Fix
→