Vitis AI 进阶认知(Torch量化基础+映射+量化参数+对称性+每通道+PTQ+QAT+敏感性)

news2024/11/27 22:37:15

目录

1. 介绍

2. 基本概念

2.1 映射函数

2.2 量化参数

2.3 校准

2.4 对称与非对称量化

2.5 Per-Tensor and Per-Channel

2.6 PTQ

2.7 QAT

2.8 敏感性分析

2.6 退火学习率

3. 几点建议

4. 总结


1. 介绍

Practical Quantization in PyTorch | PyTorchQuantization is a cheap and easy way to make your DNN run faster and with lower memory requirements. PyTorch offers a few different approaches to quantize your model. In this blog post, we’ll lay a (quick) foundation of quantization in deep learning, and then take a look at how each technique looks like in practice. Finally we’ll end with recommendations from the literature for using quantization in your workflows.icon-default.png?t=N7T8https://pytorch.org/blog/quantization-in-practice/#fundamentals-of-quantization

本文介绍了量化的基本概念和实践应用:

  • 映射函数:将浮点值映射到整数空间,常用的映射函数是线性变换。
  • 量化参数:包括比例因子 ( S ) 和零点 ( Z ),用于确定输入数据的范围和偏移。
  • 校准:确定量化过程中所需的缩放因子和零点,常用方法包括 MaxMin、Percentile、Entropy、MSE 和 Diffs。
  • 对称与非对称量化:对称量化无需计算零点偏移,而非对称量化则需要。
  • Per-Tensor 和 Per-Channel:Per-Tensor 使用相同的量化参数,而 Per-Channel 则为每个通道使用不同的量化参数。
  • PTQ(训练后静态量化):适用于大型模型,通过模块融合和定期校准来减少量化误差。
  • QAT(量化感知训练):通过在训练过程中模拟量化误差来提高小型模型的量化精度。
  • 敏感性分析:确定哪些层对量化最敏感,并保留这些层的 FP32 精度。
  • 退火学习率:通过动态调整学习率来帮助模型更好地收敛。

2. 基本概念

2.1 映射函数

映射函数是一个将值从浮点映射到整数空间的函数。

常用的映射函数是线性变换,由下式给出:

Q_{r}=round(r/S+Z)

其中,r是输入,S和Z是量化参数。

可以将量化后的模型重新转换为浮点空间,反函数由下式给出:

\widetilde{r}=(Q_{r}-Z)\cdot S

\widetilde{r}\neq r,它们的差值构成了量化误差。

2.2 量化参数

映射函数有两个参数:比例因子 S 和零点 Z。

比例因子 S

S 就是输入范围与输出范围的比率:

S=(\beta_{U}-\alpha _{L})/(\beta_{q}-\alpha _{q})

\beta_{U}\alpha _{L}:输入的裁剪范围,即允许输入数据的边界值。这个范围定义了输入数据中哪些值会被保留并映射到量化后的输出空间,而超出这个范围的值会被剪切(截断)。

例如,对于一个输入范围 [-5.0, 10.0],所有小于-5.0的值会被剪切为-5.0,所有大于10.0的值会被剪切为10.0,以确保输入数据在量化过程中不会因为极端值而导致量化精度的显著下降

零点 Z

Z 为偏差量,以确保输入空间中的 0 完美映射到量化空间中的 0。

1). 偏移调整:零点作为偏移量,将缩放后的数据移动到量化范围内。例如,在无符号量化中,零点可以将负值移动到正值范围内。

2). 对称量化:在对称量化中,零点通常为零,因为浮点范围和量化范围是对称的。

3). 非对称量化:在非对称量化中,零点用于调整量化范围,使其适应非对称的浮点数据分布。

2.3 校准

校准(Calibration)的目的是为了确定量化过程中所需的缩放因子(scale factor)和零点(zero point),以便将浮点数转换为整数表示。

Vitis AI 中,3.2.3 局部量化设置 2.Method,提供了不同的校准方法:maxmin、percentile、entropy、mse、diffs。

  • MaxMin:使用校准数据的最大值和最小值来确定范围。这是最简单的方法,但容易受到异常值的影响。
  • Percentile:基于数据的分位数来确定范围,通常使用 99.9% 分位数来避免异常值的影响。
  • Entropy:使用信息熵(如 KL 散度)来最小化原始浮点值和量化值之间的信息损失。这种方法可以最大化保留信息,但计算复杂度较高。
  • MSE(Mean Squared Error):通过最小化原始值和量化值之间的均方误差来确定范围。这种方法在保留模型精度方面表现良好。
  • Diffs:基于数据的差异来确定范围,具体实现可能因工具而异。

