TensorRT模型量化实践


在这里插入图片描述

量化基本概念

后训练量化Post Training Quantization (PTQ)

量化过程仅仅通过离线推理一些sample数据对权重和激活值进行量化,无需要进行训练微调。

量化感知训练Quantization Aware Training (QAT)

在量化的过程中,对网络进行训练,从而让网络参数能更好地适应量化带来的信息损失。这种方式更加灵活,因此准确性普遍比后训练量化要高。缺点是操作起来不太方便。大多数情况下比训练后量化精度更高,部分场景不一定比部分/混合精度量化好很多。

量化的方法

方式1:trtexec(PTQ的一种)

(1)int8量化

trtexec --onnx=XX.onnx --saveEngine=model.plan --int8 --workspace=4096

如果使用int8量化;量化需要设置calib文件夹;

trtexec 
--onnx=model.onnx 
--minShapes=input:1x1x224x224  
--optShapes=input:2x1x224x224 
--maxShapes=input:10x1x224x224 
--workspace=4096 
--int8 
--best 
--calib=D:\images 
--saveEngine=model.engine 
--buildOnly  

精度损失很大,不建议直接采用。
trtexec 有提供 --calib=接口进行校正,但需要对中间特征进行cache文件保存,比较麻烦,官方文档也是采用上述方式进行int8量化;与fp16的模型在测试集上测试指标,可以看到精度下降非常严重;
(2)int8 fp16混合量化
trtexec --onnx=XX.onnx --saveEngine=model.plan --int8 --fp16 --workspace=4096
测试集上统计指标:相比纯int8量化,效果要好,但是相比fp16,精度下降依然非常严重

方式2:PTQ

engine序列化时执行

2.1 python onnx转trt

操作流程:
按照常规方案导出onnx,onnx序列化为tensorrt engine之前打开int8量化模式并采用校正数据集进行校正;
优点:
1.导出onnx之前的所有操作都为常规操作;2. 相比在pytorch中进行PTQ int8量化,所需显存小;
缺点:
1.量化过程为黑盒子,无法看到中间过程;
2.校正过程需在实际运行的tensorrt版本中进行并保存tensorrt engine;
3.量化过程中发现,即使模型为动态输入,校正数据集使用时也必须与推理时的输入shape[N, C, H, W]完全一致,否则,效果非常非常差,动态模型慎用。
操作示例参看onnx2trt_ptq.py

2.2 polygraphy工具:应该是对2.1量化过程的封装

操作流程:
按照常规方案导出onnx,onnx序列化为tensorrt engine之前打开int8量化模式并采用校正数据集进行校正;
优点: 1. 相较于1.1,代码量更少,只需完成校正数据的处理代码;
缺点: 1. 同上所有; 2. 动态尺寸时,校正数据需与–trt-opt-shapes相同;3.内部默认最多校正20个epoch;

安装polygraphy

pip install colored polygraphy --extra-index-url https://pypi.ngc.nvidia.com

量化

polygraphy convert XX.onnx --int8 --data-loader-script loader_data.py --calibration-cache XX.cache -o XX.pl

方式3:QAT(追求精度时推荐)

注:在pytorch中执行导出的onnx将产生一个明确量化的模型,属于显式量化
操作流程:
安装pytorch_quantization库->加载训练数据->加载模型(在加载模型之前,启用quant_modules.initialize() 以保证原始模型层替换为量化层)->训练->导出onnx;
优点:
1.模型量化参数重新训练,训练较好时,精度下降较少; 2. 通过导出的onnx能够看到每层量化的过程;2. onnx导出为tensort engine时可以采用trtexec(注:命令行需加–int8,需要fp16和int8混合精度时,再添加–fp16),比较简单;3.训练过程可在任意设备中进行;
缺点:
1.导出onnx时,显存占用非常大;2.最终精度取决于训练好坏;3. QAT训练shape需与推理shape一致才能获得好的推理结果;4. 导出onnx时需采用真实的图片输入作为输入设置
操作示例参看yolov5_pytorch_qat.py感知训练,参看export_onnx_qat.py

使用TensorRT量化实践(C++版)

该方式则是利用TensorRT的API将onnx转换engine文件的过程中进行量化,其中需要校准数据(准备一个存放几百张图像的文件夹即可)。为了读取校正图像,需要写一个Int8校正类,如下所示:
calibrator.h

