常见问题解答#

在这里,我们尝试回答邮件列表中经常出现的问题。

关于项目#

项目名称是什么(很多人弄错了)?#

scikit-learn,但不是 scikit 或 SciKit 也不叫 sci-kit learn。以前使用过的 scikits.learn 或 scikits-learn 也不是。

如何发音项目名称?#

sy-kit learn。sci 代表科学!

为什么是 scikit?#

有多个 scikit,它们是围绕 SciPy 构建的科学工具箱。除了 scikit-learn 之外,另一个流行的工具箱是 scikit-image.

您支持 PyPy 吗?#

scikit-learn 定期测试和维护,以与 PyPy(具有内置即时编译器的替代 Python 实现)一起使用。

但是请注意,此支持仍被认为是实验性的,特定组件的行为可能略有不同。有关更多详细信息,请参阅特定模块的测试套件。

如何获得使用 scikit-learn 中图像的许可?#

包含在 scikit-learn 存储库 中的图像以及在 scikit-learn 文档 中生成的图像可以通过 BSD 3-Clause 许可 用于您的工作。强烈鼓励并感谢您引用 scikit-learn。请参阅 引用 scikit-learn.

实现决策#

为什么不支持深度学习或强化学习?将来会支持吗?#

深度学习和强化学习都需要丰富的词汇来定义架构,而深度学习还需要 GPU 来进行高效计算。但是,两者都不符合 scikit-learn 的设计约束。因此,深度学习和强化学习目前不在 scikit-learn 试图实现的目标范围内。

您可以在 您会添加 GPU 支持吗? 中找到有关添加 GPU 支持的更多信息。

请注意,scikit-learn 目前在 sklearn.neural_network 中实现了简单的多层感知器。我们只接受此模块的错误修复。如果您想实现更复杂的深度学习模型,请转向流行的深度学习框架,例如 tensorflowkeraspytorch.

您会将图形模型或序列预测添加到 scikit-learn 中吗?#

在可预见的将来不会。scikit-learn 试图为机器学习中的基本任务提供统一的 API,并使用管道和元算法(如网格搜索)将所有内容联系在一起。结构化学习所需的理念、API、算法和专业知识与 scikit-learn 提供的有所不同。如果我们开始进行任意结构化学习,我们将需要重新设计整个软件包,并且该项目可能会在自身重量下崩溃。

有两个 API 与 scikit-learn 相似的项目可以进行结构化预测

  • pystruct 处理一般的结构化学习(专注于任意图形结构上的 SSVM,具有近似推理;将样本定义为图形结构的实例)。

  • seqlearn 仅处理序列(专注于精确推理;具有 HMM,但主要是为了完整性;将特征向量视为样本,并使用偏移编码来表示特征向量之间的依赖关系)。

为什么您从 scikit-learn 中删除了 HMM?#

请参阅 您会将图形模型或序列预测添加到 scikit-learn 中吗?.

您会添加 GPU 支持吗?#

默认添加 GPU 支持会引入大量的硬件特定软件依赖项,并且现有算法需要重新实现。这将使普通用户更难安装 scikit-learn,也使开发人员更难维护代码。

但是,自 2023 年以来,一个有限但不断增长的 scikit-learn 估计器列表 已经可以在 GPU 上运行,前提是输入数据以 PyTorch 或 CuPy 数组的形式提供,并且 scikit-learn 已配置为接受此类输入,如 数组 API 支持(实验性) 中所述。这种数组 API 支持允许 scikit-learn 在 GPU 上运行,而不会在主包中引入大量硬件特定软件依赖项。

大多数依赖 NumPy 进行计算密集型操作的估计器可以考虑进行数组 API 支持,因此也支持 GPU。

但是,并非所有 scikit-learn 估计器都适合通过数组 API 在 GPU 上高效运行,这是由于基本算法的原因。例如,目前在 scikit-learn 中使用 Cython 实现的基于树的模型从根本上来说不是基于数组的算法。其他算法,例如 k 均值或 k 最近邻,依赖于基于数组的算法,但也使用 Cython 实现。Cython 用于手动交织连续的数组操作,以避免对大型中间数组引入性能杀手级的内存访问:这种低级算法重写称为“内核融合”,在可预见的未来无法通过数组 API 表达。

要为无法通过数组 API 高效实现的估计器添加高效的 GPU 支持,需要为 scikit-learn 设计和采用更灵活的扩展系统。这种可能性正在以下 GitHub 问题中考虑(正在讨论中)

为什么与其他工具相比,scikit-learn 中的分类变量需要预处理?#

