5.1 自定义模型与微调


5.1 自定义模型与微调集成

本节导读:当业务模型不是HuggingFace现成格式、或是微调产物(LoRA适配器、自研架构)时,如何让它跑上vLLM的高性能引擎?本节覆盖自定义模型接入、多LoRA动态服务、量化基座组合三大场景。

学习目标

  • 了解vLLM模型注册机制与自定义模型类的实现要点
  • 掌握LoRA适配器在vLLM中的静态与动态服务方式
  • 学会多租户场景下的LoRA热切换与并发管理
  • 理解"基座+适配器"与"合并后单模型"两条路线的取舍

核心概念

vLLM的模型加载机制

vLLM启动时按 config.jsonarchitectures 字段在注册表中查找对应实现类。接入自定义模型有三条路:

路线 做法 适用
复用现有架构 微调不改结构(最常见) 99%的业务场景,直接加载
转换为标准格式 导出为LLaMA/Qwen等标准结构 自研但结构相似
注册新架构 实现 *ForCausalLM 类并注册 真正的新架构

绝大多数"自训练模型"其实只是LoRA/全参微调产物,结构未变,走第一条路即可。

LoRA在vLLM中的服务形态

LoRA微调产物 = 基座权重 + 小体积适配器(几MB~几百MB)。两种服务方式:

  • 合并式merge_and_unload() 把适配器并进基座,产出一个独立模型。简单高效,但N个业务要N份完整模型副本;
  • 动态式:vLLM原生多LoRA——一份基座常驻显存,按请求携带不同适配器。显存省、切换快,是多租户的标准答案。

关键参数

--enable-lora # 开启LoRA运行时 --max-loras 4 # 并发驻留的适配器数 --max-lora-rank 32 # 支持的最大rank --max-cpu-loras 16 # CPU侧缓存池(热备)

适配器按LRU在GPU/CPU间换入换出,max_loras 建议设为并发租户数并留余量。

分步实战

步骤 1:合并式部署(最简单)

from peft import PeftModel from transformers import AutoModelForCausalLM base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", torch_dtype="bfloat16") merged = PeftModel.from_pretrained(base, "my-lora-adapter").merge_and_unload() merged.save_pretrained("my-merged-model") # tokenizer一并拷贝后,直接 vllm serve my-merged-model

步骤 2:动态多LoRA服务

vllm serve Qwen/Qwen2.5-7B-Instruct \ --enable-lora \ --max-loras 4 --max-lora-rank 32 \ --lora-modules \ legal=./adapters/legal \ medical=./adapters/medical \ cs=./adapters/customer-service

步骤 3:请求级路由到不同适配器

from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY") resp = client.chat.completions.create( model="legal", # 直接用适配器名当model messages=[{"role": "user", "content": "合同中定金条款的风险?"}], )

对租户侧完全透明:切换领域=换一个model名,无需新建连接或实例。

步骤 4:运行时热加载/卸载

# 上新适配器不停服 curl -X POST http://localhost:8000/v1/load_lora_adapter \ -d '{"lora_name": "finance", "lora_path": "./adapters/finance"}' curl -X POST http://localhost:8000/v1/unload_lora_adapter \ -d '{"lora_name": "cs"}'

新业务上线→load;旧版本下线→unload。配合CI产出适配器目录,可实现"训练完成即自动上架"。

步骤 5:自研架构注册(进阶)

from vllm.model_executor.models.registry import register_model from vllm.model_executor.models import LlamaForCausalLM @register_model("MyNetForCausalLM") class MyNetForCausalLM(LlamaForCausalLM): """结构与LLaMA兼容的自定义变体:覆写需要差异化的层""" ...

要点:①实现与基类一致的 forward() 签名;②config.jsonarchitectures 填注册名;③优先继承最接近的现有类,只覆写差异部分。这条路维护成本高,能转标准格式就转。

完整示例:多租户客服平台

平台上有12个行业客户,各自一个LoRA适配器(rank=16,约50MB):

部署形态:单实例 Qwen2.5-7B 基座 + --enable-lora --max-loras 8 --max-cpu-loras 32 访问方式:model=<tenant_lora_name> 成本对比:合并式 12份7B副本 ≈ 168GB显存 动态式 1份基座 + 适配器池 ≈ 16GB显存

峰值并发时LRU自动把冷租户适配器换到CPU,热租户保持GPU驻留,显存开销几乎与租户数无关。

常见问题 FAQ

Q1:加载LoRA报错 rank 超限?
适配器rank超过 --max-lora-rank(默认16)。查看适配器 adapter_config.json 中的 r 字段,把启动参数调到不小于该值。rank越大适配器越占显存,16/32覆盖绝大多数场景。

Q2:LoRA推理和合并后推理结果一致吗?
数学上等价,工程上LoRA运行时有轻微额外计算开销(吞吐略降),合并式吞吐最优。租户少且固定时优先合并式;租户多、更新频繁用动态式。

Q3:量化基座能挂LoRA吗?
部分组合可行:GPTQ/AWQ基座+LoRA在vLLM中有支持,但需适配器与量化方案配套训练(如用QLoRA流程)。上线前务必做精度对比,量化+LoRA的双重近似可能叠加放大误差。

Q4:训练用PEFT保存的适配器直接报"不是有效的LoRA"?
检查目录结构:需要 adapter_config.jsonadapter_model.safetensors。若保存的是完整模型(save_pretrained输出含大权重文件),说明训练时已合并,直接当普通模型部署即可。

Q5:多个适配器能同时命中一个请求吗?
不能,一个请求只挂一个适配器。需要"复合能力"时应在训练侧混合数据训练单一适配器,而非运行时叠加。

最佳实践与避坑

  • 避坑一:基座与训练时的基座不一致(如训练用Instruct版、部署用Base版)。适配器是对特定基座的增量,基座不匹配会导致输出混乱,部署配置中显式固定基座版本号;
  • 避坑二--max-loras 设得过小导致频繁换入换出,吞吐抖动。观察指标中的lora换页次数,调大或把高频租户常驻;
  • 实践:适配器目录纳入版本管理(git或制品库),lora_name 中带上版本号(legal-v3),灰度与回滚都在路由层完成;
  • 实践:每次基座升级(哪怕小版本)都要对全部适配器跑回归评估,基座权重的微小变化可能被适配器放大。

本节小结

本节的核心结论:微调产物接vLLM首选"标准结构直接加载",多租户用"一份基座+动态LoRA"获得数量级的显存节约;真自研架构才走注册路线。配套的版本纪律(基座锁定、适配器入库、回归评估)决定这套体系能否长期稳定运转。下一节看vLLM的另一块扩展疆土——多模态。

延伸阅读


作者与出处
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 引力.04c560的小龙虾 转发
评论区 (0)
U