模型量化(一)—— 非对称量化、对称量化(全代码)

模型量化是一种减少深度学习模型大小和提高推理速度的技术,通过降低模型参数的精度(例如,从32位浮点数减少到8位整数)来实现。

量化三个好处:

  • 更少的内存消耗(对于智能手机等端侧设备很重要)
  • 更少的推断时间,因为数据类型更简单。
  • 更少的能量消耗,因为推断需要更少的计算。

在这里插入图片描述
在这里插入图片描述
(题外话:反量化操作是为了让量化后的神经元输出与量化前的输出变化不大,不然整型变换后相乘的参数值会与原本的浮点值差很多,对于后面的神经元来说输入相当于就变了,模型准确率自然大打折扣,所以激活值需要经过反量化,从而使得后面的神经元察觉不到输入发生变化,对于整个神经网络来说,输入什么就输出什么,仿佛没有量化操作参与,精度自然能保持。)

 

量化可以应用于模型的权重和激活(即中间输出值)上。在量化方法中,非对称量化对称量化是两种常见的量化策略,它们各有特点和应用场景。白话说就是对称量化时0点对齐

 

在这里插入图片描述

贴上一个自己总结的笔记,看完本文回过头来看这个会很清晰。

在这里插入图片描述

 
具体的模型量化方法分为
post-training quantization(训练后量化)和quantization-aware training(量化感知训练),在之后的博客中详细讨论,本文先打好量化的基础。

 

非对称量化

非对称量化不要求量化后的值中零点对应于原始值中的零。这意味着量化操作可以有一个任意的零点,这个零点被映射到量化范围内的某个整数值上。因此,非对称量化使用三个参数(量化最小值、量化最大值和零点)来定义从原始数值到量化数值的映射关系。

在这里插入图片描述

缺点是执行量化和反量化操作可能需要更多的计算

 

对称量化

对称量化要求量化后的值中零点必须对应于原始值中的零,这意味着量化操作的零点固定不变。因此,对称量化通常使用两个参数(量化的最小值和最大值)来定义量化的范围,而这个范围是以零为中心对称的。

在这里插入图片描述

缺点是很少有参数矩阵是很对称的,无法像非对称量化那样精确地表示数据范围,导致量化误差增加

 

Python全代码

#1. Create a simple tensor with random items

import numpy as np

# Suppress scientific notation
np.set_printoptions(suppress=True)

# Generate randomly distributed parameters
params = np.random.uniform(low=-50, high=150, size=20)

# Make sure important values are at the beginning for better debugging
params[0] = params.max()
params[1] = params.min()
params[2] = 0

# Round each number to the second decimal place
params = np.round(params, 2)

# Print the parameters
print(params)



#2. Define the quantization methods and quantize

def clamp(params_q: np.array, lower_bound: int, upper_bound: int) -> np.array:
    params_q[params_q < lower_bound] = lower_bound
    params_q[params_q > upper_bound] = upper_bound
    return params_q

def asymmetric_quantization(params: np.array, bits: int) -> tuple[np.array, float, int]:
    # Calculate the scale and zero point
    alpha = np.max(params)
    beta = np.min(params)
    scale = (alpha - beta) / (2**bits-1)
    zero = -1*np.round(beta / scale)
    lower_bound, upper_bound = 0, 2**bits-1
    # Quantize the parameters
    quantized = clamp(np.round(params / scale + zero), lower_bound, upper_bound).astype(np.int32)
    return quantized, scale, zero

def asymmetric_dequantize(params_q: np.array, scale: float, zero: int) -> np.array:
    return (params_q - zero) * scale

def symmetric_dequantize(params_q: np.array, scale: float) -> np.array:
    return params_q * scale

def symmetric_quantization(params: np.array, bits: int) -> tuple[np.array, float]:
    # Calculate the scale
    alpha = np.max(np.abs(params))
    scale = alpha / (2**(bits-1)-1)
    lower_bound = -2**(bits-1)
    upper_bound = 2**(bits-1)-1
    # Quantize the parameters
    quantized = clamp(np.round(params / scale), lower_bound, upper_bound).astype(np.int32)
    return quantized, scale

