卷积计算原理:单通道卷积、多通道卷积、步长与填充、特征图尺寸计算

各位同学,欢迎来到第二章。今天咱们聊聊卷积计算的核心原理。说实话,很多初学者一上来就调框架、搭网络,结果特征图尺寸怎么算的、多通道到底怎么卷的,一问三不知。我当年刚入行时也踩过这个坑——写出来的加速器,算出来的结果和软件对不上,查了三天才发现是填充逻辑写错了。

所以这一章,咱们把卷积的底裤扒干净。从最简单的单通道开始,一步步推到多通道、步长、填充,最后把特征图尺寸公式彻底搞明白。你想想看,硬件加速的本质就是把这些计算映射到电路上,如果原理不透,后面写 RTL 代码就是空中楼阁。

1. 单通道卷积:最朴素的理解

单通道卷积,说白了就是一张灰度图和一个小窗口做点积。假设输入是一张 5×5 的灰度图,卷积核是 3×3 的矩阵。我们把核放在图的左上角,对应位置相乘再求和,得到一个输出值。然后核向右滑动,重复这个过程。

核心操作: 输入特征图与卷积核的逐元素相乘后累加,得到一个输出像素值。

我在项目中遇到过一种情况:有人把卷积核和输入图都当成矩阵,直接做矩阵乘法。这是错的。卷积是滑动窗口操作,不是矩阵乘。虽然从数学上可以用 im2col 转化,但那是优化手段,不是原理。

举个具体例子:

输入图 (5×5):
[1, 2, 3, 0, 1]
[0, 1, 2, 3, 0]
[1, 0, 1, 2, 3]
[3, 2, 1, 0, 1]
[0, 1, 2, 3, 0]

卷积核 (3×3):
[1, 0, 1]
[0, 1, 0]
[1, 0, 1]

第一次滑动(左上角):
1*1 + 2*0 + 3*1 + 0*0 + 1*1 + 2*0 + 1*1 + 0*0 + 1*1 = 1+0+3+0+1+0+1+0+1 = 7

嗯,这里要注意:输出特征图的大小取决于输入尺寸、核尺寸、步长和填充。咱们后面会统一讲公式。

2. 多通道卷积:从平面到立体

单通道理解后,多通道其实不难。你想想看,彩色图像有 RGB 三个通道,每个通道独立做卷积,然后把结果加起来。但实际卷积核也是多通道的——输入有几个通道,核就有几个通道。

我习惯这样理解:多通道卷积核是一个三维立方体。它的深度等于输入通道数,宽高是核尺寸。每个输入通道对应核的一个切片,分别做卷积,最后把所有通道的结果累加,得到一个输出通道。

个人经验: 在硬件实现时,多通道卷积的累加树设计是关键。我曾经用流水线加法器代替单累加器,吞吐量直接翻倍。但要注意位宽扩展,防止溢出。

假设输入有 Cin 个通道,输出有 Cout 个通道。那么我们需要 Cout 个三维卷积核,每个核的尺寸是 K×K×Cin。每个核生成一个输出通道。

用公式表达:

对于输出通道 j:
  Output[j] = Σ (Input[i] * Kernel[j][i])  // i 从 0 到 C_in-1
  其中 * 表示单通道卷积操作

说白了,多通道卷积就是 Cin 次单通道卷积的结果求和。硬件上,这对应 Cin 个乘累加单元并行工作,最后用加法树合并。

3. 步长与填充:控制特征图尺寸的两把刀

步长(Stride)和填充(Padding)是卷积的两个关键参数。它们直接决定了输出特征图的大小。

3.1 步长

步长就是卷积核每次滑动的像素数。步长为 1 时,核每次移动 1 个像素;步长为 2 时,每次跳 2 个像素。步长越大,输出特征图越小。

我记得有一次做目标检测网络,为了降低计算量,把某些层的步长从 1 改成 2。结果特征图尺寸减半,但精度掉了 3 个点。后来发现是下采样太猛,信息丢失严重。所以步长的选择是个权衡。

