TensorRT Efficient NMS 插件深度解析从算法原理到性能调优与 INMSLayer 迁移【免费下载链接】TensorRTNVIDIA® TensorRT™ is an SDK for high-performance deep learning inference on NVIDIA GPUs. This repository contains the open source components of TensorRT.项目地址: https://gitcode.com/GitHub_Trending/tens/TensorRT本指南以 NVIDIA TensorRT 开源仓库中的 Efficient NMS Plugin 文档 为核心系统讲解该插件为高效目标检测推理尤其是 EfficientDet、SSD、Faster R-CNN 类网络提供的高性能 Non-Maximum SuppressionNMS实现。全文将完整覆盖插件的输入输出结构、全部可配置参数、两种工作模式与 box 解码融合机制并结合仓库源码深入剖析其 CUDA 内核算法与性能调优要点最后说明其在 TensorRT 10.12 中的弃用状态与向INMSLayer迁移的建议。读完本文你将能准确配置 EfficientNMS_TRT 插件、理解其底层执行流程并据此评估与规划替代方案。一、插件定位与弃用现状Efficient NMS Plugin 是一个用于在目标检测网络中执行高效非极大值抑制NMS的 TensorRT 插件其注册名称为EfficientNMS_TRT接口为IPluginV2DynamicExt见 EfficientNMSPlugin_PluginConfig.yaml。重要弃用声明该插件自 TensorRT 10.12 起被标记为弃用deprecated并将在未来版本中移除这包括其 TF-TRT 变体EfficientNMS_Explicit_TF_TRT与EfficientNMS_Implicit_TF_TRT官方暂未计划提供替代实现。此外EfficientNMSONNXPluginEfficientNMS_ONNX_TRT已于 TensorRT 10.16 中被移除其功能由INMSLayer取代。EfficientNMS_TRT的弃用同样以INMSLayer作为推荐替代。在仓库源码中弃用状态以编译器注解的形式固化efficientNMSPlugin.h 中类声明标注了TRT_DEPRECATED_BECAUSE(Deprecated since TRT 10.12. Use INMSLayer instead.)。替代层INMSLayer定义于 include/NvInfer.h可通过INetworkDefinition::addNMSinclude/NvInfer.h创建。适用场景插件最初主要为 EfficientDet 在 TensorRT 上的部署而设计——该网络对 NMS 阶段引入的延迟尤为敏感但实现足够通用同样可正确服务于 SSD、Faster R-CNN 等其他检测架构。仓库中保留了基于该思路的完整 EfficientDet 部署示例 demo/EfficientDet/notebooks/EfficientDet-TensorRT8.ipynb可作为接入参考。二、插件结构两种输入模式插件根据接收输入张量的数量自动选择工作模式这是理解其行为的起点标准 NMS 模式Standard NMS Mode仅提供两个输入张量——(i) 边界框坐标(ii) 每个框对应的分类得分。融合 Box 解码器模式Fused Box Decoder Mode提供三个输入张量——(i) 直接来自网络定位头localization head的原始定位预测(ii) 来自分类头的分类得分(iii) 通常以网络常量张量形式硬编码的默认 anchor 框坐标。大多数检测网络通过定位头产生原始预测这些预测对标准非学习型 anchor 坐标进行调整从而得到更贴合目标的边界框。这一过程称为box 解码box decoding通常包含大量元素级运算且涉及对大量 anchor 的指数运算计算开销可观。插件因此提供将 box 解码器融合进 NMS 操作的选项以远为高效的方式完成解码降低网络整体延迟。从源码看enqueue阶段根据输入个数决定是否启用解码器configurePlugin中nbInputs 3时将mParam.boxDecoder trueefficientNMSPlugin.cpp而enqueue仅在有三个输入时取用第三个张量作为 anchorsefficientNMSPlugin.cpp。2.1 Boxes 输入输入形状[batch_size, number_boxes, 4]或[batch_size, number_boxes, number_classes, 4]数据类型float32或float16Boxes 输入可以是 3 维当所有类别共享同一组框预测时如 EfficientDet、SSD也可以是 4 维当每个类别各自生成独立的框预测时如 Faster R-CNN此时number_classes 1且必须与 scores 输入的类别数一致。最后一维始终是定义边界框预测的四个坐标。在标准 NMS 模式下该张量存放每个预测检测的最终框坐标在融合 Box 解码器模式下该张量存放原始定位预测。源码层面对此的约束可见 efficientNMSPlugin.cpp3 维输入要求最后一维为 4 且所有类别共享位置shareLocation true4 维输入时第二维要么为 1共享位置要么等于类别数最后一维必须为 4。2.2 Scores 输入输入形状[batch_size, number_boxes, number_classes]数据类型float32或float16Scores 输入为每个候选类别在每个 anchor 框上给出number_classes个预测得分。通常得分在到达 NMS 操作前已经过 sigmoid 激活。但作为一项优化也可以将 sigmoid 之前的原始得分直接送入 NMS 插件以降低网络延迟——此时需要开启score_activation参数让插件在内部完成激活处理。源码中configurePlugin从 scores 张量推导出numScoreElements number_boxes * number_classes与numClassesefficientNMSPlugin.cpp这两项直接决定了后续各缓冲区的尺寸与内核的遍历范围。2.3 Anchors 输入可选输入形状[1, number_boxes, 4]或[batch_size, number_boxes, 4]数据类型float32或float16仅用于融合 Box 解码器模式。在该模式下把 box 解码移入插件内部效率更高此时 boxes 输入被视为定位头的原始框修正量而第三个输入存放默认 anchor/prior 框坐标。Anchors 输入必须是 3 维其第一维可以是1所有 batch 图像共享同一组 anchor也可以是batch_size每张图像各有不同的 anchor——例如 Faster R-CNN 第二阶段 box refinement NMS 的情形。源码通过shareAnchors (dims[0] 1)区分这两种布局efficientNMSPlugin.cpp进而在 CUDA 内核中按对应偏移公式寻址见 efficientNMSInference.cu。2.4 动态形状支持如果 TensorRT engine 以动态输入形状构建那么绝大多数输入维度——即batch_size、number_boxes与number_classes——都可以在运行时动态定义。但一旦确定这些维度必须在所有使用它们的张量间保持一致例如 boxes 与 scores 必须给出相同的number_boxes维度。插件接口IPluginV2DynamicExt的getOutputDimensions通过IExprBuilder以维度表达式形式描述输出efficientNMSPlugin.cpp从而支持动态 batch 与动态类别数。2.5 Box 编码类型不同检测网络对框坐标系统的表示各不相同插件支持以下两种BoxCorners四个坐标表示[x1, y1, x2, y2]其中 x,y 对分别定义边界框的左上角与右下角。BoxCenterSize四个坐标表示[x, y, w, h]其中 x,y 定义框中心位置w,h 定义宽高。值得注意的是对 NMS 而言水平与垂直坐标完全可互换例如 TensorFlow 训练的许多网络使用垂直优先的[y1, x1, y2, x2]编码在 BoxCorner 编码下同样工作良好同理[y, x, h, w]也能被 BoxCenterSize 编码正确覆盖。在融合 Box 解码器模式下boxes 与 anchors 张量应使用相同的编码。源码中这两种编码对应 efficientNMSInference.cuh 中的BoxCornerT与BoxCenterSizeT结构体BoxCorner的reorder()通过交换保证y1 y2、x1 x2BoxCenterSize的decode()完成y raw_y * anchor_h anchor_y、w anchor_w * exp(raw_w)等变换efficientNMSInference.cuh。最终 IOU 计算始终在 BoxCorner 编码下进行efficientNMSInference.cu输出坐标也恒为 BoxCorner 格式。三、输出张量插件生成以下四个输出张量num_detections[batch_size, 1]的int32张量。最后一维是标量表示每张 batch 图像中有效检测的数量。它可以小于max_output_boxes只有nms_boxes[i]、nms_scores[i]、nms_classes[i]中前num_detections[i]个条目有效。detection_boxes[batch_size, max_output_boxes, 4]的float32或float16张量包含非极大值抑制后的框坐标。无论输入编码类型如何输出坐标始终为 BoxCorner 格式见 efficientNMSInference.cu 中输出缓冲区恒以BoxCornerT写入的注释。detection_scores[batch_size, max_output_boxes]的float32或float16张量包含这些框的得分。detection_classes[batch_size, max_output_boxes]的int32张量包含这些框的类别。输出数据类型的规则见getOutputDataTypeefficientNMSPlugin.cppnum_detections与detection_classes恒为int32其余输出与输入数据类型一致float32或float16。维度规则见getOutputDimensionsefficientNMSPlugin.cpp其中detection_boxes为 3 维、detection_scores/detection_classes为 2 维、num_detections为 2 维。四、可配置参数详解插件通过 PluginField 机制暴露以下参数efficientNMSPlugin.cpp创建插件时以PluginFieldCollection传入对应的属性定义、类型与取值范围同时记录在 EfficientNMSPlugin_PluginConfig.yaml 中类型参数说明floatscore_threshold*得分标量阈值低于该值的低分框被移除。floatiou_thresholdIOU 标量阈值与已选中框 IOU 重叠过高的额外框被移除。intmax_output_boxes每张图像最多输出的检测数量。intbackground_class背景类的标签 ID若没有背景类则设为-1。boolscore_activation*设为 true 时在 NMS 操作期间对置信度得分应用 sigmoid 激活。boolclass_agnostic设为 true 时执行与类别无关的 NMS否则不同类别的框在 NMS 中会被分开考虑。intbox_codingboxes及适用的 anchors的编码类型0 BoxCorner1 BoxCenterSize。标有*的参数对运行时延迟有不可忽视的影响第四节将详细说明如何最优设置。从 efficientNMSParameters.h 可以看到这些参数的默认值iouThreshold 0.5F、scoreThreshold 0.5F、numOutputBoxes 100、backgroundClass -1、scoreSigmoid false、boxCoding 0、classAgnostic false。该结构体还包含若干内部参数由配置流程自动设置无需在创建插件时指定numSelectedBoxes 4096参与 NMS 竞争的最高分数数量会在initialize()中依据设备调整、shareLocation、shareAnchors、boxDecoder、datatype等。五、算法原理源码级剖析插件的算法启动器与相关 CUDA 内核全部定义于 efficientNMSInference.cu整体流程如下得分过滤Filter以score_threshold过滤低于阈值的得分同时维护索引以将得分与其对应框坐标交叉关联。此阶段由EfficientNMSFilterCUDA 内核完成efficientNMSInference.cu内核按[numScoreElements, batchSize]网格遍历每个类, anchor元素使用atomicAdd在每张图像的选中槽位中写入数据并跳过等于backgroundClass的类别。稠密索引回退Dense Index Fallback如果保留的元素过多——例如score_threshold很低甚至为 0——过滤操作会因涉及大量原子操作而成为瓶颈。此时改用回退内核EfficientNMSDenseIndexefficientNMSInference.cu将所有得分元素以稠密、带索引的方式全部传递。该方法由EfficientNMSFilterLauncher依据启发式规则选择仅当score_threshold 0.007时启用efficientNMSInference.cu。该启动器还会在score_activation开启时对阈值做 sigmoid 逆变换logf(t / (1 - t))使阈值语义与原始得分空间一致。降序排序Sort过滤后保留下来的得分按降序排序。排序过程精心维护索引确保排序后得分与框的关系不丢失。实现上使用 CUB 的cub::DeviceSegmentedRadixSort::SortPairsDescending分段基数排序efficientNMSInference.cu按 batch 分段、以得分与索引的双缓冲方式排序。NMS 主内核排序后得分最高的numSelectedBoxes默认 4096个候选交由EfficientNMSCUDA 内核处理efficientNMSInference.cu。该内核利用前序步骤维护的索引数据定位与剩余得分对应的框若启用了融合 box 解码器解码会被推迟到这一阶段此时只需解码得分最高的少量框节省大量指数运算。高效 IOU 过滤与结果写出NMS 内核采用高效的过滤算法大幅减少框对之间的 IOU 交叉检查次数。具体而言内核以分块tile方式并行处理候选框通过共享内存中的blockState信号量协调线程当某个框被判定为保留时其余线程只需与之计算 IOU得分更低且 IOU 超过阈值的框被标记丢弃threadState -1当输出数量达到numOutputBoxes时通过blockState -2提前退出循环efficientNMSInference.cu。IOU 计算恒在 BoxCorner 编码下进行intersect / union见 efficientNMSInference.cu。只有最终保留下来的分数才会在此阶段应用 sigmoid 激活若score_activation开启从而大幅减少所需的 sigmoid 计算量。此外EfficientNMSLauncher会根据候选数量动态选择线程块大小numSelectedBoxes 512时用 512 256时用 256否则按numSelectedBoxes / NMS_TILES计算其中NMS_TILES 5见 efficientNMSInference.cu。numSelectedBoxes本身还在initialize()中依据设备寄存器容量调整regsPerBlock 65536的主流设备取 5000Jetson TX1/TX2 类设备取 2000efficientNMSPlugin.cpp。六、性能调优实战插件相较其他 NMS 插件实现了非常高效的算法大幅降低了该操作的延迟。但以下考量能帮助进一步微调性能6.1 选择合适的 Score Threshold算法对score_threshold的选择高度敏感。阈值越高需要处理的元素越少算法运行越快。因此始终选择满足应用需求的最大可行 score threshold。阈值低于约 0.01 时可能导致显著更高的延迟——这与源码中 0.007 的稠密索引回退阈值相呼应一旦阈值过低过滤阶段被迫转入全量稠密处理排序与 NMS 的输入规模随之膨胀。6.2 使用 Sigmoid 激活根据网络配置通常更高效的做法是向 NMS 插件的 scores 输入提供原始得分sigmoid 之前并开启score_activation参数。这样 sigmoid 只作用于最终选中的max_output_boxes个得分而非全部预测得分大幅降低计算开销。注意开启该参数时源码会对阈值做 sigmoid 逆变换见第五节第 2 步以保证原始得分空间中的阈值语义正确。6.3 类别无关 NMS部分检测网络/架构如 YOLO 系列需要使用类别无关的 NMS 操作。开启class_agnostic后执行类别无关 NMS否则不同类别会分别执行 NMS。在EfficientNMS内核中该参数控制 IOU 比较时是否要求两个框类别相同ignoreClass threadClass[tile] testClass仅当class_agnostic为 false 时生效见 efficientNMSInference.cu。6.4 使用融合 Box 解码器当网络包含大量 anchor如 EfficientDet、SSD时在 NMS 插件内部完成 box 解码通常更高效。做法是将原始框预测作为 boxes 输入将默认 anchor 坐标作为可选第三输入传入插件。源码层面的收益在于解码被推迟到 NMS 主内核阶段只需对排序后得分最高的少量框执行指数与乘加运算efficientNMSInference.cu而不是对全部 anchor 做解码。七、迁移到 INMSLayer替代方案评估由于EfficientNMS_TRT已弃用新项目应优先考虑内置的INMSLayer。该层定义于 include/NvInfer.h通过addNMS(boxes, scores, maxOutputBoxesPerClass)或带indicesType的重载创建include/NvInfer.h。与插件相比其关键特性如下输入BoxeskFLOAT/kHALF形状[batchSize, numInputBoundingBoxes, numClasses, 4]或[batchSize, numInputBoundingBoxes, 4]、Scores同类型[batchSize, numInputBoundingBoxes, numClasses]、MaxOutputBoxesPerClasskINT32标量以及可选的 IoUThreshold默认 0.0f与 ScoreThreshold默认 0.0f标量输入。输出SelectedIndiceskINT32/kINT64形状[NumOutputBoxes, 3]每行是(batchIndex, classIndex, boxIndex)元组按 batchIndex 升序、同 batch 内按得分降序排列与 NumOutputBoxeskINT32标量。语义按 batch 与类别执行 NMS处理方式与插件类似先按得分排序每 batch 最多考虑 TopK 个框参与选择选择阶段仅比较同类别框的重叠。硬件相关限制存在硬件相关的候选框上限 K——SM 5.3 与 6.2 设备为 2000其余设备为 5000include/NvInfer.h默认 TopK 亦为此值。这与插件initialize()依据regsPerBlock选择 5000/2000 的思路一致可视为一种沿用。需要注意的是INMSLayer的输入语义尤其MaxOutputBoxesPerClass为按类别的输出上限、输出为索引元组与插件全局max_output_boxes、输出为框/得分/类别四个张量存在差异迁移时需要相应调整网络尾部与后处理逻辑对于必须保持 ONNXNonMaxSuppression算子兼容语义的场景也需评估INMSLayer的索引输出形式是否满足需求。八、已知问题与变更历史当前文档声明该插件没有已知问题Known issues 为空。变更历史来自 插件 README 的 Changelog 部分2026 年 3 月移除EfficientNMS_ONNX_TRT插件改用INMSLayer。2025 年 5 月为EfficientNMS_TRT插件增加弃用声明。2023 年 6 月为EfficientNMSONNXPlugin插件增加弃用声明。九、深入阅读指引若需进一步研究建议在仓库中按以下路径展开算法与内核实现efficientNMSInference.cu、efficientNMSInference.cuh插件生命周期与序列化efficientNMSPlugin.cpp、efficientNMSPlugin.h参数结构定义与默认值efficientNMSParameters.h插件属性规范与类型约束EfficientNMSPlugin_PluginConfig.yamlTF-TRT 变体实现plugin/efficientNMSPlugin/tftrt/替代层 API 文档include/NvInfer.h 中INMSLayer类与addNMS接口相关检测网络部署示例demo/EfficientDet/notebooks/EfficientDet-TensorRT8.ipynb关于 NMS 算法本身的背景知识可参考目标检测领域的经典文献如 EfficientDet、SSD、Faster R-CNN 与 Mask R-CNN 等论文以及 ONNXNonMaxSuppression算子的规范定义结合本文源码级分析即可形成完整理解。【免费下载链接】TensorRTNVIDIA® TensorRT™ is an SDK for high-performance deep learning inference on NVIDIA GPUs. This repository contains the open source components of TensorRT.项目地址: https://gitcode.com/GitHub_Trending/tens/TensorRT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考