基于openvino+yolov5的模型量化记录(PTQ模式)

news2024/11/24 19:02:27

本文主要是记录学习openvino_notebootk 302-pytorch-quantization-aware-training文档的一些收获,对于yolov5在cpu部署感兴趣的可以参考下。

此文档的目的是为了了解openvino如何降低模型部署的推理时间,同时尽可能保证精度。
在这里插入图片描述

此文档一共提供了两种PTQ量化方式,下面分别介绍。

0. 数据集准备

首先两者都需要使用数据进行校准(calibration),借助于yolov5优秀的代码,这里直接调用它的dataloader即可。

data = check_dataset(DATASET_CONFIG)
val_dataloader = create_dataloader(
    data["val"], imgsz=640, batch_size=1, stride=32, pad=0.5, workers=1
)[0]

return val_dataloader

1. PTQ with POT(post-training Optimization Tool) 不推荐- _ -

1.1 准备数据

获取yolov5原始的data之后,对于POT,需要再实现openvino.tools.pot.api里面的DataLoader类, 有两个必须要实现的方法__init__ and __gtitem__.

from openvino.tools.pot.api import DataLoader

class YOLOv5POTDataLoader(DataLoader):
    """Inherit from DataLoader function and implement for YOLOv5."""

    def __init__(self, data_source):
        super().__init__({})
        self._data_loader = data_source
        self._data_iter = iter(self._data_loader)

    def __len__(self):
        return len(self._data_loader.dataset)

    def __getitem__(self, item):
        try:
            batch_data = next(self._data_iter)
        except StopIteration:
            self._data_iter = iter(self._data_loader)
            batch_data = next(self._data_iter)

        im, target, path, shape = batch_data

        im = im.float()
        im /= 255
        nb, _, height, width = im.shape
        img = im.cpu().detach().numpy()
        target = target.cpu().detach().numpy()

        annotation = dict()
        annotation["image_path"] = path
        annotation["target"] = target
        annotation["batch_size"] = nb
        annotation["shape"] = shape
        annotation["width"] = width
        annotation["height"] = height
        annotation["img"] = img

        return (item, annotation), img

1.2 配置量化流程

对于POT,量化参数需要通过配置字典(configuration dictionary)定义。包括algorithms,描述算法的参数,engine 描述推理流程的参数(optional),model描述float模型的路径。

algorithms_config = [
    {
        "name": "DefaultQuantization",
        "params": {
            "preset": "mixed",
            "stat_subset_size": 300,
            "target_device": "CPU"
        },
    }
]

engine_config = {"device": "CPU"}

model_config = {
    "model_name": f"{MODEL_NAME}",
    "model": fp32_path,
    "weights": fp32_path.replace(".xml", ".bin"),
}

配置好之后,通过以下代码加载模型和创建pipeline

from openvino.tools.pot.engines.ie_engine import IEEngine
from openvino.tools.pot.graph import load_model
from openvino.tools.pot.pipeline.initializer import create_pipeline

#  Load model as POT model representation
pot_model = load_model(model_config)

#  Initialize the engine for metric calculation and statistics collection.
engine = IEEngine(config=engine_config, data_loader=pot_data_loader)

# Step 5: Create a pipeline of compression algorithms.
pipeline = create_pipeline(algorithms_config, engine)

1.3 运行

通过调用pipeline.run(pot_model)来开始POT量化,可以通过save_model来保存,optionally,可以通过compress_model_weights来对模型的权重进行压缩。这里不是很理解,我还以为int8之后模型的大小会自动变小呢?

from openvino.tools.pot.graph.model_utils import compress_model_weights
from openvino.tools.pot.graph import load_model, save_model

compressed_model = pipeline.run(pot_model)
compress_model_weights(compressed_model)
optimized_save_dir = Path(f"{MODEL_PATH}/POT_INT8_openvino_model/")
save_model(compressed_model, optimized_save_dir, model_config["model_name"] + "_int8")
pot_int8_path = f"{optimized_save_dir}/{MODEL_NAME}_int8.xml"

2. PTQ with NNCF

2.1 准备数据

nncf不需要再实现一个Dataloader类,它只需要将yolov5中的val_dataloader 封装进nncf.Dataset中即可。此外,可以将data预处理的逻辑写进去,以使数据满足model的输入要求,上面的POT也实现了该功能。

import nncf

# Define the transformation method. This method should take a data item returned
# per iteration through the `data_source` object and transform it into the model's
# expected input that can be used for the model inference.
def transform_fn(data_item):
    # unpack input images tensor
    images = data_item[0]
    # convert input tensor into float format
    images = images.float()
    # scale input
    images = images / 255
    # convert torch tensor to numpy array
    images = images.cpu().detach().numpy()
    return images

