ONNX模型的动态和静态量化

article/2025/7/6 0:11:57

引言
 通常我们将模型转换为onnx格式之后,模型的体积可能比较大,这样在某些场景下就无法适用。最近想在移动端部署语音识别、合成模型,但是目前的效果较好的模型动辄几个G,于是便想着将模型压缩一下。本文探索了两种压缩方法,适用的场景也不相同。

对比
 我觉得额两者之间最大的区别有两点,支持的模型类型、是否需要校准数据。

 结合我的实践谈一下这两点,①在做语音合成模型量化的时候,模型中大量的卷积操作,使用netron打开后的结果如下图所示。因为一开始直接就用动态量化,但是搞了一圈发现各种报错。原因就是动态量化对卷积操作的支持力度不够,导致各种问题频出。于是兜兜转转又开始用静态量化的方法,最后才成功实现量化。 ②但是我当时量化的时候使用的校准数据是函数生成的,不是真是的输入数据,导致虽然模型量化成功了,但是效果却大打折扣。
在这里插入图片描述

 下面给出两种方法的对比:

特性动态量化静态量化
量化时机推理时动态量化(运行时对权重/激活量化)推理前离线量化(事先量化权重/激活)
是否需要校准数据不需要需要标定数据进行校准
支持的模型类型RNN、Transformer 等支持良好CNN、稳定数据分布模型
部署复杂度简单,一键量化复杂,需要数据流过模型进行校准
加速效果中等(通常1.2–2x)最优(通常2–4x)
加速效果小(但一般略大于静态)最小(通过校准降低损失)

动态量化
 直接贴代码进行分析

import onnx
from onnxruntime.quantization import QuantFormat, QuantType, quantize_dynamicname = 'model-steps-3'
# 加载原始 ONNX 模型
model_path = f"matcha-icefall-zh-baker/{name}.onnx"
quantized_model_path = f"matcha-icefall-zh-baker/{name}_quant.onnx"# 动态量化模型
quantize_dynamic(model_input=model_path,  # 输入的原始模型路径model_output=quantized_model_path,  # 输出的量化模型路径weight_type=QuantType.QInt8,  # 权重量化类型,使用 INT8nodes_to_exclude=['/encoder/prenet/conv_layers.0/Conv', '/encoder/prenet/conv_layers.1/Conv_quant', 'Transpose'] op_types_to_quantize=  ["MatMul", "Mul", "Add", "Constant", "Shape", "Unsqueeze", "Reshape", "Relu", "Sigmoid", "Softmax", "Tanh", "InstanceNormalization", "Softplus", "Slice", "Where", "RandomNormalLike", "Pad"
]         # ['MatMul', 'Attention', 'LSTM', 'Gather', 'EmbedLayerNormalization', 'Conv']
)
# 如果不想量化某个算子,可以直接通过nodes_to_exclude忽略掉
# 也可以通过op_types_to_quantize选择要对哪些算子进行量化print(f"Quantized model saved to {quantized_model_path}")

静态量化
 这个代码贴的比较长,其实主要就是针对量化前后的模型进行了分析。

