4、卷积运算原理:二维卷积、填充、步长、多通道、池化、激活函数
卷积神经网络(CNN)在硬件加速器设计中占据核心地位,其运算本质是滑动窗口内的乘累加(MAC)操作。对于RTL设计工程师而言,理解卷积的数学定义、数据复用模式以及存储访问规律,是设计高效流水线、控制DMA搬运和调度PE阵列的前提。本章从硬件视角拆解二维卷积、填充(Padding)、步长(Stride)、多通道(Multi-channel)、池化(Pooling)与激活函数(Activation)的工程实现要点,并给出RTL设计中的关键参数映射关系。
4.1 二维卷积的硬件本质
二维卷积在数学上定义为输入特征图与卷积核之间的滑动窗口乘累加运算。设输入特征图尺寸为 H×W,卷积核尺寸为 K×K,则输出特征图在无填充、步长为1时的尺寸为 (H-K+1)×(W-K+1)。从RTL视角看,每个输出像素需要 K×K 次乘法与 K×K-1 次加法,这些运算在硬件中映射为PE(Processing Element)阵列的并行MAC操作。
硬件实现时,输入特征图通常以行流(Row Stream)方式进入行缓冲器(Line Buffer)。行缓冲器的深度等于卷积核高度 K,宽度等于输入特征图宽度 W。每个时钟周期,行缓冲器输出一个 K×K 的滑动窗口数据,与预加载的卷积核权重进行乘累加。这种设计将二维卷积转化为一维数据流处理,显著降低了对存储带宽的需求。
4.2 填充与步长的RTL实现策略
填充(Padding)用于控制输出特征图的尺寸,常见模式有 VALID(无填充)和 SAME(输出尺寸等于输入尺寸)。在硬件中,填充通过控制状态机在行缓冲的起始和结束位置插入零值实现。具体而言,当行计数器位于前 P 行或后 P 行时,行缓冲写入零值;当列计数器位于前 P 列或后 P 列时,滑动窗口内对应位置强制为零。
步长(Stride)定义了卷积核每次滑动的像素数。步长为2时,输出尺寸近似减半,常用于下采样。硬件实现步长有两种方式:一是控制行缓冲的写入使能,每隔 S 个像素写入一次;二是在PE阵列的输出端增加数据选择器,仅保留步长对应的输出结果。第一种方式节省存储带宽,第二种方式简化控制逻辑,实际设计中需根据系统吞吐率要求权衡。
| 参数 | VALID 模式 | SAME 模式 | 硬件开销 |
|---|---|---|---|
| 填充行数 | 0 | floor(K/2) | 行缓冲深度增加 |
| 填充列数 | 0 | floor(K/2) | 窗口控制逻辑 |
| 输出尺寸公式 | (H-K)/S+1 | H/S | 地址生成器复杂度 |
| 典型应用 | 特征提取 | 语义分割 | — |
4.3 多通道卷积的累加与数据复用
多通道卷积是CNN中最常见的运算形式。输入特征图具有 C_in 个通道,卷积核尺寸为 K×K×C_in,输出特征图具有 C_out 个通道。每个输出通道需要 C_in 个 K×K 卷积核分别与对应输入通道卷积,然后将结果累加。硬件实现时,PE阵列通常按输出通道并行度 P 组织,每个PE负责一个输出通道的部分累加。
数据复用策略是硬件设计的关键。输入特征图数据可以被多个输出通道复用,权重数据可以被同一输入通道的多个空间位置复用。常见的复用架构包括:
- 权重固定(Weight Stationary):权重驻留在PE寄存器中,输入特征图流经PE阵列,适合权重较小的网络层。
- 输入固定(Input Stationary):输入特征图驻留在PE中,权重流经PE阵列,适合输入通道数较少的层。
- 输出固定(Output Stationary):部分和驻留在PE中,输入和权重均流式输入,减少中间累加结果的搬运。
在RTL设计中,多通道累加通常采用两级流水:第一级完成 K×K 窗口内的乘累加,第二级完成 C_in 个通道间的累加。第二级累加器需要足够的位宽以防止溢出,一般建议在输入位宽基础上增加 log2(C_in×K×K) 位。
4.4 池化与激活函数的硬件映射
池化(Pooling)用于降低特征图空间分辨率,常见操作有最大池化(Max Pooling)和平均池化(Average Pooling)。硬件实现时,池化窗口通常为 2×2,步长为2。最大池化通过比较器树实现,平均池化通过加法器和移位器实现(除以4可用右移2位完成)。池化单元可直接复用卷积的行缓冲数据,在卷积输出后级联处理,减少额外的存储访问。
激活函数引入非线性,常见的有ReLU、Leaky ReLU、Sigmoid和Tanh。ReLU在硬件中仅需判断符号位,实现成本极低;Leaky ReLU需要乘法器或移位器实现斜率;Sigmoid和Tanh通常采用查找表(LUT)或分段线性逼近实现。在FPGA设计中,ReLU是首选激活函数,因其零硬件开销且能有效缓解梯度消失问题。
4.5 案例拆解:3×3卷积+ReLU+2×2池化的RTL实现
以YOLOv3-tiny的第一层为例,输入为 416×416×3 的RGB图像,卷积核为 3×3×3×16,步长为1,填充为1,输出为 416×416×16,随后接ReLU和2×2最大池化。该层在FPGA上的资源消耗估算如下:
- 行缓冲:3行×416像素×8bit×3通道 = 29,952 bit,约3个BRAM36K。
- PE阵列:16个MAC单元,每个包含9个乘法器和8个加法器。
- 池化单元:16个2输入比较器,输出16通道的112×112特征图。
实现过程中,状态机按以下流程调度:
- 从DDR读取输入行数据,写入行缓冲,同时进行填充控制。
- 行缓冲满3行后,启动滑动窗口,每个时钟周期输出一个3×3×3窗口。
- PE阵列并行计算16个输出通道的部分和,累加27个乘累加结果。
- ReLU单元判断输出符号位,负数置零。
- 池化单元缓存2×2窗口,输出最大值。
- 结果写入输出行缓冲,通过DMA搬回DDR。
实测结果表明,该设计在100MHz时钟下可达到约 3.2 GOPS 的吞吐率,资源占用为 4.2K LUT、3.8K FF、6个BRAM,满足实时视频处理需求。若需进一步提升性能,可将PE阵列扩展为32或64个MAC单元,但需注意行缓冲带宽和DDR访问冲突。
4.6 常见误区与设计复盘
在卷积硬件设计中,以下问题值得特别关注:
- 位宽不足导致溢出:多通道累加时,部分和位宽需预留足够余量。建议使用输入位宽+log2(C_in×K×K)+2位,避免截断误差累积。
- 步长与行缓冲不匹配:步长为2时,若行缓冲仍按每像素写入,则浪费一半存储带宽。应通过写使能控制,仅写入有效像素。
- 池化窗口边界处理:当特征图尺寸为奇数时,池化窗口可能越界。需在状态机中增加边界判断,复制边缘像素或丢弃不完整窗口。
- 激活函数流水延迟:ReLU虽然逻辑简单,但插入流水线后可能影响时序收敛。建议将ReLU与池化合并为单级流水,减少寄存器延迟。
复盘上述案例,设计初期曾因填充逻辑放在PE端导致时序违例,改为行缓冲端后解决了问题。此外,池化单元复用卷积行缓冲的数据,节省了约20%的BRAM资源。这些经验表明,硬件设计需从数据流整体视角出发,而非孤立优化单个模块。