# Wrap framework-specific data source into the `nncf.Dataset` object.
nncf_calibration_dataset = nncf.Dataset(data_source, transform_fn)

2.2 参数配置

nncf有多种量化方式。nncf.quantize用来进行 DefaultQuantization Algorithm, nncf.quantize_with_accuracy_control用来进行 AccuracyAwareQuantization. 他也有一些参数诸如preset , model_type, subset_size, fast_bias_correction等,当前我只知道subset_size代表数据集的数量,其它还不是很明确。本文用到的配置如下

subset_size = 300
preset = nncf.QuantizationPreset.MIXED

2.3 运行

通过调用nncf.quantize来进行nncf量化,通过调用oepnvino.runtime.serialize保存模型。

from openvino.runtime import Core

core = Core()
ov_model = core.read_model(fp32_path)
quantized_model = nncf.quantize(
    ov_model, nncf_calibration_dataset, preset=preset, subset_size=subset_size
)
nncf_int8_path = f"{MODEL_PATH}/NNCF_INT8_openvino_model/{MODEL_NAME}_int8.xml"
serialize(quantized_model, nncf_int8_path)

3. 实验结果

这是在我本地测试一张1600*1600的c++推理结果,分为模型加载(init)和推理(infer)两部分,在效果没有明显差别的情况下,POT和nncf性能都提升了4倍左右。
在这里插入图片描述

4. 完整代码

下面是我将jupyter中的代码移植到本地yolov5中的完整代码。

import torch
import nncf
import sys
from openvino.tools import mo
from openvino.runtime import serialize
from pathlib import Path

sys.path.append('.')
sys.path.append('..')
from utils.dataloaders import create_dataloader
from utils.general import check_dataset
from openvino.tools.pot.api import DataLoader
from openvino.tools.pot.engines.ie_engine import IEEngine
from openvino.tools.pot.graph import load_model
from openvino.tools.pot.pipeline.initializer import create_pipeline
from openvino.tools.pot.graph.model_utils import compress_model_weights
from openvino.tools.pot.graph import save_model
from openvino.runtime import Core

from export import attempt_load, yaml_save
from val import run as validation_fn


IMAGE_SIZE = 1600
ONNX_PATH = './weights/best.onnx'
MODEL_NAME = "yolov5l"
MODEL_PATH = "./weights"
DATASET_CONFIG = "mydata/0612.yaml"

fp32_path = f"{MODEL_PATH}/FP32_openvino_model/{MODEL_NAME}_fp32.xml"
fp16_path = f"{MODEL_PATH}/FP16_openvino_model/{MODEL_NAME}_fp16.xml"


class YOLOv5POTDataLoader(DataLoader):
    """Inherit from DataLoader function and implement for YOLOv5."""
    def __init__(self, data_source):
        super().__init__({})
        self._data_loader = data_source
        self._data_iter   = iter(self._data_loader)

    def __len__(self):
        return len(self._data_loader.dataset)
    
    def __getitem__(self, index):
        try:
            batch_data = next(self._data_iter)
        except StopIteration:
            self._data_iter = iter(self._data_loader)
            batch_data = next(self._data_iter)
        
        im, target, path, shape = batch_data
        im = im.float()
        im /= 255
        nb, _, height, width = im.shape
        img = im.cpu().detach().numpy()
        target = target.cpu().detach().numpy()

        annotation = dict()
        annotation["image_path"] = path
        annotation["target"] = target
        annotation["batch_size"] = nb
        annotation["shape"] = shape
        annotation["width"] = width
        annotation["height"] = height
        annotation["img"] = img

        return (index, annotation), img
        
def onnx2mo(onnx_path):

    # fp32 IR model
    fp32_path = f"{MODEL_PATH}/FP32_openvino_model/{MODEL_NAME}_fp32.xml"
    print(f"Export ONNX to OpenVINO FP32 IR to: {fp32_path}")
    model = mo.convert_model(onnx_path)
    serialize(model, fp32_path)

    # fp16 IR model
    fp16_path = f"{MODEL_PATH}/FP16_openvino_model/{MODEL_NAME}_fp16.xml"
    print(f"Export ONNX to OpenVINO FP16 IR to: {fp16_path}")
    model = mo.convert_model(onnx_path, compress_to_fp16=True)
    serialize(model, fp16_path)


def create_data_source():
    data = check_dataset(DATASET_CONFIG)
    val_dataloader = create_dataloader(data['train'], imgsz=IMAGE_SIZE, batch_size=1, stride=32, pad=0.5, workers=1)[0]
    return val_dataloader


# create nncf dataset
def transform_fn(data_item):
    # unpack input images tensor
    images = data_item[0]
    # convert input tensor into float format
    images = images.float()
    # scale input
    images = images / 255
    # convert torch tensor to numpy array
    images = images.cpu().detach().numpy()
    return images

