mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1225 字
4 分钟
模型服务化
2025-02-22

某金融科技公司上线了智能投顾服务,初期用 Flask + HuggingFace Transformers 包装模型 API,单机 QPS 仅 15,P99 延迟 8 秒,高峰期请求大量超时。切换到 vLLM + Continuous Batching 后,同等硬件下 QPS 提升至 180,P99 延迟降至 1.2 秒,且支持动态扩缩容应对流量波动。

模型训练完成只是起点,如何将模型高效、稳定地服务化,才是 LLM 落地的关键工程挑战。

一、推理框架选型#

1. 为什么不能用 HuggingFace Transformers 直接服务#

HuggingFace Transformers 是研究和实验的标准工具,但直接用于生产服务存在严重问题:

  • 显存碎片化:为每个请求预分配固定大小的 KV Cache,大量显存被浪费
  • 静态批处理:同一 batch 中的请求必须等最慢的完成才能处理下一批
  • 无量化支持:只能用 FP16/BF16 推理,显存占用大
  • 单请求串行:无法充分利用 GPU 并行能力

2. 主流推理框架对比#

框架核心优化量化支持吞吐量易用性
vLLMPagedAttention, Continuous BatchingAWQ, GPTQ
TGIFlashAttention, Continuous Batchingbitsandbytes中高
TensorRT-LLMKernel 融合, CUDA 优化FP8, INT8极高
SGLangRadixAttention, 连续批处理FP8, GPTQ
LMDeployTurboMind 引擎AWQ, W4A16

3. 选型决策#

追求易用性和快速上线 → vLLM
追求极致性能(NVIDIA GPU)→ TensorRT-LLM
HuggingFace 生态深度用户 → TGI
需要前缀缓存和 RadixAttention → SGLang

二、vLLM#

1. PagedAttention#

vLLM 的核心创新是 PagedAttention,借鉴操作系统虚拟内存分页机制管理 KV Cache:

传统方式:
为每个请求预分配最大长度的连续 KV Cache
→ 显存利用率低(约 20-40%),碎片严重
PagedAttention:
将 KV Cache 分成固定大小的块(block),按需分配
→ 显存利用率接近 100%,支持更多并发请求
# vLLM 初始化
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3-8B-Instruct",
tensor_parallel_size=2, # 2 卡张量并行
gpu_memory_utilization=0.9, # GPU 显存使用率
max_model_len=8192, # 最大序列长度
enforce_eager=False, # 启用 CUDA Graph 优化
)

2. Continuous Batching#

传统静态批处理中,先完成的请求必须等待最慢的请求,GPU 利用率低。Continuous Batching(连续批处理)在请求完成后立即将其移出 batch,新请求即时填入:

静态批处理:
Batch [Req1, Req2, Req3] → 等最慢完成 → 下一批
Req1 先完成但空等,GPU 闲置
Continuous Batching:
时刻 t: [Req1, Req2, Req3]
时刻 t+1: [Req2, Req3, Req4] ← Req1 完成,Req4 立即填入
GPU 始终满载

3. 部署配置#

# vLLM 服务启动配置
host: 0.0.0.0
port: 8000
model: meta-llama/Llama-3-8B-Instruct
tensor-parallel-size: 2
gpu-memory-utilization: 0.9
max-num-batched-tokens: 8192
max-num-seqs: 256
quantization: awq
max-model-len: 8192
# 启动 vLLM OpenAI 兼容 API 服务
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-8B-Instruct \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--quantization awq \
--max-model-len 8192

三、TGI#

1. 核心特性#

TGI(Text Generation Inference)是 HuggingFace 官方的推理服务框架:

特性说明
FlashAttention高效注意力计算,降低显存占用
Continuous Batching持续批处理,提升吞吐量
Speculative Decoding推测解码加速推理
Watermark模型输出水印,检测生成内容
OpenAI 兼容 API无缝替换 OpenAI 接口

2. Docker 部署#

docker run --gpus all -p 8080:80 \
-v $PWD/data:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id meta-llama/Llama-3-8B-Instruct \
--quantize bitsandbytes-nf4 \
--max-input-length 2048 \
--max-total-tokens 4096 \
--cuda-graph

3. 客户端调用#

from openai import OpenAI
# TGI 兼容 OpenAI API
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="not-needed",
)
response = client.chat.completions.create(
model="meta-llama/Llama-3-8B-Instruct",
messages=[
{"role": "user", "content": "解释量子计算的基本原理"},
],
max_tokens=512,
temperature=0.7,
)

四、TensorRT-LLM#

1. 极致优化原理#

