一、卷积神经网络基础:从全连接层到卷积层的演进
大家好,我是你们的讲师。今天咱们聊聊卷积神经网络的基础,特别是从全连接层到卷积层这条演进路线。说实话,我刚入行那会儿,对卷积的理解也就停留在“哦,就是做滤波嘛”。直到真正动手做硬件加速器,才发现这里面的门道深着呢。
1.1 为什么要有卷积层?
先问大家一个问题:全连接层有什么毛病?
我举个例子。假设你有一张 32x32 的图片,输入就是 1024 个像素点。如果第一层用全连接,每个神经元都要连这 1024 个输入。第二层再来 512 个神经元,你算算参数量——1024×512,五十多万个参数。这还只是两层。
更麻烦的是,图片里的特征是有空间结构的。猫的眼睛在左边,耳朵在右边,这种位置关系全连接层根本学不到。它把每个像素当成独立的特征,完全忽略了“相邻像素之间有关系”这个事实。
所以,卷积层就来了。它的核心思想就两条:
- 局部连接:每个神经元只看输入的一小块区域,比如 3x3 或 5x5
- 权值共享:同一张特征图上的所有神经元,共用同一组权重
这两招一用,参数量直接降了几个数量级。我在做第一个卷积加速器项目时,对比过全连接和卷积的硬件开销——同样处理 32x32 的输入,全连接需要的内存带宽是卷积的 20 倍以上。嗯,这就是为什么现在的视觉模型几乎清一色用卷积。
1.2 卷积的数学定义
卷积的数学公式其实很简单。对于二维输入,标准卷积可以写成:
y[m][n] = Σ_i Σ_j x[m+i][n+j] · w[i][j] + b
其中 x 是输入特征图,w 是卷积核,b 是偏置。i 和 j 遍历卷积核的尺寸。
注意,这里我写的是“互相关”的形式,不是严格意义上的数学卷积。在深度学习里,我们通常不翻转卷积核,直接做点乘求和。为什么?说白了,翻转不翻转对网络学习来说没区别——反正权重是学出来的,翻转了也能学到对应的模式。
我个人习惯把卷积操作拆成三步理解:
- 滑动窗口:卷积核在输入上按步长滑动
- 点乘求和:每个窗口内,输入和卷积核对应位置相乘,再全部加起来
- 加偏置:加上一个可学习的偏置项
这里有个坑,我踩过。步长 stride 和填充 padding 的配合很容易搞错。比如 stride=2 时,输出尺寸是 (输入尺寸 - 卷积核尺寸 + 2×padding) / stride + 1。我曾经因为 padding 算错,导致加速器的地址生成逻辑全乱了,调了整整两天。
1.3 特征图的概念
特征图,说白了就是卷积层输出的结果。每一张特征图对应一个卷积核的响应。比如你用 64 个卷积核去卷输入,就会得到 64 张特征图。
特征图有个很有意思的性质:越往深层,特征越抽象。浅层的特征图通常检测边缘、纹理、颜色块;中层的特征图能识别出眼睛、鼻子这样的部件;深层的特征图则对应整个物体的概念。
我记得有一次调试一个目标检测加速器,发现网络在某个场景下总是漏检。后来把中间层的特征图 dump 出来一看,发现浅层特征图里目标区域的响应很弱。问题出在前端预处理——输入图像的对比度不够,导致边缘检测失效。从那以后,我养成了一个习惯:做硬件加速器之前,先跑一遍软件仿真,把各层特征图的统计特性摸清楚。
特征图的几个关键参数:
| 参数 | 含义 | 硬件影响 |
|---|---|---|
| H × W | 特征图的高和宽 | 决定片上 buffer 的大小 |
| C_in | 输入通道数 | 影响乘法器阵列的并行度 |
| C_out | 输出通道数 | 决定输出 buffer 和累加器的数量 |
| stride | 滑动步长 | 影响数据读取的地址跳变 |
1.4 卷积加速器的应用场景
卷积加速器现在几乎无处不在。我简单列几个典型的场景:
- 边缘端推理:手机、摄像头、无人机上的实时目标检测。这类场景对功耗和面积极其敏感,通常用定点数(INT8 甚至 INT4)做推理。
- 云端训练:数据中心里的 GPU 或专用 AI 芯片。训练需要高精度(FP32 或 BF16),而且对吞吐量的要求极高。
- 自动驾驶:车规级的卷积加速器,要求低延迟(毫秒级)和高可靠性。我参与过一个车载项目,芯片的工作温度范围是 -40°C 到 125°C,时序收敛折腾得够呛。
- 医疗影像:CT、MRI 图像的处理。这类场景对精度要求极高,而且输入尺寸通常很大(比如 512×512 甚至更大),对片外带宽是个考验。
1.5 设计挑战
卷积加速器的设计挑战,我总结成四个字:数据搬运。
你想想看,卷积的计算本身很简单——乘加运算。但为了喂饱计算单元,你得不停地从片外 DRAM 搬数据到片上 SRAM,再从 SRAM 搬到计算阵列。这个搬运过程的效率,直接决定了加速器的性能。
具体来说,有这几个核心挑战:
- 数据复用:输入特征图、卷积核、输出特征图,三者都有复用机会。怎么设计数据流来最大化复用,减少片外访问?
- 计算阵列的利用率:乘法器阵列能不能跑满?如果因为数据没准备好导致计算单元空转,那加速器的效率就大打折扣。
- 带宽瓶颈:片外 DRAM 的带宽是有限的。我曾经遇到一个项目,计算阵列的理论算力是 1 TOPS,但实际只能跑到 300 GOPS——因为带宽不够,数据搬不过来。
- 精度与功耗的权衡:用 FP32 精度高但功耗大,用 INT8 功耗低但可能掉精度。怎么选?取决于你的应用。
嗯,这些挑战我们会在后面的章节逐一展开。今天先把基础打牢。
本章小结:卷积层通过局部连接和权值共享,解决了全连接层参数量大、忽略空间结构的问题。卷积的数学本质是滑动窗口内的点乘求和。特征图是卷积层的输出,越深层特征越抽象。卷积加速器的设计核心是解决数据搬运的效率问题。
好了,这一章就到这里。下一章我们会深入卷积加速器的硬件架构,聊聊计算阵列到底该怎么设计。