首页 / 资讯中心 / 文章详情

卷积神经网络CNN核心原理与训练实战:从全连接到卷积的架构跃迁

卷积神经网络CNN核心原理与训练实战:从全连接到卷积的架构跃迁 ★ FEATURED ARTICLE
先说一个我自己复习时反复确认过很多次的结论卷积神经网络CNN真正厉害的地方不是它有多少层、用了多少参数而是它在一开始就从架构层面绕开了全连接网络在图像任务上的两大死穴——参数爆炸和空间结构丢失。2017年CS231n第五讲花了大量篇幅把这件事拆开揉碎地讲我当时没有完全听进去后来自己复现了几个经典结构才彻底想明白。这篇内容就是我对这一讲笔记的完整整理把卷积层、池化层、全连接层、激活函数、训练技巧串起来讲适合正在学深度视觉、准备复现模型、或者刚被各种卷积术语绕晕的同学。先说好这不是课程视频的逐句翻译而是我自己消化之后重新组织的版本。重点放在“为什么这么做”和“动手时要注意什么”上公式会有但每个公式我都会想办法解释到你能自己推出来。1. 从全连接层到卷积层为什么非改不可1.1 一张图片被全连接层“拉直”之后丢掉了什么假设输入是一张224×224的RGB图片数据形状是224×224×3。传统全连接层的做法很直接把整个三维数组展平成一维向量长度是224×224×3150528然后接入一个拥有若干神经元的隐层。听起来没什么大不了但如果你让这个隐层有512个神经元权重矩阵的规模就是150528×512约7700万个参数这还只是一层。更关键的问题不是参数量而是结构。图像是三维的像素之间存在天然的空间邻域关系相邻像素往往颜色相近、纹理连续猫的眼睛总是出现在脸的局部区域。展平成一维向量之后这种邻域关系被彻底打乱网络看到的只是“一长串数字”每个位置之间的关系完全失去了空间含义。你等于把一张照片撕成碎片再硬塞给一个只会数顺序的模型。课程里反复强调一个类比对图像识别来说位置相近的像素比距离远的像素关系大得多这是最基本的先验。全连接层没有利用这个先验他默认所有输入特征是全连接的、地位对等的这在图像任务上是一种巨大的浪费。1.2 参数共享和局部连接是怎么救场的卷积层就是针对这两个问题设计的。第一是局部连接。每个卷积核不再连接整张输入图而是只连接到输入的一个局部区域这个区域的大小就是卷积核的尺寸比如3×3或5×5。这和人的视觉系统很像看东西的时候也是一小块一小块地看局部纹理先被检测出来再由后面更深的层组合成更大的语义概念。第二是参数共享。同一个卷积核会游走整张输入图也就是说在左上角检测“横向边缘”的那组权重也会被用在右下角。为什么合理因为图像中同一个特征可能出现在任意位置一只猫在图片左上角和右下角本质上都是“猫”对应的边缘纹理并没有变。既然特征本身一样检测特征的那组参数也没必要区分位置。这一下子把参数量从几千万压到几百。你可以拿全连接和卷积做个对比直观感受一下差距结构处理224×224×3图像的典型参数量是否保留空间关系全连接层隐层512节点约7700万否展平成一维卷积层6个3×3卷积核6×(3×3×31)168是按局部窗口扫描168对7700万差了约46万倍。课程里常说的“卷积让深度学习在视觉上变得可行”说的就是这个差距。参数少了过拟合风险随之降低训练需要的样本量和计算资源也友好很多。1.3 平移等变性换个位置特征依然能被抓住参数共享还带来一个隐藏的好处卷积天然具备平移等变性。意思是输入图像里的一个目标平移一段距离卷积层输出的特征图也会跟着平移但特征本身仍然会被检测出来。这对图像任务太重要了因为我们不愿意模型因为猫从左边挪到右边就从“猫”变成“未知物体”。不过我后来想明白一件事只有卷积层给的是“等变”而不是“不变”。真正让你对平移不敏感的是后面的池化层它把局部区域内的响应做聚合才给了分类器一定的“无论出现在哪都能认出来”的宽容度。所以看这章的时候最好把卷积和池化当成一对搭档来理解单独拎出来看任何一个都解释不了完整的网络行为。2. 卷积层的输出尺寸、超参数与感受野亲手算一次就忘不掉2.1 卷积核到底在算什么一个卷积核本质上是一个小的权重张量。以输入RGB三通道图为例一个3×3卷积核的形状是3×3×3空间大小3×3通道数必须与输入一致。这个卷积核覆盖在输入的某个位置上和覆盖区域内的27个数值做逐元素乘法再求和加上一个偏置得到输出特征图上的一个数。这个数代表什么直观理解就是“这个局部区域与卷积核的匹配程度”。如果卷积核学到的是“横向渐变边缘”那么它在图像边缘区域就会输出较大的响应在平滑区域输出接近0。多个卷积核并存时每个核相当于一个特征检测器有多少个卷积核就有多少个输出特征图也就是输出通道数。课程把输出通道数叫深度这个叫法容易和网络层数混淆我在笔记里习惯叫“特征图数量”。2.2 三个超参数决定输出有多大多密卷积层有三个核心超参数深度卷积核个数、步长stride和零填充zero-padding。步长决定了卷积核每次滑动几步。步长为1时卷积核挨个移动步长为2时输出特征图尺寸直接减半左右。填充则是在输入边缘补一圈0目的有两个一是防止卷积把特征图越卷越小信息快速缩水二是让边缘像素也有机会被卷积核中心覆盖到毕竟边缘元素在无填充时参与的卷积次数会比中间元素少信息地位不对等。输出尺寸公式在课程里写得很清楚输出边长 (N - F 2P) / S 1。其中N是输入边长F是卷积核空间尺寸P是填充量S是步长。我在纸上算了三年才真正理解这个公式每个项的意图N - F 是“卷积核中心还能走多远”加上2P是因为填充让输入边长变长除以S是看按步长能取几个位置最后加1是因为初始位置也要算一次。我自己写了个小函数每次搭网络都过一遍def conv_out_size(N, F, S, P0): return (N - F 2 * P) // S 1 # 输入32x325x5卷积核步长1无填充 print(conv_out_size(32, 5, 1, 0)) # 28 # 输入32x325x5卷积核步长1填充2 print(conv_out_size(32, 5, 1, 2)) # 32 # 输入7x73x3卷积核步长2无填充 print(conv_out_size(7, 3, 2, 0)) # 3搭网络时最怕的就是这里算错。常见配置是卷积核3×3、步长1、填充1输出尺寸保持不变或者卷积核5×5、步长1、填充2输出尺寸也保持不变。这样在设计串联结构时你可以清晰控制哪些层改变空间尺寸哪些层只做特征变换。2.3 感受野与堆叠小卷积核的哲学感受野是另一个绕不开的概念意思是输出特征图上一个像素点对应回输入图像上的多大区域。这个理解到后面看网络结构会非常有用因为你很快就发现现代网络很少用大的卷积核了。原因很简单堆叠两个3×3卷积核换算成感受野是一个5×5因为第一个3×3让特征图每个点看到输入3×3区域再接一个3×3时看到的是上一层3×3区域对应的输入区域合起来是5×5范围。但参数量呢两个3×3卷积的参数量是2×918按单通道看而一个5×5卷积是25。参数更少感受野却一样大而且中间多了一次ReLU激活给网络增加了非线性表达能力。课程里明确指出这就是后来很多经典结构偏好使用小卷积核堆叠的根本原因。1×1卷积也值得单独说。它不改变空间尺寸但会改变通道数相当于在每个像素位置上对通道做一次线性组合可以用很低的成本控制特征图厚度。课程里把1×1卷积看成“跨通道的池化”或者说“通道层面的重组合”在GoogLeNet这类结构里被大量用来降维先把通道压下来再做大卷积核计算。3. 池化层和全连接层缩小特征图与汇总决策3.1 池化层不是“必须”但它的动机很朴素池化层负责的是特征图的降采样。最常见的是最大池化用一个2×2窗口以步长2扫过特征图每个窗口内取最大值输出宽高直接减半通道数不变。为什么需要这一步首先卷积层虽然已经控制了参数规模但特征图本身依然很大如果一路保持高分辨率后面的层计算量会一直维持在很高的水平。其次最大池化给了网络对微小位移的容忍度物体偏移一两个像素时池化窗口内的最大值往往变化不大后续层看到的仍是接近相同的特征。用课程里的说法池化是在逐步抽象位置细节慢慢让位于语义内容。这里特别想说池化并不改变通道数量。它只在宽和高两个维度上做压缩通道维度保持不动。常见配置就是2×2窗口加步长2效果是特征图每个维度减半相当于对特征图做了空间上的“浓缩”。3.2 最大池化和平均池化的取舍讲师在课上对比过两种池化。最大池化取窗口内的最强响应实际经验里效果更好因为它保留的是“这个区域里到底有没有某种特征”而不是被区域内大量低响应值拉低平均。平均池化没那么激进把窗口内所有值等权汇总信息更平滑但更容易把微弱特征稀释掉。不过平均池化有一个用法一直活到了今天全局平均池化也就是把整张特征图池化成1×1。它一般出现在网络的末端用来替代全连接层做最终分类前的特征汇总好处是参数量几乎为零并且天然输出一个与类别数一致的特征向量。课程在讲经典网络的时候提到这种设计我当时没太在意后来自己动手做小模型时试了一次才发现这种结尾方式既省参数又不容易过拟合。3.3 全连接层把二维特征图变成分类分数卷积层和池化层把图像逐步抽象成了“长宽更小、通道更多”的特征图但分类任务最终要输出一个向量比如10个类别的得分。全连接层做的就是最后这一步把特征图展平成一维然后通过一个权重矩阵映射到类别维度上。全连接层的问题和开头讲的一样参数量很大。假设最后一个卷积层输出是7×7×512展平后长度是25088如果再接一个1024节点的全连接层权重就是25088×1024约2570万。这也是为什么很多现代结构要么不用全连接层要么只保留最后一层。课程里提过一个技巧全连接层本质上可以被看成一种特殊卷积当输入特征图空间尺寸为1×1时全连接就是1×1卷积。理解这个对应关系对改造网络结构很有帮助比如你想把某个分类网络改成全卷积的版本只需要把全连接层替换成对应的1×1卷积。到这里一个经典网络的完整骨架就清楚了输入图像经过多轮“卷积-激活-池化”把空间尺寸逐步压低、通道逐步加深最后展平接全连接层再接一个Softmax把得分变成概率。整个流程可以概括成特征提取在前分类决策在后。4. 激活函数与经典网络骨架从CONV-RELU-POOL到现代变体4.1 为什么Sigmoid在深度网络里基本被放弃了2017年课程讲激活函数时仍然是从Sigmoid讲起的因为它历史上重要但随后很快就指出它的致命缺陷饱和。Sigmoid把输入压到0到1之间当输入绝对值较大时曲线变得非常平缓梯度接近0。多个层连起来的时候这些接近0的梯度一乘就会让靠近输入层的参数几乎得不到更新信号这就是梯度消失。Tanh比Sigmoid好一点输出是零中心的但同样存在饱和区的问题。ReLU就朴素多了函数是max(0,x)正半轴梯度恒为1负半轴直接置0。优势很明显计算只要一次比较收敛速度比Sigmoid快很多而且正的梯度能顺利往后传。课程里给出的建议非常直接默认用ReLU如果遇到神经元大量死亡尝试Leaky ReLU或带参数的变体。ReLU有个著名的问题叫“死亡ReLU”如果某个神经元在训练中被负区间压住它的梯度永远是0权重再也无法更新这个神经元就永久退出了。通常原因有两点一是初始化不合适二是学习率设置太大。课程里也提到合理的初始化和学习率能大幅减少这种现象所以别一见到死亡神经元就急着换激活函数先把训练配置调对。4.2 经典层布局CONV-RELU-[(POOL)]的节奏普通卷积网络的布局有一个非常清晰的节奏卷积层负责特征提取激活函数引入非线性池化层做空间降维然后重复。典型的一个阶段是“卷积-ReLU-池化”或者更细致一点“若干个卷积-ReLU-池化”。为什么卷积和池化的顺序固定成这个样子因为激活函数必须紧跟卷积否则多个卷积层叠起来还是线性变换堆再多层也等效于一个卷积。池化则可以稍微延后让卷积层先在同一分辨率下充分提取特征再用池化降低冗余。课程讲到最后的经典结构演进时其实讲的都是这个节奏的变化早期5层网络是“卷积-池化-全连接”的简单拼接8层的AlexNet把卷积层增加到5层同时用ReLU和Dropout让网络第一次在大规模图像数据上取得明显优势16层风格的VGG网络证明了“小卷积核深网络”的组合它完全堆叠3×3卷积结构极度规整22层的GoogLeNet则尝试在同一个层上并行使用不同尺寸的卷积核让网络自己选择合适尺度的特征而50层以上能训练起来的关键是残差连接让梯度能绕过一个层直接传到前面。我自己复习到这一段时最大的感受是每一代结构都不是凭空想出来的都是在解决上一代网络训练或表达的某个具体瓶颈。经典结构按课程演进顺序核心改动参数量量级早期5层网络卷积池化全连接的基本拼接百万级8层结构加深网络、引入ReLU与随机丢弃约6000万16层纯卷积结构大量堆叠3×3小卷积核结构规整约1.38亿22层多分支结构同层多种尺度卷积并行组合特征约700万50层残差结构增加跨层直连解决深度网络的梯度问题约2560万4.3 为什么2017年讲的内容到今天仍然适用虽然这是2017年的课很多具体结构和数据都已经不是最前沿但第五讲的核心思想一点没过时卷积层的局部连接和参数共享、池化层的空间降采样、激活函数的非线性、残差结构对梯度传播的改善这些仍是今天绝大多数视觉模型的基石。后面的注意力机制、大规模预训练、多模态模型都是在这些基础构件之上生长出来的。所以我现在看这篇笔记更多的不是看技术有多新而是看它如何把若干基本的视觉先验清晰地注入到架构里这个底层思路是稳定的。5. 训练卷积神经网络的现实问题初始化、正则化、数据增强5.1 输入预处理和权重初始化两台发动机的预热课程在训练细节部分很早就强调数据预处理。图像数据最基础的处理是零中心化对每个像素位置或每个通道减去均值让输入分布在0附近。这样做的原因是Sigmoid和Tanh这类激活函数在0附近梯度大、不饱和虽然ReLU对中心化不那么敏感但中心化仍然能让优化过程更稳定尤其是配合Batch Normalization时。权重初始化也是训练成败的隐形关卡。全零初始化不可行因为所有神经元会同步更新永远无法分化出不同特征。用小随机数初始化是可接受的基础做法但更关键是要配合激活函数选择方差尺度ReLU家族网络更推荐He初始化它根据输入维度调整权重方差保证前向传播时信号的方差基本稳定不会逐层放大或衰减。实测里初始化错误不会让网络立即报错而是表现为loss在某个值附近震荡或者训练很慢不下降这种问题排查起来比显式报错痛苦得多所以宁可在一开始就按规则初始化。5.2 Batch Normalization和Dropout两个作用完全不同的工具Batch Normalization简称BN在2017年已经非常常用。它的核心操作是让每一层卷积后的特征在通道维度上做一次标准化减均值再除以标准差然后用可学习的缩放和平移参数恢复表达能力。效果是网络对初始化敏感度降低收敛速度明显提升。放到卷积层时BN的作用位置是在卷积/全连接之后、激活函数之前这一点我在复现时经常记反结果效果打了折扣。Dropout则是另一个维度的事它属于正则化目标是抑制过拟合。做法非常简单粗暴训练时按概率随机把一部分神经元输出置0让网络不能过度依赖某几个节点迫使信息分散到更多连接中。这里最容易翻车的是忘记在验证和测试阶段关闭Dropout或者把训练用的缩放逻辑搞错。我见过不少人的验证效果比训练差一大截排查半天发现是验证代码里忘了关Dropout属于新手区高频失误。5.3 数据增强不要钱的训练集扩张数据增强在图像任务里性价比极高。最简单的几招随机水平翻转、随机裁剪、小幅颜色抖动、随机旋转和缩放。这些变换不改变图片的语义内容但让模型看到更多变体从而学到更鲁棒的特征。课程里明确把它当作一种正则化手段和Dropout、权值衰减并列。权值衰减L2正则化也值得提一句它给大权重加惩罚迫使模型使用分布更平缓的参数。通常系数设在10的负四次方到负五次方这个范围。配上前面的增强和Dropout三者合起来能让验证准确率提升几个点是非常划算的组合拳。5.4 调学习率的过程比调结构更影响最终结果课程里反复强调学习率可能是最需要重点调的超参数。太大容易震荡甚至发散太小则收敛极其缓慢。常见做法是阶梯式衰减比如每训练若干轮学习率乘0.1或者在验证loss不再下降时手动降低学习率。我在复现过程中体会最深的一件事是与其盯着网络结构改来改去不如先跑一个少量数据的小实验把学习率、批大小这些训练参数调到合理范围再投入全部数据。这个顺序节省下来的时间和算力非常可观。还有一条被说了无数遍但依然有效的经验在做大实验之前先用极少量数据比如一个batch或几十张图把网络跑到过拟合。如果连过拟合都做不到说明代码里有bug如果能过拟合到接近100%才说明正向流程是通的。这个“先过拟合验证代码”的思路是课程和后来各种工程实践都一致认可的起步动作。6. 复习完这一讲之后我想留下的几条实操经验先说最容易卡住新手的“尺寸问题”。每次搭网络前我建议把每一层的输入输出尺寸、通道数、参数量都手写在一个草稿表里。不要只依赖框架自动计算因为你真正调试的时候发现尺寸不匹配会很难受。我自己吃过一次亏两个分支结构里的卷积padding参数写错导致特征图尺寸对不上拼接时直接报错排查了一个多小时才找到问题。这类问题提前算一遍基本可以规避。然后是关于“复现经典结构”的路径推荐。如果你正在入门不建议一上来就啃几十层的复杂结构。先做一个小型卷积网络跑到CIFAR级别的小数据集上看清卷积、池化、全连接之间怎么配合然后照着8层风格的网络复现一遍体会什么叫“深度增加带来的表达力提升”最后再去碰残差结构这时你会真正理解为什么跨层连接能稳住在深层网络的训练。每走一步都把前面讲的尺寸计算、初始化和正则化流程走一遍这些基本功才会变成肌肉记忆。我最后一次完整复习这讲内容时也顺带整理了一个适合自己的检查清单数据有没有做零中心化BN的位置是不是在激活之前Dropout在验证时有没有关闭卷积输出尺寸有没有算错初始化是不是按激活函数选了对应的方案。如果这五项都对训练一般不会出现“完全学不动”的诡异情况。遇到loss不降时我第一反应不是改网络结构而是回头核对这个清单。最后分享一个观念上的转变。最开始看这类笔记总会忍不住去记每个经典网络的层数、参数量、准确率但这些数字过几个月就会模糊。真正留在脑子里的是卷积为什么比全连接适合图像、感受野是怎么被小卷积核扩大的、网络加深为什么需要残差连接兜底。理解了这些动机之后再去看任何新模型都不会觉得是一堆陌生名词的堆砌。这大概是第五讲对我来说最大的价值。
阅读完成 · 觉得有帮助?
咨询建站