简介本资源面向希望掌握大语言模型高效推理部署的开发者与算法工程师聚焦如何借助TensorRT-LLM对Qwen1.5进行推理加速与工程化落地解决模型规模增大后推理速度慢、显存占用高、实时响应难等部署痛点适合具备一定深度学习与GPU环境基础的中高级读者。压缩包共5个文件以4个Python脚本和1份Markdown说明文档为主脚本分别承担模型结构定义、层工具封装、权重转换与推理流程等职责文档则梳理整体操作脉络包体约25KB轻量但结构完整。目前已有593人学习下载说明该方向具备一定关注度。读者可据此获得从模型转换、推理引擎构建到实际部署运行的完整源码与流程教程理解TensorRT-LLM的优化思路与适配方法并借助开源代码进行复用与二次改进降低大模型部署的实践门槛。1. 从一张 24G 显卡说起TensorRT-LLM 部署 Qwen1.5 到底在解决什么手里有一张 24G 显存的卡想跑 Qwen1.5-7B 做企业大模型私有化部署第一反应往往是拿 transformers 直接from_pretrained加载。能跑但吞吐低得让人怀疑人生——单条请求延迟还行一旦并发上来显存和算力全浪费在重复的 KV 计算和没融合的算子上。TensorRT-LLM 就是冲着这个场景来的把 Qwen1.5 的计算图编译成 TensorRT 引擎做算子融合、KV Cache 显存复用、In-flight Batching让同一张卡在本地部署大模型时能扛住真实并发。这篇笔记拆的是「基于 TensorRT-LLM 部署 Qwen1.5 大语言模型」这条路径从权重转换、引擎编译到跑通推理服务、接上 OpenAI 兼容接口再到踩坑排查。适合已经会用 ollama 或 vllm 部署大模型、但发现吞吐或显存利用率到瓶颈的工程师也适合第一次接触 TensorRT-LLM、想照着流程走一遍的新手。核心结论先放这TensorRT-LLM 不是开箱即用的工具它是一次「编译换性能」的交易编译期的坑比运行期多但换来的吞吐提升在 7B 级别通常值得。2. 编译前必须想清楚的三件事版本、精度、显存预算2.1 为什么 TensorRT-LLM 的版本匹配比一般框架更致命TensorRT-LLM 不是一个独立框架它站在三层依赖之上CUDA、TensorRT、以及 PyTorch用于权重转换阶段。这三层任意一层版本错位编译期就会报出让人看不懂的符号错误。血泪经验是不要试图在已有环境里「升级」TensorRT-LLM直接按官方 release 的容器镜像起一个干净环境比修依赖快十倍。常见做法是拉 NVIDIA 官方提供的 TensorRT-LLM 镜像里面 CUDA、TensorRT、mpi、pytorch 都是配好的。如果你坚持裸机装至少要锁死这几个对应关系TensorRT 版本决定trtllm-build支持的量化类型CUDA 版本决定能否用 FP8需要 Hopper 及以上PyTorch 版本影响权重转换脚本能否加载 Qwen1.5 的 safetensors。组件作用版本错位的典型症状CUDA底层算力编译通过但运行报 no kernel imageTensorRT图优化与引擎生成trtllm-build报 plugin 找不到PyTorch权重转换convert_checkpoint 加载 safetensors 失败TensorRT-LLM模型定义与 runtimeimport 时报 undefined symbol选型理由很直接Qwen1.5 在 TensorRT-LLM 里有官方模型定义qwen目录不需要你自己写 attention 插件这是它比一些冷门模型省事的地方。但官方定义会随版本变动所以镜像 tag 要和你参考的文档对齐。2.2 精度选择FP16、INT8 还是 INT4先看你的卡精度直接决定显存占用和是否要额外校准。Qwen1.5-7B 在 FP16 下权重约 14G加上 KV Cache 和运行时开销24G 卡跑单并发没问题但并发一高就吃紧。INT8 权重降到约 7GINT4 降到约 4G代价是精度损失和量化校准的工作量。我一般这样选如果卡是 24G 且只做验证FP16 起步先跑通再谈优化如果要上生产且并发要求高走 INT8 weight-only--use_weight_only --weight_only_precision int8不需要校准数据集掉点可控INT4 适合显存极度紧张的场景但 Qwen1.5 这类模型在 INT4 下长文本生成容易出现重复需要自己评测。提示weight-only 量化只量化权重激活仍是 FP16所以显存节省没有理论值那么大KV Cache 仍占 FP16 空间。真正压 KV Cache 要靠 paged context 或 INT8 KV。2.3 显存预算怎么估别等 OOM 才后悔一个粗略公式显存 ≈ 权重 KV Cache 激活峰值 TensorRT 运行时预留。KV Cache 单 token 占用 2 × 层数 × hidden_size × 精度字节数。Qwen1.5-7B 大约 32 层、hidden 4096FP16 下每 token 约 0.5MB跑 4096 上下文、并发 8就是 0.5MB × 4096 × 8 ≈ 16G这才是真正吃显存的大头。所以编译引擎时--max_batch_size和--max_seq_len不是随便填的它们直接决定运行时预分配的 KV Cache 上限。填大了浪费显存甚至编译失败填小了并发上不去。我的习惯是先按目标并发和上下文长度算出 KV 需求再留 2G 给运行时反推权重能用的精度。3. 把 Qwen1.5 权重转成 TensorRT-LLM 能吃的格式3.1 权重转换convert_checkpoint 到底做了什么TensorRT-LLM 不能直接读 HuggingFace 的 safetensors需要先转成它自己的 checkpoint 格式。这一步做的是把 HF 的权重命名映射到 TensorRT-LLM 的层命名、按张量并行切分、可选地做量化。转换脚本在examples/qwen下命令形态如下。# 进入 TensorRT-LLM 源码目录下的 qwen 示例 cd TensorRT-LLM/examples/qwen # 把 HF 格式的 Qwen1.5-7B-Chat 转成 TRT-LLM checkpoint python3 convert_checkpoint.py \ --model_dir /models/Qwen1.5-7B-Chat \ # HF 权重目录含 config.json 和 safetensors --output_dir /models/qwen1.5-7b-trt \ # 转换后输出目录 --dtype float16 \ # 权重精度可选 float16 / bfloat16 --tp_size 1 # 张量并行度单卡填 1逻辑说明convert_checkpoint.py会读config.json推断层数、head 数、hidden size然后逐张量搬运。--tp_size大于 1 时会把权重按列或按行切开供多卡张量并行使用单卡必须填 1否则引擎加载时会报维度不匹配。--dtype要和后面编译引擎时的精度一致转换用 float16、编译用 bfloat16 会导致数值异常。参数上最容易翻车的是--model_dir的路径它要求目录里同时有config.json、tokenizer.json和 safetensors 权重。如果你从别处下载的权重缺 tokenizer 文件转换能过但后面跑推理时 tokenizer 加载失败。另外 Qwen1.5 的 config 里num_key_value_heads和num_attention_heads不相等GQA转换脚本要能识别老版本脚本对 GQA 支持不全会切错 KV 头这是版本匹配的又一个理由。3.2 量化转换INT8 weight-only 的额外参数如果要走 INT8转换阶段就要加量化参数而不是编译阶段。python3 convert_checkpoint.py \ --model_dir /models/Qwen1.5-7B-Chat \ --output_dir /models/qwen1.5-7b-int8 \ --dtype float16 \ --use_weight_only \ # 开启 weight-only 量化 --weight_only_precision int8 \ # 量化位宽可选 int8 / int4 --tp_size 1逻辑说明--use_weight_only让转换脚本在搬运权重时对线性层权重做 per-channel 或 per-group 量化激活保持 FP16。--weight_only_precision决定位宽。转换完成后输出目录里会多出量化 scale 文件编译引擎时会自动读取。参数说明INT8 weight-only 不需要校准数据集这是它比 smooth quant 省事的地方但如果你要 INT4 且对精度敏感可以加--group_size 128控制分组粒度组越小精度越好、开销越大。转换后建议对比一下输出目录的权重文件大小INT8 应该约为 FP16 的一半如果没变说明量化没生效多半是参数没被识别。3.3 转换后的目录长什么样怎么验证没转坏转换成功后目录里应该有config.jsonTRT-LLM 自己的配置、若干.safetensors分片、tokenizer相关文件、量化时额外的 scale 文件。验证方法不是看文件在不在而是拿转换后的 checkpoint 直接跑一次 TensorRT-LLM 自带的summarize.py做精度对比或者至少用trtllm-build编译一遍编译能过说明权重结构没问题。注意转换阶段不报错不代表权重正确。GQA 头数切错、RoPE 参数没映射都要等推理输出乱码才暴露。所以转换后第一件事是编译并跑一句「你好」看输出是否通顺别急着上并发测试。4. 用 trtllm-build 编译引擎参数怎么设、失败看什么4.1 编译命令与关键参数逐条拆转换出 checkpoint 后用trtllm-build编译成 TensorRT 引擎。这是整个流程里最耗时、最容易失败的一步。trtllm-build \ --checkpoint_dir /models/qwen1.5-7b-trt \ # 上一步转换的输出目录 --output_dir /models/qwen1.5-7b-engine \ # 引擎输出目录 --gemm_plugin float16 \ # GEMM 走 pluginFP16 必开 --gpt_attention_plugin float16 \ # attention 插件精度必开 --max_batch_size 8 \ # 运行时最大并发 --max_input_len 1024 \ # 单条输入最大 token 数 --max_seq_len 4096 \ # 输入输出总长上限 --max_num_tokens 8192 \ # 一个 batch 内总 token 上限 --paged_kv_cache enable \ # 开启分页 KV省显存 --remove_input_padding enable # 去掉 padding提升有效吞吐逻辑说明--gemm_plugin和--gpt_attention_plugin是把矩阵乘和 attention 交给高度优化的插件实现不开这两个性能会退回接近原生 PyTorch编译就白做了。--paged_kv_cache让 KV Cache 按块分配避免为最大长度预留连续显存是并发场景省显存的关键。--remove_input_padding让不同长度的请求打包进同一 batch 而不补零直接提升吞吐。参数说明--max_batch_size、--max_input_len、--max_seq_len、--max_num_tokens四个值共同决定运行时预分配的显存。max_num_tokens要大于等于max_batch_size × max_seq_len的合理子集但不必等于它限制的是单次前向的总 token 数。填得太小长请求会被拒绝填得太大编译期就可能 OOM。我的经验是max_num_tokens取max_batch_size × max_seq_len的一半到三分之二兼顾并发和显存。4.2 编译失败的三种典型报错与定位第一种Assertion failed: engine或 plugin 创建失败。多半是--gemm_plugin精度和权重精度不一致比如权重是 bfloat16 但插件写了 float16。解决是把两者对齐或者干脆都用 float16。第二种编译到一半 OOM。这是max_num_tokens或max_seq_len设太大TensorRT 在构建优化 profile 时就要分配显存。解决是先把这几个值砍半编译通过再逐步往上加找到这张卡的实际上限。第三种Unsupported SM或 kernel 相关错误。这是 CUDA 版本和显卡架构不匹配比如在 Ampere 卡上用了只支持 Hopper 的 FP8 路径。解决是回到 2.1 的版本表确认精度选项和卡架构匹配。4.3 编译产物与加载验证编译成功后output_dir里会有.engine文件和config.json。引擎文件是跟显卡架构绑定的换卡必须重新编译这是 TensorRT-LLM 部署时容易被忽略的约束——你不能把 A100 上编好的引擎直接拷到 4090 上用。验证方式是跑官方run.py或summarize.py加载引擎做一次生成。如果加载时报serialization相关错误通常是引擎和当前 TensorRT 版本不一致重新编译即可。跑通后记录下单条请求的延迟和显存占用作为后面调优的基线。提示引擎编译一次可能几分钟到十几分钟参数调整频繁时建议写个脚本把转换和编译串起来改一个参数重跑一遍别手动敲。5. 起服务、接接口让 Qwen1.5 对外提供 OpenAI 兼容 API5.1 用 trtllm-serve 起一个 OpenAI 兼容服务TensorRT-LLM 自带trtllm-serve可以直接把编译好的引擎起成 HTTP 服务接口形态兼容 OpenAI方便对接 fastgpt 这类上层应用。trtllm-serve \ /models/qwen1.5-7b-engine \ # 引擎目录 --host 0.0.0.0 \ # 监听地址 --port 8000 \ # 端口 --max_batch_size 8 \ # 要和编译时一致 --max_seq_len 4096 \ # 要和编译时一致 --kv_cache_free_gpu_memory_fraction 0.8 # KV Cache 最多占用空闲显存的 80%逻辑说明trtllm-serve内部起的是 TensorRT-LLM 的 runtime加载引擎后暴露/v1/chat/completions和/v1/completions。--max_batch_size和--max_seq_len必须和编译引擎时的值一致或更小填大了运行时会报错。--kv_cache_free_gpu_memory_fraction控制 KV Cache 能吃掉多少空闲显存留一点给激活和运行时避免跑满后 OOM。参数说明这个 fraction 是调优的关键旋钮。设太高长并发下容易 OOM设太低KV Cache 不够请求会被排队或拒绝。0.8 是个保守起点压测后可以往上调到 0.9。如果你的卡还要跑别的进程往下调。5.2 用 curl 验证接口确认真的通了服务起来后别急着接上层先用 curl 打一发确认输出正常。curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen1.5, # 模型名trtllm-serve 不校验具体值 messages: [{role: user, content: 用一句话解释什么是张量并行}], max_tokens: 128, # 生成长度上限 temperature: 0.7 # 采样温度 }逻辑说明请求体走 OpenAI 格式messages是对话历史max_tokens限制生成长度。返回里choices[0].message.content就是模型输出。如果返回空或乱码回到第 3 章检查权重转换尤其是 GQA 和 RoPE。参数说明temperature设 0 做确定性输出方便对比精度max_tokens不要超过编译时的max_seq_len减去输入长度否则会被截断或报错。压测时用stream: true开流式观察首 token 延迟和吞吐。5.3 对接上层应用时的两个现实问题第一个是模型名映射。fastgpt 这类应用会按模型名路由trtllm-serve不强制校验模型名但上层可能要求填一个固定值填qwen1.5或引擎目录名都行关键是和上层配置一致。第二个是并发模型。trtllm-serve的并发能力受max_batch_size和max_num_tokens限制上层如果一次性发几十条请求超出的会被排队。生产环境要么调大编译参数重编引擎要么在上层做限流。别指望一个 7B 引擎扛住无上限并发那不是 TensorRT-LLM 的问题是显存物理上限。注意trtllm-serve适合验证和中小规模真要上大规模生产常见做法是自己写 runtime 集成或者用 Triton Inference Server 的 TensorRT-LLM backend把调度和批处理交给更成熟的 serving 层。6. 避坑与排查五个让我重编引擎的瞬间6.1 现象推理输出重复、乱码、停不下来原因权重转换时 GQA 的 KV 头数映射错误或者 RoPE 的 base 参数没从 config 正确读取。Qwen1.5 用 GQAnum_key_value_heads小于num_attention_heads老版本转换脚本按 MHA 处理就会切错。解决确认 TensorRT-LLM 版本支持 Qwen1.5 的 GQA转换后先跑短句验证。如果已经编了引擎只能回退到转换步骤重来引擎层面改不了。6.2 现象编译通过加载引擎时报 serialization 错误原因引擎是用另一个 TensorRT 版本编译的或者换了显卡架构。引擎文件绑定了 TensorRT 版本和 SM 架构。解决在目标机器上重新编译。别想着跨机器拷贝引擎这是 TensorRT-LLM 和 vllm 这类纯权重加载方案最大的使用差异。6.3 现象并发一上来就 OOM单条却正常原因KV Cache 预分配不够或max_num_tokens设太小导致请求被拒后重试堆积也可能是kv_cache_free_gpu_memory_fraction设太高运行时没有余量。解决先看日志确认是 KV 不足还是激活峰值 OOM。KV 不足就调大 fraction 或重编引擎加大max_num_tokens激活峰值 OOM 就降max_batch_size。用nvidia-smi观察显存曲线区分是稳态占用高还是峰值冲高。6.4 现象吞吐远低于预期GPU 利用率上不去原因没开--paged_kv_cache或--remove_input_padding或者--gemm_plugin没开导致退回慢路径。也可能是请求长度差异大没开 padding 移除时短请求被长请求拖累。解决确认编译参数里三个优化都开了。压测时用混合长度请求观察 batch 内实际有效 token 比例。如果还是低检查是不是 CPU 侧 tokenize 成了瓶颈trtllm-serve的 tokenize 在 CPU 上做高并发下可能拖后腿。6.5 现象INT8 量化后精度掉得厉害原因weight-only INT8 对某些层敏感或者转换时--weight_only_precision写成了 int4 而你没注意。也可能是评测集本身对量化敏感。解决先用 FP16 跑一遍基线再对比 INT8 输出。如果掉点集中在长文本考虑只对部分层量化或者换 INT4 加 group_size 128。量化不是免费的掉点可接受与否取决于你的业务别默认 INT8 无损。7. 压测与调优把这张卡的吞吐榨到边界跑通之后真正决定这套部署值不值得上生产的是压测数据。我一般用trtllm-bench或者自己写脚本打trtllm-serve重点看三个指标首 token 延迟TTFT、每 token 输出延迟TPOT、以及稳定吞吐tokens/s。这三个指标随并发变化的曲线决定了你的服务该限流到多少。一个具体的调优技巧max_num_tokens和max_batch_size不是越大越好。把它们调大编译期显存占用上升运行时 KV Cache 可用空间反而被压缩可能出现「参数调大了吞吐反而降」的玄学现象。正确做法是固定max_seq_len从小到大扫max_batch_size每个值压测一轮找到吞吐拐点。下面这个表格是我在一张 24G 卡上跑 Qwen1.5-7B INT8、max_seq_len4096 时的典型观察数值因卡而异但趋势可参考。max_batch_size稳定吞吐(tokens/s)TTFT(ms)显存占用1基准最低最低4约 2.5 倍略升中等8约 3.5 倍明显升接近上限16不再增长或下降高OOM 风险拐点通常出现在显存快满之前。超过拐点后请求排队导致 TTFT 飙升用户体验反而变差。所以生产环境我会把max_batch_size设在拐点略低的位置留出突发余量而不是顶满。另一个容易被忽略的点是输入长度分布。如果你的业务请求大多是短输入长输出那max_input_len可以设小把省下的显存给 KV Cache反之则调大max_input_len。这两个值在编译期固定改一次要重编引擎所以上线前一定要拿真实请求的分布来定别拍脑袋填 1024。验证调优是否到位我习惯做两件事一是用固定随机种子跑一组标准 prompt记录输出调参前后对比确认优化没改变模型行为二是用nvidia-smi dmon持续采样看 GPU 利用率和显存是否平稳有没有周期性掉底——掉底往往意味着 CPU 侧或调度层有瓶颈不是 GPU 算力不够。最后说个我自己的习惯每次重编引擎前把convert_checkpoint和trtllm-build的完整命令、参数、以及当时的 TensorRT-LLM 版本记到一个build.log里。TensorRT-LLM 的参数组合太多隔两周回头看没有这份记录根本想不起来当时为什么填了某个值。这个习惯帮我省过好几次「重编一遍试试」的时间。部署大模型这件事编译期的确定性比运行期的调优更值得投入因为前者错了后面全白搭。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?