阶段二:量化与压缩

7课:训练后量化(PTQ)

📖 课程概述

训练后量化(PTQ)将FP32模型转换为INT8/INT4,无需重训练,是工业界最常用量化方法。本课深入量化原理、校准策略和硬件实现。

🔍 量化公式

q = clamp(round(x/scale + zp), qmin, qmax)
x̂ = (q - zp) × scale
对称量化非对称量化
zero_point0非零
精度损失大(不对称分布)
计算简单需处理zp
适用权重激活(ReLU后)

🔧 校准方法

MinMax(直接)、Percentile(99.9%分位)、MSE(最小化量化误差)、KL散度(TensorRT方案)。

💻 Verilog实现

支持对称/非对称/power-of-2量化,per-channel参数寻址,饱和截断,内置校准统计。

📈 性能分析

1周期延迟,INT8~800门,per-channel精度损失<1%。

🧪 练习

1) 实现MSE校准 2) INT4量化器 3) power-of-2量化(scale=2^n只需移位)

🔬 PTQ校准算法深入对比

校准算法的选择对PTQ精度至关重要。以下是四种主流方法的详细对比:

1. MinMax校准

scale = (max_val - min_val) / (2^b - 1),zero_point = round(-min_val / scale)

优点:实现简单,计算量小。缺点:对异常值(outlier)极其敏感,一个异常值就会拉大scale导致大部分值量化精度下降。

2. Percentile校准

取99.9%或99.99%分位数作为截断阈值,忽略极端值:

clip_val = percentile(|x|, 99.9%)

优点:抗异常值。缺点:百分位数选择需要实验,可能丢失极端但重要的值。

3. MSE校准

遍历多个候选阈值,选择使量化MSE最小的阈值:

threshold* = argmin_t ||x - Dequant(Quant(x, t))||²

优点:直接优化量化误差。缺点:搜索空间大,校准慢(需遍历数百个候选)。

4. KL散度校准(TensorRT)

最小化原始分布和量化分布的KL散度:

threshold* = argmin_t KL(P(x) || Q(x|t))

优点:TensorRT默认方案,效果最好。缺点:实现复杂,需要直方图统计和KL计算。

📊 不同校准方法的精度对比

校准方法ResNet-50 Top-1精度损失校准时间
FP32基线76.13%--
MinMax75.01%1.12%秒级
Percentile(99.9%)75.64%0.49%秒级
MSE75.82%0.31%分钟级
KL散度75.93%0.20%分钟级
Per-channel KL75.98%0.15%分钟级
🏆

成就解锁:训练后量化(PTQ)

完成本课后,你已掌握 训练后量化(PTQ) 的核心概念与硬件实现方法。

🔬 量化误差传播的数学分析

量化误差在网络中逐层传播,理解其传播规律对设计鲁棒的量化系统至关重要:

单层量化误差

e_quant = x - Q(x) = x - clamp(round(x/s + zp) × s - zp × s)

对于均匀量化,量化误差的统计特性:

误差通过网络层的传播

// 误差传播分析:
// 
// 卷积层:y = conv(W, x) + b
// 量化后:ŷ = conv(Q(W), Q(x)) + Q(b)
// 
// 误差分解:
// e_total = conv(W, x) - conv(Q(W), Q(x))
//         = conv(W-e_w, x-e_x) - conv(W, x)  (一阶近似)
//         ≈ -conv(e_w, x) - conv(W, e_x)
//
// 全连接层类似:
// e_total ≈ -e_w × x - W × e_x
//
// 关键结论:
// 1. 权重误差乘以输入幅度
// 2. 输入误差乘以权重幅度
// 3. 累积K次后误差放大√K倍
// 4. 因此:大权重→量化敏感,大激活→量化敏感

逐通道量化的理论优势

逐张量量化误差 = Σ_c (s_global/s_c - 1)² × ||W_c||²
逐通道量化误差 ≈ 0 (每个通道独立最优scale)

逐通道量化的误差几乎总是小于逐张量量化,因为每个通道可以有最适合的scale。代价是多存储N组量化参数。

📊 实际量化精度数据

在ImageNet验证集上,不同量化策略的效果:

模型FP32INT8 PTINT8 PCINT4 PC
ResNet-1869.7669.1069.6067.20
ResNet-5076.1375.4075.9373.50
MobileNet-V271.8769.8070.5165.30
EfficientNet-B077.0775.2076.1072.80
ViT-B/1681.8078.5079.9073.20

