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

本文主要是介绍Vitis AI 进阶认知(Torch量化基础+映射+量化参数+对称性+每通道+PTQ+QAT+敏感性),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

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 = dataax.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 nnbackend = "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. 总结

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

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

这篇关于Vitis AI 进阶认知(Torch量化基础+映射+量化参数+对称性+每通道+PTQ+QAT+敏感性)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java利用Spire.Doc for Java实现在模板的基础上创建Word文档

《Java利用Spire.DocforJava实现在模板的基础上创建Word文档》在日常开发中,我们经常需要根据特定数据动态生成Word文档,本文将深入探讨如何利用强大的Java库Spire.Do... 目录1. Spire.Doc for Java 库介绍与安装特点与优势Maven 依赖配置2. 通过替换

MySQL 数据库进阶之SQL 数据操作与子查询操作大全

《MySQL数据库进阶之SQL数据操作与子查询操作大全》本文详细介绍了SQL中的子查询、数据添加(INSERT)、数据修改(UPDATE)和数据删除(DELETE、TRUNCATE、DROP)操作... 目录一、子查询:嵌套在查询中的查询1.1 子查询的基本语法1.2 子查询的实战示例二、数据添加:INSE

JavaScript装饰器从基础到实战教程

《JavaScript装饰器从基础到实战教程》装饰器是js中一种声明式语法特性,用于在不修改原始代码的情况下,动态扩展类、方法、属性或参数的行为,本文将从基础概念入手,逐步讲解装饰器的类型、用法、进阶... 目录一、装饰器基础概念1.1 什么是装饰器?1.2 装饰器的语法1.3 装饰器的执行时机二、装饰器的

Java JAR 启动内存参数配置指南(从基础设置到性能优化)

《JavaJAR启动内存参数配置指南(从基础设置到性能优化)》在启动Java可执行JAR文件时,合理配置JVM内存参数是保障应用稳定性和性能的关键,本文将系统讲解如何通过命令行参数、环境变量等方式... 目录一、核心内存参数详解1.1 堆内存配置1.2 元空间配置(MetASPace)1.3 线程栈配置1.

SpringMVC配置、映射与参数处理​入门案例详解

《SpringMVC配置、映射与参数处理​入门案例详解》文章介绍了SpringMVC框架的基本概念和使用方法,包括如何配置和编写Controller、设置请求映射规则、使用RestFul风格、获取请求... 目录1.SpringMVC概述2.入门案例①导入相关依赖②配置web.XML③配置SpringMVC

Elasticsearch 的索引管理与映射配置实战指南

《Elasticsearch的索引管理与映射配置实战指南》在本文中,我们深入探讨了Elasticsearch中索引与映射的基本概念及其重要性,通过详细的操作示例,我们了解了如何创建、更新和删除索引,... 目录一、索引操作(一)创建索引(二)删除索引(三)关闭索引(四)打开索引(五)索引别名二、映射操作(一

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

使用MapStruct实现Java对象映射的示例代码

《使用MapStruct实现Java对象映射的示例代码》本文主要介绍了使用MapStruct实现Java对象映射的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、什么是 MapStruct?二、实战演练:三步集成 MapStruct第一步:添加 Mave