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

PEFT 怎样分别启用和停用模型中的 LoRA 层

来源:17golang原创

时间:2026-10-29 16:53:08 483浏览 收藏

PEFT 里“启用和停用 LoRA”不是一个动作。你可能是在同一个模型上临时比较基座结果,也可能是在多个 LoRA 适配器之间切换,还可能只是想让 Transformers 直连接口暂时关闭全部适配器。对应的 API 分别是 disable_adapter()、set_adapter(),以及 disable_adapters()/enable_adapters()。

官方资料:https://huggingface.co/docs/peft/package_reference/peft_model

要点速览
  • PeftModel 适合用 with model.disable_adapter(): 做一次临时基座推理,退出上下文后 LoRA 状态恢复。
  • 多个 LoRA 都已加载时,用 set_adapter("名称") 选择活动适配器;切换不等于删除,未选中的权重仍在内存中。
  • 如果模型使用 Transformers 的适配器混入接口,才使用 disable_adapters() 和 enable_adapters() 这对全局方法。

先分清:你要切换的是适配器,还是整层开关

我在整理一套“同一基座、多个任务 LoRA”的推理入口时,最容易混淆的是三个概念:适配器已经加载但没有被选中、所有适配器暂时停用,以及把 LoRA 模块从模型中移除。它们的资源状态和恢复方式完全不同。

目标推荐接口结果
临时得到基座模型结果PeftModel.disable_adapter()上下文内不应用适配器,退出后恢复
在多个 LoRA 之间切换set_adapter(name)选择活动适配器,其他适配器保留
永久去掉注入层unload() 或删除指定适配器需要重新赋值或重新加载才能恢复
Transformers 直连模型全部停用disable_adapters()关闭所有已附加适配器,再用对应恢复方法启用

因此,target_modules 是创建 LoRA 时决定“往哪些模块注入”的配置,不是运行时逐层开关。若只是想比较有无 LoRA 的输出,不要为了这次比较卸载整个模型。

PEFT 基座模型、LoRA 注入层、活动适配器和全局停用边界的静态结构说明图
图1:结构说明图,展示 PEFT 的适配器选择与全局停用边界,不是运行截图。

用 disable_adapter 做一次基座模型对照

如果对象是 PeftModel,最稳妥的做法是把停用范围限制在一个上下文里。官方 API 将 disable_adapter() 定义为上下文管理器,适合拿同一批输入分别得到“带 LoRA”和“仅基座”的结果,不会改变模型文件,也不要求重新加载权重。

from peft import PeftModel

def compare_base_and_lora(model: PeftModel, inputs):
    # 正常路径使用当前活动的 LoRA 适配器。
    lora_output = model(**inputs)

    # 上下文内临时停用适配器,得到基座模型对照结果。
    with model.disable_adapter():
        base_output = model(**inputs)

    # 离开 with 后,后续调用回到原来的适配器状态。
    restored_output = model(**inputs)
    return lora_output, base_output, restored_output

这个写法的重点是范围清晰:不要把停用状态散落到全局变量,也不要在上下文外假设 LoRA 仍然停用。若业务确实需要长期只运行基座模型,才考虑 unload();它会返回移除适配器模块后的模型,必须接住返回值。

多 LoRA 模型用 set_adapter 选择活动适配器

多个适配器共存时,加载只代表权重进入模型,并不等于它已经成为当前推理路径。可以给每个任务起稳定的名称,再用 set_adapter() 切换。对普通 PeftModel,官方参考接口按名称设置一个活动适配器;其他适配器不会被删除,只是不参与这次前向计算。

from peft import PeftModel

def select_task_adapter(model: PeftModel, adapter_name: str):
    # adapter_name 必须是已经加载到模型中的名称。
    model.set_adapter(adapter_name, inference_mode=True)

    # 这里的前向调用只使用刚选中的适配器。
    return model

# 例如:先加载名为“客服”和“摘要”的两个 LoRA,再按请求选择。
model.load_adapter("./adapter-summary", adapter_name="摘要")
model.set_adapter("摘要", inference_mode=True)

如果需求是“同一批输入同时组合多个适配器”,不要把普通 PeftModel.set_adapter() 当作通用列表接口。PEFT 文档对支持多适配器的方法给出了更低层的 model.base_model.set_adapter([...]) 入口,是否可组合要以具体 tuner 类型为准。只需要在“客服”和“摘要”之间二选一时,使用单个名称更容易维护。

按模型入口选择停用与恢复 API

PEFT 和 Transformers 都能管理 LoRA,但对象类型不同,方法名也不同。若模型通过 Transformers 的 PeftAdapterMixin 直接附加适配器,可以显式关闭和恢复全部适配器:

def run_base_only(model, inputs):
    # 关闭当前模型上的全部适配器,保留它们在内存中的权重。
    model.disable_adapters()
    try:
        return model(**inputs)
    finally:
        # 无论推理是否抛出异常,都恢复适配器,避免污染后续请求。
        model.enable_adapters()

这组方法适合“整个模型暂时回到基座”的场景;而 set_adapter() 适合“只换当前活动任务”的场景。不要把 requires_grad=False 当成停用 LoRA:它主要影响梯度和训练,不等价于前向计算不使用适配器。

PeftModel 与 Transformers 接口对应的 LoRA 停用恢复 API 静态选择说明图
图2:API 选择说明图,展示不同模型入口对应的 LoRA 激活与停用接口,不是运行截图。

上线前用状态接口确认“选中了谁”

在服务化推理里,最值得记录的是当前活动适配器和启用状态,而不是仅凭配置文件猜测。PEFT 提供的状态接口可以帮助排查“权重已经加载,但输出仍像基座”这一类问题:

def adapter_snapshot(model: PeftModel):
    # 读取模型级状态,便于在请求切换后记录当前配置。
    model_status = model.get_model_status()
    # 读取每个 LoRA 层的启用、活动和可用适配器信息。
    layer_status = model.get_layer_status()
    return model_status, layer_status

发布前可以按下面的清单排查:

  • 临时基座对照是否被限制在 with 或 try/finally 范围内?
  • set_adapter 使用的名称是否确实已经通过 from_pretrained 或 load_adapter 加载?
  • 你需要的是停用前向计算,还是只停用训练梯度?两者不要混用。
  • 是否把 unload() 当成可逆开关?它会返回一个移除适配器的模型,恢复需要重新加载。

常见问题

退出 disable_adapter 上下文后需要手动 enable 吗?

使用 PeftModel 的上下文写法时通常不需要。上下文结束后会恢复之前的适配器状态;如果你使用的是 Transformers 直连接口,才按对应 API 显式调用 enable_adapters()。

set_adapter 会释放未选中 LoRA 的显存吗?

不会。它主要切换活动适配器,其他已加载权重仍然保留。需要释放某个适配器时,使用 delete_adapter(),但这已经是资源管理动作,不是临时停用。

能不能只关闭某一个 LoRA 注入层?

高层 API 的常用边界是按适配器选择或全局停用,而不是在业务代码里遍历每个注入层改内部标志。若确实需要层级级别的实验,应先确认对应 tuner 的支持范围,再用 get_layer_status() 观察状态,避免把内部实现当成稳定公共接口。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>