模块化 Transformer
transformers
是一个有主见的框架;我们的设计理念在以下概念指南中定义。
该理念的核心体现在库的单模型、单文件方面。该组件的缺点是它限制了工具包中组件从文件到其他文件的继承和导入能力。
因此,模型组件往往在许多文件中重复出现。在 transformers
中定义的注意力层与模型一样多,其中相当一部分彼此相同。不幸的结果是,随着修复和更改应用于代码的特定部分,独立的实现往往会发生分歧。
为了平衡这个问题,我们在整个库中引入了“复制”的概念。通过添加一个注释来表明代码是另一个代码的副本,我们可以通过 CI 和本地命令来强制副本不发生分歧。然而,虽然复杂性较低,但这通常非常繁琐。
最后,这导致为贡献模型增加了大量的额外负担,我们希望消除这种负担。这种方法通常要求模型贡献者添加建模代码(约 1k 行)、处理器(约 500 行)、测试、文档等。模型贡献的 PR 很少少于 3-5k 行代码,其中大部分代码是样板代码。
这提高了贡献的门槛,而通过模块化 Transformer,我们的目标是将门槛降低到更可接受的水平。
它是什么?
模块化 Transformers 引入了模型文件夹中“模块化”文件的概念。此模块化文件接受建模/处理文件中通常不接受的代码,因为它允许从相邻模型导入以及从类继承到其他类。
此模块化文件定义了模型、处理器和配置类,否则这些类将在各自的模块中定义。
最后,此功能引入了一个新的linter
,它将“解开”模块化文件,将其转换为“单个模型,单个文件”的目录结构。这些文件将在每次运行脚本时自动生成;减少了对模块化文件的必要贡献,因此仅限于贡献的模型与其他模型之间的更改。
模型用户最终将导入并使用单文件接口,因此此处无需更改。通过这样做,我们希望将两者的优势结合起来:在保持我们理念的同时,实现简单的贡献。
因此,这取代了# Copied from
标记,并且预计未来几个月内先前贡献的模型将迁移到新的模块化 Transformers 格式。
细节
“linter”会解开继承并从模块化文件创建所有单文件,它会在尝试对 Python 用户保持透明的同时展平继承。目前,linter 展平了**单层**继承。
例如
- 如果一个配置类继承自另一个类并添加/删除了一个参数,则生成的类文件将直接引用它(如果添加)或完全删除它(如果删除)。
- 如果一个类继承自另一个类,例如:class GemmaModel(LlamaModel):,则依赖关系会自动推断。所有子模块都将从超类自动推断。
您应该能够在此modular
文件中编写所有内容(标记器、图像处理器、模型、配置),相应的类文件将为您创建。
执行
[待办] 我们正在引入一个新的测试,以确保生成的内容与modular_xxxx.py
中存在的内容相匹配。
示例
以下是一个使用 BERT 和 RoBERTa 的快速示例。这两个模型密切相关:它们的建模实现仅在嵌入层方面有所不同。
无需完全重新定义模型,以下是建模和配置类(为了示例的简洁性,目前忽略了标记器,因为它们差异很大)的modular_roberta.py
文件的样子。
from torch import nn
from ..bert.configuration_bert import BertConfig
from ..bert.modeling_bert import (
BertModel,
BertEmbeddings,
BertForMaskedLM
)
# The RoBERTa config is identical to BERT's config
class RobertaConfig(BertConfig):
model_type = 'roberta'
# We redefine the embeddings here to highlight the padding ID difference, and we redefine the position embeddings
class RobertaEmbeddings(BertEmbeddings):
def __init__(self, config):
super().__init__(config())
self.padding_idx = config.pad_token_id
self.position_embeddings = nn.Embedding(
config.max_position_embeddings, config.hidden_size, padding_idx=self.padding_idx
)
# The RoBERTa model is identical to the BERT model, except for the embedding layer.
# We redefine the embeddings above, so here there is no need to do additional work
class RobertaModel(BertModel):
def __init__(self, config):
super().__init__(config)
self.embeddings = RobertaEmbeddings(config)
# The heads now only need to redefine the model inside to the correct `RobertaModel`
class RobertaForMaskedLM(BertForMaskedLM):
def __init__(self, config):
super().__init__(config)
self.model = RobertaModel(config)
请注意,如果您没有使用您定义的依赖项,则会收到以下错误
ValueError: You defined `RobertaEmbeddings` in the modular_roberta.py, it should be used
when you define `BertModel`, as it is one of it's direct dependencies. Make sure
you use it in the `__init__` function.
此外,您可以在此处找到示例列表
它不是什么
它不是建模代码的替代品(至少目前不是),如果您的模型不基于任何其他曾经存在的东西,那么您可以像往常一样添加一个modeling
文件。