重要提示

您正在查看 NeMo 2.0 文档。此版本引入了 API 的重大更改和一个新的库 NeMo Run。我们目前正在将 NeMo 1.0 中的所有功能移植到 2.0。有关先前版本或 2.0 中尚不可用的功能的文档,请参阅 NeMo 24.07 文档

Adapters#

在 NeMo 中,我们经常训练模型并针对特定任务对其进行微调。当模型只有几百万个参数时,这是一种合理的方法。然而,当参数接近数亿甚至数十亿时,这种方法很快变得不可行。作为这种情况的潜在解决方案(微调大型模型不再可行),我们转向 Adapters [[adapters1]] 以使我们的模型专门用于特定领域或任务。Adapters 需要的参数总数仅为原始模型的一小部分,并且微调效率更高。

注意

有关向任何 PyTorch 模块添加 Adapter 支持的详细教程,请参阅 NeMo Adapters 教程

什么是 Adapters?#

Adapters 是一个简单的概念 - 下图显示了一种公式。最简单的形式,它们是残差前馈层,将输入维度 (\(D\)) 压缩到小的瓶颈维度 (\(H\)),使得 \(R^D \text{->} R^H\),计算激活函数(例如 ReLU),最后使用另一个前馈层映射 \(R^H \text{->} R^D\)。然后,此输出通过简单的残差连接添加到输入中。

像这样的 Adapter 模块通常会初始化,使得 adapter 的初始输出始终为零,以防止由于添加此类模块而导致原始模型性能下降。

带有 Adapters 的 torch.nn.Module#

在 NeMo 中,Adapters 通过 Mixin 类支持,该类可以附加到任何 torch.nn.Module。这样的模块将具有多个附加方法,这些方法将在此模块中启用 adapter 功能。

# Import the adapter mixin from NeMo
from nemo.core import adapter_mixins

# NOTE: See the *two* classes being inherited here !
class MyModule(torch.nn.Module, adapter_mixins.AdapterModuleMixin):
    pass

AdapterModuleMixin#

让我们看看 AdapterModuleMixin 为通用 PyTorch 模块添加了什么。下面列出了一些最重要的必需方法

  1. add_adapter:用于向模块添加具有唯一名称的 adapter。

  2. get_enabled_adapters:返回所有已启用 adapter 模块的名称列表。

  3. set_enabled_adapters:设置是启用还是禁用单个(或所有)adapter。

  4. is_adapter_available:检查是否有任何 adapter 可用且已启用。

扩展此 mixin 的模块通常可以直接使用这些方法,而无需扩展它们,但我们将在下面介绍您可能希望扩展这些方法的情况。

class nemo.core.adapter_mixins.AdapterModuleMixin#

基类:ABC

通用 Adapter Mixin,可以使用 Adapter 模块支持来增强任何 torch.nn.Module。

此 mixin 类添加了一种分层方式,可将任何类型的 Adapter 模块添加到预先存在的模块。由于模型本质上也是 nn.Module,因此可以将此 mixin 附加到任何模型或模块。此 mixin 类添加了多个实用程序方法,这些方法可以根据需要使用或覆盖。

Adapter 模块是任何具有以下几个属性的 Pytorch nn.Module

  • 它的输入和输出维度相同,而隐藏维度不必相同。

  • Adapter 模块的最后一层已初始化为零,以便与 adapter 的残差连接

    产生原始输出。

此 mixin 将以下实例变量添加到继承它的类中

  • adapter_layer:一个 torch.nn.ModuleDict(),其键是 adapter 的名称(全局唯一),

    值是 Adapter nn.Module()。

  • adapter_cfg:一个 OmegaConf DictConfig 对象,其中包含已初始化的 adapter 的配置。

  • adapter_name:一个 str 解析的名称,它是全局唯一的键,但多个模块可以共享

    此名称。

  • adapter_global_cfg_key:一个 str,表示模型配置中可由用户提供的键。

    该值解析为 global_cfg,可以通过 model.cfg.adapters.global_cfg.* 覆盖。

  • adapter_metadata_cfg_key:一个 str,表示模型配置中用于保留

    adapter 配置的元数据的键。

