PTQ 精度 Debug 工具

01 前言

使用 PTQ 后量化的模型量化方案,可以帮助用户非常简单便捷地完成从浮点模型到地平线混合异构模型的转换,模型转换工具会基于用户提供的校准样本对模型进行校准量化并保障模型高效地部署在地平线计算平台上。

但是在模型转换的过程中,不可避免地会因为浮点高精度到定点低精度的量化过程而引入精度损失,因此为了帮助用户准确快速地定位模型精度损失的主要原因,地平线工具链提供了一套 PTQ 精度 Debug 工具。

本文将基于 征程5 工具链 1.1.62 版本的 OpenExplorer 开发包来详细介绍这些工具的使用方法和使用流程,以及对输出结果进行解读,来帮助用户快速上手。

对于 XJ3 工具链,PTQ 精度 Debug 工具的使用方法一致,但是暂不支持命令行配置和 runall 功能。

在本文开始之前,需要用户参考文章图片校准数据准备问题介绍与处理和模型精度验证及调优建议,掌握正确处理校准数据以及评测模型精度的方法,并且排除因输入数据预处理不当和欠佳的推理结果后处理等非模型自身造成的精度问题。

02 基础介绍

在 PTQ 模型后量化过程中,通常情况下造成精度损失的主要原因可能有以下几点:

  1. 敏感节点量化问题。模型中的一部分节点对量化比较敏感会引入较大误差;

  2. 节点量化误差累积问题。模型中各个节点的量化误差累积导致模型整体出现较大的校准误差,主要包含:权重量化导致的误差累积、激活量化导致的误差累积以及全量量化导致的误差累积。

针对上述情况,地平线工具链提供的精度 Debug 工具将协助用户自主定位模型量化过程中产生的精度问题,对校准模型进行节点粒度的量化误差分析并快速定位出现精度异常的节点。

PTQ 精度 Debug 工具提供的功能包括:

  • 获取节点量化敏感度;
  • 获取模型累积误差曲线;
  • 获取指定节点的数据分布;
  • 获取指定节点输入数据通道间数据分布箱线图等。

2.1 基本流程

使用精度 Debug 工具进行精度 Debug 的基本流程为:

1.校准模型和校准数据的准备。校准模型在模型转换过程中为常态化保存,无需用户额外操作,为模型转换的输出目录model_output下的calibrated_model;校准数据的保存需要用户在 Yaml 文件中的模型参数组中增加如下参数配置(配置后精度 Debug 功能同时开启,但需要用户重新编译一次模型,编译等级可以配置为 O0 以减少编译时间):

model_parameters:debug_mode: "dump_calibration_data"

2.通过import horizon_nn.debug as dbg导入 Debug 模块(使用命令行则无需导入),加载校准模型和校准数据;

3.通过精度 Debug 工具提供的 API 或者命令行,对精度损失明显的模型进行分析。(详细的流程以及分析见后文。)

2.2 校准数据

校准数据(calibration_data)在模型转换的校准阶段使用,模型通过对这些数据进行前向推理来获取每个被量化节点的量化参数,量化参数包括:缩放因子(Scale)和阈值(Threshold)。

值得注意的是,此处保存的校准数据是经过颜色空间转换以及预处理之后的 npy 格式的数据,该数据可以通过np.load()直接送入校准模型进行推理,不同于在模型转换前数据准备阶段使用02_preprocess.sh脚本生成的校准数据。

使用脚本生成的校准数据是 bgr 颜色空间的数据,在工具链内部会将数据从 bgr 转换到 yuv444/gray 等模型实际输入的格式。

按照上述精度 Debug 流程中提到的操作保存校准数据后,自动在model_output目录下生成的calibration_data文件夹结构如下所示:

|-- calibration_data  #校准数据|---- input1  #文件夹名为模型的输入节点并保存对应的输入数据|-------- 0.npy|-------- 1.npy|-------- ...|---- input2  #对于多输入模型将保存多个文件夹|-------- 0.npy|-------- 1.npy|-------- ...|---- ...

