【大模型开发】开源大模型微调:适配器(Adapters)方案

下面将详细介绍 开源大模型适配器(Adapters) 微调技术的核心思想、常见应用场景以及如何通过一段可执行的代码示例来完成适配器的微调流程。文末还将结合实际应用给出如何自定义、修改和优化的建议,帮助读者在自己的项目中灵活落地。


一、适配器(Adapters)微调的基本原理

1.1 适配器(Adapters)是什么

适配器(Adapters) 是一种 参数高效微调(PEFT, Parameter-Efficient Fine-Tuning) 方法,最初由 Houlsby 等人在 2019 年提出,旨在应对大模型在不同下游任务中大规模微调带来的高昂计算和存储成本问题。适配器方法在 Transformer 层之间插入额外的 小型可训练模块(Adapter Modules),仅微调这些额外参数,而保持原有预训练模型的主干参数 冻结 不变。

核心思路

  • 在 Transformer 的 多头自注意力层前馈网络层(FFN) 中插入一个或多个“瓶颈”结构的小网络。
  • 新增模块的参数量相对主干模型非常小(通常在百万级别以下),适合资源受限场景。
  • 训练时,仅更新适配器的参数,保留主干模型的权重,能显著减小存储和计算需求。

1.2 优势与特点

  1. 节省计算和存储

    • 只需训练和保存少量参数,主干模型可重复使用,极大降低部署成本。
  2. 多任务与跨领域迁移灵活

    • 在同一个主干模型里可插入多个不同任务的适配器(多 Adapter Fusion),根据需求切换或组合。
  3. 与其他微调方法兼容

    • 适配器可与 LoRA、Prompt-Tuning 等方法结合,实现更高效或更精细的微调。
  4. 训练稳定性更好

    • 适配器引入的小网络可减少对主干参数的干扰,降低灾难性遗忘风险。

二、适配器微调的应用场景

  1. 文本分类:在不同领域(如金融、医疗、法律)的文本分类任务上,使用同一个大型预训练模型(如 BERT、RoBERTa),通过插入不同的适配器来实现领域定制化。
  2. 序列标注(如命名实体识别):适配器可只针对特定数据集或语言进行专门训练,而不需改动主干模型。
  3. 机器翻译/文本生成:可在多语言或特定领域插入多组适配器,方便快速切换翻译方向或主题。
  4. 多任务学习:在同一模型中为不同任务(问答、情感分析、文本摘要等)插入各自的适配器,随时切换,主干参数可共享。

三、代码示例:基于 Hugging Face 的适配器微调

本示例将使用 adapter-transformers 库,它是在 Hugging Face Transformers 之上开发的适配器管理和微调工具包。下面示例以一个 文本分类 任务(如 SST-2 或自定义数据集)为例,演示如何插入适配器、进行微调并评估效果。

前置环境准备

pip install transformers adapter-transformers datasets
  • transformers:Hugging Face 预训练模型库
  • adapter-transformers:适配器扩展库
  • datasets:方便加载公开数据集或自定义数据集

3.1 加载模型与数据

from transformers import AutoTokenizer
from transformers.adapters import AdapterConfig
from transformers import TrainingArguments, Trainer
from transformers import AutoModelForSequenceClassification
from datasets import load_dataset

# 1. 加载数据(以SST-2情感分类数据集为例)
dataset = load_dataset("glue", "sst2")
# 数据集分为 train / validation / test
train_dataset = dataset["train"]
val_dataset   = dataset["validation"]
test_dataset  = dataset["test"]

# 2. 加载预训练模型和Tokenizer
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

# 3. 数据预处理函数
def preprocess_function(examples):
    return tokenizer(examples["sentence"], truncation=True, padding="max_length", max_length=128)

# 将原始文本转为模型可处理的输入格式
train_dataset = train_dataset.map(preprocess_function, batched=True)
val_dataset = val_dataset.map(preprocess_function, batched=True)
test_dataset = test_dataset.map(preprocess_function, batched=True)

