Scikit-learn 1.8 完全指南:GPU 加速、免GIL多线程与七大新特性实战

实战拆解 scikit-learn 1.8 七大核心新特性:通过 Array API 实现 GPU 10倍加速、免GIL自由线程真正多核并行、温度缩放校准、Gap Safe Screening 高维加速、经典多维缩放等,附完整代码示例。

Scikit-learn 1.8 GPU加速完全指南 2026
On this page
  1. 一、Array API 支持与 GPU 加速
  2. 二、免 GIL 的自由线程 CPython 3.14 支持
  3. 三、温度缩放校准(Temperature Scaling)
  4. 四、Gap Safe Screening 加速 L1 正则化模型
  5. 五、经典多维缩放(ClassicalMDS)
  6. 六、DecisionTreeRegressor absolute_error 复杂度优化
  7. 七、HTML 估计器表示增强
  8. 八、其他值得关注的更新
  9. 九、安装与升级
  10. 十、总结

2025年12月,scikit-learn 终于发布了 1.8 版本——说"终于"是因为这次更新的幅度之大,社区已经期待很久了。作为 Python 机器学习生态里最核心的库(没有之一),scikit-learn 1.8 可以说是近几年来最重要的一次迭代。GPU 加速、免 GIL 多线程、温度缩放校准、经典多维缩放……每一项都值得单独拿出来聊。

所以,这篇文章会用实战代码带你逐一拆解这七大核心新特性。不管你是刚接触 sklearn 的新手,还是用了好几年的老用户,相信都能从中找到让你眼前一亮的东西。

一、Array API 支持与 GPU 加速

1.1 为什么需要 Array API

说实话,scikit-learn "只能跑在 CPU 上"这件事,一直是它被人诟病的痛点。数据集一旦到了百万级别,训练速度就让人有点抓狂。

1.8 版本通过拥抱 Python Array API 标准,终于迈出了关键一步。现在 PyTorch 张量和 CuPy 数组都可以直接丢给估计器,让 GPU 来干活。实测下来,典型工作负载能获得大约10倍的加速——这个提升不是挤牙膏,是实打实的。

更赞的是,scikit-learn 本身不会因此变重。GPU 计算是通过你自己选的数组库(CuPy 或 PyTorch)来完成的,主包依旧保持轻量。

1.2 支持 Array API 的估计器

1.8 版本中已经支持 Array API 的估计器和工具函数还挺多的,这里列一下主要的:

  • 降维:PCA(需指定 svd_solver)
  • 线性模型:Ridge、RidgeCV、RidgeClassifier、RidgeClassifierCV(需设置 solver="svd")
  • 判别分析:LinearDiscriminantAnalysis(需设置 solver="svd")
  • 朴素贝叶斯:GaussianNB
  • 聚类:GaussianMixture(需设置 init_params="random" 或 "random_from_data")
  • 预处理:StandardScaler、MinMaxScaler、MaxAbsScaler、Normalizer、PolynomialFeatures、Binarizer、LabelEncoder
  • 校准:CalibratedClassifierCV(使用 method="temperature")
  • 模型选择:GridSearchCV、RandomizedSearchCV、cross_val_predict、train_test_split
  • 评估指标:accuracy_score、f1_score、confusion_matrix、roc_curve、brier_score_loss、log_loss 等几十个函数

1.3 使用 CuPy 实现 GPU 加速

CuPy 大概是上手 GPU 加速最直接的方式了。先设好环境变量 SCIPY_ARRAY_API=1,然后在代码里开启 array_api_dispatch:

import os
os.environ["SCIPY_ARRAY_API"] = "1"

import cupy
from sklearn.datasets import make_classification
from sklearn import config_context
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

# 生成示例数据
X_np, y_np = make_classification(
    n_samples=50000, n_features=200, random_state=42
)

# 将数据转移到 GPU
X_cu = cupy.asarray(X_np)
y_cu = cupy.asarray(y_np)

