4. 模型推理框架:Triton Inference Server部署、vLLM与TensorRT-LLM对比、推理优化策略
模型训练完了,怎么让它跑起来?
这个问题,我当年刚入行时觉得很简单——写个Flask接口,load模型,predict就完事了。结果呢?第一个线上事故就教会了我:推理框架选不对,后面全是坑。
今天咱们聊聊三个主流方案:Triton、vLLM、TensorRT-LLM。它们各有脾气,选对了事半功倍。
4.1 Triton Inference Server:企业级推理的“瑞士军刀”
Triton是NVIDIA开源的推理服务器。它最大的特点就是啥都能跑——TensorFlow、PyTorch、ONNX、TensorRT,甚至自定义后端。我习惯叫它“万能插座”。
4.1.1 部署实战
部署Triton其实不复杂。核心就三步:准备模型仓库、写配置文件、启动服务。
第一步:模型仓库结构
model_repository/
├── bert_onnx/
│ ├── 1/
│ │ └── model.onnx
│ └── config.pbtxt
├── resnet50_trt/
│ ├── 1/
│ │ └── model.plan
│ └── config.pbtxt
└── ensemble_model/
├── 1/
└── config.pbtxt
每个模型一个文件夹,版本号用数字表示。嗯,这里要注意:版本号必须从1开始,不然Triton会找不到模型。
第二步:配置文件
name: "bert_onnx"
platform: "onnxruntime_onnx"
max_batch_size: 32
input [
{
name: "input_ids"
data_type: TYPE_INT64
dims: [128]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [768]
}
]
dynamic_batching {
preferred_batch_size: [4, 8, 16]
max_queue_delay_microseconds: 100
}
这里有个坑:dynamic_batching一定要配。我曾经有个项目没配这个,结果并发一上来,GPU利用率只有20%。配了之后直接飙到85%。
第三步:启动服务
docker run --gpus all -p 8000:8000 -p 8001:8001 -p 8002:8002 \
-v /path/to/model_repository:/models \
nvcr.io/nvidia/tritonserver:23.10-py3 \
tritonserver --model-repository=/models
端口8000是HTTP,8001是gRPC,8002是Prometheus监控。我个人习惯用gRPC,性能比HTTP好30%左右。
--model-control-mode=explicit,可以手动控制模型加载。这样上线新模型时不用重启服务。
4.2 vLLM vs TensorRT-LLM:大模型推理的“双子星”
大模型火了之后,vLLM和TensorRT-LLM成了最热门的两个框架。它们都针对Transformer做了极致优化,但思路完全不同。
| 对比维度 | vLLM | TensorRT-LLM |
|---|---|---|
| 核心优化 | PagedAttention + 连续批处理 | 图优化 + 内核融合 + 量化 |
| 易用性 | ⭐️⭐️⭐️⭐️⭐️(开箱即用) | ⭐️⭐️⭐️(需要编译) |
| 吞吐量 | 高(动态批处理) | 极高(静态图优化) |
| 延迟 | 中等 | 低(首token延迟更优) |
| 模型支持 | LLaMA、ChatGLM、Qwen等 | LLaMA、GPT、BLOOM等 |
| 量化支持 | FP16、INT8 | FP16、INT8、INT4、FP8 |
你看,vLLM胜在简单,TensorRT-LLM胜在极致性能。怎么选?
我个人经验:快速验证用vLLM,生产压榨用TensorRT-LLM。有个项目,我们用vLLM三天就上线了,后来换成TensorRT-LLM,吞吐量又提了40%。
4.2.1 vLLM部署示例
from vllm import LLM, SamplingParams
# 加载模型
llm = LLM(model="meta-llama/Llama-2-7b-chat-hf",
tensor_parallel_size=2,
gpu_memory_utilization=0.9)
# 配置采样参数
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512
)
# 推理
outputs = llm.generate(["请介绍一下深度学习"], sampling_params)
print(outputs[0].outputs[0].text)
就这么几行代码,一个7B模型就跑起来了。你想想看,以前用HuggingFace的transformers,同样的模型,吞吐量差5倍不止。
4.2.2 TensorRT-LLM部署示例
TensorRT-LLM就麻烦一些,需要先编译模型:
# 1. 下载模型权重
git lfs clone https://huggingface.co/meta-llama/Llama-2-7b-chat-hf
# 2. 编译TensorRT引擎
python build.py --model_dir ./Llama-2-7b-chat-hf \
--dtype float16 \
--use_gpt_attention_plugin float16 \
--use_gemm_plugin float16 \
--max_batch_size 32 \
--max_input_len 2048 \
--max_output_len 512 \
--output_dir ./trt_engines/llama_7b
# 3. 运行推理
python run.py --engine_dir ./trt_engines/llama_7b \
--tokenizer_dir ./Llama-2-7b-chat-hf \
--max_output_len 512 \
--input_text "请介绍一下深度学习"
max_batch_size、max_input_len、max_output_len这三个参数一旦确定就不能改了。我建议设大一点,比如max_batch_size设64,虽然编译慢点,但后面灵活。
4.3 推理优化策略:从“能用”到“好用”
框架选好了,模型能跑了,但离“好用”还差得远。我总结了一套优化策略,按优先级排序:
4.3.1 批处理优化
说白了就是让GPU一次干更多活。Triton的dynamic_batching、vLLM的continuous batching都是干这个的。
我曾经遇到一个场景:用户请求的输入长度差异很大,短的几十个token,长的上千。如果等长填充,浪费严重。后来用了vLLM的动态批处理,吞吐量直接翻倍。
4.3.2 量化压缩
模型太大?量化一下。从FP16降到INT8,模型大小减半,速度提升30%,精度损失不到1%。
TensorRT-LLM支持INT4量化,我试过把LLaMA-13B从26GB压到7GB,还能跑出不错的效果。当然,量化后一定要做精度验证,不然上线后出问题就麻烦了。
4.3.3 KV Cache优化
大模型推理时,KV Cache占显存大头。vLLM的PagedAttention就是解决这个问题的——像操作系统的虚拟内存一样,按需分配。
我建议:监控KV Cache的命中率。如果命中率低于80%,说明批处理策略有问题,需要调整。
4.3.4 请求调度
请求来了,先处理谁?
- FCFS(先来先服务):简单,但长请求会阻塞短请求
- 最短作业优先:延迟低,但可能饿死长请求
- 带权重的调度:给VIP用户更高优先级
我个人习惯用混合策略:短请求优先,但给长请求设置超时保护。这样既保证了响应速度,又不会让长请求一直等。
4.4 避坑指南
最后分享几个我踩过的坑:
- 显存泄漏:Triton的Python后端容易泄漏,建议用C++后端或者定期重启
- 模型版本管理:别用latest标签,用具体版本号。不然回滚时哭都来不及
- 预热:新模型上线前,先发几个请求预热。不然第一个请求延迟会很高
- 监控:一定要监控GPU利用率、显存、请求延迟。我习惯用Prometheus + Grafana
好了,这一章就到这里。下一章咱们聊聊模型服务的高可用架构——怎么让推理服务7x24小时不宕机。