scikit-learn 数据互操作性完全指南:set_output API、稀疏数据与 Array API(GPU)支持
2026/9/18 10:23:42 网站建设 项目流程

scikit-learn 数据互操作性完全指南:set_output API、稀疏数据与 Array API(GPU)支持

【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn

scikit-learn 的fit(X, y)transform(X)等核心方法会面对多种形态的输入数据:普通的 array-like 对象、稀疏矩阵与稀疏数组、pandas/polars 数据表,以及符合 Array API 规范的数组(包括 GPU 上的张量)。本文以 doc/data_interoperability.rst 为骨架,结合 sklearn/utils/_set_output.py 与 sklearn/utils/_array_api.py 等源码实现,系统讲解这四类数据的处理机制,并给出set_outputarray_api_dispatch两大配置的实际用法。读完本文,你将掌握:如何让 transformer 输出 pandas/polars DataFrame(含特征名传播)、如何控制稀疏输出的矩阵/数组形态,以及如何在 GPU 上运行支持 Array API 的估计器。

scikit-learn 处理的四类输入数据

根据 doc/data_interoperability.rst,scikit-learn 为fit(X, y)fit(X)fit_transform(X)transform(X)中的X,以及transform(X)fit_transform(X)返回的Xt,统一支持以下四类数据:

数据类型说明
array-like 对象fit(X)transform(X)中,通过numpy.asarray转换为 numpy ndarray;返回的Xt为 ndarray,或稀疏矩阵/稀疏数组
稀疏矩阵(sparse matrices)与稀疏数组多数估计器支持稀疏X,部分不支持并会抛出错误
表格数据(tabular data)pandas 与 polars DataFrame,详见 doc/modules/df_output_transform.rst
Array API 兼容数组包括 GPU 上的数组,详见 doc/modules/array_api.rst

从 sklearn/_config.py 的全局配置默认值可以看到这三类行为对应的开关:transform_output="default"(表格输出开关)、array_api_dispatch=False(Array API 开关)、sparse_interface="spmatrix"(稀疏接口形态开关)。下面逐一展开。

第一类:array-like 对象与 numpy 转换

array-like 是最基础的数据形态,包括列表、元组、numpy 数组等。scikit-learn 在内部统一调用numpy.asarray(或numpy.array)将其转换为 numpy ndarray 后再进行计算,transformfit_transform的返回结果Xt同样是 ndarray(或稀疏矩阵/稀疏数组,见下一节)。

值得注意的是,当array_api_dispatch=False(默认值)时,即使是 Array API 兼容输入(例如 JAX 数组)也会被转换为 numpy 数组。某些输入无法被成功转换,例如分配在 GPU 上的 torch 张量——这正是 doc/modules/array_api.rst 建议在使用 Array API 输入时显式打开array_api_dispatch=True的原因(详见本文 Array API 章节)。

第二类:稀疏矩阵与稀疏数组

许多估计器可以处理稀疏X,也有一些无法处理并会直接抛出错误。文档给出的两个典型例子是:

  • linear_model.LogisticRegression:可以在稀疏X上拟合;
  • isotonic.IsotonicRegression:不可以。

部分 transformer 的transform/fit_transform会返回稀疏Xt。是否返回稀疏结果通常由一个sparse_output参数控制,例如preprocessing.SplineTransformer就带有该参数。

用 sparse_interface 控制稀疏形态

返回的是scipy.sparse的稀疏矩阵(csr_matrix / csc_matrix 等)还是稀疏数组,由配置项sparse_interface决定,可通过两个入口设置:

  • config_context(sparse_interface=...):上下文管理器,局部生效;
  • set_config(sparse_interface=...):全局设置。

默认值为"spmatrix"(稀疏矩阵)。该配置同时控制拟合后稀疏类型的属性(如某些估计器的coef_components_等)是稀疏矩阵还是稀疏数组。这一开关的实际消费逻辑位于 sklearn/_config.py 的全局配置字典与config_context/set_config的参数解析中。

