史上最快的实例分割SparseInst Int8量化实录

2023-11-04 09:10

本文主要是介绍史上最快的实例分割SparseInst Int8量化实录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

近期,YOLOv7里面借鉴(复 制 粘 贴)了一个新的模型,SparseInst,我借助YOLOv7的基建能力,将其导出到了ONNX, 获得了一个非常不错的可以直接用OnnxRuntime, 或者TensorRT跑的实例分割 (后续也可能把link加到官方的repo当中)。索性就一不作二不休,把int8也给他加上。于是就有了这个踩坑记录博客。

本文将带你从0开始量化一个复杂网络,这个SparseInst也是基于Transformer的,网络结构够复杂。最终实现Int8的量化推理。

我们会cover的知识点包括:

  • 是如何将一个transformer导出到onnx的,中间会遇到哪些问题?
  • 如果重写后处理得到一个可用的onnx模型?
  • 如何对coco的模型实现普适化的量化(无需重复写乱七八糟的dataloader)
  • 如何评估量化模型与float32的误差?
  • 如何对量化后的模型进行推理,并通用的迁移到X86, ARM等架构,或者TensorRT, OpenVINO, NCNN等前推框架

先来看看SparseInst的精度。这或许是今年,速度最快,精度最高的实例分割模型:

可以看到,在mAP达到 37.9 的情况下,可以跑到 40 FPS. 

这是我在CPU下,用int8的跑到结果,请注意,是CPU, 大概是 30ms. 小目标检测的效果依旧非常不错。

这还是还没有用TensorRT,也没有用TensorRT的int8加速的情况下,如果TensorRT int8,可以达到4-6ms. 输入尺寸是640x640,不大也不小的图。TensorRT我将在下一篇继续测试。

这个模型,我认为如果你感兴趣的话,是可以在数据集上得到一个更高的精度的。这里面的attention machanism 是可以被改进的,潜力依旧很大。但毫无疑问,这是目前看来,第一个不是特别慢的实例分割的范式,也不是无脑的堆叠encoder layer寻求高精度的方法。能做到非常好的精度与推理速度的权衡。

那我们就来把他量化到int8吧。

0x1 导出onnx

首先导出到onnx , 这一步可以直接一不到 yolov7仓库,参考对应的导出流程。

https://github.com/jinfagang/yolov7

如果你遇到了什么问题,请尽情的在github提issue.

0x2 量化

这部分坑,实在是太深了,一言难尽。一入量化深似海。所以接下来这部分,我建议你慎重观摩,可能引起你的心理落差。

因为如果你不经过一些非常深的套路,结局可能是这样的:

不管那么多了,早晚是要quant的。那不如我们先来吧calibrator高一波:

import sys
from torchvision import transforms
import torchvision
import torch
from atomquant.onnx.dataloader import get_calib_dataloader_coco
import os
import cv2
import numpy as np
import onnxruntime as ort
from torchvision.datasets.coco import CocoDetection
from alfred.dl.torch.common import devicedef preprocess_func(img, target):w = 640h = 640a = cv2.resize(img, (w, h))a_t = np.array(a).astype(np.float32)boxes = []for t in target:boxes.append(t["bbox"])target = np.array(boxes)a_t = torch.as_tensor(a_t)target = torch.as_tensor(target)return a_t, targetdef collate_fn(batch):images, targets = zip(*batch)if isinstance(images[0], torch.Tensor):images = torch.stack(images)targets = torch.stack(targets)else:images = np.array(images)return imagesif __name__ == "__main__":ONNX_PATH = sys.argv[1]coco_root = os.path.expanduser("~/data/coco/images/val2017")anno_f = os.path.expanduser("~/data/coco/annotations/instances_val2017_val_val_train.json")# coco_ds = CocoDetection(coco_root, anno_f, )session = ort.InferenceSession(ONNX_PATH)input_name = session.get_inputs()[0].namecalib_dataloader = get_calib_dataloader_coco(coco_root,anno_f,preprocess_func=preprocess_func,input_names=input_name,bs=1,max_step=50,collate_fn=collate_fn)

这个地方我比较建议你使用pqq来量化。上面的atomquant是我的一个还未开源的包,由于太菜以至于不敢开源。但是我就暂且用这个里面提供的一些calibrator构造函数来丢一波coco的数据来量化。

接下来丢进来我们的onnx模型,就可以开始量化了:

