LLM 微调入生产:QLoRA 与 RLHF 实战
训练只是一半的战斗。当你的微调模型验证集 loss 达到满意的值,更难的挑战才刚开始:如何在生产环境中可靠地提供服务、追踪模型行为,以及在持续训练时管理模型版本。
与本站另一篇文章的分工:本地 LLM 微调指南 覆盖训练流程——如何在本地用 Ollama + Unsloth 做 QLoRA 训练。本文关注训练完之后的生产部署问题。
训练完了,然后呢?
微调产出的是一个 .safetensors 权重文件(或合并后的 Llama/Mistral 格式)。但生产环境需要的是:
- 服务层:能高效处理并发请求的推理服务
- 版本管理:记录哪个模型版本对应哪个训练数据和配置
- A/B 测试:新版本如何安全地上线,不影响现有用户
- 监控:模型行为是否发生了漂移?质量是否下降?
- 回滚:出问题时能在几分钟内切回旧版本
这五个问题,每一个都是独立的工程挑战。训练可以用脚本解决,生产部署需要基础设施。
模型服务层
选项一:vLLM(GPU 服务首选)
vLLM 是目前 GPU 部署开源 LLM 最主流的方案,核心优势是 PagedAttention 和 Continuous Batching:
- PagedAttention:把 KV cache 切成 page 管理,显著减少显存碎片,支持更大的 batch size
- Continuous Batching:不等 batch 凑齐就处理请求,降低 P95 延迟
# 用 vLLM 部署微调后的模型
pip install vllm
python -m vllm.entrypoints.openai.api_server \
--model ./my-finetuned-model \
--tensor-parallel-size 2 \
--max-model-len 4096 \
--port 8000vLLM 暴露 OpenAI 兼容的 API,所以现有的 OpenAI SDK 代码基本不需要改动——只改 base_url 就能切换到自部署模型。
关键指标要主动暴露:
- TTFT(Time to First Token):用户感知的响应速度
- Token Throughput(tokens/second):服务的整体吞吐
- P95 Latency:尾部延迟,反映最差体验
选项二:Ollama(轻量/边缘部署)
对于小规模部署(< 10 并发)或者边缘设备,Ollama 更简单。它自动处理量化和模型格式转换:
# 将微调模型转换为 Ollama 格式
ollama create my-model -f Modelfile
# Modelfile 示例
FROM ./merged-model-directory
PARAMETER temperature 0.7
PARAMETER num_ctx 4096
SYSTEM "你是一个专业的代码审查助手。"选项三:HuggingFace Inference Endpoints
HuggingFace Inference Endpoints 是托管方案,适合不想自己维护 GPU 集群的场景。上传模型到 HuggingFace Hub,几分钟内就能得到一个 API endpoint,支持自动扩缩容。代价是成本比自建高,数据隐私需要额外考虑。
模型版本管理
核心原则:永远不要覆盖生产中的模型版本。 每次训练都创建新版本,保留旧版本热备。
用 MLflow 追踪版本
MLflow 可以记录每次训练的完整元数据:
import mlflow
with mlflow.start_run(run_name="llama3-v2-2025-12"):
# 记录训练配置
mlflow.log_params({
"base_model": "unsloth/llama-3.2-3B",
"dataset_version": "customer-support-v3",
"lora_r": 16,
"learning_rate": 2e-4,
"epochs": 3,
})
# 记录训练指标
mlflow.log_metrics({
"val_loss": 1.23,
"eval_accuracy": 0.89,
})
# 注册模型版本
mlflow.pyfunc.log_model(
artifact_path="model",
python_model=model_wrapper,
registered_model_name="customer-support-llm",
)每个注册的模型版本会自动打上时间戳和 git commit hash,方便之后溯源。
用 W&B Model Registry
W&B Model Registry 是另一个成熟方案,特别适合团队协作。它支持模型的生命周期管理(candidate → staging → production),以及版本之间的 diff 对比。
版本命名建议包含三个维度:
{base_model}-{dataset_version}-{date}
# 例如:llama3.2-3b-custsupp-v3-20251215A/B 测试:新模型安全上线
直接把新模型替换生产环境是最危险的做法。正确的步骤是分阶段放量:
Shadow Mode(影子模式)
新模型与旧模型并行运行,新模型的输出只记录,不影响用户:
async def process_request(prompt: str) -> str:
# 主路径:用旧模型响应用户
production_output = await old_model.generate(prompt)
# 影子路径:异步跑新模型,只记录不返回
asyncio.create_task(
shadow_evaluate(prompt, production_output)
)
return production_output
async def shadow_evaluate(prompt: str, production_output: str):
new_output = await new_model.generate(prompt)
# 记录两个输出供对比分析
await log_comparison(prompt, production_output, new_output)影子模式运行 24-48 小时后,用收集到的数据评估新模型的质量差异,再决定是否正式放量。
流量分割上线
Week 1: 5% → 监控指标 → 通过则继续
Week 2: 20% → 监控指标 → 通过则继续
Week 3: 50% → 监控指标 → 通过则继续
Week 4: 100%(完成上线)每个阶段的通过门槛:
| 指标 | 要求 |
|---|---|
| 任务完成率 | 新模型 ≥ 旧模型 -2% |
| P95 延迟 | 新模型 ≤ 旧模型 +20% |
| 错误率 | 新模型 ≤ 旧模型 +0.5% |
| 用户满意度(如果有) | 新模型 ≥ 旧模型 -5% |
任何指标不达标,暂停放量,分析原因。
RLHF 在生产中的实践
生产环境是最好的 RLHF 数据来源。用户的真实反馈比手工标注更有价值,因为它反映了真实的使用场景和偏好。
收集生产偏好数据
最简单的实现是在回答下面加一个 👍/👎 按钮,记录用户反馈:
# 收集偏好数据
preference_data = {
"prompt": user_prompt,
"chosen": response_user_liked, # 用户点了 👍 的回答
"rejected": response_user_disliked, # 用户点了 👎 或没有互动的回答
}积累几千条偏好数据之后,可以用 DPO(Direct Preference Optimization)微调模型——它比传统 PPO 更稳定,不需要单独训练 reward model:
from trl import DPOTrainer, DPOConfig
training_args = DPOConfig(
beta=0.1, # KL 散度惩罚系数
output_dir="./dpo-output",
num_train_epochs=1,
per_device_train_batch_size=4,
learning_rate=5e-6,
logging_steps=10,
)
trainer = DPOTrainer(
model=model,
ref_model=ref_model, # 原始 SFT 模型作为参考
args=training_args,
train_dataset=preference_dataset,
tokenizer=tokenizer,
)
trainer.train()TRL 库 提供了 DPO、PPO、GRPO 等多种 RLHF 算法的实现,是目前最成熟的 RLHF 训练工具。
监控与回滚
质量监控
传统的模型监控关注延迟和错误率,但 LLM 还需要监控输出质量。实践中常用的方法是 LLM-as-Judge:
async def evaluate_output_quality(prompt: str, response: str) -> float:
"""用另一个 LLM 评估输出质量,返回 0-1 的分数"""
eval_prompt = f"""
评估以下回答的质量,给出 0-10 分,只返回数字:
问题:{prompt}
回答:{response}
评分标准:准确性、相关性、完整性、语言流畅性
"""
score_text = await judge_model.generate(eval_prompt)
return float(score_text.strip()) / 10对生产流量进行 5-10% 的采样,对每条采样的输出做自动评估,建立质量趋势图。质量分数下降超过阈值(比如连续 3 小时低于基线 5%),自动告警。
漂移检测
除了质量分数,还需要检测输出分布的变化:
- 输出长度分布是否改变?
- 特定类型响应(拒绝、不确定等)的比例是否上升?
- 关键词/主题分布是否漂移?
这些指标突然变化往往是模型行为改变的早期信号,比等用户投诉更快发现问题。
回滚预案
回滚要能在 5 分钟内完成:
# vLLM 场景下快速切换模型版本
# 假设 Nginx 做流量控制,upstream 指向不同的 vLLM 实例
# 1. 确认旧版本实例仍在运行(hot standby)
curl http://old-model-service:8000/health
# 2. 切换 Nginx upstream
nginx -s reload # 配置已预先更新为指向旧版本
# 3. 验证流量已切换
curl http://api.example.com/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "customer-support", "messages": [{"role": "user", "content": "test"}]}'
# 4. 确认新版本停止接收请求后,记录问题并安排事后分析关键是旧版本要始终保持热备状态,不能在切换完成前就释放资源。
训练模型相对容易,生产化才是真正的工程挑战。上面这套流程不是一次性建立的,建议从最简单的 vLLM 部署开始,逐步加入版本管理、A/B 测试和监控,每个阶段都要稳定后再推进下一个。