拉取请求的检查
当你在🤗 Transformers上打开一个拉取请求时,将会运行相当数量的检查,以确保你添加的补丁不会破坏任何现有的功能。这些检查分为四种类型:
- 常规测试
- 文档构建
- 代码和文档风格
- 通用存储库一致性
在本文档中,我们将尝试解释这些不同的检查是什么以及它们背后的原因,以及如果其中一项检查在您的PR上失败时如何在本地进行调试。
请注意,理想情况下,他们要求您有一个开发安装:
pip install transformers[dev]
或者进行可编辑安装:
pip install -e .[dev]
在Transformers仓库内部。由于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库对模型代码有非常明确的要求,并且每个模型应该完全在一个文件中实现,而不依赖于其他模型,我们添加了一种机制来检查给定模型的某一层代码的副本是否与原始代码保持一致。这样,当有bug修复时,我们可以看到所有其他受影响的模型,并选择逐步应用修改或中断副本。
如果一个文件是另一个文件的完整副本,你应该在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
中)