适配器微调(Adapter tuning)

        适配器微调(Adapter tuning)通过引入适配器模块来进行特定任务的优化。适配器模块是一组轻量级的参数,被添加到模型的中间层,以保护原有预训练模型的参数。这种方法的目标是在不改变整体模型结构的情况下,通过调整适配器模块的参数来适应新任务。

        适配器微调相对于全量微调有两个主要优势。首先,它减少了参数调整的数量,同时还能保持与全量微调相当的模型性能。在GLUE基准测试中,适配器微调几乎与完全微调的BERT性能相当,但只使用了3%的任务特定参数 (Neil Houlsby, 2019)。这使得在资源有限的情况下更容易进行模型调整。其次,适配器模块允许在特定任务上保留更多的预训练知识,因为主要的预训练参数仍然受到保护,不会被大幅度改变。

        适配器微调的步骤包括在预训练模型的中间层添加适配器模块,并对其权重进行恒等初始化,这样训练开始时模型性能和原始模型非常接近;然后在目标任务上进行微调,调整适配器模块的权重。

        如图 1所示,适配器模块一般添加在Transformer模块中的两个全连接层的后面。每个适配器模块的结构包括输入层、输出层、下投影前馈层、上投影前馈层、非线性层、和从输入到输出的跳接。在训练过程中,一般只调整图 4中绿色的部分,包括适配器的下投影前馈层、上投影前馈层、非线性层以及Transformer模块中的两个归一层的参数。适配器模块的工作原理是先把输入的d维特征向量通过下投影前馈层(d×r维矩阵)投影为r维向量(r<<d),应用非线性层,再通过上投影前馈层(r×d维矩阵)投影回一个d维向量。

1 适配器模块的结构及其与Transformer模块的整合

        如图 2所示, (Neil Houlsby, 2019)给出了Adaptor tuning与传统迁移学习(调整靠近输出的几层)在调整同等数量的参数的情况下,模型性能的差别。左边的图表是微调BERT-large基础模型在GLUE数据集下的性能。X轴是调整的参数个数,Y轴是和全参数微调对比的准确度。右边的图表是微调BERT-base基础模型来处理其他一些文本分类任务(包括20 newsgroups、Crowdflower airline、Customer complaint database等)。X轴是调整的参数个数,Y轴是和全参数微调对比的准确度(所有这些文本分类任务的平均准确度)。由此可见,Adaptor tuning与传统迁移学习相比,只需要调整1/100甚至1/1000的参数就能得到相当的性能。

