模型量化!ONNX转TensorRT(FP32, FP16, INT8)

本文为Python实现,C++实现链接模型量化
若还没有配置环境(CUDA,CUDNN,TensorRT),请移至C++实现中查看环境配置方法
  
支持三种不同精度的量化

模型单精度量化 (FP32)
模型半精度量化 (FP16)
模型Int8量化 (INT8)

经测试yolov5,yolov6,yolov7,yolov8转化成功
yolov5: https://github.com/ultralytics/yolov5
yolov6: https://github.com/meituan/YOLOv6
yolov7: https://github.com/WongKinYiu/yolov7
yolov8: https://github.com/ultralytics/ultralytics

注意:若使用INT8量化,需要额外的文件,这里是calibrator.py

main.py文件
import tensorrt as trt
import os
from calibrator import Calibrator

LOGGER = trt.Logger(trt.Logger.VERBOSE)

def buildEngine(onnx_file, engine_file, quantification, batch_size, FP16_mode, INT8_mode,
                img_height, img_wdith, calibration_images, calibration_cache):
    builder = trt.Builder(LOGGER)
    network = builder.create_network(1<<int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
    parser = trt.OnnxParser(network, LOGGER)
    config = builder.create_builder_config()
    parser.parse_from_file(onnx_file)

    config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 16*(1<<20))

    if FP16_mode == True:
        config.set_flag(trt.BuilderFlag.FP16)
    elif INT8_mode == True:
        config.set_flag(trt.BuilderFlag.INT8)
        config.int8_calibrator = Calibrator(quantification, batch_size, img_height, img_wdith, calibration_images, calibration_cache)
    engine = builder.build_serialized_network(network, config)
    if engine is None:
        print("EXPORT ENGINE FAILED!")
        # exit(0)

    with open(engine_file, "wb") as f:
        f.write(engine)


def main():
    quantification = 1  # 量化次数
    batch_size = 1
    img_height = 640
    img_wdith = 640
    calibration_images = "./images"
    onnx_file = "./weights/yolov7.onnx"
    engine_file = "./weights/yolov7.engine"
    calibration_cache = "./weights/yolov7_calibration.cache"

    '''
    模型使用单精度量化,设置 FP16_mode = False & INT8_mode = False  (FP32)
    模型使用半精度量化,设置 FP16_mode = True  & INT8_mode = False  (FP16)
    模型使用 Int8量化,设置 FP16_mode = False & INT8_mode = True   (INT8)
    '''
    FP16_mode = False
    INT8_mode = False

    if not os.path.exists(onnx_file):
        print("LOAD ONNX FILE FAILED: ", onnx_file)

    print('Load ONNX file from:%s \nStart export, Please wait a moment...'%(onnx_file))
    buildEngine(onnx_file, engine_file, quantification, batch_size, FP16_mode, INT8_mode,
                img_height, img_wdith, calibration_images, calibration_cache)
    print('Export ENGINE success, Save as: ', engine_file)


if __name__ == '__main__':
    main()
calibrator.py文件
import os
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import logging
import numpy as np
import cv2

class Calibrator(trt.IInt8EntropyCalibrator):
    def __init__(self, quantification=1, batch_size=1, height=640, width=640, calibration_images="", cache_file=""):
        trt.IInt8EntropyCalibrator.__init__(self)
        self.index = 0
        self.length = quantification
        self.batch_size = batch_size
        self.cache_file = cache_file
        self.height = height
        self.width = width
        self.img_list = [calibration_images + '/' + l for l in os.listdir(calibration_images)]
        self.calibration_data = np.zeros((self.batch_size, 3, self.height, self.width), dtype=np.float32)
        self.d_input = cuda.mem_alloc(self.calibration_data.nbytes)

    def next_batch(self):
        if self.index < self.length:
            for i in range(self.batch_size):
                img = cv2.imread(self.img_list[i + self.index*self.batch_size])
                img = self.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_batch_size(self):
        return self.batch_size

    def get_batch(self, name):
        batch = self.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 there is a cache, use it instead of calibrating again. Otherwise, implicitly return None.
        if os.path.exists(self.cache_file):
            with open(self.cache_file, "rb") as f:
                return f.read()

    def write_calibration_cache(self, cache):
        with open(self.cache_file, "wb") as f:
            f.write(cache)

    def preprocess(self, img):
        h, w, c = img.shape
        image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        r_w = self.width / w
        r_h = self.height / h
        if r_h > r_w:
            tw = self.width
            th = int(r_w * h)
            tx1 = tx2 = 0
            ty1 = int((self.height - th) / 2)
            ty2 = self.height - th - ty1
        else:
            tw = int(r_h * w)
            th = self.height
            tx1 = int((self.width - tw) / 2)
            tx2 = self.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.astype(np.float32)
        image /= 255.0
        image = np.transpose(image, [2, 0, 1])
        return image

