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

2023-11-16

近期,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 device


def preprocess_func(img, target):
    w = 640
    h = 640
    a = 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, target


def 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 images


if __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].name

    calib_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, a


def 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, img


if __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, None
            for o in output:
                if o.dtype == np.float32:
                    scores = o
                if o.dtype == np.int32 or o.dtype == np.int64:
                    labels = o
                if o.dtype == bool:
                    masks = o
            masks = 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.15
            keep = 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.0
            boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2.0
            boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2.0
            boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2.0
            # boxes_xyxy /= ratio
            dets = 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"):
                break
        else:
            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/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

史上最快的实例分割SparseInst Int8量化实录 的相关文章

  • Kali无法打开Firefox浏览器

    本文章鉴于我本人的学习过程 起初我是能正常打开Firefox浏览器的 在我提升了root权限后浏览器就打不开了 于是我看了几篇其他人解决的办法 解决办法如下 用chown改变权限 输入 chown R root 目前我所遇到的就是这种情况
  • 计算机网络安全的背景

    虽然传统的计算机发展和当今的电子商务不同 但是不可否认网络已经成 为非常重要的信息和数据互换交换的平台 但是随着网络不断发展渗透到人们的日 常生活 手机终端 交易支付等环节时 网络安全已经成为一个焦点和不可逾越的 发展鸿沟 尽管目前网上支付
  • json 插入数据_使用python将数据存入SQLite3数据库

    作者 JiekeXu 2017年毕业于某本科院校 从事于数据库运维行业 一个热爱Python的DBA 个人公众号 JiekeXu之路 Python从网站上抓取的数据为了可以重复利用 一般都会存储下来 存储方式最简单的会选择存储到文本文件 常
  • 微信中的video属性设置

  • python数据驱动测试设计_Python Selenium 之数据驱动测试的实现

    数据驱动模式的测试好处相比普通模式的测试就显而易见了吧 使用数据驱动的模式 可以根据业务分解测试数据 只需定义变量 使用外部或者自定义的数据使其参数化 从而避免了使用之前测试脚本中固定的数据 可以将测试脚本与测试数据分离 使得测试脚本在不同
  • 【 视频 】NTSC和PAL电视制式

    今天的电视机还沿用着50年代彩色电视发明时的标准 它们就是NTSC 国家电视制式委员会 和PAL 逐行倒相 NTSC多用于美国和日本 二战 PAL多用于欧洲 澳大利亚 中东和亚洲地区 本文将介绍NTSC和PAL的主要概念 这些知识对更现代的
  • 苹果Mac电脑文件夹路径怎么看?“访达”也能显示文件路径

    Windows系统中 我的电脑 或 资源管理器 会显示文件 文件夹路径 Mac在 访达 中却不显示 确实不便 也让人费解 连 三指拖移 都是默认不开启 Mac有这类奇怪逻辑就一点也不奇怪了 是的 这是苹果最不可理喻之处 老子就是要到处嚷嚷
  • 力扣刷题(day0011)两个数组的交集

    给定两个数组 nums1 和 nums2 返回 它们的交集 输出结果中的每个元素一定是 唯一 的 我们可以 不考虑输出结果的顺序 示例 1 输入 nums1 1 2 2 1 nums2 2 2 输出 2 示例 2 输入 nums1 4 9
  • python中的连续比较是什么_Python算法的分治算法,python,之,连续,子,列表,最大,和...

    连续子列表的最大和 在一个列表中找到连续子列表的最大和 列表中的数字可负可正 并且子列表不能为空 问题提出 找到以下列表的最大子列表的和 2 1 3 4 1 2 1 5 4 解题思路 最大子列表有可能在左子列表 右子列表与右子列表之间 我们
  • python小数格式:不用科学计数法、不截断

    对于一个小数 如 0 0000000000001 想得到相应的字符串 而不是转换为 1e 12 可以用 numpy 的 format float positional import numpy as np a 0 0001 b 0 0000
  • anaconda安装所有库代码集总

    1 安装jupyter conda install jupyter