第三类:表格数据与 set_output API

这是数据互操作性中与日常建模最相关的一部分,完整用户指南见 doc/modules/df_output_transform.rst,配套可运行示例见 examples/miscellaneous/plot_set_output.py。

特征名传播:从 numpy 数组到 DataFrame

默认情况下,scikit-learn 的 transformer 返回 numpy 数组(有时是稀疏数组)。由于 numpy 数组无法为轴的索引提供名称,在 1.0 版本之前pipeline.Pipeline无法传播特征名:

  • 单步估计器不知道如何处理传入的特征名;
  • pipeline 不知道如何在步骤之间传递特征名。

实际工作中大量用例始于带列名/特征名的表格数据(pandas DataFrame 或 polars DataFrame),因此这一能力至关重要。scikit-learn 支持表格数据的历程有两个里程碑:

  1. compose.ColumnTransformer在版本 0.20 引入,作为在不同特征上应用不同估计器的"网关",且能理解传入的特征名;
  2. 特征名问题最终由SLEP007(Feature names, their generation and the API)在版本 1.1 完整解决。此后,估计器在fit阶段接收到 DataFrame 时,会设置一个feature_names_in_属性保存特征名;它既能理解 pandas DataFrame,也能理解实现了 Python DataFrame interchange protocol(__dataframe__方法)的 DataFrame。同时,已拟合的估计器拥有get_feature_names_out方法,transformer 的该方法返回的就是transform输出对应的特征名。

set_output API 的引入

进一步支持"DataFrame 进、DataFrame 出"的关键是SLEP018,其 pandas 支持在版本 1.2 实现,polars 支持在版本 1.4 实现。它引入了set_outputAPI,用于配置 transformer 输出 pandas 或 polars DataFrame:

  • 按估计器配置:调用该估计器的set_output方法;
  • 全局配置:调用set_config(transform_output="pandas");若希望输出 polars DataFrame,则把"pandas"换成"polars"

完整用法示例

以下是文档中的标准用法(默认 numpy 输出):

>>> import numpy as np >>> import pandas as pd >>> from sklearn.compose import ColumnTransformer >>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import OneHotEncoder >>> from sklearn.linear_model import LinearRegression >>> X = pd.DataFrame( ... {"animals": ["cat", "cat", "dog", "dog"], "numeric": np.linspace(-1, 1, 4)} ... ) >>> y = np.array([-1.5, 0, 0.1, 1.0]) >>> ct = ColumnTransformer( ... [("categorical", OneHotEncoder(sparse_output=False), ["animals"])], ... remainder="passthrough", ... ) >>> model = make_pipeline(ct, LinearRegression()).fit(X, y) >>> model.feature_names_in_ array(['animals', 'numeric'], dtype=object) >>> model[0].get_feature_names_out() array(['categorical__animals_cat', 'categorical__animals_dog', 'remainder__numeric'], dtype=object) >>> model[0].transform(X) array([[ 1. , 0. , -1. ], [ 1. , 0. , -0.33333333], [ 0. , 1. , 0.33333333], [ 0. , 1. , 1. ]])

同样的流程,开启 pandas 输出:

>>> from sklearn import set_config >>> set_config(transform_output="pandas") >>> model[0].transform(X)

此时transform返回一个 pandas DataFrame,列名与get_feature_names_out()一致:

索引categorical__animals_catcategorical__animals_dogremainder__numeric
01.00.0-1.000000
11.00.0-0.333333
20.01.00.333333
30.01.01.000000

恢复默认 numpy 输出:

>>> set_config(transform_output="default")

从示例中可以看到关键细节:set_output配置生效的前提是估计器实现了get_feature_names_out,pandas 输出的列名正是由该方法的返回值决定(如categorical__animals_catremainder__numeric这种带步骤前缀的命名)。