import onnx
import numpy as np
import onnxruntime as ort
from onnxruntime.quantization import quantize_static, CalibrationDataReader, QuantType, QuantFormat
import os
import timeclass MelCalibrationDataReader(CalibrationDataReader):"""用于校准的数据读取器,提供MEL频谱图数据"""def __init__(self, batch_size=8, num_batches=10, input_shape=(80, 100)):"""初始化校准数据读取器Args:batch_size: 每批数据的大小num_batches: 批次数量input_shape: MEL频谱图的形状 (特征维度, 时间步长)"""self.batch_size = batch_sizeself.num_batches = num_batchesself.input_shape = input_shapeself.data_counter = 0# 创建随机数据,在实际应用中应替换为真实数据集self.data = []for _ in range(num_batches):# 随机生成FLOAT32类型的MEL频谱图数据mel_data = np.random.randn(batch_size, *input_shape).astype(np.float32) * 1.5self.data.append(mel_data)def get_next(self):"""返回下一批校准数据"""if self.data_counter >= self.num_batches:return Nonemel_batch = self.data[self.data_counter]input_feed = {'mel': mel_batch}  # 'mel'是输入节点的名称,需要与模型匹配self.data_counter += 1return input_feeddef rewind(self):"""重置数据计数器"""self.data_counter = 0def load_real_calibration_data(data_path, batch_size=8, num_batches=10):"""加载真实的校准数据(如果有)Args:data_path: 数据目录路径batch_size: 每批数据的大小num_batches: 批次数量Returns:CalibrationDataReader实例"""# 这里实现从文件加载真实MEL数据的逻辑# 如果有特定格式的数据,应该在这里进行加载和预处理# 为简化示例,这里仍使用随机数据return MelCalibrationDataReader(batch_size, num_batches)def quantize_onnx_model(model_path, quantized_model_path, calibration_data_reader=None):"""对ONNX模型进行静态量化Args:model_path: 原始ONNX模型路径quantized_model_path: 量化后模型保存路径calibration_data_reader: 校准数据读取器"""# 加载原始模型model = onnx.load(model_path)# 输出原始模型信息print(f"原始模型输入: {[i.name for i in model.graph.input]}")print(f"原始模型输出: {[o.name for o in model.graph.output]}")# 设置量化参数# QuantType.QInt8 - 8位整数量化# QuantType.QUInt8 - 无符号8位整数量化quant_type = QuantType.QInt8# 量化格式选择# QuantFormat.QDQ - 使用QuantizeLinear/DequantizeLinear节点对# QuantFormat.QOperator - 使用量化算子quant_format = QuantFormat.QDQ# 指定需要量化的算子类型op_types_to_quantize = ['Conv' , 'ConvTranspose']  # # 指定不需要量化的节点名称(如果有的话)# nodes_to_exclude = ['某些不需要量化的节点名称']# 执行静态量化print(f"开始对模型 {model_path} 进行静态量化...")start_time = time.time()quantize_static(model_input=model_path,model_output=quantized_model_path,calibration_data_reader=calibration_data_reader,quant_format=quant_format,# op_types_to_quantize=op_types_to_quantize,per_channel=True,  # 使用每通道量化可以提高精度weight_type=quant_type,activation_type=quant_type,# optimize_model=True  # 在量化前优化模型)quantization_time = time.time() - start_timeprint(f"量化完成,耗时: {quantization_time:.2f} 秒")return quantized_model_pathdef compare_models(original_model_path, quantized_model_path):"""比较原始模型与量化模型的大小和性能Args:original_model_path: 原始模型路径quantized_model_path: 量化模型路径"""# 比较文件大小original_size = os.path.getsize(original_model_path) / (1024 * 1024)  # MBquantized_size = os.path.getsize(quantized_model_path) / (1024 * 1024)  # MBprint(f"原始模型大小: {original_size:.2f} MB")print(f"量化模型大小: {quantized_size:.2f} MB")print(f"压缩比: {original_size / quantized_size:.2f}x")# 创建测试数据test_input = np.random.randn(1, 80, 100).astype(np.float32)# 测试原始模型推理性能session_options = ort.SessionOptions()session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL# 原始模型推理print("测试原始模型推理性能...")original_session = ort.InferenceSession(original_model_path, session_options)input_name = original_session.get_inputs()[0].namestart_time = time.time()num_runs = 100for _ in range(num_runs):original_output = original_session.run(None, {input_name: test_input})original_time = (time.time() - start_time) / num_runs# 量化模型推理print("测试量化模型推理性能...")quantized_session = ort.InferenceSession(quantized_model_path, session_options)input_name = quantized_session.get_inputs()[0].namestart_time = time.time()for _ in range(num_runs):quantized_output = quantized_session.run(None, {input_name: test_input})quantized_time = (time.time() - start_time) / num_runsprint(f"原始模型平均推理时间: {original_time*1000:.2f} ms")print(f"量化模型平均推理时间: {quantized_time*1000:.2f} ms")print(f"速度提升: {original_time/quantized_time:.2f}x")def evaluate_model_accuracy(original_model_path, quantized_model_path, test_data=None):"""评估量化模型的精度损失Args:original_model_path: 原始模型路径 quantized_model_path: 量化模型路径test_data: 测试数据,如果为None则生成随机数据"""# 创建测试数据if test_data is None:num_samples = 10test_data = np.random.randn(num_samples, 80, 100).astype(np.float32)# 创建会话original_session = ort.InferenceSession(original_model_path)quantized_session = ort.InferenceSession(quantized_model_path)# 获取输入输出名称input_name = original_session.get_inputs()[0].name# 计算均方误差和平均相对误差total_mse = 0total_rel_err = 0for i, sample in enumerate(test_data):# 添加批次维度sample = np.expand_dims(sample, axis=0)# 运行推理original_output = original_session.run(None, {input_name: sample})[0]quantized_output = quantized_session.run(None, {input_name: sample})[0]# 计算均方误差mse = np.mean((original_output - quantized_output) ** 2)total_mse += mse# 计算相对误差# 避免除以零epsilon = 1e-10rel_err = np.mean(np.abs((original_output - quantized_output) / (np.abs(original_output) + epsilon)))total_rel_err += rel_errif i < 3:  # 只打印前几个样本的结果print(f"样本 {i+1} - MSE: {mse:.6f}, 相对误差: {rel_err:.6f}")avg_mse = total_mse / len(test_data)avg_rel_err = total_rel_err / len(test_data)print(f"平均均方误差 (MSE): {avg_mse:.6f}")print(f"平均相对误差: {avg_rel_err:.6f}")return avg_mse, avg_rel_errdef main():# 模型路径original_model_path = "./my_model.onnx"  # 替换为实际模型路径quantized_model_path = "./my_model_quant.onnx"  # 量化后模型的保存路径# 创建校准数据读取器# 在实际应用中,应使用真实数据替代随机数据calibration_data_reader = MelCalibrationDataReader(batch_size=8,num_batches=10,input_shape=(80, 100)  # 根据实际输入形状调整)# 执行静态量化quantized_model_path = quantize_onnx_model(original_model_path,quantized_model_path,calibration_data_reader)# 比较模型大小和性能compare_models(original_model_path, quantized_model_path)# 评估量化精度evaluate_model_accuracy(original_model_path, quantized_model_path)print("ONNX模型静态量化完成!")if __name__ == "__main__":main()

