向模型添加适配器

重要

对于大多数使用场景,通过新的适配器插件接口支持新模型架构会简单得多。 详情请查看Custom Models

本文档概述了如何通过adapters支持Hugging Face Transformers的新模型架构。 在深入实现细节之前,您应该先熟悉adapters的主要设计理念:

  • 适配器应与现有模型类无缝集成:如果某个模型架构支持适配器,那么该架构的所有模型类都应该能够使用它们。

  • 复制的代码应尽可能精简: adapters 广泛使用Python混入类来为HF模型添加适配器支持。无法通过混入类充分修改的函数会被复制后再修改。请尽量避免复制函数。

相关类

为现有模型架构添加适配器支持需要修改模型前向传播逻辑的某些部分。这些修改通过src/adapters/models//目录中的四个文件实现。让我们以BERT为例来了解这些文件的作用。需要注意的是,我们正在适配原始Hugging Face模型,该模型实现在transformers/models/bert/modeling_bert.py中。src/adapters/models/bert/中的文件包括:

  1. src/adapters/models/bert/mixin_bert.py: 该文件包含我们需要修改的每个类的混入(mixin)。例如,在BertSelfAttention类中,我们需要为LoRA和Prefix Tuning进行修改。为此,我们创建了一个BertSelfAttentionAdaptersMixin来实现这些更改。我们将在下面详细讨论其工作原理。

  2. src/adapters/models/bert/modeling_bert.py: 对于BERT实现中的某些类(例如BertModelBertLayer),可以通过混入类进行充分定制。而对于其他类(如BertSelfAttention),我们需要直接编辑原始代码。这些类被复制到src/adapters/models/bert/modeling_bert.py并进行修改。

  3. src/adapters/models/bert/adapter_model.py: 该文件定义了适配器模型类。这个类允许灵活添加和切换不同类型多个预测头。每个模型的实现大致相同,区别仅在于各模型具有不同的预测头,因此会有不同的add_..._head()函数。

  4. src/adapters/models/bert/__init__.py: 定义Python的导入结构。

实现步骤 📝

既然我们已经讨论了src/adapters/models//目录下每个文件的作用,现在我们将逐步讲解如何将适配器集成到现有模型架构中。请注意,以下步骤可能并不适用于所有模型架构。

  1. 文件:

    • 创建src/adapters/models//目录,并在其中创建4个文件:mixin_.pymodeling_.pyadapter_model.py__init__.py

  2. 混入类:

    • src/adapters/models//mixin_.py中,为您想要修改且无法从其他类复用现有mixin的任何类创建mixin。

      • 要确定需要修改哪些类,请思考在何处插入LoRA、Prefix Tuning和瓶颈适配器。

      • 您可以参考类似的模型实现作为指导。

      • 通常,可以复用其他类的现有混合器。例如,BertLayerRobertaLayerXLMRobertaLayerDebertaLayerDebertaV2LayerBertGenerationLayer(所有基于BERT派生的模型)都使用BertLayerAdaptersMixin

    • 为了额外支持前缀调优,需要在相应的注意力层中对PrefixTuningLayer模块应用前向调用(关于如何修改Hugging Face类的代码,请参阅步骤3)。

    • 确保在正确的位置添加对bottleneck_layer_forward()的调用。

    • 整个基础模型类(例如BertModel)的mixin应该继承自ModelBaseAdaptersMixin,并且(如果可能的话)EmbeddingAdaptersMixin和/或InvertibleAdaptersMixin。这个mixin至少需要实现iter_layers()方法,但根据架构可能还需要额外的修改。

      • 如果模型是不同模型的组合,例如EncoderDecoderModel,请使用ModelUsingSubmodelsAdaptersMixin而不是ModelBaseAdaptersMixin

  3. 已复制的函数:

    • 对于那些仅靠混入类无法实现所需行为的类,您必须:

    • src/adapters/models//modeling_.py中创建一个名为WithAdapters的新类。该类应从相应的mixin和HF类派生。

    • 将你想要修改的函数复制到这个类中并进行修改。

      • 例如,BertSelfAttention类的forward方法需要被适配以支持前缀调优。因此我们创建了一个类BertSelfAttentionWithAdapters(BertSelfAttentionAdaptersMixin, BertSelfAttention),将forward方法复制到其中并进行修改。

      • 如果复制并修改了模块的forward方法,请确保在模块的init_adapters()方法中调用adapters.utils.patch_forward()。这能确保适配器与accelerate包正常工作。

  4. 修改 MODEL_MIXIN_MAPPING

    • 对于每个未被复制到modeling_.py中的mixin类,将该mixin/类组合添加到文件src/adapters/models/__init__.py中的MODEL_MIXIN_MAPPING里。

  5. 创建适配器模型:

    • 支持Adapter的架构应提供一个新的模型类AdapterModel。该类允许灵活添加和切换不同类型多个预测头。

    • 这是在adapter_model.py文件中完成的:

      • 该模块应实现AdapterModel类,继承自ModelWithFlexibleHeadsAdaptersMixinPreTrainedModel

      • 在模型类中,为那些适用于新模型架构的预测头添加方法。

      • 再次查看现有的实现。

    • AdapterModel添加到src/adapters/models/auto/adapter_model.py中的ADAPTER_MODEL_MAPPING_NAMES映射,以及src/adapters/__init__.py文件中。

    • src/adapters/models//__init__.py中定义要添加到Python导入结构的类。这可能仅包括AdapterModel

  6. 调整配置类:

    • 调整配置类以满足src/adapters/wrappers/configuration.py中适配器的要求。

    • 不同模型架构的配置属性存在一些命名差异。适配器实现需要一些具有特定名称的额外属性可用。目前这些属性包括num_attention_headshidden_sizehidden_dropout_probattention_probs_dropout_prob,与BertConfig类中的定义相同。如果您的模型配置未提供这些属性,请向CONFIG_CLASS_KEYS_MAPPING添加相应的映射。