set_output 的源码实现

set_output的底层实现在 sklearn/utils/_set_output.py 中,核心组件是"容器适配器"机制:

  • PandasAdapter(sklearn/utils/_set_output.py)与PolarsAdapter(sklearn/utils/_set_output.py)实现了统一的ContainerAdapterProtocol,负责create_container(把 numpy 结果包装成 DataFrame)、is_supported_containerrename_columnshstack等操作。两个适配器通过ADAPTERS_MANAGER注册(sklearn/utils/_set_output.py),因此扩展新容器类型只需注册新适配器。
  • _get_output_config(sklearn/utils/_set_output.py)决定输出容器:优先读取估计器自身的_sklearn_output_config(由set_output写入),否则回退到全局配置transform_output;若配置值不在supported_outputs{"default"} | adapters)中会抛出ValueError
  • _wrap_method_output(sklearn/utils/_set_output.py)自动包装transform/fit_transform方法的返回值,使配置后的输出被包装为目标容器。
  • 适配器要求对应库(pandas / polars)已安装,否则check_library_installed会抛出带提示的ImportError(sklearn/utils/_set_output.py)。

_wrap_data_with_container(sklearn/utils/_set_output.py)可以看到一个重要限制:当 transformer 输出的是 scipy 稀疏矩阵时,无法直接包装为 DataFrame,此时会抛出ValueError并建议"把 transformer 输出改为稠密数组,或通过set_output(transform='default')关闭 pandas 输出"。因此OneHotEncoder等默认输出稀疏结果的 transformer 若要配合set_output使用,需要显式设置sparse_output=False(示例代码中正是如此)。

set_output方法本身(sklearn/utils/_set_output.py)接受transform={"default", "pandas", "polars", None},其中"polars"选项在 1.4 版本加入,None表示不做任何修改。

元估计器中的传播

Pipeline等元估计器中调用set_output(transform="pandas")会作用于所有步骤;底层通过_safe_set_output(sklearn/utils/_set_output.py)把输出配置安全地下发给各个子估计器——若子估计器没有transform方法(如纯预测型模型)则跳过,若有transform但缺少set_output则抛出ValueError提示无法配置输出。

第四类:Array API 支持(实验性)