# prepare config and pipeline for pot
algorithms_config = [
    {
        "name": "DefaultQuantization",
        "params": {
            "preset": "mixed",
            "stat_subset_size": 300,
            "target_device": "CPU"
        },
    }
]

engine_config = {"device": "CPU"}

model_config = {
    "model_name": f"{MODEL_NAME}",
    "model": fp32_path,
    "weights": fp32_path.replace(".xml", ".bin"),
}



subset_size = 80
preset = nncf.QuantizationPreset.MIXED


# quantiaztion with pot
def quant_pot():
    compressed_model = pipeline.run(pot_model)
    compress_model_weights(compressed_model)
    optimized_save_dir = Path(f"{MODEL_PATH}/POT_INT8_openvino_model/")
    save_model(compressed_model, optimized_save_dir, model_config["model_name"] + "_int8")
    pot_int8_path = f"{optimized_save_dir}/{MODEL_NAME}_int8.xml"

# quantization with nncf
def quant_nncf():
    core = Core()
    ov_model = core.read_model(fp32_path)
    quantized_model = nncf.quantize(
        ov_model, nncf_calibration_dataset, preset=preset, subset_size=subset_size
    )
    nncf_int8_path = f"{MODEL_PATH}/NNCF_INT8_openvino_model/{MODEL_NAME}_int8.xml"
    serialize(quantized_model, nncf_int8_path)

if __name__ == '__main__':
    onnx2mo(ONNX_PATH)

    # create yolov5 dataloader class for pot
    data_source = create_data_source()
    pot_data_loader = YOLOv5POTDataLoader(data_source)
    print('create yolov5 pot data done')

    nncf_calibration_dataset = nncf.Dataset(data_source, transform_func=transform_fn)
    print('wrap data source into nncf.dataset object. ')

    #  Load model as POT model representation
    pot_model = load_model(model_config)

    #  Initialize the engine for metric calculation and statistics collection.
    engine = IEEngine(config=engine_config, data_loader=pot_data_loader)

    # Step 5: Create a pipeline of compression algorithms.
    pipeline = create_pipeline(algorithms_config, engine)

    quant_pot()
    # print('pot quant done.')
    quant_nncf()
    print('nncf quant done.')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/721427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Flutter】Flutter 使用 Stream Transform 包处理流操作

文章目录 一、 前言二、 Stream Transform 包简介三、 安装和版本信息四、 Stream Transform 的基本使用1. 扩展方法2. 异步映射 五、 示例:使用 Stream Transform 实现实时搜索功能六、 总结 一、 前言 欢迎来到我的博客!我是小雨青年,这是…

基于docker的ubuntu云服务器jupyter深度学习环境配置指南

step1 安装docker 文档中的命令如下: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings…

驱动 控制开发版3盏灯、蜂鸣器、风扇、马达

head.h #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; //查看开发扩展板原理图可知 //蜂鸣器 PB6 //风扇 PE9 //马达 PF6 //LED1 PE…

商用车线控底盘需求文档

一、 概述 商用车线控底盘主要用于接收智能驾驶域控制器(控制器ADU)的请求指令,完成ADU 对驱动、制动、档位转向、驻车声光等部分的指令控制,从而实现智能驾驶功。 即 ADU 可通过 CAN 通讯的方式以特定周期和指令控制 一台车的线控…

驱动开发作业2 —— ioctl

通过ioctl函数选择不同硬件的控制,如实现对LED、蜂鸣器、马达、风扇的控制 1.将GPIO的相关寄存器封装成结构体 --------> gpio.h 2.LED相关驱动文件 --------> led.c 3.蜂鸣器相关驱动文件 --------> beep.c 4.风扇相关驱动文件 --------> fan.c 5.马…

Ubuntu/Debian等Linux系统安装微信客户端

【写在前面】 由于本人的工作环境基本是在ubuntu下,而ubuntu使用网页版微信常常会出现无法登陆的现象,为了能够在linux系统用上微信,于是在网上找了找办法,没想到还真有大神做了,特此分享出来。 【安装步骤】 其实只…

UNIX网络编程卷一 学习笔记 第二十二章 高级UDP套接字编程

TCP是一个字节流协议,又使用滑动窗口,因此没有记录边界或发送者数据发送能力超过接收者接受能力之类的事情,但对于UDP,每个输入操作对应一个UDP数据报(一个记录),因此当收取的数据报大于引用的输…

软件测试--Fiddler的使用(持续更新)