TensorRT-LLM 是 NVIDIA 官方的推理优化框架,通过多层编译优化实现极致性能:

  • Kernel 融合:将多个算子合并为一个 CUDA Kernel,减少显存读写
  • 自动调优:针对目标 GPU 自动选择最优 Kernel 配置
  • FP8 量化:利用 H100 的 FP8 硬件加速
  • KV Cache 量化:压缩 KV Cache,支持更长上下文

2. 构建与部署#

from tensorrt_llm import LLM, BuildConfig
build_config = BuildConfig(
max_batch_size=128,
max_input_len=2048,
max_output_len=512,
quantization="fp8",
enable_context_fmha=True, # FlashAttention
)
llm = LLM(
model="meta-llama/Llama-3-8B-Instruct",
build_config=build_config,
tp_size=2,
)
Info

TensorRT-LLM 的 Engine 构建过程较慢(可能需要数十分钟),但构建完成后推理性能极佳。适合模型固定、追求极致性能的生产场景。模型更新时需要重新构建 Engine。

五、生产部署架构#

1. 部署架构选择#

模型规模并行策略推荐框架硬件需求
1-3B单卡vLLM, TGI1 × A10G
7B单卡/双卡 TPvLLM, TGI1-2 × A100
13B双卡 TPvLLM, TensorRT-LLM2 × A100
70B4-8 卡 TPvLLM, TensorRT-LLM4-8 × H100
100B+TP + PPTensorRT-LLM8+ × H100

2. 负载均衡与扩缩容#

class LLMGateway:
"""LLM 推理服务网关:负载均衡 + 健康检查"""
def __init__(self, backends: list):
self.backends = backends
def route_request(self, request):
# 基于负载的智能路由
healthy = [b for b in self.backends if b.is_healthy()]
if not healthy:
raise ServiceUnavailable("No healthy backend")
# 选择当前负载最低的后端
backend = min(healthy, key=lambda b: b.current_load())
return self.forward(backend, request)
def forward(self, backend, request):
try:
return backend.generate(
prompt=request.prompt,
max_tokens=request.max_tokens,
temperature=request.temperature,
)
except Exception as e:
backend.mark_unhealthy()
raise

3. Kubernetes 部署#

apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-inference
spec:
replicas: 3
template:
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
resources:
limits:
nvidia.com/gpu: 2
memory: "64Gi"
env:
- name: VLLM_MODEL
value: "meta-llama/Llama-3-8B-Instruct"
- name: VLLM_TP
value: "2"

4. 性能监控#

指标说明告警阈值
请求延迟 P99推理请求最大延迟> 3s
首 Token 延迟 TTFT首 Token 返回时间> 500ms
吞吐量 QPS每秒处理请求数< 预期 50%
GPU 利用率GPU 计算资源使用率< 60%
KV Cache 利用率KV Cache 块使用率> 90%
请求排队数等待处理的请求数> 100

六、多模型服务#

1. 模型路由#

生产环境中往往需要同时服务多个模型,根据请求特征路由到不同模型:

class ModelRouter:
"""基于请求特征的模型路由"""
def route(self, request):
# 简单问题路由到小模型,复杂问题路由到大模型
if request.estimated_complexity == "simple":
return self.get_model("llama-3-8b")
elif request.estimated_complexity == "complex":
return self.get_model("gpt-4")
# 按成本预算路由
if request.budget == "low":
return self.get_model("llama-3-8b-awq")
return self.get_model("llama-3-70b")

2. 模型热切换#

LoRA 微调的模型支持动态加载切换,无需重启服务:

# vLLM 多 LoRA 服务
llm = LLM(
model="meta-llama/Llama-3-8B-Instruct",
enable_lora=True,
max_loras=4, # 同时加载 4 个 LoRA
max_lora_rank=16,
)
# 请求时指定 LoRA
response = client.chat.completions.create(
model="meta-llama/Llama-3-8B-Instruct",
messages=[...],
extra_body={"lora_name": "legal-lora"}, # 使用法律领域 LoRA
)

七、服务化实践清单#

实践项说明优先级
选择推理框架根据性能需求和团队能力选择
配置 Continuous Batching提升吞吐量的核心手段
启用量化降低显存占用和推理成本
设置健康检查保证服务可用性
配置负载均衡支持水平扩展
实现模型路由多模型协同服务
监控指标埋点延迟、吞吐、GPU 利用率
设置限流熔断防止过载导致雪崩
KV Cache 监控避免显存不足影响服务

模型服务化是 LLM 从实验走向生产的最后一公里。选对推理框架、配好批处理和量化、做好监控和扩缩容——这三步决定了推理服务的成本、延迟和稳定性。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

模型服务化
https://blog.souloss.com/posts/ai-engineering/ai-engineering-model-serving/
作者
Souloss
发布于
2025-02-22
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时