📚 模块10:保存、加载和合并LoRA/QLoRA适配器

10.1 为什么不保存完整模型?

PEFT的最大优势之一是你只需要保存适配器参数(LoRA),而不是完整的基模型。这具有巨大的实际意义:

  • 一个7B模型的LoRA适配器可能占用不到10MB,而FP16格式的完整模型需要14+GB。
  • 你可以为不同任务训练多个适配器,并根据需要将它们加载到同一基模型上。
  • 便于专业模型的共享、版本控制和存储。

10.2 保存训练好的适配器

训练后,LoRA适配器作为附加权重保存。基模型保持不变。

# 保存LoRA适配器
model.save_pretrained("./lora_adapter")

# 保存tokenizer(如果修改过,虽然很少见)
tokenizer.save_pretrained("./lora_adapter")

这会创建一个./lora_adapter目录,包含以下文件:

  • adapter_config.json — LoRA配置(r, alpha, target_modules等)
  • adapter_model.bin — LoRA权重(A和B)
  • README.md(可选)— 元数据

重要提示: 基模型不会保存在这里。你必须保持对原始基模型(例如来自Hugging Face Hub)的访问权限,以便后续加载适配器。

10.3 在基模型上加载LoRA适配器

要使用训练好的适配器:

  1. 加载基模型(带或不带量化)。
  2. 在其上加载LoRA适配器。
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import PeftModel
import torch

# 量化配置(可选用于高效推理)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

# 加载基模型
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)

# 加载LoRA适配器
model = PeftModel.from_pretrained(model, "./lora_adapter")

# 模型现在具有专业化行为
model.eval()  # 设置为评估模式用于推理

10.4 将适配器与基模型合并(merge_and_unload)

虽然动态适配器加载很灵活,但为了生产部署或更快的推理,将LoRA权重合并到基模型中很有用。这会创建一个完整的专业化模型,在推理期间不需要PEFT基础设施。

# 将LoRA适配器与基模型合并
model = model.merge_and_unload()

# 现在模型是一个具有更新权重的完整模型
# 保存为标准Hugging Face模型
model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")

警告:

  • 一旦合并,你无法重新加载另一个适配器而不重新加载原始基模型。
  • 合并模型占用与原始基模型相同的磁盘空间(Qwen2.5-0.5B在FP16中约为1GB)。
  • 合并仅在模型为完整精度(FP16/BF16)时可能。如果量化为4位,首先需要反量化(需要更多内存)。

与量化模型合并:

# 如果模型是4位,首先反量化(需要更多显存)
model = model.dequantize()  # 将权重转换为BF16/FP16

# 然后合并
model = model.merge_and_unload()

# 保存
model.save_pretrained("./merged_model_full_precision")

10.5 加载合并模型进行推理

一旦合并并保存,模型表现得像任何标准Hugging Face模型:

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
    "./merged_model",
    device_map="auto",
    trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained("./merged_model", trust_remote_code=True)

# 准备好进行推理,无需PEFT!

Course Info

Course: AI-course3

Language: ZH

Lesson: Module10