def quantization_error(params: np.array, params_q: np.array):
    # calculate the MSE
    return np.mean((params - params_q)**2)

(asymmetric_q, asymmetric_scale, asymmetric_zero) = asymmetric_quantization(params, 8)
(symmetric_q, symmetric_scale) = symmetric_quantization(params, 8)

print(f'Original:')
print(np.round(params, 2))
print('')
print(f'Asymmetric scale: {asymmetric_scale}, zero: {asymmetric_zero}')
print(asymmetric_q)
print('')
print(f'Symmetric scale: {symmetric_scale}')
print(symmetric_q)

#2.1 Dequantize the parameters back to 32 bits
params_deq_asymmetric = asymmetric_dequantize(asymmetric_q, asymmetric_scale, asymmetric_zero)
params_deq_symmetric = symmetric_dequantize(symmetric_q, symmetric_scale)

print(f'Original:')
print(np.round(params, 2))
print('')
print(f'Dequantize Asymmetric:')
print(np.round(params_deq_asymmetric,2))
print('')
print(f'Dequantize Symmetric:')
print(np.round(params_deq_symmetric, 2))

#2.2 Calculate the quantization error
print(f'{"Asymmetric error: ":>20}{np.round(quantization_error(params, params_deq_asymmetric), 2)}')
print(f'{"Symmetric error: ":>20}{np.round(quantization_error(params, params_deq_symmetric), 2)}')

 

如何选 𝛼, β

Min-Max很简单不谈了,就取列表中的最大和最小值当作 𝛼, β,确实是如果出现outlier,量化误差就很大。

Percentile为了规避outlier的影响,选择列表比绝大部分数据都大的数为𝛼,但这个比例不是100%哦,不然过滤不掉outlier,可以选99%或99.9%。
np.percentile是NumPy库中的一个函数,用于计算给定数据集在指定百分位数下的值。百分位数是一种统计度量,表示在一个数据集中,有多少比例的数据点小于或等于这个值

在这里插入图片描述

下面用代码对比两种方式:


#1. Create a simple tensor with random items

import numpy as np

# Suppress scientific notation
np.set_printoptions(suppress=True)

# Generate randomly distributed parameters
params = np.random.uniform(low=-50, high=150, size=10000)

# Introduce an outlier
params[-1] = 1000

# Round each number to the second decimal place
params = np.round(params, 2)

# Print the parameters
print(params)


#2. Define the quantization methods and quantize
#2.1 Compare min-max and percentile range selection strategies

def clamp(params_q: np.array, lower_bound: int, upper_bound: int) -> np.array:
    params_q[params_q < lower_bound] = lower_bound
    params_q[params_q > upper_bound] = upper_bound
    return params_q

def asymmetric_quantization(params: np.array, bits: int) -> tuple[np.array, float, int]:
    alpha = np.max(params)
    beta = np.min(params)
    scale = (alpha - beta) / (2**bits-1)
    zero = -1*np.round(beta / scale)
    lower_bound, upper_bound = 0, 2**bits-1
    quantized = clamp(np.round(params / scale + zero), lower_bound, upper_bound).astype(np.int32)
    return quantized, scale, zero

def asymmetric_quantization_percentile(params: np.array, bits: int, percentile: float = 99.99) -> tuple[np.array, float, int]:
    # find the percentile value
    alpha = np.percentile(params, percentile)
    beta = np.percentile(params, 100-percentile)
    scale = (alpha - beta) / (2**bits-1)
    zero = -1*np.round(beta / scale)
    lower_bound, upper_bound = 0, 2**bits-1
    quantized = clamp(np.round(params / scale + zero), lower_bound, upper_bound).astype(np.int32)
    return quantized, scale, zero


def asymmetric_dequantize(params_q: np.array, scale: float, zero: int) -> np.array:
    return (params_q - zero) * scale

def quantization_error(params: np.array, params_q: np.array):
    # calculate the MSE
    return np.mean((params - params_q)**2)

