拉取请求检查
当您在 🤗 Transformers 上打开拉取请求时,将运行大量检查以确保您添加的补丁不会破坏任何现有内容。 这些检查有四种类型
- 常规测试
- 文档构建
- 代码和文档样式
- 通用仓库一致性
在本文件中,我们将尝试解释这些不同检查的含义以及其背后的原因,以及如果您的 PR 中的某个检查失败,如何在本地调试它们。
请注意,理想情况下,它们需要您在 Transformers 仓库中安装开发版本
pip install transformers[dev]
或者用于可编辑的安装。
pip install -e .[dev]
由于 Transformers 的可选依赖项数量急剧增加,您可能无法获得所有依赖项。如果开发版本安装失败,请确保安装您正在使用的深度学习框架(PyTorch、TensorFlow 和/或 Flax),然后执行以下操作:
pip install transformers[quality]
或者用于可编辑的安装。
pip install -e .[quality]
测试
所有以 ci/circleci: run_tests_
开头的作业都运行 Transformers 测试套件的一部分。每个作业都专注于库在特定环境中的某个部分:例如 ci/circleci: run_tests_pipelines_tf
在仅安装 TensorFlow 的环境中运行管道测试。
请注意,为了避免在测试模块没有实际变化时运行测试,每次只运行测试套件的一部分:一个实用程序会运行以确定 PR 前后库的差异(GitHub 在“文件更改”选项卡中显示给您的内容),并选择受该差异影响的测试。该实用程序可以在本地使用以下命令运行:
python utils/tests_fetcher.py
从 Transformers 仓库的根目录运行。它将
- 检查差异中的每个文件,查看更改是在代码中还是仅在注释或文档字符串中。仅保留包含实际代码更改的文件。
- 构建一个内部映射,该映射为库源代码中的每个文件提供它递归影响的所有文件。如果模块 B 导入模块 A,则称模块 A 影响模块 B。对于递归影响,我们需要一个从模块 A 到模块 B 的模块链,其中每个模块都导入前一个模块。
- 将此映射应用于步骤 1 中收集的文件,这将为我们提供 PR 影响的模型文件列表。
- 将这些文件中的每一个映射到它们对应的测试文件,并获取要运行的测试列表。
在本地执行脚本时,您应该看到步骤 1、3 和 4 的结果打印出来,从而了解运行了哪些测试。该脚本还会创建一个名为 test_list.txt
的文件,其中包含要运行的测试列表,您可以使用以下命令在本地运行它们:
python -m pytest -n 8 --dist=loadfile -rA -s $(cat test_list.txt)
以防万一有什么东西从缝隙中溜走,完整的测试套件也会每天运行。
文档构建
build_pr_documentation
作业构建并生成文档预览,以确保在合并您的 PR 后一切看起来都正常。机器人将在您的 PR 中添加一个指向文档预览的链接。您对 PR 所做的任何更改都会在预览中自动更新。如果文档构建失败,请单击失败作业旁边的“详细信息”以查看问题出在哪里。通常,错误很简单,例如 toctree
中缺少文件。
如果您有兴趣在本地构建或预览文档,请查看 docs 文件夹中的 README.md
。
代码和文档风格
代码格式化使用 black
和 ruff
应用于所有源文件、示例和测试。我们还使用一个自定义工具来处理文档字符串和 rst
文件的格式(utils/style_doc.py
),以及在 Transformers __init__.py
文件(utils/custom_init_isort.py
)中执行的延迟导入的顺序。所有这些可以通过执行以下命令来启动:
make style
CI 检查这些已经应用于 ci/circleci: check_code_quality
检查中。它还运行 ruff
,它会对您的代码进行基本检查,并在发现未定义变量或未使用变量时进行抱怨。要在本地运行该检查,请使用以下命令:
make quality
这可能需要很长时间,因此要仅对当前分支中修改的文件运行相同的操作,请运行以下命令:
make fixup
最后一个命令还会运行所有其他检查以确保仓库一致性。让我们来看看它们。
仓库一致性
这将所有测试分组在一起,以确保您的 PR 使仓库处于良好状态,并由 ci/circleci: check_repository_consistency
检查执行。您可以在本地通过执行以下命令来运行该检查:
make repo-consistency
这会检查:
- 添加到 init 的所有对象都被记录(由
utils/check_repo.py
执行) - 所有
__init__.py
文件的两个部分中的内容相同(由utils/check_inits.py
执行) - 所有被标识为从另一个模块复制的代码与原始代码一致(由
utils/check_copies.py
执行) - 所有配置类在它们的文档字符串中至少提及一个有效的检查点(由
utils/check_config_docstrings.py
执行) - 所有配置类只包含在相应的建模文件中使用的属性(由
utils/check_config_attributes.py
执行) - README 的翻译和文档的索引与主 README 中的模型列表相同(由
utils/check_copies.py
执行) - 文档中的自动生成表格是最新的(由
utils/check_table.py
执行) - 即使没有安装所有可选依赖项,库也拥有所有可用对象(由
utils/check_dummies.py
执行) - 所有文档字符串都正确记录了对象签名中的参数(由
utils/check_docstrings.py
执行)
如果此检查失败,前两项需要手动修复,最后四项可以通过运行以下命令自动修复:
make fix-copies
其他检查涉及添加新模型的 PR,主要包括:
- 所有添加的模型都在自动映射中(由
utils/check_repo.py
执行) - 所有模型都经过适当的测试(由
utils/check_repo.py
执行)
检查复制
由于 Transformers 库在模型代码方面非常有主见,并且每个模型都应该在单个文件中完全实现,而不依赖其他模型,我们添加了一个机制来检查给定模型的层的代码副本是否与其原始代码保持一致。这样,当出现错误修复时,我们可以看到所有其他受影响的模型,并选择将修改级联到下级或中断副本。
如果一个文件是另一个文件的完整副本,则应在 utils/check_copies.py
的常量 FULL_COPIES
中注册它。
此机制依赖于以下形式的注释:# Copied from xxx
。xxx
应该包含要复制到的类或函数的完整路径。例如,RobertaSelfOutput
是 BertSelfOutput
类的直接副本,因此您可以在 此处 看到它有一个注释
# Copied from transformers.models.bert.modeling_bert.BertSelfOutput
请注意,您可以在复制的类中应用此机制,也可以应用到复制的相应方法中。例如,在 此处,您可以看到 RobertaPreTrainedModel._init_weights
是如何从 BertPreTrainedModel
中的相同方法复制过来的,并带有注释
# Copied from transformers.models.bert.modeling_bert.BertPreTrainedModel._init_weights
有时副本完全相同,除了名称:例如在 RobertaAttention
中,我们使用 RobertaSelfAttention
而不是 BertSelfAttention
,但除此之外,代码完全相同。这就是 # Copied from
支持使用以下语法进行简单字符串替换的原因:Copied from xxx with foo->bar
。这意味着代码被复制,所有 foo
实例都被替换为 bar
。您可以看到它在 RobertaAttention
中是如何使用的,在 此处,带有注释
# Copied from transformers.models.bert.modeling_bert.BertAttention with Bert->Roberta
请注意,箭头周围不应该有任何空格(除非该空格是要替换的模式的一部分)。
您可以添加多个用逗号分隔的模式。例如,这里CamemberForMaskedLM
是RobertaForMaskedLM
的直接复制,并进行了两个替换:Roberta
替换为 Camembert
,ROBERTA
替换为 CAMEMBERT
。您可以 在这里 看到这是通过注释完成的。
# Copied from transformers.models.roberta.modeling_roberta.RobertaForMaskedLM with Roberta->Camembert, ROBERTA->CAMEMBERT
如果顺序很重要(因为其中一个替换可能会与之前的替换冲突),则替换将从左到右执行。
如果替换更改了格式(例如,用很长的名称替换短名称),则在应用自动格式化程序后检查复制。
当模式只是相同替换的不同大小写(具有大写和小写变体)时,另一种方法是添加选项 all-casing
。 这里 是 MobileBertForSequenceClassification
中的示例,带有注释。
# Copied from transformers.models.bert.modeling_bert.BertForSequenceClassification with Bert->MobileBert all-casing
在这种情况下,代码从 BertForSequenceClassification
复制而来,并进行了以下替换:
Bert
替换为MobileBert
(例如,在使用MobileBertModel
初始化时)bert
替换为mobilebert
(例如,在定义self.mobilebert
时)BERT
替换为MOBILEBERT
(在常量MOBILEBERT_INPUTS_DOCSTRING
中)