总结
 在对onnx进行量化时,我们要根据自己的模型的结构类型和是否能得到真实的校准数据来选择量化方法。


http://www.hkcw.cn/article/eteDelhjbZ.shtml

相关文章

怎样在视频号卖货入口是哪里?需要注意哪些?

今天给大家分享下我们团队降低违规的一些心法和落地技巧以及视频号卖货的入口希望对大家有所帮助。 一、视频号卖货入口在哪里&#xff1f; 1、点击【视频号】右上角人像&#xff0c;进入【创作中心】下方点击【带货中心】&#xff0c;进入视频号带货中心。 初次进入带货中心…

Ubuntu下实现nginx反向代理

1. 多个ngx实例安装 脚本已经在deepseek的指导下完成啦&#xff01; deepseek写的脚本支持ubuntu/centos两种系统。 ins_prefix"/usr/local/" makefile_gen() {ngx$1 ngx_log_dir"/var/log/"$ngx"/"ngx_temp_path"/var/temp/"${ngx}…

2025最新Nginx安装配置保姆级教程(Windows)

下载 进入官网nginx: download 选择最新的主线版本下载 启动 解压文件夹到你想要的路径下 打开文件夹双击nginx.exe 点允许访问 然后打开浏览器输入localhost回车&#xff0c;如果出现如下页面则安装成功&#xff01; winr打开控制台进入nginx安装目录&#xff0c;输入ngi…

FreeRTOS通俗理解指南:基础概念 + 架构+ 内核组件+练手实验

RTOS 基础概念 想象一下&#xff0c;你是一个忙碌的厨师&#xff0c;在厨房里同时要完成煎牛排和煮意大利面两项任务。 1.传统单线程模式&#xff08;没有RTOS&#xff09; 如果你只能按顺序一项一项地做&#xff0c;就会是这样的过程&#xff1a; 先煎一会儿牛排然后去看看…

端午最全攻略!景点、天气、交通三件套

端午假期去哪玩?各地文旅放大招如何既能体验传统文化又能玩得轻松不踩坑?这份攻略请收好!端午假期去哪玩?端午假期各地文旅放大招端午主题活动丰富多彩部分景区还推出节日优惠政策北京北京推出1700余场活动,与广大市民游客共享初夏好时节。1700余场活动具体戳详情>>…

【DAY34】GPU训练及类的call方法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数GPU性能的查看&#xff1a;看显存、看级别、看架构代际GPU训练的方法&#xff1a;数据和模型移动到GPU device上类的call方法&#xff1a;为什么定义前…

干泵,干式螺杆真空泵

干式真空泵&#xff1a; 无油干式机械真空泵&#xff08;又简称干式机械泵&#xff09;是指泵能从大气压力下开始抽气&#xff0c;又能将被抽气体直接排到大气中去&#xff0c;泵腔内无油或其他工作介质&#xff0c;而且泵的极限压力与油封式真空泵同等量级或者接近的机械真空泵…

day 26 函数专题

一、函数的基本写法 def function_name(parameter1, parameter2, ...):"""Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)"""# 函数体: 实现功能的代码# ...return value # 可选&#xff0c;用于返回结果def 关键字&#xff1a;用于…

目标检测学习

RCNN 默认找出2000个可能存在目标的候选区域 region proposal将候选区域调整为适合AlexNet网络的输入图像的大小&#xff0c;通过CNN对候选区域提取特征向量&#xff0c;2000个建议区域经过Alexnet生成20004096的特征矩阵将20004096的特征向量经过20个类别的svm分类器进行分类&…

谷歌Gemma模型实现智能看病、翻译手语、与海豚沟通