结束~

<think>好的,我现在需要帮助用户解决如何将PyTorch模型通过ONNX换为TensorRT格式的问题。首先,用户的问题是关于换的教程,我需要提供一个结构清晰的步骤指南。 首先,我应该回忆一下PyTorch到ONNX再到TensorRT的一般流程。通常步骤包括:导出PyTorch模型ONNX,然后使用TensorRT换工具将ONNX模型换为TensorRT引擎。需要确保用户安装了必要的库,比如torch、onnxTensorRT。 接下来,用户可能需要具体的代码示例。例如,导出ONNX模型时需要使用torch.onnx.export函数,这里要注意输入的形状和动态轴设置。然后,换到TensorRT可以使用trtexec工具或者Python API。对于Python API,可能需要用到TensorRTonnx解析器来构建引擎。 还要考虑常见的问题和注意事项,比如算子支持。ONNXTensorRT支持的算子可能不同,如果模型中有不支持的层,换会失败。这时候可能需要自定义插件或者修改模型结构。另外,动态形状的支持也很重要,用户可能需要指定不同的输入尺寸。 另外,性能优化方面,比如FP16INT8量化,用户可能会感兴趣。需要提到在换时如何启用这些优化选项,例如在构建配置中设置标记。 还要检查用户提供的引用内容,看看是否有相关信息需要整合。例如,引用[1]提到避免使用Tensor.data,这可能影响模型导出时的追踪,所以在示例代码中应该使用推荐的方法。引用[3]提到对称量化,可能在量化部分需要提到这一点,但用户的问题主要是换流程,可能不需要深入量化细节。 然后,生成相关问题,用户可能想了解性能优化、调试换错误、动态形状处理等。需要确保问题覆盖这些方面。 最后,确保回答符合系统级指令,比如使用正确的中文,LaTeX格式,引用标识等。例如,在提到张量形状时可能需要用$...$,但根据示例,可能不需要太多数学公式,除非涉及具体参数。 现在需要组织这些思路,分步骤写出详细的换流程,代码示例,注意事项,并添加相关问题。确保引用正确,比如在提到避免使用Tensor.data时引用[^1]。</think>以下是将PyTorch模型通过ONNX换为TensorRT格式的详细步骤: --- ### **1. 环境准备** - 安装必要库: ```bash pip install torch torchvision onnx tensorrt ``` - 确保安装TensorRTPython包(如`pycuda`和`nvidia-tensorrt`) --- ### **2. PyTorch模型导出为ONNX** #### **关键代码示例** ```python import torch import torchvision.models as models # 定义模型并设为评估模式 model = models.resnet18(pretrained=True) model.eval() # 生成示例输入 dummy_input = torch.randn(1, 3, 224, 224) # 导出为ONNX torch.onnx.export( model, dummy_input, "resnet18.onnx", input_names=["input"], output_names=["output"], dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}} # 支持动态批次 ) ``` #### **注意事项** - 避免使用`.data`操作(可能影响模型追踪)[^1] - 若模型中包含自定义操作,需注册ONNX符号(Symbolic) --- ### **3. ONNX模型换为TensorRT引擎** #### **方法一:使用`trtexec`工具** ```bash trtexec --onnx=resnet18.onnx --saveEngine=resnet18.trt --fp16 ``` - `--fp16`启用FP16精度加速 - `--workspace=N`指定显存大小(默认1GB) #### **方法二:Python API换** ```python import tensorrt as trt # 初始化构建器和配置 logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) config = builder.create_builder_config() config.max_workspace_size = 1 << 30 # 1GB # 解析ONNX模型 parser = trt.OnnxParser(builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)), logger) with open("resnet18.onnx", "rb") as f: parser.parse(f.read()) # 构建引擎 engine = builder.build_engine(network, config) with open("resnet18.trt", "wb") as f: f.write(engine.serialize()) ``` --- ### **4. 常见问题与优化** 1. **算子不支持** - 错误示例:`Unsupported ONNX operator: GridSample` - 解决方案:使用TensorRT插件或修改模型结构 2. **动态形状支持** ```python profile = builder.create_optimization_profile() profile.set_shape("input", min=(1,3,224,224), opt=(4,3,224,224), max=(8,3,224,224)) config.add_optimization_profile(profile) ``` 3. **INT8量化** ```python config.set_flag(trt.BuilderFlag.INT8) config.int8_calibrator = MyCalibrator() # 需实现校准数据接口 ``` --- ### **引用说明** - 避免使用`.data`以保证计算图完整性[^1] - 对称量化(Symmetric Quantization)可减少零点的计算开销[^3] ---
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值