岁月袅袅 發表於 2025-12-16 20:17:00

吴恩达深度学习课程四:计算机视觉 第二周:经典网络结构 (二)残差网络

<p>此分类用于记录吴恩达深度学习课程的学习笔记,目前已完结,点击进入全集目录<br>
课程相关信息链接如下:</p>
<ol>
<li>原课程视频链接:[双语字幕]吴恩达深度学习deeplearning.ai</li>
<li>github课程资料,含课件与笔记:吴恩达深度学习教学资料</li>
<li>课程配套练习(中英)与答案:吴恩达深度学习课后习题与答案</li>
</ol>
<p>本篇为第四课的第二周内容,2.3到2.4的内容。</p>
<hr>
<p>本周为第四课的第二周内容,这一课所有内容的中心只有一个:<strong>计算机视觉</strong>。应用在深度学习里,就是专门用来进行图学习的模型和技术,是在之前全连接基础上的“特化”,也是相关专业里的一个重要研究大类。<br>
<strong>这一整节课都存在大量需要反复理解的内容和机器学习、数学基础。</strong> 因此我会尽可能的补足基础,用比喻和实例来演示每个部分,从而帮助理解。<br>
第二周的内容是对一些经典网络模型结构和原理的介绍,自然会涉及到相应的<strong>文献论文</strong>。因此,我也会在相应的模型下附上提出该模型的论文链接。<br>
本篇的内容关于<strong>残差网络</strong>,同样是为了让深层网络仍能够有效训练,缓解梯度现象而出现的技术。</p>
<h1 id="1-什么是残差网络resnet">1. 什么是残差网络(ResNet)?</h1>
<p>残差网络英文原名为 residual networks ,简称 <strong>ResNet</strong>,它在 2015 年的一篇论文:Deep Residual Learning for Image Recognition 中被提出,这篇论文提出了残差学习框架,是 ResNet 系列网络的开创之作。<br>
要了解什么是残差网络,首先要了解什么是<strong>残差</strong>。</p>
<h2 id="11-什么是残差">1.1 什么是残差?</h2>
<p>先给出一个残差学习的<strong>严谨结论</strong>:<strong>残差学习并不是让网络直接学习目标映射,而是让网络学习目标映射相对于“恒等映射”的偏差。</strong><br>
你可以把恒等映射理解为“什么都不做,直接把输入原样传下去”,而残差学习做的事情,就是在这个“原样输出”的基础上,学习<strong>还需要额外补充哪些变化。</strong></p>
<p>用一句直观的话说就是:<strong>这一层并不负责“从零得到结果”,而只负责在原有输入的基础上“该改多少”。</strong></p>
<p>下面我们从普通网络的传播逻辑开始,对比引出残差的含义。</p>
<h4 id="1-非残差网络的传播逻辑">(1) 非残差网络的传播逻辑</h4>
<p>在监督学习的神经网络里,<strong>每一层都试图直接学一个从输入到输出的映射关系</strong><br>
可以写成:</p>
<p></p><div class="math display">\[y = F(x)
\]</div><p></p><p>意思是:<strong>给你输入 <span class="math inline">\(x\)</span> 和运算单元,你自己学习怎么映射出输出 <span class="math inline">\(y\)</span>。</strong><br>
这种结构在浅层网络中通常没有问题,但当网络不断加深时,会逐渐暴露出训练困难。<br>
常见原因包括梯度消失或爆炸等优化问题。<br>
但是注意:<strong>普通深层网络的问题并不在于“表达能力不够”,而在于“优化过程本身变得困难”。</strong><br>
因此,<strong>即便梯度问题被一定程度缓解,普通深层网络仍然难以有效逼近“恒等映射”,从而导致训练误差随着深度增加反而上升</strong>。这种现象在 ResNet 论文中被称为 <strong>degradation problem(退化问题)</strong>。<br>
简单举个例子:<br>
假设有一个<strong>极简的一维回归任务</strong>:</p>
<ul>
<li>输入样本:<span class="math inline">\(x = 5\)</span></li>
<li>真实标签:<span class="math inline">\(y = 8\)</span></li>
</ul>
<p>在<strong>非残差网络</strong>里,这一层要直接学:$$y = F(x)$$也就是说,这一层的目标是学会:$$F(5) = 8$$如果当前网络参数学得不好,比如此时:$$F(5) = 3$$那么误差就是:$$\text{error} = 8 - 3 = 5$$此时,误差信号需要通过多层非线性变换向前反向传播,深层参数只能接收到被多次“稀释”后的梯度,导致参数更新缓慢、训练困难。这也是为什么<strong>深层普通网络并不一定比浅层网络更容易训练</strong>。<br>
<strong>因此,深层网络并不像我们直觉里那样的“结构越复杂,拟合能力越强”。</strong><br>
为了让深层网络仍能训练,其中一种解决方法就是<strong>应用残差学习</strong>。</p>
<h4 id="2残差是什么">(2)残差是什么?</h4>
<p>在数学上,“残差”可以理解为<strong>目标值与某个基准值之间的差异</strong>。在残差网络中,这个“基准值”并不是零,而是<strong>恒等映射本身,也就是 <span class="math inline">\(x\)</span></strong>。</p>
<p>更准确地说,残差学习并不是让网络直接去学习目标映射 <span class="math inline">\(H(x)\)</span>,而是将其重写为: $$H(x)=x+F(x)$$其中:</p>
<ul>
<li><span class="math inline">\(x\)</span> 表示输入特征(恒等映射)</li>
<li><strong><span class="math inline">\(F(x)\)</span> 表示网络需要学习的残差项</strong>,即目标映射相对于 <span class="math inline">\(x\)</span> 的偏差<br>
需要注意的是,这里的“残差”并不是"标签减输入"这样的一个已知量,而是通过<strong>优化损失函数间接逼近的结果。</strong><br>
我们继续:</li>
</ul>
<h2 id="12-如何应用残差形成残差网络">1.2 如何应用残差形成残差网络?</h2>
<p>将上面的逻辑应用到神经网络结构中,其核心做法可以概括为一句话:<strong>不直接学习输出本身,而是学习输出相对于输入的修正量,并将二者相加得到最终结果。</strong><br>
简单来说就是:<strong>“我不让你学 <span class="math inline">\(y\)</span>,我让你学 <span class="math inline">\(y\)</span> 和 <span class="math inline">\(x\)</span> 之间的差。”</strong><br>
也就是:</p>
<p></p><div class="math display">\[y = x + F(x)
\]</div><p></p><p>这里的 <span class="math inline">\(F(x)\)</span> 通常由若干层卷积、线性变换和非线性激活函数组成,而 <span class="math inline">\(x\)</span> 则通过一条恒等分支直接传递到输出端。<br>
换句话说,网络这一层只负责回答一句话:<strong>“保持输入不变的情况下,我还需要补充或修正多少?”</strong><br>
现在我们<strong>把残差应用到刚刚的例子</strong>里:</p>
<ul>
<li>输入:<span class="math inline">\(x = 5\)</span></li>
<li>目标:<span class="math inline">\(y = 8\)</span><br>
残差网络不让这一层直接学 <span class="math inline">\(y\)</span>,而是先把输入“原封不动”地传下去,再只学差值:</li>
</ul>
<p></p><div class="math display">\[F(x) = y - x
\]</div><p></p><p>代入数值:</p>
<p></p><div class="math display">\[F(5) = 8 - 5 = 3
\]</div><p></p><p>于是这一层真正要学习的目标变成:</p>
<p></p><div class="math display">\[y = x + F(x) = 5 + 3 = 8
\]</div><p></p><p>如果当前网络学得不太好,比如:</p>
<p></p><div class="math display">\[F(5) = 2.5
\]</div><p></p><p>那么输出是:</p>
<p></p><div class="math display">\[y = 5 + 2.5 = 7.5
\]</div><p></p><p>误差只剩:</p>
<p></p><div class="math display">\[8 - 7.5 = 0.5
\]</div><p></p><p>你可以看到一个关键变化:<br>
<strong>网络不再为“从 0 到 8”负责,而只是为“在 5 的基础上补多少”负责。</strong><br>
可以看到,与直接从零拟合目标相比,残差结构将学习任务转化为<strong>围绕输入的小幅修正</strong>。<br>
更重要的是,由于恒等分支的存在,<strong>反向传播时梯度可以绕过复杂的非线性变换直接传递到前层</strong>,从而显著缓解深层网络的优化难题。<br>
了解了残差网络本身后,我们来看看如何具体实现残差网络。</p>
<h1 id="2-如何实现残差网络">2. 如何实现残差网络?</h1>
<p>同样先用一句话说明一下结论:<strong>要实现残差网络,就要使用旁路连接形成残差块,实现残差学习。</strong><br>
现在,我们来逐个展开这句话里出现的新名词。</p>
<h2 id="21-什么是旁路连接shortcut-connections">2.1 什么是旁路连接(shortcut connections)?</h2>
<p><strong>旁路连接是一种网络结构设计,用于在神经网络中实现残差映射,从而支持残差学习。</strong></p>
<p>用严谨的说法来具体描述一下它:<strong>旁路连接是指在神经网络中引入一条恒等映射(或线性映射)分支,将某一层的输入 <span class="math inline">\(x\)</span> 直接传递到后续层,在后续层的线性变换结果与非线性激活之前,与主分支的输出进行融合(通常为逐元素相加),从而在正向传播中显式保留输入信息,并在反向传播中为梯度提供一条直接的传递通道。</strong></p>
<p>而说简单点,<strong>旁路连接就像给网络开了一条“捷径”</strong>:你不再要求每一层都从头学习一个完整的映射关系,而是把已有的信息直接送到后面去,让网络只需要决定“在此基础上还要改多少”。<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201531456-326278593.png" alt="image.png" loading="lazy"><br>
我们已经十分熟悉它的传播过程了,就不再多说了。<br>
现在,我们按照上面的定义,在这一层中加入旁路连接:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201533601-1095955844.png" alt="image.png" loading="lazy"><br>
可以看到,在加入旁路连接后,<span class="math inline">\(a^{}\)</span> 不仅沿着原有的主分支参与下一层的线性变换,还通过旁路连接这一条恒等分支,直接传输到下一层的线性输出之前,并与 <span class="math inline">\(z^{}\)</span> 相加,随后再经过激活函数得到 <span class="math inline">\(a^{}\)</span>。</p>
<p>这种结构改变了网络在<strong>正向传播阶段</strong>的建模方式:网络不再直接学习目标映射 <span class="math inline">\(H(x)\)</span>,而是将其拆解为“输入本身”与“输入到目标之间的修正量”两部分,即学习残差函数:</p>
<p></p><div class="math display">\[F(x)=H(x)−x
\]</div><p></p><p>从而使得深层网络在训练过程中更容易优化,同时在反向传播时,梯度也可以通过恒等分支直接传播,有效缓解随网络加深而出现的退化问题。</p>
<h2 id="22-什么是残差块residual-block">2.2 什么是残差块(residual block)?</h2>
<p>如果你仔细读了上面提到的旁路连接的定义,就会发现这一点:<strong>旁路连接并不局限于跨越单一层</strong>。<br>
是的,实际的残差网络中,输入往往会跨越多个中间层直接传递。由主分支中的若干层卷积、线性变换与非线性激活函数,以及与之配套的一条旁路连接,共同构成的这一基本结构单元,被称为一个<strong>残差块</strong>。</p>
<p>残差块是 ResNet 中最基本、也是最重要的结构单元。整个 ResNet 的主干网络,正是通过不断堆叠这样的残差块构建而成的。<br>
接下来,我们继续沿用刚刚的例子,具体看看一个典型的残差块在结构上是如何实现的,我们延伸刚刚的网络结构如下:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201532029-1077956763.png" alt="image.png" loading="lazy"><br>
它的传播同样简单明了。现在,我们试试<strong>跨越一层的旁路连接</strong>:<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201531563-578461374.png" alt="image.png" loading="lazy"></p>
<p>可以看到,此时旁路连接从第 <span class="math inline">\(l\)</span> 层的输出 <span class="math inline">\(a^{}\)</span> 出发,跨越第 <span class="math inline">\(l+1\)</span> 层,直接与主分支在第 <span class="math inline">\(l+2\)</span> 层的线性输出进行融合,并共同生成最终的输出 <span class="math inline">\(a^{}\)</span>。</p>
<p>从整体上看,这一结构不再关注中间每一层的具体细节,而是将输入 <span class="math inline">\(a^{}\)</span> 映射为输出 <span class="math inline">\(a^{}\)</span>,其数学形式可以概括为:</p>
<p></p><div class="math display">\} = a^{} + F\big(a^{}\big)
\]</div><p></p><p>其中,<span class="math inline">\(F(\cdot)\)</span> 表示由主分支中的若干层共同构成的残差函数。<br>
因此,<strong>残差块并不是简单意义上的“若干层网络”,而是一个以旁路连接为核心、用于学习残差映射的整体函数单元</strong>。在这一视角下,输入 <span class="math inline">\(a^{}\)</span> 与输出 <span class="math inline">\(a^{}\)</span> 共同界定了一个残差块的作用范围。</p>
<p>总结一下:残差网络的引入,使得<strong>特征在前向传播过程中可以通过恒等分支直接传递</strong>,不再随着网络深度的增加而被层层变换所削弱。<br>
同时,将直接学习目标映射转化为学习残差映射,也<strong>为反向传播提供了更加顺畅的梯度通道</strong>,从而在一定程度上缓解了深层网络中常见的梯度消失、优化困难等问题。<br>
了解了基础知识后,我们下面就来看看 ResNet 是如何应用残差思想的。</p>
<h1 id="3-resnet-的网络结构">3. ResNet 的网络结构</h1>
<p>在前面对残差块的讨论中,你可能已经注意到一个关键前提:<strong>在残差块中,参与融合(相加)的两条分支,其输出张量的维度必须一致,否则残差相加无法进行。</strong></p>
<p>在 ResNet 的实现中,这一条件可以通过多种方式满足。 例如 same padding 或 <span class="math inline">\(1\times1\)</span> 卷积。<br>
正是由于残差结构在设计上显式地处理了这些维度匹配问题,深层网络在结构上变得更加可控和稳定,从而为构建更深的网络提供了基础条件。</p>
<p>我们接下来来看 ResNet 在论文中给出的整体网络结构。需要注意的是,<strong>ResNet 并不是某一个具体模型,而是一系列采用残差块作为基本单元的网络架构的统称</strong>,包括 ResNet-18、ResNet-34、ResNet-50 等不同深度的版本。<br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201531946-473933867.png" alt="微信图片_20251216193050_430_14.png" loading="lazy"><br>
<img src="https://img2024.cnblogs.com/blog/3708248/202512/3708248-20251216201531493-1938319159.png" alt="image.png" loading="lazy"><br>
这些都是原论文中的内容,如果抛开残差思想,你会发现,这就是我们在经典卷积网络部分中LeNet-5 验证的“范式”,即通过卷积和池化层层提取和组合特征,最后输入全连接层进行决策。</p>
<p>可偏偏正是这一看似简单的结构改动——<strong>将前层的输入通过旁路连接直接引入后层,与主分支的输出进行融合</strong>——显著改善了深层网络的可训练性,使得网络深度得以大幅提升,也推动了 CV 模型性能又一次发展。<br>
其实 ResNet 的论文原文中对于模型的构建还有很多这里没有展开的细节,但是涉及到一些还没介绍的技术,我们之后再慢慢展开。<br>
在之前的迁移学习部分,我们已经演示过预训练过的 ResNet-18 的效果,而在本周的实践部分,我会再次演示单纯使用 ResNet-18 本身所实现的效果。</p>
<h1 id="4-总结">4. 总结</h1>
<table>
<thead>
<tr>
<th>概念</th>
<th>原理</th>
<th>比喻</th>
</tr>
</thead>
<tbody>
<tr>
<td>残差网络(ResNet)</td>
<td>通过引入残差学习框架,将深层网络的直接映射学习问题,转化为对输入恒等映射的修正问题,从而缓解深层网络的优化困难</td>
<td>不要求你“从零做出结果”,而是在已有基础上“微调改进”</td>
</tr>
<tr>
<td>残差(Residual)</td>
<td>目标映射 <span class="math inline">\(H(x)\)</span> 相对于恒等映射 <span class="math inline">\(x\)</span> 的偏差,形式为 <span class="math inline">\(F(x)=H(x)-x\)</span>,由网络隐式学习</td>
<td>在原有结果上补差,而不是重新做一遍</td>
</tr>
<tr>
<td>恒等映射</td>
<td>输入 <span class="math inline">\(x\)</span> 原样传递到后续层,不引入任何变换</td>
<td>什么都不做,直接照搬</td>
</tr>
<tr>
<td>非残差网络的映射</td>
<td>每一层直接学习完整映射 <span class="math inline">\(y=F(x)\)</span>,深层时优化困难</td>
<td>每一关都要求你“从头交付成品”</td>
</tr>
<tr>
<td>退化问题(Degradation Problem)</td>
<td>随网络深度增加,训练误差反而上升的问题,源于优化难度而非模型表达能力不足</td>
<td>路越修越宽,但车反而跑不动</td>
</tr>
<tr>
<td>旁路连接(Shortcut Connection)</td>
<td>通过恒等(或线性)分支将输入直接传递到后层,与主分支结果融合,为信息与梯度提供直达路径</td>
<td>给网络开一条不堵车的“快速通道”</td>
</tr>
<tr>
<td>主分支</td>
<td>由卷积、线性变换和非线性激活构成,用于学习残差函数 <span class="math inline">\(F(x)\)</span></td>
<td>真正“干活”的加工流程</td>
</tr>
<tr>
<td>残差块(Residual Block)</td>
<td>由若干主分支层与一条旁路连接共同组成的结构单元,实现 <span class="math inline">\(a^{}=a^{}+F(a^{})\)</span></td>
<td>一整套“加工 + 校正”的工作单元</td>
</tr>
<tr>
<td>特征融合(相加)</td>
<td>要求两条分支输出维度一致,通过逐元素相加完成信息整合</td>
<td>两条尺寸相同的轨道合并</td>
</tr>
<tr>
<td>ResNet 网络结构</td>
<td>通过堆叠大量残差块构建深层网络,而非单纯增加普通层数</td>
<td>用稳定模块搭高楼,而不是硬堆砖</td>
</tr>
</tbody>
</table><br><br>
来源:https://www.cnblogs.com/Goblinscholar/p/19359236
頁: [1]
查看完整版本: 吴恩达深度学习课程四:计算机视觉 第二周:经典网络结构 (二)残差网络