REQUIRE_ANALYSE = False
BATCHSIZE = 1
# INPUT_SHAPE = [3, 224, 224]
INPUT_SHAPE = [640, 640, 3]
DEVICE = "cuda"  # only cuda is fully tested :(  For other executing device there might be bugs.
# PLATFORM = TargetPlatform.PPL_CUDA_INT8  # identify a target platform for your network.
PLATFORM = (
TargetPlatform.ORT_OOS_INT8
# TargetPlatform.PPL_CUDA_INT8
)  # identify a target platform for your network.
# PLATFORM = TargetPlatform.ONNXRUNTIME  # identify a target platform for your network.EXECUTING_DEVICE = "cpu"  # 'cuda' or 'cpu'.# create a setting for quantizing your network with PPL CUDA.
# quant_setting = QuantizationSettingFactory.pplcuda_setting()
quant_setting = QuantizationSettingFactory.default_setting()
quant_setting.equalization = True  # use layerwise equalization algorithm.
quant_setting.dispatcher = (
"conservative"  # dispatch this network in conservertive way.
)# quantize your model.
quantized = quantize_onnx_model(
onnx_import_file=ONNX_PATH,
calib_dataloader=calib_dataloader.dataloader_holder,
calib_steps=88,
input_shape=[BATCHSIZE] + INPUT_SHAPE,
setting=quant_setting,
# collate_fn=collate_fn,
platform=PLATFORM,
device=DEVICE,
verbose=0,
)

记得import你需要的东西,这里默认我们进行x86的计算模拟,因为我们想跑在CPU上,至于GPU,那是下一篇的事情。

然后我们就可以得到一个quantize的模型:

在这里面,我们展示的是一个实例分割模型,这里面包含了非常多的复杂操作,例如各种shape的组合,以及各种concat,各种interpolate, 其中很多算子是没有办法去量化的,至少很多前推引擎并不支持。

但是我们不管那么多,一顿梭哈,无脑梭哈。

然后我们就可以得到这么一个int8的模型:

模型体积从 140M -> 50M.

好了,接下来进入下一段。

0x3 量化模型推理

虽然我上面写起来很简单,这当中忽略了很多坑,多得数不过来,更别说记录了。如果你也遇到了坑,欢迎前往YOLOv7留言。

接着就是量化模型推理了,:

def load_test_image(f, h, w):a = cv2.imread(f)a = cv2.resize(a, (w, h))a_t = np.expand_dims(np.array(a).astype(np.float32), axis=0)return a_t, adef preprocess_image(img, h, w):a = cv2.resize(img, (w, h))a_t = np.expand_dims(np.array(a).astype(np.float32), axis=0)return a_t, imgif __name__ == "__main__":args = make_parser().parse_args()input_shape = tuple(map(int, args.input_shape.split(",")))session = onnxruntime.InferenceSession(args.model)iter = ImageSourceIter(args.image_path)while True:im = next(iter)if isinstance(im, str):im = cv2.imread(im)inp, ori_img = preprocess_image(im, h=input_shape[0], w=input_shape[1])ort_inputs = {session.get_inputs()[0].name: inp}output = session.run(None, ort_inputs)if "sparse" in args.model:masks, scores, labels = None, None, Nonefor o in output:if o.dtype == np.float32:scores = oif o.dtype == np.int32 or o.dtype == np.int64:labels = oif o.dtype == bool:masks = omasks = masks[0]print(masks.shape)if len(masks.shape) > 3:masks = np.squeeze(masks, axis=1)scores = scores[0]labels = labels[0]# keep = scores > 0.15keep = scores > (0.15 if args.int8 else 0.32)scores = scores[keep]labels = labels[keep]masks = masks[keep]print(scores)print(labels)print(masks.shape)img = vis_res_fast(im, None, masks, scores, labels)else:predictions = demo_postprocess(output[0], input_shape, p6=args.with_p6)[0]boxes = predictions[:, :4]scores = predictions[:, 4:5] * predictions[:, 5:]boxes_xyxy = np.ones_like(boxes)boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2] / 2.0boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2.0boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2.0boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2.0# boxes_xyxy /= ratiodets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.65, score_thr=0.1)final_boxes, final_scores, final_cls_inds = (dets[:, :4],dets[:, 4],dets[:, 5],)img = visualize_det_cv2_part(ori_img, final_scores, final_cls_inds, final_boxes)cv2.imshow("aa", img)cv2.waitKey(0)cv2.imshow("YOLOv7 SparseInst CPU int8", img)if iter.video_mode:if cv2.waitKey(1) & 0xFF == ord("q"):breakelse:cv2.waitKey(0)

这部分代码在YOLOv7 deploy当中。

最后我们传入命令:

python ort_infer.py -m ../weights/sparse_inst.onnx -i ../datasets/public/images --int8

就 可以看到int8的推理结果:

0x4 总结

本文实现了一个较为复杂的transformer的实例分割的ONNX导出,同时实现了精度一定范围内得到了保证的int8量化。但其实这还只是一个粗狂的尝试,未来我们会进一步的精细化int8的量化误差、让量化误差进一步可控。

下一篇预告:使用ncnn前端推理量化模型。让int8在CPU下跑得更快。

同时,也会吧量化的结果,迁移到TensorRT,用int8来跑tensorrt.

如果你对量化感兴趣,可以扫码加入我们的 “高端” 量化交流群,群内大佬云集。

本文所有代码,将会在下面链接公布:

https://manaai.cn/

这篇关于史上最快的实例分割SparseInst Int8量化实录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/344728

相关文章

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

使用Python将长图片分割为若干张小图片

《使用Python将长图片分割为若干张小图片》这篇文章主要为大家详细介绍了如何使用Python将长图片分割为若干张小图片,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果1. Python需求

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数