1、TensorRT动态Shape概述:什么是动态Shape、为什么需要动态Shape、TensorRT中的动态Shape支持情况
1.1 什么是动态Shape?先别急着背定义
动态Shape,说白了就是——你的模型输入尺寸可以变。
举个例子。你训练了一个图像分类模型,输入是224x224。但用户上传的图片可能是1920x1080,也可能是640x480。你总不能每次都把图片硬缩成224x224吧?那样信息损失太大了。
动态Shape允许你在推理时,每次传入不同大小的输入。模型会根据实际输入尺寸,动态调整内部的计算图。嗯,这里要注意——不是所有算子都支持动态变化,后面我会细说。
我个人习惯把动态Shape分成两类:
- Batch维度动态:每次推理的批大小可以不同。比如这次处理1张图,下次处理8张图。
- 空间维度动态:每张图的高宽可以不同。比如检测模型里,输入可能是640x640,也可能是1280x720。
我在项目中遇到过最头疼的情况是——客户要求同时支持Batch动态和空间动态。那会儿调试了整整一周,才把各种边界情况理清楚。
1.2 为什么需要动态Shape?
你可能会问:静态Shape不香吗?固定输入尺寸,优化得最彻底,性能最好。
没错,静态Shape确实快。但现实世界不是实验室。
我总结了几种必须用动态Shape的场景:
- Web服务:用户请求的图片大小不一。你没法要求所有用户都上传相同尺寸的图片。
- 视频流处理:视频分辨率可能变化。比如从1080p切换到4K,模型得能自适应。
- NLP模型:文本长度天然就是动态的。一句话可能5个字,也可能500个字。
- 目标检测:输入图片的宽高比千差万别。强行resize会破坏物体比例。
说白了,动态Shape解决的是「通用性」问题。你想想看,如果每个模型只能处理固定尺寸,那部署起来得多麻烦?
我曾经给一个安防客户做方案,他们的摄像头分辨率五花八门——200万像素、500万像素、800万像素都有。如果用静态Shape,得为每种分辨率单独优化一个模型。那维护成本,啧啧,想想都头大。
1.3 TensorRT中的动态Shape支持情况
TensorRT从7.0版本开始正式支持动态Shape。但说实话,早期的支持很有限,踩坑是家常便饭。到了8.x版本,情况好了很多。
目前TensorRT支持三种Shape模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 静态Shape | 输入输出尺寸完全固定 | 性能要求极致,输入尺寸固定的场景 |
| 动态Shape(优化范围) | 指定最小、最优、最大三个尺寸 | 大部分实际部署场景 |
| 完全动态 | 不限制输入尺寸,运行时动态分配 | 输入尺寸变化极大,无法预估的场景 |
核心要点:TensorRT的动态Shape不是「完全自由」的。你需要给每个动态维度指定一个范围——最小值、最优值、最大值。TensorRT会在优化时,针对这个范围做专门的调优。
举个例子。假设你的模型输入是图像,宽高范围是[224, 224]到[1920, 1080]。你可以这样设置:
// C++ API 示例
IBuilderConfig* config = builder->createBuilderConfig();
IOptimizationProfile* profile = config->createOptimizationProfile();
// 设置动态范围
profile->setDimensions("input", OptProfileSelector::kMIN, Dims4{1, 3, 224, 224});
profile->setDimensions("input", OptProfileSelector::kOPT, Dims4{1, 3, 640, 640});
profile->setDimensions("input", OptProfileSelector::kMAX, Dims4{1, 3, 1920, 1080});
config->addOptimizationProfile(profile);
我的经验:最优值(OPT)一定要选实际部署中最常见的尺寸。比如你的用户大部分上传640x640的图片,那就把OPT设成640。这样TensorRT会针对这个尺寸做最激进的优化,性能最好。
1.4 动态Shape的代价
天下没有免费的午餐。动态Shape虽然灵活,但也是有代价的:
- 性能损失:动态Shape的推理速度通常比静态Shape慢10%-30%。因为TensorRT没法做某些极致的算子融合。
- 显存占用增加:TensorRT会为优化范围内的多个尺寸预分配显存。范围越大,占的显存越多。
- 构建时间变长:动态Shape的引擎构建时间可能是静态的2-3倍。因为TensorRT需要探索更多的优化空间。
避坑指南:我曾经把动态范围设得太大——从32x32到4096x4096。结果引擎构建花了40分钟,而且推理速度慢得离谱。后来我把范围缩小到实际业务需要的[512, 512]到[2048, 2048],构建时间降到5分钟,性能也上来了。
1.5 什么时候该用动态Shape?
我个人的判断标准很简单:
- 如果输入尺寸固定,或者可以无损地缩放到固定尺寸——用静态Shape,省心又高效。
- 如果输入尺寸变化,但变化范围可控(比如2倍以内)——用动态Shape,设置好优化范围。
- 如果输入尺寸变化极大(比如10倍以上)——考虑分桶策略。把尺寸分成几个区间,每个区间单独优化一个引擎。
嗯,分桶策略是个好办法。我有个项目就是按[224, 512]、[512, 1024]、[1024, 2048]三个区间分别优化,效果比一个全范围动态引擎好得多。
好了,这一章先聊到这儿。动态Shape的概念和必要性应该清楚了。下一章我们动手实战——用Python API构建一个支持动态Shape的TensorRT引擎。