大多数 scikit-learn 假设数据位于单个数字数据类型的 NumPy 数组或 SciPy 稀疏矩阵中。目前,这些数据类型没有明确地表示分类变量。因此,与 R 的 data.framespandas.DataFrame 不同,我们需要将分类特征显式转换为数值,如 编码分类特征 中所述。另请参阅 具有混合类型的列转换器,了解处理异构(例如分类和数值)数据的示例。

为什么 scikit-learn 无法直接使用,例如,pandas.DataFrame#

目前预期的同构 NumPy 和 SciPy 数据对象对于大多数操作来说是最有效的处理方式。要支持 Pandas 分类类型,还需要进行大量工作。因此,将输入限制为同构类型可以降低维护成本,并鼓励使用高效的数据结构。

但是请注意,ColumnTransformer 使得通过将按名称或数据类型选择的 DataFrame 列的同构子集映射到专用的 scikit-learn 转换器,方便处理异构 Pandas DataFrame。因此,在处理异构 DataFrame 时,ColumnTransformer 通常用作 scikit-learn 管道的第一步(有关更多详细信息,请参阅 管道:链接估计器)。

另请参阅 具有混合类型的列转换器,了解处理异构(例如分类和数值)数据的示例。

您是否计划在管道中实现目标 y 的转换?#

目前,转换仅适用于管道中的特征 X。关于无法在管道中转换 y 的问题,一直存在着长期讨论。请关注 GitHub 问题 #4143。同时,您可以查看 TransformedTargetRegressorpipegraphimbalanced-learn。请注意,scikit-learn 解决了在训练之前应用可逆转换并在预测之后进行逆转换的情况。scikit-learn 旨在解决在训练时需要转换 y 而不是在测试时转换 y 的用例,用于重采样和类似用途,例如在 imbalanced-learn 中。通常,这些用例可以通过自定义元估计器而不是 Pipeline 来解决。

为什么线性模型有这么多不同的估计器?#

通常,每种模型类型都有一个分类器和一个回归器,例如 GradientBoostingClassifierGradientBoostingRegressor。两者具有相似的选项,并且两者都有参数 loss,这在回归情况下特别有用,因为它可以估计条件均值以及条件分位数。

对于线性模型,有许多估计器类彼此非常接近。让我们看一下

维护者视角:从原则上讲,它们都做同样的事情,只是它们施加的惩罚不同。然而,这会对解决底层优化问题的方式产生重大影响。最终,这相当于使用线性代数中的不同方法和技巧。一个特例是 SGDRegressor,它包含所有 4 种之前的模型,并且通过优化过程有所不同。另一个副作用是,不同的估计器偏好不同的数据布局(X C 连续或 F 连续,稀疏 csr 或 csc)。这种看似简单的线性模型的复杂性是拥有针对不同惩罚的不同估计器类的原因。

用户视角:首先,当前的设计灵感来自科学文献,其中具有不同正则化/惩罚的线性回归模型被赋予了不同的名称,例如岭回归。拥有具有相应名称的不同模型类,使用户更容易找到这些回归模型。其次,如果将上述 5 种线性模型统一到一个类中,将会有一个具有很多选项的参数,例如 solver 参数。除此之外,不同参数之间还会有很多排他性交互。例如,参数 solverprecomputeselection 的可能选项将取决于所选的惩罚参数 alphal1_ratio 的值。

贡献#

如何为 scikit-learn 做贡献?#

请参阅 贡献。在想要添加新算法之前(这通常是一项重大且漫长的工作),建议从 已知问题 开始。请勿直接联系 scikit-learn 的贡献者,询问有关为 scikit-learn 做贡献的问题。

为什么我的拉取请求没有得到任何关注?#

scikit-learn 的代码审查过程需要相当长的时间,贡献者不应该因为他们的拉取请求缺乏活动或审查而感到沮丧。我们非常重视第一次就把事情做对,因为维护和后期的更改成本很高。我们很少发布任何“实验性”代码,因此我们所有的贡献都会立即受到高度使用,并且应该从一开始就具有尽可能高的质量。

除此之外,scikit-learn 在审查带宽方面受到限制;许多审查者和核心开发人员在业余时间工作于 scikit-learn。如果您的拉取请求审查速度很慢,很可能是因为审查者很忙。我们恳请您理解,并请求您不要仅仅因为这个原因而关闭您的拉取请求或停止您的工作。

新算法的包含标准是什么?#

