阶段二:量化与压缩

9课:权值剪枝

📖 课程概述

权值剪枝移除神经网络中不重要的连接,减少计算量和存储。从非结构化剪枝到结构化剪枝,从硬阈值到渐进式剪枝,本课全面覆盖。

🔍 剪枝方法分类

类型粒度加速效果精度影响
非结构化单个权重低(需稀疏硬件)最小
行/列剪枝整行/列中等
通道剪枝整个通道最高较大
关键洞察:非结构化剪枝稀疏度可达90%+但需专用稀疏引擎;结构化剪枝稀疏度50-70%但直接减少MAC操作。

💻 Verilog实现

支持非结构化(逐权重阈值)和结构化(行级)剪枝。内置稀疏度统计计数器。

📈 性能分析

非结构化90%稀疏→理论10倍加速(需稀疏硬件);结构化50%剪枝→2倍实际加速。

🧪 练习

1) 实现渐进式剪枝(逐步降低阈值) 2) 添加通道级剪枝 3) 设计剪枝后微调控制流程

🔬 剪枝粒度与硬件效率

剪枝粒度决定了理论稀疏度到实际加速的转化效率:

细粒度剪枝 vs 粗粒度剪枝

粒度示例最大稀疏度加速转化率硬件支持
单个权重|w|<θ→095%+10-30%(需稀疏索引)专用稀疏引擎
2:4结构化NVIDIA方案50%90%+硬件直接跳过
行/列整行=070%95%+地址跳过
通道整个通道=050%100%无需特殊硬件
整层=030%100%调度跳过

NVIDIA 2:4结构化稀疏

每4个权重中恰好2个为零,硬件用2-bit索引编码非零位置:

存储节省 = 50%,计算节省 = 50%(每周期跳过一半MAC)

这是目前最实用的结构化稀疏方案——在稀疏度和硬件效率间取得完美平衡。

📊 渐进式剪枝策略

一次性大幅剪枝会严重损害精度,渐进式剪枝(Iterative Pruning)分多步进行:

  1. 训练模型到收敛
  2. 剪枝10%最小权重
  3. 微调1-2个epoch恢复精度
  4. 重复步骤2-3直到目标稀疏度
稀疏度曲线:s_t = s_f + (s_i - s_f) × (1 - t/(n×Δt))³

其中s_f是最终稀疏度,s_i=0是初始稀疏度,三次方曲线保证开始慢后面快。

🏆

成就解锁:权值剪枝

完成本课后,你已掌握 权值剪枝 的核心概念与硬件实现方法。

🔬 剪枝的数学基础与硬件映射

剪枝的理论基础是Lottery Ticket Hypothesis(彩票假设):

彩票假设

在随机初始化的网络中,存在一个稀疏子网络(winning ticket),单独训练可以达到原始网络 comparable 的精度

这意味着我们可以安全地移除大量权重而不损失精度——关键是找到正确的子网络。

剪枝标准对比

标准公式最优稀疏度硬件友好
幅度剪枝|w| < θ90%+否(非结构化)
梯度剪枝|∂L/∂w| × |w| < θ85%+
Fisher信息w² × ∂²L/∂w² < θ90%+
L1行/列范数‖w_row‖₁ < θ60-70%是(结构化)
几何中值距几何中值最近70-80%是(通道级)

2:4结构化稀疏的硬件实现

// NVIDIA 2:4 稀疏格式:
// 每4个权重中恰好2个为零
// 压缩存储:2个非零值 + 2-bit索引
//
// 示例:[w0, 0, w2, 0] → 存储[w0, w2] + 索引[00,10]
//       (索引00=w0在位置0, 10=w2在位置2)
//
// 硬件实现:
// 1. 索引解码器:2-bit→4路MUX选择激活值
// 2. 2个MAC单元:同时计算2个非零乘法
// 3. 累加:2个部分和相加
//
// 面积:与密集PE相当(索引解码器≈MAC面积)
// 计算:50%MAC + 索引开销≈5% → 净省45%

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

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

🔧 剪枝硬件的面积效率分析

剪枝对硬件面积的影响

剪枝减少计算量但增加了控制逻辑:

剪枝类型计算节省面积节省控制开销净面积变化
非结构化90%5%(需要索引)+15%+10%
2:4结构化50%40%+10%-30%
通道剪枝50%50%+0%-50%
行/列剪枝60%55%+5%-50%

📋 本课关键概念总结

概念定义硬件影响
计算密度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)优化循环