# 启用 Array API 调度并训练模型
with config_context(array_api_dispatch=True):
    lda = LinearDiscriminantAnalysis(solver="svd")
    X_trans = lda.fit_transform(X_cu, y_cu)
    print(type(X_trans))  # <class 'cupy.ndarray'>

# 如需将结果转回 NumPy
X_trans_np = X_trans.get()

就这么简单。数据放到 GPU 上,模型训练也自动在 GPU 上跑,返回的结果还是 CuPy 数组。需要的话再 .get() 转回 NumPy 就行。

1.4 使用 PyTorch 张量在 CUDA 上计算

如果你的项目本来就在用 PyTorch(大概率是吧),那就更方便了——直接把 CUDA 张量扔给 sklearn:

import os
os.environ["SCIPY_ARRAY_API"] = "1"

import torch
import numpy as np
from sklearn import config_context
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# 准备 GPU 上的 PyTorch 张量
X_np = np.random.randn(10000, 50).astype(np.float32)
X_torch = torch.asarray(X_np, device="cuda", dtype=torch.float32)

with config_context(array_api_dispatch=True):
    # 标准化 + PCA 流水线,全部在 GPU 上执行
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X_torch)

    pca = PCA(n_components=10, svd_solver="full")
    X_pca = pca.fit_transform(X_scaled)

    print(X_pca.device)  # cuda:0
    print(X_pca.shape)   # torch.Size([10000, 10])

1.5 GPU 加速流水线的完整示例

来看一个更完整的例子,把数据转换、模型训练、概率校准、交叉验证全都串在 GPU 上跑:

import os
os.environ["SCIPY_ARRAY_API"] = "1"

import numpy as np
import torch
import sklearn
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from sklearn.calibration import CalibratedClassifierCV
from sklearn.linear_model import RidgeClassifierCV
from sklearn.model_selection import cross_validate
from sklearn.datasets import make_classification

X_np, y_np = make_classification(
    n_samples=20000, n_features=100, n_classes=3,
    n_informative=30, random_state=42
)

alphas = np.logspace(-3, 3, 20)

# 构建 GPU 加速流水线
gpu_pipeline = make_pipeline(
    StandardScaler(),
    FunctionTransformer(
        lambda x: torch.tensor(
            x if isinstance(x, np.ndarray) else x.numpy(),
            device="cuda", dtype=torch.float32
        )
    ),
    CalibratedClassifierCV(
        RidgeClassifierCV(alphas=alphas),
        method="temperature"
    ),
)

with sklearn.config_context(array_api_dispatch=True):
    results = cross_validate(gpu_pipeline, X_np, y_np, cv=5)
    print(f"平均准确率: {results['test_score'].mean():.4f}")

二、免 GIL 的自由线程 CPython 3.14 支持

2.1 什么是自由线程 CPython

如果你写过 Python 多线程代码,大概都被 GIL 折磨过。全局解释器锁(GIL)确保同一时刻只有一个线程在执行 Python 字节码——也就是说,哪怕你的机器有 64 个核,纯 Python 多线程也只能干看着。

CPython 3.14 带来了自由线程(free-threaded)构建版本,直接把 GIL 给摘掉了。多个线程终于可以真正同时执行 Python 代码。

好消息是,scikit-learn 1.8 已经为所有平台提供了自由线程的预编译 wheel 包。直接 pip install 就能用,不用自己从源码编译(这一点太重要了)。

2.2 使用线程后端加速 GridSearchCV

在自由线程 CPython 下,你可以把 joblib 的后端从进程(loky)切换到线程(threading)。好处很明显:省去了进程间数据序列化和传输的开销。

import joblib
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

X, y = make_classification(n_samples=5000, n_features=20, random_state=42)

param_grid = {
    "n_estimators": [50, 100, 200],
    "max_depth": [5, 10, 20, None],
    "min_samples_split": [2, 5, 10],
}

