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

Hugging Face PEFT 怎样用 LoRA 保存适配器而不保存底模

来源:17golang原创

时间:2026-10-25 00:53:24 357浏览 收藏

我第一次把 LoRA 训练结果交给同事时,最先关心的不是“能不能推理”,而是交付目录里到底装了什么:如果把底模也一起打包,传输、存储和权限范围都会明显变重;如果只留下几兆的适配器文件,接收方又必须知道它依赖哪个底模。PEFT 的保存边界正好解决了这个问题。

官方文档:https://huggingface.co/docs/peft/quicktour、https://huggingface.co/docs/peft/developer_guides/checkpoint

训练完成后,对 PeftModel 调用 save_pretrained(),默认保存适配器权重和配置,而不是完整底模。重新使用时,先按配置准备匹配的底模,再调用 PeftModel.from_pretrained() 挂载适配器。需要完整独立模型时,才考虑合并权重。
要点速览
  • 适配器交付包的核心通常是 adapter_model.safetensors 和 adapter_config.json。
  • 适配器文件变轻,不代表它脱离底模就能单独推理;底模仍是加载依赖。
  • modules_to_save 或自动保存的 embedding 层可能让目录出现额外参数,不能只凭文件名猜内容。

先把底模、适配器和配置分成三类资产

PEFT 的思路是冻结底模,只训练少量额外参数。对 LoRA 来说,真正需要交付的通常是低秩更新矩阵及其配置;底模权重仍由部署方单独准备。这样做的好处不只在于文件变小,也在于底模不必随着每个任务重复复制。

我会把目录里的文件分成三层看:adapter_model.safetensors 是适配器状态,adapter_config.json 描述 PEFT 类型、目标模块和底模标识,底模权重则留在另一套模型目录或仓库中。配置里记录的 base_model_name_or_path 是加载时的重要线索,但它不是底模权重本身。

PEFT 底模权重、LoRA 适配器文件与交付目录的资产边界结构图
图1:资产边界结构图,展示 PeftModel 如何把底模依赖与 LoRA 适配器文件分开;这是静态说明图,不是运行截图。

最容易把底模带进去的风险路径

第一个风险是保存错对象:base_model.save_pretrained() 面向的是完整 Transformer 模型,它的职责不是导出 PEFT 适配器。训练代码最后应明确保存包装后的 PeftModel,不要因为变量名相似就把底模引用传给保存函数。

第二个风险是把“没有底模文件”误解成“适配器可以独立运行”。PEFT checkpoint 的轻量性换来的是一个依赖关系:加载者仍需要可用的底模,并且底模架构、配置和适配器的目标模块要匹配。

第三个风险是忽略额外可训练模块。modules_to_save 可以把指定模块一并保存;某些配置还可能自动决定是否保存 embedding 层。它们仍属于适配器交付范围,但目录大小和加载条件会因此变化。

用一套保存与加载代码控制交付边界

下面的示例只展示 API 关系,不代表已经在本机运行。训练结束后把 peft_model 保存到独立目录,加载时先准备底模,再挂载适配器:

from transformers import AutoModelForCausalLM
from peft import PeftModel, PeftConfig

# 训练阶段得到的对象应当是 PeftModel,而不是裸底模对象。
peft_model.save_pretrained("./artifacts/lora_adapter")

# 部署阶段先读取适配器配置,用它找到训练时记录的底模标识。
config = PeftConfig.from_pretrained("./artifacts/lora_adapter")
base_model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)

# 默认按推理用途挂载;若还要继续训练,可显式传 is_trainable=True。
model = PeftModel.from_pretrained(base_model, "./artifacts/lora_adapter")

这段代码的重点是两个目录角色:保存目录只由 save_pretrained() 管理,底模由 AutoModelForCausalLM.from_pretrained() 单独准备。官方文档说明,PEFT 的保存格式默认使用 safetensors;如果你配置了额外模块,保存内容会随配置扩展。

PEFT save_pretrained 保存适配器、准备底模并由 from_pretrained 挂载的静态关系图
图2:保存、底模准备与适配器挂载的静态关系图;这是结构说明图,不是运行结果。

交付前做文件级审计,再决定是否合并

如果目标是“一个任务一个轻量适配器”,我会先检查保存目录是否包含适配器配置和权重,并把底模依赖写进交付说明,而不是把整个底模复制进来。可以用文件列表做一个低成本的人工审计:

# 只查看适配器目录,确认交付包的文件边界。
find ./artifacts/lora_adapter -maxdepth 1 -type f -print

# 读取配置中的底模标识;不要把它当成底模权重文件。
python -c 'import json; print(json.load(open("./artifacts/lora_adapter/adapter_config.json"))["base_model_name_or_path"])'

如果部署环境无法取得底模,或者你确实需要一个不依赖 PEFT 的完整目录,才考虑 merge_and_unload() 后保存。这样会把 LoRA 更新合进底模,文件会变大,也会失去继续切换多个适配器、禁用适配器等 PEFT 能力;是否合并应由部署约束决定。

相关问题

只保存适配器后能脱离底模推理吗?

不能把它当成独立完整模型。加载时仍要提供训练时对应的底模,配置文件只记录识别信息和适配器参数,不携带完整底模权重。

为什么保存目录里不止两个文件?

除了适配器权重和配置,可能还会有 README、特殊 tokenizer 文件,或由 modules_to_save、embedding 设置带来的额外参数。重点是确认它们是否属于你的交付设计,而不是机械要求目录只能有两个文件。

什么时候应该保存完整模型?

当接收方没有稳定的底模获取方式、推理链路不支持 PEFT,或你更看重单目录部署时,可以选择合并后的完整模型;代价是体积更大,且不再保留原来的适配器操作能力。

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