一、卷积神经网络基础:卷积运算的数学原理、特征图与感受野、权值共享与局部连接
各位同学好,我是你们的FPGA工程师老K。今天咱们开始聊卷积神经网络的基础,这部分内容我做了快十年,踩过的坑不少,但收获也很多。说实话,很多做FPGA加速的同学,一上来就撸代码,结果连卷积到底在算什么都没搞明白——嗯,这其实挺要命的。
咱们先别急着写Verilog,先把数学底子打牢。你想想看,FPGA加速的本质是什么?就是把算法里的计算密集型操作,用硬件并行流水的方式跑得更快。如果连算法本身都理解不透,那优化就无从谈起。
1.1 卷积运算的数学原理
卷积运算,说白了就是“加权求和”。我习惯用一个简单的例子来理解:
假设你有一张灰度图,大小是5×5像素。你拿一个3×3的小窗口(我们叫它“卷积核”或者“滤波器”),在这个大图上从左到右、从上到下地滑动。每滑动到一个位置,就把窗口里的9个像素值,和卷积核里对应的9个权重值,一一相乘,然后把这9个乘积加起来。这个结果,就是输出特征图上的一个像素值。
数学表达式长这样:
输出像素值 = Σ (输入像素 × 对应权重) + 偏置
我在项目中遇到过一件事:有个同事把卷积和互相关搞混了。其实在CNN里,我们用的严格来说是“互相关”运算,不是数学意义上的卷积。数学卷积需要把核翻转180度再算,但CNN里不翻转。为什么?因为权重是训练出来的,翻转不翻转,网络都能学到特征,只是参数位置不同而已。所以大家放心,咱们平时写的卷积代码,就是直接乘加,不用翻转。
核心要点:卷积 = 滑动窗口 × 加权求和。就这么简单。
再举个例子。假设输入是4×4,卷积核是3×3,步长是1,没有填充。那么输出尺寸怎么算?
输出尺寸 = (输入尺寸 - 核尺寸) / 步长 + 1
= (4 - 3) / 1 + 1
= 2
所以输出是2×2的特征图。这个公式我建议你记牢,写RTL代码时经常要用到。
1.2 特征图与感受野
特征图是什么?就是卷积层输出的结果。每一层卷积,都会生成一组特征图。每个特征图对应一个卷积核的检测结果。比如你有32个卷积核,那这一层就会输出32张特征图。
我刚开始做FPGA加速时,有个误区:以为特征图就是“图像”。其实不是。特征图是“特征”的响应图。浅层的特征图可能检测边缘、纹理;深层的特征图检测更抽象的概念,比如眼睛、车轮。
感受野这个概念,我觉得特别重要。它指的是输出特征图上的一个像素点,对应到输入图像上有多大区域。你想想看,第一层卷积,3×3的核,感受野就是3×3。但到了第二层,如果还是3×3的核,感受野就变大了——因为第二层的输入已经是第一层的输出,而第一层的每个像素已经“看过”了3×3的区域。
我习惯用一个递推公式来算:
感受野大小 = 上一层的感受野 + (当前核尺寸 - 1) × 步长累积
举个例子:
- 第一层:核3×3,步长1,感受野 = 3
- 第二层:核3×3,步长1,感受野 = 3 + (3-1)×1 = 5
- 第三层:核3×3,步长1,感受野 = 5 + (3-1)×1×1 = 7
你看,三层3×3卷积堆叠,感受野达到了7×7。而直接用7×7卷积,参数量是3×3×3 = 27个权重,而7×7卷积需要49个权重。这就是为什么现代网络喜欢用小卷积核堆叠——感受野大,参数量还少。
我的经验:做FPGA加速时,感受野决定了你的数据复用策略。感受野越大,数据重用的机会就越多,流水线设计就越有优化空间。
1.3 权值共享与局部连接
这两个概念,是CNN能火起来的核心原因。咱们对比一下全连接网络就明白了。
全连接网络:每个输入神经元,都连接到每个输出神经元。假设输入是1000×1000的图像,第一层有1000个神经元,那参数量就是1000×1000×1000 = 10^9,10亿个参数。这谁受得了?
CNN用了两招来解决:
- 局部连接:每个神经元只连接输入的一个局部区域(感受野)。比如3×3的核,每个神经元只连9个输入像素。参数量瞬间降到9×1000 = 9000。
- 权值共享:同一个卷积核,在整个输入图上滑动时,权重是不变的。也就是说,检测同一个特征(比如水平边缘)的神经元,共用同一套权重。
我打个比方:全连接就像每个员工都要认识公司所有人;局部连接就像每个员工只认识自己小组的人;权值共享就像所有小组都用同一套考核标准。这样管理成本就低多了。
权值共享还有个好处——平移不变性。一个特征不管出现在图像的哪个位置,都能被同一个卷积核检测到。这在FPGA实现时特别友好,因为你可以把权重存在BRAM里,反复读取使用,不用为每个位置存不同的权重。
注意:权值共享不是所有层都适用。有些网络(比如SENet)会在通道维度上做注意力,每个通道的权重是不同的。但基础的卷积层,权值共享是默认的。
1.4 本章知识体系总览
下面这张图,是我自己画的,把本章的核心逻辑串起来了。你看一眼,应该能对卷积基础有个整体把握。
这张图把三个核心概念串起来了。卷积运算是基础操作,特征图和感受野是理解网络行为的工具,权值共享和局部连接是减少参数量的关键。这三者结合起来,才有了我们在FPGA上做加速优化的空间。
1.5 小结
这一章咱们聊了卷积的数学本质、特征图和感受野的概念、以及权值共享和局部连接为什么能大幅减少参数量。我个人觉得,这些基础概念比具体的代码实现更重要——方向对了,代码怎么写都行;方向错了,优化再狠也白搭。
我曾经见过一个团队,花三个月优化卷积核的乘法器,结果发现算法本身用的是depthwise卷积,权值共享的方式完全不同。嗯,这就是基础没打牢的代价。
下一章咱们会深入聊卷积在FPGA上的具体实现,包括行缓存、数据复用、流水线设计这些实战内容。到时候我会拿我实际做过的项目来举例,把踩过的坑都告诉你。
课后思考:如果输入是RGB三通道彩色图像,卷积核的维度应该怎么设计?输出特征图的通道数又由什么决定?想明白这个问题,你就真正理解了卷积的维度变换。