量化观察器:

import torch
from torch.quantization.observer import MinMaxObserver, MovingAverageMinMaxObserver, HistogramObserver
C, L = 3, 4
# 使用正态分布生成两个随机张量作为输入数据
normal = torch.distributions.normal.Normal(0,1)
inputs = [normal.sample((C, L)), normal.sample((C, L))]
print(inputs)

observers = [MinMaxObserver(), MovingAverageMinMaxObserver(), HistogramObserver()]
for obs in observers:
  for x in inputs: obs(x) 
  print(obs.__class__.__name__, obs.calculate_qparams())

执行结果:

[tensor([[-0.1551,  0.4171,  0.0281,  0.8844],
        [-0.0766,  1.4027,  0.1924,  0.8369],
        [ 0.7786,  1.0915,  0.4398, -1.8102]]),

tensor([[-1.2902, -1.3943, -1.6080,  0.1695],
        [-0.9307, -0.5508,  0.6164, -2.2461],
        [-1.1094, -0.3126,  0.5751,  0.6137]])]

MinMaxObserver (tensor([0.0143]), tensor([157], dtype=torch.int32))
MovingAverageMinMaxObserver (tensor([0.0126]), tensor([144], dtype=torch.int32))
HistogramObserver (tensor([0.0125]), tensor([141], dtype=torch.int32))

上述结果中,含缩放因子(tensor([0.0143])),零点(tensor([157], dtype=torch.int32))。

通过这个例子,可以看到不同的观察器在处理相同的数据时,可能会生成不同的量化参数。这有助于理解不同观察器的行为和它们在量化过程中可能产生的影响。 

2.4 对称与非对称量化

对称量化方案

对称量化方案(Symmetric quantization schemes),将输入范围集中在 0 附近,无需计算零点偏移。范围计算如下:

-\alpha _{L}=\beta _{U}=max(\left | max(r) \right |,\left | min(r) \right |)

对于倾斜信号(如非负激活),这可能会导致量化分辨率不佳,因为剪切范围包含永远不会出现在输入中的值。

非对称量化方案

非对称量化方案(Asymmetric quantization schemes),将输入空间的最小和最大观测值分配给(\beta_{U},\alpha _{L})。范围计算如下:

\beta_{U}=max(r), \alpha _{L}=min(r)

对于量化非负激活非常有用(如果输入张量从不为负,则不需要输入范围包含负值)。

import torch
import matplotlib.pyplot as plt
import numpy as np

# 从帕累托分布中生成的激活值样本
act = torch.distributions.pareto.Pareto(1, 10).sample((1, 1024))
# 从正态分布中生成的权重样本,并将其展平
weights = torch.distributions.normal.Normal(0, 0.12).sample((3, 64, 7, 7)).flatten()

def get_range(x, scheme):
    if scheme == 'asymmetric':
        return x.min().item(), x.max().item()
    elif scheme == 'symmetric':
        beta = torch.max(x.max(), x.min().abs())
        return -beta.item(), beta.item()

# 计算直方图、边界、以及直方图中非零部分的25%和95%分位数。
def prepare_data(data, scheme):
    boundaries = get_range(data, scheme)
    hist, bin_edges = np.histogram(data.numpy(), bins=100, density=True)
    ymin, ymax = np.quantile(hist[hist > 0], [0.25, 0.95])
    return hist, bin_edges, boundaries, ymin, ymax

# 准备激活和权重数据
act_asymmetric = prepare_data(act, 'asymmetric')
act_symmetric  = prepare_data(act, 'symmetric')
weights_asymmetric = prepare_data(weights, 'asymmetric')
weights_symmetric  = prepare_data(weights, 'symmetric')

# 绘图循环
fig, axs = plt.subplots(2, 2, figsize=(12, 8))
titles = ["Activation, Asymmetric-Quantized", "Activation, Symmetric-Quantized", 
          "Weights, Asymmetric-Quantized", "Weights, Symmetric-Quantized"]
data_list = [act_asymmetric, act_symmetric, weights_asymmetric, weights_symmetric]