注意

此模块负责维护其配置。子类必须确保根据需要更新或保留配置。子类有责任将最新的配置传播到较低层。

adapter_global_cfg_key = 'global_cfg'#
adapter_metadata_cfg_key = 'adapter_meta_cfg'#
add_adapter(
name: str,
cfg: omegaconf.DictConfig | AdapterConfig,
**kwargs,
)#

向此模块添加 Adapter 模块。

参数:
  • name – adapter 的全局唯一名称。将用于访问、启用和禁用 adapter。

  • cfg – 包含最基本 __target__ 以实例化新 Adapter 模块的 DictConfig 或 Dataclass。

is_adapter_available() bool#

检查是否已实例化任何 Adapter 模块。

返回:

bool,确定是否已实例化任何 Adapter 模块。即使 adapter 已启用或禁用,也返回 true,仅当不存在 adapter 时才返回 false。

set_enabled_adapters(
name: str | None = None,
enabled: bool = True,
)#

更新了内部 adapter 配置,确定是启用还是禁用 adapter(或所有 adapter)。

常见的用户模式是禁用所有 adapter(在添加它们之后或恢复带有预先存在的 adapter 的模型之后),然后仅启用其中一个 adapter。

module.set_enabled_adapters(enabled=False)
module.set_enabled_adapters(name=<some adapter name>, enabled=True)
参数:
  • name – 可选 str。如果给定 str 名称,则配置将更新为 enabled 的值。如果未给定名称,则将启用/禁用所有 adapter。

  • enabled – Bool,确定是否启用/禁用 adapter。

get_enabled_adapters() List[str]#

返回所有已启用 adapter 名称的列表。名称将始终是已解析的名称,不包含模块信息。

返回:

每个已启用 adapter 名称的 str 名称列表。

get_adapter_module(name: str)#

如果可能,按名称获取 adapter 模块,否则返回 None。

参数:

name – 与 Adapter 对应的 str 名称(已解析或未解析)。

返回:

如果可以解析和匹配名称,则为 nn.Module,否则为 None/

get_adapter_cfg(name: str)#

get_adapter_module 相同的逻辑,但用于获取配置

set_accepted_adapter_types(
adapter_types: List[type | str],
) None#

具有此 mixin 的模块可以定义它将接受的 adapter 名称列表。应在模块的 init 方法中调用此方法,并设置模块期望添加的 adapter 名称。

参数:

adapter_types – 与类对应的 str 路径列表。将实例化类路径以确保类路径正确。

get_accepted_adapter_types() Set[type]#

用于获取模块接受的所有类集的实用程序函数。

返回:

返回接受的 adapter 类型集(作为类),否则返回空集。

unfreeze_enabled_adapters(
freeze_batchnorm: bool = True,
) None#

用于仅解冻已启用 Adapter 模块的实用程序方法。

常见的用户模式是冻结所有模块(包括所有 adapter),然后仅解冻所需的 adapter。

module.freeze()  # only available to nemo.core.NeuralModule !
module.unfreeze_enabled_adapters()
参数:

freeze_batchnorm – 冻结任何和所有 BatchNorm*D 层的移动平均缓冲区更新的可选(和推荐)做法。这对于确保禁用所有 adapter 将精确地产生原始(基础)模型的输出是必要的。

forward_enabled_adapters(input: torch.Tensor)#

使用提供的输入逐个转发所有活动 adapter,并将每个 adapter 层的输出链接到下一个。

在计算 adapter 的输出以及该输出如何与原始输入合并时,利用每个 adapter 的隐式合并策略。

参数:

input – 调用模块的输出张量是第一个 adapter 的输入,其输出然后链接到下一个 adapter,直到消耗完所有 adapter。

