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

Diffusers LoRA 权重融合后怎么恢复基础模型

来源:17golang原创

时间:2026-10-05 06:50:22 291浏览 收藏

在 Diffusers 里调用 fuse_lora() 之后,LoRA 增量已经写进底层模型权重。此时只调用 disable_lora() 或 unload_lora_weights(),并不等于把融合增量从基础权重里减掉。恢复方法取决于三个事实:融合的是一个还是多个 LoRA、当前进程是否还保留融合上下文、融合后的管线是否已经保存并重新加载。

官方文档:https://huggingface.co/docs/diffusers/main/en/using-diffusers/merge_loras

先记结论
  • 仅加载、没有融合:停用或卸载 LoRA 即可。
  • 当前管线只融合了一个 LoRA:先 unfuse_lora(),再 unload_lora_weights()。
  • 融合了多个 LoRA:官方文档要求重新加载基础模型。
  • 融合结果已保存并重新加载:把它视为新的完整检查点,想回到基础模型就从原始模型 ID 重新加载。

先看清:融合改变的是基础权重

load_lora_weights() 把 LoRA 作为独立适配器加载到管线中;基础权重仍然存在,适配器也可以单独切换。fuse_lora() 则把 LoRA 的增量直接融合到 UNet、Transformer 或文本编码器的原始权重中,以减少推理时的适配器开销。两种状态看起来都能生成 LoRA 风格图像,但恢复方式完全不同。

Diffusers LoRA 加载、融合与恢复的权重边界原创关系图
图1:LoRA 从独立适配器写入基础权重后,恢复动作必须匹配当前状态。

我处理这类问题时,会先问一句:现在的 LoRA 还是“挂载层”,还是已经“写入基础权重”?如果只是挂载层,停用或卸载就够了;如果已经写入,必须反融合,或者重建一条干净管线。

最小配方:单个 LoRA 融合后原地恢复

当当前管线只把一个 LoRA 融合进原始模型,而且还没有把融合结果保存后重新加载时,官方提供的恢复方法是 unfuse_lora()。恢复基础权重后,再卸载 LoRA 适配器,可让管线回到不携带该适配器的状态。

from diffusers import DiffusionPipeline
import torch

base_model_id = "stabilityai/stable-diffusion-xl-base-1.0"
lora_repo_id = "your-org/your-sdxl-lora"

# 创建原始基础模型管线
pipe = DiffusionPipeline.from_pretrained(
    base_model_id,
    torch_dtype=torch.float16,
).to("cuda")

# 加载一个 LoRA,并给它明确名称
pipe.load_lora_weights(lora_repo_id, adapter_name="style")

# 把这个 LoRA 融合进当前模型权重
pipe.fuse_lora(adapter_names=["style"], lora_scale=1.0)

# 撤销单个 LoRA 对基础权重的融合
pipe.unfuse_lora()

# 移除不再需要的 LoRA 层和配置
pipe.unload_lora_weights()

这里的顺序很重要:unfuse_lora() 处理“权重已经相加”的问题,unload_lora_weights() 处理“适配器模块仍然挂在模型上”的问题。只卸载适配器而不反融合,已写入基础权重的效果可能仍然保留。

关键 API 各自负责什么

API作用能否恢复已融合权重
disable_lora()临时停用已加载的 LoRA 层,便于在同一管线中比较不能替代反融合
unload_lora_weights()移除 LoRA 层和相关配置单独调用不能保证撤销已融合增量
unfuse_lora()从当前模型权重中撤销融合可以,但官方限定为只融合一个 LoRA 的情况
from_pretrained()从原始基础模型重新构建干净管线可以,也是多融合和状态不明时的稳妥方案

另一个容易混淆的点是 set_adapters()。它负责选择和缩放已加载的适配器,而 fuse_lora() 才会把选中的增量写入底层权重。调整适配器权重和恢复基础权重不是同一个动作。

三种状态对应三种恢复策略

Diffusers LoRA 未融合、单融合和多融合状态的恢复方式原创关系图
图2:恢复方式取决于是否融合、融合数量以及融合权重是否已被保存重载。

状态一:LoRA 只是加载,还没有融合

如果代码只执行了 load_lora_weights(),没有执行 fuse_lora(),可以临时停用,也可以直接卸载。临时停用适合做同种子对照;确定不再使用时再卸载释放结构。

# 临时停用已加载的 LoRA,基础权重本身没有被修改
pipe.disable_lora()

# 需要时重新启用,避免重复加载权重
pipe.enable_lora()