额外(可选)实现步骤 📝

  • 通过Parallel组合块实现并行适配器推理(参见文档PR#150)。

  • 将架构现有的(静态)预测头映射到adapters灵活头(参见实现)。

测试

❓ 除了通用的Hugging Face模型测试外,还有适配器特定的测试用例。所有测试都是从tests文件夹执行的。您需要添加两个不同的测试类。

📝 步骤

  1. tests/目录下添加一个新的test_.py模块

    • 此文件用于测试与适配器使用相关的所有功能(添加、移除、激活等)是否正常工作。

    • 该模块通常包含2个测试类和一个测试基类:

      • AdapterTestBase: 该类包含 tokenizer_name, config_classconfig

      • AdapterTest 继承自一组测试混合类,这些混合类包含各种适配器测试(具体取决于实现)。

      • (可选) ClassConversionTest 如果实现了预测头的转换,则运行正确类转换的测试。

  2. tests/models/目录下添加一个新的test_.py模块

    • 此文件用于测试AdapterModel类。

    • 该模块通常包含一个名为AdapterModelTest的测试类

      • AdapterModelTest 直接继承自 Hugging Face 现有的模型测试类 ModelTest,并添加 AdapterModel 作为待测试的类。

文档

❓ 关于adapters的文档存放在docs文件夹中。

📝 步骤

  • 添加 docs/classes/models/.rst(参考HF文档中的文档文件)。确保包含 AdapterModel 的自动文档。最后在 index.rst 中列出该文件。

  • 在概览页面的模型表格docs/model_overview.md中为模型添加新行,列出新模型实现的所有方法。

训练示例适配器

❓ 为确保新适配器实现正常工作,训练一些示例适配器并将训练结果与完整模型微调进行比较非常有用。理想情况下,这应包括在一个(或多个)适合展示新模型架构的任务上训练适配器(例如BERT的GLUE基准测试,BART的摘要生成任务),并将它们上传至AdapterHub。

我们在此提供多种任务的训练脚本:https://github.com/Adapter-Hub/adapters/tree/main/examples/pytorch/