Fix ImportError: cannot import name from sklearn (Version Mismatch Guide)
- Confirm which Python is running and which scikit-learn it sees:
python -c "import sys, sklearn; print(sys.executable, sklearn.__version__, sklearn.__file__)". Most "it's installed but won't import" cases are a second interpreter. No module named 'sklearn': install withpython -m pip install scikit-learn, using the samepythonthat runs your code.pip install sklearnno longer works at all; it fails with "The 'sklearn' PyPI package is deprecated".cannot import name XorNo module named 'sklearn.cross_validation': the name was removed. Update the import (table below):model_selectionfor the old CV/search modules,import joblibforsklearn.externals.joblib,sklearn.impute.SimpleImputerforImputer.IterativeImputerorHalvingGridSearchCVwon't import: they are not removed, they are experimental. Importsklearn.experimental.enable_iterative_imputer(orenable_halving_search_cv) first.- Pinning an old scikit-learn instead? Check a wheel exists for your Python. On Python 3.12 the oldest installable release is 1.3.1;
scikit-learn==0.24.2fails to build. Old releases also need an old numpy: 1.3.2 requiresnumpy<2, and forcing numpy 2.5.3 under it gavenumpy.dtype size changed.
You open an old notebook, run the first cell, and it fails with:
ImportError: cannot import name 'cross_validation' from 'sklearn'
Or you clone a project from GitHub, install its requirements, and immediately see:
ImportError: cannot import name 'Imputer' from 'sklearn.preprocessing'
These errors all have the same root cause: scikit-learn reorganized and removed large parts of its public API between the 0.x series and 1.x. Code written against older scikit-learn breaks as soon as the package is upgraded: sklearn itself still imports, but the specific name no longer lives where the old code expects it.
This guide shows which names moved, where they went, and how to fix each one. Every error message quoted below was reproduced in a fresh virtual environment on the date above, not copied from forum posts.
Reproduced: Each Error and the Fix That Worked
Setup: python3 -m venv on Ubuntu with Python 3.12.3, pip install --upgrade pip, then pip install scikit-learn (resolved to 1.9.1 with numpy 2.5.3). Each row is one command, the last line of the error it printed, and the change that made it run.
| What I ran | Exact error | Fix that worked |
|---|---|---|
import sklearn in a fresh venv | ModuleNotFoundError: No module named 'sklearn' | python -m pip install scikit-learn |
pip install sklearn | The 'sklearn' PyPI package is deprecated, use 'scikit-learn' rather than 'sklearn' for pip commands. then ERROR: Failed to build 'sklearn' | Install scikit-learn; replace sklearn in requirements files |
pip install scikit-learn with another venv's pip, then import sklearn | pip reported success; import still gave No module named 'sklearn' | python -m pip install scikit-learn (installed into the right site-packages) |
python3 -m pip install scikit-learn with the system Python, no venv | error: externally-managed-environment | Create and activate a venv first |
from sklearn.cross_validation import train_test_split | ModuleNotFoundError: No module named 'sklearn.cross_validation' | from sklearn.model_selection import train_test_split |
from sklearn import cross_validation | ImportError: cannot import name 'cross_validation' from 'sklearn' | Same: sklearn.model_selection |
from sklearn.preprocessing import Imputer | ImportError: cannot import name 'Imputer' from 'sklearn.preprocessing' | from sklearn.impute import SimpleImputer |
from sklearn.externals import joblib | ImportError: cannot import name 'joblib' from 'sklearn.externals' | import joblib (already installed as a scikit-learn dependency) |
from sklearn.impute import IterativeImputer | ImportError: IterativeImputer is experimental and the API might change without any deprecation cycle. | from sklearn.experimental import enable_iterative_imputer first |
pip install "scikit-learn==0.24.2" on Python 3.12 | ModuleNotFoundError: No module named 'pkg_resources' then ERROR: Failed to build 'scikit-learn' when getting requirements to build wheel | Use an older Python for 0.24, or port the imports |
scikit-learn 1.3.2, then pip install "numpy>=2", then import | ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject | pip install "numpy<2" (got 1.26.4; import worked) |
What the common advice gets wrong, based on these runs:
- "Just run
pip install sklearn." That package name is now a deliberate build failure. It never installs anything. - "Downgrade to 0.24.2." Not on a current Python. Pip found no wheel, tried a source build and failed before compiling anything. To see which releases have a wheel for your interpreter, run
pip download --only-binary=:all: --no-deps "scikit-learn==1.3.0": the error lists every available version. On Python 3.12 it listed 1.3.1 as the oldest. - "Pip said it installed, so the environment is fine." The wrong-venv row above printed no error at install time. Only
python -m pipties the install to the interpreter you run. - "IterativeImputer was removed." It is still in 1.9.1; it just needs the experimental import.
- "The numpy error means your install is corrupt." It was a version pairing. Pip itself warned about it (
scikit-learn 1.3.2 requires numpy<2.0,>=1.17.3, but you have numpy 2.5.3) andpip checkreported the same line. For more on that error see the numpy binary incompatibility guide.
The Error: Three Real Tracebacks
The error surface varies depending on which removed or moved name you are trying to import. These three are the exact output from scikit-learn 1.9.1 (paths shortened to the venv location).
Variant 1: Entire submodule removed
Traceback (most recent call last):
File "/tmp/ietest/v1.py", line 1, in <module>
from sklearn.cross_validation import train_test_split, StratifiedKFold
ModuleNotFoundError: No module named 'sklearn.cross_validation'
Note the exception type depends on how the old code wrote the import. from sklearn.cross_validation import ... raises ModuleNotFoundError; from sklearn import cross_validation raises ImportError: cannot import name 'cross_validation' from 'sklearn'. Same cause, same fix.
Variant 2: Class moved to a new module
Traceback (most recent call last):
File "/tmp/ietest/v2.py", line 1, in <module>
from sklearn.preprocessing import Imputer
ImportError: cannot import name 'Imputer' from 'sklearn.preprocessing' (/tmp/ietest/cur/lib/python3.12/site-packages/sklearn/preprocessing/__init__.py)
Variant 3: Third-party package re-exported through sklearn.externals
Traceback (most recent call last):
File "/tmp/ietest/v3.py", line 1, in <module>
from sklearn.externals import joblib
ImportError: cannot import name 'joblib' from 'sklearn.externals' (/tmp/ietest/cur/lib/python3.12/site-packages/sklearn/externals/__init__.py)
The message gives no hint about the fix. from sklearn.externals.joblib import Parallel, delayed fails differently, with ModuleNotFoundError: No module named 'sklearn.externals.joblib'.
In all three cases the installed scikit-learn version is newer than the code was written for. The fix is either to update the import path in your code or to pin the package to an older version that still contains the name.
Step 1: Check Your sklearn Version
Before changing any code, confirm which version is installed in the environment that is running your script. The version tells you exactly which APIs are available.
import sklearn
print(sklearn.__version__)
# 1.9.1 (current release on PyPI as of 2026-09-27)
# 0.20 or newer: cross_validation, grid_search, learning_curve are gone
# 0.22 or newer: preprocessing.Imputer is gone too
# 0.23 or newer: sklearn.externals.joblib is gone too
You can also check from the shell without starting a Python session:
python -m pip show scikit-learn
# Name: scikit-learn
# Version: 1.9.1
# ...
# Location: /tmp/ietest/cur/lib/python3.12/site-packages
# Full report: Python, executable path, numpy/scipy/joblib versions, BLAS
python -c "import sklearn; sklearn.show_versions()"
# Does NOT work, despite being widely copied:
python -m sklearn --version
# No module named sklearn.__main__; 'sklearn' is a package and cannot be directly executed
If the version is 1.0 or higher and your code imports from sklearn.cross_validation, sklearn.grid_search, sklearn.learning_curve, or sklearn.externals, those imports will fail. The submodules were deprecated in 0.18 and removed in 0.20 (September 2018). sklearn.preprocessing.Imputer was removed in 0.22 as well.
API Migration Table: Old Import to New Import
The table below covers every common import path that broke across the 0.x → 1.x transition. The "Notes" column explains whether the class was renamed, moved, or replaced by a different API.
| Old import (0.x) | New import (1.x) | Notes |
|---|---|---|
from sklearn.cross_validation import train_test_split |
from sklearn.model_selection import train_test_split |
Entire cross_validation submodule folded into model_selection in 0.18. Removed in 0.20. |
from sklearn.cross_validation import StratifiedKFold, KFold |
from sklearn.model_selection import StratifiedKFold, KFold |
Same move. All CV splitters now live in model_selection. |
from sklearn.cross_validation import cross_val_score |
from sklearn.model_selection import cross_val_score |
Same move. cross_val_predict and cross_validate also live here. |
from sklearn.grid_search import GridSearchCV |
from sklearn.model_selection import GridSearchCV |
sklearn.grid_search removed in 0.20. RandomizedSearchCV also moved here. |
from sklearn.grid_search import RandomizedSearchCV |
from sklearn.model_selection import RandomizedSearchCV |
Same move. ParameterGrid and ParameterSampler also in model_selection. |
from sklearn.learning_curve import learning_curve |
from sklearn.model_selection import learning_curve |
sklearn.learning_curve submodule removed in 0.20. validation_curve also moved here. |
from sklearn.learning_curve import validation_curve |
from sklearn.model_selection import validation_curve |
Same move. |
from sklearn.externals import joblib |
import joblib |
joblib is a standalone package. scikit-learn depends on it (joblib 1.6.0 came in with scikit-learn 1.9.1), so import joblib works with no extra install; pip install joblib is only needed if it was removed. Removed from sklearn.externals in 0.23. |
from sklearn.externals.joblib import Parallel, delayed |
from joblib import Parallel, delayed |
Same external-package change. All joblib symbols import directly from joblib. |
from sklearn.utils.linear_assignment_ import linear_assignment |
from scipy.optimize import linear_sum_assignment |
Deprecated in 0.21, removed in 0.23. scipy's version returns (row_ind, col_ind) arrays instead of a 2-column array; callers need minor adaptation. Tested: list(zip(row_ind, col_ind)) gives the old row pairs. |
from sklearn.preprocessing import Imputer |
from sklearn.impute import SimpleImputer |
Imputer removed in 0.22. Replaced by SimpleImputer in the new sklearn.impute module. API is nearly identical; constructor param missing_values now accepts np.nan by default. |
from sklearn.base import BaseEstimator |
from sklearn.base import BaseEstimator ✓ |
Not moved. BaseEstimator, TransformerMixin, ClassifierMixin, and RegressorMixin all remain in sklearn.base across all versions. |
from sklearn.cross_decomposition import PLSRegression |
from sklearn.cross_decomposition import PLSRegression ✓ |
Not moved. sklearn.cross_decomposition still exists and contains PLSRegression, PLSCanonical, and CCA. |
from sklearn.neighbors import LSHForest |
No direct replacement in sklearn | LSHForest was removed in 0.21 without a replacement. Use sklearn.neighbors.NearestNeighbors with algorithm 'ball_tree' or 'kd_tree', or switch to faiss for approximate nearest neighbor search. |
from sklearn.covariance import GraphLasso |
from sklearn.covariance import GraphicalLasso |
Renamed in 0.20. GraphLassoCV → GraphicalLassoCV as well. |
Fix: Update the Import Paths in Your Code
Most of these are one-line changes. Before and after, by category:
cross_validation and grid_search
# Before (sklearn 0.17 and earlier)
from sklearn.cross_validation import train_test_split, StratifiedKFold, cross_val_score
from sklearn.grid_search import GridSearchCV, RandomizedSearchCV
from sklearn.learning_curve import learning_curve, validation_curve
# After (sklearn 0.18+, required in 1.x)
from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from sklearn.model_selection import learning_curve, validation_curve
joblib
# Before (sklearn 0.22 and earlier)
from sklearn.externals import joblib
from sklearn.externals.joblib import Parallel, delayed
# After (sklearn 0.23+, required in 1.x)
import joblib
from joblib import Parallel, delayed
# Install if missing:
# pip install joblib
Imputer
# Before (sklearn 0.19 and earlier)
from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
# After (sklearn 0.20+, required in 1.x)
from sklearn.impute import SimpleImputer
imp = SimpleImputer(missing_values=float('nan'), strategy='mean')
# Note: keep missing_values as np.nan (the default). Passing the string 'NaN'
# on float data fails in 1.9.1 with "ValueError: Input X contains NaN."
# Note: axis was removed; SimpleImputer(axis=0) raises
# TypeError: SimpleImputer.__init__() got an unexpected keyword argument 'axis'
# SimpleImputer always operates column-wise
linear_assignment
# Before (sklearn 0.20 and earlier)
from sklearn.utils.linear_assignment_ import linear_assignment
cost_matrix = build_cost_matrix()
assignment = linear_assignment(cost_matrix)
# assignment is shape (n, 2) where assignment[i] = [row, col]
# After (sklearn 0.21+)
from scipy.optimize import linear_sum_assignment
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# Equivalent: list(zip(row_ind, col_ind)) reproduces the old row-pair format
Fix: Virtual Environments and Pinning Versions
If you need to run code that targets a specific older version of scikit-learn (for example to reproduce a paper that lists scikit-learn 0.24), the cleanest approach is a dedicated virtual environment with pinned dependencies. The catch: the interpreter has to be old enough to have a wheel for that release. On Python 3.12, pip install "scikit-learn==0.24.2" fell back to a source build and failed with No module named 'pkg_resources'. The oldest release with a Python 3.12 wheel is 1.3.1.
Creating an isolated environment
# Create a new environment named after the project.
# For 0.24.x the interpreter must be old enough to have its wheels (3.9 or older).
# With python3.12 here, the install below fails to build.
python3.9 -m venv env-legacy-sklearn
source env-legacy-sklearn/bin/activate # Linux/macOS
# env-legacy-sklearn\Scripts\activate # Windows
# Install an exact scikit-learn version
pip install "scikit-learn==0.24.2"
# Verify
python -c "import sklearn; print(sklearn.__version__)"
# expected: 0.24.2 (this Python 3.9 path was not part of the test run)
requirements.txt pinning pattern
For a project that should run on a specific version, pin it in requirements.txt:
scikit-learn==0.24.2
numpy==1.23.5
scipy==1.9.3
joblib==1.2.0
Use == for exact pinning when you need to guarantee a specific set of APIs. Use >= with an upper bound when you can tolerate a range:
# Allows any 1.x version but not 2.0 if it ever ships
scikit-learn>=1.0,<2.0
Install from the file with:
pip install -r requirements.txt
If you want to capture the exact state of an environment that is currently working, freeze it first:
pip freeze > requirements.txt
# Produces lines like:
# scikit-learn==1.3.2
# numpy==1.26.4
# scipy==1.12.0
# joblib==1.3.2
Fix: Installing the Right Version
If you have already diagnosed the version mismatch and know which version to target, here is how to install or downgrade.
Upgrade to current stable
# Upgrade to latest scikit-learn (recommended for new projects)
pip install --upgrade scikit-learn
# With version floor
pip install "scikit-learn>=1.3"
Downgrade to a specific old version
# Pin to an older 1.x release (needs a wheel for your Python)
python -m pip install "scikit-learn==1.3.2"
# pip uninstalls the current version and installs the requested one.
# Checking the result:
python -m pip show scikit-learn
# Version: 1.3.2
# Old releases predate numpy 2. If numpy 2.x is already installed, force it back:
python -m pip install "numpy<2"
python -m pip check # prints any remaining version conflict
In my run, installing 1.3.2 into a clean venv pulled numpy 1.26.4 on its own, because 1.3.2 declares numpy<2.0. The binary error only appeared after a later pip install "numpy>=2" upgraded numpy underneath it, which is exactly what happens when another package in the same environment asks for numpy 2.
Using conda
# Create a new conda environment with a specific scikit-learn version
conda create -n sklearn-legacy python=3.9 scikit-learn=0.24.2
conda activate sklearn-legacy
# Or update within an existing environment
conda install scikit-learn=1.3.2
When using conda, be aware that conda install scikit-learn and pip install scikit-learn in the same environment can install different versions into different locations. Using one package manager per environment avoids that ambiguity.
How to Check What Is Available in Your Installed Version
If you are not sure whether a specific name exists in the version you have installed, inspect the module's contents directly using dir().
import sklearn.model_selection
print(dir(sklearn.model_selection))
# Public names in 1.9.1 (private "_" names omitted):
# ['BaseCrossValidator', 'BaseShuffleSplit', 'FixedThresholdClassifier',
# 'GridSearchCV', 'GroupKFold', 'GroupShuffleSplit', 'KFold',
# 'LearningCurveDisplay', 'LeaveOneGroupOut', 'LeaveOneOut',
# 'LeavePGroupsOut', 'LeavePOut', 'ParameterGrid', 'ParameterSampler',
# 'PredefinedSplit', 'RandomizedSearchCV', 'RepeatedKFold',
# 'RepeatedStratifiedKFold', 'ShuffleSplit', 'StratifiedGroupKFold',
# 'StratifiedKFold', 'StratifiedShuffleSplit', 'TimeSeriesSplit',
# 'TunedThresholdClassifierCV', 'ValidationCurveDisplay', 'check_cv',
# 'cross_val_predict', 'cross_val_score', 'cross_validate',
# 'learning_curve', 'permutation_test_score', 'train_test_split',
# 'typing', 'validation_curve']
# HalvingGridSearchCV is not listed until you run
# from sklearn.experimental import enable_halving_search_cv
import sklearn.impute
print(dir(sklearn.impute))
# Public names in 1.9.1:
# ['KNNImputer', 'MissingIndicator', 'SimpleImputer', 'typing']
# 'Imputer' is not here: it was removed in 0.22.
# 'IterativeImputer' is not here either, but it still exists; it appears
# after: from sklearn.experimental import enable_iterative_imputer
import sklearn.preprocessing
# Confirm Imputer is gone
print('Imputer' in dir(sklearn.preprocessing))
# False (on any version 0.22 or newer)
You can also search across all sklearn submodules for a name using pkgutil:
import pkgutil
import importlib
import sklearn
def find_in_sklearn(target_name):
"""Search all sklearn submodules for a given class or function name."""
found = []
for importer, modname, ispkg in pkgutil.walk_packages(
path=sklearn.__path__,
prefix='sklearn.',
onerror=lambda x: None
):
try:
mod = importlib.import_module(modname)
if hasattr(mod, target_name):
found.append(modname)
except Exception:
pass
return found
# Output from scikit-learn 1.9.1 (with sklearn.*.tests modules skipped):
print(find_in_sklearn('SimpleImputer'))
# ['sklearn.impute', 'sklearn.impute._base', 'sklearn.impute._iterative',
# 'sklearn.utils._test_common.instance_generator']
print(find_in_sklearn('GridSearchCV'))
# ['sklearn.linear_model._ridge', 'sklearn.model_selection',
# 'sklearn.model_selection._search',
# 'sklearn.utils._test_common.instance_generator']
print(find_in_sklearn('Imputer'))
# [] (confirms it is truly gone)
Modules that merely import a name also show up, so read the list for the public path (no leading underscore). Skipping modules whose name contains .tests, the three searches took 0.3 seconds in total. An empty list is definitive: the name does not exist in your installed version.
Common Cause: Installing Into a Different Python
This is also the usual cause of ModuleNotFoundError: No module named 'sklearn' right after a successful install. I reproduced it by activating one venv and running another venv's pip install scikit-learn: pip printed no error, and import sklearn still failed. pip --version gave it away, since it pointed at the other venv's site-packages. Running python -m pip install scikit-learn fixed it immediately.
The same applies to conda: pip and conda in one environment can put different scikit-learn versions in different places, and the one your script gets depends on which Python executable runs it. These three checks, run in the venv from that reproduction, show where everything points:
# Check which Python binary your shell uses
which python
# /tmp/ietest/other/bin/python
# Check which pip that Python uses
python -m pip --version
# pip 24.0 from /tmp/ietest/other/lib/python3.12/site-packages/pip (python 3.12)
# (24.0 is what a fresh venv got on this machine; this one was not upgraded)
# Check scikit-learn through that exact Python
python -c "import sklearn; print(sklearn.__version__, sklearn.__file__)"
# 1.9.1 /tmp/ietest/other/lib/python3.12/site-packages/sklearn/__init__.py
Look at sklearn.__file__ first. The path tells you exactly which installation is being loaded. If the path is different from where you expect (for example it points to a system Python instead of your venv), your environment is not activated correctly.
# Diagnose stale environment: run this before importing sklearn
python -c "import sys; print(sys.prefix)"
# Should print the path to your active venv or conda environment
# If it prints /usr or /usr/local, you are using system Python, not your env
Jupyter notebooks and kernel mismatch
Notebooks add another layer of indirection: the kernel that runs your cells may be attached to a different Python than the one you used to install scikit-learn. You can check from inside the notebook (not part of the test run above; the paths are placeholders):
import sys
print(sys.executable)
# /home/user/miniconda3/envs/myenv/bin/python
import sklearn
print(sklearn.__version__)
# 1.3.2
# If the version here differs from what `python -c "import sklearn; print(sklearn.__version__)"` shows
# in your terminal, the notebook kernel is using a different Python installation.
To fix a kernel mismatch, install the ipykernel package into the correct environment and register it:
conda activate myenv
pip install ipykernel
python -m ipykernel install --user --name myenv --display-name "Python (myenv)"
# Restart Jupyter and select "Python (myenv)" from the kernel menu
Quick-Reference: Version Removal Timeline
If you are trying to trace when a specific API disappeared, this timeline covers the major removal milestones. It comes from the scikit-learn release notes; the only part re-run here is that every removed name below fails to import in 1.9.1.
- 0.18:
sklearn.cross_validation,sklearn.grid_search,sklearn.learning_curvedeprecated (deprecated means: import still works but emits a warning) - 0.20: above three submodules removed;
sklearn.covariance.GraphLassorenamed toGraphicalLasso - 0.21:
LSHForestremoved;sklearn.utils.linear_assignment_deprecated (removed in 0.23) - 0.22:
sklearn.preprocessing.Imputerremoved (replaced bysklearn.impute.SimpleImputer) - 0.23:
sklearn.externals.joblibremoved; useimport joblib - 1.2:
get_feature_namesremoved from transformers in favor ofget_feature_names_out(deprecated in 1.0)
If the Error Persists After Updating the Import
If the import path is updated and the error continues, check these:
- Stale
.pycfiles (rarely the cause). Python recompiles bytecode when the.pyfile changes, so this only matters for unusual setups such as copied__pycache__folders or sourceless deployments. To rule it out, delete__pycache__directories and.pycfiles:find . -name "*.pyc" -delete && find . -name "__pycache__" -type d -exec rm -rf {} + - A dependency re-exports the old path. Some older third-party packages (e.g., old versions of
imbalanced-learn,mlxtend, oreli5) internally import from the old sklearn paths. Upgrading those packages alongside scikit-learn usually resolves it:pip install --upgrade imbalanced-learn mlxtend - Two installations in the same environment. Run
python -m pip show scikit-learnand check theLocationfield. If you see a path you do not recognize, you may have a system-level installation shadowing your venv one. Activate your venv and re-install. - Editable install of an old package. If you ran
pip install -e .on a project that pins old sklearn, that project's code still uses the old import paths even if you upgrade sklearn globally. Update the source code of that project.
See Also
If your model trains successfully but emits warnings during fitting, see Fix sklearn ConvergenceWarning: What It Means and How to Fix It, which covers the separate issue of iterative solvers hitting their iteration limit before converging.