阶段二:量化与压缩

8课:量化感知训练(QAT)

📖 课程概述

量化感知训练(QAT)在训练过程中模拟量化效果,让网络学会适应低精度。相比PTQ,QAT精度损失更小但需要训练数据和计算资源。

🔍 Fake Quantization原理

前向:x̂ = Dequantize(Quantize(x)) — 模拟量化误差
反向:∂L/∂x = ∂L/∂x̂ × 1 (STE:在量化范围内梯度直通)

STE(Straight-Through Estimator)是QAT的关键:量化函数不可导,STE假设在量化范围内梯度为1,范围外为0。

💻 Verilog实现

前向路径实现fake quantize(量化→反量化),反向路径实现STE梯度。训练模式用fake量化,推理模式直通。

📈 性能分析

QAT比PTQ精度提升2-5%,但需要1-3个epoch微调。硬件开销仅为PTQ+STE逻辑,约1200门。

🧪 练习

1) 实现学习型scale参数(梯度更新scale) 2) 对比STE与近似梯度的收敛性 3) 设计per-tensor QAT引擎

🔬 STE与替代梯度估计器

直通估计器(STE)是QAT的核心,但不是唯一选择:

STE变体对比

STE变体前向反向梯度收敛性
标准STEQuantize-Dequantize1(范围内) / 0(范围外)
软量化tanh近似平滑梯度更好
直通+clampclamp后量化1(范围内)
EWGS量化+误差补偿1+误差权重最好

📐 QAT训练流程详解

  1. 准备FP32模型:训练好的FP32模型作为QAT的初始化
  2. 插入Fake Quantize节点:在权重和激活后插入量化-反量化操作
  3. 校准初始参数:用校准数据集运行几个batch,统计激活值范围
  4. QAT微调:用STE反向传播,1-3个epoch微调
  5. 导出INT8模型:去掉fake量化,使用整数算子

📊 QAT vs PTQ精度对比

模型FP32PTQ(INT8)QAT(INT8)QAT提升
ResNet-5076.13%75.93%76.05%+0.12%
MobileNet-V271.87%70.51%71.42%+0.91%
BERT-Base82.1%78.3%81.5%+3.2%
YOLO-V5s37.4 mAP36.1 mAP37.0 mAP+0.9 mAP
🏆

成就解锁:量化感知训练(QAT)

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

🔬 QAT训练技巧与最佳实践

QAT的微调策略对最终精度影响巨大:

学习率策略

// QAT微调的学习率设置:
// 1. 起始LR = 原训练最终LR的1/10
// 2. 使用余弦退火(Cosine Annealing)
// 3. 不使用预热(Warmup)——模型已收敛
// 4. 通常1-3个epoch即可
//
// 典型设置:
// ResNet-50: LR=0.001, 3 epochs, batch=256
// MobileNet: LR=0.0005, 5 epochs, batch=128
// BERT:     LR=2e-5, 1 epoch, seq_len=512

量化参数的学习

scale和zero_point也可以作为可学习参数参与训练:

// 学习型scale的梯度推导:
// 前向:q = round(x / scale + zp)
// 反向:∂L/∂scale = ∂L/∂q × ∂q/∂scale
//               = ∂L/∂q × (-x / scale²)
//
// 实现方式:
// 1. scale存储为log2格式,梯度只更新整数部分
// 2. 或者直接用浮点scale,用STE近似梯度
//
// 实验发现:学习型scale比固定scale精度提升0.2-0.5%

不同QAT方案的对比

方案权重精度激活精度ResNet-50训练成本
全INT8INT8INT875.98%
权重INT8+激活FP16INT8FP1676.05%1.5×
混合INT8/INT4INT4/8INT875.80%
全INT4INT4INT473.50%

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

本节提供该模块的详细实现指南、常见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. 参数是否正确传递

🔧 QAT的硬件-软件协同设计

QAT训练加速的硬件需求

QAT训练比推理更复杂,需要硬件支持反向传播:

组件前向反向硬件开销
Fake Quantize量化→反量化STE梯度+20%
权重梯度∂L/∂W+50%(需要转置)
激活梯度∂L/∂x+30%
参数更新W -= lr × ∂L/∂W+15%

📋 本课关键概念总结

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

🎯 学习目标检查

📚 拓展阅读

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

🔬 深入探讨:从理论到实践的映射

将算法原理映射到硬件实现时,需要解决多个层次的抽象差距:

算法→微架构映射原则

  1. 计算并行化:识别算法中的数据并行性和流水线并行性,映射到PE阵列
  2. 存储层次化:根据数据生命周期和复用模式,分配到不同存储层次
  3. 通信局部化:减少全局通信,使用局部传递和广播
  4. 控制简化:将复杂控制流简化为有限状态机

硬件设计的Pareto最优

硬件设计没有唯一最优解,只有Pareto前沿上的多个平衡点:

设计点面积(mm²)功耗(mW)算力(GOPS)能效比
最小面积0.5103.2320
平衡设计2.05025.6512
最高性能5.0200102.4512
最低功耗1.056.41280

验证驱动开发(VDD)

先写testbench,再写RTL,确保每个功能都有对应的测试:

// 验证驱动开发流程:
// 1. 根据规格编写testbench框架
// 2. 定义输入输出接口和预期行为
// 3. 编写RTL代码使测试通过
// 4. 添加边界条件测试
// 5. 添加随机化测试(约束随机)
// 6. 覆盖率分析确保测试充分
//
// 优势:
// - 减少bug引入率
// - 测试即文档
// - 重构有安全网
// - 提高设计质量

功耗-性能-面积(PPA)优化循环