尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

推理系统落地指南:从模型跑到稳定服务的完整链路

推理系统落地指南:从模型跑到稳定服务的完整链路 很多做算法的同学第一次接触推理系统都是从这句话开始的模型明明已经跑通了测试集上的指标也正常但一到上线就各种出问题。我见过不少团队在“把模型跑通”这一步只花了两天真正做服务化却折腾了两周原因很简单——推理系统不是一个模型脚本它是从模型产物到稳定、高效、可运维的在线服务之间的整套工程链路。这篇内容不会给你列什么环境变量大全也不会把某框架的文档抄一遍而是从“把模型跑起来”和“离服务化还很远”这两个状态之间把中间那一大段没人明说但必须做的事拆开看。无论你手里是一个几百万参数的文本分类模型还是一个几十亿参数的transformer模型或者正在调扩散模型、做YOLOv5模型轻量化、准备把LightGBM回归模型推到线上服务这篇文章都值得看完——因为它讲的不是某个具体框架的用法而是推理系统的通用思维和落地方法。1. 内容整体设计与思路拆解1.1 先厘清概念推理、推理系统、服务化各是什么推理这个词在深度学习圈里已经被说烂了但很多人对它的理解停留在“加载模型喂数据拿输出”这一步。严格来说推理指的是模型在训练完成之后对新输入数据进行前向计算并得到输出的过程。一次前向计算多快通常用“延迟”来衡量单位是毫秒。比如你拿一个LSTM模型做文本分类单条样本在GPU上跑一次可能只要5毫秒这个数字很好看但没人会拿它来说明系统快不快。推理系统就不只是这一个计算过程了。它涵盖模型格式标准化、计算图优化、运行时调度、显存/内存管理、批处理策略、并发控制甚至包括请求怎么排队、超时了怎么处理、GPU显存不够了怎么降级。换句话说推理是“算一下”推理系统是“怎么才能持续、稳定、高效地算很多下”。服务化则是更上一层的事。把模型推理能力封装成标准的接口比如HTTP或gRPC对外提供稳定、可靠、可观测、可伸缩的服务。这时候要考虑的不只是推理快不快还有接口的可用性、请求的限流熔断、模型的版本管理、服务的容量评估、日志和指标监控。我用一个生活化的类比来说这三者的差别。推理是你照着菜谱做出一道菜这道菜好不好吃取决于你的手艺和食材推理系统是厨房里的全套设备和出菜流程从洗菜切菜到装盘出餐都有人管目标是稳定出菜服务化是你把这家店开在闹市区每天面对几百个顾客你得考虑排队叫号、翻台率、厨师会不会累倒、菜凉了怎么保温。很多人以为自己完成了第一步就想直接开连锁店中间的厨房工程和门店运营全被跳过了。1.2 为什么“能跑”和“服务化”之间差了十万八千里我见过太多的项目死在“模型能跑”这个幻觉里。线下Demo阶段模型脚本跑的很好测试集准确率95%推理一次只要30毫秒于是产品经理觉得两周就能上线。结果一上生产环境问题全冒出来了。第一是性能差距。单次推理30毫秒不代表并发100路请求的时候还能保持30毫秒。GPU的算力是固定的但显存带宽、锁竞争、内存拷贝、请求排队都会让延迟急剧恶化。更麻烦的是推理服务通常被要求保证P99延迟也就是说99%的请求都得在目标时间内返回单次平均延迟漂亮根本没用。第二是可靠性差距。训练脚本挂了你重启一下损失的是几分钟的算力。在线服务挂了用户直接无法使用损失的是真金白银。模型推理服务要应对断电、网络抖动、GPU驱动异常、显存泄漏要能自动恢复、优雅退出、不丢请求。第三是可观测性差距。脚本只看输出就行服务必须能看到延迟分布、吞吐量、饱和度、错误率、GPU利用率、显存水位这些指标。出了问题没有指标排查起来就像摸黑找东西。第四是治理差距。模型上线不是一锤子买卖你会有V1、V2、V3要灰度发布、要能回滚要按用户或业务线做配额限制。这些事在“能跑”阶段根本不会遇到但在服务化阶段全是必答题。从工程投入来看把模型跑通可能只占整个项目20%的工作量剩下80%都在围绕推理系统和服务化做文章。这不是某一类业务的特例做YOLOv5模型轻量化端侧部署也好做LLM在线服务也好背后的逻辑是共通的。1.3 推理系统的核心链路一条流水线上的四个环节拆开推理系统你会发现它本质上是一条流水线任何一个环节没做好最终服务都会出问题。我在实际项目中习惯把它拆成四个环节。模型预处理是所有事情的起点。训练出来的PyTorch权重、TensorFlow的SavedModel先要统一格式转成ONNX、TensorRT的engine或者TorchScript。这个环节还要做计算图优化、算子融合、量化和剪枝。很多人忽略这一步觉得直接拿原始模型跑不就行了但原始框架的推理性能通常只有优化后的五到八成显存占用还可能高三四倍。推理引擎是真正干活的环节。它负责把计算图调度到硬件上执行管理显存、内存、线程池做内核优化。市面上的选择很多CPU上有ONNX Runtime、OpenVINOGPU上有TensorRT、vLLM、Triton Inference Server。选型没有绝对的好坏只看你的业务场景和硬件条件。服务封装是把引擎包装成对外可用接口的环节。需要设计同步还是异步接口、要不要做动态批处理、支不支持流式输出、请求超时和排队策略怎么定、健康检查和优雅退出怎么实现。这块最容易被做算法的同学忽略因为写算法的人脑子里装的是张量是模型结构很少会去想HTTP状态码和连接池。运维治理是最后一个但绝不意味着它不重要。你要有监控指标能看出服务是否健康要有日志能定位问题要设计好限流和降级策略保护服务不被突发流量打垮要考虑模型更新时怎么平滑替换不掉线还要算清楚每GPU卡能扛多大流量、成本是否划算。这四环是串在一起的前一环的输出就是后一环的输入。模型格式没统一好引擎选型再先进也白搭引擎性能不够服务封装做得再漂亮也难支撑高并发。后面我会逐个环节讲实操细节和踩坑经验。2. 核心细节解析与实操要点2.1 模型转换与优化先统一格式再谈性能模型格式统一这件事听起来很基础但很多人是在上线第二天才意识到它的重要性。你想想公司里可能同时存在PyTorch训练出的transformer模型、TensorFlow的LSTM、用于数据挖掘的LightGBM回归模型如果每个模型都用原生框架去做推理那你得同时维护多套推理环境升级一个CUDA版本都要祈祷别把其他框架搞崩。ONNX作为中间表示格式是解决多框架混乱的标准方案。PyTorch的torch.onnx.export、TensorFlow的tf2onnx都能把各自框架的模型转为ONNX让下游推理引擎用统一的方式加载和推理。转换过程中图结构会被标准化很多框架特有的算子会被重写为ONNX标准算子。真正坑人的是动态shape。训练好的transformer模型在推理时输入序列长度是不固定的如果你在导出ONNX时没处理好动态轴导出的模型只能接受固定长度的输入序列长一点直接报错。我踩过的坑是导出ONNX时忘了保存tokenizer的padding规则结果线上来的请求长度比训练时短模型输出的分类概率明显偏移。这个问题后来花了半天时间对比输入输出才定位到是padding长度不一致导致的。导出ONNX时要把batch维度和序列长度维度都标记为动态代码里就是dynamic_axes这个参数不要漏。模型优化是另一个大头。剪枝、权重共享是结构层面的事见效慢、风险高量化是性价比最高的手段。FP16半精度几乎是无损的显存减半速度通常能提升一到两倍。INT8量化的压缩比更高但精度会有不同程度衰减大型transformer模型尤其明显。做量化要准备一套校准数据集拿真实数据分布去校准缩放因子量化完必须在验证集上跑一遍对比不是看一眼准确率差不多就行了。还有一个容易忽略的点算子融合。ONNX Runtime和TensorRT在加载模型时会自动把Attention里的矩阵乘法和激活函数融合减少内核启动次数和显存读写。但前提是模型是结构清晰的、没有乱七八糟的自定义算子。如果训练时用了自定义的CUDA算子导出时ONNX不认那融合就无从谈起性能直接拉胯。2.2 推理引擎选型没有最好的只有最合适的推理引擎这块天天有人争论PyTorch好还是TensorRT好其实这种争论没什么意义。选什么引擎首先要看你跑在什么硬件上、什么业务场景。CPU上推理适合的是中小模型和对延迟容忍度较高的场景。比如你有一个训练好的LightGBM回归模型做预估或者一个LSTM模型做序列分类请求量不大CPU上的延迟完全能接受。ONNX Runtime在这方面做得比较成熟支持AVX512指令集多线程调度也很稳定。很多中小型推荐系统、风控系统用CPU做在线推理是很常见的事不一定非要上GPU。GPU上推理是另一套逻辑。重计算密集型模型比如transformer、扩散模型CPU根本顶不住。GPU推理首选TensorRT它对计算图做层融合、内核自动调优在同等精度下通常能比PyTorch原生推理快两到四倍。但TensorRT有它自己的脾气模型结构对TensorRT版本很敏感算子不支持或版本不匹配就会转换失败。如果是大语言模型的高并发服务vLLM是目前绕不开的选择。它在显存管理上做了PagedAttention把显存利用率拉得很高再配合continuous batching能让并发吞吐打满。后面我会专门讲怎么用它部署。还有一类引擎是更偏服务化的比如NVIDIA的Triton Inference Server它把模型加载、并发调度、批处理、统计指标都做进了服务端支持多框架多模型统一管理。如果你的业务复杂到要同时部署多个模型、要求高吞吐低延迟甚至要做模型Ensemble集成Triton是很合适的选择。引擎适合场景核心优势主要注意点PyTorch原生开发调试、低并发原型上手快、不用转换格式吞吐性能和显存利用都不理想ONNX RuntimeCPU/GPU中小模型跨平台、多框架兼容、安装简单需要先转ONNX图优化不如专用引擎深TensorRTGPU重计算模型推理速度极快、显存优化好构建engine复杂、算子兼容性要求高vLLM大语言模型服务PagedAttention显存管理、continuous batching主要面向LLM其他模型支持有限Triton多模型、高并发生产服务并发调度完善、多框架支持配置复杂度高、学习曲线陡峭我自己的选型逻辑很简单手里模型结构复杂、对延迟极度敏感、能接受投入时间做优化调优的选TensorRT模型结构标准化程度高、要做快速验证多框架兼容的选ONNX Runtime做大模型对话或生成式任务的直接vLLM起步。别一上来就追求最强的把能跑的最小闭环先跑通再逐步优化选型。2.3 服务封装把模型变成API的关键设计服务封装是整个推理系统里最容易被“懂算法不懂工程”的人搞砸的部分。我在评估一个推理服务成熟度的时候第一眼不是看模型准确率而是看接口设计。同步接口是最常见的客户端发一个HTTP请求服务端同步推理返回结果。适合单次请求耗时短的场景。但真正高并发的服务不能一直用同步阻塞的方式做否则一个慢请求拖垮所有线程池。异步接口、消息队列、请求排队这些机制都要考虑进去。很多做算法的不良习惯是有什么请求立刻开线程去算结果并发一高线程数暴增CPU空转延迟飙升。批处理是推理服务吞吐量提升的关键手段之一。GPU推理有个特点单条数据和多条数据的计算时间差不了多少也就是说batch越大每条数据均摊成本越低。传统批处理是把一段时间内的请求攒一批再统一推理积累到一定数量或一定时间窗口就触发。更高阶的是动态批处理边积攒请求边调度资源请求来了先排队满了就跑一批不等不靠。vLLM的continuous batching就是这种设计。流式输出是大模型服务特有的需求。普通模型的推理结果是一整个矩阵等算完返回JSON就行。大模型生成式推理是逐字输出的如果等全部生成完再返回用户等待时间可能是几十秒。流式输出按token推送配合Server-Sent Events或WebSocket用户能看到打字机效果。这块改造会牵扯到接口协议、前端适配、网络超时设置工作量不小。超时控制是我反复强调的重点。一个推理服务要设置三层超时网关层的总超时、推理引擎单次推理的超时、队列等待的超时。每层超时时间要合理设定过大导致请求积压雪上加霜过小误杀慢请求。我遇到过线上问题前端等了60秒没有响应就报错结果发现是后端队列无限制积压慢请求把排队时间拉长到两分钟早该用超时切断。服务封装的细节还包括模型预热和优雅退出。加载权重之后先跑几条验证数据把GPU内核、缓存都拉起来否则第一个真实请求会慢上一个量级。进程退出时要等正在处理的推理请求结束再关闭而不是一刀切强杀。这些都是生产环境要设计的细节不是“能用就行”的范畴。3. 实操过程与核心环节实现3.1 先从一个小模型开始导出ONNX到部署HTTP服务这一节我带你完整走一遍从PyTorch模型到在线服务的流程用的案例是一个LSTM文本分类模型结构简单非常适合理解推理系统各环节的作用。假设你已经训练好一个LSTM模型输入层是token id序列输出层是一个二分类概率。第一步是导出ONNX格式。导出前要确认模型的动态轴设置否则上线遇到变长序列就完蛋。PyTorch导出ONNX的代码大概长这样import torch model.eval() dummy_input torch.randint(0, 1000, (1, 32)) # batch1, seq_len32 torch.onnx.export( model, dummy_input, lstm_cls.onnx, opset_version14, input_names[input_ids], output_names[logits], dynamic_axes{ input_ids: {0: batch_size, 1: seq_len}, logits: {0: batch_size} } )这里dynamic_axes是关键把batch_size和seq_len都设为动态这样运行时模型就能接受不同长度的输入。导出过程中可能遇到不支持导出的算子解决方法通常是升级PyTorch版本或者把自定义算子重写为标准算子。导出成功之后用ONNX Runtime加载推理import onnxruntime as ort import numpy as np sess ort.InferenceSession(lstm_cls.onnx, providers[CUDAExecutionProvider]) inputs sess.get_inputs()[0].name outputs sess.get_outputs()[0].name # 假设输入是形状为 (1, 32) 的 int64 token ids res sess.run([outputs], {inputs: np.random.randint(0, 1000, (1, 32)).astype(np.int64)}) print(res[0])这时候模型已经跑在ONNX Runtime上了单次推理速度可能比PyTorch原生还快一些因为ONNX Runtime做了图优化和算子融合。但这两步只完成了“能跑”和“稍微快点跑”离服务化还差很远。下一步是封装成HTTP服务。我一般用FastAPI响应体用Pydantic定义方便校验参数和自动生成文档from fastapi import FastAPI, HTTPException from pydantic import BaseModel import onnxruntime as ort import numpy as np app FastAPI() class PredictRequest(BaseModel): token_ids: list[int] max_len: int 32 class PredictResponse(BaseModel): label: int probability: float sess ort.InferenceSession(lstm_cls.onnx, providers[CUDAExecutionProvider]) def preprocess(token_ids: list[int], max_len: int) - np.ndarray: # 补齐或截断到固定长度 if len(token_ids) max_len: token_ids token_ids[:max_len] else: token_ids token_ids [0] * (max_len - len(token_ids)) return np.array([token_ids], dtypenp.int64) app.post(/predict, response_modelPredictResponse) def predict(req: PredictRequest): try: arr preprocess(req.token_ids, req.max_len) logits sess.run(None, {input_ids: arr})[0] proba float(1.0 / (1.0 np.exp(-logits[0][0]))) label 1 if proba 0.5 else 0 return PredictResponse(labellabel, probabilityproba) except Exception as e: raise HTTPException(status_code500, detailstr(e)) app.get(/healthz) def healthz(): return {status: ok}这里我额外加了一个/healthz健康检查接口是为了后面接负载均衡和K8s探针做准备。没有健康检查的服务上线后万一进程僵死但端口还开着负载均衡还会继续往死节点分发请求这时候线上问题会非常难排查。服务跑起来之后用压测工具看一眼基本性能。我习惯用abApacheBench简单压一下比如ab -n 1000 -c 20 -p request.json -T application/json http://127.0.0.1:8000/predict压测结果里重点看两个指标Requests per second和Time per request。如果QPS低得离谱先排查是不是CPU核心没充分利用ONNX Runtime的线程数默认可能只有1个。给SessionOptions加线程数配置通常能立竿见影地提升吞吐。3.2 用vLLM把大模型跑成高并发在线服务中小模型做服务化核心矛盾是性能优化。但在大语言模型这个赛道问题更加棘手——模型动辄几十GB显存放不下推理一次要算几百亿次乘加并发一高显存直接爆掉。vLLM这套开源推理框架正是因为解决了LLM推理的显存和调度问题这两年才会这么火。vLLM最核心的技术是PagedAttention。它借鉴了操作系统虚拟内存的分页思想把KV Cache切成固定大小的块需要才分配不用就释放避免了传统方案中预分配一大块显存导致的大量浪费。其次它实现了continuous batching请求不用等一个batch完全结束才开始下一个batch只要有空闲的KV Cache块新请求就能插入当前batch显存利用率高了很多。部署vLLM服务很简单一条命令就能把主流开源LLM拉起来。以Qwen系列为例vllm serve Qwen/Qwen2-7B-Instruct \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --tensor-parallel-size 1 \ --dtype auto \ --port 8000这里几个参数我逐个解释一下。--gpu-memory-utilization 0.9表示vLLM最多可用GPU显存的90%。剩下的10%留给CUDA上下文、激活值和其他开销。如果你设成1.0大概率会OOM因为总得留点缓冲给运行环境。--max-model-len是模型支持的最大序列长度包括输入的prompt和输出的response加在一起。这个值越大KV Cache占用越大能支撑的并发就越少。很多人上来就设32768结果显存预算根本撑不住几个请求并发直接被压到个位数。--tensor-parallel-size是张量并行度。单卡能放下就设为1多卡就按卡数设。这里有个容易踩的坑如果两张卡显存型号不一致并行推理时速度会被慢的那张卡拖住导致整体吞吐不升反降。服务起来之后用OpenAI兼容接口调用from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY ) resp client.chat.completions.create( modelQwen/Qwen2-7B-Instruct, messages[{role: user, content: 给我讲一个程序员的笑话}], temperature0.7, max_tokens512 ) print(resp.choices[0].message.content)大模型服务评估指标和普通模型不太一样重点看两项TTFT首token延迟和TPOT每个token的生成时间。TTFT太长说明排队严重或者prefill阶段计算开销大TPOT太长说明decode阶段被卡住了。vLLM自带metrics接口从/metrics拉指标能直接看到这两个数据。实际压测时我之前用7B模型配单张A10080GBmax-model-len设8192并发拉到32路TTFT稳定在300到500毫秒TPOT在20毫秒左右。这时候GPU利用率能跑到90%以上显存几乎打满。这个表现传统推理方案很难达到验证了vLLM在LLM服务化里的优势。但vLLM不是万能的。它默认支持的是主流LLM架构如果你的模型结构不标准或者用了自定义算子就得自己写支持代码工作量不小。另外vLLM的批处理策略倾向于最大化吞吐对单请求延迟的保障不如专门的在线推理引擎精细。如果你既要做LLM又要做其他模型的一致路由可以考虑前面加一层代理网关后面接不同引擎。3.3 生产环境服务化的隐藏清单很多刚开始接触服务的同学以为“接口能响应”就万事大吉了但生产环境不是这么回事。我整理了一份上线前必须自查的清单每一条都是踩过坑换来的。健康检查是第一条。K8s里liveness探针和readiness探针如果没配好服务进程活着但推理能力已经退化比如显存泄漏导致OOM边缘负载均衡照样把流量打进来。健康检查接口应该检测的不只是进程状态最好还能反映依赖资源的状态比如GPU是否健康、显存是否充足。指标暴露是第二条。Prometheus格式的/metrics接口在中小团队里不一定有完善的可观测平台但你至少要能回答三个问题当前QPS是多少P99延迟是多少GPU显存还有多少没有这三样出问题只能靠用户反馈太被动了。日志策略是第三条。日志最好结构化用JSON格式输出打上模型版本号和服务版本号这样排查问题时能清楚知道某个请求是被哪一版模型服务的。我还遇到过一个问题日志里堆了海量的debug信息生产环境磁盘直接被打满排查花了不少力气从此日志级别必须严格区分。限流和降级是第四条。要有针对单用户的速率限制防止个别用户把整个服务的算力打满同时准备降级策略比如当排队队列超过阈值时返回“繁忙”而不是无限等待。很多做算法的同学在写接口时会忽略这点但高并发服务没有限流就像高速路没有收费站车一多全堵死。模型版本管理是第五条。服务端应该能同时加载多个版本的模型通过不同的路由路径或请求头来切换。这样灰度发布时可以先让5%流量到新模型对比一段时间效果确认无误再全量切换。如果新模型效果不行一条命令就能切回旧版本。模型加载优化是第六条。大模型权重动辄几个GB到几十GB每次冷启动进程都要重新加载权重耗时好几分钟。上线前要把模型持久化挂载到本地磁盘或者直接编译成engine构建镜像减少启动时间。成本评估是第七条。每张GPU卡能扛多少QPS需要几块卡才能支撑业务峰值这些数字在服务化之前就要算清楚。我见过不少项目模型效果很好但推理成本太高一个请求要算三秒钟业务根本无法规模推广最后只能回去做模型压缩或选更小的底座模型。这些点每一个都值得单独展开但在实操中它们是交织在一起的。健康检查、指标、日志、限流、版本管理往往没有现成的代码模板能直接套需要根据团队的基础设施做适配。我的建议是别追求一步到位先保证核心健康检查和种子指标再逐步完善。4. 常见问题与排查技巧实录4.1 模型加载慢、首Token延迟高怎么排查模型冷启动加载权重占用了大量时间尤其在大模型场景几个GB权重从硬盘读进来再初始化到显存这个过程跑个几十秒是常事。很多人第一反应是“磁盘太慢”于是加钱上NVMe SSD结果发现提升有限。真正的瓶颈往往在反序列化权重的CPU环节或者GPU上下文初始化。排查思路是分段计时把模型加载拆成文件读取、权重反序列化、显存拷贝、初始化预热四个阶段看哪一段耗时最长再对症下药。首Token延迟高的问题是另一类。请求来了之后系统要先做prefill阶段把整个输入序列都算一遍生成第一个token这个过程输入长度越长耗时越久。如果线上业务经常出现超长输入考虑限制最大输入长度或者把公共的、反复出现的prompt部分做成前缀缓存避免重复计算。vLLM的prefix caching功能就是干这个的打开之后相同前缀的请求可以复用之前的KV Cache首Token延迟能明显下降。4.2 并发一上来就OOM或者超时怎么办最典型的场景是压测时QPS稍微提上去一点服务直接报显存OOM或者延迟从20毫秒暴涨到两秒。这个问题背后通常是两个原因。第一个原因是显存碎片化严重。传统深度学习框架的显存分配方式是预分配大块显存再按需切分高并发下KV Cache反复申请释放显存碎片越积越多最终有大块连续显存分配不出来就OOM了。vLLM的PagedAttention正是为了解决这个问题才诞生。用传统引擎做LLM推理要么换vLLM要么限制并发数给每个请求预分配充足显存。第二个原因是线程池被慢请求拖垮。同步接口场景下线程池大小是固定的如果某个请求特别慢比如输入序列特别长计算时间要好几秒它占住线程后续所有请求都在排队。排查方法是看线程池活跃度确认是不是个别慢请求堵住了整个队列。解决思路是设置合理的超时时间或者改成异步批处理模式不让单请求占死线程。显存排查的命令也很常规nvidia-smi看当前显存占用nvidia-smi dmon实时监控显存变化配合系统的日志看OOM发生的时间点反推是哪个环节分配的显存。OOM的日志里通常有当前显存分配块的信息别只看到“CUDA out of memory”就慌往上看日志拿到具体分配请求大小和当时的显存碎片情况。4.3 线下模型指标正常一到线上就慢或者不准这种现象我见过太多次。一种情况是线上输入和线下测试集分布不一致。线下测试用的是干净、标准化的数据线上真实用户输入五花八门有超长文本、有乱码、有特殊符号预处理逻辑一旦没覆盖到这些情况输入长度暴涨导致计算量骤增延迟自然飙升。解决方案是预处理阶段做输入长度和内容的兜底清洗并提前定义阈值超长就截断不该进模型的直接挡在外面。另一种情况是精度漂移。模型在FP32下验证acc是95%上服务时为了性能切了FP16或INT8精度掉到80%以下。这种情况多数是量化校准集选取不当造成的。校准集应该能代表真实线上数据的分布如果你拿训练集的子集做校准而线上数据和训练集差异很大量化后的模型就在真实分布上犯错误。排查方法是先在测试环境跑一批线上真实请求的镜像数据对比FP32和INT8的输出差异看是本可接受的微小误差还是系统性偏差。如果差异集中在某些特定输入上优先补充这类输入到校准集里重新量化。结构层面的问题也有。TensorRT对模型结构比较敏感动态shape处理不好会导致算子无法融合性能不升反降。碰到这种情况别硬调TensorRT参数先把模型结构拆开看找到输出出问题的子图能合并的算子尽量合并能写成标准算子的就别用自定义实现。4.4 服务“看起来正常”但调用方始终报超时这一类问题最气人服务端口通着healthz接口也没挂但真正的推理接口就是不返回。排查方向要跳出模型本身往网络和服务框架层面看。首先确认负载均衡和后端服务的连接是否正常。如果后端进程因为句柄泄漏或线程崩溃处于半死状态此时连接可以建立但请求得不到处理前面的LB探测不一定能发现问题。解决办法是设置更严格的readiness探针不只是TCP连通性检测而是真的发一个轻量推理请求返回正常才给流量。其次是Python这类语言的GIL问题。如果你的推理服务是Python写的多线程并发推理时GIL会限制CPU层面的并行能力。GPU推理本身不太受GIL影响因为算力主要在GPU上但数据预处理、tokenizer、后处理这些CPU操作都会排队。优化手段是把预处理做成独立的异步管道不要和推理线程抢GIL极端情况下把服务改成多进程部署每个进程持有独立模型实例。再一个是日志阻塞问题。日志模块如果使用同步写盘在高并发时IO会拖慢整个进程。我碰到过某次线上排查发现服务一直在正常处理但请求在凌晨流量高峰时会卡住几秒最后定位是日志写入盘IO饱和把请求线程阻塞了。解决办法是日志采用异步写入生产环境控制日志级别必要的时候临时关闭debug日志。最后别忽略GC暂停。Java写推理服务相对少见但如果用了Java的Triton client就会出现这种情况JVM在做GC时用户线程会暂停。低并发时几乎察觉不到高并发时GC暂停会被放大。要关注服务日志里有无频繁的Full GC调整堆参数和GC策略后能明显缓解。综合来看这些问题的排查思路是一致的先看指标再分段打点最后定位到是模型计算慢、网络阻塞、还是资源竞争。很多问题没有银弹靠的是对系统的细致观察和科学排查方法。5. 最后再分享一点个人心得如果你手里有一个模型正在走向服务化我的建议是先把最小闭环跑通模型导出、引擎推理、HTTP接口、健康检查这四步看起来简单但已经覆盖了推理系统的主干。在此基础上再加监控、限流、版本管理逐步完善。我在实际项目中反复体会最深的一点是推理系统的性能不是堆出来的而是设计出来的。PagedAttention、Continuous Batching这些漂亮方案的核心思想都是更精细地管理资源、更聪明地调度请求。当你的服务遇到瓶颈别急着换更大号的GPU先看看资源的利用率和分配策略是不是合理。最后一个小技巧给你的服务保留一个debug模式。平时不开启问题排查时动态打开把模型推理的输入输出、耗时明细都记录下来。这个功能会帮你节省大量定位时间也会让你对模型的线上行为有更深的把握。把模型跑起来只是第一步把它变成稳定、高效、能治理的服务这条路没有捷径但绝对值得走。
返回列表