适配器训练

本节介绍针对不同场景训练适配器方法的一些示例。我们重点讨论如何将适配器方法集成到现有的Transformer模型训练脚本中。所有展示的脚本仅对Hugging Face Transformers的原始示例进行了轻微修改。要运行这些脚本,请确保您拥有该仓库的最新版本并已安装一些额外的依赖项:

git clone https://github.com/adapter-hub/adapters
cd adapters
pip install .
pip install -r ./examples/pytorch/<your_examples_folder>/requirements.txt

训练任务适配器

与训练整个模型相比,在数据集上训练任务适配器模块只需要进行少量修改。假设我们已有用于训练Transformer模型的现有脚本。接下来,我们将使用Hugging Face的run_glue.py示例脚本来在GLUE基准上进行训练。我们将逐步介绍所有必需的更改:

步骤 A - 解析 AdapterArguments

集成到adapters中的AdapterArguments类提供了一组对训练adapters有用的命令行选项。这些选项包括用于激活adapter训练的--train_adapter和用于从检查点加载adapters的--load_adapter。因此,集成adapters的第一步是将这些参数添加到实例化HfArgumentParser的行中:

parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments, AdapterArguments))
# ...
model_args, data_args, training_args, adapter_args = parser.parse_args_into_dataclasses()

步骤 B - 切换模型类(可选)

在我们的示例中,我们将内置的AutoModelForSequenceClassification类替换为adapters引入的AutoAdapterModel类。 因此,模型实例化变为:

model = AutoAdapterModel.from_pretrained(
        model_args.model_name_or_path,
        config=config,
)
model.add_classification_head(data_args.task_name, num_labels=num_labels)

或者,您也可以使用原始的 transformers 类,并通过调用 adapters.init(model) 来初始化模型以使用适配器。 了解更多关于 AdapterModel 类优势的信息 here

步骤 C - 设置适配器方法

提示

以下我们将展示如何手动设置适配器。在大多数情况下,您可以使用内置的setup_adapter_training()方法来自动完成这项工作。只需在模型实例化和训练开始之间的脚本任意位置添加类似这样的语句:setup_adapter_training(model, adapter_args, task_name)

与对整个模型进行微调相比,我们只需做一项重要调整:添加适配器设置并激活它。

# task adapter - only add if not existing
if task_name not in model.adapters_config:
    # resolve the adapter config
    adapter_config = AdapterConfig.load(adapter_args.adapter_config)
    # add a new adapter
    model.add_adapter(task_name, config=adapter_config)
# Enable adapter training
model.train_adapter(task_name)

重要

训练适配器模块最关键的一步是冻结模型中除适配器之外的所有权重。在前面的代码片段中,这是通过调用train_adapter()方法实现的,该方法会禁用任务适配器之外的所有权重训练。如果您之后想要解冻所有模型权重,可以使用freeze_model(False)

除此之外,我们只需确保任务适配器和预测头处于激活状态,以便它们在每次前向传递中被使用。要指定使用的适配器模块,我们可以使用model.set_active_adapters()方法并传入适配器配置。如果仅使用单个适配器,直接传入适配器名称即可。关于复杂配置的更多信息,请查看Composition Blocks

model.set_active_adapters(task_name)

步骤 D - 切换到 AdapterTrainer

最后,我们将Transformers内置的Trainer类替换为专为训练适配器方法优化的AdapterTrainer类。 更多信息请参阅下文

从技术上讲,这一改动并非必需,因为训练适配器不需要对训练循环进行任何修改。不过,AdapterTrainer提供了更好的检查点支持和适配器权重重载功能。

步骤 E - 开始训练

训练过程的其余部分不需要对代码进行任何进一步的更改。

您可以在我们代码库的examples文件夹中找到完整版的GLUE修改训练脚本run_glue.py。 我们还适配了其他各种示例脚本(例如run_glue.pyrun_multiple_choice.pyrun_squad.py等)以支持adapter训练。

要在GLUE任务上开始适配器训练,您可以运行类似以下命令:

export TASK_NAME=mrpc

