很多人做到这一步已经训练出了一个挺不错的国产大模型微调版本,但随之而来的问题是:
“我怎么把它做成一个 API?”
“怎么上线一套本地服务供团队调用?”
“要不要上 vLLM?用 Docker 好不好?”
我们先快速了解几种常见的部署方式,然后再进入实战。
方式 | 简述 | 优势 | 适合场景 |
---|---|---|---|
transformers + FastAPI |
直接加载模型 + 提供接口 | 简单直观、快速上线 | PoC验证 / 小团队使用 |
vLLM + FastAPI |
高性能推理引擎 + API | 多并发、高吞吐 | SaaS服务 / 多人并发 |
transformers + Docker |
模型与服务打包容器 | 可移植、便于上线 | 部署上线 / 多环境使用 |
FastAPI
封装 + 手动加载 LoRA 模型vLLM
(支持 LoRA 加载 + 批处理 + 热插拔)Docker
我们以 Qwen2.5-Chat-7B 为例,假设你已经完成了 LoRA 微调,输出目录为:./qwen_lora_output
现在我们将:
/chat
路由供外部系统调用# qwen_lora_infer.py
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
BASE_MODEL = "Qwen/Qwen1.5-7B-Chat"
ADAPTER_DIR = "./qwen_lora_output"
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=True)
base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, trust_remote_code=True, device_map="auto")
model = PeftModel.from_pretrained(base_model, ADAPTER_DIR)
def chat_with_model(prompt: str, max_new_tokens=512) -> str:
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=max_new_tokens)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
from qwen_lora_infer import chat_with_model
app = FastAPI()
class ChatRequest(BaseModel):
prompt: str
@app.post("/chat")
def chat(req: ChatRequest):
response = chat_with_model(req.prompt)
return {"response": response}
运行服务:
uvicorn main:app --host 0.0.0.0 --port 8000
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"prompt": "请简要介绍公司差旅报销流程"}'
随着国产模型生态越来越成熟,不少项目都面临这样的问题:
“我们想部署 Qwen + DeepSeek 两套模型,能按业务来切换。”
“能不能动态切换模型,不用每次重启服务?”
“不同部门用不同的精调版本,能统一接入一套系统吗?”
这就是“多模型热切换”的典型需求场景。
关键目标是:
思路:每个模型一个实例,维护一个模型池(dict),按 model_id
路由。
# 模型注册
MODEL_POOL = {}
def load_model(model_id: str):
if model_id in MODEL_POOL:
return MODEL_POOL[model_id]
base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL_MAP[model_id], trust_remote_code=True, device_map="auto")
model = PeftModel.from_pretrained(base_model, ADAPTER_PATH_MAP[model_id])
MODEL_POOL[model_id] = model
return model
API调用示例:
@app.post("/chat")
def chat(req: ChatRequest):
model = load_model(req.model_id)
response = model.chat(tokenizer, req.prompt)
return {"response": response}
缺点:模型多了显存不够 → 后面引入自动释放策略
适合部署多个模型但不希望常驻 GPU 内存。
核心做法:
LRU
或 时间阈值
管理模型生命周期model.cpu()
或 del
释放显存from collections import OrderedDict
import time
MODEL_CACHE = OrderedDict()
MODEL_TIMEOUT = 1800 # 30分钟不使用就卸载
def get_model(model_id):
now = time.time()
if model_id in MODEL_CACHE:
MODEL_CACHE[model_id]['last_used'] = now
return MODEL_CACHE[model_id]['model']
# 加载新模型
base = AutoModelForCausalLM.from_pretrained(...)
model = PeftModel.from_pretrained(base, ...)
MODEL_CACHE[model_id] = {"model": model, "last_used": now}
# 清理旧模型
if len(MODEL_CACHE) > 3:
oldest = sorted(MODEL_CACHE.items(), key=lambda x: x[1]['last_used'])[0][0]
MODEL_CACHE[oldest]['model'].cpu()
del MODEL_CACHE[oldest]
return model
✅ 这样你可以同时支持 Qwen、Baichuan、DeepSeek 等多种模型,按需加载、自动卸载,避免显存崩掉。
需求 | 建议 |
---|---|
多模型切换 | 封装统一接口(API请求中带上 model_id ) |
多租户场景 | 每个业务线一个模型,可映射 用户ID → 模型ID |
精调版本多 | 不要复制整个基础模型,只保留 LoRA adapter 目录即可 |
热加载慢? | 模型加载可使用异步 asyncio 实现预热 |
显存不够? | 尽可能加载为 load_in_4bit=True ,并合理使用 model.cpu() 卸载机制 |
很多团队完成微调之后,下一步就会进入环境打包、服务器部署、团队协作、外部对接等环节。
❓“怎么部署到新服务器?”
❓“我部署了模型,结果别人跑不了?”
❓“我怎么打包一个能复用的本地 AI 服务?”
这时候 Docker 就是你的好帮手。
好处 | 说明 |
---|---|
✅ 可移植 | 一次封装,哪里都能跑(云 / 本地 / K8S) |
✅ 可还原 | 不依赖本机环境,不怕“装不起来” |
✅ 可交付 | 可直接交给同事 / 客户部署 |
✅ 可扩展 | 后期支持模型切换、API拓展更方便 |
qwen-lora-server/
├── Dockerfile
├── requirements.txt
├── app/
│ ├── main.py # FastAPI 服务主入口
│ ├── qwen_lora_infer.py # 模型推理逻辑
├── qwen_lora_output/ # 精调后的 LoRA adapter 目录
FROM nvidia/cuda:11.8.0-runtime-ubuntu22.04
# 安装系统依赖
RUN apt update && apt install -y git curl python3 python3-pip
# 创建目录
WORKDIR /app
# 拷贝代码
COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY ./app ./app
COPY ./qwen_lora_output ./qwen_lora_output
# 设置环境变量
ENV TRANSFORMERS_CACHE=/app/.cache/huggingface
ENV HF_HUB_DISABLE_SYMLINKS_WARNING=1
# 启动 FastAPI 服务
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
transformers==4.38.0
peft==0.8.2
bitsandbytes
accelerate
torch==2.1.0
uvicorn
fastapi
docker build -t qwen-lora-api .
docker run --gpus all -p 8000:8000 qwen-lora-api
✅ 你现在已经拥有了一个「可部署、可复现、精调可用的大模型推理服务容器」。
场景 | 建议 |
---|---|
内网调用 | 使用 --host=0.0.0.0 + 网关反代部署 |
云服务器 | 建议使用 nginx 反代 + TLS证书接入 |
多用户调用 | API 增加 token 鉴权 / 用户隔离机制 |
多服务管理 | 使用 docker-compose 或 supervisor 管理多个服务 |
部署完成只是第一步,更关键的是能不能扛得住真实用户请求,别刚上线就“卡爆、超时、OOM”。
这章我来给你一些实战中真正有用的性能建议,以及一个“轻量网关”搭建方案,适合小团队/企业内部部署使用。
模型设置 | 吞吐速度(tokens/s) | 平均响应时间(256 tokens) | 显存占用 |
---|---|---|---|
FP16 全模型 | ~22 tokens/s | ≈12秒 | 22 GB |
QLoRA(INT4) | ~28 tokens/s | ≈9秒 | 11.2 GB |
QLoRA + 静态 batch | ~45 tokens/s | ≈5秒 | 12.3 GB |
✅ 推荐组合:QLoRA + 推理时静态 batch(batch_size=2~4),可以大幅提升吞吐而不增加显存压力。
默认 FastAPI 是单线程 / 单 worker 的,用来跑模型推理并不高效,推荐升级方案:
# 多进程 + 每进程固定单线程,避免模型权重重复加载
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 1
如果你希望支持高并发 + 任务排队处理:
queue
排队机制(如 FastAPI + Celery + Redis)gunicorn
统一调度多个实例vLLM
,它本身就是为“多并发大吞吐”设计的推理引擎功能 | 说明 |
---|---|
多模型路由 | 根据 model_id 调用不同微调模型 |
日志记录 | 记录每次调用时间、用户、输入、响应 |
超时控制 | 限定推理最大时间,防止模型卡死 |
鉴权支持 | 加入 API Key 或 Token 校验 |
WebUI | 简易管理界面(可选) |
组件 | 工具 |
---|---|
Web 接口 | FastAPI / Flask |
模型调度 | 自定义模型缓存池(见第3章)或 vLLM |
鉴权管理 | JWT / APIKey Header |
任务队列(可选) | Celery + Redis |
日志审计 | loguru / ELK(高级) |
├── /api/
│ └── chat.py # 路由:POST /chat
│ └── models.py # 模型池与动态加载管理
├── /services/
│ └── qwen_lora.py # Qwen微调模型服务
│ └── deepseek_lora.py # DeepSeek微调模型服务
├── /utils/
│ └── logger.py
│ └── auth.py
├── main.py # FastAPI 主启动文件
全面打通了 QLoRA 微调模型从“训练 → 封装 → Docker上线 → 并发调用 → 服务网关”的闭环部署路径。
你已经学会了:
这篇内容我写得比较实在,希望对你在部署国产大模型、搭建多模态服务的路上,真能起到点作用。
如果你觉得有用,或者正好解决了你的一个卡点:
✅ 点个 赞,让我知道你喜欢这类内容
点个 收藏,以后再找就不怕翻记录
点个 关注,后续还有更多实战、案例、脚本更新不断
你的点赞和留言,是我持续更新的最大动力。有问题欢迎评论区交流,看到都会认真回复