for ax, data, title in zip(axs.flatten(), data_list, titles):
    hist, bin_edges, boundaries, ymin, ymax = data
    ax.hist(bin_edges[:-1], bin_edges, weights=hist)
    ax.vlines(x=boundaries, ls='--', colors='purple', ymin=ymin, ymax=ymax)
    ax.set_title(title)

plt.tight_layout()
plt.show()

结果:

2.5 Per-Tensor and Per-Channel

Per-Tensor:整个张量使用相同的比例因子 S 和零点 Z。

Per-Channel:每个通道使用一组比例因子 S 和零点 Z。

Per-Channel 可以减少量化误差,因为异常值只会影响它所在的通道,而不是整个张量。 

2.6 PTQ

训练后静态量化(Post-Training Static Quantization)。

PTQ 方法非常适合大型模型(>10M),但在较小的模型中准确性会受到影响。

模块融合将多个顺序模块(例如: [Conv2d, BatchNorm, ReLU] )合并为一个。融合模块意味着编译器只需要运行一个内核而不是多个;这可以通过减少量化误差来加快速度并提高准确性。

静态量化模型可能需要定期重新校准,以保持对分布漂移的鲁棒性。

2.7 QAT

量化感知训练(Quantization-aware Training)。

QAT 通过将量化误差包含在训练损失中来解决小型模型(<10M)量化数值精度的损失。

所有权重和偏差都存储在 FP32 中,正常进行反向传播。然而,在前向传播中,量化是通过 FakeQuantize 模块进行内部模拟的。FakeQuantize 对数据进行量化并立即反量化,从而添加类似于量化推理期间可能遇到的量化噪声。因此,最终的损失考虑了任何预期的量化误差。

QAT 比 PTQ 具有更高的准确度。

在 QAT 中重新训练模型的计算成本可能是数百个 epoch。

import torch
from torch import nn

backend = "fbgemm"  # 在x86 CPU上运行。如果在ARM上运行,请使用 "qnnpack"。

m = nn.Sequential(
     nn.Conv2d(2,64,8),
     nn.ReLU(),
     nn.Conv2d(64, 128, 8),
     nn.ReLU()
)

"""融合模块"""
torch.quantization.fuse_modules(m, ['0','1'], inplace=True) # 融合第一对Conv-ReLU
torch.quantization.fuse_modules(m, ['2','3'], inplace=True) # 融合第二对Conv-ReLU

"""插入量化和去量化节点"""
m = nn.Sequential(torch.quantization.QuantStub(), 
                  *m, 
                  torch.quantization.DeQuantStub())

"""准备进行量化感知训练"""
m.train()
m.qconfig = torch.quantization.get_default_qconfig(backend)
torch.quantization.prepare_qat(m, inplace=True)

"""训练循环"""
n_epochs = 10
opt = torch.optim.SGD(m.parameters(), lr=0.1)
loss_fn = lambda out, tgt: torch.pow(tgt-out, 2).mean()
for epoch in range(n_epochs):
  x = torch.rand(10,2,24,24)  # 生成随机数据
  out = m(x)  # 通过模型传递数据
  loss = loss_fn(out, torch.rand_like(out))  # 计算损失
  opt.zero_grad()  # 清除梯度
  loss.backward()  # 反向传播
  opt.step()  # 更新参数

"""转换为量化模型"""
m.eval()  # 设置为评估模式
torch.quantization.convert(m, inplace=True)  # 转换模型为量化版本

2.8 敏感性分析

并非所有层对量化的响应都相同,有些层对精度下降比其他层更敏感。

确定最小化精度下降的最佳层组合非常耗时。

进行一次一个的敏感性分析,可以确定哪些层最敏感,并保留这些层的 FP32 精度。

实验中,仅跳过 2 个卷积层(MobileNet v1 中总共 28 个)即可获得接近 FP32 的精度。

2.6 退火学习率

退火学习率(Annealing learning rate)是一种动态调整学习率的方法,灵感来自于物理中的退火过程。退火是指通过逐渐降低温度来减少系统的能量,从而达到更稳定的状态。在机器学习中,退火学习率计划(Annealing learning rate schedule)通过逐渐降低学习率来帮助模型更好地收敛,避免陷入局部最优解。

常见的 Annealing learning rate schedule 方法包括:

  • 指数衰减(Exponential Decay):学习率按指数函数逐渐减小。
  • 余弦退火(Cosine Annealing):学习率按余弦函数周期性减小。
  • 分段衰减(Step Decay):学习率在特定的训练轮次后按固定比例减小。

