PEFT 文档

模型合并

Hugging Face's logo
加入 Hugging Face 社区

并获得增强型文档体验

以开始使用

模型合并

为每个任务训练模型可能会很昂贵,占用存储空间,而且模型无法学习新信息来提高其性能。多任务学习可以通过训练模型来学习多个任务来克服其中一些限制,但这训练起来很昂贵,并且为其设计数据集很困难。模型合并通过将多个预训练模型组合成一个模型来提供解决方案,使其具有每个单独模型的组合能力,而无需任何额外训练。

PEFT 提供了几种模型合并方法,例如线性或 SVD 组合。本指南重点介绍两种更有效地合并 LoRA 适配器的方法,方法是消除冗余参数

  • TIES - TrIm,Elect 和 Merge (TIES) 是一种用于合并模型的三步方法。首先,修剪冗余参数,然后将冲突符号解析为聚合向量,最后将符号与聚合符号相同的参数取平均值。这种方法考虑到某些值(冗余和符号不一致)可能会降低合并模型中的性能。
  • DARE - Drop And REscale 是一种可以用于为其他模型合并方法(如 TIES)做准备的方法。它的工作原理是根据丢弃率随机丢弃参数,然后重新调整剩余参数的比例。这有助于减少多个模型之间冗余和可能存在干扰的参数数量。

模型使用 add_weighted_adapter() 方法进行合并,具体模型合并方法在 combination_type 参数中指定。

合并方法

对于 TIES 和 DARE,可以通过设置 combination_typedensity 来启用合并,这些值代表从单个模型中保留的权重的比例。例如,假设要合并三个微调后的 TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T 模型:tinyllama_lora_nobotstinyllama_lora_sqltinyllama_lora_adcopy

在尝试使用 TIES 合并完全训练的模型时,应该注意每个模型是否在嵌入层中添加了任何特殊标记,这些标记不属于原始检查点的词汇表。这可能会导致问题,因为每个模型都可能在相同的嵌入位置添加了特殊标记。如果出现这种情况,应该使用 resize_token_embeddings 方法,以避免在相同的嵌入索引处合并特殊标记。


如果只合并从相同基础模型训练的 LoRA 适配器,则不会出现此问题。

加载基础模型,并使用 load_adapter() 方法加载并为每个适配器分配一个名称。

from peft import PeftConfig, PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

config = PeftConfig.from_pretrained("smangrul/tinyllama_lora_norobots")
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path, load_in_4bit=True, device_map="auto").eval()
tokenizer = AutoTokenizer.from_pretrained("smangrul/tinyllama_lora_norobots")

model = PeftModel.from_pretrained(model, "smangrul/tinyllama_lora_norobots", adapter_name="norobots")
_ = model.load_adapter("smangrul/tinyllama_lora_sql", adapter_name="sql")
_ = model.load_adapter("smangrul/tinyllama_lora_adcopy", adapter_name="adcopy")

使用 add_weighted_adapter() 方法设置适配器、权重、adapter_namecombination_typedensity

TIES
DARE

权重值大于 1.0 通常会产生更好的结果,因为它们可以保留正确的比例。权重的良好默认起始值是将所有值设置为 1.0

adapters = ["norobots", "adcopy", "sql"]
weights = [2.0, 1.0, 1.0]
adapter_name = "merge"
density = 0.2
model.add_weighted_adapter(adapters, weights, adapter_name, combination_type="ties", density=density)

使用 set_adapter() 方法将新合并的模型设置为活动模型。

model.set_adapter("merge")

现在,可以使用合并后的模型作为指令微调模型来编写广告文案或 SQL 查询!

指令
广告文案
SQL
messages = [
    {"role": "user", "content": "Write an essay about Generative AI."},
]
text = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
inputs = tokenizer(text, return_tensors="pt")
inputs = {k: v.to("cuda") for k, v in inputs.items()}
outputs = model.generate(**inputs, max_new_tokens=256, do_sample=True, top_p=0.95, temperature=0.2, repetition_penalty=1.2, eos_token_id=tokenizer.eos_token_id)
print(tokenizer.decode(outputs[0]))

合并 (IA)³ 模型

(IA)³ 模型支持适配器的线性合并。要合并 (IA)³ 模型中的适配器,请使用 IA3Model 类中的 add_weighted_adapter 方法。此方法类似于 LoraModel 中使用的 add_weighted_adapter 方法,关键区别在于没有 combination_type 参数。例如,要将三个 (IA)³ 适配器合并到 PEFT 模型中,可以按照以下步骤进行操作。

adapters = ["adapter1", "adapter2", "adapter3"]
weights = [0.4, 0.3, 0.3]
adapter_name = "merge"
model.add_weighted_adapter(adapters, weights, adapter_name)

建议将权重之和设置为 1.0,以保留模型的比例。然后,可以使用 set_adapter 方法将合并后的模型设置为活动模型。

model.set_adapter("merge")
< > 更新 on GitHub