Transformers 4.37 中文文档(十)(3)https://developer.aliyun.com/article/1564904
拉取请求检查列表
☐ 拉取请求标题应总结您的贡献。
☐ 如果您的拉取请求解决了一个问题,请在拉取请求描述中提及问题编号,以确保它们链接在一起(并且查看问题的人知道您正在处理它)。
☐ 要指示正在进行的工作,请在标题前加上[WIP]
。这对于避免重复工作以及将其与准备合并的 PR 区分开很有用。
☐ 确保现有测试通过。
☐ 如果要添加新功能,请为其添加测试。
- 如果要添加新模型,请确保使用
ModelTester.all_model_classes = (MyModel, MyModelWithLMHead,...)
来触发常见测试。 - 如果要添加新的
@slow
测试,请确保使用RUN_SLOW=1 python -m pytest tests/models/my_new_model/test_my_new_model.py
来运行它们。 - 如果要添加新的分词器,请编写测试并确保
RUN_SLOW=1 python -m pytest tests/models/{your_model_name}/test_tokenization_{your_model_name}.py
通过。 - CircleCI 不运行慢速测试,但 GitHub Actions 每晚都会运行!
☐ 所有公共方法必须具有信息性的文档字符串(请参阅modeling_bert.py
作为示例)。
☐ 由于存储库正在迅速增长,请不要添加任何图像、视频和其他非文本文件,这些文件会显著增加存储库的大小。相反,请使用 Hub 存储库,例如 hf-internal-testing
来托管这些文件,并通过 URL 引用它们。我们建议将与文档相关的图像放在以下存储库中:huggingface/documentation-images。您可以在此数据集存储库上打开一个 PR,并请求 Hugging Face 成员合并它。
有关在拉取请求上运行的检查的更多信息,请查看我们的 拉取请求上的检查 指南。
测试
包含了一个广泛的测试套件,用于测试库的行为和几个示例。库测试可以在 tests 文件夹中找到,示例测试可以在 examples 文件夹中找到。
我们喜欢 pytest
和 pytest-xdist
,因为它更快。从存储库的根目录开始,指定一个子文件夹路径或测试文件来运行测试。
python -m pytest -n auto --dist=loadfile -s -v ./tests/models/my_new_model
同样,对于 examples
目录,指定一个子文件夹路径或测试文件来运行测试。例如,以下命令测试 PyTorch examples
目录中的文本分类子文件夹:
pip install -r examples/xxx/requirements.txt # only needed the first time python -m pytest -n auto --dist=loadfile -s -v ./examples/pytorch/text-classification
实际上,这就是我们实现 make test
和 make test-examples
命令的方式(不包括 pip install
)!
您还可以指定一小组较小的测试,以便仅测试您正在处理的功能。
默认情况下,慢测试会被跳过,但您可以将 RUN_SLOW
环境变量设置为 yes
来运行它们。这将下载许多千兆字节的模型,因此请确保您有足够的磁盘空间、良好的互联网连接或足够的耐心!
记得指定一个子文件夹路径或测试文件来运行测试。否则,您将运行 tests
或 examples
文件夹中的所有测试,这将花费很长时间!
RUN_SLOW=yes python -m pytest -n auto --dist=loadfile -s -v ./tests/models/my_new_model RUN_SLOW=yes python -m pytest -n auto --dist=loadfile -s -v ./examples/pytorch/text-classification
与慢测试类似,还有其他环境变量可用,这些环境变量在测试期间默认未启用:
RUN_CUSTOM_TOKENIZERS
: 启用自定义分词器的测试。RUN_PT_FLAX_CROSS_TESTS
: 启用 PyTorch + Flax 集成的测试。RUN_PT_TF_CROSS_TESTS
: 启用 TensorFlow + PyTorch 集成的测试。
更多环境变量和其他信息可以在 testing_utils.py 中找到。
🤗 Transformers 仅使用 pytest
作为测试运行器。它不在测试套件本身中使用任何 pytest
特定功能。
这意味着 unittest
得到了完全支持。以下是如何使用 unittest
运行测试的方法:
python -m unittest discover -s tests -t . -v python -m unittest discover -s examples -t examples -v
样式指南
对于文档字符串,🤗 Transformers 遵循 Google Python Style Guide。查看我们的 文档编写指南 获取更多信息。
在 Windows 上开发
在 Windows 上(除非您在 Windows Subsystem for Linux 或 WSL 中工作),您需要配置 git 将 Windows 的 CRLF
行结束符转换为 Linux 的 LF
行结束符:
git config core.autocrlf
在 Windows 上运行 make
命令的一种方法是使用 MSYS2:
- 下载 MSYS2,我们假设它安装在
C:\msys64
中。 - 打开命令行
C:\msys64\msys2.exe
(应该可以从开始菜单中找到)。 - 在 shell 中运行:
pacman -Syu
并使用pacman -S make
安装make
。 - 将
C:\msys64\usr\bin
添加到您的 PATH 环境变量中。
您现在可以从任何终端(Powershell、cmd.exe 等)使用 make
了!🎉
将派生存储库与上游主存储库同步(Hugging Face 存储库)
更新分叉存储库的主分支时,请按照以下步骤操作,以避免向上游存储库发送引用注释并向参与这些拉取请求的开发人员发送不必要的通知。
- 尽量避免使用分支和拉取请求与上游同步。而是直接合并到分叉主分支。
- 如果绝对必要,使用以下步骤在检出分支后:
git checkout -b your-branch-for-syncing git pull --squash --no-commit upstream main git commit -m '<your message without GitHub references>' git push --set-upstream origin your-branch-for-syncing
如何将模型添加到🤗 Transformers?
原文链接:
huggingface.co/docs/transformers/v4.37.2/en/add_new_model
🤗 Transformers 库通常能够通过社区贡献者提供新模型。但这可能是一个具有挑战性的项目,需要深入了解🤗 Transformers 库和要实现的模型。在 Hugging Face,我们正在努力赋予更多社区成员积极添加模型的能力,并为您提供这个指南,以指导您添加一个 PyTorch 模型(确保您已经安装了 PyTorch)。
如果您有兴趣实现一个 TensorFlow 模型,请查看如何将🤗 Transformers 模型转换为 TensorFlow 指南!
沿途,您将:
- 了解开源最佳实践
- 了解最受欢迎的深度学习库背后的设计原则
- 学习如何有效地测试大型模型
- 学习如何集成 Python 实用程序,如
black
、ruff
和make fix-copies
,以确保代码整洁可读
Hugging Face 团队成员将随时为您提供帮助,因此您永远不会孤单。🤗 ❤️
要开始,请为您想在🤗 Transformers 中看到的模型打开一个新模型添加问题。如果您对贡献特定模型不是特别挑剔,您可以按New model label进行筛选,看看是否有任何未认领的模型请求并开始处理。
一旦您打开了一个新模型请求,如果您还不熟悉🤗 Transformers,第一步是熟悉它!
🤗 Transformers 的概述
首先,您应该对🤗 Transformers 有一个总体了解。🤗 Transformers 是一个非常主观的库,因此您可能不同意一些库的理念或设计选择。然而,根据我们的经验,我们发现库的基本设计选择和理念对于有效扩展🤗 Transformers 并保持维护成本在合理水平上至关重要。
更好地了解库的一个很好的起点是阅读我们哲学的文档。由于我们的工作方式,有一些选择我们试图应用于所有模型:
- 通常更喜欢组合而不是抽象
- 复制代码并不总是坏事,如果它极大地提高了模型的可读性或可访问性
- 模型文件尽可能自包含,这样当您阅读特定模型的代码时,理想情况下只需查看相应的
modeling_....py
文件。
在我们看来,库的代码不仅仅是提供产品的手段,例如使用 BERT 进行推断的能力,而且也是我们想要改进的产品本身。因此,当添加一个模型时,用户不仅是将使用您的模型的人,还有所有将阅读、尝试理解和可能调整您的代码的人。
有了这个想法,让我们更深入地了解一下一般的库设计。
模型概述
要成功添加一个模型,重要的是要理解您的模型与其配置、PreTrainedModel 和 PretrainedConfig 之间的交互。为了举例说明,我们将要添加到🤗 Transformers 的模型称为BrandNewBert
。
让我们看一看:
正如您所看到的,我们在🤗 Transformers 中确实使用了继承,但我们将抽象级别保持到绝对最低限度。库中任何模型的抽象级别永远不会超过两个。BrandNewBertModel
继承自BrandNewBertPreTrainedModel
,后者又继承自 PreTrainedModel,就是这样。一般规则是,我们希望确保新模型仅依赖于 PreTrainedModel。自动提供给每个新模型的重要功能是 from_pretrained()和 save_pretrained(),用于序列化和反序列化。所有其他重要功能,如BrandNewBertModel.forward
,应完全在新的modeling_brand_new_bert.py
脚本中定义。接下来,我们要确保具有特定头层的模型,如BrandNewBertForMaskedLM
,不继承自BrandNewBertModel
,而是使用BrandNewBertModel
作为可以在其前向传递中调用的组件,以保持抽象级别低。每个新模型都需要一个配置类,称为BrandNewBertConfig
。此配置始终存储为 PreTrainedModel 中的属性,因此可以通过config
属性访问所有继承自BrandNewBertPreTrainedModel
的类:
model = BrandNewBertModel.from_pretrained("brandy/brand_new_bert") model.config # model has access to its config
与模型类似,配置从 PretrainedConfig 继承基本的序列化和反序列化功能。请注意,配置和模型始终以两种不同的格式进行序列化 - 模型保存为pytorch_model.bin文件,配置保存为config.json文件。调用 save_pretrained()将自动调用 save_pretrained(),以便同时保存模型和配置。
代码风格
在编写新模型时,请记住 Transformers 是一个持有意见的库,关于代码应该如何编写,我们有自己的一些怪癖 😃
- 您的模型的前向传递应完全在建模文件中编写,同时完全独立于库中的其他模型。如果要重用另一个模型中的块,请复制代码并在顶部添加
# Copied from
注释(请参见此处以获取一个很好的示例,以及此处以获取有关复制的更多文档)。 - 代码应该是完全可理解的,即使对于非母语英语的人也是如此。这意味着您应该选择描述性的变量名称并避免缩写。例如,
activation
比act
更受欢迎。除非是循环中的索引,否则强烈不建议使用一个字母的变量名。 - 总的来说,我们更喜欢长而明确的代码,而不是短而神奇的代码。
- 在 PyTorch 中避免对
nn.Sequential
进行子类化,而是对nn.Module
进行子类化并编写前向传递,以便使用您的代码的任何人都可以通过添加打印语句或断点来快速调试它。 - 您的函数签名应该有类型注释。对于其余部分,良好的变量名称比类型注释更可读和可理解。
分词器概述
还不太准备好 😦 此部分将很快添加!
将模型添加到🤗 Transformers 的逐步配方
每个人对如何移植模型都有不同的偏好,因此查看其他贡献者如何将模型移植到 Hugging Face 可能会对您非常有帮助。以下是关于如何移植模型的社区博客文章列表:
- 移植 GPT2 模型 by Thomas
- 移植 WMT19 MT 模型 by Stas
根据经验,我们可以告诉您在添加模型时要牢记的最重要的事情是:
- 不要重复造轮子!您将为新的🤗 Transformers 模型添加的大部分代码已经存在于🤗 Transformers 的某个地方。花些时间找到类似的、已经存在的模型和分词器,您可以从中复制。grep和rg是您的朋友。请注意,您的模型的分词器可能基于一个模型实现,而您的模型的建模代码可能基于另一个模型实现。例如,FSMT 的建模代码基于 BART,而 FSMT 的分词器代码基于 XLM。
- 这更多是一个工程挑战而不是一个科学挑战。您应该花更多时间创建一个高效的调试环境,而不是试图理解论文中模型的所有理论方面。
- 当您遇到困难时,请寻求帮助!模型是🤗 Transformers 的核心组件,因此我们在 Hugging Face 非常乐意在每个步骤帮助您添加您的模型。如果您发现自己没有取得进展,请不要犹豫询问。
接下来,我们尝试为您提供一个我们在将模型移植到🤗 Transformers 时发现最有用的一般步骤。
以下列表总结了添加模型时必须完成的所有工作,并可以作为待办事项清单使用:
□ (可选)理解模型的理论方面
□ 准备🤗 Transformers 开发环境
□ 设置原始存储库的调试环境
□ 创建脚本,成功使用原始存储库和检查点运行forward()
传递
□ 成功将模型骨架添加到🤗 Transformers
□ 成功将原始检查点转换为🤗 Transformers 检查点
□ 在🤗 Transformers 中成功运行forward()
传递,输出与原始检查点相同
□ 在🤗 Transformers 中完成模型测试
□ 在🤗 Transformers 中成功添加了分词器
□ 运行端到端集成测试
□ 完成文档
□ 将模型权重上传到 Hub
□ 提交拉取请求
□ (可选)添加演示笔记本
通常建议首先对BrandNewBert
有一个良好的理论理解。但是,如果您更喜欢在工作中理解模型的理论方面,那么直接深入BrandNewBert
的代码库也是完全可以的。如果您的工程技能比理论技能更强,如果您难以理解BrandNewBert
的论文,或者如果您更喜欢编程而不是阅读科学论文,那么这个选项可能更适合您。
1. (可选)BrandNewBert 的理论方面
您应该花些时间阅读BrandNewBert的论文,如果存在这样的描述性工作。论文中可能有一些难以理解的大段内容。如果是这种情况,没关系 - 不要担心!目标不是深入理解论文,而是提取在🤗 Transformers 中有效重新实现模型所需的必要信息。也就是说,您不必花太多时间在理论方面,而是要专注于实践方面,即:
- brand_new_bert是什么类型的模型?类似 BERT 的仅编码器模型?类似 GPT2 的仅解码器模型?类似 BART 的编码器-解码器模型?如果您对这些之间的区别不熟悉,请查看[model_summary]。
- brand_new_bert的应用是什么?文本分类?文本生成?Seq2Seq 任务,例如,摘要?
- 该模型的新特性是什么,使其与 BERT/GPT-2/BART 不同?
- 已经存在的🤗 Transformers 模型中哪一个与brand_new_bert最相似?
- 使用了什么类型的分词器?是句子片段分词器?词片段分词器?它是否与 BERT 或 BART 使用的相同的分词器?
当您感觉对模型的架构有了很好的概述后,您可能希望向 Hugging Face 团队发送任何可能有的问题。这可能包括有关模型架构、注意力层等的问题。我们将非常乐意帮助您。
2. 接下来准备您的环境
- 点击存储库页面上的“Fork”按钮来 fork 这个存储库。这将在您的 GitHub 用户账户下创建代码的副本。
- 将您的
transformers
fork 克隆到本地磁盘,并将基本存储库添加为远程:
git clone https://github.com/[your Github handle]/transformers.git cd transformers git remote add upstream https://github.com/huggingface/transformers.git
- 设置一个开发环境,例如通过运行以下命令
python -m venv .env source .env/bin/activate pip install -e ".[dev]"
根据您的操作系统,由于 Transformers 的可选依赖项数量正在增加,您可能会在此命令中遇到失败。如果是这种情况,请确保安装您正在使用的深度学习框架(PyTorch、TensorFlow 和/或 Flax),然后执行:
pip install -e ".[quality]"
这对于大多数用例应该足够了。然后您可以返回到父目录
cd ..
- 我们建议将 PyTorch 版本的brand_new_bert添加到 Transformers 中。要安装 PyTorch,请按照
pytorch.org/get-started/locally/
上的说明操作。
注意: 您不需要安装 CUDA。使新模型在 CPU 上运行就足够了。
- 要移植brand_new_bert,您还需要访问其原始存储库:
git clone https://github.com/org_that_created_brand_new_bert_org/brand_new_bert.git cd brand_new_bert pip install -e .
现在您已经设置好了一个开发环境,可以将brand_new_bert移植到🤗 Transformers。
3.-4. 在原始存储库中运行预训练检查点
首先,您将在原始brand_new_bert存储库上工作。通常,原始实现非常“研究性”。这意味着文档可能缺失,代码可能难以理解。但这应该正是您重新实现brand_new_bert的动力所在。在 Hugging Face,我们的主要目标之一是让人们“站在巨人的肩膀上”,这在这里非常好地体现为拿一个可用的模型并重写它,使其尽可能易于访问、用户友好和美观。这是重新实现模型到🤗 Transformers 的首要动机 - 尝试使复杂的新 NLP 技术对每个人都可访问。
因此,您应该首先深入研究原始存储库。
在原始存储库中成功运行官方预训练模型通常是最困难的一步。根据我们的经验,花一些时间熟悉原始代码库非常重要。您需要弄清楚以下内容:
- 在哪里找到预训练权重?
- 如何将预训练权重加载到相应的模型中?
- 如何独立于模型运行分词器?
- 跟踪一次前向传递,以便了解哪些类和函数需要进行简单的前向传递。通常,您只需要重新实现这些函数。
- 能够找到模型的重要组件:模型的类在哪里?是否有模型子类,例如 EncoderModel,DecoderModel?自注意力层在哪里?是否有多个不同的注意力层,例如 self-attention,cross-attention…?
- 如何在存储库的原始环境中调试模型?您是否需要添加print语句,是否可以使用交互式调试器如ipdb,或者是否应该使用高效的 IDE 来调试模型,如 PyCharm?
在开始移植过程之前,非常重要的是您可以有效地调试原始存储库中的代码!还要记住,您正在使用一个开源库,因此不要犹豫在原始存储库中打开问题,甚至提交拉取请求。这个存储库的维护者很可能会对有人查看他们的代码感到非常高兴!
在这一点上,真的取决于您更喜欢使用哪种调试环境和策略来调试原始模型。我们强烈建议不要设置昂贵的 GPU 环境,而是在开始深入研究原始存储库和开始编写🤗 Transformers 模型实现时都使用 CPU。只有在模型已经成功移植到🤗 Transformers 后,才应验证模型在 GPU 上是否按预期工作。
一般来说,有两种可能的调试环境可用于运行原始模型
- Jupyter 笔记本 / google colab
- 本地 python 脚本。
Jupyter 笔记本的优势在于它们允许逐个单元格执行,这有助于更好地将逻辑组件彼此分离,并且具有更快的调试周期,因为中间结果可以被存储。此外,笔记本通常更容易与其他贡献者共享,如果您想要向 Hugging Face 团队寻求帮助,这可能非常有帮助。如果您熟悉 Jupyter 笔记本,我们强烈建议您使用它们。
Jupyter 笔记本的明显缺点是,如果您不习惯使用它们,您将不得不花费一些时间适应新的编程环境,可能无法再使用您已知的调试工具,如ipdb
。
对于每个代码库,一个很好的第一步总是加载一个小的预训练检查点,并能够使用一个虚拟整数向量的输入 ID 进行单个前向传递。这样的脚本可能如下所示(伪代码):
model = BrandNewBertModel.load_pretrained_checkpoint("/path/to/checkpoint/") input_ids = [0, 4, 5, 2, 3, 7, 9] # vector of input ids original_output = model.predict(input_ids)
接下来,关于调试策略,通常有几种选择:
- 将原始模型分解为许多小的可测试组件,并在每个组件上运行前向传递以进行验证
- 将原始模型仅分解为原始tokenizer和原始model,在这些上运行前向传递,并使用中间打印语句或断点进行验证
再次,选择哪种策略取决于你。通常,根据原始代码库的情况,一种或另一种策略都有优势。
如果原始代码库允许您将模型分解为较小的子组件,例如,如果原始代码库可以轻松在急切模式下运行,那么通常值得这样做。在一开始采取更困难的道路有一些重要的优势:
- 在稍后阶段,当将原始模型与 Hugging Face 实现进行比较时,您可以自动验证每个组件是否与🤗 Transformers 实现的相应组件匹配,而不是依赖通过打印语句进行视觉比较
- 它可以让您将将模型移植为较小问题的大问题分解为仅将单个组件移植为结构化工作的更好的方法。
- 将模型分解为逻辑有意义的组件将有助于更好地了解模型的设计,从而更好地理解模型
- 在稍后阶段,这些逐个组件的测试有助于确保在继续更改代码时不会发生退化
Lysandre 的 ELECTRA 集成检查为如何执行此操作提供了一个很好的示例。
然而,如果原始代码库非常复杂,或者只允许以编译模式运行中间组件,那么将模型分解为可测试的较小子组件可能会耗费太多时间,甚至是不可能的。一个很好的例子是T5 的 MeshTensorFlow库,它非常复杂,没有提供将模型分解为子组件的简单方法。对于这种库,人们通常依赖于验证打印语句。
无论您选择哪种策略,推荐的程序通常是相同的,即应该从调试起始图层开始,最后调试结束图层。
建议按照以下顺序检索以下图层的输出,可以通过打印语句或子组件函数来实现:
- 检索传递给模型的输入 ID
- 检索单词嵌入
- 检索第一个 Transformer 层的输入
- 检索第一个 Transformer 层的输出
- 检索以下 n-1 个 Transformer 层的输出
- 检索整个 BrandNewBert 模型的输出
输入 ID 应该由整数数组组成,例如 input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19]
以下图层的输出通常由多维浮点数组组成,可能如下所示:
[[ [-0.1465, -0.6501, 0.1993, ..., 0.1451, 0.3430, 0.6024], [-0.4417, -0.5920, 0.3450, ..., -0.3062, 0.6182, 0.7132], [-0.5009, -0.7122, 0.4548, ..., -0.3662, 0.6091, 0.7648], ..., [-0.5613, -0.6332, 0.4324, ..., -0.3792, 0.7372, 0.9288], [-0.5416, -0.6345, 0.4180, ..., -0.3564, 0.6992, 0.9191], [-0.5334, -0.6403, 0.4271, ..., -0.3339, 0.6533, 0.8694]]],
我们期望每个添加到🤗 Transformers 的模型都经过几个集成测试,这意味着原始模型和🤗 Transformers 中重新实现的版本必须在精度为 0.001 的情况下给出完全相同的输出!由于相同模型在不同库中编写可能会根据库框架给出略有不同的输出,我们接受 1e-3(0.001)的误差容限。如果模型给出的输出几乎相同是不够的,它们必须几乎完全相同。因此,您肯定会多次将🤗 Transformers 版本的中间输出与brand_new_bert的原始实现的中间输出进行比较,在这种情况下,原始存储库的高效调试环境绝对重要。以下是一些建议,以使您的调试环境尽可能高效。
- 找到调试中间结果的最佳方法。原始存储库是用 PyTorch 编写的吗?那么您可能需要花时间编写一个更长的脚本,将原始模型分解为较小的子组件以检索中间值。原始存储库是用 Tensorflow 1 编写的吗?那么您可能需要依赖 TensorFlow 的打印操作,如 tf.print 来输出中间值。原始存储库是用 Jax 编写的吗?那么请确保在运行前向传递时模型未被 jit 编译,例如查看 此链接。
- 使用您能找到的最小的预训练检查点。检查点越小,您的调试周期就越快。如果您的预训练模型太大,导致前向传递超过 10 秒,那就不高效了。如果只有非常大的检查点可用,可能更有意义的是在新环境中创建一个带有随机初始化权重的虚拟模型,并保存这些权重以便与您模型的🤗 Transformers 版本进行比较。
- 确保您正在使用原始存储库中调用前向传递的最简单方法。理想情况下,您希望找到原始存储库中仅调用单个前向传递的函数,即通常称为
predict
、evaluate
、forward
或__call__
。您不希望调试多次调用forward
的函数,例如生成文本的autoregressive_sample
、generate
。 - 尝试将标记化与模型的forward传递分开。如果原始存储库显示示例,您必须输入一个字符串,则尝试找出在前向调用中字符串输入何时更改为输入 id,并从此点开始。这可能意味着您可能需要自己编写一个小脚本或更改原始代码,以便您可以直接输入 id 而不是输入字符串。
- 确保您调试设置中的模型不处于训练模式,这通常会导致模型由于模型中的多个 dropout 层而产生随机输出。确保您调试环境中的前向传递是确定性的,以便不使用 dropout 层。或者如果旧实现和新实现在同一框架中,则使用transformers.utils.set_seed。
下一节将为您提供有关如何为brand_new_bert执行此操作的更具体详细信息/提示。
Transformers 4.37 中文文档(十)(5)https://developer.aliyun.com/article/1564906