3. 几点建议

需要注意的几点:

1. 大型模型(参数超过1000万)对量化误差更具鲁棒性。

2. 从预训练的32位量化模型比从头训练INT8模型提供更好的准确性。

3. 通过运行时剖析模型,可以帮助识别在推理中造成瓶颈的层。

4. 动态量化是一个简单的第一步,特别是如果你的模型有很多线性或递归层。

5. 对于权重量化,使用带有MinMax观察器的对称通道量化。对于激活量化,使用带有移动平均MinMax观察器的仿射张量量化。

6. 使用 SQNR 等指标来识别哪些层最容易受到量化误差的影响。关闭这些层的量化。

7. 使用量化感知训练(QAT)进行微调,训练时间约为原始训练计划的10%,并采用从初始训练学习率的1%开始的退火学习率计划。

4. 总结

量化技术在深度学习模型优化中具有重要作用,通过合理选择量化方法和参数,可以在不显著降低模型精度的情况下,显著提高模型的推理速度和内存效率。

本文记录的概念和建议能帮助在今后的实际应用中更好地利用量化技术。

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

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

相关文章

OpenCV几何图像变换(4)亚像素图像截取函数getRectSubPix()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从图像中以亚像素精度检索像素矩形。 getRectSubPix 函数从 src 中提取像素&#xff1a; p a t c h ( x , y ) s r c ( x center.x − ( dst.…

怎么为pdf文件加密设置密码?8个实用pdf文件加密方法

随着数字化信息的普及&#xff0c;保护PDF文件免受未授权访问和篡改的需求日益增强。为了确保您的重要文档安全无虞&#xff0c;加密PDF文件并设置密码成为了一个至关重要的步骤。以下是详细指导&#xff0c;帮助您为PDF文档加密并设置密码&#xff0c;确保其内容的安全。怎么为…

深入理解Java虚拟机(类加载器)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 类加载器 类加载器有什么作用呢&#xff1f; 对于任意的一个类&a…

基于一阶高斯低通滤波器的软件设计

前言 一阶RC低通滤波器&#xff08;巴特沃斯滤波&#xff09;-CSDN博客 一阶高斯低通滤波器不是一个直接的概念&#xff08;因为一阶滤波器通常不产生高斯型频率响应&#xff09;&#xff0c;这里我用软件的方式来模拟高斯低通滤波器的效果。 有纰漏请指出&#xff0c;转载…

C++竞赛初阶L1-13-第五单元-循环嵌套(29~30课)538: T456457 第 n 小的质数

题目内容 输入一个正整数 n&#xff0c;求正整数范围中第 n 小的质数。 输入格式 一个不超过 30000 的正整数 n。 输出格式 第 n 小的质数。 样例 1 输入 10 全部程序代码&#xff1a; #include<bits/stdc.h> using namespace std; int main() {long long n,i;ci…

【数据分享】《中国社会统计年鉴》(2006-2023)

而今天要限时免费分享的数据就是2006-2023年间出版的《中国社会统计年鉴》并以多格式提供免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 一、《中国社会统计年鉴》是一部反映我国社会发展相关领域基本情况的综合性统计资料年刊。书中收录了年全国和…

Leetcode每日刷题之1004.最大连续1的个数|||(C++)

1.题目解析 本题的目的是找出能最多翻转k个0的情况下最长连续的1的个数&#xff0c;并且这是一个二进制数组&#xff0c;只存在0和1&#xff0c;翻转0就是将0变为1 2.算法原理 首先我们想到的一定是暴力枚举&#xff0c;即依次列举出在最多翻转k个0的情况下所有连续1的子数组的…

类和对象(下)(2)

类和对象&#xff08;下&#xff09;(2) static成员 • ⽤static修饰的成员变量&#xff0c;称之为静态成员变量&#xff0c;静态成员变量⼀定要在类外进⾏初始化。 • 静态成员变量为当前类的所有对象所共享&#xff0c;不属于某个具体的对象&#xff0c;不存在对象中&#…

m4a转wav,使用FFmpeg和Python将M4A文件转换为WAV