Array API 规范(https://data-apis.org/array-api/latest/)定义了所有具备 NumPy 类似 API 的数组库的统一标准接口。部分主要依赖 NumPy(而非 Cython)实现fit/predict/transform算法逻辑的 scikit-learn 估计器,可以配置为接受任何 Array API 兼容的输入数据结构,并把运算自动分发到对应的数组库命名空间,而不是依赖 NumPy。完整文档见 doc/modules/array_api.rst。

重要:此支持目前被视为实验性,必须显式开启array_api_dispatch配置,并且假设安装了最新版本的依赖库。实验性期间不保证向后兼容:当新版本依赖修复 bug 时,scikit-learn 不会为旧版本做 backport 或维护兼容。

受支持且定期跑合规测试的数组库

scikit-learn 会定期对下表列出的库和硬件运行自动化合规测试(其他符合 Array API 的库和设备也可能开箱即用,覆盖面会随时间增长):

Library安装支持设备备注
PyTorch官方安装指南CPU、NVIDIA GPU (CUDA)、Apple GPU (MPS)、Intel GPU (XPU)见 MPS 支持、Intel GPU (XPU) 支持、float64 设备支持说明
CuPy官方安装指南CUDA
dpnp官方安装指南CPU、Intel GPU (SYCL)驱动安装见安装链接;见 XPU 支持与 float64 设备支持说明

如何启用 Array API 支持

需要把配置参数array_api_dispatch设为True。官方建议全局设置以保证行为一致、防止不同数组命名空间被意外混用;文档示例中使用上下文管理器config_context则只是为了在代码片段结束后不必手动复位为False

开启前必须设置环境变量SCIPY_ARRAY_API=1(需在导入 scipy 与 scikit-learn 之前):

export SCIPY_ARRAY_API=1

该环境变量是临时性的,详细说明见 SciPy 的 Array API 文档。源码层面的校验在_check_array_api_dispatch(sklearn/utils/_array_api.py):开启array_api_dispatch时,若 scipy 版本低于 1.14.0 会抛出ImportError;若未设置SCIPY_ARRAY_API=1则抛出RuntimeError

启用后行为变化:

  • array_api_dispatch=False时,Array API 输入(如 JAX 数组)会被numpy.asarray/numpy.array转换为 NumPy,输出数组也总是 NumPy;转换可能失败(例如分配在 GPU 上的 torch 张量);
  • array_api_dispatch=True时,输出数组的类型取决于输入数组(详见下文"输入与输出数组类型处理")。

示例:PyTorch 在 CUDA GPU 上运行 LDA

文档给出的示例用 PyTorch 在 CUDA GPU 上运行LinearDiscriminantAnalysis

>>> from sklearn.datasets import make_classification >>> from sklearn import config_context >>> from sklearn.discriminant_analysis import LinearDiscriminantAnalysis >>> import torch >>> X_np, y_np = make_classification(random_state=0) >>> X_torch = torch.asarray(X_np, device="cuda", dtype=torch.float32) >>> y_torch = torch.asarray(y_np, device="cuda", dtype=torch.float32) >>> with config_context(array_api_dispatch=True): ... lda = LinearDiscriminantAnalysis() ... X_trans = lda.fit_transform(X_torch, y_torch) >>> type(X_trans) <class 'torch.Tensor'> >>> X_trans.device.type 'cuda'

这一模式对任何受支持的数组库都适用:把torch.asarray(..., device="cuda")换成cupy.asarray(...)(CuPy)或dpnp.asarray(...)(dpnp)即可;在 PyTorch 内也可以通过修改device=参数(如"cpu""xpu""mps")切换不同设备。

训练完成后,估计器的拟合属性(数组类型)与训练数据来自同一个 Array API 命名空间、存放在同一设备上。向transform/predict传入不同命名空间或同一命名空间内不同设备的数据会报错:

>>> with config_context(array_api_dispatch=True): ... lda.transform(X_np) Traceback (most recent call last): ... ValueError: Inputs passed to LinearDiscriminantAnalysis.transform() must use the same namespace and the same device as those passed to fit()...

该错误信息的生成逻辑在check_same_namespace(sklearn/utils/_array_api.py):它会对比X与拟合属性(如coef_)的命名空间和设备,不一致时抛出ValueError,并提示用get_namespace_and_devicemove_estimator_to把估计器迁移过去。

在设备/命名空间之间移动估计器

move_estimator_to(sklearn/utils/_array_api.py)可以把估计器的数组属性迁移到不同的命名空间和设备(非数组属性保持不变):

>>> from sklearn.utils._array_api import move_estimator_to >>> import numpy as np >>> lda_np = move_estimator_to(lda, np, device="cpu") >>> with config_context(array_api_dispatch=True): ... X_trans = lda_np.transform(X_np) >>> type(X_trans) <class 'numpy.ndarray'>

支持 Array API 的组件清单

下列组件支持 Array API 兼容输入。

估计器

  • covariance.LedoitWolfcovariance.OAS(见 float64 设备支持说明)
  • decomposition.PCAsvd_solver="full"svd_solver="covariance_eigh"svd_solver="randomized"randomized仅当power_iteration_normalizer="QR"
  • kernel_approximation.Nystroem
  • linear_model.LogisticRegressionsolver="lbfgs"solver="newton-cg"
  • linear_model.LogisticRegressionCVsolver="lbfgs"solver="newton-cg"
  • linear_model.PoissonRegressorsolver="lbfgs"
  • linear_model.Ridgesolver="svd"
  • linear_model.RidgeCV(见 float64 设备支持说明)
  • linear_model.RidgeClassifiersolver="svd"
  • linear_model.RidgeClassifierCV(见 float64 设备支持说明)
  • discriminant_analysis.LinearDiscriminantAnalysissolver="svd"
  • naive_bayes.GaussianNB
  • preprocessing.BinarizerKernelCentererLabelBinarizersparse_output=False)、LabelEncoderMaxAbsScalerMinMaxScalerNormalizerPolynomialFeaturesStandardScaler(见 float64 设备支持说明)
  • mixture.GaussianMixtureinit_params="random"init_params="random_from_data",且warm_start=False

