常见问题解答#
这里我们尝试回答一些在邮件列表中经常出现的问题。
关于项目#
项目的名称是什么(很多人弄错了)?#
scikit-learn,但不是scikit或SciKit也不是sci-kit learn。 也不是scikits.learn或scikits-learn,这些名称之前曾被使用过。
项目名称怎么发音?#
sy-kit learn。sci代表科学!
为什么叫scikit?#
有多个scikits,它们是围绕SciPy构建的科学工具箱。 除了scikit-learn,另一个流行的是 scikit-image 。
你们支持PyPy吗?#
- 由于维护者资源有限和用户数量较少,使用
PyPy (一个内置即时编译器的替代Python实现)
运行scikit-learn没有官方支持。
我如何获得使用scikit-learn中的图像的权限?#
scikit-learn仓库 以及在 scikit-learn 文档 中生成的图像可以通过 BSD 3-Clause 许可证 用于您的工作。强烈鼓励并感谢引用 scikit-learn。参见 引用 scikit-learn 。
实现决策#
为什么没有支持深度学习或强化学习?未来会有这样的支持吗?#
深度学习和强化学习都需要丰富的词汇来定义架构,深度学习还需要 GPU 进行高效计算。然而,这两者都不符合 scikit-learn 的设计约束。因此,深度学习和强化学习目前不在 scikit-learn 的范围内。
您可以在 `你们会添加 GPU 支持吗? `_ 中找到更多关于添加 GPU 支持的信息。
请注意,scikit-learn 目前实现了简单的多层感知器在 :mod:`sklearn.neural_network`中。我们只会接受此模块的错误修复。如果您想实现更复杂的深度学习模型,请转向流行的深度学习框架,如 `tensorflow <https://www.tensorflow.org/>`_ 、keras 和 pytorch 。
你们会添加图形模型或序列预测到 scikit-learn 吗?#
在可预见的未来不会。scikit-learn 试图为机器学习中的基本任务提供统一的 API,并通过管道和元算法(如网格搜索)将所有内容联系在一起。所需的概念、API、算法和 结构化学习所需的专长与scikit-learn所提供的不同。如果我们开始进行任意的结构化学习,我们需要重新设计整个包,项目很可能会因为自身的重量而崩溃。
有两个项目的API与scikit-learn类似,它们进行结构化预测:
*pystruct 处理一般的结构化学习(专注于任意图结构上的SSVMs和近似推理;将样本定义为图结构的一个实例)。
*seqlearn 仅处理序列(专注于精确推理;有HMMs,但主要是为了完整性;将特征向量视为样本,并使用偏移编码来表示特征向量之间的依赖关系)。
为什么你从scikit-learn中移除了HMMs?#
参见 :ref:` adding_graphical_models ` 。
你们会添加GPU支持吗?#
默认添加GPU支持会引入沉重的硬件特定软件依赖,并且现有的算法需要重新实现。这将使得普通用户更难安装scikit-learn,并且开发者更难维护代码。
然而,自2023年以来,有限但不断增长的 scikit-learn估计器列表 已经可以在输入数据作为PyTorch或CuPy数组提供,并且scikit-learn已配置为接受此类输入的情况下在GPU上运行,如 数组 API 支持(实验性) 中所述。这种Array API支持允许scikit-learn在GPU上运行,而不会为主包引入沉重的硬件特定软件依赖。
大多数依赖NumPy进行计算密集型操作的估计器都可以考虑进行Array API支持,因此也可以进行GPU支持。
然而,并非所有scikit-learn估计器都适合高效地在GPU上运行。 由于基本算法的原因,通过数组API在GPU上进行处理。例如,目前使用Cython在scikit-learn中实现的基于树的模型本质上不是基于数组的算法。其他算法,如k均值或k近邻,依赖于基于数组的算法,但也使用Cython实现。Cython用于手动交错连续的数组操作,以避免引入对大型中间数组的性能致命的内存访问:这种低级算法重写称为“内核融合”,并且无法在可预见的将来通过数组API表达。
为无法通过数组API有效实现的估计器添加高效的GPU支持,需要为scikit-learn设计和采用一个更灵活的扩展系统。这一可能性正在以下GitHub问题中考虑(正在讨论):
为什么分类变量在scikit-learn中需要预处理,而与其他工具相比?#
scikit-learn的大多数假设数据是NumPy数组或SciPy稀疏矩阵,具有单一的数值类型。目前这些数据结构并不明确表示分类变量。因此,与R的 data.frames
或 pandas.DataFrame
不同,我们要求将分类特征显式转换为数值,如 Encoding categorical features 中所讨论的。另请参见 带有混合类型的列转换器 ,了解处理异构(例如分类和数值)数据的示例。
为什么scikit-learn不能直接处理,例如 pandas.DataFrame
?#
目前预期的同质NumPy和SciPy数据对象对于大多数操作来说是最有效的处理方式。还需要大量的工作来 以支持Pandas的分类类型。因此,限制输入为同质类型可以降低维护成本并鼓励使用高效的数据结构。
然而,需要注意的是,ColumnTransformer
通过将由名称或数据类型选择的同质数据框列子集映射到专门的scikit-learn转换器,使得处理异构的pandas数据框变得方便。因此,在处理异构数据框时,ColumnTransformer
通常作为scikit-learn管道的第一步使用(更多详情参见 流水线:链接估计器 )。
有关处理异构(例如分类和数值)数据的示例,请参阅 带有混合类型的列转换器 。
你是否计划在管道中实现目标 y
的转换?#
目前,转换仅适用于管道中的特征 X
。关于无法在管道中转换 y
的问题,一直存在讨论。请关注GitHub问题 #4143 。同时,你可以查看 TransformedTargetRegressor
、 pipegraph 和 imbalanced-learn 。需要注意的是,scikit-learn 解决了在训练前对 y
应用可逆变换并在预测后进行逆变换的情况。scikit-learn 旨在解决在训练时对 y
进行变换而不是在测试时进行变换的用例,例如重采样和类似用途,如在 imbalanced-learn 中。通常,这些用例可以通过自定义元估计器而不是 Pipeline
来解决。
为什么有这么多不同类型的线性模型估计器?#
通常,每种模型类型都有一个分类器和一个回归器,例如
GradientBoostingClassifier
和
GradientBoostingRegressor
。两者具有相似的选项,
并且都具有参数 loss
,这在回归情况下特别有用,因为它可以估计条件均值以及条件分位数。
对于线性模型,有许多估计器类彼此非常接近。让我们来看一下:
LinearRegression
,无惩罚Ridge
,L2 惩罚Lasso
,L1 惩罚(稀疏模型)ElasticNet
,L1 + L2 惩罚(较稀疏模型)SGDRegressor
使用loss="squared_loss"
维护者视角:
它们原则上都做同样的事情,只是施加的惩罚不同。然而,这大大影响了底层优化问题的解决方式。最终,这归结为使用线性代数中的不同方法和技巧。一个特殊情况是 SGDRegressor
,它包含了前面四个模型,并且通过优化过程有所不同。进一步的副作用是,不同的估计器偏好不同的数据布局( X
是 C 连续或 F 连续,稀疏 csr 或 csc)。这种看似简单的线性模型的复杂性是为什么针对不同惩罚有不同估计器类的原因。
用户视角:
首先,当前设计受到科学文献的启发,其中具有不同正则化/惩罚的线性回归模型被赋予了不同的名称,例如 岭回归。拥有不同名称的模型类使用户更容易找到这些回归模型。其次,如果上述五个线性模型统一为一个类,将会有许多具有大量选项的参数,如 solver
参数。除此之外,还会有许多互斥的交互。
不同参数之间。例如,参数 solver
、 precompute
和 selection
的可能选项将取决于所选的惩罚参数 alpha
和 l1_ratio
的值。
贡献#
我如何为 scikit-learn 做出贡献?#
请参阅 贡献指南 。在想要添加一个通常是重大且耗时的新的算法之前,建议先从 已知问题 开始。请不要直接联系 scikit-learn 的贡献者关于贡献的事宜。
为什么我的拉取请求没有得到任何关注?#
scikit-learn 的审查过程需要大量时间,贡献者不应因拉取请求缺乏活动或审查而感到沮丧。我们非常重视第一次就把事情做对,因为维护和后续更改的成本很高。我们很少发布任何“实验性”代码,因此我们所有的贡献都将立即受到高度使用,并且最初应具有最高质量。
除此之外,scikit-learn 在审查带宽方面有限制;许多审查者和核心开发者是在自己的时间内进行 scikit-learn 的工作。如果你的拉取请求审查缓慢,可能是因为审查者很忙。我们请求你的理解,并请你不要仅仅因为这个原因关闭你的拉取请求或中断你的工作。
新算法的纳入标准是什么?#
我们只考虑纳入已经确立的算法。一个经验法则是至少自发表以来 3 年,200+ 引用,并且广泛使用和有用。提供明显改进的技术(例如,增强的数据结构或更有效的近似技术) 一个广泛使用的方法也将被考虑纳入。
从符合上述标准的算法或技术中,只有那些适合当前scikit-learn API的,即具有 fit
、 predict/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 使用帮助的最佳方式是什么?#
一般机器学习问题:使用带有
[machine-learning]
标签的 Cross Validated 。scikit-learn 使用问题:使用带有
[scikit-learn]
和[python]
标签的 Stack Overflow 。你也可以使用 邮件列表 。
请确保包含一个最小化的重现代码片段(理想情况下少于 10 行),该片段在你的玩具数据集上突出显示你的问题(例如来自 sklearn.datasets
或使用 numpy.random
函数随机生成的数据集,并设置固定的随机种子)。请删除任何不必要的代码行以重现你的问题。
问题应该可以通过简单地将你的代码片段复制粘贴到安装了 scikit-learn 的 Python shell 中来重现。不要忘记包含导入语句。更多关于编写良好重现代码片段的指导可以在以下链接找到: https://stackoverflow.com/help/mcve。
如果你的问题引发了你不理解的异常(即使在谷歌搜索后),请确保包含运行重现脚本时获得的完整回溯信息。
- 对于错误报告或功能请求,请使用
Warning
请不要直接给任何作者发邮件寻求帮助、报告错误或与 scikit-learn 相关的任何其他问题。
如何保存、导出或部署用于生产的估计器?#
请参阅 模型持久化 。
如何创建一个 bunch 对象?#
Bunch 对象有时作为函数和方法的输出使用。它们通过允许按键访问值( bunch["value_key"]
)或按属性访问值( bunch.value_key
)来扩展字典。
它们不应作为输入使用。因此,除非您在扩展 scikit-learn 的 API,否则几乎不需要创建 Bunch
对象。
如何将我自己的数据集加载为 scikit-learn 可用的格式?#
通常,scikit-learn 适用于存储为 numpy 数组或 scipy 稀疏矩阵的任何数值数据。其他可转换为数值数组的类型,如 pandas.DataFrame
,也是可以接受的。
有关将数据文件加载到这些可用数据结构的更多信息,请参阅 加载外部数据集 。
如何处理字符串数据(或树、图…)?#
scikit-learn 估计器假设您将向它们提供实值特征向量。这一假设在库的几乎所有部分都是硬编码的。然而,您可以通过几种方式将非数值输入提供给估计器。
如果您有文本文档,可以使用词频特征;请参阅 文本特征提取 了解内置的 文本向量化器。对于任何类型数据的通用特征提取,请参阅 字典特征提取 和 特征哈希 。
另一个常见情况是您有非数值数据和自定义距离度量。 (或相似性)度量在这些数据上。例如,包含编辑距离(也称为Levenshtein距离)的字符串,例如DNA或RNA序列。这些可以被编码为数字,但这样做既痛苦又容易出错。在任意数据上使用距离度量可以通过两种方式进行。
首先,许多估计器接受预计算的距离/相似性矩阵,因此如果数据集不是太大,您可以计算所有输入对之间的距离。如果数据集很大,您可以使用只有一个“特征”的特征向量,该特征是一个指向单独数据结构的索引,并提供一个自定义度量函数,该函数在这个数据结构中查找实际数据。例如,要使用 dbscan
与Levenshtein距离:
>>> 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]) # 提取索引
... return levenshtein(data[i], data[j])
...
>>> X = np.arange(len(data)).reshape(-1, 1)
>>> X
array([[0],
[1],
[2]])
>>> # 我们需要指定algorithm='brute',因为默认假设是一个连续的特征空间。
>>> 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工具,如 GridSearchCV
和 cross_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
模块,它为了减少启动和使用新 Python 进程进行并行计算的开销,执行了 fork
而没有执行 exec
。不幸的是,这是对 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
使用它,你可以为你的
程序。在您的主脚本中插入以下指令:
import multiprocessing
# 其他导入,自定义代码,加载数据,定义模型...
if __name__ == "__main__":
multiprocessing.set_start_method("forkserver")
# 在此处调用 n_jobs > 1 的 scikit-learn 工具
您可以在 multiprocessing 文档 中找到有关新启动方法的更多默认设置。
为什么我的作业使用的核心数超过了 n_jobs
指定的数量?#
这是因为 n_jobs
仅控制使用 joblib
并行化的例程的作业数量,但并行代码可能来自其他来源:
某些例程可能使用 OpenMP 并行化(对于用 C 或 Cython 编写的代码),
scikit-learn 大量依赖 numpy,而 numpy 又可能依赖于 MKL、OpenBLAS 或 BLIS 等数值库,这些库可以提供并行实现。
更多详情,请参阅我们的 并行性说明 。
如何为整个执行过程设置 random_state
?#
请参阅 随机性 。