2.3 校准模型

校准模型(calibrated_model.onnx)是将在校准阶段计算得到的每个被量化节点的量化参数保存在校准节点中,从而得到的模型。

模型转换过程中,模型转换工具将浮点模型进行结构优化后,通过校准数据计算得到每个节点对应的量化参数并将其保存在校准节点中,形成了校准模型。

因此校准模型是一种中间产物,主要特点是模型中包含校准节点,校准节点的节点类型为 HzCalibration。

校准节点主要分为两类:

  1. 激活(activation)校准节点。激活校准节点的输入是当前节点的上一个节点的输出,并基于当前激活校准节点中保存的量化参数对输入数据进行量化和反量化后输出;

  2. 权重(weight)校准节点。权重校准节点的输入是模型的原始浮点权重,并基于当前权重校准节点中保存的量化参数对输入的原始浮点权重进行量化和反量化后输出。

除了上述的校准节点,校准模型中的其他节点称为普通节点(node),普通节点的类型包括:Conv、Mul 和 Add 等。

下图直观地展示了校准节点和普通节点在校准模型中扮演的角色:

03 一键运行

为了方便用户快速获取 PTQ Debug 工具的结果,这里推荐使用一键运行功能,可以一键运行 PTQ Debug 工具中的全部功能。每个功能的详细解读可以参考第三部分功能详解。

根据经验,校准数据的数量对 PTQ Debug 的结果不会有太大影响,为了加速工具运行,建议用户使用一份校准数据即可,配置详见参数介绍。

此外 PTQ 精度 Debug 工具可以使用 GPU 进行计算加速,用户需要进入 GPU Docker 中先卸载预安装的 CPU 版本horizon-nn,再安装对应版本的horizon-nn-gpu,安装所需的 wheel 文件在路径ddk/package/host/ai_toolchain下,这样可以保证完整覆盖。

注意:因涉及 ONNXRuntime 版本问题,使用 GPU 加速计算仅作为尝试,如遇报错,建议回退 CPU 进行计算。

3.1 使用方法

1. API 使用

# 导入debug模块
import horizon_nn.debug as dbgdbg.runall(model_or_file='./calibrated_model.onnx',calibrated_data='./calibration_data',)

2. 命令行使用

# 例如
hmct-debugger runall calibrated_model.onnx calibration_data

可通过hmct-debugger runall -h/--help查看相关参数。

3. 参数介绍

详细的 API 和命令行参数说明以及配置介绍,阅读原文后至社区搜索《 J5 算法工具链—PTQ Debug 工具使用》。

4. 执行顺序

PTQ Debug 工具各个功能的运行顺序如下:

  1. 分别获取权重校准节点和激活校准节点的量化敏感度;
  2. 根据 step1 的结果,分别取权重校准节点的 top5 和激活校准节点的 top5 绘制其数据分布;
  3. 针对 step2 获取的节点,分别绘制其通道间数据分布的箱线图;
  4. 绘制分别只量化权重和只量化激活的累积误差曲线。

注:当指定node_type='node'时,工具会获取 top5 节点,并分别找到每个节点对应的校准节点,并获取其校准节点的数据分布和箱线图。

3.2 Debug 分析

PTQ Debug 分析的流程如下所示:

  1. 首先根据累积误差曲线判断模型量化掉点是由激活量化导致的还是由权重量化导致的,确定是激活量化问题还是权重量化问题。如果激活和权重的单独量化误差不明显,可以判断是两者共同作用导致的。
  2. 对于激活量化问题,根据激活节点的量化敏感度排序确定敏感节点;对于权重量化问题,根据权重节点的量化敏感度排序确定敏感节点;对于激活和权重量化问题,计算普通节点的量化敏感度并排序,确定敏感节点。
  3. 获取量化敏感节点后,进行部分节点以较高精度量化或部分节点不量化测试。在 J5 工具链上,可以对敏感节点设置 Int16 量化,Int16 量化调优工具的使用参考社区文章 PTQ 精度调优手段—设置 Int16 量化,同时也可以让敏感节点run_on_cpu。XJ3 工具链暂不支持用户手动设置节点以 Int16 精度量化,可以让敏感节点run_on_cpu
  4. 同时还可以根据敏感节点数据分布直方图以及通道间数据分布的箱线图来明确优化方向,例如激活敏感节点数据分布不均匀则可以通过优化模型结构来改善,权重敏感节点数据分布不均匀则建议使用 QAT 等。

