Skip to content

Fix ImportError: cannot import name from sklearn (Version Mismatch Guide)

Tested with: scikit-learn 1.9.1 (plus 1.3.2 for the numpy test), numpy 2.5.3 and 1.26.4, scipy 1.18.1, joblib 1.6.0, pip 26.2.1, Python 3.12.3, Linux, fresh venvs. Last run 2026-09-27.

TL;DR, in the order to check them:
  1. 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.
  2. No module named 'sklearn': install with python -m pip install scikit-learn, using the same python that runs your code. pip install sklearn no longer works at all; it fails with "The 'sklearn' PyPI package is deprecated".
  3. cannot import name X or No module named 'sklearn.cross_validation': the name was removed. Update the import (table below): model_selection for the old CV/search modules, import joblib for sklearn.externals.joblib, sklearn.impute.SimpleImputer for Imputer.
  4. IterativeImputer or HalvingGridSearchCV won't import: they are not removed, they are experimental. Import sklearn.experimental.enable_iterative_imputer (or enable_halving_search_cv) first.
  5. 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.2 fails to build. Old releases also need an old numpy: 1.3.2 requires numpy<2, and forcing numpy 2.5.3 under it gave numpy.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 ranExact errorFix that worked
import sklearn in a fresh venvModuleNotFoundError: No module named 'sklearn'python -m pip install scikit-learn
pip install sklearnThe '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 sklearnpip 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 venverror: externally-managed-environmentCreate and activate a venv first
from sklearn.cross_validation import train_test_splitModuleNotFoundError: No module named 'sklearn.cross_validation'from sklearn.model_selection import train_test_split
from sklearn import cross_validationImportError: cannot import name 'cross_validation' from 'sklearn'Same: sklearn.model_selection
from sklearn.preprocessing import ImputerImportError: cannot import name 'Imputer' from 'sklearn.preprocessing'from sklearn.impute import SimpleImputer
from sklearn.externals import joblibImportError: cannot import name 'joblib' from 'sklearn.externals'import joblib (already installed as a scikit-learn dependency)
from sklearn.impute import IterativeImputerImportError: 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.12ModuleNotFoundError: No module named 'pkg_resources' then ERROR: Failed to build 'scikit-learn' when getting requirements to build wheelUse an older Python for 0.24, or port the imports
scikit-learn 1.3.2, then pip install "numpy>=2", then importValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObjectpip 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 pip ties 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) and pip check reported 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_curve deprecated (deprecated means: import still works but emits a warning)
  • 0.20: above three submodules removed; sklearn.covariance.GraphLasso renamed to GraphicalLasso
  • 0.21: LSHForest removed; sklearn.utils.linear_assignment_ deprecated (removed in 0.23)
  • 0.22: sklearn.preprocessing.Imputer removed (replaced by sklearn.impute.SimpleImputer)
  • 0.23: sklearn.externals.joblib removed; use import joblib
  • 1.2: get_feature_names removed from transformers in favor of get_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:

  1. Stale .pyc files (rarely the cause). Python recompiles bytecode when the .py file changes, so this only matters for unusual setups such as copied __pycache__ folders or sourceless deployments. To rule it out, delete __pycache__ directories and .pyc files: find . -name "*.pyc" -delete && find . -name "__pycache__" -type d -exec rm -rf {} +
  2. A dependency re-exports the old path. Some older third-party packages (e.g., old versions of imbalanced-learn, mlxtend, or eli5) internally import from the old sklearn paths. Upgrading those packages alongside scikit-learn usually resolves it: pip install --upgrade imbalanced-learn mlxtend
  3. Two installations in the same environment. Run python -m pip show scikit-learn and check the Location field. 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.
  4. 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.