# 确定不再使用时,移除 LoRA 层和配置
pipe.unload_lora_weights()

状态二:当前管线只融合了一个 LoRA

用 unfuse_lora() 撤销融合,再卸载适配器。这是成本最低的原地恢复方式。若恢复后还想用不同的 lora_scale 重新融合,应重新加载或重新启用所需适配器,再按新的比例调用 fuse_lora()。

状态三:融合了多个 LoRA,或融合历史不明确

Diffusers 官方合并 LoRA 文档明确说明:unfuse_lora() 只适用于原始模型仅融合一个 LoRA 的情况;融合多个 LoRA 时需要重新加载模型。不要试图连续调用多次 unfuse_lora() 猜测恢复顺序,因为不同适配器的权重和缩放可能已经共同写入底层参数。

from diffusers import DiffusionPipeline
import torch

# 从原始基础模型 ID 重新构建干净管线
clean_pipe = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
).to("cuda")

已保存融合模型时为什么必须重新加载基础模型

常见部署做法是先融合 LoRA,调用 unload_lora_weights(),再用 save_pretrained() 保存完整管线。这样得到的目录已经包含融合后的模型权重,后续从这个目录加载时,不需要再单独加载 LoRA。代价是这个检查点本身不再等同于原始基础模型。

# 保存的是融合后的完整管线,而不是原始基础模型
pipe.fuse_lora(adapter_names=["style"])
pipe.unload_lora_weights()
pipe.save_pretrained("./fused-pipeline")

# 重新加载融合目录后,如需基础模型,应改用原始模型 ID
base_pipe = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
).to("cuda")

换句话说,unfuse_lora() 是当前运行实例中的撤销动作,不是对任意融合检查点的通用“还原按钮”。如果基础模型来自本地目录,就重新加载融合前保留的那份目录;如果来自 Hub,就保留原始模型 ID、revision 和 variant,避免恢复时误用另一个版本。

完整片段:恢复后做同参数对照

恢复是否成功,最实用的检查方式是使用相同提示词、随机种子、步数、尺寸和调度器,对比恢复后的管线与一条全新基础管线。浮点精度、硬件和注意力实现可能让像素不完全一致,因此重点检查 LoRA 特征是否消失,以及配置和模型来源是否一致。

from diffusers import DiffusionPipeline
import torch

base_model_id = "stabilityai/stable-diffusion-xl-base-1.0"
prompt = "a quiet library interior, soft daylight"

# 当前管线只融合过一个 LoRA,可先原地反融合并卸载
pipe.unfuse_lora()
pipe.unload_lora_weights()

# 用同一基础模型再创建一条对照管线
reference_pipe = DiffusionPipeline.from_pretrained(
    base_model_id,
    torch_dtype=torch.float16,
).to("cuda")

# 为两条管线分别创建相同种子的生成器
generator_a = torch.Generator(device="cuda").manual_seed(2026)
generator_b = torch.Generator(device="cuda").manual_seed(2026)

# 使用相同参数生成恢复结果与基础模型对照结果
restored_image = pipe(
    prompt,
    generator=generator_a,
    num_inference_steps=30,
).images[0]
reference_image = reference_pipe(
    prompt,
    generator=generator_b,
    num_inference_steps=30,
).images[0]

兼容坑与排查要点

  • 多适配器不能原地保证还原:只要调用过一次多 LoRA 融合,就优先重新加载基础模型。
  • 保存顺序会改变产物含义:融合、卸载再保存,得到的是融合检查点;它适合部署,不适合作为可随时还原的基础模型备份。
  • 文本编码器也可能被融合:LoRA 不一定只作用于 UNet,恢复时不要只替换某一个组件就认为全部干净。
  • 编译模型先处理权重状态:若使用 torch.compile,通常应先融合并卸载再编译;需要恢复时,重建管线往往比在编译图上反复切换更清晰。
  • 保留可追溯信息:记录基础模型 ID、revision、variant、LoRA 名称、缩放值和融合顺序,恢复与复现实验都会更可靠。

总结:Diffusers LoRA 融合后的恢复不是单纯“关掉适配器”。单个 LoRA 在当前管线中融合时,使用 unfuse_lora() 撤销权重变化,再用 unload_lora_weights() 清理适配器;多个 LoRA、已保存重载的融合检查点或状态不明时,直接从原始基础模型重新创建管线。把“适配器状态”和“基础权重状态”分开判断,就不会在恢复时误用 API。

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