一、卷积神经网络基础
各位同学好,我是你们的讲师。今天咱们聊聊卷积神经网络的基础——说白了,就是搞明白卷积到底在算什么。
我记得刚入行那会儿,总觉得卷积神经网络是个黑盒子。后来自己动手搭过几个加速器,才慢慢摸清了门道。嗯,咱们今天就把这层窗户纸捅破。
1.1 卷积运算原理
卷积运算,本质上就是「滑动窗口做点积」。你想想看,一个小的矩阵(我们叫它卷积核)在输入数据上滑来滑去,每到一个位置,就把对应位置的数值相乘再求和。
举个最简单的例子。假设输入是一个 5×5 的图像,卷积核是 3×3 的。卷积核从左上角开始,覆盖住输入的前 3 行 3 列,然后做逐元素相乘再累加,得到一个数值。接着往右滑动一个位置,重复操作。
核心公式:
输出特征图(i, j) = Σ Σ 输入(i+m, j+n) × 卷积核(m, n)
其中 m、n 遍历卷积核的行和列。
我在项目中遇到过一个问题:很多人把卷积和互相关搞混。其实在深度学习里,我们用的「卷积」严格来说是互相关运算——卷积核没有翻转。为什么可以这样?因为卷积核的参数是学出来的,翻转不翻转,网络都能学到对应的权重。
1.2 卷积核与特征图
卷积核,也叫滤波器。每个卷积核负责提取一种特征。比如边缘、纹理、颜色变化等等。
我习惯把卷积核看作「特征探测器」。一个 3×3 的卷积核,有 9 个可学习的参数。这些参数在训练过程中不断调整,最终学会识别某种模式。
输出叫特征图。一张输入图,用 N 个不同的卷积核去卷积,就会得到 N 张特征图。每张特征图对应一种特征的响应强度。
| 概念 | 维度 | 说明 |
|---|---|---|
| 输入特征图 | H × W × C_in | 高度、宽度、输入通道数 |
| 卷积核 | K × K × C_in × C_out | 每个输出通道对应一组卷积核 |
| 输出特征图 | H_out × W_out × C_out | 经过卷积后的结果 |
避坑指南:我曾经在设计加速器时忽略了通道维度的并行处理,结果吞吐量上不去。后来把输入通道分组并行计算,性能直接翻倍。设计硬件时,通道维度的并行度一定要提前规划好。
1.3 步长与填充
步长,就是卷积核每次滑动的步数。步长为 1,卷积核一个像素一个像素地挪。步长为 2,就跳着走。
步长越大,输出特征图越小。为什么?因为滑动的次数变少了。这在硬件设计里很重要——步长直接影响计算量和数据复用率。
填充,就是在输入图像的周围补一圈零。为什么要填充?两个原因:
- 保持尺寸:不填充的话,每做一次卷积,特征图就会缩小一圈。层数多了,特征图就没了。
- 利用边缘信息:不填充的话,边缘像素只被卷积核覆盖一次,中间像素被覆盖多次。边缘信息就丢失了。
输出尺寸的计算公式:
H_out = (H_in + 2×P - K) / S + 1
W_out = (W_in + 2×P - K) / S + 1
其中 P 是填充大小,S 是步长,K 是卷积核尺寸。
注意:在 FPGA 实现中,填充意味着要多存几行数据。我见过有人为了省 BRAM 资源,把填充省掉了,结果精度掉了两个点。该花的资源不能省,尤其是边缘像素的处理。
1.4 池化层作用
池化层,说白了就是「下采样」。把特征图变小,同时保留最重要的信息。
最常见的两种池化:
- 最大池化:取窗口内的最大值。保留最强烈的响应。
- 平均池化:取窗口内的平均值。保留整体趋势。
池化层的作用有三点:
- 降维:减少参数量和计算量。比如 2×2 的最大池化,特征图尺寸直接减半。
- 防止过拟合:池化相当于一种天然的降采样,让模型更关注局部特征而非精确位置。
- 平移不变性:稍微移动一下输入,池化后的结果变化不大。这在图像识别里很有用。
我习惯把池化层放在卷积层后面。在硬件实现时,池化几乎不占什么资源——只需要比较器或者加法器。但要注意时序,尤其是最大池化的比较链,路径延迟可能会成为瓶颈。
本章知识体系
下面这张图,把卷积神经网络的基础结构串起来了。你看一遍,应该能有个整体印象。
这张图展示的就是一个典型的卷积块。输入图像先经过卷积层提取特征,然后通过 ReLU 激活函数增加非线性表达能力,最后经过池化层降维。输出的特征图就是下一层的输入。
嗯,基础概念就这些。说白了,卷积神经网络就是靠卷积核一层层提取特征,池化层一步步压缩信息,最终把原始图像变成高层的语义特征。搞懂了这些,后面咱们讲硬件加速器设计,你就能理解为什么要做数据复用、为什么要做流水线了。