1.工具界面介绍 2.抓取请求 打开Fiddler,随便访问一些网址,左边便会抓取到很多请求 3.删除请求(Remove all或者输入命令) 4.过滤请求 ps: 5.打开抓HTTPS设置 6.界面熟悉 7.抓包图标说明

MPC vs Multi-sig——误解及重点关注

1. 引言 资金托管的2大主流方案为: MPC:MPC钱包——对应EOA账号。用于高信任企业场景。Multi-sig:多签钱包——对应智能合约钱包。用于个人场景,可强化安全性并易于恢复。 不过V神认为,基于MPC的EOA账号存在根本性缺…

不变的誓言 字符串常量

## 不变的誓言 字符串常量字符串常量,这节课的主题、 水帘洞一直都没有变1.什么是字符串呢? 就是一个一个字符连起来就是字符串,qq聊天 都是字符串。 字符串常量,“”1.什么是字符串呢? 就是一个一个字符连起来就是字…

sqlserver收缩数据库

1.收缩数据库 首先收缩的前提是需要有可用空间如下图,没有可用空间无法收缩数据库 2.减小数据库大小 通过链接: 查询数据库中各表的大小 如果查询的比较大而且无用的数据可以直接把表结构给拿出来,然后删除该表空间就直接释放出来了 3.收缩文件 我…

SpringBoot项目打包部署后页面无法访问问题定位处理记录

问题描述 项目在idea中可以正常访问,但是达成jar包后访问时候访问异常,并报错,部分异常栈如下: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/views/login], template might not exist or might not be a…

插入排序(直接插入排序 折半插入排序)

直接插入排序 void InsertSort(ElemType A[],int n) {int i,j;for(i2;i<n;i)if(A[i]<A[i-1]){A[0]A[i];for(ji-1;A[0]<A[j];--j)A[j1]A[j];A[j1]A[0];} }折半插入排序 void BinInsertSort(int A[],int n) {int i,j,low,high,mid;for(i2;i<n;i){A[0]A[i];low1;high…

【MySQL】基本查询之表的增删改查

【MySQL】表的增删改查 一、插入操作----insert1.1 简单插入1.2 插入时是否更新----ON DUPLICATE KEY UPDATE1.3 插入时替换----REPLACE 二、查询----select2.1 简单查询与去重2.2 基本查询----where条件2.2.3 案列演示 2.4 排序----order by 三、修改操作----update四、删除--…

产品经理有效管理项目进度的5个关键方法

作为产品经理&#xff0c;管理项目进度是确保产品开发成功的关键组成部分。产品经理负责推动产品开发过程&#xff0c;并确保团队在最后期限前完成并保持在正轨上。以下是产品经理有效管理项目进度的一些关键方法。 1、创建详细的项目进度表 这个时间表应该包括开发过程的所有任…

java业务开发经典常见错误例子

java业务开发经典常见错误例子 文章目录 java业务开发经典常见错误例子1.ThreadLocal线程重用导致用户信息错乱的 Bug2.使用了线程安全的并发工具&#xff0c;并不代表解决了所有线程安全问题3.没有认清并发工具的使用场景&#xff0c;导致性能问题4.加锁前要清楚锁和被保护的对…

DM8:达梦数据库数据文件与日志文件介绍

DM8:达梦数据库数据文件与日志文件介绍 环境介绍1 表空间中的数据文件1.1 表空间1.2 数据文件1.3 系统自带的表空间1.3.1 SYSTEM 表空间1.3.2 ROLL 表空间1.3.3 TEMP 表空间1.3.4 MAIN 表空间1.3.5 用户自定义表空间1.3.6 DM数据库的表空间和数据文件分布 2 日志文件2.1 重做日…

Redis 数据库的概念、常用命令

Redis数据库 一、关系数据库与非关系型数据库概述1、关系型数据库2、非关系型数据库3、关系数据库与非关系型数据库区别&#xff08;1&#xff09;数据存储方式不同&#xff08;2&#xff09;扩展方式不同&#xff08;3&#xff09;对事务性的支持不同 4、非关系型数据库产生背…

【数据结构课程设计系列】完全二叉树操作演示

完全二叉数操作演示 1.完全二叉树操作演示要求&#xff1a; &#xff08;1&#xff09;创建完全二叉树(用顺序方式存储) &#xff08;2&#xff09;求二叉树的深度和叶子结点数 &#xff08;3&#xff09;实现二叉树的前序、中序、后序和层次遍历。 &#xff08;4&#xff09;…

基于ubuntu的驱动开发

一般的linux驱动开发都是基于交叉编译来进行的&#xff0c;本文尝试着从另一个角度&#xff1a;基于ubuntu的本地驱动开发来学习一下驱动的开发 一、驱动的开发与编译 1.1、编写驱动文件 #include <linux/init.h> #include <linux/module.h> static int hello_i…