(asymmetric_q, asymmetric_scale, asymmetric_zero) = asymmetric_quantization(params, 8)
(asymmetric_q_percentile, asymmetric_scale_percentile, asymmetric_zero_percentile) = asymmetric_quantization_percentile(params, 8)

print(f'Original:')
print(np.round(params, 2))
print('')
print(f'Asymmetric (min-max) scale: {asymmetric_scale}, zero: {asymmetric_zero}')
print(asymmetric_q)
print(f'')
print(f'Asymmetric (percentile) scale: {asymmetric_scale_percentile}, zero: {asymmetric_zero_percentile}')
print(asymmetric_q_percentile)

#2.2 Dequantize the parameters back to 32 bits

params_deq_asymmetric = asymmetric_dequantize(asymmetric_q, asymmetric_scale, asymmetric_zero)
params_deq_asymmetric_percentile = asymmetric_dequantize(asymmetric_q_percentile, asymmetric_scale_percentile, asymmetric_zero_percentile)

print(f'Original:')
print(np.round(params, 2))
print('')
print(f'Dequantized (min-max):')
print(np.round(params_deq_asymmetric,2))
print('')
print(f'Dequantized (percentile):')
print(np.round(params_deq_asymmetric_percentile,2))

#3. Evaluate the quantization error (excluding the outlier)

# Calculate the quantization error
print(f'{"Error (min-max) excluding outlier: ":>40}{np.round(quantization_error(params[:-1], params_deq_asymmetric[:-1]),2)}')
print(f'{"Error (percentile) excluding outlier: ":>40}{np.round(quantization_error(params[:-1], params_deq_asymmetric_percentile[:-1]),2)}')

 

浮点数存储格式(IEEE-754)

题外话,浮点数的存储格式

在这里插入图片描述

### 对称量化图在机器学习中的作用 对称量化种常见的数值表示方法,在机器学习领域主要用于降低模型的存储需求加速推理过程。其核心思想是对权重或激活值进行线性缩放,而不改变原始数据的符号特性[^2]。 #### 1. **对称量化的定义** 对称量化假设输入数据分布在正负范围内,并采用相同的缩放因子来处理正值负值部分。这意味着零点始终映射到整数零的位置,从而简化了量化后的计算流程。具体而言,对称量化可以表达为: \[ q(x) = \text{round}\left(\frac{x}{s}\right), \] 其中 \( s \) 是缩放因子,\( q(x) \) 表示量化后的值。 #### 2. **对称量化的作用** - **减少计算复杂度** 相较于非对称量化对称量化不需要额外的偏移项(zero-point compensation),因此减少了矩阵乘法运算中的冗余操作[^1]。 - **提高硬件兼容性** 许多现代硬件架构(如TPU、GPU)针对对称量化进行了优化设计,使得基于对称量化模型能够在这些平台上更高效地运行[^3]。 - **保持精度的同时提升性能** 当与适当的技术结合时(例如微调),对称量化可以在不显著损失模型准确性的情况下大幅削减内存占用并加快推断速度。 #### 3. **实现方式** 以下是利用Python实现简单对称量化的代码片段: ```python def symmetric_quantize(data, scale_factor): """ 实现简单的对称量化函数 参数: data (numpy.ndarray): 输入浮点数组 scale_factor (float): 缩放因子 返回: numpy.ndarray: 量化后的整型数组 """ import numpy as np quantized_data = np.round(data / scale_factor).astype(np.int8) return quantized_data # 示例用法 data = np.array([-1.5, -0.75, 0., 0.75, 1.5], dtype=np.float32) scale_factor = 0.75 quantized_result = symmetric_quantize(data, scale_factor) print(quantized_result) ``` 上述代码展示了如何通过指定`scale_factor`将组浮点数转换为其对应的整数量化形式。 --- ### §相关问题§ 1. 如何选择合适的缩放因子以平衡模型精度计算效率? 2. 在实际部署过程中,哪些场景更适合使用对称量化而非非对称量化? 3. 微调阶段是否会对不同类型的量化方案产生差异化的影响? 4. 是否存在某些特殊神经网络结构天然适合某种特定量化模式? 5. 能否进步探讨混合量化策略的应用前景及其潜在挑战?
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈O-Jay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值