scikit-learn 1.9 came out in June 2026, and the bug-fix release 1.9.1 followed in September. It is the library behind most classic machine learning in Python: regression, classification, clustering and the pipelines around them. This release is less about new algorithms and more about the experience of using them: you can finally watch training progress, more models run on GPUs, and several long-standing quirks are fixed. It also starts the clock on a set of deprecations that become errors in 1.11.
Release timeline
scikit-learn 1.9.0
New features and deprecations.
scikit-learn 1.9.1
Bug fixes, including metadata routing in pipelines.
Deprecations removedUpcoming
Warnings from 1.9 become errors.
pip install --upgrade scikit-learnCallbacks: watch your models train
A long grid search used to be a black box: you started it and waited. scikit-learn 1.9 adds an experimental callback API, so estimators can report what they are doing during fit. It ships with two built-in callbacks, sklearn.callback.ProgressBar and sklearn.callback.ScoringMonitor, and supports nested progress, even when work runs in parallel.
Where callbacks work in 1.9
- LogisticRegression
- With solver="lbfgs" only.
- GridSearchCV and RandomizedSearchCV
- Plus HalvingGridSearchCV and HalvingRandomSearchCV.
- Pipeline
- Progress through each step.
- StandardScaler
- A first transformer to support it.
Sparse arrays instead of sparse matrices
SciPy is moving from sparse matrices to sparse arrays, which behave more like NumPy arrays (for example, * means element-wise multiplication, not matrix multiplication). scikit-learn can now return either. The default is still "spmatrix", and the plan is to switch to "sparray" in a few releases, so it is worth trying now:
import sklearn
sklearn.set_config(sparse_interface="sparray")
# From here on, scikit-learn returns SciPy sparse arrays
# (for example csr_array) wherever it supports the new setting.
print(sklearn.get_config()["sparse_interface"]) # "sparray"Faster and more accurate models
Speed and memory
New- LogisticRegression with lbfgs computes in float32 when your data is float32, using less memory
- RidgeCV and RidgeClassifierCV are faster and more stable
- SpectralEmbedding is faster
- Gap safe screening speeds up sparse squared-loss regressors
Correctness
Fixed- Sample weights handled correctly in HistGradientBoosting, RandomForest and ExtraTrees
- GradientBoosting's friedman_mse criterion fixed (the criterion parameter is deprecated)
- PowerTransformer's yeo-johnson is more numerically stable
- Some scores and fitted values may shift slightly after upgrading
New tools
Added- metrics.metric_at_thresholds computes a metric at every threshold
- PrecisionRecallDisplay.from_cv_results plots curves from cross_validate
- RandomForestRegressor handles missing values with absolute error
- MiniBatchKMeans accepts sample weights
Also new: notebook displays of fitted estimators now show fitted attributes, and the ColumnTransformer view shows how output columns are assembled. narwhals is now a dependency and powers dataframe output, so set_output(transform="polars") and pandas output work more consistently.
More of scikit-learn runs on the GPU
Through the array API standard, scikit-learn can run some estimators directly on PyTorch or CuPy arrays, on a GPU, without converting to NumPy. 1.9 adds LogisticRegression and PoissonRegressor (with the lbfgs solver), Nystroem, FeatureUnion when all its steps support it, and more metrics such as average precision.
Deprecations to fix before 1.11
These raise warnings in 1.9 and will stop working in 1.11. Each has a simple fix:
# Deprecated: SVC(probability=True) is not thread-safe
from sklearn.svm import SVC
from sklearn.calibration import CalibratedClassifierCV
clf = CalibratedClassifierCV(SVC(), ensemble=False)
clf.fit(X_train, y_train)
proba = clf.predict_proba(X_test)from sklearn.metrics import log_loss
# Deprecated: log_loss(y_test, proba) via y_pred
score = log_loss(y_test, y_proba=proba)from sklearn.linear_model import LogisticRegressionCV
# The default scoring changes from accuracy to "neg_log_loss" in 1.11.
# Set it explicitly to keep today's behaviour and silence the warning.
model = LogisticRegressionCV(scoring="accuracy")from sklearn.preprocessing import TargetEncoder
from sklearn.model_selection import KFold
# shuffle and random_state are deprecated: pass a CV splitter instead
enc = TargetEncoder(cv=KFold(n_splits=5, shuffle=True, random_state=0))Other deprecations and changes
- lasso_path and enet_path
- n_alphas is deprecated. Pass alphas as an integer or a list.
- GradientBoosting
- The criterion parameter is deprecated.
- confusion_matrix_at_thresholds
- Pass pos_label and sample_weight as keywords.
- Metrics with strings
- accuracy_score, confusion_matrix and others now raise an error when y_true is text and y_pred is numeric.
A safe upgrade in four steps
- 1
Pin and branch
Upgrade in a separate environment or branch, not in production.
- 2
Run with warnings visible
Run your tests with python -W error::FutureWarning to find every deprecation.
- 3
Compare scores
Retrain key models and compare metrics; weight and binning fixes can shift results slightly.
- 4
Upgrade to 1.9.1
It fixes metadata routing in pipelines, so use it rather than 1.9.0.
Check your understanding
1/3Which callbacks ship with scikit-learn 1.9?
Both live in sklearn.callback and the API is experimental.
2/3What replaces SVC(probability=True)?
The probability parameter is deprecated because it is not thread-safe.
3/3What does sparse_interface="sparray" change?
The default is still spmatrix, with a planned switch later.
You got 0 out of 3.
Upgrading your Python stack this autumn? Read the pandas 3.0 upgrade guide, what's new in Python 3.15 and set up clean environments with uv.
Sources: the scikit-learn blog "release 1.9" (12 June 2026) and the scikit-learn 1.9 release notes, including 1.9.1 (September 2026).
