onnx模型和tensorrt模型的量化标定

部署运行你感兴趣的模型镜像

onnx模型的量化标定

import os
import onnx
import numpy as np
import onnxruntime as ort
from onnxruntime.quantization import quantize_dynamic, QuantType

def dynamic_quantize_onnx_model(
    input_model_path,
    output_model_path=None,
    weight_type=QuantType.QUInt8,
    optimize_model=True,
    verbose=False
):
    """
    对ONNX模型进行INT8动态量化
    
    参数:
        input_model_path: 输入ONNX模型路径
        output_model_path: 输出量化模型路径,默认为输入路径加"_int8"后缀
        weight_type: 权重量化类型,可选QUInt8(默认)或QInt8
        optimize_model: 是否在量化前优化模型
        verbose: 是否打印详细日志
    
    返回:
        量化模型的保存路径
    """
    # 生成默认输出路径
    if output_model_path is None:
        dir_name, file_name = os.path.split(input_model_path)
        base_name, ext = os.path.splitext(file_name)
        output_model_path = os.path.join(dir_name, f"{base_name}_int8{ext}")
    
    # 验证输入模型有效性
    try:
        model = onnx.load(input_model_path)
        onnx.checker.check_model(model)
        if verbose:
            print(f"模型验证成功: {input_model_path}")
    except Exception as e:
        raise ValueError(f"输入模型无效: {str(e)}")
    
    # 执行动态量化
    if verbose:
        print(f"开始动态量化...")
        print(f"输入模型: {input_model_path}")
        print(f"输出模型: {output_model_path}")
        print(f"权重量化类型: {weight_type}")
    
    quantize_dynamic(
        model_input=input_model_path,
        model_output=output_model_path,
        weight_type=weight_type,
        #activation_type=QuantType.QInt8,  # 动态量化通常对激活使用QInt8
        #optimize_model=optimize_model,
        per_channel=False,  # 动态量化通常不使用按通道量化
        reduce_range=False  # 是否缩小量化范围(如127->128)
    )
    
    if verbose:
        print(f"动态量化完成!")
    
    return output_model_path

def compare_quantized_model(original_model_path, quantized_model_path, test_input=None):
    """
    比较原始模型和量化模型的输出差异
    
    参数:
        original_model_path: 原始模型路径
        quantized_model_path: 量化模型路径
        test_input: 测试输入数据,如果为None则生成随机输入
    
    返回:
        输出差异的统计信息
    """
    # 创建推理会话
    sess_original = ort.InferenceSession(original_model_path, providers=['CPUExecutionProvider'])
    sess_quantized = ort.InferenceSession(quantized_model_path, providers=['CPUExecutionProvider'])
    
    # 获取输入输出信息
    input_name = sess_original.get_inputs()[0].name
    output_name = sess_original.get_outputs()[0].name
    input_shape = [dim if dim > 0 else 1 for dim in sess_original.get_inputs()[0].shape]
    
    # 生成测试输入
    if test_input is None:
        test_input = np.random.rand(*input_shape).astype(np.float32)
    
    # 执行推理
    output_original = sess_original.run([output_name], {input_name: test_input})[0]
    output_quantized = sess_quantized.run([output_name], {input_name: test_input})[0]
    
    # 计算差异
    abs_diff = np.abs(output_original - output_quantized)
    stats = {
        'max_diff': np.max(abs_diff),
        'mean_diff': np.mean(abs_diff),
        'std_diff': np.std(abs_diff),
        'original_range': (np.min(output_original), np.max(output_original)),
        'quantized_range': (np.min(output_quantized), np.max(output_quantized))
    }
    
    # 打印结果
    print("\n模型对比结果:")
    print(f"  最大差异: {stats['max_diff']:.6f}")
    print(f"  平均差异: {stats['mean_diff']:.6f}")
    print(f"  差异标准差: {stats['std_diff']:.6f}")
    
    return stats