图 2 适配器微调与传统迁移学习的性能比较

        开源项目Adapters(https://github.com/adapter-hub/adapters)已经实现了开箱即用的适配器微调实现,我们只需要稍加配置,就可以训练和加载相应的Adapter模型。另外,Adapters还可以和HuggingFace的Transformer包无缝整合,可以直接加载HuggingFace上的模型进行Adapter微调。

        下面以使用BERT预训练模型来做文本分类任务为例,来展示如何使用适配器微调来微调一个大语言模型。首先,需要加载BERT预训练模型及其分词器。

from transformers import AutoTokenizer, AutoConfig

from adapters import AutoAdapterModel


model_path = "bert-base-chinese"

tokenizer = AutoTokenizer.from_pretrained(model_path)

config = AutoConfig.from_pretrained(model_path, num_labels=3)

model = AutoAdapterModel.from_pretrained(model_path, config=config)

        然后为预训练模型设置适配器。这里需要注意,在Adapters包里,本节所介绍的适配器结构被称为瓶颈适配器(Bottleneck adapters),使用BnConfig类来配置。这里需要为适配器取一个名字,之后可以通过这个名字来激活或者禁用这个适配器。

from adapters import BnConfig


adapter_name = "trouble_shooting"

# 添加一个新的adapter,类型为Bn adapter,即bottleneck adapter

config = BnConfig(mh_adapter=True, output_adapter=True, reduction_factor=16, non_linearity="relu")

model.add_adapter(adapter_name, config=config)


# 添加一个分类头

model.add_classification_head(adapter_name,num_labels=3, activation_function="relu")


# 激活这个adapter

model.train_adapter(adapter_name)

这里主要的配置参数有:

    1. mh_adapter:设置是否要在多头注意力模块之后添加适配器(即图 4左边下方的那个适配器)。
    2. output_adapter:设置是否要在Transformer模块的输出层添加适配器(即图 4左边上方的那个适配器)。
    3. reduction_factor:模型参数量与需调整的适配器参数量的比值。
    4. non_linearity:设置图 4右边的非线性部分使用的激活函数。

        最后,设置好训练参数,即可通过adapters包的AdapterTrainer类进行训练了,再调用trainer的save_model方法即可把训练好的适配器(不包括基础模型)保存在本地。

from transformers import TrainingArguments

from adapters import AdapterTrainer


training_args = TrainingArguments(

    num_train_epochs=5,

    per_device_train_batch_size = 16,

    logging_steps=2,

    save_steps = 10,

    gradient_accumulation_steps = 4,

    output_dir="/LLM/BERT/bert-adapter",

)


trainer = AdapterTrainer (

model=model, tokenizer=tokenizer

args=training_args, train_dataset=train_dataset,

    optimizers=(optimizer, None)

)

trainer.train() # 开始训练

trainer.save_model() # 保存训练好的模型

        这里用到了以下训练参数:

  1. per_device_train_batch_size:每次迭代的批次里包含的训练数据量。
  2. gradient_accumulation_steps:这是一种优化策略,减少更新梯度的次数,不用每次迭代都更新梯度,而是积累一定次数之后再更新。每次更新梯度称为一个基础步。
  3. logging_steps:每隔多少基础步记录一下训练进度,打印在控制台上。
  4. save_steps:每隔多少基础步存储一次训练模型的检查点(checkpoint)。
  5. num_train_epochs:每个epoch表示对所有训练数据完成了一遍训练。这个参数设置总共对训练数据集训练多少遍。
  6. output_dir:模型检查点和最后模型存储的路径。

        在推理时,除了要加载基础模型以外,还要加载Adapter,才能构成完整的模型。下面代码加载本地路径下的一个Adapter,并将其激活。

model.load_adapter("/LLM/BERT/bert-adapter", set_active=True)

参考文献:

Neil Houlsby, A. G. (2019). Parameter-Efficient Transfer Learning for NLP. Proceedings of ICML.

### Adapter 微调概述 Adapter微调是一种参数高效的迁移学习方法,通过在预训练模型中插入小型适配器模块来适应特定下游任务。这种方法允许保留大部分原始权重不变,仅更新少量新引入的参数[^1]。 ### 实现方法 为了实现Adapter微调,在原有网络结构基础上增加额外的小型神经网络作为adapter层。这些adapter通常由一层或多层全连接层组成,并放置于编码器各层内部的关键位置。具体来说: - **架构设计**:选择合适的层次加入adapter; - **初始化策略**:采用随机初始化或其他更优方案; - **正则化处理**:防止过拟合现象发生; ```python class BertModelWithAdapters(BertPreTrainedModel): def __init__(self, config): super().__init__(config) self.bert = BertModel(config) # Add adapters to each layer of the transformer encoder for i in range(len(self.bert.encoder.layer)): adapter_layer = nn.Sequential( nn.Linear(config.hidden_size, 64), nn.ReLU(), nn.Linear(64, config.hidden_size) ) setattr(self.bert.encoder.layer[i], 'adapter', adapter_layer) def forward(...): outputs = self.bert(...) ... ``` ### 教程指南 对于希望深入了解并掌握这一技术的研究人员而言,建议按照如下路径逐步深入研究: - 阅读经典论文《Parameter-Efficient Transfer Learning for NLP》以理解理论基础; - 学习开源项目Hugging Face Transformers中的`peft`库文档,该库提供了多种类型的adapters实现方式; - 尝试基于现有框架完成简单的实验案例,比如情感分类或命名实体识别等自然语言处理任务; ### 最佳实践 当应用Adapter微调时,需注意以下几点最佳实践经验: - 对不同规模的数据集合理设置超参,包括但不限于batch size、learning rate等; - 使用早停法(Early Stopping)监控验证集性能变化趋势,及时终止效果不佳的试验; - 定期保存checkpoint以便后续恢复训练过程或对比分析多个版本间的差异;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值