我们只考虑成熟的算法进行包含。一个经验法则是,至少在发表后 3 年,引用次数超过 200 次,并且得到广泛使用和应用。一种在广泛使用的方法上提供明确改进的技术(例如,增强的數據结构或更有效的近似技术)也将被考虑包含。

在满足上述标准的算法或技术中,只有那些与 scikit-learn 的当前 API 很好地契合的算法或技术才会被接受,即 fitpredict/transform 接口,并且通常具有 numpy 数组或稀疏矩阵的输入/输出。

贡献者应该用研究论文和/或其他类似包中的实现来支持所提议的添加的重要性,通过常见的用例/应用程序来证明其有用性,并用基准测试和/或图表来证实性能改进(如果有)。预计所提出的算法至少在某些方面应该优于 scikit-learn 中已经实现的方法。

如果包含一个加速现有模型的新算法,则更容易

  • 它不引入新的超参数(因为它使库更具未来可行性),

  • 当贡献提高速度时,以及当它没有提高速度时,很容易清楚地记录下来,例如,“当 n_features >> n_samples 时”,

  • 基准测试清楚地显示了加速。

此外,请注意,您的实现不必在 scikit-learn 中才能与 scikit-learn 工具一起使用。您可以以与 scikit-learn 兼容的方式实现您喜欢的算法,将其上传到 GitHub 并告诉我们。我们很乐意将其列在 相关项目 下。如果您已经在 GitHub 上有一个遵循 scikit-learn API 的包,您可能也对查看 scikit-learn-contrib 感兴趣。

为什么您对包含在 scikit-learn 中的算法如此挑剔?#

代码伴随着维护成本,我们需要平衡我们拥有的代码量与团队规模(并在此基础上加上这样一个事实,即复杂度随着特征数量的增加而呈非线性增长)。该包依赖于核心开发人员利用他们的空闲时间来修复错误、维护代码和审查贡献。任何添加的算法都需要开发人员的未来关注,此时原始作者可能早就失去了兴趣。另请参阅 新算法的包含标准是什么?。有关开源软件长期维护问题的精彩阅读,请查看 《道路与桥梁》的执行摘要

使用 scikit-learn#

获取有关 scikit-learn 用法的帮助的最佳方法是什么?#

  • 一般机器学习问题:使用 Cross Validated 以及 [machine-learning] 标签。

  • scikit-learn 用法问题:使用 Stack Overflow 以及 [scikit-learn][python] 标签。您也可以使用 邮件列表

请确保包含一个最小的可重现代码片段(理想情况下少于 10 行),该片段在玩具数据集上突出显示您的问题(例如,来自 sklearn.datasets 或使用 numpy.random 的函数以及固定的随机种子随机生成)。请删除任何不必要重现问题的代码行。

该问题应该可以通过简单地在安装了 scikit-learn 的 Python shell 中复制粘贴您的代码片段来重现。不要忘记包含导入语句。有关编写良好可重现代码片段的更多指导,请访问:https://stackoverflow.com/help/mcve

如果您的问题引发了您不理解的异常(即使在谷歌搜索后也是如此),请确保包含在运行可重现脚本时获得的完整回溯。

对于错误报告或功能请求,请使用 GitHub 上的问题跟踪器

警告

请不要直接向任何作者发送电子邮件以寻求帮助、报告错误或任何其他与 scikit-learn 相关的问题。

我应该如何保存、导出或部署用于生产的估计器?#

请参阅 模型持久性

我如何创建一个 Bunch 对象?#

Bunch 对象有时用作函数和方法的输出。它们通过允许通过键访问值来扩展字典,bunch["value_key"],或通过属性访问值,bunch.value_key

它们不应该用作输入。因此,您几乎不需要创建 Bunch 对象,除非您正在扩展 scikit-learn 的 API。

我如何将自己的数据集加载到 scikit-learn 可用的格式中?#

通常,scikit-learn 处理存储为 numpy 数组或 scipy 稀疏矩阵的任何数值数据。其他可转换为数值数组的类型,例如 pandas.DataFrame 也是可以接受的。

有关将您的数据文件加载到这些可用数据结构中的更多信息,请参阅 加载外部数据集

如何处理字符串数据(或树、图…)?#

scikit-learn 估计器假设您将向它们提供实值特征向量。这个假设在库中几乎是硬编码的。但是,您可以通过几种方式将非数值输入馈送到估计器。

如果您有文本文档,您可以使用词频特征;请参阅 文本特征提取 以了解内置的文本向量化器。有关从任何类型的数据中进行更一般的特征提取,请参阅 从字典加载特征特征哈希