目录 引言&#xff1a;AI的新篇章——从通用走向专属的智慧革命 一、 MedGemma&#xff1a;智能医疗的守护者与革新者 1.1 MedGemma的双重火力 1.2 高效部署与开发者生态 1.3 未来展望 二、SignGemma&#xff1a;跨越无声世界的沟通桥梁 2.1 SignGemma的核心能力 2.2 从…

TDengine 运维——巡检工具(定期检查)

背景 TDengine 在运行一段时间后需要针对运行环境和 TDengine 本身的运行状态进行定期巡检&#xff0c;本文档旨在说明如何使用巡检工具对 TDengine 的运行环境进行自动化检查。 安装工具使用方法 工具支持通过 help 参数查看支持的语法 Usage: taosinspect [OPTIONS]Check…

火语言UI组件--地图

【组件功能】&#xff1a;调用高德地图api,可通过定义经纬度定位显示具体位置。 样式预览 设置 基础设置 属性名称属性释义输入值类型开发者Key(key)高德地图 JSAPI 开发者Key字符串类型安全密钥(securityJsCode)高德地图 JSAPI 安全密钥字符串类型缩放级别(zoom)设置地图缩…

安防通信枢纽新贵:HT-CKNU 网络报警服务器深度解析

在安防体系不断升级的当下&#xff0c;报警服务器作为连接前端设备与接警中心的关键枢纽&#xff0c;其性能优劣直接关乎安全防护的成效。HT-CKNU 网络报警服务器凭借卓越的技术架构、广泛的兼容性和便捷的操作体验&#xff0c;正逐渐成为安防行业的中流砥柱&#xff0c;为各领…

打通仿真数据孤岛,实现精细化权限管理,「共享空间」深度解析

在上一期《资深仿真工程师必备&#xff01;详解平台“控制中枢”——「命令终端」》中&#xff0c;我们详解了如何通过命令行实现作业高效提交与资源调度。然而&#xff0c;工业仿真从来不是单兵作战——复杂模型的多方协作、海量数据的跨团队流转&#xff0c;才是研发场景的常…

桌面工具站

桌面工具站软件&#xff0c;可以将桌面软件、word文档和网页等拖拽到工具站&#xff0c;在工具站点击直接跳转&#xff0c;方便快捷。 可以拖拽进入软件&#xff0c;也可以迪纳基添加&#xff0c;添加界面如下&#xff1a; 支持自定义排序 可自定义背景&#xff0c;效果如下; 支…

人工智能编程学习心得:从零基础到独立开发的蜕变之路

引言&#xff1a;一场改变认知的技术之旅 2022年冬天&#xff0c;我在深夜的办公室里经历了第一次深度学习模型的完整训练过程。当GPU使用率曲线从波动到平稳&#xff0c;当验证集准确率突破85%的那一刻&#xff0c;显示器的蓝光映照着满桌的咖啡杯&#xff0c;这个场景成为我…

Docker 与 Serverless 架构:无服务器环境下的容器化部署

Serverless(无服务器)架构作为云计算领域的革命性范式,以其无需管理服务器、按需付费、自动伸缩的特性,正在改变着应用开发和部署的方式。然而,传统的函数即服务(Function-as-a-Service, FaaS),如 AWS Lambda,在运行时环境、部署包大小和复杂依赖管理方面存在一定的局…

SI24R05国产低功耗2.4GHz+125K低频唤醒SoC人员定位/畜牧业牛羊定位/资产管理定位方案芯片

目录 SI24R05简介功能框图 主要特性开发工具方案特性 SI24R05简介 Si24R05 是一款高度集成的低功耗 SOC 芯片&#xff0c;具有低功耗、Low Pin Count、 宽电压工作范围&#xff0c;集成了 13/14/15/16 位精度的 ADC、LVD、UART、SPI、I2C、TIMER、WUP、IWDG、RTC、无线收发器、…

Apptrace:APP安全加速解决方案

2021 年&#xff0c;某知名电商平台在 “618” 大促期间遭遇 DDoS 攻击&#xff0c;支付系统瘫痪近 2 小时&#xff1b;2022 年&#xff0c;一款热门手游在新版本上线时因 CC 攻击导致服务器崩溃。观察发现&#xff0c;电商大促、暑期流量高峰和年末结算期等关键商业周期&#…

AI生态警报:MCP协议风险与应对指南(中)——MCP Server运行时安全​​

作为连接AI模型与外部工具的“USB-C接口”&#xff0c;MCP协议成为AI生态的核心枢纽&#xff0c;其安全风险已从理论威胁转化为实际攻击目标。 AI生态警报&#xff1a;MCP协议风险与应对指南&#xff08;上&#xff09;——架构与供应链风险https://blog.csdn.net/WangsuSecur…