Nvidia TensorRT系列01-TensorRT的功能1
B站:肆十二-的个人空间-肆十二-个人主页-哔哩哔哩视频 (bilibili.com)
博客:肆十二-CSDN博客
C++和Python API
TensorRT的API同时支持C++和Python两种语言,两者的功能几乎完全相同。Python API便于与Python数据处理工具包和库(如NumPy和SciPy)进行互操作。C++ API可能更高效,并可能更好地满足某些合规性要求,例如在汽车应用中。
注意:Python API并非在所有平台上都可用。更多信息请参考NVIDIA TensorRT支持矩阵。
编程模型
TensorRT在两个阶段中运行。在第一阶段,通常在线下进行,您为TensorRT提供一个模型定义,然后TensorRT会针对目标GPU进行优化。在第二阶段,您使用优化后的模型来运行推理。
第一阶段 构建阶段
TensorRT构建阶段的最高级接口是Builder(C++, Python).。构建器负责优化模型并生成一个Engine。
为了构建一个引擎,您必须:
- 创建一个网络定义。
- 为构建器指定配置。
- 调用构建器来创建引擎。
NetworkDefinition接口 (C++, Python) 用于定义模型。将模型转移到TensorRT的最常见路径是将其从框架中以ONNX格式导出,并使用TensorRT的ONNX解析器来填充网络定义。但是,您也可以使用TensorRT的Layer (C++, Python) 和Tensor(C++, Python) 接口逐步构建定义。
无论您选择哪种方式,还必须定义哪些张量是网络的输入和输出。未标记为输出的张量被视为可以由构建器优化的瞬态值。输入和输出张量必须命名,以便在运行时,TensorRT知道如何将输入和输出缓冲区绑定到模型。
BuilderConfig接口 (C++, Python)用于指定TensorRT应如何优化模型。在可用的配置选项中,您可以控制TensorRT降低计算精度的能力,控制内存和运行执行速度之间的权衡,并限制CUDA®内核的选择。由于构建器可能需要数分钟或更长时间来运行,因此您还可以控制构建器如何搜索内核,并缓存搜索结果以供后续运行使用。
当您有了网络定义和构建器配置后,可以调用构建器来创建引擎。构建器会消除无效计算,折叠常量,并重新排序和组合操作,以便在GPU上更高效地运行。它可以选择性地降低浮点计算的精度,方法是将它们简单地以16位浮点数运行,或者通过量化浮点值以便可以使用8位整数进行计算。它还会对每个层使用不同的数据格式进行多次实现计时,然后计算执行模型的最佳计划,从而将内核执行和格式转换的综合成本降至最低。
构建器以称为计划的序列化形式创建引擎,该计划可以立即反序列化,也可以保存到磁盘以供以后使用。
注意:
默认情况下,TensorRT创建的引擎特定于创建它们的TensorRT版本和创建它们的GPU。请参阅版本兼容性和硬件兼容性部分(Version Compatibility and Hardware Compatibility ),了解如何为向前兼容性配置引擎。
TensorRT的网络定义不会深度复制参数数组(例如卷积的权重)。因此,在构建阶段完成之前,您不得释放这些数组的内存。当使用ONNX解析器导入网络时,解析器拥有权重,因此在构建阶段完成之前不得销毁它。
构建器会计算算法的时间以确定最快的算法。如果构建器与其他GPU工作并行运行,可能会干扰计时,从而导致优化效果不佳。
第二阶段 运行时阶段(runtime)
TensorRT执行阶段的最高级别接口是运行时(Runtime) (C++, Python).。
在使用运行时,你通常会执行以下步骤:
反序列化一个计划以创建一个引擎。
从引擎中创建一个执行上下文。
然后,重复以下步骤:
为推理填充输入缓冲区。
在执行上下文上调用enqueueV3()来运行推理。
引擎接口(Engine) (C++, Python) 代表一个优化的模型。你可以查询引擎以获取有关网络输入和输出张量的信息------预期的维度、数据类型、数据格式等。
从引擎创建的执行上下文接口(ExecutionContext) (C++, Python) 是调用推理的主要接口。执行上下文包含与特定调用相关联的所有状态------因此,你可以有一个引擎关联的多个上下文,并并行运行它们。
在调用推理时,你必须在适当的位置设置输入和输出缓冲区。根据数据的性质,这可能是在CPU或GPU内存中。如果你的模型不明显,你可以查询引擎以确定在哪个内存空间中提供缓冲区。
设置好缓冲区后,可以排队进行推理(enqueueV3)。所需的内核在CUDA流上排队,并尽快将控制权返回给应用程序。有些网络需要CPU和GPU之间进行多次控制传输,因此控制权可能不会立即返回。要等待异步执行完成,请使用cudaStreamSynchronize在流上进行同步。
插件
TensorRT具有插件接口,允许应用程序提供TensorRT本身不支持的操作实现。在转换网络时,ONNX解析器可以找到并使用在TensorRT的PluginRegistry中创建和注册的插件。
TensorRT附带了一个插件库,可以在此处找到其中许多插件以及一些额外插件的源代码。here.
您也可以编写自己的插件库,并将其与引擎一起序列化。
如果需要cuDNN或cuBLAS,请安装该库,因为TensorRT不再附带它们。要获取cudnnContext或cublasContext,必须使用nvinfer1::IBuilderConfig::setTacticSource()设置相应的TacticSource标志。
有关更多详细信息,请参阅"使用自定义层扩展TensorRT"一章。
支持的类型和精度
类型
TensorRT支持FP32、FP16、BF16、FP8、INT4、INT8、INT32、INT64、UINT8和BOOL数据类型。有关层I/O数据类型的规范,请参阅TensorRT操作符文档。
-
FP32、FP16、BF16:未量化的浮点类型
-
INT8:低精度整数类型
- 隐式量化
- 解释为量化的整数。INT8类型的张量必须具有一个相关的比例因子(通过校准或setDynamicRange API设置)。
- 显式量化
- 解释为有符号整数。从INT8类型转换或转换到INT8类型需要一个明确的Q/DQ层。
-
INT4:用于权重压缩的低精度整数类型
- INT4仅用于权重量化。在执行计算之前需要进行反量化。
- 从INT4类型转换或转换到INT4类型需要一个明确的Q/DQ层。
- INT4权重预计将通过每字节打包两个元素进行序列化。有关更多信息,请参阅"量化权重"部分。
-
FP8:低精度浮点类型
- 8位浮点类型,其中1位用于符号,4位用于指数,3位用于尾数
- 从FP8类型转换或转换到FP8类型需要一个明确的Q/DQ层。
-
UINT8:无符号整数I/O类型
- 数据类型只能用作网络I/O类型。
- 在UINT8中的网络级输入必须在使用数据执行其他操作之前,使用CastLayer从UINT8转换为FP32或FP16。
- UINT8中的网络级输出必须由明确插入到网络中的CastLayer生成(将仅支持从FP32/FP16到UINT8的转换)。
- 不支持UINT8量化。
- ConstantLayer不支持UINT8作为输出类型。
-
BOOL
- 与支持层一起使用的布尔类型。
强类型和弱类型
当向TensorRT提供网络时,您需要指定它是强类型还是弱类型,默认为弱类型。
对于强类型网络,TensorRT的优化器将根据网络输入类型和操作符规范静态推断中间张量类型,这与框架中的类型推断语义相匹配。然后,优化器将严格遵守这些类型。有关更多信息,请参阅强类型网络。
对于弱类型网络,如果可以提高性能,TensorRT的优化器可能会为张量替换不同的精度。在这种模式下,TensorRT默认对所有浮点运算使用FP32,但有两种方法可以配置不同级别的精度:
要在模型级别控制精度,可以使用BuilderFlag选项(C++,Python)向TensorRT指示,在搜索最快速度时,它可以选择较低精度的实现(并且由于较低精度通常更快,如果允许,它通常会这样做)。
例如,通过设置单个标志,您可以轻松地指示TensorRT对整个模型使用FP16计算。对于输入动态范围大约为1的正则化模型,这通常会在精度变化可忽略不计的情况下显著提高速度。
为了进行更精细的控制,如果某层必须以更高精度运行,因为网络的部分区域对数字敏感或需要高动态范围,则可以为该层指定算术精度。
有关更多详细信息,请参阅弱类型网络中的降低精度。