#pragma once
#include <NvInfer.h>
#include<vector>
#include <opencv2/opencv.hpp>
class Calibrator : public nvinfer1::IInt8EntropyCalibrator2 {
   
   
public:
	Calibrator(int batchsize, int input_w, int input_h, std::string img_dir, const char* calib_table_name, bool read_cache = true);

	virtual ~Calibrator();
	int getBatchSize() const noexcept override;
	bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override;
	const void* readCalibrationCache(size_t& length) noexcept override;
	void writeCalibrationCache(const void* cache, size_t length) noexcept override;

private:
	int BATCHSIZE;
	int WIDTH;
	int HEIGHT;
	int INDEX;
	std::string IMAGEDIR;
	std::vector<std::string> IMAGEFILES;
	size_t INPUTSIZE;
	std::string CALIBRATORTABLE;
	bool READCACHE;
	void* DEVICEINPUT;
	std::vector<char> CALIBRATORCACHE;

	cv::Mat preprocess_img(cv::Mat& img, int input_w, int input_h);
	void getFiles(std::string path, std::vector<std::string>& files);
};

calibrator.cpp

#include <fstream>
#include <io.h>
#include "calibrator.h"

cv::Mat Calibrator::preprocess_img(cv::Mat& img, int input_w, int input_h) {
   
   
    int w, h, x, y;
    float r_w = input_w / (img.cols * 1.0);
    float r_h = input_h / (img.rows * 1.0);
    if (r_h > r_w) {
   
   
        w = input_w;
        h = r_w * img.rows;
        x = 0;
        y = (input_h - h) / 2;
    }
    else {
   
   
        w = r_h * img.cols;
        h = input_h;
        x = (input_w - w) / 2;
        y = 0;
    }
    cv::Mat re(h, w, CV_8UC3);
    cv::resize(img, re, re.size(), 0, 0, cv::INTER_LINEAR);
    cv::Mat out(input_h, input_w, CV_8UC3, cv::Scalar(128, 128, 128));
    re.copyTo(out(cv::Rect(x, y, re.cols, re.rows)));
    return out;
}

void Calibrator::getFiles(std::string path, std::vector<std::string>& files){
   
   
    intptr_t Handle;
    struct _finddata_t FileInfo;
    std::string p;
    Handle = _findfirst(p
### TensorRT 中线性量化的 Nichols 方法 关于TensorRT中线性量化的Nichols方法的具体描述,在现有参考资料中并未直接提及。然而,可以推测这可能是一个特定于某些工业实践的方法,或者是对经典控制理论中的Nichols图法在现代深度学习框架下的某种适应。 #### 控制论背景介绍 经典的Nichols图表是一种图形化工具,用于分析和设计控制系统[^1]。通过绘制开环频率响应的相角与幅值关系来评估闭环系统的稳定性边界以及调整控制器参数。尽管这一概念最初应用于模拟电路及时域建模领域,但在当前AI加速硬件优化过程中也可能存在类似的考量方式。 #### TensorRT量化过程概述 TensorRT 是 NVIDIA 提供的一个高性能推理引擎,支持多种类型的模型部署到生产环境中。为了提高效率并减少资源消耗,TensorRT 支持不同形式的量化技术,其中包括但不限于: - **INT8 量化**:将浮点权重转换成整数表示以降低存储需求并加快运算速度。 - **混合精度**:允许在同一网络内部署 FP32 和 INT8 数据类型组合使用。 虽然官方文档并没有特别指出所谓的 "Nichols 方法",但是考虑到其广泛的应用场景和技术积累,NVIDIA 很有可能已经开发出了针对具体应用场景的有效实现方案。 #### 假设性的应用案例 如果确实存在这样一种名为 “Nichols”的线性量化方法,则可能是借鉴自传统自动化控制领域的经验教训,旨在解决如下挑战之一: - 实现高效能功耗比的最佳平衡状态; ```cpp // 这里展示了一个假设性的C++代码片段, // 描述如何在一个简化版的张量操作库中执行类似于Nichols方法的操作。 void applyNicholsLinearQuantization(Tensor& tensor){ float maxAbsValue = getMaxAbsoluteElement(tensor); int scale_factor = computeScaleFactor(maxAbsValue); // 计算缩放因子 for(auto &element : tensor.elements()){ element = round(element * scale_factor / maxAbsValue); } } ``` 上述伪代码仅作为示意用途,并不代表实际API接口定义或工作流程细节。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值