你可以使用Python程序或Linux命令来将M4A文件转换为WAV文件。下面分别介绍这两种方法。 方法一:使用FFmpeg命令行工具(Linux命令) FFmpeg 是一个强大的多媒体处理工具,支持音视频转换、处理等操作。你可以在终端中使用以下命令将M4A文件转换为WAV文件: ffmpeg -i input…

网络通信(1)

网络&#xff1a; 不同主机间的通信问题 实现网络通信 物理层面 有一个 信息通路 &#xff08;有线 &#xff1b;无线 &#xff1b;5G&#xff1b;4G&#xff1b;星链 &#xff09;软件层面(逻辑层面) 也需要 一个通路 网络编程 Open System Interconnect (OSI网…

[OC]萝卜圈玩行车记录仪

图1-1&#xff0c;你的手动小车 代码是 #机器人驱动主程序 #请在main中编写您自己的机器人驱动代码 import tkinter as tk import turtle v0 # 速度 accFalse;slowFalse;leftFalse;rightFalse # 按键状态 step0.5 # 一次速度变化量 def keyup_press(event):global acc;accTru…

归并排序(Java实现)

目录 归并排序的思想 代码实现 思路 代码 归并排序的特点 归并排序的思想 归并排序的核心思想是分治&#xff0c;分而治之&#xff0c;就是把数组先分成若干个子数组&#xff0c;先将这些子数组排序后&#xff0c;再合并到一起去。 我们常说的归并排序是二路归并排序&…

uview-plus upload组件在上传视频在小程序环境下点击无反应

你们好&#xff0c;我是金金金。 场景 我正在使用uniapp开发微信小程序&#xff0c;使用的vue3&#xff0c;所以集成的uview-plus这个ui库 代码非常的简单&#xff0c;就是一个上传组件 在h5环境下点击是可以上传视频的&#xff0c;在微信小程序开发者工具里面点击完全没反应…

初阶数据结构之计数排序

非比较排序 计数排序 计数排序⼜称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应⽤。 操作步骤&#xff1a; 1&#xff09;统计相同元素出现次数 2&#xff09;根据统计的结果将序列回收到原来的序列中 #include "CountSort.h" void Count(int* arr, int n)…

利用GBDT进行对表格类数据的机器学习的实战项目

一&#xff1a;题目简介 在数据集中&#xff0c;每个样本都对应一个葡萄牙大学的学生。原始数据集中共有4424名学生&#xff0c;。对于每个学生&#xff0c;我们获得了人口统计数据、宏观经济数据以及课程前两个学期的表现。目标是预测学生在三年或四年学习后的状态&#xff1…

虚拟机桥接模式下设置静态IP

虚拟机桥接模式下设置静态ip 1. 设置虚拟机网络适配器为桥接模式2. 配置 CentOS 虚拟机的静态 IP3. 重启网络服务4. 验证配置5. 网络测试 要将 CentOS 虚拟机的网络模式从 NAT 模式更改为桥接模式&#xff0c;并设置静态 IP 地址以与 Windows 内网保持在相同网段&#xff0c;你…

PPP简介

介绍PPP特性的定义和目的。 定义 PPP&#xff08;Point-to-Point Protocol&#xff09;协议是一种点到点链路层协议&#xff0c;主要用于在全双工的同异步链路上进行点到点的数据传输。 目的 PPP协议是在串行线IP协议SLIP&#xff08;Serial Line Internet Protocol&#x…

vue---echarts环形图

1、完整代码直接可以cv <template><div id"main1"></div> </template><script> import * as echarts from echarts; // import { mapState } from vuex; // import { Alarm_Device } from ../utils/api.js; export default {name: P…

视频美颜SDK与直播美颜工具的架构设计与性能优化

本篇文章&#xff0c;小编将深入讲解视频美颜SDK与直播美颜工具的架构设计&#xff0c;并分享一些性能优化的实践经验。 一、视频美颜SDK的架构设计 视频美颜SDK的核心在于其模块化的设计思路。通常&#xff0c;视频美颜SDK由以下几个主要模块组成&#xff1a; 1.图像预处理…

【Qt】常用控件QCalendarWidget的使用

常用控件QCalendarWidget的使用 QCalendarWidget表示一个日历 核心属性 属性说明 selectDate 当前选中的⽇期 minimumDate 最⼩⽇期 maximumDate 最⼤⽇期 firstDayOfWeek 每周的第⼀天(也就是⽇历的第⼀列) 是周⼏. gridVisible 是否显⽰表格的边框 selectionMode…