# 使用示例
if __name__ == "__main__":
    # 配置参数
    INPUT_MODEL = "yolov6n_fp32.onnx"  # 替换为你的ONNX模型路径
    OUTPUT_MODEL = "yolov6n_int8.onnx"
    
    # 执行动态量化
    quantized_model = dynamic_quantize_onnx_model(
        input_model_path=INPUT_MODEL,
        output_model_path=OUTPUT_MODEL,
        weight_type=QuantType.QUInt8,
        verbose=True
    )
    
    # 比较量化前后模型
    compare_quantized_model(INPUT_MODEL, quantized_model)
    
    # 打印模型大小变化
    original_size = os.path.getsize(INPUT_MODEL) / (1024 * 1024)  # MB
    quantized_size = os.path.getsize(quantized_model) / (1024 * 1024)  # MB
    print(f"\n模型大小变化:")
    print(f"  原始模型: {original_size:.2f} MB")
    print(f"  量化模型: {quantized_size:.2f} MB")
    print(f"  压缩比例: {quantized_size/original_size:.2f}x")

tensorrt模型的量化标定

calibrator.py

import os
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import logging
logger = logging.getLogger(__name__)


# calibrator
class Calibrator(trt.IInt8EntropyCalibrator2):
    def __init__(self, stream, cache_file=""):
        trt.IInt8EntropyCalibrator2.__init__(self)       
        self.stream = stream
        self.d_input = cuda.mem_alloc(self.stream.calibration_data.nbytes)
        self.cache_file = cache_file

    def get_batch_size(self):
        return self.stream.batch_size

    def get_batch(self, names):
        batch = self.stream.next_batch()
        if not batch.size:   
            return None
        cuda.memcpy_htod(self.d_input, batch)
        return [int(self.d_input)]

    def read_calibration_cache(self):
        if os.path.exists(self.cache_file):
            with open(self.cache_file, "rb") as f:
                logger.info("Using calibration cache to save time: {:}".format(self.cache_file))
                return f.read()

    def write_calibration_cache(self, cache):
        with open(self.cache_file, "wb") as f:
            logger.info("Caching calibration data for future use: {:}".format(self.cache_file))
            f.write(cache)

main.py

import glob, os, cv2
import numpy as np
import tensorrt as trt
from calibrator import Calibrator


height = 640
width = 640
image_path = 'images'
model_path = "./yolov6n_fp32.onnx"
engine_model_path = "yolov6n_int8.engine"
calibration_table = 'yolov6n_int8_calibration.cache'

TRT_LOGGER = trt.Logger(trt.Logger.WARNING) 


def preprocess(image):
    h, w, c = image.shape
    r_w = width / w
    r_h = height / h
    if r_h > r_w:
        tw = width
        th = int(r_w * h)
        tx1 = tx2 = 0
        ty1 = int((height - th) / 2)
        ty2 = height - th - ty1
    else:
        tw = int(r_h * w)
        th = height
        tx1 = int((width - tw) / 2)
        tx2 = width - tw - tx1
        ty1 = ty2 = 0
    image = cv2.resize(image, (tw, th))
    image = cv2.copyMakeBorder(image, ty1, ty2, tx1, tx2, cv2.BORDER_CONSTANT, (128, 128, 128))  
    image = image / 255.0
    #image = image - np.array([0.406, 0.456, 0.485])
    #image = image / np.array([0.225, 0.224, 0.229])
    image = image[:, :, ::-1].transpose(2, 0, 1).astype(dtype=np.float32)
    return image


class DataLoader:
    def __init__(self):
        self.index = 0
        self.length = 8
        self.batch_size = 16
        self.img_list = glob.glob(os.path.join(image_path, "*.jpg"))
        assert len(self.img_list) >= self.batch_size * self.length
        self.calibration_data = np.zeros((self.batch_size, 3, height, width), dtype=np.float32)

    def next_batch(self):
        if self.index < self.length:
            for i in range(self.batch_size):
                assert os.path.exists(self.img_list[i + self.index * self.batch_size]), 'not found!!'
                img = cv2.imread(self.img_list[i + self.index * self.batch_size])
                img = preprocess(img)
                self.calibration_data[i] = img
            self.index += 1
            return np.ascontiguousarray(self.calibration_data, dtype=np.float32)
        else:
            return np.array([])

    def __len__(self):
        return self.length


