
【斯坦福CS231n 学习笔记3】卷积神经网络与训练方法
主要说明了卷积神经网络与训练方法。
从全连接神经网络到卷积神经网络
特征提取的演变
Pixel Space
如果把一张图像当成一个高维向量,并且直接使用一个线性函数来预测类别分数,那么实际上就是在像素空间中使用一个线性模型来完成分类任务。这会导致在向量化后图片的局部相关性没有被模型显式利用,且该方案对几何变化极其敏感,轻微的平移、尺度变化或旋转,其像素向量都会发生剧烈的变化,但是在语义上看他们是同一个物体。此外,在像素空间中,线性可分性极差,难以通过简单的超平面来区分物体。
Image Feature
基于像素空间的缺陷,人们就想要构造一个更好的表示空间,先把图像映射到一个更有判别力的特征空间。
Lecture 5 给到的第一个例子是 Color Histogram,其思想是与其关心每一个像素的精确位置,不如统计整张图中各种颜色出现的概率。具体而言就是将 RGB 空间离散成若干个颜色区间,统计每个 bin 中像素的数量,并且将这些统计结果拼成一个向量,作为图像的表示:
这种特征在一定程度上缓解了平移问题,因为像素的位置被忽略了,但是代价是其丢失了空间结构信息,因此对形状、边缘、几何布局几乎没有任何感知能力。
Lecture 5 给到的第二个案例是 Histogram of Oriented Gradients,在自然图像中,物体的形状和结构主要体现在局部梯度方向的分布上。一个典型的 HOG 特征提取过程包括,将图像划分为小的空间单元,计算每个空间单元内的像素梯度,并且将梯度方向量化为若干个方向区间,统计每个方向区间内梯度幅值的累积,将这些结果拼接为一个长特征向量:
HOG 的本质是在利用人类先验知识强行将图像映射到一个更加线性可分的空间。
Lecture 5 给到的第三个案例是 Bag of Visual Words,将 NLP 中的词袋模型推广到视觉领域。其工作过程为首先通过从大量图像中随机采样局部 patch,使用聚类算法将 patch 聚为若干视觉词来构建视觉词典;然后对一张新图像提取其局部 patch,将每个 patch 分配到最近的视觉词,统计每个视觉词出现的次数,形成特征向量。
这种方法的局限性是特征提取过程与任务解耦,无法进行端到端的优化;并且视觉词典高度依赖于人工选择;丢失了空间信息。
全连接网络的缺陷
前面的特征提取演变说明手工特征并不够好,那就往神经网络方向靠,让网络自己来学习特征。对于一张 的 RGB 图片,在送入全连接网络之前,需要将其拉直成一个 3072 维的向量,这一操作导致了空间邻接关系被破坏,哪些像素原本相邻、原本不相邻不再是模型可以直接感知的结构属性,只能通过权重模式被间接、低效地重新学习;且对于一个大尺寸的图像,全连接层的参数量会爆炸性增长,导致计算效率低下且容易过拟合。
从模型结构的角度看,全连接神经网络就根本不适合视觉相关任务,因为其从一开始就不尊重图像的二维结构。
卷积神经网络
卷积神经网络主要包括卷积层、激活函数、池化层、全连接层。其中真正为视觉任务量身定做的是卷积层和池化层。
CNN 的核心思想是将特征提取本身变为一个可学习的过程,在前面提到的一系列传统方法中,特征提取部分都是人为设计且固定不变的,分类器才是需要学习的对象;而在 CNN 中,特征提取本身就是网络的一部分,并且可以通过反向传播端到端地优化。
卷积层主要负责的任务是在局部范围内检测模式,池化层负责逐步降低空间分辨率,同时保留关键信息,更深层的卷积通过组合浅层特征,逐渐形成更抽象、更具语义的表示,而全连接层在最后的阶段,将已经高度抽象的特征汇聚起来,完成最终的判别。
卷积层
卷积层是 CNN 的核心构建模块,其设计目的是在保留空间结构的前提下进行特征提取。
- 输入与输出:输入是一个三维张量,维度为 。输出也是一个三维张量。
- 滤波器:权重不再是一个巨大的矩阵,而是一组小型的滤波器,每个滤波器的空间尺寸 通常很小(如 或),但是在深度方向上则贯穿整个输入深度()。
所谓的滤波器,其实也就是一个特征提取器,其本质上仍然是一个权重矩阵,通过与图像进行点积运算来衡量局部区域与特定模式的相似度,通过堆叠多个滤波器,网络能够从图像中并行提取出多种多样的视觉线索。
在深度神经网络中,不同层级的滤波器会通过训练来自动学习识别不同的特征:
- 浅层滤波器:直接与原始像素交互,通常学习到的是类似于人类视觉皮层的特征,例如边缘、颜色斑点、特定方向的纹理等。在可视化后看起来会像是 Gabor 滤波器。
- 中层滤波器:基于浅层特征的组合,可能识别的是角点、圆弧、简单的形状等。
- 深层滤波器:识别复杂的语义对象。例如狗的眼睛、汽车的轮子、蜂窝状纹理等。
卷积层并没有改变“点积+偏置”的基本计算形式,而是改变了“点积发生在什么地方”。
卷积的计算过程就是取一个 的小块(patch),与卷积核做逐元素相乘并求和,加上一个偏置项,得到一个标量输出,然后将卷积核在图像的空间维度上从左向右、从上到下地滑动,在每一个空间位置重复相同的计算,最终输出一个二维的响应结果,这个二维结果就叫激活图/特征图。
假设有 个滤波器,即一层卷积层同时学习了 组不同的卷积核参数,每一组参数负责“检测”一种模式,最终得到 张二维激活图,将其沿“深度/通道”方向堆叠起来,得到输出张量的深度为 。
值得注意的是,在卷积层中,同一个卷积核的权重会被应用到图像的所有空间位置,这在结构上体现了,如果某种局部模式在图像的一个位置是有意义的,那么在其他位置也应该有意义,可以再推广到一个 batch 上:卷积操作是在 batch 维度上并行进行的,但是权重在 batch 之间是共享的。
核心超参数
卷积层的输出由下列四个超参数严格决定,假设输入宽度为 ,滤波器宽度为 ,填充为 ,步长为 :
- 滤波器数量 :决定输出张量的深度。
- 空间尺寸 :决定局部感受野的大小。
- 步长 :滤波器滑动的间隔。若 ,则代表逐像素滑动,保留较高的分辨率。若 ,则为跳跃滑动,产生下采样效果。
- 零填充 :在输入的边缘填充 0 值,目的是保持输出的空间尺寸不变,如果不填充,每次卷积后特征图都会变小。通常情况下,为了保持输入输出尺寸一致,会设置 。
最终输出的尺寸计算公式如下:
线性与非线性
卷积操作本质上仍是线性变换,因此在两个卷积层之间必须插入非线性激活函数,否则多层卷积在数学上等价于单层卷积,网络将无法学习复杂的非线性模式。
Pooling、感受野
Pooling
如果始终使用 stride=1 的卷积,并且通过零填充保持空间尺寸不变,那么随着网络不断加深,特征图在空间维度上将始终保持很大,但是其并不符合我们对视觉理解的视觉,当语义变得逐渐抽象,不再关心精确的像素级位置,而是关心是否存在这种模式。
在此背景下,引入了 Pooling layer。
Pooling layer 的目的是让表示变得更小、更可管理,在网络的浅层,关心局部结构的精确位置,而在网络逐渐变深之后,更加关心这些结构是否存在,以及它们之间的组合关系。
Max Pooling
在一个局部区域中,只保留最大的那个响应值。从“特征检测”的角度去理解,其实际上在做这样一件事:只要某个特征在这个小区域内被强烈激活过一次,那么在更粗粒度的位置也应当存在。
Pooling 同时还会降低空间分辨率与引入对小范围平移的不敏感性。降低空间分辨率是指一个 的特征图,经过一个 、stride=2 的池化后,会直接变成 ,这意味着后续层的计算量会成倍下降,使得深层网络在计算上成为可能。引入对小范围平移的不敏感性是指当一个边缘或角点在输入中发生轻微位移时,在池化窗口内仍然可能被同一个最大值捕获,从而使得高层表示对这些微小变化更加稳定,这种不变性是以牺牲精确位置信息为代价换来的。
感受野
一个神经元的感受野是指它在输入空间中所依赖的那一部分区域。在最简单的情况下,第一层卷积中一个神经元的感受野大小直接等于卷积核的空间大小,但是当将多个卷积层堆叠起来时,第二层的一个神经元依赖的是第一层的一个局部区域,而第一层的一个局部区域本身又依赖于输入中的一块区域,因此第二层神经元在输入空间中的感受野实际上是多层感受野的组合结果。
对于 stride=1、核大小为 的卷积层,每多堆叠一层,感受野在边长上就会增加 。但是如果只依靠 stride=1 的小卷积核来扩大感受野,为了让高层神经元“看到”整张输入图像,就不得不堆叠非常多的层。这在计算和优化上都不现实,因此才需要进行下采样,也就有了 pooling 或者 stride>1 的卷积层。
总而言之,Lecture 5 主要讨论了三样东西:卷积、池化、感受野。卷积负责发现局部模式;池化和下采样负责压缩与整合;感受野的逐步扩展,使得高层特征最终具备全局语义。
Lecture 6 主要是在说如何构建一个 CNN 和训练一个 CNN。
CNNs组件
- 卷积层:负责在空间局部上提取可共享的模式并逐级形成更高层的语义表征。
- 池化或其他下采样算子:负责控制空间分辨率与计算量并引入一定的鲁棒性。
- FC 层:在传统架构中主要承担“将空间表征投影到类别空间”的分类头功能。
- 激活函数:负责提供非线性以提升表征能力。
- Normalization Layers:负责将尺度特征与分布控制在更适合梯度传播与优化的范围内。
- Dropout:一个可选的正则化手段,在一些设置中通过随机屏蔽促使表征冗余与抑制特征共适应,但不是所有的现代 CNN 都依赖它。
Normalization Layers
归一化层并不是将数据强行变成某种固定分布,而是通过引入可学习参数,使得网络既能够将中间特征先规范化到更利于优化的数值尺度,又能在需要时将这些规范化后的特征重新拉伸到与平移到对任务最合适的表示空间。
其中的 和 意味着 并不是每个样本一套,而是对所有样本共享一套按特征维度定义的可学习参数,因此其既不会破坏“同一层对不同样本采用一致的表示坐标系”的需求,又允许网络在训练中自行学习,哪些维度应该被放大、哪些维度应该被压缩、以及整体应该平移到什么范围,从而避免单纯的归一化限制模型的表达能力。
此外还有其他的归一化类型:
所有这些归一化层实际上都是先基于某个维度集合计算统计量、再将激活规范化、最后用可学习参数恢复表达自由度,只不过统计量的计算域不同,从而带来对 batch size、任务形态以及训练稳定性的不同敏感性。
Dropout
Dropout 就是在每一次前向传播时都随机将一部分神经元置为零,也就是对某一层的激活向量随机乘上一个二值随机掩码,让某些维度在这一轮计算中完全不参与后续层的输入,而丢弃的概率本身也是一个需要手动设定的超参数,常见的取值大约是 0.5。
训练时不断随机屏蔽部分激活时,网络为了保持输出可用,就不得不将判别所需的信息分散地编码在多个特征上,被迫形成一种“冗余表示”,从而避免某个单独特征或几个特征成为具有决定性的东西。在更深一层上,Dropout 会抑制特征之间不健康的 co-adaptation,即某些特征只在另一些特征同时存在时才有意义、两者绑定过紧以至于一旦环境稍有变化就整体失效的现象。
从统计学习的视角来看,Dropout 可以看成是在训练一个巨大规模的模型集成,只不过模型之间共享参数,因为每一个二值掩码都对应一次前向传播中哪些单元被保留,哪些单元被屏蔽,从结构图的角度看,每个掩码都在定义一个不同的子模型,而在训练时每一步都随机采样一个子模型来更新共享参数,即实际上是在让参数同时为大量不同的子模型服务,这天然会带来一种类似于集成学习的泛化效益。
但是在模型预测的时候,就不再 Dropout,但是如果直接将训练时的随机屏蔽关掉,会遇到分布不一致的问题,巡训练时某个神经元的下游收到的输入在期望意义上会更小,测试时突然变大,就会导致输出发生偏移,因此在测试的时候需要做一个缩放,最常见的做法是用训练时的保留概率 来进行匹配:
另外一种实现是 inverted dropout,其原理是在训练时将保留下来的激活除以 ,这样在训练时的期望就已经进行了缩放,测试时可以什么都不做。
再谈激活函数
之前提到过 Sigmoid 和 ReLU 的局限性,Sigmoid 会导致梯度消失且计算量较大;ReLU 的输出不是零中心,导致后续层受到的激活分布带有偏移,可能会让某些参数更新出现更强的相关性与不对称性等;以及会出现Dead ReLU ,即在 时梯度为 0,完全不再更新。
在 ReLU 之外,还有许多变体(其中下面两个是 CNNs 中最常用的):
以 GELU 为例, (其中 是标准高斯分布),其在 0 附近的行为更加柔和,不会像 ReLU 一样在 0 点处产生硬折角。
平滑的非线性会让损失在局部更不尖锐,使得基于梯度的优化更容易稳定下降,但其计算成本更高,较小的负数仍然会导致梯度消失。
激活函数应该放在线性算子之后,例如全连接、卷积这类,这样每个线性变换先把输入投影到一个新的线性子空间,然后用非线性对投影结果进行“弯折”,从而让多层组合形成真正的非线性函数族。
CNNs演进方向
从 ILSVRC 的历年赢家中可以看到,视觉识别的主流方法在 2012 年前后发生了巨大的转变,AlexNet 为代表的 CNN 开始崛起,并且网络深度迅速增长,现代 CNN 架构演进就是朝着“更深的层级表征+更可训练的结构设计”方向进行发展。
以 VGGNet 为例,通过一致而可重复的设计原则将网络堆深,把卷积核尺寸统一向更小的 靠拢,通过连续堆叠三层 stride=1 的 卷积,在有效感受野层面可以达到 卷积的覆盖范围,这种替代并不是单纯的“等效复刻”,因为三层 的堆叠在中间插入了更多非线性,从而让同样感受野的映射变得更加富有表达力,并且在参数量层面往往更加经济。
但是当卷积网络堆叠层数越来越高,优化难度也越来越高,模型更深并不必然更强,训练误差都可能会变得更差,这不是因为模型容量不够,而是因为梯度与优化路径出现了结构性问题,如果模型架构没有提供一种让信息与梯度更容易跨层流动的机制,那么深度很快就会从资产变为负债。
为了解决这个问题,出现了残差连接,假设 是我们希望网络拟合的目标的底层映射,传统的网络是直接尝试让网络层去拟合 ,而残差网络则提出,如果想要学习 ,不如将目标分解,让非线性层去拟合残差 。于是原始的映射就变为了 ,用一个通俗的说法就是,如果这一层不知道能学到什么,那么就原封不动地输出原始的输入 ,而不是乱改,因为在传统的网络中,求解器学习一组非线性权重使其逼近恒等变换矩阵是非常困难的,而学习 则相对容易得多。
基于之前的假设,ResNet 的基本单元——残差块诞生了,其由两条路径组成,残差路径包括权重层,记为 ,以及捷径连接,直接传递输入 。
输出公式为
其中 是 ReLU 激活函数,最后的 ReLU 操作是在加法操作之后进行的。
从单个残差块到整个网络设计,就是在不断堆叠残差块,基础版本中每个残差块由两层 卷积构成,同时网络会在若干位置“周期性地”将通道数加倍,并且使用 stride=2 做空间下采样,从而将激活体积减半并控制计算量,此外在网络最前面还会有一个额外的起始卷积层负责最初的特征抽取与尺度变换。
推广到更广泛的梯度匹配,在 中, 和 的维度必须一致才能够相加,如果维度发生变化,捷径需要进行线性投影:
通常由 stride=2 的 卷积实现,用于调整 的维度。(当然还是要具体情况具体分析)。
从梯度流的角度看,加入忽略 BN 和 ReLU,考虑一个堆叠了 个残差块的简化模型:
利用递归关系,可以将任意深层 的输出 表示为浅层 的输入 加上中间所有残差函数的和:
这表明 ResNet 具有加性结构,而不是传统网络的乘性结构。
假设损失函数为 ,根据链式法则,计算关于 的梯度:
将求和公式带入 ,得到:
最终的梯度公式为:
由于 1 的存在,梯度可以通过 1 直接传回任意浅层 ,并且即便中间权重混乱导致 很小,总梯度也不会消失。
权重初始化
权重初始化并不是随便填一个随机数就行,其是深层网络能否稳定训练的前置条件。在网络刚开始训练时,如果前向传播的激活分布在层与层之间迅速衰减到接近 0,或者迅速爆炸到极大值,那么反向传播的梯度也会同步出现消失或爆炸的倾向。
初始化权重太小
随着层数的加深,激活会越来越接近 0, 网络在初始阶段几乎输出不了有区分度的新号,反向传播时也更容易得到非常小的梯度,于是训练会显著变慢。
初始化权重太大
如果把权重整体放大,那么前向传播会出现相反的情形:激活在层与层之间迅速放大并爆炸,一旦激活爆炸,数值会变得非常不稳定,反向传播的梯度也更容易爆炸,训练会出现剧烈震荡、loss 变成 NaN,或者对学习率极端敏感。
Kaiming / MSRA Initialization
修复的方案取决于 layer 的 size,即初始化尺度需要显式依赖于这一层的输入维度,只有这样才能在统计意义上让每一层输出的激活分布保持在一个相对稳定的范围中,而不是随着深度系统性地缩小或放大。Kaiming/MSRA Initialization 对 ReLU 网络的一个常用修正就是将权重初始化的标准差设置为
其中 是这一层的输入维度,使用该尺度初始化后,激活在各层之间不会系统性衰减或爆炸,从而让训练在数值上更加稳定。
数据预处理
对图像做归一化处理的默认做法就是对每个通道分别减去该通道的均值,再除以该通道的标准差,这套做法几乎适用于所有现代模型。
这样可以在训练开始之前将输入分布规整化,从而避免网络在第一层就被迫去处理一些完全可以提前消除的数值尺度问题:自然图像的三个通道往往具有不同亮度范围与统计偏置,如果直接将原始像素喂给网络,那么第一层卷积核在随机初始化状态下会接收一个非零中心、尺度不一致且跨通道统计不平衡的输入分布,这会让优化器在一开始就处在一个条件数更差的区域中工作,相反,如果先把每个通道都变成“近似零均值、单位方差”的分布,从梯度下降的角度看,模型的参数更新更接近一种各向同性的局部几何,从而让学习率的可用空间更宽、收敛更稳定,并且更不容易出现某些通道主导梯度、另一些通道几乎学不到东西的现象。
对每个通道 ,先在整个训练集上计算该通道的均值 与标准差 ,其中统计通常是对所有训练图像的该通道所有像素求总体均值/方差,然后对任意输入图像的该通道像素 ,做
数据增强
数据增强是对抗过拟合最简单、最有效也最必不可少的手段之一。通常是在训练阶段往系统中加入某种随机性,测试阶段再将这种随机性的影响通过平均消除掉。
每次迭代中从数据集中取出一张原图与标签,在送入 CNN 之前对图像做一次随机变换,然后再用变换后的图像去计算 loss 并反向传播,这样模型在训练中看到的就是一个被随机扰动的输入分布,而不是一个固定的训练集,等价于将训练数据覆盖范围在不改变标签语义的前提下扩展了,并且迫使模型学习到对这些扰动更稳定的判别特征。
Lecture 6 在此处提到了几种数据增强方法:
- 水平翻转:对很多自然图像分类任务而言,左右翻转不会改变语义类别,却会显著改变像素排列,以非常低的代价将对左右镜像的鲁棒性这一需求注入了训练过程,使得模型不至于将物体朝向这种因素当作过强的判别线索。
- 随机裁剪与尺度抖动:训练时从某个区间中选择一个短边长度 L,将训练图像 resize 到短边等于 L,再从中随机采样一个 的 patch 作为网络输入,一方面是通过随机尺度模型让模型不要把“物体在画面里占多大的比例”作为稳定线索,另一方面通过随机裁剪让模型让模型在不同局部视野下仍然要输出一致类别,从而把对平移、裁剪、以及尺度变化的鲁棒性以内隐的方式写进了训练分布中。
- 颜色对比度:通过随机化对比度与亮度来制造颜色与光照条件的扰动,从而迫使模型将注意力更多放在形状、纹理、结构等相对更加“语义稳定”的线索上,而不是过度依赖于某一种固定的曝光或色彩统计。
- Cutout:训练时将图像的随机区域直接置零,而测试时仍然使用完整的图像,可以将 Cutout 视为一种在输入空间做的 dropout,通过随机抹除迫使模型不要将判别完全押注在某个局部证据上,从而提升对遮挡与局部缺失的鲁棒性。该方法在小数据集上往往效果很好,但是在大规模数据集上相对不常见。
迁移学习
当目标任务数据量不大、训练预算有限、而且不想把大量时间浪费在从头训练与反复调参上,最稳妥的策略不是重新发明一个网络,而是利用在大规模数据集上已经学习到的通用视觉表征,将其迁移到任务上。
迁移学习是否有效、以及应该采用哪种迁移方式,主要归结为两个维度的共同作用——目标数据集有多大,以及目标数据与预训练数据有多相似。数据量决定了能否可靠地拟合大量参数而不至于严重过拟合,相似度则决定预训练学到的特征是否刚好匹配任务语义与成像分布,从而决定需要改动多少层才能将模型调到正确的工作点。
- 如果目标数据集很小且与预训练数据相似,则最常见且最稳定的做法是将预训练网络当成一个“固定的特征提取器”,即冻结大部分 backbone 的参数,只训练最后的线性分类头或一个很浅的任务头。因为此时预训练特征往往已经包含了足够通用的边缘、纹理、局部形状与中层部件表征,过度微调反而容易将这些通用特征破坏,并且在小数据上过拟合。
- 如果目标数据集很小且与预训练数据差异明显,则从预训练权重出发,微调更多层,至少要微调更靠近输出端的高层语义部分,因为高层特征更任务相关,也更需要适配新的予以边界。
- 如果目标数据集很大时,更大范围的 fine-tuning 甚至从头训练才更为可行。
超参数选择
- Step 1:先验证搭建起来的训练 pipeline在数值上是合理的。
- Step 2:检查模型能不能把极小数据集拟合到极低的训练误差,因为如果选择的模型容量与损失实现没有根本性问题,那么一个正确实现的网络在小规模数据上能够明显过拟合。
- Step 3:沿用上一步的架构,使用全部的训练数据,设置较小的权重衰减,寻找一个能在大约 100 此迭代内让 loss 显著下降的学习率。学习率首先是一个数量级问题,其次才是精细调整问题。
- Step 4:使用粗网格扫描一批超参数组合,并且每个组合只训练约 1-5 个 epoch,以最小的预算快速淘汰明显不行的区域。
- Step 5:围绕表现最好的一批做 refine,并且训练更久。
- Step 6:看损失曲线和准确率曲线,如果准确率持续上升,说明只是训练时间不够;而如果训练和验证之间偏差较大,说明出现了过拟合,需要增强正则化。
搜索策略
当只有少数的超参数真正决定性能时,随机搜索往往比规则网格要更加高效,因为网格会在不重要的维度上浪费大量的预算,随机采样更有可能覆盖到重要维度的有效区间。
总而言之,Lecture 5 和 Lecture 6 都聚焦于卷积神经网络。Lecture 5 系统建立了卷积神经网络的核心语言:卷积层通过局部连接与参数共享在保持空间结构的同时提取特征,padding 与 stride 决定输出特征图的尺寸与采样密度,池化或 stride 卷积实现下采样以控制计算量并扩大有效感受野,最终形成“卷积—非线性—下采样—更高层表征”的层级抽象框架。Lecture 6 在此基础上聚焦“可训练性与工程化训练”:从激活函数、归一化与 Dropout 等组件出发解释深层网络为何容易优化困难,并通过 VGG/ResNet 等架构展示深度扩展与残差连接带来的稳定训练;同时从初始化(如 Kaiming)、数据预处理(按通道标准化)、数据增强(训练注入随机性、测试可做多视角平均)、迁移学习(数据不足时优先用预训练再微调)到超参数选择流程(先做 sanity check 与小样本过拟合,再粗到细搜索并用曲线诊断)给出一套可复用的训练方法论,从而把“网络怎么搭”与“网络怎么训”统一到同一条实践路径上。
参考如下:
https://cs231n.stanford.edu/index.html
