Transformers 4.37 中文文档(十)(4)https://developer.aliyun.com/article/1564905
5.-14. 将 BrandNewBert 移植到🤗 Transformers
接下来,您可以开始向🤗 Transformers 添加新代码。进入您🤗 Transformers 分支的克隆:
cd transformers
在特殊情况下,如果您要添加的模型的架构与现有模型的模型架构完全匹配,则只需添加一个转换脚本,如此部分所述。在这种情况下,您可以直接重用已存在模型的整个模型架构。
否则,让我们开始生成一个新模型。您在这里有两个选择:
transformers-cli add-new-model-like
以添加一个类似于现有模型的新模型transformers-cli add-new-model
以从我们的模板中添加一个新模型(将看起来像 BERT 或 Bart,具体取决于您选择的模型类型)
在这两种情况下,您将被提示填写有关您的模型的基本信息的问卷。第二个命令需要安装cookiecutter
,您可以在这里找到更多信息。
在主 huggingface/transformers 仓库上打开一个拉取请求
在开始调整自动生成的代码之前,现在是时候在🤗 Transformers 中打开一个“进行中的工作(WIP)”拉取请求,例如“[WIP]添加brand_new_bert”,以便您和 Hugging Face 团队可以并肩合作将模型集成到🤗 Transformers 中。
您应该执行以下操作:
- 从主分支创建一个具有描述性名称的分支
git checkout -b add_brand_new_bert
- 提交自动生成的代码:
git add . git commit
- 获取并 rebase 到当前主分支
git fetch upstream git rebase upstream/main
- 使用以下命令将更改推送到您的帐户:
git push -u origin a-descriptive-name-for-my-changes
- 一旦您满意,转到 GitHub 上您的分支的网页。点击“拉取请求”。确保将 Hugging Face 团队的一些成员的 GitHub 句柄添加为审阅者,以便 Hugging Face 团队在将来的更改中收到通知。
- 点击 GitHub 拉取请求网页右侧的“转换为草稿”将 PR 转换为草稿。
在接下来的过程中,每当您取得一些进展时,不要忘记提交您的工作并将其推送到您的帐户,以便在拉取请求中显示。此外,您应该确保不时使用以下方法更新您的工作与当前主分支:
git fetch upstream git merge upstream/main
总的来说,您可能对模型或您的实现有任何问题都应该在您的 PR 中提出,并在 PR 中讨论/解决。这样,当您提交新代码或有问题时,Hugging Face 团队将始终收到通知。将 Hugging Face 团队指向您添加的代码通常非常有帮助,以便 Hugging Face 团队可以高效地理解您的问题或疑问。
为此,您可以转到“更改的文件”选项卡,在那里您可以看到所有更改,转到您想要提问的行,并单击“+”符号添加评论。每当问题或问题得到解决时,您可以单击已创建评论的“解决”按钮。
同样,Hugging Face 团队在审查您的代码时会开放评论。我们建议在 GitHub 上的 PR 上提出大多数问题。对于一些对公众不太有用的非常一般性的问题,可以通过 Slack 或电子邮件联系 Hugging Face 团队。
5. 为 brand_new_bert 调整生成的模型代码
首先,我们将只关注模型本身,不关心分词器。所有相关代码应该在生成的文件src/transformers/models/brand_new_bert/modeling_brand_new_bert.py
和src/transformers/models/brand_new_bert/configuration_brand_new_bert.py
中找到。
现在您可以开始编码了 😃. 生成的代码在src/transformers/models/brand_new_bert/modeling_brand_new_bert.py
中将具有与 BERT 相同的架构(如果是仅编码器模型)或与 BART 相同的架构(如果是编码器-解码器模型)。此时,您应该回想起您在开始时学到的关于模型理论方面的知识:“该模型与 BERT 或 BART 有何不同?”实现这些变化通常意味着更改self-attention层,规范化层的顺序等等…再次强调,查看 Transformers 中已经存在的类似模型的架构通常是有用的,以更好地了解如何实现您的模型。
注意,在这一点上,您不必非常确定您的代码是否完全正确或干净。相反,建议将原始代码的第一个不干净、复制粘贴版本添加到src/transformers/models/brand_new_bert/modeling_brand_new_bert.py
,直到您觉得所有必要的代码都已添加。根据我们的经验,快速添加所需代码的第一个版本,并使用下一节中描述的转换脚本迭代地改进/纠正代码效率更高。在这一点上,唯一需要工作的是您可以实例化🤗 Transformers 实现的brand_new_bert,即以下命令应该可以工作:
from transformers import BrandNewBertModel, BrandNewBertConfig model = BrandNewBertModel(BrandNewBertConfig())
上述命令将根据BrandNewBertConfig()
中定义的默认参数创建一个模型,具有随机权重,从而确保所有组件的init()
方法正常工作。
请注意,所有随机初始化应该在您的BrandnewBertPreTrainedModel
类的_init_weights
方法中进行。它应该根据配置的变量初始化所有叶子模块。这里有一个使用 BERT _init_weights
方法的示例:
def _init_weights(self, module): """Initialize the weights""" if isinstance(module, nn.Linear): module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.bias is not None: module.bias.data.zero_() elif isinstance(module, nn.Embedding): module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.padding_idx is not None: module.weight.data[module.padding_idx].zero_() elif isinstance(module, nn.LayerNorm): module.bias.data.zero_() module.weight.data.fill_(1.0)
如果需要某些模块的特殊初始化,您可以使用一些自定义方案。例如,在Wav2Vec2ForPreTraining
中,最后两个线性层需要使用常规 PyTorch nn.Linear
的初始化,但所有其他层应该使用上述初始化。这样编码:
def _init_weights(self, module): """Initialize the weights""" if isinstnace(module, Wav2Vec2ForPreTraining): module.project_hid.reset_parameters() module.project_q.reset_parameters() module.project_hid._is_hf_initialized = True module.project_q._is_hf_initialized = True elif isinstance(module, nn.Linear): module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.bias is not None: module.bias.data.zero_()
_is_hf_initialized
标志在内部用于确保我们只初始化一个子模块一次。通过将其设置为True
,我们确保自定义初始化不会被后来覆盖,_init_weights
函数不会应用于它们。
6. 编写转换脚本
接下来,您应该编写一个转换脚本,让您可以将您在原始存储库中用于调试brand_new_bert的检查点转换为与您刚刚创建的🤗 Transformers 实现brand_new_bert兼容的检查点。不建议从头开始编写转换脚本,而是查看🤗 Transformers 中已经存在的转换脚本,找到一个已经用于转换与brand_new_bert相同框架编写的类似模型的脚本。通常,复制一个已经存在的转换脚本并稍微调整以适应您的用例就足够了。不要犹豫向 Hugging Face 团队询问是否有类似的已经存在的转换脚本适用于您的模型。
- 如果您正在将模型从 TensorFlow 转换到 PyTorch,一个很好的起点可能是 BERT 的转换脚本此处
- 如果您正在将模型从 PyTorch 转换到 PyTorch,一个很好的起点可能是 BART 的转换脚本此处
接下来,我们将快速解释 PyTorch 模型如何存储层权重并定义层名称。在 PyTorch 中,层的名称由您给予该层的类属性的名称定义。让我们在 PyTorch 中定义一个名为SimpleModel
的虚拟模型,如下所示:
from torch import nn class SimpleModel(nn.Module): def __init__(self): super().__init__() self.dense = nn.Linear(10, 10) self.intermediate = nn.Linear(10, 10) self.layer_norm = nn.LayerNorm(10)
现在我们可以创建此模型定义的实例,该实例将填充所有权重:dense
、intermediate
、layer_norm
,使用随机权重。我们可以打印模型以查看其架构
model = SimpleModel() print(model)
这将打印如下内容:
SimpleModel( (dense): Linear(in_features=10, out_features=10, bias=True) (intermediate): Linear(in_features=10, out_features=10, bias=True) (layer_norm): LayerNorm((10,), eps=1e-05, elementwise_affine=True) )
我们可以看到层的名称由 PyTorch 中类属性的名称定义。您可以打印特定层的权重值:
print(model.dense.weight.data)
查看权重是否已随机初始化
tensor([[-0.0818, 0.2207, -0.0749, -0.0030, 0.0045, -0.1569, -0.1598, 0.0212, -0.2077, 0.2157], [ 0.1044, 0.0201, 0.0990, 0.2482, 0.3116, 0.2509, 0.2866, -0.2190, 0.2166, -0.0212], [-0.2000, 0.1107, -0.1999, -0.3119, 0.1559, 0.0993, 0.1776, -0.1950, -0.1023, -0.0447], [-0.0888, -0.1092, 0.2281, 0.0336, 0.1817, -0.0115, 0.2096, 0.1415, -0.1876, -0.2467], [ 0.2208, -0.2352, -0.1426, -0.2636, -0.2889, -0.2061, -0.2849, -0.0465, 0.2577, 0.0402], [ 0.1502, 0.2465, 0.2566, 0.0693, 0.2352, -0.0530, 0.1859, -0.0604, 0.2132, 0.1680], [ 0.1733, -0.2407, -0.1721, 0.1484, 0.0358, -0.0633, -0.0721, -0.0090, 0.2707, -0.2509], [-0.1173, 0.1561, 0.2945, 0.0595, -0.1996, 0.2988, -0.0802, 0.0407, 0.1829, -0.1568], [-0.1164, -0.2228, -0.0403, 0.0428, 0.1339, 0.0047, 0.1967, 0.2923, 0.0333, -0.0536], [-0.1492, -0.1616, 0.1057, 0.1950, -0.2807, -0.2710, -0.1586, 0.0739, 0.2220, 0.2358]]).
在转换脚本中,您应该使用相应层中的确切权重填充这些随机初始化的权重。例如
# retrieve matching layer weights, e.g. by # recursive algorithm layer_name = "dense" pretrained_weight = array_of_dense_layer model_pointer = getattr(model, "dense") model_pointer.weight.data = torch.from_numpy(pretrained_weight)
在执行此操作时,您必须验证您的 PyTorch 模型的每个随机初始化权重及其对应的预训练检查点权重在形状和名称上完全匹配。为此,必须添加形状的 assert 语句并打印出检查点权重的名称。例如,您应该添加类似以下语句:
assert ( model_pointer.weight.shape == pretrained_weight.shape ), f"Pointer shape of random weight {model_pointer.shape} and array shape of checkpoint weight {pretrained_weight.shape} mismatched"
此外,您还应该打印出两个权重的名称,以确保它们匹配,例如
logger.info(f"Initialize PyTorch weight {layer_name} from {pretrained_weight.name}")
如果形状或名称不匹配,则您可能将错误的检查点权重分配给了🤗 Transformers 实现的随机初始化层。
不正确的形状很可能是由于在BrandNewBertConfig()
中不正确设置配置参数,这些参数与您要转换的检查点使用的参数不完全匹配。但是,也可能是 PyTorch 的层实现要求在之前对权重进行转置。
最后,您还应该检查所有必需的权重是否已初始化,并打印出所有未用于初始化的检查点权重,以确保模型已正确转换。完全正常的是,转换尝试可能会因为错误的形状语句或错误的名称分配而失败。这很可能是因为您在BrandNewBertConfig()
中使用了不正确的参数,在🤗 Transformers 实现中有错误的架构,您在🤗 Transformers 实现的一个组件的init()
函数中有错误,或者您需要转置一个检查点权重。
应该通过前面的步骤迭代此步骤,直到正确加载所有检查点的权重到 Transformers 模型中。正确加载检查点到🤗 Transformers 实现后,您可以将模型保存在您选择的文件夹中/path/to/converted/checkpoint/folder
,该文件夹应包含一个pytorch_model.bin
文件和一个config.json
文件:
model.save_pretrained("/path/to/converted/checkpoint/folder")
7. 实现正向传递
成功将预训练权重正确加载到🤗 Transformers 实现中后,现在应确保正向传递已正确实现。在熟悉原始存储库中,您已经创建了一个脚本,该脚本使用原始存储库运行模型的正向传递。现在,您应该编写一个类似的脚本,使用🤗 Transformers 实现而不是原始实现。它应该如下所示:
model = BrandNewBertModel.from_pretrained("/path/to/converted/checkpoint/folder") input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19] output = model(input_ids).last_hidden_states
🤗 Transformers 实现和原始模型实现很可能不会在第一次给出完全相同的输出,或者前向传递会出错。不要失望 - 这是预期的!首先,您应该确保前向传递不会出错。经常发生使用了错误的维度导致 维度不匹配 错误,或者使用了错误的数据类型对象,例如 torch.long
而不是 torch.float32
。如果您无法解决某些错误,请毫不犹豫地向 Hugging Face 团队寻求帮助。
确保 🤗 Transformers 实现正确工作的最后一部分是确保输出精度达到 1e-3
。首先,您应该确保输出形状相同,即脚本的 outputs.shape
应该对 🤗 Transformers 实现和原始实现产生相同的值。接下来,您应该确保输出值也相同。这是添加新模型中最困难的部分之一。输出不相同的常见错误包括:
- 某些层未添加,即未添加 激活 层,或者遗忘了残差连接。
- 单词嵌入矩阵未绑定
- 使用了错误的位置嵌入,因为原始实现使用了偏移
- 在前向传递期间应用了辍学。要修复此问题,请确保 model.training 为 False,并且在前向传递期间不会误激活任何辍学层,即将 self.training 传递给 PyTorch 的功能性辍学
通常修复问题的最佳方法是同时查看原始实现和 🤗 Transformers 实现的前向传递,并检查是否有任何差异。理想情况下,您应该调试/打印出两个实现的前向传递的中间输出,以找到 🤗 Transformers 实现显示与原始实现不同输出的网络中的确切位置。首先,确保两个脚本中硬编码的 input_ids
是相同的。接下来,验证 input_ids
的第一个转换的输出(通常是单词嵌入)是否相同。然后逐层向网络的最后一层工作。在某个时候,您会注意到两个实现之间的差异,这应该指向 🤗 Transformers 实现中的错误。根据我们的经验,一个简单而有效的方法是在原始实现和 🤗 Transformers 实现中的相同位置分别添加许多打印语句,并逐步删除显示中间表示值相同的打印语句。
当您确信两个实现产生相同输出时,使用 torch.allclose(original_output, output, atol=1e-3)
验证输出,您已经完成了最困难的部分!恭喜 - 剩下的工作应该很轻松 😊。
8. 添加所有必要的模型测试
在这一点上,您已成功添加了一个新模型。但是,很可能该模型尚未完全符合所需的设计。为确保实现与 🤗 Transformers 完全兼容,所有常见测试都应该通过。Cookiecutter 应该已自动为您的模型添加了一个测试文件,可能位于相同的 tests/models/brand_new_bert/test_modeling_brand_new_bert.py
下。运行此测试文件以验证所有常见测试是否通过:
pytest tests/models/brand_new_bert/test_modeling_brand_new_bert.py
在修复所有常见测试后,现在至关重要的是确保您所做的所有工作都经过了充分测试,以便
- a) 社区可以通过查看 brand_new_bert 的特定测试轻松理解您的工作
- b) 对模型的未来更改不会破坏模型的任何重要功能。
首先,应添加集成测试。这些集成测试基本上与您早期用于将模型实现到🤗 Transformers 的调试脚本相同。Cookiecutter 已经添加了这些模型测试的模板,称为BrandNewBertModelIntegrationTests
,您只需填写即可。为确保这些测试通过,请运行
RUN_SLOW=1 pytest -sv tests/models/brand_new_bert/test_modeling_brand_new_bert.py::BrandNewBertModelIntegrationTests
如果您使用 Windows,应将RUN_SLOW=1
替换为SET RUN_SLOW=1
。
其次,brand_new_bert特有的所有功能还应在BrandNewBertModelTester
/BrandNewBertModelTest
下的单独测试中进行额外测试。这部分经常被遗忘,但在两个方面非常有用:
- 通过展示brand_new_bert的特殊功能应该如何工作,将您在模型添加过程中获得的知识传递给社区是有帮助的。
- 未来的贡献者可以通过运行这些特殊测试快速测试模型的更改。
9. 实现分词器
接下来,我们应该添加brand_new_bert的分词器。通常,分词器等同于或非常类似于🤗 Transformers 的已有分词器。
找到/提取原始的分词器文件并成功加载到🤗 Transformers 的分词器实现中非常重要。
为了确保分词器正常工作,建议首先在原始存储库中创建一个脚本,输入一个字符串并返回input_ids
。它可能看起来类似于这样(伪代码):
input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words." model = BrandNewBertModel.load_pretrained_checkpoint("/path/to/checkpoint/") input_ids = model.tokenize(input_str)
您可能需要再次深入研究原始存储库,找到正确的分词器函数,或者甚至可能需要对原始存储库的克隆进行更改,以仅输出input_ids
。编写了一个使用原始存储库的功能性分词脚本后,应创建一个类似于🤗 Transformers 的脚本。它应该看起来类似于这样:
from transformers import BrandNewBertTokenizer input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words." tokenizer = BrandNewBertTokenizer.from_pretrained("/path/to/tokenizer/folder/") input_ids = tokenizer(input_str).input_ids
当input_ids
产生相同的值时,最后一步应该添加一个分词器测试文件。
类似于brand_new_bert的建模测试文件,brand_new_bert的分词测试文件应包含一些硬编码的集成测试。
10. 运行端到端集成测试
添加了分词器后,还应在🤗 Transformers 的tests/models/brand_new_bert/test_modeling_brand_new_bert.py
中添加一些端到端集成测试,使用模型和分词器。这样的测试应该在一个有意义的文本对文本示例上展示🤗 Transformers 的实现是否符合预期。有意义的文本对文本示例可以包括例如源到目标翻译对、文章到摘要对、问题到答案对等。如果没有任何迁移检查点在下游任务上进行了微调,仅依赖于模型测试就足够了。为了确保模型完全功能正常,建议您还在 GPU 上运行所有测试。有时您可能会忘记向模型的内部张量添加一些.to(self.device)
语句,在这样的测试中会显示错误。如果您无法访问 GPU,Hugging Face 团队可以负责为您运行这些测试。
11. 添加文档字符串
现在,brand_new_bert所需的所有功能都已添加 - 您几乎完成了!唯一剩下的是添加一个良好的文档字符串和文档页面。Cookiecutter 应该已经添加了一个名为docs/source/model_doc/brand_new_bert.md
的模板文件,您应该填写该文件。您的模型用户通常会在使用您的模型之前首先查看此页面。因此,文档必须易于理解和简洁。向社区添加一些提示以显示模型应如何使用是非常有用的。不要犹豫与 Hugging Face 团队联系有关文档字符串。
接下来,确保添加到src/transformers/models/brand_new_bert/modeling_brand_new_bert.py
的文档字符串是正确的,并包含所有必要的输入和输出。我们有关于编写文档和我们的文档字符串格式的详细指南在这里。值得提醒自己的是,文档应该至少像🤗 Transformers 中的代码一样小心对待,因为文档通常是社区与模型的第一个接触点。
代码重构
很好,现在你已经为brand_new_bert添加了所有必要的代码。在这一点上,你应该通过运行以下代码来纠正一些潜在的不正确的代码风格:
make style
并验证你的编码风格是否通过了质量检查:
make quality
在🤗 Transformers 中还有一些其他非常严格的设计测试可能仍然失败,这会在你的拉取请求的测试中显示出来。这往往是因为文档字符串中缺少一些信息或一些名称不正确。如果你遇到困难,Hugging Face 团队肯定会帮助你。
最后,经过确保代码正确运行后,重新设计代码总是一个好主意。现在所有的测试都通过了,现在是一个好时机再次检查添加的代码并进行一些重构。
恭喜!你现在已经完成了编码部分,太棒了!🎉 你真棒!😎
12. 将模型上传到模型中心
在这最后一部分,你应该将所有检查点转换并上传到模型中心,并为每个上传的模型检查点添加一个模型卡片。你可以通过阅读我们的模型分享和上传页面来熟悉中心的功能。在这里,你应该与 Hugging Face 团队一起工作,决定为每个检查点选择一个合适的名称,并获得所需的访问权限,以便能够将模型上传到作者组织brand_new_bert下。transformers
中的所有模型中都有push_to_hub
方法,这是将你的检查点快速有效地推送到中心的方法。下面是一个小片段:
brand_new_bert.push_to_hub("brand_new_bert") # Uncomment the following line to push to an organization. # brand_new_bert.push_to_hub("<organization>/brand_new_bert")
值得花一些时间为每个检查点创建合适的模型卡片。模型卡片应该突出显示这个特定检查点的特定特征,例如这个检查点是在哪个数据集上进行预训练/微调的?这个模型应该用于哪个下游任务?还应该包括一些关于如何正确使用模型的代码。
13. (可选)添加笔记本
添加一个展示brand_new_bert如何用于推理和/或在下游任务上进行微调的详细笔记本非常有帮助。这不是合并你的 PR 所必需的,但对社区非常有用。
14. 提交你完成的 PR
你现在已经完成了编程工作,可以进入最后一步,即将你的 PR 合并到主分支。通常情况下,Hugging Face 团队在这一点上应该已经帮助过你了,但值得花一些时间为你的完成的 PR 添加一个好的描述,并最终为你的代码添加注释,如果你想指出某些设计选择给你的审阅者。
分享你的工作!
现在,是时候从社区中获得一些对你工作的认可了!完成模型添加是对 Transformers 和整个 NLP 社区的重大贡献。你的代码和移植的预训练模型肯定会被数百甚至数千名开发人员和研究人员使用。你应该为自己的工作感到自豪,并与社区分享你的成就。
你又制作了一个对社区中每个人都很容易访问的模型!🤯
如何将🤗 Transformers 模型转换为 TensorFlow?
原始文本:
huggingface.co/docs/transformers/v4.37.2/en/add_tensorflow_model
在🤗 Transformers 中有多个可用的框架可以使用,这使您在设计应用程序时可以灵活发挥其优势,但这意味着必须根据每个模型添加兼容性。好消息是,将 TensorFlow 兼容性添加到现有模型比从头开始添加新模型更简单!无论您希望更深入地了解大型 TensorFlow 模型,做出重大的开源贡献,还是为您选择的模型启用 TensorFlow,本指南都适合您。
本指南授权您,我们社区的一员,贡献 TensorFlow 模型权重和/或架构,以供在🤗 Transformers 中使用,几乎不需要 Hugging Face 团队的监督。编写一个新模型并不是一件小事,但希望这个指南能让它不再像坐过山车🎢那样,而更像在公园里散步🚶。利用我们的集体经验绝对是使这个过程变得更加容易的关键,因此我们强烈鼓励您对本指南提出改进建议!
在深入研究之前,建议您查看以下资源,如果您对🤗 Transformers 还不熟悉:
- 🤗 Transformers 的概述
- 拥抱面的 TensorFlow 哲学
在本指南的其余部分,您将学习添加新的 TensorFlow 模型架构所需的内容,将 PyTorch 转换为 TensorFlow 模型权重的过程,以及如何有效地调试跨 ML 框架的不匹配。让我们开始吧!
您是否不确定您想要使用的模型是否已经有相应的 TensorFlow 架构?
检查您选择的模型的config.json
文件中的model_type
字段(示例)。如果🤗 Transformers 中相应的模型文件夹有一个以“modeling_tf”开头的文件,这意味着它有一个相应的 TensorFlow 架构(示例)。
逐步指南添加 TensorFlow 模型架构代码
设计大型模型架构的方法有很多,实现该设计的方式也有多种。然而,您可能还记得我们在🤗 Transformers 的概述中提到,我们是一个有主见的团队 - 🤗 Transformers 的易用性依赖于一致的设计选择。从经验中,我们可以告诉您一些关于添加 TensorFlow 模型的重要事项:
- 不要重复造轮子!往往至少有两个参考实现您应该检查:您正在实现的模型的 PyTorch 等效版本以及同一类问题的其他 TensorFlow 模型。
- 出色的模型实现经得起时间的考验。这不是因为代码漂亮,而是因为代码清晰,易于调试和构建。如果您通过在 TensorFlow 实现中复制其他 TensorFlow 模型中的相同模式并最小化与 PyTorch 实现的不匹配,使维护者的生活变得轻松,您就确保您的贡献将长期存在。
- 当遇到困难时寻求帮助!🤗 Transformers 团队在这里帮助您,我们可能已经找到了您面临的相同问题的解决方案。
以下是添加 TensorFlow 模型架构所需步骤的概述:
- 选择您希望转换的模型
- 准备 transformers 开发环境
- (可选)理解理论方面和现有实现
- 实现模型架构
- 实现模型测试
- 提交拉取请求
- (可选)构建演示并与世界分享
1.-3. 准备您的模型贡献
1.选择要转换的模型
让我们从基础知识开始:您需要了解要转换的架构。如果您没有特定的架构,向🤗 Transformers 团队寻求建议是最大化影响的好方法 - 我们将指导您选择在 TensorFlow 方面缺失的最突出的架构。如果您想要在 TensorFlow 中使用的特定模型已经在🤗 Transformers 中具有 TensorFlow 架构实现,但缺少权重,请随时直接转到本页的添加 TensorFlow 权重到 hub 部分。
为简单起见,本指南的其余部分假定您已决定使用 TensorFlow 版本的BrandNewBert(与指南中添加新模型的示例相同)做出贡献。
在开始 TensorFlow 模型架构的工作之前,请仔细检查是否有正在进行的工作。您可以在拉取请求 GitHub 页面上搜索BrandNewBert
以确认是否有与 TensorFlow 相关的拉取请求。
2.准备 transformers 开发环境
选择模型架构后,打开一个草稿 PR 以表示您打算进行工作。按照以下说明设置您的环境并打开草稿 PR。
- 通过单击存储库页面上的“Fork”按钮来分叉存储库。这将在您的 GitHub 用户帐户下创建代码副本。
- 将您的
transformers
分支克隆到本地磁盘,并将基础存储库添加为远程存储库:
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 的可选依赖项数量正在增加,您可能会在此命令中失败。如果是这种情况,请确保安装 TensorFlow,然后执行:
pip install -e ".[quality]"
**注意:**您不需要安装 CUDA。使新模型在 CPU 上运行就足够了。
- 从主分支创建一个具有描述性名称的分支
git checkout -b add_tf_brand_new_bert
- 获取并将当前主分支重新设置为基础
git fetch upstream git rebase upstream/main
- 在
transformers/src/models/brandnewbert/
中添加一个名为modeling_tf_brandnewbert.py
的空.py
文件。这将是您的 TensorFlow 模型文件。 - 使用以下命令将更改推送到您的帐户:
git add . git commit -m "initial commit" git push -u origin add_tf_brand_new_bert
- 一旦您满意了,转到 GitHub 上您的分支的网页。点击“拉取请求”。确保将 Hugging Face 团队的一些成员的 GitHub 句柄添加为审阅者,以便 Hugging Face 团队在未来的更改中收到通知。
- 通过单击 GitHub 拉取请求网页右侧的“转换为草稿”将 PR 更改为草稿。
现在您已经设置了一个开发环境,可以将BrandNewBert移植到🤗 Transformers 中的 TensorFlow 中。
3.(可选)了解理论方面和现有实现
您应该花一些时间阅读BrandNewBert的论文,如果存在这样的描述性工作。论文中可能有一些难以理解的大段内容。如果是这种情况,没关系 - 不要担心!目标不是深入理解论文的理论,而是提取在🤗 Transformers 中使用 TensorFlow 有效重新实现模型所需的必要信息。也就是说,您不必花太多时间在理论方面,而是要专注于实践方面,即现有模型文档页面(例如 BERT 的模型文档)。
在掌握了即将实现的模型的基础知识之后,了解现有的实现是很重要的。这是确认工作实现是否符合您对模型的期望,以及预见 TensorFlow 方面的技术挑战的绝佳机会。
你感到不知所措刚刚吸收了大量信息是非常自然的。在这个阶段,你并不需要理解模型的所有方面。尽管如此,我们强烈鼓励你在我们的论坛中解决任何紧迫的问题。
4. 模型实现
现在是时候开始编码了。我们建议的起点是 PyTorch 文件本身:将modeling_brand_new_bert.py
的内容复制到src/transformers/models/brand_new_bert/
中的modeling_tf_brand_new_bert.py
。本节的目标是修改文件并更新🤗 Transformers 的导入结构,以便你可以成功导入TFBrandNewBert
和TFBrandNewBert.from_pretrained(model_repo, from_pt=True)
,从而加载一个可工作的 TensorFlow BrandNewBert模型。
遗憾的是,没有将 PyTorch 模型转换为 TensorFlow 的规定。但是,你可以遵循我们的一些提示,使这个过程尽可能顺利:
- 将所有类的名称前加上
TF
(例如,BrandNewBert
变为TFBrandNewBert
)。 - 大多数 PyTorch 操作都有直接的 TensorFlow 替代。例如,
torch.nn.Linear
对应于tf.keras.layers.Dense
,torch.nn.Dropout
对应于tf.keras.layers.Dropout
等。如果对特定操作不确定,可以使用TensorFlow 文档或PyTorch 文档。 - 在🤗 Transformers 代码库中寻找模式。如果你遇到某个操作没有直接替代,那么很可能有其他人已经遇到了同样的问题。
- 默认情况下,保持与 PyTorch 相同的变量名称和结构。这将使调试、跟踪问题和添加修复更容易。
- 一些层在每个框架中具有不同的默认值。一个显著的例子是批量归一化层的 epsilon(在PyTorch中为
1e-5
,在TensorFlow中为1e-3
)。务必仔细检查文档! - PyTorch 的
nn.Parameter
变量通常需要在 TF Layer 的build()
中初始化。参见以下示例:PyTorch / TensorFlow - 如果 PyTorch 模型在函数顶部有
#copied from ...
,那么你的 TensorFlow 模型很可能也可以从被复制的架构中借用该函数,假设它有一个 TensorFlow 架构。 - 在 TensorFlow 函数中正确设置
name
属性对于进行from_pt=True
权重交叉加载至关重要。name
几乎总是 PyTorch 代码中相应变量的名称。如果name
没有正确设置,加载模型权重时会在错误消息中看到。 - 基础模型类
BrandNewBertModel
的逻辑实际上将驻留在TFBrandNewBertMainLayer
中,这是一个 Keras 层子类(示例)。TFBrandNewBertModel
将简单地是这个层的包装器。 - Keras 模型需要被构建以加载预训练权重。因此,
TFBrandNewBertPreTrainedModel
将需要保存模型的输入示例,即dummy_inputs
(示例)。 - 如果遇到困难,请寻求帮助 - 我们在这里帮助你!🤗
除了模型文件本身,您还需要添加指向模型类和相关文档页面的指针。您可以完全按照其他 PR 中的模式完成此部分(示例)。以下是所需手动更改的列表:
- 在
src/transformers/__init__.py
中包含 BrandNewBert 的所有公共类 - 在
src/transformers/models/auto/modeling_tf_auto.py
中将 BrandNewBert 类添加到相应的 Auto 类中 - 在
src/transformers/utils/dummy_tf_objects.py
中添加与 BrandNewBert 相关的延迟加载类 - 更新
src/transformers/models/brand_new_bert/__init__.py
中公共类的导入结构 - 在
docs/source/en/model_doc/brand_new_bert.md
中为 BrandNewBert 的公共方法添加文档指针 - 在
docs/source/en/model_doc/brand_new_bert.md
中将自己添加到 BrandNewBert 的贡献者列表中 - 最后,在
docs/source/en/index.md
中 BrandNewBert 的 TensorFlow 列中添加一个绿色勾 ✅
当您对实现感到满意时,请运行以下检查表以确认您的模型架构已准备就绪:
- 在训练时行为不同的所有层(例如 Dropout)都使用
training
参数调用,并且该参数从顶层类一直传播下去 - 在可能的情况下,您已经使用了
#copied from ...
TFBrandNewBertMainLayer
和所有使用它的类都将其call
函数装饰为@unpack_inputs
TFBrandNewBertMainLayer
被装饰为@keras_serializable
- 可以使用
TFBrandNewBert.from_pretrained(model_repo, from_pt=True)
从 PyTorch 权重加载 TensorFlow 模型 - 您可以使用预期的输入格式调用 TensorFlow 模型
5. 添加模型测试
万岁,您已经实现了一个 TensorFlow 模型!现在是添加测试以确保您的模型表现如预期的时候了。与前一节一样,我们建议您首先将 tests/models/brand_new_bert/
中的 test_modeling_brand_new_bert.py
文件复制到 test_modeling_tf_brand_new_bert.py
中,然后继续进行必要的 TensorFlow 替换。目前,在所有 .from_pretrained()
调用中,您应该使用 from_pt=True
标志来加载现有的 PyTorch 权重。
完成后,是真相时刻:运行测试!😬
NVIDIA_TF32_OVERRIDE=0 RUN_SLOW=1 RUN_PT_TF_CROSS_TESTS=1 \ py.test -vv tests/models/brand_new_bert/test_modeling_tf_brand_new_bert.py
最有可能的结果是您会看到一堆错误。不要担心,这是正常的!调试 ML 模型是非常困难的,成功的关键因素是耐心(和 breakpoint()
)。根据我们的经验,最困难的问题来自于 ML 框架之间的微妙不匹配,我们在本指南末尾提供了一些指针。在其他情况下,一般测试可能不直接适用于您的模型,这种情况下,我们建议在模型测试类级别进行覆盖。无论问题是什么,请不要犹豫在您的草稿拉取请求中寻求帮助,如果您遇到困难。
当所有测试通过时,恭喜,您的模型几乎可以添加到 🤗 Transformers 库中了!🎉
6.-7. 确保每个人都可以使用您的模型
6. 提交拉取请求
完成实现和测试后,现在是提交拉取请求的时候了。在推送代码之前,请运行我们的代码格式化工具 make fixup
🪄。这将自动修复任何格式问题,否则会导致我们的自动检查失败。
现在是将草稿拉取请求转换为真正拉取请求的时候了。为此,请点击“准备好审查”按钮,并将 Joao (@gante
) 和 Matt (@Rocketknight1
) 添加为审阅者。模型拉取请求将需要至少 3 名审阅者,但他们会负责为您的模型找到合适的额外审阅者。
在所有审阅者对您的 PR 的状态满意后,最后一个行动点是在 .from_pretrained()
调用中移除 from_pt=True
标志。由于没有 TensorFlow 权重,您将需要添加它们!查看下面的部分以获取如何执行此操作的说明。
最后,当 TensorFlow 权重合并时,你至少有 3 个审阅者的批准,并且所有 CI 检查都是绿色的时候,最后再本地再次检查测试
NVIDIA_TF32_OVERRIDE=0 RUN_SLOW=1 RUN_PT_TF_CROSS_TESTS=1 \ py.test -vv tests/models/brand_new_bert/test_modeling_tf_brand_new_bert.py
我们将合并你的 PR!祝贺你达到的里程碑🎉
7. (可选)构建演示并与世界分享
开源项目中最困难的部分之一是发现。其他用户如何了解你出色的 TensorFlow 贡献的存在?当然是通过适当的沟通!
有两种主要的方法可以与社区分享你的模型:
- 构建演示。这些包括 Gradio 演示、笔记本和其他有趣的方式来展示你的模型。我们强烈鼓励你向我们的社区驱动演示添加一个笔记本。
- 在社交媒体上分享故事,比如 Twitter 和 LinkedIn。你应该为自己的工作感到自豪,并与社区分享你的成就 - 你的模型现在可以被全球数千名工程师和研究人员使用!我们将很乐意转发你的帖子,并帮助你与社区分享你的工作。
将 TensorFlow 权重添加到🤗 Hub
假设 TensorFlow 模型架构在🤗 Transformers 中可用,将 PyTorch 权重转换为 TensorFlow 权重将变得轻而易举!
以下是如何做到这一点:
- 确保你已经在终端中登录到你的 Hugging Face 账户。你可以使用命令
huggingface-cli login
登录(你可以在这里找到你的访问令牌) - 运行
transformers-cli pt-to-tf --model-name foo/bar
,其中foo/bar
是包含你想要转换的 PyTorch 权重的模型存储库的名称 - 在🤗 Hub PR 中标记
@joaogante
和@Rocketknight1
,这是上面命令创建的
就是这样!
跨 ML 框架调试不匹配🐛
在添加新架构或为现有架构创建 TensorFlow 权重时,你可能会遇到关于 PyTorch 和 TensorFlow 之间不匹配的错误。你甚至可能决定打开两个框架的模型架构代码,并发现它们看起来是相同的。发生了什么?
首先,让我们谈谈为什么理解这些不匹配很重要。许多社区成员将直接使用🤗 Transformers 模型,并相信我们的模型表现如预期。当两个框架之间存在较大的不匹配时,这意味着模型至少在一个框架中没有遵循参考实现。这可能导致悄无声息的失败,即模型运行但性能不佳。这可能比根本无法运行的模型更糟!因此,我们的目标是在模型的所有阶段都有小于1e-5
的框架不匹配。
就像其他数值问题一样,魔鬼就在细节中。就像任何注重细节的工艺一样,耐心是秘密的关键。以下是我们建议的工作流程,当你遇到这种类型的问题时:
- 找到不匹配的源头。你要转换的模型可能在某个特定点上有几乎相同的内部变量。在两个框架的架构中放置
breakpoint()
语句,并以自上而下的方式比较数值变量的值,直到找到问题的源头。 - 现在你已经找到了问题的源头,请与🤗 Transformers 团队联系。可能我们之前见过类似的问题,并且可以迅速提供解决方案。作为备选方案,浏览像 StackOverflow 和 GitHub 问题这样的热门页面。
- 如果看不到解决方案,这意味着你将不得不深入研究。好消息是你已经找到了问题所在,所以你可以专注于有问题的指令,将模型的其余部分抽象出来!坏消息是你将不得不深入研究该指令的源实现。在某些情况下,你可能会发现参考实现存在问题 - 不要放弃在上游存储库中开启问题。
在某些情况下,在与🤗 Transformers 团队讨论后,我们可能会发现修复不匹配是不可行的。当模型的输出层中不匹配非常小(但在隐藏状态中可能很大)时,我们可能会决定忽略它,以便分发模型。上面提到的pt-to-tf
CLI 具有一个--max-error
标志,可以在权重转换时覆盖错误消息。
t1`) 添加为审阅者。模型拉取请求将需要至少 3 名审阅者,但他们会负责为您的模型找到合适的额外审阅者。
在所有审阅者对您的 PR 的状态满意后,最后一个行动点是在 .from_pretrained()
调用中移除 from_pt=True
标志。由于没有 TensorFlow 权重,您将需要添加它们!查看下面的部分以获取如何执行此操作的说明。
最后,当 TensorFlow 权重合并时,你至少有 3 个审阅者的批准,并且所有 CI 检查都是绿色的时候,最后再本地再次检查测试
NVIDIA_TF32_OVERRIDE=0 RUN_SLOW=1 RUN_PT_TF_CROSS_TESTS=1 \ py.test -vv tests/models/brand_new_bert/test_modeling_tf_brand_new_bert.py
我们将合并你的 PR!祝贺你达到的里程碑🎉
7. (可选)构建演示并与世界分享
开源项目中最困难的部分之一是发现。其他用户如何了解你出色的 TensorFlow 贡献的存在?当然是通过适当的沟通!
有两种主要的方法可以与社区分享你的模型:
- 构建演示。这些包括 Gradio 演示、笔记本和其他有趣的方式来展示你的模型。我们强烈鼓励你向我们的社区驱动演示添加一个笔记本。
- 在社交媒体上分享故事,比如 Twitter 和 LinkedIn。你应该为自己的工作感到自豪,并与社区分享你的成就 - 你的模型现在可以被全球数千名工程师和研究人员使用!我们将很乐意转发你的帖子,并帮助你与社区分享你的工作。
将 TensorFlow 权重添加到🤗 Hub
假设 TensorFlow 模型架构在🤗 Transformers 中可用,将 PyTorch 权重转换为 TensorFlow 权重将变得轻而易举!
以下是如何做到这一点:
- 确保你已经在终端中登录到你的 Hugging Face 账户。你可以使用命令
huggingface-cli login
登录(你可以在这里找到你的访问令牌) - 运行
transformers-cli pt-to-tf --model-name foo/bar
,其中foo/bar
是包含你想要转换的 PyTorch 权重的模型存储库的名称 - 在🤗 Hub PR 中标记
@joaogante
和@Rocketknight1
,这是上面命令创建的
就是这样!
跨 ML 框架调试不匹配🐛
在添加新架构或为现有架构创建 TensorFlow 权重时,你可能会遇到关于 PyTorch 和 TensorFlow 之间不匹配的错误。你甚至可能决定打开两个框架的模型架构代码,并发现它们看起来是相同的。发生了什么?
首先,让我们谈谈为什么理解这些不匹配很重要。许多社区成员将直接使用🤗 Transformers 模型,并相信我们的模型表现如预期。当两个框架之间存在较大的不匹配时,这意味着模型至少在一个框架中没有遵循参考实现。这可能导致悄无声息的失败,即模型运行但性能不佳。这可能比根本无法运行的模型更糟!因此,我们的目标是在模型的所有阶段都有小于1e-5
的框架不匹配。
就像其他数值问题一样,魔鬼就在细节中。就像任何注重细节的工艺一样,耐心是秘密的关键。以下是我们建议的工作流程,当你遇到这种类型的问题时:
- 找到不匹配的源头。你要转换的模型可能在某个特定点上有几乎相同的内部变量。在两个框架的架构中放置
breakpoint()
语句,并以自上而下的方式比较数值变量的值,直到找到问题的源头。 - 现在你已经找到了问题的源头,请与🤗 Transformers 团队联系。可能我们之前见过类似的问题,并且可以迅速提供解决方案。作为备选方案,浏览像 StackOverflow 和 GitHub 问题这样的热门页面。
- 如果看不到解决方案,这意味着你将不得不深入研究。好消息是你已经找到了问题所在,所以你可以专注于有问题的指令,将模型的其余部分抽象出来!坏消息是你将不得不深入研究该指令的源实现。在某些情况下,你可能会发现参考实现存在问题 - 不要放弃在上游存储库中开启问题。
在某些情况下,在与🤗 Transformers 团队讨论后,我们可能会发现修复不匹配是不可行的。当模型的输出层中不匹配非常小(但在隐藏状态中可能很大)时,我们可能会决定忽略它,以便分发模型。上面提到的pt-to-tf
CLI 具有一个--max-error
标志,可以在权重转换时覆盖错误消息。