预测头
本节概述了如何将不同的预测头与适配器模块结合使用,以及如何在AdapterHub中同时分发预训练适配器及匹配的预测头。
我们将介绍适配器引入的AdapterModel类(例如BertAdapterModel),这些类为预测头提供灵活支持,同时也包含Hugging Face Transformers开箱即用的静态头模型(例如BertForSequenceClassification)。
提示
我们建议尽可能使用AdapterModel类。这些灵活的模型是专门为使用适配器而创建的。
AdapterModel 类
由adapters提供的AdapterModel类允许在预训练语言模型之上灵活配置预测头。
首先,我们通过AutoAdapterModel类从Hugging Face Hub加载预训练模型:
model = AutoAdapterModel.from_pretrained("bert-base-uncased")
默认情况下,该模型尚未包含任何头部结构,因此我们需要在模型顶部添加一个新的二元序列分类头部:
model.add_classification_head("mrpc", num_labels=2)
所有头部都有一个名称,我们将这个新头部命名为"mrpc"。由于所有头部都有名称,我们可以向同一个模型添加多个具有不同名称的其他头部。
要查看模型的头部类型及其配置方式,请参考相应模型类的类引用,例如BertAdapterModel。
单独的头部只是一个参数很少的层。因此,我们希望将分类头与适配器一起训练,所以让我们添加一个:
model.add_adapter("mrpc", config="seq_bn")
model.set_active_adapters("mrpc")
由于我们给任务适配器取了与头部相同的名称,可以轻松识别它们属于同一组。
第二行调用set_active_adapters()告诉模型默认在前向传递中使用我们指定的适配器-头部配置。
此时,我们可以开始训练我们的设置。
注意
set_active_adapters() 会搜索并激活指定名称的适配器和预测头。此外,预测头也可以显式激活(即无需适配器模块)。以下是三种可能的选项(当指定多个时按优先级顺序):
如果将
head传递给forward调用,则使用具有给定名称的head。如果在
AdapterSetup上下文中执行forward调用,则从该上下文中读取头部配置。如果设置了
active_head属性,则从该属性读取头部配置。
训练完成后,我们可以通过一次调用保存整个设置(适配器模块和预测头):
model.save_adapter("/path/to/dir", "mrpc", with_head=True)
现在,您只需与世界分享您的工作。 当您将适配器及其头部发布到Hub后,其他任何人都可以通过使用相同的模型类来加载适配器和头部。
或者,我们也可以将预测头与适配器模块分开保存和加载:
# save
model.save_head("/path/to/dir", "mrpc")
# load
model.load_head("/path/to/dir")
最后,也可以再次删除已添加的头部:
model.delete_head("mrpc")
带有静态头部的模型类(Hugging Face Transformers)
transformers库为各种不同任务提供了带有预测头的强类型模型类(例如RobertaForSequenceClassification、AutoModelForMultipleChoice等)。
如果使用这些开箱即用的类训练适配器模块,建议将预测头权重与适配器权重一起分发。
因此,我们也可以轻松地将这些模型的预测头权重与适配器一起保存:
model.save_adapter("/path/to/dir", "mrpc", with_head=True)
在下一步中,我们可以将适配器权重和预测头权重都提供给Hub。 当其他人下载预训练的适配器时,解析方法会检查预测头是否与其模型的类别匹配。 如果类别匹配,预测头权重也会被自动加载。
自动转换
adapters 支持将静态头部(例如使用 AutoModelForSequenceClassification 创建的)加载到具有灵活头部的模型类中,例如 AutoAdapterModel。
为此,对于使用AutoModelForSequenceClassification创建的模型,我们首先需要通过调用init()方法来启用适配器支持。
from adapters import init, AutoAdapterModel
from transformers import AutoModelForSequenceClassification
import os
static_head_model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased")
# Enable adapter support
init(static_head_model)
现在我们可以像往常一样添加一个适配器并将其与头部一起保存:
static_head_model.add_adapter("test")
temp_dir = os.path.join(os.getcwd(), "temp_dir")
static_head_model.save_adapter(temp_dir, "test", with_head=True)
当加载适配器并将其集成到新的AdapterModel中时,权重的转换会在调用load_adapter()时自动完成,因此无需额外步骤:
flex_head_model = AutoAdapterModel.from_pretrained("bert-base-uncased")
flex_head_model.load_adapter(temp_dir)
assert "test" in flex_head_model.adapters_config
assert "test" in flex_head_model.heads
注意
不支持反向转换,即您无法将使用AutoAdapterModel创建的头部加载到AutoModelForSequenceClassification类型的模型中。
自定义头部
如果现有的预测头都不符合您的需求,您可以自定义并添加一个自定义头。
首先,我们需要定义新的头部类。为此,需要实现初始化方法和前向传播过程。头部初始化时需获取模型引用、头部名称以及额外定义的kwargs参数。您可参考以下模板作为指南。
class CustomHead(PredictionHead):
def __init__(
self,
model,
head_name,
**kwargs,
):
# innitialization of the custom head
def forward(self, outputs, cls_output=None, attention_mask=None, return_dict=False, **kwargs):
# implementation of the forward pass
接下来,我们可以注册新的自定义头部并为该新头部类型命名。这仅用于通知模型存在一种新的头部类型。然后,我们可以通过调用add_custom_head来向模型添加该新头部的实例,调用时需要传入新头部类型的名称、我们正在创建的头部实例名称以及该头部所需的其他参数。
model.register_custom_head("my_custom_head", CustomHead)
model.add_custom_head(head_type="my_custom_head", head_name="custom_head", **kwargs)
添加自定义头部后,您可以像处理任何其他内置头部类型一样使用它。