元估计器(在基估计器也支持 Array API 的前提下接受 Array API 输入):

  • calibration.CalibratedClassifierCVmethod="temperature"
  • pipeline.FeatureUnion
  • model_selection.GridSearchCVRandomizedSearchCVHalvingGridSearchCVHalvingRandomSearchCV

指标

  • 分类:accuracy_scoreaverage_precision_scorebalanced_accuracy_scorebrier_score_losscohen_kappa_scoreconfusion_matrixf1_scorefbeta_scorehamming_lossjaccard_scorelog_lossmatthews_corrcoef(见 float64 设备支持说明)、multilabel_confusion_matrixprecision_scoreprecision_recall_curveprecision_recall_fscore_supportrecall_scoreroc_curvezero_one_loss
  • 回归:d2_absolute_error_scored2_brier_scored2_log_loss_scored2_pinball_scored2_tweedie_scoreexplained_variance_scoremax_errormean_absolute_errormean_absolute_percentage_errormean_gamma_deviancemean_pinball_lossmean_poisson_deviancemean_squared_errormean_squared_log_errormean_tweedie_deviancemedian_absolute_errorr2_scoreroot_mean_squared_errorroot_mean_squared_log_error
  • 聚类与核函数:calinski_harabasz_scorecluster.contingency_matrixdet_curvepairwise.additive_chi2_kernelpairwise.chi2_kernelpairwise.cosine_similaritypairwise.cosine_distancespairwise_distances(仅支持"cosine""euclidean""manhattan""l2")、pairwise_distances_argminpairwise.euclidean_distances(见 float64 设备支持说明)、pairwise.laplacian_kernelpairwise.linear_kernelpairwise.manhattan_distancespairwise.paired_cosine_distancespairwise.paired_euclidean_distancespairwise.paired_manhattan_distancespairwise_kernelspairwise.polynomial_kernelpairwise.rbf_kernel(见 float64 设备支持说明)、pairwise.sigmoid_kernel

工具

  • preprocessing.label_binarizesparse_output=False
  • model_selection.cross_val_predictmodel_selection.train_test_split
  • utils.check_consistent_length

输入与输出数组类型处理

array_api_dispatch=True时,估计器和评分函数可以接受来自不同数组库/不同设备的混合输入,scikit-learn 会按需转换数组使之一致。规则如下:

  • 估计器:一切跟随X。混合输入会被转换为与X相同的数组库和设备。这包括构造参数(如ysample_weight);当X是 Array API 兼容输入时,其他数组输入也会被转换(如果尚未匹配)。该行为使得在 pipeline 内的任意环节都可以从 CPU 切换到 GPU——无需显式移动y(注意:scikit-learn 的 pipeline 不允许转换y,以避免数据泄漏)。
  • 评分函数:一切跟随y_pred。混合输入(如y_truesample_weight)会被转换为与y_pred相同的数组库和设备。这使得评分函数可被用于会在设备之间移动输入数组的元估计器中。输出类型取决于输出值的数量:返回标量时是 Python 标量(通常为float实例);支持多分类或多输出的评分函数在需要输出多个值时,会返回与y_pred同库同设备的数组。