def get_engine(onnx_file_path="", engine_file_path="", calibration_stream=None, calibration_table_path=""):
    with trt.Builder(TRT_LOGGER) as builder, builder.create_network(1) as network, trt.OnnxParser(network, TRT_LOGGER) as parser:      
        if not os.path.exists(onnx_file_path):
            quit('ONNX file {} not found'.format(onnx_file_path))
        with open(onnx_file_path, 'rb') as model:
            parser.parse(model.read())
            assert network.num_layers > 0, 'Failed to parse ONNX model. Please check if the ONNX model is compatible '      
        #builder.max_batch_size = 1
        config = builder.create_builder_config()
        #config.max_workspace_size = 1 << 32
        config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 << 32)
        config.set_flag(trt.BuilderFlag.INT8)
        assert calibration_stream, 'Error: a calibration_stream should be provided for int8 mode'
        config.int8_calibrator  = Calibrator(calibration_stream, calibration_table_path)
        runtime = trt.Runtime(TRT_LOGGER)
        plan = builder.build_serialized_network(network, config)
        engine = runtime.deserialize_cuda_engine(plan)
        if engine is None:
            print('Failed to create the engine')
        with open(engine_file_path, "wb") as f:
            f.write(engine.serialize())


if __name__ == '__main__':
    get_engine(model_path, engine_model_path, calibration_stream=DataLoader(), calibration_table_path=calibration_table)

您可能感兴趣的与本文相关的镜像

TensorRT-v8.6

TensorRT-v8.6

TensorRT

TensorRT 是NVIDIA 推出的用于深度学习推理加速的高性能推理引擎。它可以将深度学习模型优化并部署到NVIDIA GPU 上,实现低延迟、高吞吐量的推理过程。

### YOLO模型 INT8量化实现方法步骤 INT8量化是一种通过降低模型权重精度(从FP32或FP16转换为INT8)来加速推理过程的技术,同时保持较高的准确率。在YOLO模型中,INT8量化通常涉及以下几个关键步骤: #### 1. 准备工作 首先需要准备好YOLO模型的原始权重文件(如`.pt`、`.onnx`或`.weights`格式),以及用于校准的数据集。这些数据集应具有代表性,以确保量化后的模型能够保持良好的性能[^1]。 - **安装依赖库**:确保已安装TensorRT、PyTorch(如果使用YOLOv5/v7)、ONNX等必要的库。 - **选择合适的YOLO版本**:不同版本的YOLO(如YOLOv5、YOLOv8)可能有不同的量化流程,需根据具体模型调整步骤。 #### 2. 模型转换 将YOLO模型转换为适合TensorRT处理的格式,通常是ONNX格式。 - 使用PyTorch导出ONNX模型: ```python import torch model = torch.load('yolov5s.pt')['model'].eval() dummy_input = torch.randn(1, 3, 640, 640) torch.onnx.export(model, dummy_input, "yolov5s.onnx", opset_version=13) ``` - 确保ONNX模型正确无误后,可以使用`trtexec`工具将其转换为TensorRT引擎[^2]。 #### 3. 插入量化节点 对于基于PyTorch的YOLO模型(如YOLOv7),可以在模型中手动插入量化模块。 - 使用`torch.quantization`模块初始化量化操作: ```python import torch.quantization model = torch.quantization.QuantWrapper(model) model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm') torch.quantization.prepare_qat(model, inplace=True) ``` - 对于更复杂的自定义量化策略,可以使用`QuantDescriptor()`进行配置,并手动插入量化层到网络结构中[^3]。 #### 4. 数据标定与校准 INT8量化过程中,需要使用少量数据对模型进行标定,以确定激活值的动态范围。 - 标定数据集通常包含数百张图像,覆盖训练集中的多样性。 - 在TensorRT中,可以通过指定`--calibBatchSize``--calibFile`参数来执行标定操作。 - 使用`IInt8EntropyCalibrator2`等校准器接口进行自动标定。 #### 5. 构建INT8 TensorRT引擎 使用`trtexec`命令行工具或Python API构建INT8优化的TensorRT引擎。 - 示例命令(使用`trtexec`): ``` trtexec --onnx=yolov5s.onnx --int8 --calibBatchSize=16 --calibFile=calibration_data.txt --saveEngine=yolov5s_int8.engine ``` - 此命令会生成一个经过INT8量化TensorRT引擎文件,可用于部署到NVIDIA GPU上。 #### 6. 推理测试与性能评估 加载并运行量化后的TensorRT引擎,验证其推理速度准确率是否满足要求。 - 使用TensorRT Python API加载引擎并执行推理: ```python import tensorrt as trt with open("yolov5s_int8.engine", "rb") as f, trt.Runtime(trt.Logger()) as runtime: engine = runtime.deserialize_cuda_engine(f.read()) context = engine.create_execution_context() # 设置输入输出内存 # ... # 执行推理 # ... ``` - 对比FP16与INT8模式下的FPS、延迟及mAP指标,评估量化效果。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值