返回:

所有活动 adapter 完成其前向传递后的结果张量。

resolve_adapter_module_name_(
name: str,
) Tuple[str, str]#

用于将给定的全局/模块 adapter 名称解析为其组件的实用程序方法。始终返回表示(module_name,adapter_name)的元组。“:”用作分隔符,用于表示模块名称与 adapter 名称。

如果元数据配置存在以供访问,则还将尝试将给定的 adapter_name 单独解析回(module_name,adapter_name)。

参数:

name – 全局 adapter 或模块 adapter 名称(结构为 module_name:adapter_name)。

返回:

表示(module_name,adapter_name)的元组。如果提供全局 adapter,则 module_name 设置为 ''。

forward_single_enabled_adapter_(
input: torch.Tensor,
adapter_module: torch.nn.Module,
*,
adapter_name: str,
adapter_strategy: nemo.core.classes.mixins.adapter_mixin_strategies.AbstractAdapterStrategy,
)#

对某些输入数据执行单个 adapter 模块的前向步骤。

注意

子类可以覆盖此方法以适应更复杂的 adapter 前向步骤。

参数:
  • input – input:调用模块的输出张量是第一个 adapter 的输入,其输出然后链接到下一个 adapter,直到消耗完所有 adapter。

  • adapter_module – 当前需要执行前向传递的 adapter 模块。

  • adapter_name – 正在进行当前前向传递的 adapter 的已解析名称。

  • adapter_strategyAbstractAdapterStrategy 的子类,它确定 adapter 的输出应如何与输入合并,或者是否应合并。

返回:

当前活动 adapter 完成其前向传递后的结果张量。

check_supported_adapter_type_(
adapter_cfg: omegaconf.DictConfig,
supported_adapter_types: Iterable[type] | None = None,
)#

用于检查 adapter 模块是否为模块支持的类型的实用程序方法。

子类应调用此方法以确保 adapter 模块是受支持的类型。

使用 Adapter 模块#

现在 MyModule 支持 adapter,我们可以轻松添加 adapter、设置其状态、检查它们是否可用并执行其前向传递。请注意,如果启用了多个 adapter,则会以链式方式调用它们,前一个 adapter 的输出将作为输入传递给下一个 adapter,依此类推。

# Import the adapter mixin and modules from NeMo
import torch
from nemo.core import adapter_mixins
from nemo.collections.common.parts import adapter_modules

class MyModule(torch.nn.Module, adapter_mixins.AdapterModuleMixin):

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        output = self.layers(x)  # assume self.layers is some Sequential() module

        if self.is_adapter_available():  # check if adapters were added or not
            output = self.forward_enabled_adapters()  # perform the forward of all enabled adapters in a chain

        return output

# Now let us create a module, add an adapter and do a forward pass with some random inputs
module = MyModule(dim)  # assume dim is some input and output dimension of the module.

# Add an adapter
module.add_adapter("first_adapter", cfg=adapter_modules.LinearAdapter(in_features=dim, dim=5))

# Check if adapter is available
module.is_adapter_available()  # returns True

# Check the name(s) of the enabled adapters
module.get_enabled_adapters()  # returns ['first_adapter']

# Set the state of the adapter (by name)
module.set_enabled_adapters(name="first_adapter", enabled=True)

# Freeze all the weights of the original module  (equivalent to calling module.freeze() for a NeuralModule)
for param in module.parameters():
    param.requires_grad = False

# Unfreeze only the adapter weights (so that we finetune only the adapters and not the original weights !)
module.unfreeze_enabled_adapters()

# Now you can train this model's adapters !
input_data = torch.randn(4, dim)  # assume dim is the input-output dim of the module
outputs_with_adapter = module(input_data)

# Compute loss and backward ...

Adapter 兼容模型#