# 转换为torch的格式
train_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"])
val_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"])
test_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"])

解释

  1. 这里示例使用了 SST-2 数据集(情感分类),也可替换为自定义数据。
  2. 加载 bert-base-uncased 作为初始主干模型。
  3. 对文本进行分词、截断并指定最大长度为 128。
  4. 转换为 torch 格式,便于后续 Trainer 训练。

3.2 插入适配器

# 1. 定义适配器配置
config = AdapterConfig.load(
    "houlsby",  # 或者 "pfeiffer", "parallel", "compacter" 等其他Adapter类型
    reduction_factor=16,  # 控制瓶颈层大小,越小越省参数
    non_linearity="relu"  # 在适配器网络中使用的激活函数
)

# 2. 为模型添加一个适配器
adapter_name = "sst2_adapter"
model.add_adapter(adapter_name, config=config)

# 3. 激活此适配器作为训练的默认头部
model.set_active_adapters(adapter_name)

# 4. 设置分类头
model.add_classification_head(
    adapter_name,
    num_labels=2,  # SST-2是2分类
    overwrite_ok=True
)

# 5. 激活这个任务头
model.set_active_adapters(adapter_name)

解释

  1. AdapterConfig.load("houlsby"):加载 Houlsby 适配器结构。你也可以选择 pfeiffer 或其他变体。
  2. reduction_factor:设置瓶颈大小,越小意味着适配器更轻量。
  3. model.add_adapter(adapter_name, config=config):给模型插入新的适配器模块并命名为 "sst2_adapter"
  4. model.add_classification_head():因为是分类任务,需要一个输出层(classification head)。
  5. model.set_active_adapters(adapter_name):指定当前适配器和输出头为激活状态,后续的训练和推理都将使用这个适配器。

3.3 训练设置与微调

# 定义超参数
training_args = TrainingArguments(
    output_dir="./sst2_adapter_checkpoints",
    evaluation_strategy="epoch",   # 每个epoch进行评估
    save_strategy="epoch",        # 每个epoch保存
    learning_rate=5e-4,           # 适配器通常可用更大学习率
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=3,
    logging_steps=100,
    load_best_model_at_end=True,
)

# 定义评估指标(accuracy为例)
import evaluate
accuracy_metric = evaluate.load("accuracy")

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    preds = logits.argmax(axis=-1)
    return {"accuracy": accuracy_metric.compute(predictions=preds, references=labels)["accuracy"]}

# 训练器
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

# 开始训练
trainer.train()

解释

  1. 学习率(learning_rate=5e-4):适配器方法一般可以使用比全参数微调稍高的学习率,因为训练参数量更少。
  2. compute_metrics:使用 accuracy 度量测试集表现,你也可添加 F1、Precision/Recall 等。
  3. trainer.train():仅训练适配器和分类头的参数,而 BERT 主干权重被冻结

3.4 测试与推理

# 在验证集或测试集上进行评估
eval_results = trainer.evaluate(eval_dataset=val_dataset)
print("Validation Results:", eval_results)

test_results = trainer.evaluate(eval_dataset=test_dataset)
print("Test Results:", test_results)

# 推理示例
sample_text = "This film is absolutely fantastic!"
inputs = tokenizer(sample_text, return_tensors="pt")
outputs = model(**inputs)
pred_class = outputs.logits.argmax(dim=-1).item()
print("Prediction (0=negative, 1=positive):", pred_class)

解释

  1. trainer.evaluate():调用评估流程,输出 accuracy 等指标。
  2. 推理阶段也需要 model.set_active_adapters(adapter_name),确保使用对应的适配器来进行预测。
  3. 输出分类结果,如 1 代表正面情感,0 代表负面情感。