两个特殊处理点:

  • y为字符串数组(NumPy)而其他输入为任意容器的数值数组时也支持:由于 Array API 规范不覆盖字符串数组,scikit-learn 会把y内部转换为数值表示(如 one-hot 编码或序数编码),并迁移到其他输入的命名空间和设备;
  • 估计器的classes_属性始终保留在原始y所在数组库中,以支持字符串类别标签。

文档给出的经典场景示例:一个 pipeline 中Xy都在 CPU 上,经过TargetEncoder(需要y,因此Xy都必须在 CPU)、FunctionTransformer(func=partial(torch.asarray, device="cuda"))(把X移到 GPU)、Ridge(在 GPU 上做大规模矩阵运算性能更佳)三个步骤;由于Ridge接受混合输入,即使y无法被 pipeline 转换,整体流程依然可以运行。

开发者:array_api_support 标签与合规测试

为估计器添加array_api_supporttag 即可启用通用测试(common tests)中的专项检查,验证使用纯 NumPy 与 Array API 输入时结果一致。运行这些检查需要安装array-api-strict(无需 GPU 即可模拟非 CPU 设备,便于快速迭代调试):

pip install array-api-strict # 以及其它需要的库 pytest -k "array_api" -v

注意用-v查看哪些检查因缺少依赖而被自动跳过。要在真实 GPU 上运行完整测试,还需安装 PyTorch、CuPy 与/或 dpnp 并具备相应 GPU 硬件;由于 CUDA、MPS、Intel GPU 后端需要不同硬件,完整 GPU 覆盖通常分散在多台机器上。

注意事项

  • MPS 设备支持(macOS):PyTorch 可通过 Metal Performance Shaders (MPS) 使用 M1/M2 芯片的内部 GPU,但 MPS 设备支持尚不完整。运行测试前设置PYTORCH_ENABLE_MPS_FALLBACK=1

    PYTORCH_ENABLE_MPS_FALLBACK=1 pytest -k "array_api" -v

    当前所有 scikit-learn 测试应能通过,但计算速度不一定优于 CPU。

  • float64 设备支持:scikit-learn 中某些操作会自动使用float64精度以防溢出并保证正确性(如metrics.pairwise.euclidean_distancespreprocessing.StandardScaler)。但部分命名空间与设备的组合不支持float64(如 PyTorch on MPS、部分 Intel GPU 设备上的 PyTorch XPU 或 dpnp),此时 scikit-learn 会回退到float32,可能导致与不使用 Array API 分发(或使用支持float64的设备)时行为不同(通常表现为数值不稳定)。

  • Intel GPU 支持:PyTorch XPU 支持仅适用于 PyTorch >= 2.12。

小结:如何选择合适的数据通路

回到 doc/data_interoperability.rst 的四类数据划分,实际选型可以遵循以下原则:

  • 普通 numpy 工作流:保持默认配置即可,array-like 输入会被numpy.asarray规范化;
  • 带稀疏特征的场景:优先使用支持稀疏输入的估计器,并通过 transformer 的sparse_output参数控制是否返回稀疏结果,再用sparse_interface配置决定稀疏矩阵/稀疏数组形态;
  • 需要保留列名的表格数据场景:通过set_output(transform="pandas")(或全局set_config(transform_output="pandas"))让 pipeline 输出带特征名的 DataFrame,结合feature_names_in_get_feature_names_out实现端到端的特征名传播;
  • 需要 GPU 加速或希望接入非 NumPy 数组生态(PyTorch、CuPy、dpnp)的场景:先设置SCIPY_ARRAY_API=1环境变量,再开启array_api_dispatch=True,并核对所选估计器/指标/工具是否在支持清单内。

这三套机制(numpy 转换、稀疏接口配置、set_outputarray_api_dispatch)共享 sklearn/_config.py 中的同一套全局配置体系,均可通过set_configconfig_context或估计器自身方法按需切换,让 scikit-learn 在 numpy、稀疏、pandas/polars 与 GPU 数组之间保持高度互操作。

【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询