登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

LoRA 适配器怎样与基础模型分离加载

来源:17golang原创

时间:2026-11-07 23:31:18 102浏览 收藏

我第一次把 LoRA 用到推理服务时,最容易混淆的是“加载适配器”和“加载一个新模型”。LoRA 目录通常只保存相对基础模型的增量参数,正确的做法是先加载兼容的基础模型,再把适配器挂到这个模型上。这样基础模型可以复用,适配器也能按任务切换;只有在准备做单体部署时,才考虑把两者合并。

官方地址:https://huggingface.co/docs/peft/en/package_reference/lora

下面的示例以 Transformers 和 PEFT 的文本模型为主,路径和模型名使用占位值,重点是加载顺序与生命周期,不把静态示意当成已经运行的输出。

先分清基础模型和 LoRA 权重

基础模型提供完整的网络结构和原始参数,LoRA 适配器只保存要插入目标层的低秩增量。适配器目录至少要能让加载器找到配置与权重,例如 adapter_config.json 和适配器权重文件。它不能脱离匹配的基础模型独立变成一个完整的语言模型。

这也是“分离加载”的核心:模型对象负责承载基础参数,适配器对象负责额外的可切换参数。两者分开存储并不代表推理时完全分成两个互不相干的网络,而是由 PEFT 在目标层建立组合关系。

基础模型权重与 LoRA 适配器通过推理组合层连接的静态结构图
图1:基础模型、LoRA 适配器与推理组合层的关系说明图,不是运行截图。

用 PeftModel 把适配器挂到已有模型

如果基础模型已经由 Transformers 加载,PEFT 的显式写法最直观:先得到 base_model,再调用 PeftModel.from_pretrained。这一步不会把基础模型目录改写成适配器目录,适配器仍然可以单独替换。

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