python run_glue.py \
  --model_name_or_path bert-base-uncased \
  --task_name $TASK_NAME \
  --do_train \
  --do_eval \
  --max_seq_length 128 \
  --per_device_train_batch_size 32 \
  --learning_rate 1e-4 \
  --num_train_epochs 10.0 \
  --output_dir /tmp/$TASK_NAME \
  --overwrite_output_dir \
  --train_adapter \
  --adapter_config seq_bn

这里的重要标志是--train_adapter,它将从微调整个模型切换为为给定的GLUE任务训练一个适配器模块。

提示

适配器权重通常随机初始化,因此我们需要更高的学习率。我们发现默认适配器学习率1e-4在大多数情况下效果良好。

提示

根据您的数据集大小,您可能还需要比平时训练更长时间。为避免过拟合,您可以在每个训练周期后在开发集上评估适配器,并仅保存最佳模型。

训练语言适配器

训练语言适配器与训练任务适配器同样简单直接。类似于上文描述的任务适配器步骤,我们在现有模型训练脚本中添加一个语言适配器模块。这里,我们修改了Hugging Face的run_mlm.py脚本,用于基于BERT模型的掩码语言建模。

使用此脚本在BERT上训练语言适配器可能如下所示:

export TRAIN_FILE=/path/to/dataset/train
export VALIDATION_FILE=/path/to/dataset/validation

python run_mlm.py \
    --model_name_or_path bert-base-uncased \
    --train_file $TRAIN_FILE \
    --validation_file $VALIDATION_FILE \
    --do_train \
    --do_eval \
    --learning_rate 1e-4 \
    --num_train_epochs 10.0 \
    --output_dir /tmp/test-mlm \
    --train_adapter \
    --adapter_config "seq_bn_inv"

训练AdapterFusion

我们提供了一个在GLUE数据集上训练AdapterFusion(Pfeiffer et al., 2020)的示例:run_fusion_glue.py。 您可以修改此脚本,在自己的数据集上使用不同的预训练适配器来训练AdapterFusion。

重要

AdapterFusion在目标任务上的训练是在独立训练各个任务的适配器后的第二个训练阶段进行的。 当在模型上设置融合架构时,请确保在添加融合层之前使用model.load_adapter()加载要融合的预训练适配器模块。 有关AdapterFusion的更多信息,请参阅Pfeiffer et al., 2020

要在SST-2作为目标任务上开始融合训练,您可以运行类似以下命令:

export GLUE_DIR=/path/to/glue
export TASK_NAME=SST-2

python run_fusion_glue.py \
  --model_name_or_path bert-base-uncased \
  --task_name $TASK_NAME \
  --do_train \
  --do_eval \
  --data_dir $GLUE_DIR/$TASK_NAME \
  --max_seq_length 128 \
  --per_device_train_batch_size 32 \
  --learning_rate 5e-5 \
  --num_train_epochs 10.0 \
  --output_dir /tmp/$TASK_NAME \
  --overwrite_output_dir

AdapterTrainer

类似于Hugging Face提供的Trainer类,adapters提供了一个AdapterTrainer类。这个类仅用于训练适配器。完整微调模型时仍应使用Trainer类。要使用AdapterTrainer类训练适配器,只需像初始化Trainer类一样进行初始化,例如:

from transformers.training_args import TrainingArguments 

model.add_adapter(task_name)
model.train_adapter(task_name)

training_args =  TrainingArguments(
    learning_rate=1e-4,
    num_train_epochs=6,
)

trainer = AdapterTrainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        tokenizer=tokenizer,
        data_collator=data_collator,
    )

提示

当您从使用Trainer类进行适配器训练和完全微调的旧版本迁移时,请注意专用的AdapterTrainer类没有参数do_save_full_modeldo_save_adaptersdo_save_adapter_fusion

量化模型训练

适配器支持通过集成到Transformers中的bitsandbytes库对量化语言模型进行类似QLoRA (Dettmers等人,2023)的微调。基于LoRA的适配器、瓶颈适配器以及前缀调优都支持量化训练。具体实践指南请参考此笔记本

梯度检查点

除Hugging Face Transformers不支持的模型(如ALBERT)外,所有模型(例如Llama 1/2/3)均支持梯度检查点技术。实践指南请参阅此笔记本