四、如何修改并在自己项目中使用

  1. 自定义数据集

    • 如果你有自己的文本分类数据,可在 datasets.Dataset.from_dictload_dataset("csv", data_files=...) 方式加载,然后按照相似处理流程进行分词和格式转换即可。
    • 注意修改 num_labels、适配器的名字和分类头输出维度,以符合实际标签数量。
  2. 更换适配器结构

    • AdapterConfig.load("houlsby") 中,可选择 "pfeiffer", "houlsby", "parallel", "compacter" 等不同变体。
    • 不同结构在瓶颈维度、激活函数、插入位置有所差异,你可以多做实验,对比性能与速度。
  3. 更换预训练模型

    • 适配器方法不仅适用于 BERT,也支持 RoBERTa、GPT-2、T5 等大模型。只需替换 AutoModelForSequenceClassification 或相应的其他任务模型即可。
  4. 多任务、多语言微调

    • 可以 model.add_adapter("taskA"),再 model.add_adapter("taskB"),分别加载不同任务或语言的适配器,极大增强可扩展性。
  5. 使用混合精度或量化

    • TrainingArguments 中启用 fp16=Truebf16=True 可加速训练。
    • 如果模型太大,可进一步结合量化技术(如 8-bit、4-bit 量化)提升推理效率。
  6. 保存并加载适配器

    • 适配器库支持 model.save_adapter()model.load_adapter(),只保存/加载适配器参数,方便分发给其他同事或合作者使用,无需共享主干模型权重。

五、优化与实践建议

  1. 选择合适的瓶颈维度(reduction_factor)

    • 值越小,适配器越轻量;值越大,则有可能带来更好效果,但占用更多参数。
  2. 控制学习率

    • 适配器层能承受 略高 的学习率(如 1e-3 到 5e-4)并加快收敛。遇到过拟合或不稳定,可以尝试 减少 学习率。
  3. 冻结/微调策略

    • 适配器方法默认冻结主干参数,但也可以选择 逐层解冻 或保留前几层冻结,仅微调后几层和适配器,视任务数据规模和效果而定。
  4. 多适配器融合

    • 对于多任务或多语言场景,可以在推理时 融合多个适配器 (AdapterFusion),可以获得跨任务或跨语言的综合能力。
  5. 监控训练过程

    • 通过 TensorBoard 或其他可视化工具实时查看损失和评价指标的曲线,及时调参并早停(early stopping)以避免过拟合。
  6. 实际部署注意事项

    • 推理时,需要加载主干模型 + 已训练好的适配器权重文件。
    • 适配器可能会稍微增加推理开销(额外的前向传播分支),但相比全参数微调重新部署要 大幅节省 存储与内存。

六、总结

适配器(Adapters)作为 参数高效微调 领域的经典方法,具有 高灵活性低计算量易于多任务扩展 等显著优势。通过在 Hugging Face 生态中的简单配置,即可在预训练大模型里插入适配器模块,然后仅更新少量参数就能完成针对任务或领域的定制化微调。

  • 计算资源有限需要同时处理多个任务,适配器微调是一个非常值得尝试的方案。
  • 结合 LoRA、P-Tuning 等其他 PEFT 手段,也能进一步提高微调效率或兼顾多种使用场景。
  • 在生产部署中,通过保存和共享适配器参数文件,可大幅减少存储与分发成本,让团队协作与模型迭代更加轻松。

通过上面的代码示例和实践建议,你可以快速掌握适配器的使用流程,并根据需要在自有项目中自由配置和调整,从而在 文本分类、序列标注、机器翻译、对话生成 等多种 NLP 任务上高效地发挥开源大模型的潜力。祝你在实践中取得理想效果!

哈佛博后带小白玩转机器学习哔哩哔哩_bilibili

总课时超400+,时长75+小时

