第二节 卷积运算的数学原理
各位同学,今天我们来聊聊卷积运算最核心的数学原理。说实话,很多做硬件加速的同学,一上来就撸代码、搭电路,结果连输入输出尺寸怎么算都搞不清楚。我当年刚入行时也犯过这个错——有一次设计一个加速器,算错了输出特征图的尺寸,导致整个数据流都乱了,改了两版才搞定。
所以这节课,咱们把基础打扎实。你想想看,卷积运算说白了就是四个要素:输入特征图、卷积核、步长、填充。搞懂它们之间的关系,后面的硬件设计才能顺风顺水。
2.1 输入特征图
输入特征图,就是我们要处理的数据。在卷积神经网络里,它通常是一个三维的张量。
举个例子,一张彩色图片输入到网络中,它的维度是:
- 高度 H:图片的行数
- 宽度 W:图片的列数
- 通道数 C:RGB 三个通道,所以 C=3
我习惯用 (H, W, C) 来表示。比如一个 224×224 的彩色图,就是 (224, 224, 3)。
2.2 卷积核
卷积核,也叫滤波器。它就是一个小的权重矩阵,在输入图上滑动做点积运算。
卷积核也有三个维度:
- 核高度 K_h:比如 3、5、7
- 核宽度 K_w:通常和高度相等,比如 3×3
- 输入通道数 C_in:必须和输入特征图的通道数一致
另外,每个卷积层会有多个卷积核,数量就是输出通道数 C_out。
举个例子,一个 3×3 的卷积层,输入 64 通道,输出 128 通道。那么卷积核的维度就是 (3, 3, 64, 128)。嗯,这里要注意,最后一个维度是输出通道数。
2.3 步长
步长,就是卷积核每次滑动的步数。用 S 表示。
步长 = 1 时,卷积核一个像素一个像素地挪。步长 = 2 时,每次跳两个像素。说白了,步长越大,输出特征图越小。
我在项目中遇到过一个问题:步长设得太大,导致输出尺寸太小,丢失了很多空间信息。后来我们一般用步长 1 或 2,很少用更大的。
2.4 填充
填充,就是在输入特征图的边缘补一圈数据。用 P 表示填充的层数。
为什么要填充?两个原因:
- 保持尺寸:如果不填充,卷积后输出会变小。比如 5×5 的图,用 3×3 的核,步长 1,输出只有 3×3。
- 利用边缘信息:边缘的像素被卷积的次数少,填充后边缘信息也能被充分提取。
常见的填充方式有两种:
- Valid 卷积:不填充,P=0
- Same 卷积:填充后输出尺寸和输入相同,P = (K-1)/2
2.5 输出特征图的计算
好了,前面四个要素都讲完了。现在来看最关键的——输出特征图怎么算?
公式其实很简单:
输出高度 H_out = floor((H_in + 2P - K_h) / S) + 1
输出宽度 W_out = floor((W_in + 2P - K_w) / S) + 1
输出通道数 C_out = 卷积核的个数
举个例子:
- 输入:32×32×3
- 卷积核:5×5,6 个
- 步长:1
- 填充:2
计算一下:
H_out = floor((32 + 2×2 - 5) / 1) + 1 = floor(31) + 1 = 32
W_out = floor((32 + 2×2 - 5) / 1) + 1 = 32
C_out = 6
输出就是 32×32×6。你看,用了 Same 卷积,尺寸没变。
再举一个步长为 2 的例子:
- 输入:64×64×3
- 卷积核:3×3,16 个
- 步长:2
- 填充:0(Valid 卷积)
H_out = floor((64 + 0 - 3) / 2) + 1 = floor(61/2) + 1 = 30 + 1 = 31
W_out = 31
C_out = 16
输出就是 31×31×16。尺寸缩小了一半左右。
2.6 知识体系总览
下面我用一张图来总结卷积运算的核心逻辑。这张图是我自己画的,把输入、卷积核、步长、填充、输出之间的关系都串起来了。
这张图把整个流程串起来了。你想想看,输入特征图经过填充后,和卷积核做点积运算,步长控制着滑动间隔,最终得到输出特征图。说白了,就是一套数学公式在驱动。
2.7 实际项目中的注意事项
最后,我分享几个在实际项目中踩过的坑:
- 边界条件:当输入尺寸不是步长的整数倍时,floor 操作会导致最后一行/列被丢弃。硬件设计时一定要考虑这种情况。
- 填充的硬件开销:填充意味着要多存几行数据。如果填充很大,片上缓存会吃紧。我一般建议用 Same 卷积,P 不超过 2。
- 多通道的累加:每个输出像素是多个输入通道的累加结果。在硬件里,这个累加器要设计得足够宽,防止溢出。
好了,这节课就到这里。记住,公式要烂熟于心,硬件设计才能游刃有余。