跳转至

微调模型部署

模型微调好了,下一步是把它部署成一个服务让应用调用。本篇介绍从最简单到最高性能的几种部署方案。

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 服务

pip install fastapi uvicorn

新建 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}

启动:

uvicorn server:app --host 0.0.0.0 --port 8000

测试:

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 倍

安装

pip install vllm

启动 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 量化方式:awqgptqfp8
--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:

response = client.chat.completions.create(
    model="translate",   # 或 "summarize"
    messages=[...],
)

5. 方案四:Ollama(本地最便捷)

Ollama 是面向个人开发者的本地推理工具,操作极简。

安装

# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh

转换模型为 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|>"

注册并使用

ollama create my-model -f Modelfile
ollama run my-model

也可以用 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)

精度高,推理快,生产推荐

pip install autoawq
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 模型:

python -m vllm.entrypoints.openai.api_server \
    --model ./merged_model_awq \
    --quantization awq

GPTQ

老牌量化方案,社区支持广泛。

GGUF(CPU/Mac 友好)

llama.cpp 的格式,支持 Q2/Q4/Q5/Q8 多档量化,能在 CPU 和 Apple Silicon 上跑。

8. 性能压测

部署后做一下压力测试,确认吞吐和延迟:

pip install vllm-benchmark

或简单的 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"]
docker build -t my-llm-service .
docker run --gpus all -p 8000:8000 my-llm-service

总结

  • LoRA/QLoRA 模型部署前先用 merge_and_unload() 合并权重
  • vLLM 是生产环境首选,吞吐高、兼容 OpenAI API、支持量化和动态 LoRA
  • Ollama 是本地最便捷方案,适合桌面应用
  • 量化部署用 AWQ 性价比最高,配合 vLLM 使用
  • 生产部署关注健康检查、限流、监控、灰度发布
  • Docker 化让部署可复制、可回滚

评论