<think>好的,我现在需要帮助用户解决他们遇到的ModuleNotFoundError问题,特别是关于transformers库中的BertForPreTraining导入错误。首先,我要确认用户的问题是什么。用户提到在导入BertForPreTraining时遇到了模块未找到的错误,可能类似于之前引用的错误信息,比如找不到transformers.modeling_bert或者transformers.utils.modeling_auto_mapping。 首先,我应该回想一下transformers库的结构变化。根据引用[3],transformers在版本更新后可能会改变模块的路径,例如从modeling_bert移动到models.bert.modeling_bert。因此,用户可能在使用旧版本的导入方式,而他们的transformers库已经更新到了新版本,导致路径不匹配。 接下来,我需要确认正确的导入路径。当前版本的transformers(比如4.x及以上)中,BertForPreTraining应该位于transformers.models.bert模块下。因此,正确的导入语句应该是from transformers.models.bert import BertForPreTraining。或者使用更简洁的导入方式from transformers import BertForPreTraining,如果库有维护向后兼容的话。 另外,用户可能没有正确安装transformers库,或者安装的版本过旧。根据引用[2],如果未安装该库,需要先使用pip install transformers进行安装。如果已安装但版本过旧,可能需要升级到最新版,例如pip install --upgrade transformers。 还需要检查用户是否在代码中错误地引用了不存在的子模块。例如,旧的导入方式可能是from transformers.modeling_bert import BertForPreTraining,而新版本中这个路径已经失效,应该替换成新的路径。 此外,用户可能遇到了版本兼容性问题。比如,他们的代码是基于旧版本的transformers编写的,而他们现在安装了新版本。这时候需要查看官方文档或更新日志,确认模块路径的变化,并相应调整导入语句。 可能的解决方案步骤包括: 1. 确认transformers库已正确安装,并且版本较新。 2. 更新导入语句,使用新的模块路径。 3. 如果必要的话,降级transformers到兼容的版本,但这不是推荐的做法,最好还是适配新版本。 还需要提醒用户检查他们的代码是否有其他过时的导入,因为可能不止一个地方需要修改。此外,建议用户查阅Hugging Face的官方文档,以获取最新的模块结构和导入方式,确保代码的兼容性。 最后,根据用户提供的引用,特别是引用[3]中的情况,他们可能需要将导入语句从旧的from transformers.modeling_auto import AutoModel更改为新的from transformers.models.auto import AutoModel,类似的调整可能适用于BertForPreTraining。 总结下来,用户需要更新他们的导入路径,并确保库的版本正确,这样才能解决ModuleNotFoundError的问题。</think>针对`transformers`库中`BertForPreTraining`导入报错的解决方案如下: ### 分步解决方案 1. **检查库安装状态** 执行命令确认是否安装`transformers`库: ```bash pip show transformers ``` 若未安装,通过以下命令安装: ```bash pip install transformers ``` 2. **验证库版本兼容性** *旧版本(<4.0)*的导入方式为: ```python from transformers.modeling_bert import BertForPreTraining # 已过时[^3] ``` *新版本(≥4.0)*需修改为: ```python from transformers.models.bert import BertForPreTraining # 正确路径[^3] ``` 或直接使用快捷导入: ```python from transformers import BertForPreTraining # 推荐方式 ``` 3. **升级库版本** 若本地版本过旧(可通过`pip list`查看),建议升级: ```bash pip install --upgrade transformers ``` 4. **验证代码适配性** 如果代码涉及其他旧版模块(如`modeling_auto_mapping`),需按新版路径调整: ```python # 旧版(报错) from transformers.utils.modeling_auto_mapping import ... # 新版(正确) from transformers.models.auto.modeling_auto import ... # ``` --- ### 关键说明 - **路径变更原因**:Hugging Face在`transformers` v4.0后重构了模块结构,将模型代码按类别迁移至`models`子目录下 - **兼容性建议**:优先使用`from transformers import ...`简化导入方式,避免直接引用底层路径 - **版本对照**:可通过[官方文档](https://huggingface.co/docs/transformers/model_doc/bert)查询API变动
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值