PEFT 文档

模型合并

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

模型合并

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

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

  • TIES - TrIm, Elect, and 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 合并完全训练的模型时,您应该注意每个模型可能已添加到嵌入层中的任何特殊 token,这些 token 不是原始检查点词汇表的一部分。这可能会导致问题,因为每个模型可能已将特殊 token 添加到相同的嵌入位置。如果是这种情况,您应该使用 resize_token_embeddings 方法来避免在相同的嵌入索引处合并特殊 token。


如果您仅合并从同一基础模型训练的 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.config.vocab_size = 32005
model.resize_token_embeddings(32005)

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")
< > 在 GitHub 上更新