# 先加载完整基础模型;它决定网络结构和词表兼容性
base_model_id = "your-org/your-base-model"
base_model = AutoModelForCausalLM.from_pretrained(
    base_model_id,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(base_model_id)

# 再加载 LoRA 增量权重;这里的目录不是完整基础模型
adapter_path = "./adapters/customer-service"
model = PeftModel.from_pretrained(
    base_model,
    adapter_path,
    adapter_name="customer_service",
)

# 明确指定推理时启用的适配器,避免依赖隐含状态
model.set_adapter("customer_service")
model.eval()

这里的关键不是某个模型名,而是顺序:基础模型先存在,适配器再附着。device_map="auto" 只是大模型常见的设备分配选项;如果你的设备策略不同,应按部署环境调整。

也可以直接在 Transformers 模型上加载

较新的 Transformers 集成了适配器管理能力。若你不需要单独包装成 PeftModel,可以在已有的 Transformers 模型上直接调用 load_adapter。仓库或本地目录仍然需要包含适配器配置与权重。

from transformers import AutoModelForCausalLM

# 基础模型先加载,适配器随后附加到同一个模型对象
model = AutoModelForCausalLM.from_pretrained("your-org/your-base-model")
model.load_adapter(
    "./adapters/customer-service",
    adapter_name="customer_service",
)

# 只启用指定适配器;其它已加载适配器保持未激活
model.set_adapter("customer_service")

如果适配器的 adapter_config.json 中记录了基础模型信息,也可以使用支持自动检测的加载方式。但在服务端显式先加载基础模型更容易控制缓存、设备和多个适配器的生命周期。

多个 LoRA 适配器怎样切换

我在一个进程里同时放置“客服语气”和“代码解释”两个适配器时,最重要的经验是给它们稳定的名字。加载第二个适配器不会自动把第一个替换掉;真正改变当前推理行为的是 set_adapter。

# 两个适配器共用同一个基础模型,但保留各自的目录和名字
model.load_adapter("./adapters/customer-service", adapter_name="customer_service")
model.load_adapter("./adapters/code-explainer", adapter_name="code_explainer")

# 按请求或路由选择当前适配器;名称必须与加载时一致
model.set_adapter("code_explainer")

# 不需要适配器时关闭适配器,回到基础模型的行为
model.disable_adapters()

真实服务中不要把“当前适配器”藏在全局可变变量里再并发修改。可以按请求路由建立隔离的模型实例,或者在明确的串行临界区内切换;PEFT 的适配器名只解决权重选择,不自动解决你的并发状态管理。

卸载和删除不是同一件事

只想暂时去掉 LoRA 模块时,可以使用 unload();如果适配器以后不再需要,可以使用 delete_adapter 释放对应的适配器状态。unload() 返回新的模型对象,不能假设它会原地改变原变量。

# unload 返回卸载后的模型,必须接住返回值
base_like_model = model.unload()

# 如果只是不再需要某个命名适配器,可以删除它
# model.delete_adapter("customer_service")

# 后续如果还要使用另一个适配器,重新明确激活它
# model.set_adapter("code_explainer")

这里不要把 unload() 和“删除基础模型”混为一谈:基础模型仍然是模型主体,卸载的是附加的 LoRA 模块。对长期服务进程,还要结合显存引用、缓存和请求生命周期处理对象回收。

LoRA 适配器保持分离、切换、卸载与合并部署的生命周期对比图
图2:适配器切换、卸载与合并部署的关系说明图,不是运行截图。

什么时候保持分离,什么时候合并

保持分离适合多个任务共享一个基础模型、按请求切换风格或任务、以及仍在试验不同适配器的场景。它的代价是加载和管理路径更复杂,服务需要明确当前适配器及其设备位置。

如果模型已经定型、服务只需要一套固定行为,可以考虑 merge_and_unload(),把 LoRA 权重合入基础模型并返回一个不再携带适配器模块的模型。合并后切换适配器的灵活性会消失,因此不要在实验阶段过早合并。

# 只在确定要做固定单体部署时合并
merged_model = model.merge_and_unload()

# 合并后的模型可以按普通 Transformers 模型保存
merged_model.save_pretrained("./export/merged-model")
tokenizer.save_pretrained("./export/merged-model")

PEFT 文档还提供了在不合并的情况下加载不同适配器、设置活动适配器的方式;Diffusers 等其他模型管线也有自己的 load_lora_weights 和 set_adapters 接口,不能把文本模型的 API 原样套到图像管线。

排查分离加载失败的三个方向

现象优先检查处理思路
找不到适配器配置目录是否包含 adapter_config.json 和权重确认传入的是适配器目录,而不是训练缓存的上级目录
加载后尺寸或层不匹配基础模型、任务类型和 target modules换回训练该适配器时使用的兼容基础模型
切换后输出没有变化adapter_name、set_adapter 和 disable 状态先确认活动适配器,再排查权重是否确实属于当前基础模型

从经验上看,最稳妥的排查顺序是先确认“目录结构”,再确认“基础模型身份”,最后确认“当前激活状态”。不要一遇到输出异常就把 LoRA 合并;合并会让定位和回退都更麻烦。

结论:把基础模型当底座,把适配器当可替换策略

LoRA 分离加载可以归纳成一句话:基础模型先加载,适配器后附加,推理前显式选择,生命周期结束时再卸载或删除。需要多任务切换时保持分离,需要固定单体部署时再合并。这样既能复用基础模型,又能把适配器的兼容性、激活状态和部署取舍说清楚。

相关问题

  • LoRA 适配器能脱离基础模型单独推理吗?不能把它当完整模型使用,必须找到训练时兼容的基础模型。
  • 加载两个适配器会自动叠加吗?不会。先用唯一名称加载,再按 API 明确设置当前适配器或组合策略。
  • 什么时候使用 merge_and_unload?固定模型行为、减少运行时适配器管理时使用;需要频繁切换时保持分离更合适。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>