贡献指南#
本项目是一个社区共同努力的成果,欢迎所有人参与贡献。项目托管在 scikit-learn/scikit-learn。scikit-learn 的决策过程和治理结构在 Scikit-learn 治理与决策制定 中有详细说明。
在添加新算法方面,scikit-learn 有一定的 选择性 ,而最好的贡献方式是开始处理已知的问题。请参阅 新贡献者的问题 以开始贡献。
如果您在使用此包时遇到问题,请毫不犹豫地向 GitHub 问题跟踪器 提交工单。您也可以发布功能请求或拉取请求。
贡献方式#
有许多方式可以为 scikit-learn 做出贡献,最常见的是向项目贡献代码或文档。改进文档与改进库本身同样重要。如果您 在文档中发现了一个拼写错误,或者已经进行了改进,请不要犹豫,发送电子邮件到邮件列表,或者最好是提交一个 GitHub 拉取请求。完整的文档可以在 doc/ 目录下找到。
但还有许多其他方式可以帮助。特别是帮助:ref:改进、分类和调查问题 <bug_triaging>
以及:ref:审查其他开发者的拉取请求 <code_review>
是非常有价值的贡献,可以减轻项目维护者的负担。
另一种贡献方式是报告你面临的问题,并在其他人报告的相关问题上给予“点赞”。如果你能传播这个项目,那也很有帮助:从你的博客和文章中引用该项目,从你的网站链接到它,或者简单地给它加星表示“我在使用它”:
如果一个贡献/问题涉及对 API 原则的更改,或者对依赖项或支持版本的更改,必须有:ref:slep
的支持,其中 SLEP 必须作为拉取请求提交到 enhancement proposals 使用 SLEP template 并遵循:ref:governance
中概述的决策过程。
自动化贡献政策#
请避免提交由完全自动化工具生成的议题或拉取请求。维护者保留在他们的唯一判断下关闭此类提交并阻止任何负责账户的权利。
理想情况下,贡献应源自以议题形式进行的人与人之间的讨论。
提交错误报告或功能请求#
我们使用GitHub议题来跟踪所有错误和功能请求;如果您发现了错误或希望看到某个功能实现,请随时打开一个议题。
- 如果您在使用此包时遇到问题,请毫不犹豫地向
错误跟踪器 提交工单。我们也欢迎您发布功能请求或拉取请求。
在提交议题之前,建议检查您的议题是否符合以下规则:
如何提交一个好的错误报告#
当您向 GitHub 提交问题时,请尽力遵循以下指南!这将使我们更容易为您提供良好的反馈:
理想的错误报告包含一个 简短的可复现代码片段 ,这样任何人都可以轻松尝试重现该错误。如果您的代码片段超过大约 50 行,请链接到 Gist 或 GitHub 仓库。
如果无法包含可复现的代码片段,请具体说明涉及的 估计器和/或函数以及数据的形状。
如果引发了异常,请 提供完整的回溯信息。
请包括您的 操作系统类型和版本号,以及您的 Python、scikit-learn、numpy 和 scipy 版本。这些信息可以通过运行以下命令找到:
python -c "import sklearn; sklearn.show_versions()"
请确保所有 代码片段和错误消息都以适当的代码块格式化。有关更多详细信息,请参阅 创建和突出显示代码块 。
如果您想帮助管理问题,请阅读关于 Bug 分类和问题管理 。
贡献代码#
Note
为了避免重复工作,强烈建议您搜索 问题跟踪器 和 PR 列表 。如果有疑问或想要处理非平凡的功能,建议首先在 问题跟踪器 中打开一个问题,以获得核心开发人员的反馈。
找到要处理的问题的一个简单方法是应用 “help wanted”
在你的搜索中使用标签。这将列出所有迄今为止未被认领的问题。为了为自己认领问题,请在问题下精确评论 /take
,以便CI自动将问题分配给你。
为了维护代码库的质量并简化审查过程,任何贡献必须符合项目的 编码指南 ,特别是:
不要修改无关的行,以保持PR专注于其描述或问题中声明的范围。
只写有价值的内联注释,避免陈述显而易见的内容:解释“为什么”而不是“是什么”。
最重要的是:不要贡献你不理解的代码。
视频资源#
这些视频是关于如何为scikit-learn贡献代码的逐步介绍,是以下文本指南的绝佳补充。请确保仍然查看我们下面的指南,因为它们描述了我们最新的最新工作流程。
Note
在2021年1月,默认分支名称从 master
更改为 main
为了使scikit-learn GitHub仓库使用更多包容性术语,这些视频是在分支重命名之前创建的。对于正在观看这些视频以设置其工作环境并提交PR的贡献者,应将 master
替换为 main
。
如何贡献#
向scikit-learn贡献的首选方式是在GitHub上fork 主仓库 ,然后提交一个“拉取请求”(PR)。
在前几步中,我们将解释如何在本地安装scikit-learn,以及如何设置你的git仓库:
如果你还没有GitHub账户,请在 GitHub上创建一个账户 。
分叉 项目仓库:点击页面顶部的“Fork”按钮。这会在你的GitHub用户账户下创建代码的副本。有关如何分叉仓库的更多详细信息,请参阅 此指南 。
将你在GitHub账户下的scikit-learn仓库分叉克隆到你的本地磁盘:
git clone git@github.com:YourLogin/scikit-learn.git # 如果你的连接较慢,可以添加 --depth 1 cd scikit-learn
按照 从源代码构建 中的步骤2-6,以开发模式构建scikit-learn,然后返回此文档。
安装开发依赖项:
pip install pytest pytest-cov ruff mypy numpydoc black==24.3.0
添加
upstream
远程。这将保存对主scikit-learn仓库的引用,你可以使用它来保持你的仓库与最新更改同步:git remote add upstream git@github.com:scikit-learn/scikit-learn.git
通过运行
git remote -v
检查upstream
和origin
远程别名是否配置正确,应该显示:upstream git@github.com:scikit-learn/scikit-learn.git (fetch) upstream git@github.com:scikit-learn/scikit-learn.git (push) origin git@github.com:YourLogin/scikit-learn.git (fetch) origin git@github.com:YourLogin/scikit-learn.git (push)
origin git@github.com:YourLogin/scikit-learn.git (fetch)
origin git@github.com:YourLogin/scikit-learn.git (push)
upstream git@github.com:scikit-learn/scikit-learn.git (fetch)
upstream git@github.com:scikit-learn/scikit-learn.git (push)
你现在应该已经安装好了 scikit-learn,并且你的 git 仓库也配置正确了。运行一些测试来验证你的安装可能会有所帮助。请参考 有用的 pytest 别名和标志 获取示例。
接下来的步骤描述了修改代码并提交 PR 的过程:
将你的
main
分支与upstream/main
分支同步,更多详情请参见 GitHub 文档 :git checkout main git fetch upstream git merge upstream/main
创建一个特性分支来保存你的开发更改:
git checkout -b my_feature
并开始进行更改。始终使用特性分支是一个好习惯!永远不要在
main
分支上工作!(可选) 安装 pre-commit 以在每次提交前运行代码风格检查:
pip install pre-commit pre-commit install
pre-commit 检查可以通过
git commit -n
禁用某个特定的提交。在你的特性分支上开发特性,使用 Git 进行版本控制。编辑完成后,使用
git add
添加更改的文件,然后使用git commit
:git add modified_files git commit
以在 Git 中记录你的更改,然后使用以下命令将更改推送到你的 GitHub 账户:
git push -u origin my_feature
按照 这些指南 创建一个从 fork 提交的 PR。
创建从你的分支发起拉取请求的说明。这将向提交者发送一封电子邮件。你可能希望考虑向邮件列表发送电子邮件以获得更多可见性。
通常有助于保持你的本地特性分支与主scikit-learn仓库的最新更改同步:
git fetch upstream
git merge upstream/main
随后,你可能需要解决冲突。你可以参考 使用命令行解决合并冲突的Git文档 。
拉取请求清单#
在PR可以被合并之前,它需要被两位核心开发者批准。一个不完整的贡献——你期望在获得全面审查之前做更多工作——应该被标记为 草稿拉取请求 ,并在成熟时改为“准备审查”。草稿PR可能有助于:表明你正在处理某事以避免重复工作,请求功能或API的广泛审查,或寻求合作者。草稿PR通常受益于在PR描述中包含一个 任务列表 。
为了简化审查过程,我们建议你的贡献在标记PR为“准备审查”之前遵守以下规则。**加粗**的尤其重要:
1. 给你的拉取请求一个有帮助的标题,总结你的贡献内容 贡献的作用。这个标题在合并后通常会成为提交信息,因此它应该为后人总结你的贡献。在某些情况下,“修复 <ISSUE 标题>”就足够了。“修复 #<ISSUE 编号>”从来不是一个好的标题。
确保你的代码通过测试。整个测试套件可以通过
pytest
运行,但通常不推荐这样做,因为它需要很长时间。通常只需运行与你更改相关的测试就足够了:例如,如果你在sklearn/linear_model/_logistic.py
中做了一些更改,运行以下命令通常就足够了:pytest sklearn/linear_model/_logistic.py
以确保文档测试示例正确pytest sklearn/linear_model/tests/test_logistic.py
以运行特定于该文件的测试pytest sklearn/linear_model
以测试整个linear_model
模块pytest doc/modules/linear_model.rst
以确保用户指南示例正确。pytest sklearn/tests/test_common.py -k LogisticRegression
以运行我们所有的估计器检查(特别是针对LogisticRegression
,如果你更改了该估计器)。
可能还有其他失败的测试,但它们会被 CI 捕获,因此你不需要在本地运行整个测试套件。有关如何高效使用
pytest
的指南,请参阅 有用的 pytest 别名和标志 。确保你的代码得到适当的注释和文档化,并 确保文档正确渲染。要构建文档,请参考我们的 文档 指南。CI 也会构建文档:请参考 GitHub Actions生成的文档 。
增强功能需要测试才能被接受。错误修复或新功能应提供 非回归测试 。这些测试验证修复或功能的正确行为。通过这种方式,进一步 对代码库的修改应确保与预期行为一致。在修复错误的情况下,在提交PR时,非回归测试应针对
main
分支上的代码库失败,而对PR代码通过。遵循 编码指南 。
在适用的情况下,使用
sklearn.utils
模块中的验证工具和脚本。开发者可用的实用程序列表可以在 开发者工具 页面找到。通常,拉取请求会解决一个或多个其他问题(或拉取请求)。如果合并您的拉取请求意味着某些其他问题/PRs应该被关闭,您应该 使用关键字创建指向它们的链接 (例如,
Fixes #1234
;允许多个问题/PRs,只要每个前面都有关键字)。合并后,这些问题/PRs将由GitHub自动关闭。如果您的拉取请求仅与某些其他问题/PRs相关,或者它仅部分解决了目标问题,请创建指向它们的链接而不使用关键字(例如,Towards #1234
)。PRs 应通过性能和效率的基准测试(参见 性能监控 )或通过使用示例来证明更改的合理性。示例还向用户展示了库的功能和复杂性。请参考 examples/ 目录中的其他示例。示例应展示新功能在实践中的有用性,并在可能的情况下与scikit-learn中可用的其他方法进行比较。
9. 新功能有一定的维护开销。我们期望PR作者至少在最初阶段参与他们提交的代码的维护。新功能需要在使用指南中通过小代码片段进行叙述性文档说明。 如果相关,请在文献中添加参考文献,并在可能的情况下附上PDF链接。
用户指南还应包括算法的预期时间和空间复杂度以及可扩展性,例如:“该算法可以扩展到大量样本(> 100000),但在维度上不具有可扩展性:
n_features
预期低于 100”。
您还可以查看我们的 代码审查指南 ,以了解评审人员会期望什么。
您可以使用以下工具检查常见的编程错误:
具有良好单元测试覆盖率的代码(至少 80%,最好是 100%),使用以下命令检查:
pip install pytest pytest-cov pytest --cov sklearn path/to/tests
另请参阅 测试和提高测试覆盖率 。
使用
mypy
进行静态分析:mypy sklearn
这不应在您的拉取请求中产生新的错误。使用
# type: ignore
注释可以作为一些 mypy 不支持的情况的变通方法,特别是:当导入 C 或 Cython 模块时,
在带有装饰器的属性上。
对于包含性能分析的贡献,附带基准脚本和分析输出(参见 性能监控 ),将获得额外加分。另请参阅 如何优化速度 指南,以获取有关分析和 Cython 优化的更多详细信息。
Note
scikit-learn 代码库的当前状态并不符合所有这些指南,但我们期望在所有新贡献上强制执行这些约束将使整体代码库质量朝着正确的方向发展。
See also
有关开发工作流程的两个非常详细且文档完善的指南,请访问 Scipy 开发工作流程 和 Astropy 开发者工作流程 部分。
持续集成(CI)#
Azure pipelines 用于在 Linux、Mac 和 Windows 上测试 scikit-learn,使用不同的依赖项和设置。
CircleCI 用于构建文档以供查看。
Github Actions 用于各种任务,包括构建轮子和源代码分发。
Cirrus CI 用于在 ARM 上构建。
请注意,如果以下标记之一出现在最新的提交消息中,将采取以下操作。
提交消息标记 |
CI 采取的操作 |
[ci skip] |
完全跳过 CI |
[cd build] |
运行 CD(构建轮子和源代码分发) |
[cd build gh] |
仅对 GitHub Actions 运行 CD |
[cd build cirrus] |
仅对 Cirrus CI 运行 CD |
[lint skip] |
Azure pipeline 跳过代码检查 |
[scipy-dev] |
使用我们的依赖项(numpy、scipy 等)开发版本构建和测试 |
[free-threaded] |
使用 CPython 3.13 自由线程构建和测试 |
[pyodide] |
使用 Pyodide 构建和测试 |
[azure parallel] |
并行运行 Azure CI 作业 |
[cirrus arm] |
运行 Cirrus CI ARM 测试 |
[float32] |
通过设置 |
[doc skip] |
不构建文档 |
[doc quick] |
构建文档,但不包括示例图库绘图 |
[doc build] |
构建文档,包括示例图库绘图(非常耗时) |
请注意,默认情况下,文档会被构建,但只有被拉取请求直接修改的示例会被执行。
停滞的拉取请求#
由于贡献一个功能可能是一个漫长的过程,一些拉取请求看似不活跃但尚未完成。在这种情况下,接管它们是对项目的一项重要服务。接管的良好礼仪是:
确定 PR 是否停滞
如果一个拉取请求被标记为“停滞”或“需要帮助”,这意味着我们已经将其确定为其他贡献者的候选对象。
要判断一个不活跃的PR是否停滞,可以询问贡献者她/他是否计划在不久的将来继续处理该PR。如果在2周内没有回应或没有推动PR进展的活动,则表明该PR停滞,并将导致该PR被标记为“需要帮助”。
注意,如果一个PR之前收到了关于贡献的评论,但一个月内没有回复,那么可以安全地假设该PR停滞,并将等待时间缩短至一天。
在冲刺结束后,将向冲刺参与者传达在冲刺期间打开的未合并PR的跟进情况,并将这些PR标记为“冲刺”。标记为“冲刺”的PR可以由冲刺领导者重新分配或宣布停滞。
接管停滞的PR:要接管一个PR,重要的是在停滞的PR上评论你正在接管,并从新的PR链接到旧的PR。新的PR应该通过从旧的PR拉取来创建。
停滞和未认领的问题#
一般来说,可以认领的问题会有一个 “需要帮助” 标签。然而,并非所有需要贡献者的问题都会有这个标签,因为“需要帮助”标签并不总是与问题的状态保持最新。贡献者可以使用以下指南找到仍然可以认领的问题:
新贡献者的问题#
新贡献者在寻找问题时应注意以下标签。我们强烈建议新贡献者先解决 “简单” 问题:这有助于贡献者熟悉贡献流程,并让核心开发者熟悉贡献者;此外,我们经常低估了解决一个问题的容易程度!
Good first issue 标签
开始为 scikit-learn 做贡献的一个好方法是选择问题跟踪器中 good first issues 列表中的一个项目。解决这些问题可以让你在没有太多先验知识的情况下开始为项目做贡献。如果你已经为 scikit-learn 做过贡献,你应该查看 Easy 问题。
Easy 标签
如果你已经为 scikit-learn 做过贡献,另一个为 scikit-learn 做贡献的好方法是选择问题跟踪器中 Easy issues 列表中的一个项目。你在这一领域的帮助将受到更有经验的开发者的极大赞赏,因为它有助于腾出他们的时间专注于其他问题。
Help wanted 标签
我们经常使用 help wanted 标签来标记问题,无论难度如何。
此外,我们使用“help wanted”标签来标记那些被原始贡献者放弃的Pull Requests,这些PR可供其他人接手继续原始贡献者的工作。带有“help wanted”标签的问题列表可以在此处找到 scikit-learn/scikit-learn 。请注意,并非所有需要贡献者的问题都会有此标签。
文档#
我们乐于接受任何形式的文档:
函数/方法/类文档字符串: 也称为“API文档”,这些文档描述了对象的功能以及任何参数、属性和方法的详细信息。文档字符串位于 sklearn/ 中,并根据 doc/api_reference.py 生成。要添加、更新、删除或弃用在 API Reference 中列出的公共API,请参考此处。
用户指南: 这些文档提供了关于scikit-learn中实现的算法的更详细信息,通常位于根目录 doc/ 和 doc/modules/ 中。
示例: 这些提供了完整的代码示例,可能演示了scikit-learn模块的使用,比较了不同的算法或讨论了它们的解释等。示例位于 examples/ 中。
其他reStructuredText文档: 这些提供了各种其他有用的信息(例如,贡献指南 指南),并位于 doc/ 中。
#编写文档字符串的指南
在记录参数和属性时,以下是一些格式良好的示例列表
n_clusters : int, default=3 算法检测到的聚类数量。 some_param : {"hello", "goodbye"}, bool 或 int, default=True 参数描述在这里,可以是字符串字面量( `hello` 或 `goodbye` )、布尔值或整数。默认值为 True。 array_parameter : {array-like, sparse matrix} of shape (n_samples, n_features) \ or (n_samples,) 此参数接受上述任一形式的数据,具有上述任一形状。默认值为 `np.ones(shape=(n_samples,))` 。 list_param : list of int typed_ndarray : ndarray of shape (n_samples,), dtype=np.int32 sample_weight : array-like of shape (n_samples,), default=None multioutput_array : ndarray of shape (n_samples, n_classes) 或 list of such arrays
通常要记住以下几点:
使用 Python 基本类型。(
bool
而不是boolean
)使用括号定义形状:
array-like of shape (n_samples,)
或array-like of shape (n_samples, n_features)
对于具有多个选项的字符串,使用方括号:
input: {'log', 'squared', 'multinomial'}
1D 或 2D 数据可以是
{array-like, ndarray, sparse matrix, dataframe}
的子集。注意array-like
也可以是list
,而ndarray
明确仅指numpy.ndarray
。当使用“帧状”特性(如列名)时,指定
dataframe
。当指定列表的数据类型时,使用
of
作为分隔符:list of int
。当参数支持提供形状和/或数据类型详细信息的数组以及此类数组的列表时,可以使用array-like of shape (n_samples,) or list of such arrays
之一。
在指定 ndarray 的数据类型时,例如在定义形状后使用
dtype=np.int32
:ndarray of shape (n_samples,), dtype=np.int32
。你可以指定多个数据类型作为一个集合:array-like of shape (n_samples,), dtype={np.float64, np.float32}
。如果想要提及任意精度,使用integral
和floating
而不是 Python 数据类型int
和float
。当同时支持int
和floating
时,不需要指定数据类型。当默认值为
None
时,只需在末尾指定default=None
。务必在文档字符串中包含参数或属性为None
时的含义。在文档字符串中添加 “See Also” 以引用相关类/函数。
文档字符串中的 “See Also” 应每行一个引用,并带有冒号和解释,例如:
See Also -------- SelectKBest : 根据 k 个最高分数选择特征。 SelectFpr : 根据假阳性率测试选择特征。
在 “Example” 部分添加一个或两个代码片段,展示如何使用。
#编写用户指南和其他 reStructuredText 文档的指南
在数学和算法细节之间保持良好的平衡,并向读者提供算法的直观理解是很重要的。
从对算法/代码在数据上作用的简明扼要的解释开始。
强调该功能的实用性及其推荐的应用。如果可用,考虑包括算法的复杂度(\(O\left(g\left(n\right)\right)\) ),因为“经验法则”可能非常依赖于机器。只有在这些复杂度不可用时,才可能提供经验法则。
包含一个相关的图(从示例生成)以提供直观理解。
包含一两个简短的代码示例来展示该功能的使用方法。
介绍任何必要的数学公式,随后附上参考文献。通过推迟数学方面的内容,文档变得更加易于用户理解该功能的实际意义,而非其底层机制。
在编辑 reStructuredText(
.rst
)文件时,尽可能保持每行长度不超过 88 个字符(链接和表格等例外情况除外)。在 scikit-learn 的 reStructuredText 文件中,无论是单引号还是双引号包围的文本都会渲染为内联字面量(常用于代码,例如
list
)。这是由于我们设置了特定的配置。如今应使用单引号。过多的信息使得用户难以获取他们感兴趣的内容。使用下拉菜单来分解信息,使用以下语法:
.. dropdown:: 下拉菜单标题 下拉菜单内容。
上述代码片段将生成以下下拉菜单:
#下拉菜单标题
下拉菜单内容。
可以使用下拉菜单默认隐藏的信息包括:
低层次的章节,如
参考文献
、属性
等(例如 检测错误权衡(DET) 中的子章节);深入的数学细节;
特定用例的叙述;
一般来说,可能只对那些希望深入了解某个工具的用户感兴趣的叙述。
不要对低层次章节
示例
使用下拉菜单,因为它应该对所有用户可见。确保示例
章节紧随主要讨论之后,中间尽可能少地折叠章节。请注意,下拉菜单会破坏交叉引用。如果这样做有意义,请将与提及该文本的引用一起隐藏。否则,不要使用下拉菜单。
#编写参考文献的指南
当参考文献可通过 arxiv 或 数字对象标识符 识别号获取时,请使用 Sphinx 指令
:arxiv:
或:doi:
。例如,请参阅 谱聚类图 中的参考文献。对于文档字符串中的“参考文献”部分,请参阅
sklearn.metrics.silhouette_score
作为示例。要在 scikit-learn 文档中交叉引用其他页面,请使用 reStructuredText 交叉引用语法:
章节: 要链接到文档中的任意章节,请使用引用标签(参见 Sphinx 文档 )。例如:
.. _my-section: 我的章节 ---------- 这是章节的内容。 要引用自身,请使用 :ref:`my-section` 。
您不应修改现有的 Sphinx 引用标签,因为这会破坏现有的交叉引用和指向 scikit-learn 文档中特定章节的外部链接。
术语表: 链接到 术语表 中的术语:
:term:`cross_validation`
函数: 要链接到函数的文档,请使用函数的完整导入路径:
:func:`~sklearn.model_selection.cross_val_score`
然而,如果在文档中您上方有
.. currentmodule::
指令,则只需使用当前指定模块之后的函数路径。例如:.. currentmodule:: sklearn.model_selection :func:`cross_val_score`
类: 要链接到类的文档,请使用类的完整导入路径: 类,除非文档上方有
.. currentmodule::
指令(见上文)::class:`~sklearn.preprocessing.StandardScaler`
您可以使用任何文本编辑器编辑文档,然后按照 构建文档 生成 HTML 输出。生成的 HTML 文件将放置在 _build/html/
目录中,并且可以在网络浏览器中查看,例如通过打开本地的 _build/html/index.html
文件或通过运行本地服务器
python -m http.server -d _build/html
构建文档#
在提交拉取请求之前,请检查您的修改是否引入了新的 Sphinx 警告,方法是本地构建文档并尝试修复它们。
首先,确保您已 正确安装 了开发版本。除此之外,构建文档还需要安装一些额外的包:
pip install sphinx sphinx-gallery numpydoc matplotlib Pillow pandas \
polars scikit-image packaging seaborn sphinx-prompt \
sphinxext-opengraph sphinx-copybutton plotly pooch \
pydata-sphinx-theme sphinxcontrib-sass sphinx-design \
sphinx-remove-toctrees
要构建文档,您需要进入 doc
文件夹:
cd doc
在绝大多数情况下,您只需要生成完整的网站,而不需要示例图库:
make
文档将生成在 _build/html/stable
目录中,并且可以在网络浏览器中查看,例如通过打开本地的 _build/html/stable/index.html
文件。
要同时生成示例图库,您可以使用:
make html
这将运行所有示例,这需要一段时间。如果你只想生成几个示例,这在只修改几个示例时特别有用,你可以使用:
EXAMPLES_PATTERN=your_regex_goes_here make html
如果你打算在离线环境下查看文档,请设置环境变量 NO_MATHJAX=1
。要构建PDF手册,请运行:
make latexpdf
Sphinx版本
尽管我们尽力让文档在尽可能多的Sphinx版本下构建,但不同版本的行为往往略有不同。为了获得最佳结果,你应该使用我们在CircleCI上使用的相同版本。查看这个 GitHub搜索 以了解确切版本。
GitHub Actions生成的文档#
当你在拉取请求中更改文档时,GitHub Actions会自动构建它。要查看GitHub Actions生成的文档,只需转到你的PR页面底部,查找项目“Check the rendered docs here!”并点击旁边的“details”:
测试和提高测试覆盖率#
高质量的 单元测试 是scikit-learn开发过程的基石。为此,我们使用 pytest 包。测试是适当命名的函数,位于 tests
子目录中,用于检查算法的有效性和代码的不同选项。
在文件夹中运行 pytest
将运行相应模块的所有测试。
子包。有关更详细的 pytest
工作流程,请参阅
拉取请求清单 。
我们期望新功能的代码覆盖率至少达到约 90%。
#编写与 matplotlib 相关的测试
测试夹具确保一组测试将在适当的初始化和清理环境中执行。scikit-learn 测试套件实现了一个 pyplot
夹具,可以与 matplotlib
一起使用。
pyplot
夹具应在测试函数处理 matplotlib
时使用。 matplotlib
是一个软依赖项,不是必需的。此夹具负责在未安装 matplotlib
时跳过测试。此外,测试期间创建的图形将在测试函数执行完毕后自动关闭。
要在测试函数中使用此夹具,需要将其作为参数传递:
def test_requiring_mpl_fixture(pyplot):
# 现在可以安全地使用 matplotlib
性能监控#
本节大量借鉴了 pandas 文档 。
在提出对现有代码库的更改时,确保它们不会引入性能退化非常重要。scikit-learn 使用 asv benchmarks 来监控一组常用估计器和函数的性能。您可以查看
这些基准测试可以在 scikit-learn 基准测试页面 上找到。
相应的基准测试套件可以在 asv_benchmarks/
目录中找到。
要使用 asv 的所有功能,您需要 conda
或 virtualenv
。更多详情请查看 asv 安装网页 。
首先,您需要安装 asv 的开发版本:
pip install git+https://github.com/airspeed-velocity/asv
然后,将您的目录更改为 asv_benchmarks/
:
cd asv_benchmarks
基准测试套件配置为针对您本地的 scikit-learn 克隆运行。请确保它是最新的:
git fetch upstream
在基准测试套件中,基准测试按照与 scikit-learn 相同的结构组织。例如,您可以比较特定估计器在 upstream/main
和您正在工作的分支之间的性能:
asv continuous -b LogisticRegression upstream/main HEAD
该命令默认使用 conda 创建基准测试环境。如果您想使用 virtualenv,请使用 -E
标志:
asv continuous -E virtualenv -b LogisticRegression upstream/main HEAD
您还可以指定整个模块进行基准测试:
asv continuous -b linear_model upstream/main HEAD
您可以将 HEAD
替换为任何本地分支。默认情况下,它只会报告变化至少 10% 的基准测试。您可以使用 -f
标志控制此比率。
要运行完整的基准测试套件,只需移除 -b
标志:
asv continuous upstream/main HEAD
然而,这可能需要长达两个小时。 -b
标志还接受正则表达式,以运行更复杂的基准测试子集。
要运行基准测试而不与其他分支进行比较,请使用 run
命令:
asv run -b linear_model HEAD^!
你也可以使用当前Python环境中已安装的scikit-learn版本运行基准测试套件:
asv run --python=same
这在以可编辑模式安装scikit-learn时特别有用,以避免每次运行基准测试时创建新的环境。默认情况下,使用现有安装时结果不会保存。要保存结果,必须指定一个提交哈希:
asv run --python=same --set-commit-hash=<commit hash>
基准测试按机器、环境和提交进行保存和组织。要查看所有已保存的基准测试列表:
asv show
并查看特定运行的报告:
asv show <commit hash>
当你为正在处理的拉取请求运行基准测试时,请在GitHub上报告结果。
基准测试套件支持额外的可配置选项,这些选项可以在 benchmarks/config.json
配置文件中设置。例如,基准测试可以针对提供的 n_jobs
参数值列表运行。
有关如何编写基准测试以及如何使用asv的更多信息,请参阅 asv文档 。
维护向后兼容性#
弃用#
如果任何公开可访问的类、函数、方法、属性或参数被重命名,我们仍然支持旧版本两个版本,并在调用、传递或访问时发出弃用警告。
弃用一个类或函数
假设函数 zero_one
被重命名为 zero_one_loss
,我们为 zero_one
添加装饰器 utils.deprecated
并从该函数调用 zero_one_loss
from ..utils import deprecated
def zero_one_loss(y_true, y_pred, normalize=True):
# 实际实现
pass
@deprecated(
"函数 `zero_one` 在0.13版本中被重命名为 `zero_one_loss` ,并将在0.15版本中移除。默认行为从 `normalize=False` 改为 `normalize=True` "
)
def zero_one(y_true, y_pred, normalize=False):
return zero_one_loss(y_true, y_pred, normalize)
还需要将 zero_one
从 API_REFERENCE
移动到 DEPRECATED_API_REFERENCE
,并在 doc/api_reference.py
文件中将 zero_one_loss
添加到 API_REFERENCE
,以反映 API Reference 中的更改。
弃用一个属性或方法
如果一个属性或方法将要被弃用,请使用装饰器
deprecated
在属性上。请注意,如果存在 property
装饰器,
deprecated
装饰器应放置在其之前,以便正确渲染文档字符串。例如,将属性 labels_
重命名为 classes_
可以这样做:
@deprecated(
"属性 `labels_` 在 0.13 版本中被弃用,并将在 0.15 版本中移除。请改用 `classes_` "
)
@property
def labels_(self):
return self.classes_
如果一个参数需要被弃用,必须手动引发 FutureWarning
警告。在以下示例中, k
被弃用并重命名为 n_clusters:
import warnings
def example_function(n_clusters=8, k="deprecated"):
if k != "deprecated":
warnings.warn(
" `k` 在 0.13 版本中被重命名为 `n_clusters` ,并将在 0.15 版本中移除",
FutureWarning,
)
n_clusters = k
当变化在类中时,我们在 fit
方法中验证并引发警告:
import warnings
class ExampleEstimator(BaseEstimator):
def __init__(self, n_clusters=8, k='deprecated'):
self.n_clusters = n_clusters
self.k = k
def fit(self, X, y):
if self.k != "deprecated":
warnings.warn(
" `k` 在 0.13 版本中被重命名为 `n_clusters` ,并将在 0.15 版本中移除。",
FutureWarning,
)
self._n_clusters = self.k
else:
self._n_clusters = self.n_clusters
如这些示例所示,警告消息应始终提供弃用发生的版本和旧行为将被移除的版本。如果弃用发生在 0.x-dev 版本中,消息应说明弃用发生在 0.x 版本中, 移除将在0.(x+2)版本中进行,以便用户有足够的时间来适应新行为。例如,如果弃用发生在0.18-dev版本,消息应表明发生在0.18版本,并且旧行为将在0.20版本中移除。
警告消息还应包含对更改的简要说明,并指导用户使用替代方案。
此外,应在文档字符串中添加弃用注释,回顾与上述弃用警告相同的信息。使用 .. deprecated::
指令:
.. deprecated:: 0.13
``k`` 在0.13版本中重命名为 ``n_clusters`` ,并将在0.15版本中移除。
此外,弃用需要一个测试,确保在相关情况下发出警告,而在其他情况下不发出警告。警告应在所有其他测试中捕获(例如,使用 @pytest.mark.filterwarnings
),并且在示例中不应有警告。
更改参数的默认值#
如果需要更改参数的默认值,请将默认值替换为特定值(例如, "warn"
),并在用户使用默认值时引发 FutureWarning
。以下示例假设当前版本为0.20,我们将 n_clusters
的默认值从5(0.20的旧默认值)更改为10(0.22的新默认值):
import warnings
def example_function(n_clusters="warn"):
if n_clusters == "warn":
warnings.warn(
" `n_clusters` 的默认值将从5更改为10,从0.22版本开始。",
FutureWarning,
)
n_clusters = 5
当更改在类中时,我们在 fit
中验证并引发警告:
import warnings
class ExampleEstimator:
def __init__(self, n_clusters="warn"):
self.n_clusters = n_clusters
def fit(self, X, y):
if self.n_clusters == "warn":
warnings.warn(
"The default value of `n_clusters` will change from 5 to 10 in 0.22.",
FutureWarning,
)
self._n_clusters = 5
与弃用类似,警告信息应始终提供更改发生的版本以及旧行为将被移除的版本。
文档字符串中的参数描述需要相应更新,通过添加一个包含旧默认值和新默认值的 versionchanged
指令,并指向更改生效的版本:
.. versionchanged:: 0.22
The default value for `n_clusters` will change from 5 to 10 in version 0.22.
最后,我们需要一个测试来确保在相关情况下发出警告,而在其他情况下不发出警告。警告应在所有其他测试中捕获(例如,使用 @pytest.mark.filterwarnings
),并且在示例中不应有警告。
代码审查指南#
对作为 PR 贡献给项目的代码进行审查是 scikit-learn 开发的关键组成部分。我们鼓励任何人开始审查其他开发者的代码。代码审查过程对所有参与者来说往往非常有教育意义。如果你希望使用某个功能,因此可以对 PR 是否满足你的需求进行批判性回应,那么这尤其合适。虽然每个拉取请求都需要由两名核心开发者签署,但你可以通过提供反馈来加快这一过程。
Note
客观改进和主观细节之间的区别并不总是明确的。审查者应记住,代码审查主要是关于降低项目中的风险。在审查代码时,应旨在防止可能需要错误修复、弃用或撤回的情况。关于文档:拼写错误、语法问题和歧义最好立即解决。
#代码审查中需要涵盖的重要方面
以下是一些在任何代码审查中需要涵盖的重要方面,从高层次的问题到更详细的检查清单。
我们是否希望将此功能加入库中?它是否有可能被使用?作为scikit-learn的用户,您是否喜欢这个更改并打算使用它?它是否在scikit-learn的范围内?维护新功能的成本是否值得其带来的好处?
代码是否与scikit-learn的API一致?公共函数/类/参数是否命名良好且设计直观?
所有公共函数/类及其参数、返回类型和存储属性是否按照scikit-learn的约定命名并清晰地记录?
任何新功能是否在用户指南中描述并附有示例?
每个公共函数/类是否都经过测试?是否对一组合理的参数、它们的值、值类型及其组合进行了测试?测试是否验证了代码的正确性,即是否按照文档所述执行?如果更改是修复错误,是否包含回归测试?请参阅 这个 以开始在Python中进行测试。
测试是否在持续集成构建中通过?如果适当,帮助贡献者理解测试失败的原因。
测试是否覆盖了每一行代码(参见构建日志中的覆盖率报告)?如果没有,缺失覆盖的行是否是好的例外?
代码是否易于阅读且冗余较低?变量名称是否应改进以提高清晰度或一致性?是否应添加注释?是否应删除无用或多余的注释?
代码是否可以轻松重写以在相关设置中更高效地运行?
代码是否向后兼容以前的版本?(或者是否需要一个弃用周期?)
新代码是否会添加对其他库的依赖?(这不太可能被接受)
文档是否正确渲染(有关更多详细信息,请参阅 文档 部分),并且图表是否具有教学意义?
评审的标准回复 包含一些评审人员可能会提出的常见评论。
#沟通指南
审查开放的拉取请求(PR)有助于推动项目前进。这是熟悉代码库的好方法,并应激励贡献者继续参与项目。[1]
每个 PR,无论好坏,都是一种慷慨的行为。以积极的评论开始将帮助作者感到被奖励,你随后的评论可能会更清晰地被听到。你也可能感觉良好。
如果可能,从大的问题开始,这样作者就知道他们被理解了。抵制立即逐行检查或从小问题开始的诱惑。
不要让完美成为好的敌人。如果你发现自己提出了许多不属于 代码审查指南 的小建议,请考虑以下方法:
避免提交这些建议;
将它们标记为 “Nit”,以便贡献者知道不处理它们是可以的;
在后续的 PR 中跟进,出于礼貌,你可能想让原始贡献者知道。
不要匆忙,花时间使你的评论清晰并证明你的建议。
你是项目的代表。每个人都会有糟糕的日子,在这种情况下你值得休息:试着花点时间并保持离线。
阅读现有代码库#
阅读和消化现有代码库总是一项艰巨的任务 需要时间和经验才能掌握。尽管我们通常尽量编写简单的代码,但由于项目的庞大,一开始理解代码可能会显得非常困难。以下是一些可能有助于使这项任务更轻松和更快的提示(不分先后顺序)。
熟悉 scikit-learn 对象的 API :了解 fit 、predict 、transform 等是用来做什么的。
在深入阅读函数/类的代码之前,先浏览一下文档字符串,并尝试了解每个参数/属性在做什么。停下来思考一下“如果我必须自己实现这个功能,我会怎么做?”也可能有所帮助。
最棘手的事情通常是识别代码中哪些部分是相关的,哪些是不相关的。在 scikit-learn 中,大量 的输入检查是在 fit 方法的开始部分进行的。有时,只有一小部分代码在做实际的工作。例如,查看
LinearRegression
的fit
方法,你可能只是在寻找对scipy.linalg.lstsq
的调用,但它被埋藏在多行输入检查和不同类型参数的处理中。由于使用了 继承 ,一些方法可能在父类中实现。所有估计器至少继承自
BaseEstimator
,以及一个Mixin
类(例如ClassifierMixin
),该类根据估计器的性质(分类器、回归器、转换器等)启用默认行为。有时,阅读给定函数的测试会让你了解其预期用途。你可以使用
git grep
(见下文)来查找为某个函数编写的所有测试。特定函数/类的多数测试都放在模块的tests/
文件夹下。你经常会看到这样的代码:
out = Parallel(...)(delayed(some_function)(param) for param in some_iterable)
。这会使用 Joblib 并行运行some_function
。out
则是一个包含每次调用some_function
返回值的可迭代对象。我们使用 Cython 编写快速代码。Cython 代码位于
.pyx
和.pxd
文件中。Cython 代码具有更多类似 C 的风格:我们使用指针,进行手动内存分配等。在这里,拥有一些 C/C++ 的最小经验几乎是必须的。更多信息请参见 Cython 最佳实践、约定和知识 。掌握你的工具。
配置
git blame
以忽略将代码风格迁移到black
的提交。git config blame.ignoreRevsFile .git-blame-ignore-revs
更多信息请参见 black 的 避免破坏 git blame 的文档 。