clf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(
    clf, param_grid=param_grid, cv=5, n_jobs=4, scoring="accuracy"
)

# 使用线程后端,在自由线程 CPython 下可获得真正的并行加速
with joblib.parallel_config(backend="threading"):
    grid_search.fit(X, y)

print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳准确率: {grid_search.best_score_:.4f}")

2.3 性能对比与注意事项

传统 CPython 里用线程后端跑并行?别想了,GIL 会让你白忙一场。但在自由线程 CPython 3.14 下,线程后端真的能让多核 CPU 火力全开,而且比进程后端少了数据复制的内存开销。

有个小建议:体验自由线程最好直接上 Python 3.14,别用 3.13。3.13 的自由线程支持有不少已知问题,3.14 里都修了。安装时通常需要编译选项 --disable-gil,或者找一个支持自由线程的预编译发行版。scikit-learn 官方也鼓励大家积极测试并反馈问题。

三、温度缩放校准(Temperature Scaling)

3.1 概率校准的重要性

你有没有注意过,很多分类器的 predict_proba 输出其实不太靠谱?比如 GaussianNB 给出的概率预测往往过度自信——说是 95% 的概率,实际可能只有 70%。概率校准就是为了解决这个问题:让模型输出的概率更接近真实情况。

之前 scikit-learn 支持 sigmoid(Platt 缩放)和 isotonic(保序回归)两种校准方法。1.8 版本加了第三种:温度缩放。

3.2 温度缩放的原理与优势

温度缩放的思路其实很简洁:把分类器的 logits 除以一个温度参数 T,再过 softmax 得到校准后的概率。

p_calibrated = softmax(logits / T)

T 通过最小化 log loss 来优化。跟 sigmoid 校准比起来,温度缩放只有一个自由参数,天然适合多分类问题。类别再多,参数也不会膨胀。这意味着在多分类场景下,它通常比 sigmoid 校准效果更好,也更不容易过拟合。

3.3 实战代码

