在线看免费观看视频网站大全,状元村建设官方网站,河南省建筑一体化平台官网,wordpress oauth2插件目录 前言一、PTQ量化浅析二、YOLOv7模型训练1. 项目的克隆和必要的环境依赖1.1 项目的克隆1.2 项目代码结构整体介绍1.3 环境安装 2. 数据集和预训练权重的准备2.1 数据集2.2 预训练权重准备 3. 训练模型3.1 修改模型配置文件3.2 修改数据配置文件3.3 训练模型3.4 mAP测试 三、… 目录 前言一、PTQ量化浅析二、YOLOv7模型训练1. 项目的克隆和必要的环境依赖1.1 项目的克隆1.2 项目代码结构整体介绍1.3 环境安装 2. 数据集和预训练权重的准备2.1 数据集2.2 预训练权重准备 3. 训练模型3.1 修改模型配置文件3.2 修改数据配置文件3.3 训练模型3.4 mAP测试 三、YOLOv7-PTQ量化部署1. 源码下载2. 环境配置2.1 配置CMakeLists.txt2.2 配置Makefile 3. ONNX导出3.1 静态batch导出3.2 动态 batch 的导出 4. PTQ量化4.1 前置工作4.2 源码修改4.3 编译运行4.4 PTQ模型mAP测试 四、讨论1. 校准图片数量2. 不同精度模型对比3. YOLOv5-PTQ vs. YOLOv7-PTQ 结语下载链接参考 前言 博主又来水文章了最近在学习 YOLOv7 QAT 量化相关的一个 repo本来想和大家直接分享 QAT 量化的但转念一想貌似还可以水一篇 PTQ 量化的文章因此博主就准备在这篇文章中分享基于 YOLOv7 的 PTQ 量化部署的相关实现具体实现在 tensorRT_Pro 这个 repo 中已经提供博主只是简单过了一遍流程。 博主为初学者欢迎交流讨论若有问题欢迎各位看官批评指正 一、PTQ量化浅析 在正式开始之前我们先来回顾下关于 PTQ 量化的一些知识具体可参考TensorRT量化第四课PTQ与QAT TensorRT 有两种量化模式分别是隐式implicitly量化和显式explicitly量化。前者在 TRT7 版本之前用得比较多而后者在 TRT8 版本后才完全支持具体就是可以加载带有 QDQ 信息的模型然后生成对应量化版本的 engine。
这篇文章主要分享隐式量化即 PTQ 量化关于显式量化即 QAT 量化我们将在下篇文章中分享。
PTQPost-Training Quantization即训练后量化也叫隐式量化tensorRT 的训练后量化算法第一次公布是在 2017 年那年 NVIDIA 放出了使用交叉熵量化的一个 PPT简单说明了其量化原理和流程其思想集中在 tensorRT 内部可供用户去使用。对用户是闭源的我们只能通过 tensorRT 提供的 API 去实现量化。
PTQ 量化不需要训练只需要提供一些样本图片然后在已经训练好的模型上进行校准统计出来需要的每一层的 scale 就可以实现量化了大概流程如下
在准备好的校准数据集上评估预训练模型使用校准数据来校准模型校准数据可以是训练集的子集计算网络中权重和激活的动态范围用来算出量化参数 q-params使用 q-params 量化网络并执行推理 图2-1 PTQ量化流程 具体使用就是我们导出 ONNX 模型转换为 engine 的过程中使用 tensorRT 提供的 Calibration 方法去校准可以使用 tensorRT 官方提供的 trtexec 工具去实现也可以使用它提供的 Python 或者 C 的 API 接口去实现。
在 tensorRT_Pro 中 INT8 模型的编译就是 PTQ 量化因此我们只需要提供好 ONNX 模型和校准数据即可其它不用我们关心。
tensorRT 还提供了多种校准算法分别适用于不同的任务
EntropyCalibratorV2适合于基于 CNN 的网络MinMaxCalibrator适合于 NLP 任务如 BERTEntropyCalibrator老版本的交叉熵校准LegacyCalibrator
通过上述这些校准算法进行 PTQ 量化时tensorRT 会在优化网络的时候尝试 INT8 精度假设网络某一层在 INT8 精度下的速度优于默认精度FP32/FP16则优先使用 INT8。
值得注意的是PTQ 量化中我们无法控制某一层的精度因为 tensorRT 是以速度优化为优先的很可能某一层你想让它跑 INT8 结果却是 FP16当然 PTQ 优点是流程简单速度快。
OK关于 PTQ 量化我们就简单聊下让我们开始具体的实现吧
二、YOLOv7模型训练 首先我们需要训练一个 YOLOv7 模型当然拿官方的预训练权重也行博主这边为了完整性还是整体走一遍流程熟悉 YOLOv7 模型训练的看官可以跳过直接到量化部分。 1. 项目的克隆和必要的环境依赖
1.1 项目的克隆
yolov7 的代码是开源的可直接从 github 官网上下载源码下载地址是 https://github.com/WongKinYiu/yolov7由于 yolov7 目前就只固定 v0.1 一个版本而 v0.1 版本并未提供训练的详细说明故采用主分支进行模型的训练和部署工作。Linux下代码克隆指令如下
git clone https://github.com/WongKinYiu/yolov7.git也可手动点击下载点击右上角的绿色的 Code 按键将代码下载下来。至此整个项目就已经准备好了。也可以点击 here【pwd:yolo】下载博主准备好的代码注意该代码下载于 2023/10/14 日若有改动请参考最新 1.2 项目代码结构整体介绍
将下载后的 yolov7 代码解压其代码目录如下图所示 现在来对代码的整体目录做一个介绍
|-cfg存放yolov7不同模型的yaml文件如yolov7.yaml、yolov7-tiny.yaml等包括训练和部署时的yolov7模型yaml|-data存放一些超参数的配置文件以及配置训练集和验证集路径的coco.yaml文件如果需要修改自己的数据集那么需要修改其中的yaml文件|-deploy针对部署的文件夹|-figure存放yolov7测试的效果图片|-inference存放推理时的图片|-models存放yolov7整体网络模型搭建的py文件|-paper存放yolov7论文|-scripts脚本文件用于获取coco数据集|-tools该文件夹主要存放一些示例教程如yolov7关键点检测、yolov7实例分割、yolov7onnx等等|-utils存放工具类函数包括loss、metrics、plots函数等|- detect.py检测代码包括图像检测、视频流检测等export.py模型导出代码如onnx导出hubconf.pypytorch扩展模型requirements.txt文本文件里面包含使用yolov7项目的环境依赖包以及相应的版本号test.py测试代码train.py训练代码train_aux.py训练辅助头代码(不确定)
1.3 环境安装
关于深度学习的环境安装可参考炮哥的利用Anaconda安装pytorch和paddle深度学习环境pycharm安装—免额外安装CUDA和cudnn(适合小白的保姆级教学)这里不再赘述。如果之前配置过 yolov5 的环境yolov7 可直接使用。
2. 数据集和预训练权重的准备
2.1 数据集
这里训练采用的数据集是 PASCAL VOC 数据集但博主并没有使用完整的 VOC 数据集而是选用了部分数据具体分布如下
训练集(VOC2007train VOC2007val) x 80% 4013验证集(VOC2007train VOC2007val) x 20% 998测试集0
这里给出下载链接 Baidu Drive【pwd:yolo】下载解压后整个数据集文件夹内容如下所示 其中 images 存放训练集和验证集的图片文件labels 存放着对应的 YOLO 格式的 .txt 文件。
完整的 VOC 数据集的相关介绍和下载可参考目标检测PASCAL VOC 数据集简介
由于大家可能从其它地方拿到的是 XML 格式的标签文件这里提供一个 XML2YOLO 转换的代码如下所示(from ChatGPT)
import os
import cv2
import xml.etree.ElementTree as ET
import shutil
from multiprocessing import Pool, cpu_count
from tqdm import tqdm
import numpy as np
from functools import partialdef process_xml(xml_filename, img_path, xml_path, img_save_path, label_save_path, class_dict, ratio):# 解析 xml 文件xml_file_path os.path.join(xml_path, xml_filename)tree ET.parse(xml_file_path)root tree.getroot()# 获取图像的宽度和高度img_filename os.path.splitext(xml_filename)[0] .jpgimg cv2.imread(os.path.join(img_path, img_filename))height, width img.shape[:2]# 随机决定当前图像和标签是属于训练集还是验证集subset train if np.random.random() ratio else val# 打开对应的标签文件进行写入label_file os.path.join(label_save_path, subset, os.path.splitext(xml_filename)[0] .txt)with open(label_file, w) as file:for obj in root.iter(object):# 获取类别名并转换为类别IDclass_name obj.find(name).textclass_id class_dict[class_name]# 获取并处理边界框的坐标xmlbox obj.find(bndbox)x1 float(xmlbox.find(xmin).text)y1 float(xmlbox.find(ymin).text)x2 float(xmlbox.find(xmax).text)y2 float(xmlbox.find(ymax).text)# 计算中心点坐标和宽高并归一化x_center (x1 x2) / 2 / widthy_center (y1 y2) / 2 / heightw (x2 - x1) / widthh (y2 - y1) / height# 写入文件file.write(f{class_id} {x_center} {y_center} {w} {h}\n)# 将图像文件复制到对应的训练集或验证集目录shutil.copy(os.path.join(img_path, img_filename), os.path.join(img_save_path, subset, img_filename))def check_and_create_dir(path):# 检查并创建 train 和 val 目录for subset in [train, val]:if not os.path.exists(os.path.join(path, subset)):os.makedirs(os.path.join(path, subset))if __name__ __main__:# 1. 定义路径和类别字典不要使用中文路径img_path D:\\Data\\PASCAL_VOC\\VOCdevkit\\VOC2007\\JPEGImagesxml_path D:\\Data\\PASCAL_VOC\\VOCdevkit\\VOC2007\\Annotationsimg_save_path D:\\Data\\PASCAL_VOC\\dataset\\imageslabel_save_path D:\\Data\\PASCAL_VOC\\dataset\\labelsclass_dict {aeroplane: 0,bicycle: 1,bird: 2,boat: 3,bottle: 4,bus: 5,car: 6,cat: 7,chair: 8,cow: 9,diningtable: 10,dog: 11,horse: 12,motorbike: 13,person: 14,pottedplant: 15,sheep: 16,sofa: 17,train: 18,tvmonitor: 19
}train_val_ratio 0.8 # 2. 定义训练集和验证集的比例# 检查并创建必要的目录check_and_create_dir(img_save_path)check_and_create_dir(label_save_path)# 获取 xml 文件列表xml_filenames os.listdir(xml_path)# 创建进程池并执行with Pool(cpu_count()) as p:list(tqdm(p.imap(partial(process_xml, img_pathimg_path, xml_pathxml_path, img_save_pathimg_save_path, label_save_pathlabel_save_path, class_dictclass_dict, ratiotrain_val_ratio), xml_filenames), totallen(xml_filenames)))上述代码的功能是将 PASCAL VOC 格式的数据集包括 JPEG 图像和 XML 格式的标签文件转换为 YOLO 需要的 .txt 标签格式同时会将转换后的数据集按照比例随机划分为训练集和验证集。
你需要修改以下几项
img_path需要转换的图像文件路径xml_path需要转换的 xml 标签文件路径img_save_path转换后保存的图像路径label_save_path转换后保存的 txt 标签路径class_dict数据集类别字典train_val_ratio训练集和验证集划分的比例注意以上路径都不要包含中文Windows 下路径记得使用 \\ 或者 / 防止转义
XML 标签文件中目标框保存的格式是 [xmin, ymin, xmax, ymax] 四个变量分别代表着未经归一化的左上角和右下角坐标。
YOLO 标签中目标框保存的格式是每一行代表一个目标框信息每一行共包含 [label_id, x_center, y_center, w, h] 五个变量分别代表着标签 ID经过归一化后的中心点坐标和目标框宽高。
关于代码的分析可以参考tensorRT模型性能测试
至此数据集的准备工作完毕。
2.2 预训练权重准备
yolov7 预训练权重可以通过 here【pwd:yolo】下载注意这是 yolov7-v0.1 版本的预训练权重若后续有版本更新记得替换。本次训练 VOC 数据集使用的预训练权重为 yolov7-tiny.pt 3. 训练模型
将准备好的数据集文件夹即 VOC 复制到 yolov7 项目环境中将准备好的预训练权重 yolov7-tiny.pt 复制到 yolov7 项目环境中完整的项目结构如下图所示。训练目标检测模型主要修改 cfg 文件夹下的模型配置文件 yolov7-tiny.ymal 以及 data 文件夹下的数据配置文件 coco.yaml 3.1 修改模型配置文件
由于该项目使用的是 yolov7-tiny.pt 这个预训练权重所以需要使用 cfg/training 目录下的 yolov7-tiny.yaml 这个文件由于不同的预训练权重对应不同的网络结构所以用错预训练权重会报错。主要修改 yolov7-tiny.yaml 文件的第二行即需要识别的类别数由于这里识别 VOC 的 20 个类别故修改为 20 即可如下所示 3.2 修改数据配置文件
修改 data 目录下相应的 yaml 文件找到目录下的 coco.yaml 文件主要修改如下
1. 注释第 4 行2. 修改第 7 行训练集的路径3. 修改第 8 行验证集的路径4. 注释第 9 行因为未使用到测试集5. 修改第 12 行需要检测的类别数个数6. 修改第 15 行需要检测的类别数名称 3.3 训练模型
在终端执行如下指令即可开始训练参考自 yolov7 的 README.md/Training
python train.py --workers 8 --device 0 --batch-size 32 --data data/coco.yaml --img 640 640 --cfg cfg/training/yolov7-tiny.yaml --weights yolov7-tiny.pt --name yolov7 --hyp data/hyp.scratch.p5.yaml --epochs 100博主训练的模型为 p5 models 且使用的是单个 GPU 进行训练显卡为 RTX3060操作系统为 Ubuntu20.04pytorch 版本为 1.12.0训练时长大概 1 小时左右。训练的参数的指定和 yolov5 差不多简要解释如下
–-workers 最大工作核心数–-device 指定训练的设备CPU0(代表第一个 GPU 设备)–-batch-size 每次输入到网络的图片数-–data 数据配置文件的路径–-img 输入图像的尺寸–-cfg 模型配置文件路径–-weights 预训练权重路径–-name 训练保存的文件夹名字-–hyp 超参数文件路径–epochs 训练轮数
还有其它参数博主并未设置如 –-multi-scale 多尺度训练等。大家一定要根据自己的实际情况(如显卡算力)指定不同的参数如果你之前训练过 yolov5那我相信这对你来说应该是小 case
训练完成后的模型权重保存在 run/train/weights 文件夹下和 yolov5 不同的是它保存了多个权重文件使用 best.pt 进行后续模型部署量化即可这里提供博主训练好的权重文件下载链接 Baidu Drive【pwd:yolo】 3.4 mAP测试
由于后续我们要对模型进行 PTQ 量化需要一些指标来衡量模型的性能mAP 是一个重要的衡量指标。我们需要对比量化前后模型的 mAP首先来看量化前原始 pytorch 模型的 mAP测试的数据集直接选用验证集的 998 张图片。
我们将置信度阈值设置为 0.001NMS 阈值设置为 0.65方便与后续 PTQ 量化模型对比。
mAP 测试的指令如下
python test.py --data data/coco.yaml --img 640 --batch 32 --conf 0.001 --iou 0.65 --device 0 --weights best.pt --name yolov7_640_val测试完成后的结果会保存在 runs/test/yolov7_640_val 文件夹下这里总结下原始 pytorch 模型的性能
ModelSizemAPval0.5:0.95mAPval0.5Params (M)FLOPs (G)YOLOv7-tiny6400.4910.7445.813.3
三、YOLOv7-PTQ量化部署 由于博主手头没有合适的 Jetson 嵌入式设备因此打算使用自己的主机完成 YOLOv7-PTQ 量化及部署工作量化部署使用的 repo 是 tensorRT_Pro。 接下来我们主要是针对 tensorRT_Pro 项目中的 YOLOv7 完成 PTQ 模型的量化和部署体现在 tensorRT_Pro 中其实就是 YOLOv7 的 INT8 量化本次量化的模型是 YOLOv7-tiny.pt数据集为 VOC类别数为 20。 1. 源码下载
tensorRT_Pro 的代码可以直接从 GitHub 官网上下载源码下载地址是 https://github.com/shouxieai/tensorRT_ProLinux 下代码克隆指令如下
$ git clone https://github.com/shouxieai/tensorRT_Pro也可手动点击下载点击右上角的 Code 按键将代码下载下来。至此整个项目就已经准备好了。也可以点击 Baidu Drive【pwd:yolo】 下载博主准备好的源代码注意代码下载于 2023/9/24 日若有改动请参考最新
2. 环境配置 需要使用的软件环境有 TensorRT、CUDA、cuDNN、OpenCV、Protobuf所有软件环境的安装可以参考 Ubuntu20.04部署YOLOv5这里不再赘述需要各位看官自行配置好相关环境外网访问较慢这里提供下博主安装过程中的软件安装包下载链接 Baidu Drive【pwd:yolo】 tensorRT_Pro 提供 CMakeLists.txt 和 Makefile 两种方式编译二者选一即可 2.1 配置CMakeLists.txt
主要修改六处
1. 修改第 10 行选择不支持 python (也可选择支持)
set(HAS_PYTHON OFF)2. 修改第 18 行修改 OpenCV 路径
set(OpenCV_DIR /usr/local/include/opencv4/)3. 修改第 20 行修改 CUDA 路径
set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda-11.6)4. 修改第 21 行修改 cuDNN 路径
set(CUDNN_DIR /usr/local/cudnn8.4.0.27-cuda11.6)5. 修改第 22 行修改 tensorRT 路径
set(TENSORRT_DIR /opt/TensorRT-8.4.1.5)6. 修改第 33 行修改 protobuf 路径
set(PROTOBUF_DIR /home/jarvis/protobuf)完整的 CMakeLists.txt 的内容如下
cmake_minimum_required(VERSION 2.6)
project(pro)option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/workspace)# 如果要支持python则设置python路径
set(HAS_PYTHON OFF) # 修改 1
set(PythonRoot /datav/software/anaconda3)
set(PythonName python3.9)# 如果你是不同显卡请设置为显卡对应的号码参考这里https://developer.nvidia.com/zh-cn/cuda-gpus#compute
#set(CUDA_GEN_CODE -gencodearchcompute_75,codesm_75)# 如果你的opencv找不到可以自己指定目录
set(OpenCV_DIR /usr/local/include/opencv4/) # 修改 2 set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda-11.6) # 修改 3
set(CUDNN_DIR /usr/local/cudnn8.4.0.27-cuda11.6) # 修改 4
set(TENSORRT_DIR /opt/TensorRT-8.4.1.5) # 修改 5 # set(CUDA_TOOLKIT_ROOT_DIR /data/sxai/lean/cuda-10.2)
# set(CUDNN_DIR /data/sxai/lean/cudnn7.6.5.32-cuda10.2)
# set(TENSORRT_DIR /data/sxai/lean/TensorRT-7.0.0.11)# set(CUDA_TOOLKIT_ROOT_DIR /data/sxai/lean/cuda-11.1)
# set(CUDNN_DIR /data/sxai/lean/cudnn8.2.2.26)
# set(TENSORRT_DIR /data/sxai/lean/TensorRT-7.2.1.6)# 因为protobuf需要用特定版本所以这里指定路径
set(PROTOBUF_DIR /home/jarvis/protobuf) # 修改 6 find_package(CUDA REQUIRED)
find_package(OpenCV)include_directories(${PROJECT_SOURCE_DIR}/src${PROJECT_SOURCE_DIR}/src/application${PROJECT_SOURCE_DIR}/src/tensorRT${PROJECT_SOURCE_DIR}/src/tensorRT/common${OpenCV_INCLUDE_DIRS}${CUDA_TOOLKIT_ROOT_DIR}/include${PROTOBUF_DIR}/include${TENSORRT_DIR}/include${CUDNN_DIR}/include
)# 切记protobuf的lib目录一定要比tensorRT目录前面因为tensorRTlib下带有protobuf的so文件
# 这可能带来错误
link_directories(${PROTOBUF_DIR}/lib${TENSORRT_DIR}/lib${CUDA_TOOLKIT_ROOT_DIR}/lib64${CUDNN_DIR}/lib
)if(${HAS_PYTHON} STREQUAL ON)message(Usage Python ${PythonRoot})include_directories(${PythonRoot}/include/${PythonName})link_directories(${PythonRoot}/lib)set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -DHAS_PYTHON)
endif()set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -stdc11 -Wall -O0 -Wfatal-errors -pthread -w -g)
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -stdc11 -O0 -Xcompiler -fPIC -g -w ${CUDA_GEN_CODE})
file(GLOB_RECURSE cpp_srcs ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB_RECURSE cuda_srcs ${PROJECT_SOURCE_DIR}/src/*.cu)
cuda_add_library(plugin_list SHARED ${cuda_srcs})
target_link_libraries(plugin_list nvinfer nvinfer_plugin)
target_link_libraries(plugin_list cuda cublas cudart cudnn)
target_link_libraries(plugin_list protobuf pthread)
target_link_libraries(plugin_list ${OpenCV_LIBS})add_executable(pro ${cpp_srcs})# 如果提示插件找不到请使用dlopen(xxx.so, NOW)的方式手动加载可以解决插件找不到问题
target_link_libraries(pro nvinfer nvinfer_plugin)
target_link_libraries(pro cuda cublas cudart cudnn)
target_link_libraries(pro protobuf pthread plugin_list)
target_link_libraries(pro ${OpenCV_LIBS})if(${HAS_PYTHON} STREQUAL ON)set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/example-python/pytrt)add_library(pytrtc SHARED ${cpp_srcs})target_link_libraries(pytrtc nvinfer nvinfer_plugin)target_link_libraries(pytrtc cuda cublas cudart cudnn)target_link_libraries(pytrtc protobuf pthread plugin_list)target_link_libraries(pytrtc ${OpenCV_LIBS})target_link_libraries(pytrtc ${PythonName})target_link_libraries(pro ${PythonName})
endif()add_custom_target(yoloDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro yolo
)add_custom_target(yolo_gpuptrDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro yolo_gpuptr
)add_custom_target(yolo_fastDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro yolo_fast
)add_custom_target(centernetDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro centernet
)add_custom_target(alphapose DEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro alphapose
)add_custom_target(retinafaceDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro retinaface
)add_custom_target(dbfaceDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro dbface
)add_custom_target(arcface DEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro arcface
)add_custom_target(bert DEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro bert
)add_custom_target(fallDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro fall_recognize
)add_custom_target(scrfdDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro scrfd
)add_custom_target(lessonDEPENDS proWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/workspaceCOMMAND ./pro lesson
)add_custom_target(pyscrfdDEPENDS pytrtcWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/example-pythonCOMMAND python test_scrfd.py
)add_custom_target(pyinstallDEPENDS pytrtcWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/example-pythonCOMMAND python setup.py install
)add_custom_target(pytorchDEPENDS pytrtcWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/example-pythonCOMMAND python test_torch.py
)add_custom_target(pyyolov5DEPENDS pytrtcWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/example-pythonCOMMAND python test_yolov5.py
)add_custom_target(pycenternetDEPENDS pytrtcWORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/example-pythonCOMMAND python test_centernet.py
)2.2 配置Makefile
主要修改六处
1. 修改第 4 行修改 protobuf 路径
lean_protobuf : /home/jarvis/protobuf2. 修改第 5 行修改 tensorRT 路径
lean_tensor_rt : /opt/TensorRT-8.4.1.53. 修改第 6 行修改 cuDNN 路径
lean_cudnn : /usr/local/cudnn8.4.0.27-cuda11.64. 修改第 7 行修改 OpenCV 路径
lean_opencv : /usr/local5. 修改第 8 行修改 CUDA 路径
lean_cuda : /usr/local/cuda-11.66. 修改第 9 行选择不支持 python (也可选择支持)
use_python : false完整的 Makefile 的内容如下
cc : g
nvcc ${lean_cuda}/bin/nvcclean_protobuf : /home/jarvis/protobuf # 修改 1
lean_tensor_rt : /opt/TensorRT-8.4.1.5 # 修改 2
lean_cudnn : /usr/local/cudnn8.4.0.27-cuda11.6 # 修改 3
lean_opencv : /usr/local # 修改 4
lean_cuda : /usr/local/cuda-11.6 # 修改 5
use_python : false # 修改 6
python_root : /datav/software/anaconda3# python_root指向的lib目录下有个libpython3.9.so因此这里写python3.9
# 对于有些版本so名字是libpython3.7m.so你需要填写python3.7m
# /datav/software/anaconda3/lib/libpython3.9.so
python_name : python3.9# 如果是其他显卡请修改-gencodearchcompute_75,codesm_75为对应显卡的能力
# 显卡对应的号码参考这里https://developer.nvidia.com/zh-cn/cuda-gpus#compute
cuda_arch : # -gencodearchcompute_75,codesm_75cpp_srcs : $(shell find src -name *.cpp)
cpp_objs : $(cpp_srcs:.cpp.cpp.o)
cpp_objs : $(cpp_objs:src/%objs/%)
cpp_mk : $(cpp_objs:.cpp.o.cpp.mk)cu_srcs : $(shell find src -name *.cu)
cu_objs : $(cu_srcs:.cu.cu.o)
cu_objs : $(cu_objs:src/%objs/%)
cu_mk : $(cu_objs:.cu.o.cu.mk)include_paths : src \src/application \src/tensorRT \src/tensorRT/common \$(lean_protobuf)/include \$(lean_opencv)/include/opencv4 \$(lean_tensor_rt)/include \$(lean_cuda)/include \$(lean_cudnn)/include library_paths : $(lean_protobuf)/lib \$(lean_opencv)/lib \$(lean_tensor_rt)/lib \$(lean_cuda)/lib64 \$(lean_cudnn)/liblink_librarys : opencv_core opencv_imgproc opencv_videoio opencv_imgcodecs \nvinfer nvinfer_plugin \cuda cublas cudart cudnn \stdc protobuf dl# HAS_PYTHON表示是否编译python支持
support_define : ifeq ($(use_python), true)
include_paths $(python_root)/include/$(python_name)
library_paths $(python_root)/lib
link_librarys $(python_name)
support_define -DHAS_PYTHON
endifempty :
export_path : $(subst $(empty) $(empty),:,$(library_paths))run_paths : $(foreach item,$(library_paths),-Wl,-rpath$(item))
include_paths : $(foreach item,$(include_paths),-I$(item))
library_paths : $(foreach item,$(library_paths),-L$(item))
link_librarys : $(foreach item,$(link_librarys),-l$(item))cpp_compile_flags : -stdc11 -g -w -O0 -fPIC -pthread -fopenmp $(support_define)
cu_compile_flags : -stdc11 -g -w -O0 -Xcompiler $(cpp_compile_flags) $(cuda_arch) $(support_define)
link_flags : -pthread -fopenmp -Wl,-rpath$$ORIGINcpp_compile_flags $(include_paths)
cu_compile_flags $(include_paths)
link_flags $(library_paths) $(link_librarys) $(run_paths)ifneq ($(MAKECMDGOALS), clean)
-include $(cpp_mk) $(cu_mk)
endifpro : workspace/pro
pytrtc : example-python/pytrt/libpytrtc.so
expath : library_path.txtlibrary_path.txt : echo LD_LIBRARY_PATH$(export_path):$$LD_LIBRARY_PATH $workspace/pro : $(cpp_objs) $(cu_objs)echo Link $mkdir -p $(dir $)$(cc) $^ -o $ $(link_flags)example-python/pytrt/libpytrtc.so : $(cpp_objs) $(cu_objs)echo Link $mkdir -p $(dir $)$(cc) -shared $^ -o $ $(link_flags)objs/%.cpp.o : src/%.cppecho Compile CXX $mkdir -p $(dir $)$(cc) -c $ -o $ $(cpp_compile_flags)objs/%.cu.o : src/%.cuecho Compile CUDA $mkdir -p $(dir $)$(nvcc) -c $ -o $ $(cu_compile_flags)objs/%.cpp.mk : src/%.cppecho Compile depends CXX $mkdir -p $(dir $)$(cc) -M $ -MF $ -MT $(:.cpp.mk.cpp.o) $(cpp_compile_flags)objs/%.cu.mk : src/%.cuecho Compile depends CUDA $mkdir -p $(dir $)$(nvcc) -M $ -MF $ -MT $(:.cu.mk.cu.o) $(cu_compile_flags)yolo : workspace/procd workspace ./pro yoloyolo_gpuptr : workspace/procd workspace ./pro yolo_gpuptrdyolo : workspace/procd workspace ./pro dyolodunet : workspace/procd workspace ./pro dunetdmae : workspace/procd workspace ./pro dmaedclassifier : workspace/procd workspace ./pro dclassifieryolo_fast : workspace/procd workspace ./pro yolo_fastbert : workspace/procd workspace ./pro bertalphapose : workspace/procd workspace ./pro alphaposefall : workspace/procd workspace ./pro fall_recognizeretinaface : workspace/procd workspace ./pro retinafacearcface : workspace/procd workspace ./pro arcfacetest_warpaffine : workspace/procd workspace ./pro test_warpaffinetest_yolo_map : workspace/procd workspace ./pro test_yolo_maparcface_video : workspace/procd workspace ./pro arcface_videoarcface_tracker : workspace/procd workspace ./pro arcface_trackertest_all : workspace/procd workspace ./pro test_allscrfd : workspace/procd workspace ./pro scrfdcenternet : workspace/procd workspace ./pro centernetdbface : workspace/procd workspace ./pro dbfacehigh_perf : workspace/procd workspace ./pro high_perflesson : workspace/procd workspace ./pro lessonplugin : workspace/procd workspace ./pro pluginpytorch : pytrtccd example-python python test_torch.pypyscrfd : pytrtccd example-python python test_scrfd.pypyretinaface : pytrtccd example-python python test_retinaface.pypycenternet : pytrtccd example-python python test_centernet.pypyyolov5 : pytrtccd example-python python test_yolov5.pypyyolov7 : pytrtccd example-python python test_yolov7.pypyyolox : pytrtccd example-python python test_yolox.pypyarcface : pytrtccd example-python python test_arcface.pypyinstall : pytrtccd example-python python setup.py installclean :rm -rf objs workspace/pro example-python/pytrt/libpytrtc.so example-python/build example-python/dist example-python/pytrt.egg-info example-python/pytrt/__pycache__rm -rf workspace/single_inferencerm -rf workspace/scrfd_result workspace/retinaface_resultrm -rf workspace/YoloV5_result workspace/YoloX_resultrm -rf workspace/face/library_draw workspace/face/resultrm -rf buildrm -rf example-python/pytrt/libplugin_list.sorm -rf library_path.txt.PHONY : clean yolo alphapose fall debug# 导出符号使得运行时能够链接上
export LD_LIBRARY_PATH:$(export_path):$(LD_LIBRARY_PATH)3. ONNX导出
训练的模型使用 yolov7-tiny.pttorch 版本 1.12.1onnx 版本 1.13.1ONNX 导出参考自 YoloV5案例第一部分导出ONNX
关于静态 batch 和动态 batch 有以下几点说明更多细节请查看 YoloV8的动态静态batch如何理解和使用
静态batch
导出的 onnx 指定所有维度均为明确的数字是静态 shape 模型在推理的时候它永远都是同样的 batch 推理即使你目前只有一个图推理它也需要 n 个 batch 的耗时适用于大部分场景整个代码逻辑非常简单
动态batch
导出的时候指定特定维度为 dynamic也就是不确定状态模型推理时才决定所需推理的 batch 大小耗时最优但 onnx 复杂度提高了适用于如 server 有大量不均匀的请求时的场景
3.1 静态batch导出
静态 batch 的导出不需要修改任何内容直接将训练好的 VOC 权重 best.pt 放在 yolov7 主目录下在终端执行如下指令
cd yolov7
python export.py --grid --weightsbest.pt执行完成后会在当前目录生成导出的 best.onnx 模型用于后续量化部署。
3.2 动态 batch 的导出
动态 batch 的导出也不需要修改任何文件的内容我们这次利用 onnxsim 第三方库来简化我们的 onnx 模型首先确保你当前的环境中安装了 onnxsim否则执行如下指令进行安装
pip install onnxsim -i https://pypi.tuna.tsinghua.edu.cn/simple然后将训练好的 VOC 权重 best.pt 放在 yolov7 主目录下在终端执行如下指令
cd yolov7
python export.py --dynamic-batch --grid --weightsbest.pt执行完成后会在当前目录生成导出的 best.onnx 模型用于后续量化部署。
4. PTQ量化
4.1 前置工作
在开始PTQ 量化之前我们需要准备两个东西模型和校准图片
模型我们采用动态 batch 导出的 best.onnx 模型将它放在 tensorRT_Pro/workspace 文件夹下
校准图片我们从训练集随机选取 1000 张图片进行校准将它也放在 tensorRT_Pro/workspace 文件夹下
1000 张校准数据集随机选取的代码如下
import os
import random
import shutildef random_copy_images(source_folder, destination_folder, num_images1000):# 确保目标文件夹存在if not os.path.exists(destination_folder):os.makedirs(destination_folder)# 获取源文件夹中的所有图片文件image_files [file for file in os.listdir(source_folder) if file.lower().endswith((.png, .jpg, .jpeg, .bmp, .gif))]# 随机选择1000张图片selected_images random.sample(image_files, min(num_images, len(image_files)))# 复制选中的图片到目标文件夹for image_file in selected_images:source_path os.path.join(source_folder, image_file)destination_path os.path.join(destination_folder, image_file)shutil.copy(source_path, destination_path)source_folder /home/jarvis/Learn/Datasets/VOC_PTQ/images/train # 带有图片的文件夹路径
destination_folder calib_data # 目标文件夹路径
num_images 1000 # 需要随机获取的图片数量random_copy_images(source_folder, destination_folder, num_images)你需要修改以下几项
source_folder源训练集文件夹路径destination_folder校准数据集文件夹路径num_images随机选择的图片数量
4.2 源码修改 将上述模型和校准图片准备好后还要修改下源码yolo 模型的推理代码主要在 src/application/app_yolo.cpp 文件中我们就只需要修改这一个文件中的内容即可源码修改较简单主要有以下几点 1. app_yolo.cpp 177 行TRT::Mode 修改为 INT8“yolov7” 改成 “best”2. app_yolo.cpp 25 行新增 voclabels 数组添加 voc 数据集的类别名称3. app_yolo.cpp 100 行cocolabels 修改为 voclabels4. app_yolo.cpp 149 行“inference” 修改为 “calib_data” 指定校准图片的路径
具体修改如下
test(Yolo::Type::V7, TRT::Mode::INT8, best) // 修改1 177行yolov7改成beststatic const char *voclabels[] {aeroplane, bicycle, bird, boat, bottle,bus, car, cat, chair, cow,diningtable, dog, horse, motorbike, person,pottedplant, sheep, sofa, train, tvmonitor}; // 修改2 25行新增代码为自训练模型的类别名称for(auto obj : boxes){...auto name voclabels[obj.class_label]; // 修改3 100行cocolabels修改为voclabels...
}TRT::compile(mode, // FP32、FP16、INT8test_batch_size, // max batch sizeonnx_file, // source model_file, // save to{},int8process,calib_data // 修改4 149行 inference 修改为 calib_data
);4.3 编译运行
OK源码修改好了Makefile 编译文件也搞定了可以编译运行了直接在终端执行如下指令即可
make yolo图解如下所示 编译运行后在 workspace 文件夹下会生成 INT8 的 engine 模型 best.INT8.trtmodel 用于模型推理同时它还会生成 best_Yolov5_INT8_result 文件夹该文件夹下保存了推理的图片
模型推理效果如下图所示 4.4 PTQ模型mAP测试 我们再来测试下经过 PTQ 量化后模型的 mAPtensorRT_Pro 中已经提供了对应 mAP 测试的代码在 src/application/test_yolo_map.cpp 文件中我们就只需要修改这一个文件中的内容即可源码修改较简单主要有以下几点 1. test_yolo_map.cpp 172 行修改要测试的验证集文件夹路径2. test_yolo_map.cpp 175 行修改要测试的 INT8 模型yolov5s 修改为 best3. test_yolo_map.cpp 176 行Yolo::Type 修改为 V7TRT::Mode 修改为 INT84. test_yolo_map.cpp 125 行将 save_to_json 函数简单修改下
修改后完整的 test_yolo_map.cpp 如下所示
#include builder/trt_builder.hpp
#include infer/trt_infer.hpp
#include common/ilogger.hpp
#include common/json.hpp
#include app_yolo/yolo.hpp
#include vector
#include stringusing namespace std;bool requires(const char* name);struct BoxLabel{int label;float cx, cy, width, height;float confidence;
};struct ImageItem{string image_file;Yolo::BoxArray detections;
};vectorImageItem scan_dataset(const string images_root){vectorImageItem output;auto image_files iLogger::find_files(images_root, *.jpg);for(int i 0; i image_files.size(); i){auto image_file image_files[i];if(!iLogger::exists(image_file)){INFOW(Not found: %s, image_file.c_str());continue;}ImageItem item;item.image_file image_file;output.emplace_back(item);}return output;
}static void inference(vectorImageItem images, int deviceid, const string engine_file, TRT::Mode mode, Yolo::Type type, const string model_name){auto engine Yolo::create_infer(engine_file, type, deviceid, 0.001f, 0.65f,Yolo::NMSMethod::CPU, 10000);if(engine nullptr){INFOE(Engine is nullptr);return;}int nimages images.size();vectorshared_futureYolo::BoxArray image_results(nimages);for(int i 0; i nimages; i){if(i % 100 0){INFO(Commit %d / %d, i1, nimages);}image_results[i] engine-commit(cv::imread(images[i].image_file));}for(int i 0; i nimages; i)images[i].detections image_results[i].get();
}void detect_images(vectorImageItem images, Yolo::Type type, TRT::Mode mode, const string model){int deviceid 0;auto mode_name TRT::mode_string(mode);TRT::set_device(deviceid);auto int8process [](int current, int count, const vectorstring files, shared_ptrTRT::Tensor tensor){INFO(Int8 %d / %d, current, count);for(int i 0; i files.size(); i){auto image cv::imread(files[i]);Yolo::image_to_tensor(image, tensor, type, i);}};const char* name model.c_str();INFO( test %s %s %s , Yolo::type_name(type), mode_name, name);if(not requires(name))return;string onnx_file iLogger::format(%s.onnx, name);string model_file iLogger::format(%s.%s.trtmodel, name, mode_name);int test_batch_size 16;if(not iLogger::exists(model_file)){TRT::compile(mode, // FP32、FP16、INT8test_batch_size, // max batch sizeonnx_file, // source model_file, // save to{},int8process,inference);}inference(images, deviceid, model_file, mode, type, name);
}bool save_to_json(const vectorImageItem images, const string file){Json::Value predictions(Json::arrayValue);for(int i 0; i images.size(); i){auto image images[i];auto file_name iLogger::file_name(image.image_file, false);string image_id file_name;auto boxes image.detections;for(auto box : boxes){Json::Value jitem;jitem[image_id] image_id;jitem[category_id] box.class_label;jitem[score] box.confidence;auto bbox jitem[bbox];bbox.append(box.left);bbox.append(box.top);bbox.append(box.right - box.left);bbox.append(box.bottom - box.top);predictions.append(jitem);}}return iLogger::save_file(file, predictions.toStyledString());
}int test_yolo_map(){/*结论1. YoloV5在tensorRT下和pytorch下只要输入一样输出的差距最大值是1e-32. YoloV5-6.0的mAP官方代码跑下来是mAP.5:.95 0.367, mAP.5 0.554与官方声称的有差距3. 这里的tensorRT版本测试的精度为mAP.5:.95 0.357, mAP.5 0.539与pytorch结果有差距4. cv2.imread与cv::imread在操作jpeg图像时在我这里测试读出的图像值不同最大差距有19。而png图像不会有这个问题若想完全一致请用png图像5. 预处理部分若采用letterbox的方式做预处理由于tensorRT这里是固定640x640大小测试采用letterbox并把多余部分设置为0. 其推理结果与pytorch相近但是依旧有差别6. 采用warpAffine和letterbox两种方式的预处理结果在mAP上没有太大变化小数点后三位差7. mAP差一个点的原因可能在固定分辨率这件事上还有是pytorch实现的所有细节并非完全加入进来。这些细节可能有没有找到的部分*/auto images scan_dataset(/home/jarvis/Learn/Datasets/VOC_PTQ/images/val);INFO(images.size %d, images.size());string model best;detect_images(images, Yolo::Type::V7, TRT::Mode::INT8, model);save_to_json(images, model .prediction.json);return 0;
}上述代码会将 INT8 模型在验证集中所有图像的检测结果存储到一个 JSON 文件中每个检测到的物体都被序列化为 JSON 格式信息包括图像 ID、类别 ID、置信度和边界框坐标。后续我们就可以拿着这个预测结果的 JSON 文件和我们真实标签的 JSON 文件通过 COCO Python API 去计算 mAP 指标。
有以下几点需要注意
博主将 JSON 文件中的 image_id 保存为一个字符串考虑到图片命名的差异性博主将 JSON 文件中的 category_id 直接保存为类别标签没有做转换mAP 测试使用的 NMS_threshold 0.65fConf_threshold 0.001f 与 pytorch 保持一致关于 mAP 的相关原理介绍可参考 目标检测mAP计算以及coco评价标准
将源码修改好后直接在终端执行如下指令即可
make test_yolo_map图解如下所示 运行成功后在 workspace 文件夹下会生成 best.prediction.json 文件该 JSON 文件中保存着 INT8 模型在验证集上的推理结果。
我们拿到了模型预测结果的 JSON 文件后还需要拿到真实标签的 JSON 文件但是现在我们只有验证集真实的 YOLO 标签文件因此需要将 YOLO 标签转换为 JSON 文件转换代码如下(from chatGPT)
import os
import cv2
import json
import logging
import os.path as osp
from tqdm import tqdm
from functools import partial
from multiprocessing import Pool, cpu_countdef set_logging(nameNone):rank int(os.getenv(RANK, -1))logging.basicConfig(format%(message)s, levellogging.INFO if (rank in (-1, 0)) else logging.WARNING)return logging.getLogger(name)LOGGER set_logging(__name__)def process_img(image_filename, data_path, label_path):# Open the image file to get its sizeimage_path os.path.join(data_path, image_filename)img cv2.imread(image_path)height, width img.shape[:2]# Open the corresponding label filelabel_file os.path.join(label_path, os.path.splitext(image_filename)[0] .txt)with open(label_file, r) as file:lines file.readlines()# Process the labelslabels []for line in lines:category, x, y, w, h map(float, line.strip().split())labels.append((category, x, y, w, h))return image_filename, {shape: (height, width), labels: labels}def get_img_info(data_path, label_path):LOGGER.info(fGet img info)image_filenames os.listdir(data_path)with Pool(cpu_count()) as p:results list(tqdm(p.imap(partial(process_img, data_pathdata_path, label_pathlabel_path), image_filenames), totallen(image_filenames)))img_info {image_filename: info for image_filename, info in results}return img_infodef generate_coco_format_labels(img_info, class_names, save_path):# for evaluation with pycocotoolsdataset {categories: [], annotations: [], images: []}for i, class_name in enumerate(class_names):dataset[categories].append({id: i, name: class_name, supercategory: })ann_id 0LOGGER.info(fConvert to COCO format)for i, (img_path, info) in enumerate(tqdm(img_info.items())):labels info[labels] if info[labels] else []img_id osp.splitext(osp.basename(img_path))[0]img_h, img_w info[shape]dataset[images].append({file_name: os.path.basename(img_path),id: img_id,width: img_w,height: img_h,})if labels:for label in labels:c, x, y, w, h label[:5]# convert x,y,w,h to x1,y1,x2,y2x1 (x - w / 2) * img_wy1 (y - h / 2) * img_hx2 (x w / 2) * img_wy2 (y h / 2) * img_h# cls_id starts from 0cls_id int(c)w max(0, x2 - x1)h max(0, y2 - y1)dataset[annotations].append({area: h * w,bbox: [x1, y1, w, h],category_id: cls_id,id: ann_id,image_id: img_id,iscrowd: 0,# masksegmentation: [],})ann_id 1with open(save_path, w) as f:json.dump(dataset, f)LOGGER.info(fConvert to COCO format finished. Resutls saved in {save_path})if __name__ __main__:# Define the pathsdata_path /home/jarvis/Learn/Datasets/VOC_PTQ/images/vallabel_path /home/jarvis/Learn/Datasets/VOC_PTQ/labels/valclass_names [aeroplane, bicycle, bird, boat, bottle, bus,car, cat, chair, cow, diningtable, dog, horse,motorbike, person, pottedplant, sheep, sofa, train, tvmonitor] # 类别名称请务必与 YOLO 格式的标签对应save_path ./val.jsonimg_info get_img_info(data_path, label_path)generate_coco_format_labels(img_info, class_names, save_path)上述代码的功能是将 YOLO 格式的数据集包括图像文件和对应的 .txt 标签文件转换成 COCO JSON 格式的标注。转换后的数据包括一个 JSON 标签文件JSON 标签文件中包含了每个图像的所有物体的类别和边界框信息。
你需要修改以下几项
data_path需要转换的图像文件路径label_path需要转换的 txt 标签文件路径class_names数据集的类别列表请务必与 YOLO 标签的相对应save_path转换后 JSON 文件保存的路径注意以上路径都不要包含中文Windows 下路径记得使用 \\ 或者 / 防止转义
YOLO 标签中目标框保存的格式是每一行代表一个目标框信息每一行共包含 [label_id, x_center, y_center, w, h] 五个变量分别代表着标签 ID经过归一化后的中心点坐标和目标框宽高。
JSON 文件中目标框保存的格式是 [xywh] 四个变量分别代表着经过归一化的左上角坐标和目标框宽高。
关于代码的分析可以参考tensorRT模型性能测试
至此两个 JSON 文件都准备好了一个是模型推理的预测结果一个是真实结果。拿到两个 JSON 文件后我们就可以进行 mAP 测试了具体代码如下
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval# Run COCO mAP evaluation
# Reference: https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynbannotations_path val.json
results_file best.prediction.json
cocoGt COCO(annotation_fileannotations_path)
cocoDt cocoGt.loadRes(results_file)
imgIds sorted(cocoGt.getImgIds())
cocoEval COCOeval(cocoGt, cocoDt, bbox)
cocoEval.params.imgIds imgIds
cocoEval.evaluate()
cocoEval.accumulate()
cocoEval.summarize()你需要修改以下几项
annotations_path真实标签的 JSON 文件路径results_file模型预测结果的 JSON 文件路径
执行后测试结果如下图所示 我们将它与原始 pytorch 的模型放在一起进行对比下
ModelSizemAPval0.5:0.95mAPval0.5Params (M)FLOPs (G)YOLOv7-tiny6400.4910.7445.813.3YOLOv7-tiny-INT86400.3460.562--
可以看到相比于原始 pytorch 模型PTQ 量化后的模型 mAP 下降了近 18 个点
那博主之前有测试过 YOLOv5 的 PTQ 量化其 mAP 也就下降了近 6 个点YOLOv7 的 PTQ 量化模型精度损失未免太严重了呀是什么原因导致的呢
经博主测试发现是由于校准图片数量的原因1000 张校准图片能将 YOLOv5 量化得很好但这并不适用于 YOLOv7YOLOv7 的校准图片选取 10 张的校准结果也比 1000 张要好具体细节可以查看 4.1 小节
OK至此 YOLOv7 模型的 PTQ 量化到这里结束了各位看官可以在自己的数据集测试下 PTQ 量化后模型的性能。
四、讨论
1. 校准图片数量 那可能有不少看官好奇为什么校准图片选择 1000 张呢是由什么来决定的呢 这小节我们就来看看校准图片数量对 PTQ 量化模型的影响博主测试了在不同校准图片下量化的 PTQ 模型在同一个验证集上的 mAP分别在训练集随机挑选了 10、50、100、500、600、700、800、900、1000、4013 张图片其中 4013 张图片是整个训练集的数量。
测试结果如下表所示
ModelCalib DatamAPval0.5:0.95mAPval0.5YOLOv7-tiny-INT8100.3990.621YOLOv7-tiny-INT8500.4640.699YOLOv7-tiny-INT81000.4690.703YOLOv7-tiny-INT85000.4710.703YOLOv7-tiny-INT86000.3450.559YOLOv7-tiny-INT87000.4710.703YOLOv7-tiny-INT88000.4730.705YOLOv7-tiny-INT89000.3470.562YOLOv7-tiny-INT810000.3460.562YOLOv7-tiny-INT84013(all)0.3440.557
可视化图如下所示 从表中的数据我们可以分析得到下面的一些结论
1. 校准数据量与模型性能的关系校准数据的数量对模型 PTQ 量化后的性能有明显的影响。特别是当校准数据从 10 增加到 500 时模型的 mAP 明显增加说明在这个区间内增加校准数据可以有效提高模型的性能。
2. 最佳校准数据量在这个测试中当使用 800 张校准图片时模型达到了最高点的 mAP分别为 0.473 和 0.705。这意味着并不是校准数据越多越好需要找到一个适当的平衡点。
3. 校准数据过多可能导致性能下降当校准数据从 800 增加到 900、100 或 4013 时模型的性能反而有所下降。这可能是因为过多的校准数据可能引入了噪声使得量化的过程过于复杂从而降低了模型的性能。
4. 整个训练集并非最佳选择尽管使用整个训练集4013 张图片进行校准可能看起来是一个直观的选择但在这个测试中它并没有提供最佳的性能。这可能意味着在实际应用中只需要选择一个子集进行校准即可无需使用整个训练集。
5. 初步校准数据的不足当仅使用 10 张校准图片时模型的性能也较低。这说明在实际应用中如果只有有限的校准数据可能需要考虑采集更多的数据以提高量化后的模型性能。
综上所述选择合适的校准数据量是 PTQ 量化的一个重要步骤。不同的模型和应用场景可能需要不同的校准数据量。因此为了得到最佳的量化性能可能需要进行多次实验来确定最佳的校准数据量。
博主一般推荐校准图片的数量在 500~1000 张即可没必要太多当然也不能太少。
2. 不同精度模型对比 PTQ 量化的模型性能到底怎么样呢与其它精度的模型相比有哪些优势又有哪些劣势呢 这个小节我们就来看看不同精度的模型的性能对比主要从 mAP 和速度两个方面衡量。博主测试了在同一个验证集上原始 pytorch 模型FP32 模型FP16 模型INT8 模型的性能。
原始 pytorch 模型和 INT8 模型性能我们之前已经了解过了下面我们来看看 FP32 模型和 FP16 模型的性能。
FP32模型 图4-1 FP32模型速度测试 图4-2 FP32模型mAP测试 FP16模型 图4-3 FP16模型速度测试 图4-4 FP16模型mAP测试 INT8模型 图4-5 INT8模型速度测试 图4-6 INT8模型mAP测试(800张校准) 值得注意的是关于速度的测试我们之前似乎并没有提到它具体是如何测试的呢 其实在 inference_and_performance 函数中就有关于速度相关的测试主要说明如下
1. 输入分辨率 640x6402. batch_size 13. 图像预处理 推理 后处理4. CUDA-11.6cuDNN-8.4.0TensorRT-8.4.1.55. NVIDIA RTX30606. 测试次数100 次取平均去掉 warmup7. 测试代码src/application/app_yolo.cpp8. 测试图像 6 张位于 workspace/inference 分辨率分别为810x1080500x8061024x684550x6761280x720800x533 9. 测试方式加载 6 张图后以原图重复 100 次不停的塞进去。让模型经历完整的图像的预处理后处理
测试结果如下表所示
ModelPrecisionmAPval0.5:0.95mAPval0.5Elapsed Time/msFPSYOLOv7-tiny.pt-0.4910.744--YOLOv7-tiny-FP32FP320.4880.7242.82355.15YOLOv7-tiny-FP16FP160.4890.7251.26792.23YOLOv7-tiny-INT8INT80.4730.7050.941066.55
可视化图如下所示 从表中的数据我们可以分析得到下面的一些结论
1. 精度与模型性能的关系
当我们从原始 pytorch 模型转到 FP32 模型时正常来说应该基本是无损的但是 mAP 掉了将近 2 个点左右这并不符合我们的直觉。mAP 差 2 个点的原因可能是在固定分辨率这件事上tensorRT 将图片分辨率固定在 640x640 大小。还有就是 pytorch 实现的所有细节并未完全加入进来这些细节可能有没有找到的部分。FP32 模型和 FP16 模型的 mAP 几乎一样没有任何精度的损失这倒是符合我们的直觉
2. 速度与模型性能的关系
FP16 和 INT8 的 FPS 分别为 792.23 和 1066.55远高于 FP32 的 355.15INT8 模型是所有模型中最快的达到了 1000 FPS 的速度尽管其精度稍低。
3. 权衡速度与精度
FP32 提供了较好的精度但速度较慢FP16 提供了与 FP32 类似的精度但速度提高了约 2.2 倍是一个非常不错的选择。INT8 提供了略低的精度但速度却是最快的比 FP32 快约 3 倍。
综上所述在实际应用中需要根据具体的需求权衡速度和精度。例如对于实时应用可能会选择 FP16 或 INT8 以获得更高的速度尽管可能牺牲一些精度。而对于需要高精度的应用可能会选择 FP32。
博主对比了同一张图片在不同精度模型下的推理效果如下所示让大家有个更直观的感受。 图4-7 car-FP32 图4-8 car-FP16 图4-9 car-INT8 3. YOLOv5-PTQ vs. YOLOv7-PTQ
最后我们当然是来对比下 YOLOv5-PTQ 量化后模型的性能和 YOLOv7-PTQ 量化后模型的性能哪个会更好那其实两个模型训练用的数据集都是同一个啦所以还是有可比性的
结果对比如下表所示
ModelPrecisionmAPval0.5:0.95mAPval0.5Elapsed Time/msFPSYOLOv5s.pt-0.4710.711--YOLOv7-tiny.pt-0.4910.744--YOLOv5s-FP32FP320.4470.6843.15317.79YOLOv7-tiny-FP32FP320.4880.7242.82355.15YOLOv5s-FP16FP160.4480.6831.34748.93YOLOv7-tiny-FP16FP160.4890.7251.26792.23YOLOv5s-INT8INT80.4090.6570.991008.93YOLOv7-tiny-INT8INT80.4730.7050.941066.55
可视化图如下 从表中我们可以看到对博主当前的 VOC 数据集而言YOLOv7-tiny 模型似乎碾压 YOLOv5s 模型呀不论是 pytorch 模型的效果还是 PTQ 量化后模型的推理速度和效果YOLOv7-tiny 都比 YOLOv5s 要优秀不少
当然也不排除 YOLOv5s 训练过程中并没有完全收敛得到最佳的性能因为博主只训练了 100 个 epoch那具体的对比结果各位看官可以自行测试博主这边只是简单分析下。
OKYOLOv7-PTQ 量化的内容到这里就结束了各位看官可以自行测试。
结语 本篇博客介绍了关于 yolov7 的 PTQ 量化以及部署流程博主在这里只做了最基础的演示如果有更多的需求需要各位看官自己去挖掘啦。下篇文章我们将会分享关于 yolov7 的 QAT 量化以及部署流程感谢各位看到最后创作不易读后有收获的看官帮忙点个⭐️ 下载链接 软件安装包下载链接【提取码:yolo】 源代码、权重、数据集下载链接【提取码:yolo】
参考
COCO Python APItensorRT模型性能测试Ubuntu20.04部署YOLOv5YoloV5案例第一部分导出ONNXTensorRT量化第四课PTQ与QAT目标检测mAP计算以及coco评价标准目标检测PASCAL VOC 数据集简介YoloV8的动态静态batch如何理解和使用https://github.com/ultralytics/yolov5https://github.com/shouxieai/tensorRT_Pro利用Anaconda安装pytorch和paddle深度学习环境pycharm安装—免额外安装CUDA和cudnn(适合小白的保姆级教学)