在计算机视觉领域内,卷积神经网络无疑是一项奠基性的工作。我们当今火热的图像识别、目标检测、乃至自动驾驶都是建立在它的基础上的。
因此,本篇文章三水儿将带大家深入卷积神经网络的世界,理解CNN的工作原理,最终让你能根据自己的数据特点,合理地设计和调整网络结构,从“调包侠”向“架构师”迈出坚实的一步。
引言:为什么要专门为图像设计网络?
在我们刚学完全连接神经网络(Fully Connected Neural Network, FNN)时,一个自然的想法是:能否用它来分辨一张图片是猫猫还是狗狗?这与用FNN解决MNIST手写数字识别问题似乎很相似,毕竟FNN在该任务上已能达到97%以上的高准确率。
但当我们将如下的224x224像素的彩色图片输入FNN时,很快我们就发现了几个严重的问题。
首先是参数量的灾难,一张我们当前看起来很小的彩色图片,共有个像素。如果输入层后的第一个隐藏层设置1000个神经元,也需要1.5亿个参数。要知道,2012年赢得ImageNet挑战赛的里程碑式模型AlexNet,其总参数量也“仅”为6000万左右。仅仅一层就远超一个复杂网络,这在计算资源和效率上是难以接受的。
更重要的是,FNN在处理图像时,会先将图像“展平”(Flatten)成一个长向量。这个操作破坏了图像内在的空间结构信息。像素之间并非孤立的,相邻的像素点紧密相关,共同构成了物体的边缘、纹理和形状。FNN却将每个像素视为独立的特征,完全忽略了这种空间关联性。
最后对于FNN而言,如果我们调皮的小猫在图上稍微挪挪,对于FNN来说,这是一个全新的、需要重新学习的输入模式,因为它只认得特定位置的像素组合。
让我们总结一下FNN处理图像时的三大痛点:
- 参数量爆炸:计算成本过高,且容易过拟合。
- 空间信息丢失:忽略了像素间的局部相关性。
- 缺乏平移不变性:对物体的位置变化非常敏感。
面对这些挑战,研究者们从生物视觉皮层中获得灵感,设计出了CNN。它通过其独特的局部感知、参数共享、下采样等机制,优雅地解决了上述所有问题,成为了计算机视觉领域当之无愧的基石。
揭开CNN的神秘面纱—卷积神经网络的基本组成
首先,我们来介绍一下卷积神经网络的基本组成,其网络一般由类似下图的多个卷积模块叠加后添加几个全连接层组成。每个卷积模块一般包含三个核心组件:卷积层(Convolution),激活层(下图中为ReLU),池化层(Max pooling)。
CNN是如何运行的?
我们可以将CNN的运行过程概括为两大步:
第一步:特征提取,堆叠多个卷积模块。
- 卷积操作
- 在初始化时,我们会在每层设置多个卷积核。
- 卷积核会通过反向传播算法自适应的调整出最适合这当前特征的卷积核。
- 这些卷积核会在图片上滑动运算,运算后我们得到的也就是对某一特征强化过后的图片,我们一般称为特征图(Feature Map)。因为每层有多个卷积核也就会有一组特征图,每张图片体现的是不同的特征。
- ReLU
- 对这些生成的特征图做ReLU的操作。
- Pooling
- 在初始化时,我们会设置每层Pooling层的大小。他也是一个窗口。
- 我们按照特定的规则从窗口内取出一个值。
- 让Pooling窗口在经过ReLU的特征图上滑动,我们得到一个缩小的特征图。
然后就是重复堆叠这种结构最终我们得到一个高度抽象的特征图。
第二步:分类决策,用FNN来分类
-
展平
我们讲上一步得到的特征图一张一张的展平然后首尾相连拼接到一起
-
全连接层
全连接层对刚才展品的多个特征进行汇总,学习这些特征间的非线性组合关系。
-
输出分类
根据设定的类别,输出当前这张图片的概率。
即如图所示:
大家可以去这个网站自己玩一下,里面还有3D的图,以及各层的隐藏,让你更快速的理解:
2D Visualization of a Convolutional Neural Network
卷积神经网络的核心——卷积模块详解
现在,让我们逐一拆解卷积模块的每个组件,看看它们是如何协同工作,解决FNN的三大痛点的。
在这里我们再强调一下FNN的三个问题
- 参数量大
- 空间信息丢失
- 平移不变性
1.卷积层(Convolutional Layer):CNN的灵魂
解决FNN的问题:参数量和空间信息
核心思想:参数共享和局部感知
- 局部感知:与FNN每个神经元连接所有输入像素不同,卷积层的神经元只关注输入图像的一小块区域,这个区域被称为“感受野”(Receptive Field)。这个神经元就像一个拿着放大镜的侦探,只检查图像的局部细节。用来进行检查的“放大镜”,就是卷积核(Kernel/Filter)。
- 参数共享:一个卷积核(比如用于检测垂直边缘的核)在图像的左上角和右下角使用的权重是完全相同的。这意味着,无论物体出现在图像的哪个位置,我们都可以用同一个特征检测器去识别它。这一机制,将参数量从“亿”级别骤降到了“万”甚至“千”级别。或者说图像中的基础视觉特征其实是相对有限的(即我们在一层网络中使用多个卷积核),卷积核帮助我们把特征从图像的位置上剥离了出来。这样就使得原本与位置信息强绑定特征解耦了,无论这个特征在哪我都可以用一个小的卷积核把他提取出来。
卷积核在神经网络的工作方式为如上图所示。我们可以看到其逐步的划过图像计算各位置的卷积结果。下面我们来介绍卷积层的几个重要概念。
-
卷积核(Kernel)
- 作用:卷积核本质上是一个小型的、可学习的权重矩阵,是特征探测器。在训练中,网络通过反向传播算法和梯度下降,不断更新卷积核的权重,使其能够自动地从数据中提取出对完成最终任务最有用的特征。详细可见我这篇文章:图像处理中的卷积究竟是什么?
- 输出图像尺寸:
- k为卷积核大小
- n为图像大小
为什么要有多个卷积核?
我们之前讲过卷积核的主要作用是特征提取和特征筛选,一张图像中的特征会有多个,因此我们需要多个卷积核来提取和筛选不同的特征。举个不恰当的例子就是某个卷积核是负责提取鼻子的,某个卷积核是负责提取眼睛的。(在浅层网络中一般为边缘纹理特征)
-
填充(Padding)
我们细心观察会发现按照我们上面的卷积方法边缘处的计算次数是小于中间的,并且卷积输出的特征图的尺寸是小于原始图片的。
这就导致了卷积神经网络中存在两个问题
- **边缘信息丢失:**边缘计算次数远小于中心的像素,导致边缘像素被利用的机会远少于中心像素。
- 无法构建深层网络: 特征图是逐渐缩小的,我们没有办法堆叠很深的设计网络。
为了解决这两个问题我们引入了参数Padding,即我们在图像的四周各补两圈0。我们设输入图像的尺寸为,我们选择大小的卷积核。添加padding后输入的尺寸变成了。根据卷积输出图像的大小的计算公式即。我们使得输出的特征图保持原本的大小。
保持原始图像大小的padding计算公式:
- p为填充大小
- k为卷积核大小
-
步幅(Stride)
- 作用:Stride定义了卷积核在输入图像上移动的步长,其可以扩大感受野,减少计算量(其他条件不变时特征图的大小变小了)。
- 影响:
- Stride=1:输出尺寸变化很小,保留了大部分空间信息。计算量和内存占用会升高。感受野增长变快,网络会更关注局部和相对位置信息。
- Stride>1:输出尺寸会急剧的减小。我们实现了下采样,主动丢弃了精细的空间信息。计算量和内存占用会减小。感受野增长变快,更容易整合全局信息,捕捉到大尺度目标和上下文的关系。
- 添加步幅后的输出特征图尺寸:
- n为图像大小
- p为填充大小
- k卷积核大小
- s为图像大小
2.激活层(Activation Function):引入非线性
核心思想:引入非线形变换,提升模型表达能力。
卷积本身是一种线性运算(加权求和)。如果只堆叠卷积层,无论多少层,其效果等同于一个单一的线性变换,无法学习现实世界中复杂的非线性关系。因此,我们必须在每个卷积层后引入非线性激活函数(如ReLU),赋予网络拟合复杂模式的能力。
3.池化层(Pooling Layer):降维与增强不变性
解决FNN的问题:引入平移不变性、减少计算量核心思想:降低特征维度、给予网络平移不变性
池化层通常紧跟在激活层之后,其作用是对特征图进行“压缩”,以降低其空间尺寸。
- 最大池化 (Max Pooling):最常用的池化方式。它将特征图划分为若干个不重叠的区域,并只取每个区域中的最大值作为输出。这相当于只保留该区域最显著的特征,忽略不重要的细节。
- 平均池化 (Average Pooling):计算每个区域的平均值作为输出。
池化的两大作用:
- 降低维度:显著减小特征图的尺寸,从而大幅降低后续网络层的参数数量和计算复杂度,有效控制过拟合。
- 增强平移不变性:由于池化操作只关心某个特征是否在区域内出现(最大值),而不是它出现的精确位置,因此即使物体在图像中有微小的位移,池化后的输出也往往是相同的。这使得网络对物体的姿态和位置变化更加鲁棒。
💡 思考:为什么是“卷积→激活→池化”的顺序?
这个顺序符合高效的信息处理逻辑。
- 卷积是线性操作,用于特征提取。
- 激活(如ReLU)引入非线性,对提取出的特征进行筛选,增强模型的表达能力。如果先池化再激活,可能会丢失重要的负值信息(例如,一个很大的负数响应在池化中可能被忽略,但它本可以提供有用的特征信息;而如果先激活,它会变为0,这个信息就被合理地抑制了)。
- 池化在特征经过非线性筛选后进行降维和汇总,保留最显著的激活特征,从而实现不变性并减少计算量。
卷积神经网路的理论基础——感受野
“感受野”(Receptive Field)是CNN中一个极其重要的概念,它指的是输出特征图上的一个神经元,能够“看到”的原始输入图像的区域大小。
从生物视觉出发
CNN的灵感直接来源于对猫和猴子的视觉皮层的研究。生物视觉系统就是层次化的:
- V1区(初级视觉皮层)的细胞对局部的、简单的刺激(如特定方向的边缘)有反应。
- V2、V4区的细胞则整合V1区的信号,对更复杂的模式(如轮廓、简单形状)有反应。
- 最终,IT区(下颞叶皮层)的细胞能够对非常复杂的模式(如一张脸、一个苹果)产生反应,其感受野也非常大。
进入卷积神经网络
CNN完美地模拟了这一机制。随着网络层数的加深,每一层神经元的感受野都在逐渐扩大。
- 浅层网络:感受野较小,关注的是边缘、颜色、纹理等局部细节。
- 深层网络:拥有巨大的感受野,可以将浅层提取的众多局部细节组合起来,形成更抽象、更具语义的特征(如“猫耳朵”、“汽车轮廓”)。
这就是CNN能够从像素级的局部信息,逐步构建出高级语义理解的核心原理。
为了方便理解我们来看一张图:
如图所示,输出中的绿色可以“看到”输入中几乎全部的细节。这也就意味着,随着网络层数的加深,感受野会逐渐的扩大。这就是CNN能够从局部到全局,层次化提取特征的关键。
从数据出发,像架构师一样优化你的网络
理解了上述所有组件后,我们便可以告别盲目地堆叠网络层,开始有目的地进行结构设计。
- 网络宽度(每层的卷积核数量)原则:特征的复杂性和多样性决定了所需卷积核的数量。 实践:对于像CIFAR-10这样的小尺寸、类别差异明显的图像数据集,初始卷积层使用32或64个卷积核通常就足够了。但对于ImageNet这种包含1000个细粒度类别的大型数据集,初始层通常需要更多的卷积核(如64或128个)来捕捉更丰富的底层特征。
- 网络深度(层数与感受野)原则:目标物体的大小和上下文依赖性决定了所需的感受野,进而影响网络深度。 实践:如果你在做一个医疗影像的肿瘤检测任务,肿瘤可能只占图像的一小部分,但其诊断往往需要结合周围的组织信息。这时,你需要一个足够深的网络来获得足够大的最终感受野,以整合全局上下文来做出判断。相反,如果你只是做交通标志识别,目标物体大小相对固定且特征明显,一个类似LeNet-5的较浅网络可能就已足够,过深的网络反而容易导致过拟合和计算浪费。
- 其他考量
- 卷积核大小:现代网络(如VGG)普遍证明,使用多个小的 卷积核堆叠,比使用一个大的 或 卷积核,能在获得相同感受野的同时,引入更多的非线性,并减少参数量。
- 池化的替代:在一些现代网络(如ResNet)中,设计者有时会用步幅为2的卷积层来替代池化层,以实现下采样的目的。这样做可以避免池化层带来的信息损失,但会增加计算成本。
我们从FNN的局限性出发,深入剖析了CNN的核心组件——卷积、激活与池化,理解了它们如何通过局部连接和参数共享解决了参数爆炸问题,并利用层次化的结构从数据中自主学习特征。
希望通过这篇博客,你不再将CNN视为一个神秘的黑箱。当你理解了感受野的扩张、特征的层次化提取以及深度与宽度的权衡之后,你便拥有了像架构师一样思考的能力。从今天起,尝试不再满足于调用model.fit(),而是开始有目的地、有依据地去定制和优化你的网络结构,真正解决你所面临的实际问题。
这,才是从“知道”到“掌握”的飞跃。让我们一起变得更强!
文章首发gzh【破茧plan】,欢迎关注。