随机推荐

  • 接口请求合并的3种技巧,性能直接爆表!

    将相似或重复请求在上游系统中合并后发往下游系统 可以大大降低下游系统的负载 提升系统整体吞吐率 文章介绍了 hystrix collapser ConcurrentHashMultiset 自实现BatchCollapser 三种请求合并技
  • 5.3.6 复合主键@IdClass

    有时一个实体的主键可能同时为多个 例如同样是之前使用的 CustomerEO 实体 需要通过name和email来查找指定实体 当且仅当name和email的值完全相同时 才认为是相同的实体对象 要配置这样的复合主键 步骤如以下所示 1 编
  • 快速排序(Java实现) 单边循环、多边循环

    package com xiaoxin sort import java util Arrays public class quickSort me public static void main String args int arr 9
  • 关于CSS及JS的使用技巧浅谈

    使用CSS预处理器 例如Sass和Less 可以帮助开发者更快速 更有效地编写CSS代码 模块化 将CSS和JS代码分解为模块 可以帮助开发者更好地组织和管理代码 压缩和合并 使用压缩工具可以帮助减少CSS和JS文件的大小 使网站更容易加载
  • minikube单机安装nfs服务

    1 安装 nfs server sudo apt get update sudo apt get install y nfs kernel server 2 创建目录 配置 IP 共享目录绑定 vim etc exports 新增 data
  • Shiro权限框架-实现分布式会话SessionManager(7)

    1 会话的问题 2 分布式会话实现思路 1 原理分析 所有服务器的session信息都存储到了同一个Redis集群中 即所有的服务都将 Session 的信息存储到 Redis 集群中 无论是对 Session 的注销 更新都会同步到集群中
  • vue+elementUI图片预览,<el-image> 的使用

    vue elementUI图片预览 el image 的使用 本文转载自 https www cnblogs com allanlau p 13397625 html 首页定义data data return imgs imgsVisibl
  • 手把手教你使用transciver-ip核的配置

    目前很多行业都会用到transceiver 甚至像pcie srio等高速接口都调用了transceiver 所以了解并学会其使用方法还是很重要的 本文结合作者的使用经验 让你快速的了解并上手使用 Xilinx提供了Transceivers
  • (Scikit-Learn)朴素贝叶斯使用方法:高斯朴素贝叶斯 多项式朴素贝叶斯(文本分类)

    在贝叶斯分类中 我们希望确定一个具有某些特征的样本属于某类标签的概率 通常记为 P L 特征 贝叶斯定理告诉我们 可以直接用下面的公式计算这个概率 假如需要确定两种标签 定义为 L1 和 L2 一种方法就是计算这两个标签的后验概率的比值 其
  • Python自学之路第九步——用户输入和while循环

    主要用到的是input 函数 他可以接受用户的输入 这样便可以编写交互式的程序了 还介绍了while循环 这个和C中一样 包括if判断都可以尝试测试一下 很有意思 函数input pr 我们将统计您的基本信息 pr n请输入您的名字 nam
  • Mybatis - NoSuchMethodError: net.sf.jsqlparser.statement.select.SetOperationList.getSelects()Ljava/

    昨天在修改一个接口功能时 需要在原来的接口上提供分页和模糊查询 就使用了分页 PageHelper来做 但是在mybatis的xml文件中又使用了UNION来合并查询结果 导致项目启动直接报错 Handler processing fail
  • MySQL 中读写分离可能遇到的问题

    前言 MySQL 中读写分离是经常用到了的架构了 通过读写分离实现横向扩展的能力 写入和更新操作在源服务器上进行 从服务器中进行数据的读取操作 通过增大从服务器的个数 能够极大的增强数据库的读取能力 MySQL 中的高可用架构越已经呈现出越
  • ubuntu 光盘读取

    把光盘放入光驱后 要挂载光驱 将光驱设备挂在到 mnt 下 sudo mount dev sr0 mnt mount dev sr0 is write protected mounting read only 到 mnt目录下就可以看到光盘
  • 一条SQL语句求前面记录的平均值

    有算法要求如下 For i 1 i lt 10 i ta i t 1 t 2 t i i 用一条SQL语句实现它 分别用表变量 ta 和 t 来对应 ta 和 t declare t table id int d decimal 18 4
  • 第一课 认识Python

    相比大家都听说过Python是一门容易学习的语言 那么实际是怎么样呢 我们从以下几点看看 1 首先 Python是最容易学习和最好用的语言 Python容易阅读和编写 比较清晰 格式非常简洁 表达能力强 这样同样写一个程序 Python比其
  • uniApp条件编译以及跳转方法

    1 使用Uniapp的方法获取系统环境 仅在JS中可以使用 uni getSystemInfoSync platform 获取应用所在的平台 if uni getSystemInfoSync platform ios if uni getS
  • pickle与.pkl文件

    经常遇到在Python程序运行中得到了一些字符串 列表 字典等数据 想要保存下来 长长久久的 方便以后使用 这个时候Pickle模块就派上用场了 pickle 模块及其同类模块 cPickle 向 Python 提供了 pickle 支持
  • mipi介绍

    文章目录 1 MIPI简介 1 1 DSI layer 2 D PHY 2 1 D PHY介绍 2 2 电平状态 2 3 lane结构 2 4 data lane操作模式 2 4 1 escape mode和space one hot co
  • kylin启动netstat: n: unknown or uninstrumented protocol

    检查hadoop配置的时候出现问题 报错如下 lcc lcc apache kylin 2 6 0 hbase1x bin check env sh Retrieving hadoop conf dir KYLIN HOME is set
  • 史上最快的实例分割SparseInst Int8量化实录

    近期 YOLOv7里面借鉴 复 制 粘 贴 了一个新的模型 SparseInst 我借助YOLOv7的基建能力 将其导出到了ONNX 获得了一个非常不错的可以直接用OnnxRuntime 或者TensorRT跑的实例分割 后续也可能把lin