如果目标是在单个模块中支持 adapter,则目标已完成。然而,在现实世界中,我们使用多个模块构建大型复合模型,并将它们组合起来以构建最终模型,然后我们对其进行训练。我们使用 AdapterModelPTMixin 来做到这一点。

注意

有关支持分层 adapter 模块的深入指南,请参阅 NeMo Adapters 教程

class nemo.core.adapter_mixins.AdapterModelPTMixin#

基类:AdapterModuleMixin

Adapter Mixin,可以使用 Adapter 支持来增强 ModelPT 子类。

此 mixin 类应仅与顶级 ModelPT 子类一起使用。此 mixin 类添加了多个实用程序方法,这些方法应进行子类化和覆盖,以便根据需要传播到子模块。

Adapter 模块是任何具有以下几个属性的 Pytorch nn.Module

  • 它的输入和输出维度相同,而隐藏维度不必相同。

  • Adapter 模块的最后一层已初始化为零,以便与 adapter 的残差连接

    产生原始输出。

此 mixin 将以下实例变量添加到继承它的类中

  • adapter_layer:一个 torch.nn.ModuleDict(),其键是 adapter 的名称(全局唯一),

    值是 Adapter nn.Module()。

  • adapter_cfg:一个 OmegaConf DictConfig 对象,其中包含已初始化的 adapter 的配置。

  • adapter_global_cfg_key:一个 str,表示模型配置中可由用户提供的键。该值解析为 global_cfg,可以通过 model.cfg.adapters.global_cfg.* 覆盖。

注意

此模块负责维护其配置。在 ModelPT 级别,它将访问 Adapter 配置信息并将其写入 self.cfg.adapters

setup_adapters()#

在 ASR ModelPT 实现构造函数中调用的实用程序方法,以便恢复先前添加的任何 adapter。

应由子类覆盖以执行其他必要的设置步骤。

此方法应在构造函数时仅调用一次。

add_adapter(
name: str,
cfg: omegaconf.DictConfig | AdapterConfig,
)#

向此模型添加 Adapter 模块。

应由子类覆盖,并且必须使用 super() 调用 - 这将设置配置。调用 super() 后,将此调用转发到实现 mixin 的模块。

参数:
  • name – adapter 的全局唯一名称。将用于访问、启用和禁用 adapter。

  • cfg – 包含最基本 __target__ 以实例化新 Adapter 模块的 DictConfig。

is_adapter_available() bool#

检查是否已实例化任何 Adapter 模块。

应由子类覆盖。

返回:

bool,确定是否已实例化任何 Adapter 模块。即使 adapter 已启用或禁用,也返回 true,仅当不存在 adapter 时才返回 false。

set_enabled_adapters(
name: str | None = None,
enabled: bool = True,
)#

更新了内部 adapter 配置,确定是启用还是禁用 adapter(或所有 adapter)。

常见的用户模式是禁用所有 adapter(在添加它们之后或恢复带有预先存在的 adapter 的模型之后),然后仅启用其中一个 adapter。

应由子类覆盖,并且必须使用 super() 调用 - 这将设置配置。调用 super() 后,将此调用转发到实现 mixin 的模块。

model.set_enabled_adapters(enabled=False)
model.set_enabled_adapters(name=<some adapter name>, enabled=True)
参数:
  • name – 可选 str。如果给定 str 名称,则配置将更新为 enabled 的值。如果未给定名称,则将启用/禁用所有 adapter。

  • enabled – Bool,确定是否启用/禁用 adapter。

get_enabled_adapters() List[str]#

返回所有已启用 adapter 的列表。

应由子类实现。

返回:

每个已启用 adapter 的 str 名称列表。

check_valid_model_with_adapter_support_()#

用于测试此 mixin 的子类是否为 ModelPT 本身的适当子类的实用程序方法。

应由子类实现。

save_adapters(
filepath: str,
name: str | None = None,
)#