from sklearn.calibration import CalibratedClassifierCV, CalibrationDisplay
from sklearn.datasets import make_classification
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 生成三分类数据集
X, y = make_classification(
    n_samples=5000, n_classes=3, n_features=20,
    n_informative=8, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# 训练基础分类器
base_clf = GaussianNB().fit(X_train, y_train)

# Sigmoid 校准
sigmoid_clf = CalibratedClassifierCV(
    base_clf, method="sigmoid", ensemble=False
).fit(X_train, y_train)

# 温度缩放校准(1.8 新特性)
temp_clf = CalibratedClassifierCV(
    base_clf, method="temperature", ensemble=False
).fit(X_train, y_train)

# 绘制可靠性图
fig, axes = plt.subplots(figsize=(12, 4), ncols=3, sharey=True)
for i, c in enumerate(temp_clf.classes_):
    CalibrationDisplay.from_predictions(
        y_test == c, base_clf.predict_proba(X_test)[:, i],
        name="未校准", ax=axes[i], marker="s"
    )
    CalibrationDisplay.from_predictions(
        y_test == c, temp_clf.predict_proba(X_test)[:, i],
        name="温度缩放", ax=axes[i], marker="o"
    )
    CalibrationDisplay.from_predictions(
        y_test == c, sigmoid_clf.predict_proba(X_test)[:, i],
        name="Sigmoid", ax=axes[i], marker="v"
    )
    axes[i].set_title(f"类别 {c}")

fig.suptitle("各类别可靠性图对比")
plt.tight_layout()
plt.show()

从可靠性图里能看得很清楚:温度缩放在三个类别上都把预测概率拉向了对角线(也就是完美校准线)。而且因为只优化一个参数,它确实比 sigmoid 更不容易过拟合。个人觉得,对于大多数多分类任务,温度缩放应该是首选方案。

四、Gap Safe Screening 加速 L1 正则化模型

4.1 技术原理

L1 正则化(Lasso、ElasticNet 这些)的一个关键特性就是产生稀疏解——很多系数直接被拍成零。那问题来了:既然最终有那么多系数是零,为什么还要在每次迭代中都去更新它们呢?

Gap Safe Screening 正是基于这个思路。它是一种数学上可以证明安全的筛选规则,能在求解器的早期就把那些"注定为零"的特征踢出去,后续迭代就不用管它们了。

scikit-learn 1.8 在坐标下降求解器里引入了这个规则,受益的模型包括:

  • Lasso / LassoCV
  • ElasticNet / ElasticNetCV
  • MultiTaskLasso / MultiTaskLassoCV
  • MultiTaskElasticNet / MultiTaskElasticNetCV
  • GraphicalLasso / GraphicalLassoCV
  • SparsePCA / MiniBatchSparsePCA
  • DictionaryLearning / MiniBatchDictionaryLearning

4.2 性能对比

L1 惩罚越强,被筛掉的特征就越多,加速效果也越明显。在高维数据上,加速比能到数倍甚至几十倍。

import time
from sklearn.datasets import make_regression
from sklearn.linear_model import ElasticNetCV, LassoCV

# 生成高维回归数据
X, y = make_regression(
    n_samples=2000, n_features=10000, n_informative=50, random_state=0
)

# ElasticNetCV 性能测试
model_en = ElasticNetCV(cv=5)
tic = time.time()
model_en.fit(X, y)
elapsed_en = time.time() - tic
print(f"ElasticNetCV 拟合耗时: {elapsed_en:.1f} 秒")
print(f"非零系数数量: {(model_en.coef_ != 0).sum()}")

# LassoCV 性能测试
model_lasso = LassoCV(cv=5)
tic = time.time()
model_lasso.fit(X, y)
elapsed_lasso = time.time() - tic
print(f"LassoCV 拟合耗时: {elapsed_lasso:.1f} 秒")
print(f"非零系数数量: {(model_lasso.coef_ != 0).sum()}")

在这个高维场景里,Gap Safe Screening 把大部分特征在较大的 alpha 值处就提前排除了,坐标下降每次迭代只需要更新少量活跃特征。和 scikit-learn 1.7 比,同等配置下 ElasticNetCV 的拟合时间可能缩短到原来的十分之一甚至更少。

4.3 对用户的影响

最棒的是,这个优化对你来说完全透明。不用改一行代码,升级到 1.8 就自动加速。特别是做基因组学、文本分类这类特征维度远超样本数的场景,提升会非常明显。

五、经典多维缩放(ClassicalMDS)

5.1 算法介绍

经典多维缩放(Classical MDS),也叫主坐标分析(PCoA)或 Torgerson 缩放,是一种把高维数据映射到低维空间的方法。和非度量 MDS 需要迭代优化不同,经典 MDS 通过对双中心化距离矩阵做特征值分解,直接给出解析解——算得快、结果确定。

scikit-learn 1.8 在 sklearn.manifold 模块里新增了 ClassicalMDS。一个有趣的数学事实是:用欧氏距离的时候,ClassicalMDS 的结果和 PCA 完全等价。但它的真正优势在于,它能接受任意距离度量,甚至可以直接输入你预先算好的距离矩阵。

5.2 API 与参数

ClassicalMDS 的接口很简洁:

from sklearn.manifold import ClassicalMDS

cmds = ClassicalMDS(
    n_components=2,       # 目标维度
    metric="euclidean",   # 距离度量,也可设为 "precomputed"
    metric_params=None    # 传递给距离度量的额外参数
)

5.3 实战示例:S 曲线降维

import matplotlib.pyplot as plt
from matplotlib import ticker
from sklearn import datasets, manifold

# 生成三维 S 曲线数据
n_samples = 1500
S_points, S_color = datasets.make_s_curve(n_samples, random_state=0)

# 使用 ClassicalMDS 降到二维
cmds = manifold.ClassicalMDS(n_components=2)
S_2d = cmds.fit_transform(S_points)

# 可视化
fig = plt.figure(figsize=(12, 5))

# 原始三维 S 曲线
ax1 = fig.add_subplot(1, 2, 1, projection="3d")
ax1.scatter(*S_points.T, c=S_color, s=30, alpha=0.8, cmap="Spectral")
ax1.set_title("原始三维 S 曲线", fontsize=14)
ax1.view_init(azim=-60, elev=9)

# ClassicalMDS 二维映射
ax2 = fig.add_subplot(1, 2, 2)
ax2.scatter(*S_2d.T, c=S_color, s=30, alpha=0.8, cmap="Spectral")
ax2.set_title("ClassicalMDS 二维投影", fontsize=14)

plt.tight_layout()
plt.show()

5.4 使用预计算距离矩阵

ClassicalMDS 还支持直接输入预计算的距离矩阵。当你需要用一些不太常规的距离度量时,这个功能就特别好用了:

from sklearn.manifold import ClassicalMDS
from sklearn.metrics import pairwise_distances
from sklearn.datasets import load_digits

X, y = load_digits(return_X_y=True)
X = X[:300]  # 取前 300 个样本

# 计算余弦距离矩阵
dist_matrix = pairwise_distances(X, metric="cosine")

# 使用预计算距离矩阵进行降维
cmds = ClassicalMDS(n_components=2, metric="precomputed")
X_2d = cmds.fit_transform(dist_matrix)
print(f"降维结果形状: {X_2d.shape}")  # (300, 2)

六、DecisionTreeRegressor absolute_error 复杂度优化

6.1 问题背景

决策树回归器的 criterion="absolute_error"(用中位数绝对误差作为分裂准则)之前的性能简直一言难尽。每次找最佳分裂点都要重算中位数,复杂度是 O(n²)。数据量一过万,训练时间就完全不可接受了。

6.2 优化效果

1.8 版本直接把复杂度优化到了 O(n log n)。效果有多夸张呢?10 万个样本上单次分裂,从大约 20 秒降到 0.1 秒左右,快了超过 100 倍。

import time
from sklearn.datasets import make_regression
from sklearn.tree import DecisionTreeRegressor

# 生成大规模数据
X, y = make_regression(n_samples=500_000, n_features=10, random_state=42)

# 使用优化后的 absolute_error 准则
tree = DecisionTreeRegressor(criterion="absolute_error", max_depth=10)

tic = time.time()
tree.fit(X, y)
elapsed = time.time() - tic

print(f"样本数量: 500,000")
print(f"训练耗时: {elapsed:.2f} 秒")
print(f"树的叶节点数: {tree.get_n_leaves()}")

6.3 适用场景

absolute_error 准则用中位数而非均值来衡量节点的中心趋势,所以对异常值的鲁棒性天然更强。做房价预测、金融时间序列这类包含噪声和离群点的回归任务时,它通常比默认的 "squared_error" 更稳健。但之前的性能问题让它基本只能停留在小数据集上。1.8 的优化终于让这个好用的准则可以在大规模数据上落地了。

七、HTML 估计器表示增强

7.1 功能概述

这个特性可能不像 GPU 加速那么耀眼,但对日常开发体验的提升真的是润物细无声。scikit-learn 1.8 大幅增强了估计器在 Jupyter Notebook 里的 HTML 展示效果:

  • 参数链接:每个超参数名称都变成了指向在线文档的超链接,点一下就能查到完整说明
  • 文档字符串工具提示:鼠标悬停在参数上,就能看到简要描述
  • 交互式下拉:复杂流水线和嵌套估计器可以展开/折叠

7.2 使用示例

from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.model_selection import GridSearchCV

# 构建一个嵌套流水线
pipe = make_pipeline(
    StandardScaler(),
    PolynomialFeatures(degree=2, interaction_only=True),
    LogisticRegression(C=10, max_iter=1000, random_state=0)
)

# 包装在 GridSearchCV 中
param_grid = {"logisticregression__C": [0.1, 1, 10]}
grid = GridSearchCV(pipe, param_grid, cv=5)

# 在 Jupyter Notebook 中直接显示
# 将看到带有参数链接和工具提示的交互式 HTML 表示
grid

说个实际的使用场景:当团队成员在审阅你的 Notebook 时,他们可以直接在估计器的 HTML 展示里了解每个参数是什么意思,不用跳出去翻文档。这对团队协作和代码可读性的提升是很大的。

八、其他值得关注的更新

除了上面这七个大的新特性,1.8 还有一些值得留意的改动:

  • QuadraticDiscriminantAnalysis 增强:新增了 solver、covariance_estimator 和 shrinkage 参数,API 终于跟 LinearDiscriminantAnalysis 对齐了,协方差矩阵估计的灵活性大幅提升。
  • LogisticRegression API 重构:penalty 参数已弃用,改为用 l1_ratio 来统一控制正则化类型(l1_ratio=0.0 等价于 L2,l1_ratio=1.0 等价于 L1)。这个改动其实挺大的,迁移时注意一下。
  • PassiveAggressive 系列弃用:PassiveAggressiveClassifier 和 PassiveAggressiveRegressor 已弃用,推荐用 SGDClassifier(learning_rate="pa1") 替代。
  • 新评估指标:新增 d2_brier_score(Brier 损失的 D² 得分)和 confusion_matrix_at_thresholds(在多个阈值处计算混淆矩阵)。
  • SplineTransformer 缺失值处理:新增对缺失值的原生支持。终于不用手动处理了。
  • MaxAbsScaler 裁剪:新增 clip=True 参数,可以把变换后超出范围的值裁剪回 [-1, 1]。
  • MDS 初始化改进:MDS 现在可以用 ClassicalMDS 的结果作为初始化,通过 init 参数指定,同时支持自定义距离度量。

九、安装与升级

升级很简单,几条命令的事:

# 使用 pip 安装或升级
pip install --upgrade scikit-learn

# 使用 conda 安装或升级
conda install -c conda-forge scikit-learn

# 验证版本
python -c "import sklearn; print(sklearn.__version__)"
# 输出: 1.8.0

想体验自由线程 CPython 3.14 的话,需要装对应版本的 Python:

# 在自由线程 CPython 3.14 环境中安装
pip install scikit-learn
# scikit-learn 会自动提供免 GIL 的 wheel 包

GPU 加速还需要额外装 CuPy 或 PyTorch:

# 安装 CuPy(根据 CUDA 版本选择)
pip install cupy-cuda12x

# 或安装 PyTorch(根据 CUDA 版本选择)
pip install torch --index-url https://download.pytorch.org/whl/cu121

十、总结

说句掏心窝子的话,scikit-learn 1.8 确实是一次让人兴奋的大版本更新。它在好几个维度上都有实质性的突破:

  1. 硬件利用率:Array API 让 GPU 加速成为现实,自由线程 CPython 支持了真正的多核并行。scikit-learn 终于摆脱了"只能跑在单核 CPU"的标签。
  2. 算法丰富度:温度缩放校准和经典多维缩放的加入,补上了概率校准和流形学习方面的重要短板。
  3. 性能优化:Gap Safe Screening 和决策树 absolute_error 的优化,在高维和大规模数据上带来了一到两个数量级的提速,而且你不需要改任何代码。
  4. 开发体验:HTML 估计器展示的增强虽然不起眼,但用起来真的让调试和协作顺畅不少。

我的建议是尽快升级。特别是如果你在做高维特征选择(Gap Safe Screening 加速太明显了)、多分类概率校准(温度缩放就是比 sigmoid 好用)、或者处理大规模数据(GPU 加速 + 优化的决策树),这些改进不是锦上添花,是能实实在在帮你省时间、出更好模型的。

文章更新记录 (1)
  • — Title and description updated