1、TensorRT动态形状概述
什么是动态形状
动态形状,说白了就是让模型能处理不同大小的输入。
我举个例子你就明白了。假设你训练了一个图像分类模型,输入是 224x224 的图片。但实际部署时,用户上传的图片可能是 1920x1080 的,也可能是 800x600 的。静态形状下,你得先把图片缩放到 224x224 才能推理。动态形状呢?直接扔进去,TensorRT 自己会处理。
在 TensorRT 里,动态形状用三个维度来描述:最小尺寸、最优尺寸和最大尺寸。比如一个输入张量可以定义为:
// 动态形状定义示例
input: [1, 3, -1, -1] // batch=1, channel=3, 高和宽可变
// 实际配置时指定范围
opt_profile:
min: [1, 3, 224, 224]
opt: [1, 3, 1024, 1024]
max: [1, 3, 2048, 2048]
嗯,这里要注意:-1 就代表这个维度是可变的。你可以在构建引擎时指定一个优化范围,TensorRT 会在这个范围内做最优的 kernel 选择。
为什么需要动态形状
我刚开始接触 TensorRT 时也觉得静态形状够用了。直到有一次,我在做视频分析项目——
视频里的每一帧大小都不一样。有的场景是 4K 高清,有的是 720P。如果用静态形状,要么把所有帧都缩放到一个固定尺寸,要么为每种分辨率都建一个引擎。前者损失精度,后者浪费显存。
动态形状解决了三个核心痛点:
- 输入尺寸不确定:比如 NLP 模型,每个句子的长度都不一样。用动态形状,你就不用 padding 到最大长度了
- batch size 动态变化:线上服务的请求量忽高忽低。动态 batch 能让你在低负载时省显存,高负载时提吞吐
- 多模型复用:同一个引擎,既能处理 512x512 的输入,也能处理 1024x1024 的输入。省去了重复构建的麻烦
核心观点:动态形状不是锦上添花,而是生产环境部署的刚需。尤其是做 SaaS 服务或者边缘部署,你永远不知道下一个请求的输入长什么样。
动态形状与静态形状的对比
咱们直接看对比表,一目了然:
| 对比维度 | 静态形状 | 动态形状 |
|---|---|---|
| 构建时间 | 快(几秒到几十秒) | 慢(几分钟到几十分钟) |
| 推理性能 | 最优(针对固定尺寸优化) | 次优(需要兼顾多个尺寸) |
| 显存占用 | 固定,可预测 | 动态分配,峰值可能更高 |
| 灵活性 | 差(输入尺寸必须匹配) | 好(支持尺寸范围内的任意输入) |
| 适用场景 | 嵌入式设备、固定分辨率 | 云端服务、视频流、NLP |
你想想看,静态形状就像定制西装,合身但只能一个人穿。动态形状像均码衣服,谁都能穿但可能没那么贴身。
我个人习惯是:能用静态就用静态,不得不用动态才用动态。为什么?因为动态形状的构建时间确实让人头疼。我曾经有个模型,静态形状 30 秒就构建完了,改成动态形状后跑了将近 20 分钟。
我的建议:如果你不确定该用哪种,先跑一次静态形状看看效果。如果输入尺寸确实会变化,再切换到动态形状。别一上来就搞动态,容易把自己绕进去。
避坑指南
我曾经踩过一个坑,说出来你可能不信——
有个模型在动态形状下推理结果全是错的。排查了两天,最后发现是动态形状的优化范围设得太宽了。最小尺寸和最大尺寸差了 10 倍,TensorRT 为了兼顾所有尺寸,选了一个很差的 kernel 组合。
所以给你三个建议:
- 优化范围别太大:最小和最大尺寸差距控制在 4 倍以内比较安全
- 最优尺寸选最常见的:比如你的服务 80% 的请求是 1024x1024,那就把 opt 设成这个值
- 测试要覆盖边界:最小、最优、最大三个尺寸都得测一遍。我见过有人在边界尺寸上性能骤降 50%
特别注意:动态形状下,TensorRT 的算子融合策略会发生变化。有些在静态形状下能融合的算子,动态形状下可能就融合不了了。这会导致推理速度变慢。所以构建完引擎后,一定要做性能验证。
好了,这一章就聊到这儿。动态形状的概念其实不复杂,但用好了能解决很多实际问题。下一章我会详细讲怎么在代码里配置动态形状,包括 C++ 和 Python 两种方式,到时候咱们再细聊。