微调模型部署
模型微调好了,下一步是把它部署成一个服务让应用调用。本篇介绍从最简单到最高性能的几种部署方案。
1. 部署前:合并 LoRA 权重
LoRA / QLoRA 训练得到的是适配器权重,需要先合并到基座模型上:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct"
LORA_DIR = "./output_sft"
MERGED_DIR = "./merged_model"
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
torch_dtype=torch.bfloat16,
device_map="cpu", # 显存不够就用 CPU 合并
)
model = PeftModel.from_pretrained(model, LORA_DIR)
model = model.merge_and_unload()
model.save_pretrained(MERGED_DIR, safe_serialization=True)
tokenizer.save_pretrained(MERGED_DIR)
合并后的目录是一个标准的 HuggingFace 模型,可以被任何工具加载。
2. 方案一:Transformers 直接加载(最简单)
适合本地测试、低并发场景。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_DIR = "./merged_model"
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoModelForCausalLM.from_pretrained(
MODEL_DIR,
torch_dtype=torch.bfloat16,
device_map="auto",
)
def chat(user_input):
messages = [{"role": "user", "content": user_input}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
outputs = model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
temperature=0.7,
top_p=0.9,
)
return tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(chat("你好"))
优点: 简单、灵活 缺点: 推理慢、无 batching、无 KV cache 优化
3. 方案二:用 FastAPI 包装成 HTTP 服务
新建 server.py:
import torch
from fastapi import FastAPI
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_DIR = "./merged_model"
# 启动时加载模型(只加载一次)
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoModelForCausalLM.from_pretrained(
MODEL_DIR,
torch_dtype=torch.bfloat16,
device_map="auto",
)
model.eval()
app = FastAPI()
class ChatRequest(BaseModel):
messages: list
max_tokens: int = 512
temperature: float = 0.7
@app.post("/chat")
def chat(req: ChatRequest):
text = tokenizer.apply_chat_template(req.messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=req.max_tokens,
do_sample=True,
temperature=req.temperature,
)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
return {"response": response}
启动:
测试:
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "你好"}]}'
4. 方案三:vLLM(生产推荐)
vLLM 是 UC Berkeley 出品的高性能推理引擎,吞吐量是 transformers 的 10-20 倍。
安装
启动 OpenAI 兼容服务
python -m vllm.entrypoints.openai.api_server \
--model ./merged_model \
--served-model-name my-model \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 4096
用 OpenAI SDK 调用
vLLM 完全兼容 OpenAI API 格式:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="any-key", # vLLM 默认不校验 key
)
response = client.chat.completions.create(
model="my-model",
messages=[{"role": "user", "content": "你好"}],
max_tokens=512,
)
print(response.choices[0].message.content)
这意味着你可以用任何支持 OpenAI 的客户端(LangChain、OpenWebUI 等)调用你的微调模型。
vLLM 关键参数
| 参数 | 说明 |
|---|---|
--tensor-parallel-size |
张量并行数,多卡时设为 GPU 数 |
--max-model-len |
最大上下文长度 |
--gpu-memory-utilization |
GPU 显存使用率,默认 0.9 |
--quantization |
量化方式:awq、gptq、fp8 |
--enable-lora |
启用动态 LoRA 切换 |
vLLM 动态加载多个 LoRA
vLLM 一个亮点:不合并 LoRA,运行时动态切换:
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--enable-lora \
--lora-modules translate=./lora_translate summarize=./lora_summarize
请求时通过 model 字段指定哪个 LoRA:
5. 方案四:Ollama(本地最便捷)
Ollama 是面向个人开发者的本地推理工具,操作极简。
安装
转换模型为 GGUF 格式
Ollama 使用 GGUF 格式(CPU/GPU 都能跑)。先用 llama.cpp 转换:
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
pip install -r requirements.txt
# 转换为 fp16 GGUF
python convert_hf_to_gguf.py /path/to/merged_model --outfile model.gguf
# 量化(可选,进一步压缩)
./llama-quantize model.gguf model-q4_k_m.gguf q4_k_m
创建 Modelfile
FROM ./model-q4_k_m.gguf
TEMPLATE """{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}<|im_end|>
{{ end }}<|im_start|>assistant
{{ .Response }}<|im_end|>
"""
PARAMETER stop "<|im_end|>"
PARAMETER stop "<|im_start|>"
注册并使用
也可以用 HTTP API:
curl http://localhost:11434/api/chat -d '{
"model": "my-model",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'
6. 方案对比
| 方案 | 适用场景 | 吞吐 | 易用度 |
|---|---|---|---|
| Transformers | 测试、低并发 | 低 | ⭐⭐⭐⭐⭐ |
| FastAPI 包装 | 简单 Web 服务 | 低 | ⭐⭐⭐⭐ |
| vLLM | 生产环境、高并发 | 高 | ⭐⭐⭐⭐ |
| Ollama | 本地、桌面应用 | 中 | ⭐⭐⭐⭐⭐ |
| TGI | 生产环境(HuggingFace 出品) | 高 | ⭐⭐⭐ |
| LMDeploy | 国产工具,对中文优化 | 高 | ⭐⭐⭐ |
7. 量化部署(省显存关键)
部署时常见的量化方案:
AWQ(Activation-aware Weight Quantization)
精度高,推理快,生产推荐:
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
MODEL_DIR = "./merged_model"
QUANT_DIR = "./merged_model_awq"
quant_config = {"zero_point": True, "q_group_size": 128, "w_bit": 4}
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoAWQForCausalLM.from_pretrained(MODEL_DIR)
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(QUANT_DIR)
tokenizer.save_pretrained(QUANT_DIR)
vLLM 直接支持加载 AWQ 模型:
GPTQ
老牌量化方案,社区支持广泛。
GGUF(CPU/Mac 友好)
llama.cpp 的格式,支持 Q2/Q4/Q5/Q8 多档量化,能在 CPU 和 Apple Silicon 上跑。
8. 性能压测
部署后做一下压力测试,确认吞吐和延迟:
或简单的 Python 脚本:
import time
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="any")
async def request(i):
start = time.time()
response = await client.chat.completions.create(
model="my-model",
messages=[{"role": "user", "content": "写一首关于春天的诗"}],
max_tokens=200,
)
return time.time() - start
async def main():
tasks = [request(i) for i in range(50)]
durations = await asyncio.gather(*tasks)
print(f"50 个请求总耗时: {sum(durations):.2f}s")
print(f"平均延迟: {sum(durations)/len(durations):.2f}s")
print(f"P50: {sorted(durations)[25]:.2f}s")
print(f"P95: {sorted(durations)[47]:.2f}s")
asyncio.run(main())
9. 生产部署 Checklist
上线前确认:
- 健康检查:HTTP 服务有
/health端点 - 限流:避免单个用户打满资源
- 超时:长请求自动取消
- 日志:记录请求、响应、耗时
- 监控:QPS、延迟、显存、GPU 利用率
- 降级:模型异常时返回友好错误
- 版本管理:模型和代码独立版本号
- A/B 测试:新模型上线先小流量验证
- 灰度发布:10% → 50% → 100% 流量切换
- 回滚预案:能 1 分钟内切回旧版本
10. Docker 化部署
打包成 Docker 镜像方便部署:
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04
RUN apt-get update && apt-get install -y python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install vllm
COPY merged_model /app/model
WORKDIR /app
EXPOSE 8000
CMD ["python3", "-m", "vllm.entrypoints.openai.api_server", \
"--model", "/app/model", \
"--host", "0.0.0.0", \
"--port", "8000"]
总结
- LoRA/QLoRA 模型部署前先用
merge_and_unload()合并权重 - vLLM 是生产环境首选,吞吐高、兼容 OpenAI API、支持量化和动态 LoRA
- Ollama 是本地最便捷方案,适合桌面应用
- 量化部署用 AWQ 性价比最高,配合 vLLM 使用
- 生产部署关注健康检查、限流、监控、灰度发布
- Docker 化让部署可复制、可回滚