04 功能详解

接下来将针对 PTQ 精度 Debug 工具提供的功能和对应的 API 或命令行使用方法,逐一进行详解。

4.1 获取累积误差曲线

通过只量化浮点模型中的某一个节点,并依次计算该模型中每个节点与浮点模型中节点输出的误差,获得累积误差曲线。

API 使用方法:

# 导入debug模块
import horizon_nn.debug as dbgdbg.plot_acc_error(save_dir='./',calibrated_data='./calibration_data/',model_or_file='./calibrated_model.onnx',quantize_node=['weight', 'activation'],metric='cosine-similarity',average_mode=False)

命令行使用方法:

# 例如
hmct-debugger plot-acc-error calibrated_model.onnx calibration_data -q ['weight', 'activation']

可通过hmct-debugger plot-acc-error -h/--help查看相关参数。

参数介绍:

详细的 API 和命令行参数说明以及配置介绍,阅读原文后到社区搜索《 J5 算法工具链—PTQ Debug 工具使用》。

配置方式与 Debug 结果分析:

  • 指定单节点量化/不量化:

    如下配置意为分别只量化Conv_2Conv_90并保持其他节点不量化,计算累积误差曲线;或分别解除量化Conv_2Conv_90并保持其他节点量化,计算累积误差曲线。

    API 配置方式为:

    quantize_node=['Conv_2', 'Conv_90']/non_quantize_node=['Conv_2', 'Conv_90']

    命令行配置方式为:

    -q ['Conv_2', 'Conv_90']/-nq ['Conv_2', 'Conv_90']

  • 指定多个节点量化/不量化:如下配置意为分别只量化Conv_2以及只量化Conv_2Conv_90并保持其他节点不量化,计算累积误差曲线;或分别解除量化Conv_2以及解除量化Conv_2Conv_90并保持其他节点量化,计算累积误差曲线。

    API 配置方式为:

    quantize_node=[['Conv_2'], ['Conv_2', 'Conv_90']]/non_quantize_node=[['Conv_2'], ['Conv_2', 'Conv_90']]

    命令行配置方式为:

    -q [['Conv_2'], ['Conv_2', 'Conv_90']]/-nq [['Conv_2'], ['Conv_2', 'Conv_90']]

  • 按照量化敏感度排序选择节点量化/不量化:

注:non_quantize_node设置不量化的节点等价于让其运行在 CPU 上。

测试部分量化精度时,可以按照量化敏感度排序进行多组量化策略的精度对比,此时可以参考以下用法:

# 导入debug模块
import horizon_nn.debug as dbg# 首先使用量化敏感度排序函数获取模型中节点的量化敏感度排序,或者可以直接加载保存的量化敏感度
node_message = dbg.get_sensitivity_of_nodes(model_or_file='./calibrated_model.onnx',metrics='cosine-similarity',calibrated_data='./calibration_data/',output_node=None,node_type='node',verbose=False,interested_nodes=None)# node_message为字典类型,其key值为节点名称
nodes = list(node_message.keys())# 通过nodes来指定不量化节点,可以方便使用
dbg.plot_acc_error(save_dir='./',calibrated_data='./calibration_data/',model_or_file='./calibrated_model.onnx',non_quantize_node=[nodes[:1],nodes[:2]], #分别解除量化敏感度排序靠前的节点metric='cosine-similarity',average_mode=True)

可视化结果:

针对部分量化精度分析的累积误差曲线图,在分析时,用户应当更关注模型输出位置(曲线尾部)的量化相似度,例如下图,qmodel_1 模型的精度要好于 qmodel_0:

  • 指定激活节点/权重节点分别量化:

如下配置意为分别只量化权重节点和激活节点并保持其他节点不量化,计算累积误差曲线。

API 配置方式为:quantize_node=['weight', 'activation']

命令行配置方式为:-q ['weight', 'activation']

可视化结果:通过下图可以分析出激活节点量化引入了大量的量化累积误差,而权重节点量化对模型精度无负面影响。

4.2 获取节点量化敏感度

API 使用方法:

# 导入debug模块
import horizon_nn.debug as dbg
# 导入log日志模块
import logging# 若verbose=True时,需要先设置log level为INFO
logging.getLogger().setLevel(logging.INFO)
# 获取节点量化敏感度
node_message = dbg.get_sensitivity_of_nodes(model_or_file='./calibrated_model.onnx',metrics=['cosine-similarity', 'mse'],calibrated_data='./calibration_data/',output_node=None,node_type='node',data_num=None,verbose=True,interested_nodes=None)

命令行使用方法:

# 例如
hmct-debugger get-sensitivity-of-nodes calibrated_model.onnx calibration_data -m ['cosine-similarity','mse']

可通过hmct-debugger get-sensitivity-of-nodes -h/--help查看相关参数

参数介绍:

详细的 API 和命令行参数说明以及配置介绍,见 J5 算法工具链—PTQ Debug 工具使用。

Debug 结果分析:

首先您通过node_type设置需要计算敏感度的节点类型,然后工具获取校准模型中所有符合node_type的节点,并获取这些节点的量化敏感度。当verbose设置为 True 时,工具会将节点量化敏感度进行排序后打印在终端,排序越靠前,说明该节点量化引入的量化误差越大。

同时对于不同的node_type,工具会显示不同的节点量化敏感度信息,设置verbose=True,结果如下:

# node_type='node'
=================node sensitivity=================
node                  cosine-similarity  mse
---------------------------------------------------
Conv_60               0.77795            68.02103
...# node_type='weight'
# weight:权重校准节点名
# node:权重校准节点对应的普通节点名,即权重校准节点的输出为其输入
====================================node sensitivity====================================
weight                                              node     cosine-similarity  mse
-----------------------------------------------------------------------------------------
471_HzCalibration                                   Conv_2   0.99978            0.07519
...# node_type='activation'
# activation:激活校准节点名
# node:激活校准节点后的普通节点,即激活校准节点的输出为其输入
# threshold:校准阈值,若有多个阈值则取最大值
# bit:量化比特
===================================node sensitivity===================================
activation          node                  threshold  bit  cosine-similarity  mse
---------------------------------------------------------------------------------------
406_HzCalibration   Conv_60               0.91501    8    0.77851            67.82422
...

此外,调用 API 时,API 还将以字典格式(Key 为节点名称,Value 为节点的量化敏感度信息)返回节点量化敏感度信息,以供用户后续使用,返回格式如下:

node_message:
{'Conv_60': {'cosine-similarity': 0.77795, 'mse': 68.02103},'Conv_48': {'cosine-similarity': 0.78428, 'mse': 64.36318},...
}

量化敏感度保存与加载:

由于模型的量化敏感度在 PTQ 精度 Debug 过程中扮演着重要的角色,累积误差曲线的计算分析中会用到,因此建议用户及时保存模型量化敏感度,直接加载以节省时间,可以参考如下代码进行量化敏感度的保存与加载:

注意:命令行暂时无法手动保存量化敏感度。

# node_message保存为text文件
filename = open('sensitivity_of_nodes.txt', 'w')
for node, sensitivity in node_message.items():filename.write(str(node) + ':' + str(sensitivity))filename.write('\n')
filename.close()
# 加载保存的text文件
node_message = {}
filename = open('sensitivity_of_nodes.txt')
for line in filename:line_split = line.split(':', 1) #按照第一个出现的':'进行分割node_message[line_split[0]] = line_split[1] #{str: str,...}
filename.close()# node_message保存为json文件(推荐)
import json
save_json = json.dumps(node_message, sort_keys=False, indent=4, separators=(',', ': '))
filename = open('sensitivity_of_nodes.json', 'w')
filename.write(save_json)
filename.close()
# 加载保存的json文件
filename = open('sensitivity_of_nodes.json', 'r')
node_message = json.load(filename)
filename.close()

4.3 获取节点数据分布

指定节点,分别获取该节点在浮点模型和校准模型中的输出,得到输出数据分布。另外,将两个输出结果做差,获取两个输出之间的误差分布。

API 使用方法:

# 导入debug模块
import horizon_nn.debug as dbgdbg.plot_distribution(save_dir='./',model_or_file='./calibrated_model.onnx',calibrated_data='./calibration_data',nodes_list=['317_HzCalibration', #激活节点'471_HzCalibration', #权重节点'Conv_2']) #普通节点

命令行使用方法:

hmct-debugger plot-distribution calibrated_model.onnx calibration_data -n ['317_HzCalibration','471_HzCalibration','Conv_2']

可通过hmct-debugger plot-distribution -h/--help查看相关参数。

参数介绍:

详细的 API 和命令行参数说明以及配置介绍,阅读原文后至社区搜索《 J5 算法工具链—PTQ Debug 工具使用》。

Debug 结果分析:

可视化结果:

注:蓝色三角表示数据绝对值的最大值;红色虚线表示最大的校准阈值。

数据分布结果的分析标准为是否满足对量化友好的正态分布,只要分布中有一个很明显的单峰就认为满足正态分布,不需要严格满足正态分布公式,例如可视化结果中的第一张图,输入数据分布相对比较集中,对于激活节点而言输入数据分布是友好的;同理第二张图校准前后权重节点的数据分布也是友好的。

第三张图是普通节点校准前后的输出数据分布以及量化误差分布,也符合正态分布,当节点输出不符合正态分布时,用户可以尝试在模型中增加 BatchNorm 层并重新训练,然后再进行 PTQ 量化。

4.4 获取节点通道间数据分布

绘制指定校准节点输入数据通道间数据分布的箱线图。

API 使用方法:

# 导入debug模块
import horizon_nn.debug as dbgdbg.get_channelwise_data_distribution(save_dir='./',model_or_file='./calibrated_model.onnx',calibrated_data='./calibration_data',nodes_list=['317_HzCalibration'],axis=None)

命令行使用方法:

hmct-debugger get-channelwise-data-distribution calibrated_model.onnx calibration_data -n ['317_HzCalibration']

可通过hmct-debugger get-channelwise-data-distribution -h/--help查看相关参数。

参数介绍:

详细的API和命令行参数说明以及配置介绍,见J5算法工具链—PTQ Debug工具使用。

Debug结果分析:

可视化结果:

注:横坐标表示节点输入数据的通道数;纵坐标表示每个 channel 的数据分布范围,其中红色实线表示该 channel 数据的中位数,蓝色虚线表示均值。

通过箱线图可以直观地了解当前数据每个通道之间的数据分布情况。通过观察箱线图纵坐标确认数据分布范围,当某一个通道有异常值时(即数值极大或极小,例如上图中第 21 通道),认为当前节点采用 per-tensor 量化会有较大的量化风险,需要尝试使用 per-channel 量化去减少量化误差。箱线图的阅读可以参考下图:

05 实例参考

我们还提供了三篇社区文章,来展示如何在具体模型上使用 PTQ Debug 工具进行分析:

  1. 【PTQ 精度 debug 示例】mnasnet_1.0_96 精度问题分析
  2. 【PTQ 精度 debug 示例】repvgg_b2_deploy 精度问题分析
  3. 【PTQ 精度 debug 示例】MobileVit_s 精度问题分析

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字符串的遍历、统计字符案例

1.两种字符串遍历方法1.toCharArray 将字符串转换成一个新的字符类型的数组 调用方式:对象.如图,其实就相当于把字符串全部拆开,变成一个个的字符,再由字符数组来接收2.charAt 根据输入的索引,从字符串里找出对应的字符 调用方法:对象. 如图如果我们要遍历字符串,那不就…

synchronized同步锁机制

目录synchronized 的使用Java的对象头和 Monitor对象头实例数据对齐填充synchronized 原理synchronized修饰代码块示例对象锁的四种状态无锁偏向锁轻量级锁重量级锁synchronized 的使用如果修饰的是具体对象:锁的是对象 如果修饰的是成员方法:那锁的就是 this 如果修饰的是静…

员工出入更衣室穿戴规范识别检测系统

员工出入更衣室穿戴规范识别检测系统能够通过安装在更衣室入口的摄像机,员工出入更衣室穿戴规范识别检测系统实时检测员工的穿戴情况。系统的工作流程如下:当员工进入更衣室时,摄像机捕捉到图像,算法迅速识别图像中的人员,并检测他们是否穿戴了规定的防护服、护目镜、口罩…

Lock接口

目录Lock接口Lock接口概述API方法锁获取与中断Synchronized和Lock的区别 Lock接口大佬地址: AQS(AbstractQueuedSynchronizer)源码深度解析(2)—Lock接口以及自定义锁的实现Lock接口概述 Lock接口同样自于JDK1.5,它被描述成JUC中的锁的超级接口,所有的JUC中的锁都会实现Lock…

作文的深度解析

目录题目一:There is a growing awareness of the importance of digital literacy and skills in todays world题目二:Nowadays more and more college students have come to realize social practice and academic learning are equally important. 题目一:There is a gr…

终极Redis

Redis是世界上最流行的数据存储之一,功能丰富。这里有8个简单的步骤可以帮助你理解Redis的基本原理。1、什么是Redis?Redis(远程字典服务器)是一个多模式数据库,提供亚毫秒级的延迟。Redis背后的核心思想是缓存也可以作为一个完整的数据库。2、Redis采用Airbnb、Uber、Sla…

AI电动车头盔识别系统解决方案

AI电动车头盔识别系统解决方案通过在关键路段及社区入口等位置安装高清摄像头,AI电动车头盔识别系统解决方案结合深度学习算法对电动车骑行者进行实时监测,确保骑行者的安全。识别到未佩戴头盔的骑行者时,AI电动车头盔识别系统解决方案将立即联动附近的智能广播系统播放预先…

H5-17 选择器

CSS语法 规则由两个主要的部分构成:选择器,以及一条或多条声明(样式) 1、全局选择器可以与任何元素匹配,优先级最低,一般做样式初始化*{margin:0;padding:0;} 2、元素选择器HTML 文档中的元素,p、b、div、a、img、body等标签选择器,选择的是页面上所有这种类型的标签…

canal的安装搭建

canal介绍主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 这里我们可以简单地把canal理解为一个用来同步增量数据的一个工具。canal能做什么数据库镜像 数据库实时备份 索引构建和实时维护 业务cache(缓存)刷新 带业务逻辑的增量数据处理首先开启mysql bi…

ctfshow 信息搜集web入门思路

做ctfshow的思路(web2)js前台拦截 无法使用f12打开代码 可以使用 ctrl+u 或者在网站前面加上view-source: 或者使用bp进行抓包(web4)robots.txt 中可能包含着信息(web5)phps源码泄露index.phps(web6)源码泄露 www.zip 泄露(web7)/.git/ 源文件泄露(web8)index.p…

分区函数partition by的基本用法【转载】

本章将和大家分享分区函数partition by的基本用法。本章将和大家分享分区函数partition by的基本用法(此处以MySQL为例)。废话不多说,下面我们直接进入主题。 一、建表语句-- 创建商品表 CREATE TABLE commodity (id int NOT NULL PRIMARY KEY COMMENT 主键,position VARCHA…

JavaSwing外观美化

系统可选风格 windows风格: com.sun.java.swing.plaf.windows.WindowsLookAndFeelwindows Classic风格: com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel Metal风格 ( Swing默认) :avax.swing.plaf.metal.MetalLookAndFeel Motif风格 : com.sun.java.swing.plaf…