步长输出尺寸变化计算量信息保留
1基本不变或略小高完整
2约减半低有损失
≥3大幅缩小很低严重损失

3.2 填充

填充是在输入特征图周围补一圈(或多圈)像素。最常见的填充方式是补 0,即 zero-padding。填充的目的是:

  • 保持尺寸: 如果不填充,每做一次卷积,特征图就会缩小。深层网络很快就缩没了。
  • 利用边缘信息: 不填充时,边缘像素只被卷积核覆盖一次,而中间像素被覆盖多次。填充可以让边缘信息被更充分地利用。

我曾经在 FPGA 上实现过一个加速器,填充逻辑没处理好,导致边界像素的计算结果全是错的。排查后发现是填充数据没有正确写入行缓冲。嗯,硬件上的填充不是简单的补 0,还要考虑数据对齐和时序。

避坑指南: 在硬件实现中,填充会增加行缓冲的深度。如果输入特征图很大,填充带来的额外存储开销不可忽视。我曾经因为没算好行缓冲深度,导致 BRAM 资源爆了。

4. 特征图尺寸计算:一个公式搞定

好了,前面铺垫了这么多,现在给出终极公式。输出特征图的尺寸由四个参数决定:

  • 输入尺寸:Hin × Win
  • 卷积核尺寸:K
  • 填充:P(每边补的像素数)
  • 步长:S

输出尺寸公式:

H_out = floor((H_in + 2*P - K) / S) + 1
W_out = floor((W_in + 2*P - K) / S) + 1

这个公式必须刻在脑子里。我面试 FPGA 工程师时,第一道题就是让候选人默写这个公式。写不出来的,基本可以送走了。

举个例子:

输入:32×32
卷积核:3×3
填充:1
步长:1

H_out = floor((32 + 2*1 - 3) / 1) + 1 = floor(31) + 1 = 32
W_out = 32

输出尺寸:32×32(尺寸不变)

再举个例子:

输入:32×32
卷积核:3×3
填充:0
步长:2

H_out = floor((32 + 0 - 3) / 2) + 1 = floor(29/2) + 1 = 14 + 1 = 15
W_out = 15

输出尺寸:15×15(尺寸缩小)

重要结论: 当 P = (K-1)/2 且 S = 1 时,输出尺寸等于输入尺寸。这就是所谓的 "same" 卷积。很多经典网络都用这个配置。

5. 知识体系总览

下面这张图把本章的核心逻辑串起来了。从单通道到多通道,从步长填充到尺寸计算,每一步都是下一层的基础。

卷积计算原理知识体系 单通道卷积 滑动窗口 + 点积 多通道卷积 通道独立卷积 + 累加 步长 (Stride) 控制滑动步进 填充 (Padding) 边缘补零 特征图尺寸计算 H_out = floor((H_in+2P-K)/S)+1 硬件映射 乘累加 + 行缓冲 关键理解 • 单通道是基础,多通道是扩展,步长和填充是控制参数 • 尺寸公式是硬件设计的输入输出接口规范 • 硬件实现时,步长决定数据读取间隔,填充决定行缓冲深度 • 所有参数最终影响计算单元的数量和调度逻辑

从这张图可以看出,单通道卷积是基础,多通道卷积是扩展,步长和填充是控制参数,尺寸公式是最终的计算结果。硬件映射时,所有这些因素都要考虑进去。

6. 本章小结

咱们把这一章的核心点再捋一遍:

  • 单通道卷积: 滑动窗口 + 点积,最基础的操作单元。
  • 多通道卷积: 每个输出通道对应一组三维核,通道间独立卷积后累加。
  • 步长: 控制核的滑动步进,步长越大输出越小。
  • 填充: 边缘补零,用于保持尺寸或利用边缘信息。
  • 尺寸公式: Hout = floor((Hin + 2P - K) / S) + 1,必须烂熟于心。