添加适配器方法

本文档描述了如何将不同的高效微调方法集成到adapters代码库中。 它可作为添加新的高效微调/适配器方法的指南。

在我们深入实现细节之前,首先介绍一些关于adapters的重要设计理念:

  • 适配器应无缝集成到现有模型类中: 这意味着 (a) 如果模型架构支持适配器,则该架构的所有模型类都应能使用适配器;(b) 适配器应完全可选,即模型类在不使用适配器时仍必须能正常工作。

  • 原始代码的复制应尽可能少: adapters 尽量避免了复制原始HF代码。我们广泛使用Python混入类来实现这一点。

现在我们将重点介绍将适配器方法集成到Transformer模型中的最关键组件。 每个集成高度依赖于适配器方法的具体细节。 因此,描述的步骤可能并不适用于每个实现。

实现

❓ 由于适配器方法通常会在现有的Transformer模型中注入新的参数块,它们大多可以通过继承自torch.nn.Module的多个类块来实现。 这些模块类随后需要被插入到Transformer模型实现中的正确位置。 因此,每种适配器方法的实现至少应提供两个类:

  • 一个继承自AdapterConfig的配置类,为该方法的所有配置选项提供属性

  • 一个继承自抽象类AdapterLayerBase的模块类,提供方法参数和一组标准适配器管理功能

    • 支持adapter组合的模块应改为从ComposableAdapterLayerBase派生

配置

所有配置类都位于src/adapters/configuration/adapter_config.py中。

  • 要为新方法添加新的配置类,请创建AdapterConfig的新子类。确保在类中设置architecture属性。

  • 最后,还需确保将配置类添加到src/adapters目录下的__init__.py文件中。

建模

所有适配器方法的实现都位于src/adapters/methods中。

对于不支持组合的方法

所有新适配器模块都应继承的AdapterLayerBase类位于src/adapters/methods/adapter_layer_base.py文件中。

  • 这个抽象基类定义了一组应由每个派生类实现的方法,包括添加、启用和删除适配器权重的方法。这些方法在基类中被标记为抽象方法。详情请参阅AdapterLayerBase

  • 然而最重要的是,从这个基类派生的模块类应该实现通过适配组件的正向传递。

  • 这些类的具体实现很大程度上取决于适配器方法的细节。

对于支持组合的方法

ComposableAdapterLayerBase类(作为AdapterLayerBase的子类)位于src/adapters/methods/adapter_layer_base.py文件中,为实现适配器组合提供了基础框架。

  • 您的派生模块类首先需要实现AdapterLayerBase要求的所有方法。详情请参阅上文相关章节。

  • 对于适配器组合,预先实现的compose()方法是主要入口点。该方法应在适配器模块的前向传递过程中调用。

  • compose() 期望接收一个 state 对象,这是由您的适配器方法定义的通用命名元组对象。该状态对象应包含适配器实现所需的所有张量(如隐藏状态、注意力掩码等)和状态属性。示例可参考 BottleneckState

  • 特定组合块的实现在以compose_开头的方法中提供。部分组合块提供通用默认实现,若需支持某些组合块则必须由派生类实现。请确保在派生模块的supported_compositions类属性中列出所有支持的组合块。

  • 无论如何,任何派生模块都应实现一小部分辅助方法来支持基本的组合逻辑。这些方法在ComposableAdapterLayerBase中被标记为抽象方法,目前包括以下内容:vslice()、pad_and_concat()、repeat()、mean()、compose_single()。详情请参阅ComposableAdapterLayerBase

如需参考实现,请查看BottleneckLayer中的瓶颈适配器。

适用于所有方法

为了实际利用新实现的类,最终需要在模型的实际实现中集成对这些模块的前向调用。

  • 这同样高度依赖于适配器方法与基础模型类的交互方式。通常,模块类可以通过混入(参见src/adapters/models中以"mixin"开头的模块)或直接作为相应模型组件的子模块进行集成。

  • The model class integration has to be repeated for each supported Transformer model, as they typically don’t share a codebase. At this point it is often important to consider where the adapters need to be added to the transformer model and whether there is an implementation that does not require more copying of classes than the current implementation. Please try to integrate any new adapter method into every model class when it’s reasonable. You can find all currently supported model classes at https://docs.adapterhub.ml/model_overview.html.

其他需要考虑的事项

  • 新的适配器方法通常也需要对src/adapters/loading.py中的AdapterLoader类进行一些修改(另见此处)。

  • 根据要集成的方法,可能需要对其他类进行进一步的修改。

测试

adapters 提供了一个框架,用于在tests中测试实现模型的适配器方法。 每个适配器方法的测试通过mixin类提供。 所有测试mixin类都继承自通用的AdapterMethodBaseTestMixin类,并位于tests/methods目录中。

📝 步骤

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

    • 该模块应包含一个从AdapterMethodBaseTestMixin派生的TestMixin类,该类实现了添加、加载和训练新适配器方法模块的典型方法。

    • 参考现有的测试混合类。

  • 接下来,将新实现的测试混入添加到所有支持新适配器方法的模型类型的测试中。

    • 每种模型类型都有其对应的测试类tests/test_.py,其中包含一个AdapterTest类。 将新的测试mixin添加到该类的mixin中。 例如,如果BERT支持新方法,则将其测试mixin添加到BertAdapterTest中。

文档

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

📝 步骤

  • docs/classes/adapter_config.rst中添加新方法的配置类文档。

  • docs/overview.md 文件中,为新的适配器方法添加一个新章节,描述最重要的概念。请尽量遵循现有方法的通用格式。

  • docs/model_overview.md表格中添加新列,并勾选支持新适配器方法的模型。

最后,请在本仓库主README.md文件已实现方法章节下的支持方法表格中,为新方法添加一行。

训练示例适配器

❓ 为确保新适配器实现正常工作,训练一些示例适配器并将训练结果与完整模型微调和/或参考实现进行比较非常有用。 理想情况下,这应包括在一个(或多个)适合展示新方法的任务上训练适配器,并将它们上传到AdapterHub。

Hugging Face已经为许多任务提供了示例训练脚本,其中部分脚本已修改以支持适配器训练(参见https://github.com/Adapter-Hub/adapters/tree/main/examples)。