用于仅保存 adapter 模块,而不保存整个模型本身的实用程序方法。这允许共享 adapter,adapter 通常只是完整模型大小的一小部分,从而更容易交付。

注意

保存的文件是 pytorch 兼容的 pickle 文件,其中包含 adapter 的状态字典以及 adapter 配置的二进制表示形式。

参数:
  • filepath – str 文件路径,其中 .pt 文件将包含 adapter 状态字典。

  • name – 将保存到此文件的 adapter 的可选名称。如果未传递 None,则所有 adapter 将保存到该文件。名称可以是全局名称 (adapter_name) 或模块级名称 (module:adapter_name)。

load_adapters(
filepath: str,
name: str | None = None,
map_location: str | None = None,
strict: bool = True,
)#

用于仅恢复 adapter 模块,而不恢复整个模型本身的实用程序方法。这允许共享 adapter,adapter 通常只是完整模型大小的一小部分,从而更容易交付。

注意

在恢复期间,假定模型当前尚不具有具有名称(如果提供)的 adapter,或者任何与状态字典的模块共享名称的 adapter(如果未提供名称)。这是为了确保每个 adapter 名称在模型中都是全局唯一的。

参数:
  • filepath – .pt 文件的文件路径。

  • name – 将保存到此文件的 adapter 的可选名称。如果未传递 None,则所有 adapter 将保存到该文件。名称必须是全局名称 (adapter_name) 或模块级名称 (module:adapter_name),以完全匹配状态字典。

  • map_location – Pytorch 标志,用于放置 adapter 状态字典。

  • strict – Pytorch 标志,指示是否严格加载 adapter 的权重。

update_adapter_cfg(cfg: omegaconf.DictConfig)#

用于使用提供的配置递归更新所有 Adapter 模块配置的实用程序方法。

注意

它不是(深)复制,而是引用复制。对配置所做的更改将反映到 adapter 子模块,但仍然鼓励使用此方法显式更新 adapter_cfg。

参数:

cfg – 包含 model.cfg.adapters 值的 DictConfig。

replace_adapter_compatible_modules(
update_config: bool = True,
verbose: bool = True,
)#

用于替换所有带有 Adapter 变体的子模块(如果存在)的实用程序方法。不递归遍历子模块的子模块(仅限直接子模块)。

参数:
  • update_config – 确定是否应更新配置的标志。

  • verbose – 确定方法是否应记录所做更改的标志。

property adapter_module_names: List[str]#

模型支持的有效适配器模块的列表。

注意

子类应该重写此属性并返回一个 str 名称列表,其中包含它们支持的所有模块,这将使用户能够确定适配器模块的放置位置。

返回:

一个 str 列表,每个元素对应一个受支持的适配器模块。默认情况下,子类应支持“默认适配器”(‘’)。

property default_adapter_module_name: str | None#

如果提供了 ‘’ 名称,则用作“默认”的适配器模块的名称。

注意

子类应该重写此属性并返回一个 str 名称,表示他们希望指定为默认值的模块。

返回:

模块的 str 名称,表示为“默认”适配器;如果为 None,则表示不支持默认适配器。

下面,我们将讨论适配器兼容模型的一些有用功能。

  1. 保存 恢复 具有 适配器 功能的 模型:任何正确实现此 NeMo 模型的类都可以保存和恢复具有适配器功能的 NeMo 模型,从而允许共享适配器。

  2. save_adaptersload_adapters:适配器通常只包含少量参数,因此无需为每个适配器复制整个模型。此方法允许将适配器模块与模型分开存储,以便您可以使用相同的“基础”模型,并仅共享适配器模块。

参考#

[adapters1]

Neil Houlsby, Andrei Giurgiu, Stanislaw Jastrzebski, Bruna Morrone, Quentin De Laroussilhe, Andrea Gesmundo, Mona Attariyan, and Sylvain Gelly. Parameter-efficient transfer learning for nlp. In International Conference on Machine Learning, 2790–2799. PMLR, 2019.