TensorRT教程17: 使用混合精度--fp32、fp16、int8(重点)

TensorRT 使用混合精度

五种精度类型

kFLOAT //!< FP32 format.
kHALF  //!< FP16 format.
kINT8  //!< INT8 format.
kINT32 //!< INT32 format.
kTF32  //!< TF32 format.

TF32精度

TF32 Tensor Cores 可以使用 FP32 加速网络,通常不会损失准确性。 对于需要高动态范围权重或激活的模型,它比 FP16 更健壮。

在构建引擎时设置环境变量 NVIDIA_TF32_OVERRIDE=0 将禁用 TF32

//如果设备支持TF32精度,则使用TF32精度
if(builder->platformHasTf32())
{
    builder->setTf32Mode(true);//不确定是否对,先这么写
	builder->setTf32Mode(dataType == DataType::kTF32);//不确定是否对,先这么写
};

//step6:创建config并设置最大batchsize和最大工作空间
IBuilderConfig * config = builder->createBuilderConfig();
//停用TF32指令
config->clearFlag(BuilderFlag::kTF32);

//也可通过设置环境变量禁用TF32
NVIDIA_TF32_OVERRIDE=0

此外可以对特定的网络层指定精度,这个是通用方法

//指定layer层精度是INT8
layer->setPrecision(nvinfer1::DataType::kINT8)

//指定输出层精度是FP32
layer->setOutputType(out_tensor_index, nvinfer1::DataType::kFLOAT)    

FP16精度的设置

fp16只需略微修改代码,明显提高速度,基本不影响精度, TensorRT3.0的官方文档上说,如果只是使用 float 16 的数据精度代替 float-32 , 实际上并不会有多大的性能提升。真正提升性能的是 half2mode ,使用16位交叉存存储方式的模式。在 batchsize 大于 1的情况下,这种模式的运行速度是最快的

//如果设备支持FP16精度,则使用FP16精度
if(builder->platformHasFastFp16())
{
    builder->setFp16Mode(true);
	builder->setFp16Mode(dataType == DataType::kHALF);
};

//step6:创建config并设置最大batchsize和最大工作空间
IBuilderConfig * config = builder->createBuilderConfig();
//设置构建器标志可启用 FP16 精度推理。
config->setFlag(BuilderFlag::kFP16);
//强制使用 FP16 位精度:
config->setFlag(BuilderFlag::kSTRICT_TYPES)//这可能没有最佳性能。  建议仅出于调试目的使用此标志。

INT8精度的设置

精度并没有损失太多,速度提升很大,尤其是当 batch_size 大于1时,提升更明显

TensorRT 的INT8模式只支持计算能力为6.1以上的GPU

注意:parser解析模型的时候传进去的dataType,使用INT8 inference的话,这个地方传进去的是kFLOAT,也就是 FP32,这是因为INT8 需要先用FP32的精度来确定转换系数,TensorRT自己会在内部转换成INT8。这个看起来就跟使用FP32是一样的流程,INT8 MODE inference的输入和输出都是 FP32的。

//如果设备支持INT8精度,则使用INT8精度
if(builder->platformHasFastInt8())
{
    builder->setInt8Mode(true);
    builder->setInt8Mode(dataType == DataType::kINT8);
    builder->setInt8Calibrator(calibrator);//校准器接口
};

//step6:创建config并设置最大batchsize和最大工作空间
IBuilderConfig * config = builder->createBuilderConfig();
//设置构建器标志可启用 INT8 精度推理。
config->setFlag(BuilderFlag::kINT8);
//强制使用INT8位精度:
config->setFlag(BuilderFlag::kSTRICT_TYPES)//这可能没有最佳性能。  建议仅出于调试目的使用此标志。
//配置校准器
config->setInt8Calibrator(calibrator.get());

TensorRT 提供了多种 IInt8Calibrator 校准器变体:

//这是推荐的校准器,是 DLA 所必需的。  默认情况下,校准发生在图层融合之前。  推荐用于基于 CNN 的网络。
IEntropyCalibratorV2
    
//这个校准器似乎更适合 NLP 任务。  默认情况下,校准发生在图层融合之前。  推荐用于 NVIDIA BERT(谷歌官方实现的优化版本)等网络   
IMinMaxCalibrator
    
//这是传统的熵校准器。这比传统的校准器更简单,并且产生更好的结果。  默认情况下,在层融合之后进行校准。
IEntropyCalibrator
    
//该校准器与 TensorRT 2.0 EA 兼容。  该校准器需要用户参数化,并在其他校准器产生不良结果时作为备用选项提供。  默认情况下,在层融合之后进行校准。  
ILegacyCalibrator    

int8量化流程

典型的工作流还是直接使用 GTC2017 PPT 原文说法吧:

  • You will need:

    • Model trained in FP32.
    • Calibration dataset.
  • TensorRT will:

    • Run inference in FP32 on calibration dataset.

    • Collect required statistics(直方图).

    • Run calibration algorithm → optimal scaling factors.

    • Quantize FP32 weights → INT8.

    • 根据直方图和参数生成校准表CalibrationTable,根据网络定义和校准表生成int8 engine

结论 Conclusion

  • 对称的,不饱和的线性量化,会导致精度损失较大;
  • weights权值的int8量化使用的是不饱和的方式,对activation激活值使用的是饱和的量化方式
  • 通过最小化 (相对熵)KL散度来选择 饱和量化中的 阈值 |T|;
  • FP32完全可以降低为INT8推理,精度几乎持平,速度有很大提升。

你可能感兴趣的:(TensorRT教程,计算机视觉,目标检测,机器学习,人工智能,深度学习)