另一种常见情况是,当您有非数值数据以及这些数据的自定义距离(或相似度)度量时。例如,具有编辑距离(也称为莱文斯坦距离)的字符串,例如 DNA 或 RNA 序列。这些可以编码为数字,但这样做很痛苦且容易出错。可以使用两种方法处理任意数据的距离度量。

首先,许多估计器接受预先计算的距离/相似度矩阵,因此,如果数据集不太大,您可以计算所有输入对的距离。如果数据集很大,您可以使用只有一个“特征”的特征向量,该特征是单独数据结构中的索引,并提供一个自定义度量函数,该函数在该数据结构中查找实际数据。例如,要使用 dbscan 以及莱文斯坦距离

>>> import numpy as np
>>> from leven import levenshtein  
>>> from sklearn.cluster import dbscan
>>> data = ["ACCTCCTAGAAG", "ACCTACTAGAAGTT", "GAATATTAGGCCGA"]
>>> def lev_metric(x, y):
...     i, j = int(x[0]), int(y[0])  # extract indices
...     return levenshtein(data[i], data[j])
...
>>> X = np.arange(len(data)).reshape(-1, 1)
>>> X
array([[0],
       [1],
       [2]])
>>> # We need to specify algorithm='brute' as the default assumes
>>> # a continuous feature space.
>>> dbscan(X, metric=lev_metric, eps=5, min_samples=2, algorithm='brute')  
(array([0, 1]), array([ 0,  0, -1]))

请注意,上面的示例使用了第三方编辑距离包 leven。类似的技巧可以谨慎地用于树核、图核等。

为什么我有时会在 OSX 或 Linux 下使用 n_jobs > 1 时遇到崩溃/冻结?#

一些 scikit-learn 工具,例如 GridSearchCVcross_val_score,在内部依赖 Python 的 multiprocessing 模块,通过传递 n_jobs > 1 作为参数,将执行并行化到多个 Python 进程上。

问题在于 Python 的 multiprocessing 执行 fork 系统调用,但出于性能原因,没有在后面跟着 exec 系统调用。许多库,例如 OSX 下的 (某些版本的) Accelerate 或 vecLib、(某些版本的) MKL、GCC 的 OpenMP 运行时、Nvidia 的 Cuda(以及可能还有许多其他库),都管理着它们自己的内部线程池。在调用 fork 时,子进程中的线程池状态会被破坏:线程池认为它拥有许多线程,而实际上只有主线程状态被复制了。可以修改这些库,让它们在发生 fork 时检测到并重新初始化线程池:我们已经为 OpenBLAS 做了这件事(从 0.2.10 版本开始合并到主分支),并且为 GCC 的 OpenMP 运行时贡献了一个 补丁(尚未被审核)。

但最终的罪魁祸首是 Python 的 multiprocessing,它执行 fork 但没有执行 exec,以减少启动和使用新 Python 进程进行并行计算的开销。不幸的是,这违反了 POSIX 标准,因此一些软件编辑器,例如 Apple,拒绝将 Accelerate 和 vecLib 中缺乏 fork 安全性视为 bug。

在 Python 3.4+ 中,现在可以配置 multiprocessing 使用 "forkserver""spawn" 启动方法(而不是默认的 "fork")来管理进程池。为了解决在使用 scikit-learn 时遇到的这个问题,可以将 JOBLIB_START_METHOD 环境变量设置为 "forkserver"。但是,用户应该注意,使用 "forkserver" 方法会阻止 joblib.Parallel 在 shell 会话中调用交互式定义的函数。

如果您有使用 multiprocessing 的自定义代码,而不是通过 joblib 使用它,则可以为您的程序全局启用 "forkserver" 模式。在您的主脚本中插入以下指令

import multiprocessing

# other imports, custom code, load data, define model...

if __name__ == "__main__":
    multiprocessing.set_start_method("forkserver")

    # call scikit-learn utils with n_jobs > 1 here

您可以在 multiprocessing 文档 中找到有关新启动方法的更多默认信息。

为什么我的作业使用的核心数量超过了使用 n_jobs 指定的数量?#

这是因为 n_jobs 仅控制使用 joblib 并行化的例程的作业数量,但并行代码可能来自其他来源

  • 一些例程可能使用 OpenMP 并行化(对于用 C 或 Cython 编写的代码),

  • scikit-learn 很大程度上依赖于 numpy,而 numpy 又可能依赖于 MKL、OpenBLAS 或 BLIS 等数值库,这些库可以提供并行实现。

有关更多详细信息,请参阅我们的 并行性说明

如何为整个执行设置 random_state#

请参阅 控制随机性