PT=逐张量,PC=逐通道。MobileNet对量化更敏感(深度可分离卷积)。

🔧 第7课:实现细节与调试指南

本节提供该模块的详细实现指南、常见bug和调试方法:

模块接口定义

每个模块应遵循统一的接口规范:

// 标准模块接口模板:
// module xxx #(
//   parameter DATA_W = 16,
//   parameter ...
// )(
//   input  wire                  clk,      // 时钟
//   input  wire                  rst_n,    // 异步复位,低有效
//   input  wire                  en,       // 模块使能
//   // 数据输入
//   input  wire [DATA_W-1:0]     data_in,
//   input  wire                  data_valid,
//   // 数据输出
//   output reg  [DATA_W-1:0]     data_out,
//   output reg                   data_out_valid,
//   // 状态
//   output wire                  busy,
//   output wire                  error
// );
//
// 设计规则:
// 1. 所有时序逻辑使用posedge clk
// 2. 复位使用异步复位(negedge rst_n)
// 3. 参数化设计,便于配置
// 4. valid-ready握手协议
// 5. 每个模块有独立的en信号

常见Bug与解决方案

症状可能原因调试方法解决方案
输出全零复位后未初始化检查复位逻辑确保复位释放后en有效
数据错位流水线级数不匹配波形对比输入输出对齐valid信号延迟
溢出累加器位宽不够监测累加器最高位增加位宽或加饱和逻辑
死锁valid-ready互等追踪握手信号确保下游始终能接收
时序违例组合逻辑路径太长查看综合报告插入流水线寄存器

Verilator验证步骤

// Step 1: 语法检查
// verilator --lint-only module.v
//
// Step 2: 编写Testbench
// module tb;
//   reg clk, rst_n;
//   // 实例化DUT
//   // 生成测试向量
//   // 自动比对结果
// endmodule
//
// Step 3: 编译仿真
// verilator --cc module.v tb.v
// make -C obj_dir -f Vmodule.mk
// ./obj_dir/Vmodule
//
// Step 4: 波形调试(如果需要)
// verilator --trace --cc module.v tb.v
// 在testbench中: $dumpfile("wave.vcd"); $dumpvars;

性能优化checklist

与其他模块的集成

// 模块集成清单:
// 1. 确认接口信号匹配(位宽、协议)
// 2. 时钟域是否一致(跨域需FIFO)
// 3. 复位策略是否统一
// 4. 信号命名是否规范
// 5. 是否有环路依赖(需打破)
// 6. 参数是否正确传递

🔧 量化参数的硬件存储与管理

量化参数存储设计

Per-channel量化需要为每个通道存储scale和zero_point,硬件上需要高效的参数寻址机制:

// 量化参数存储方案:
// 方案1: 寄存器阵列(小规模)
//   reg [SCALE_W-1:0] scale [0:NUM_CH-1];
//   reg [QW-1:0]      zp    [0:NUM_CH-1];
//   面积: NUM_CH × (SCALE_W + QW) 寄存器
//   64通道INT8: 64×24 = 1536 bit ≈ 300门
//
// 方案2: SRAM(大规模)
//   参数存储在专用SRAM中,按通道索引读取
//   512通道: 512×24 = 12Kbit ≈ 1.5KB SRAM
//   延迟: 1-2 cycle (SRAM读取)
//
// 方案3: 压缩存储(功耗优化)
//   Power-of-2 scale: scale = 2^n,只需存n
//   每通道仅需5-bit(scale指数) + 8-bit(zp) = 13 bit
//   512通道: 512×13 = 6.5Kbit ≈ 0.8KB

📋 本课关键概念总结

概念定义硬件影响
计算密度FLOPs/访存字节数决定计算/带宽受限
数据复用同一数据被多个PE使用减少DRAM访问次数
流水线多级操作重叠执行提高吞吐量,增加延迟
双缓冲两组缓冲区交替使用隐藏数据加载延迟
分块计算大矩阵分成小块处理适配有限片上存储
饱和算术溢出时钳位到极值防止精度崩溃
时钟门控空闲模块关闭时钟降低动态功耗

🎯 学习目标检查

📚 拓展阅读

下一课预告:本课掌握的知识将在下一课中进一步深化。建议先完成练习,确保理解核心概念后再继续。