nptel-dlcv-merge-2

NPTEL 深度学习计算机视觉笔记(三)

损失:分类交叉熵损失。

对比学习:近期主流方法

最近一年,自监督学习领域主要由基于对比学习的方法主导。其总体目标是通过对比正样本和负样本来学习表示。

核心思想

目标是学习一个编码器,使得图像与其相似样本之间的相似度得分,大于该图像与不相似样本之间的得分。如果没有类别标签,如何定义正负样本呢?这是对比学习方法需要解决的关键问题。

一个通用的损失函数形式如下(基于Softmax的对比损失):

核心公式:

L = -log( exp(sim(q, k+) / τ) / Σ_{i=0}^{K} exp(sim(q, k_i) / τ) )

其中,sim 是相似度函数(如点积),τ 是温度超参数,用于调节分布的尖锐程度。

经典方法:MoCo

MoCo代表动量对比,它提出使用动态字典查找的思想进行视觉表示的无监督学习。它维护一个作为队列的字典,其中包含大量数据样本的编码表示(键)。查询样本通过一个编码器得到表示q,键样本通过一个动量编码器得到表示k。损失使用上述的对比损失。

动量更新:

动量编码器的参数 θ_k 通过以下方式更新:

θ_k = m * θ_k + (1 - m) * θ_q

其中 θ_q 是查询编码器的参数,m 是动量系数(通常接近1,如0.999)。

经典方法:SimCLR

SimCLR的目标是通过最大化同一数据样本的不同增强视图之间的一致性来学习表示。在一个小批量中,对每个样本应用两种不同的数据增强,产生一对正样本。该样本与批次内其他所有样本构成负样本对。它使用一个基础编码器网络 F 和一个投影头网络 G 来学习表示。

优势与局限:

SimCLR使用了强大的数据增强和MLP投影头,但负样本数量受限于批次大小。而MoCo通过字典队列可以包含更多负样本,通常能带来更好的性能。

前沿方法:BYOL

Bootstrap Your Own Latent 方法声称在不依赖任何负样本的情况下达到了最先进的结果。它通过“引导”网络自身的输出来创建目标。具体来说,它使用两个网络:在线网络和目标网络。在线网络的目标是预测目标网络对同一图像另一个增强版本的表示。损失是L2损失。

核心流程:

  1. 对输入图像 x 应用两种随机增强,得到 v 和 v‘。

  2. v 通过在线网络得到表示 y_θ 和投影 z_θ。

  3. v‘ 通过目标网络得到表示 y_ξ’ 和投影 z_ξ’。

  4. 在线网络还有一个预测器 q_θ,其目标是预测 z_ξ’。

  5. 损失:L = || q_θ(z_θ) - z_ξ’ ||_2^2。

  6. 目标网络的参数通过动量方式从在线网络更新。

总结

本节课我们一起学习了自监督学习。我们从其基本概念和动机出发,了解了它如何通过设计前置任务从无标签数据中学习有用表示。我们介绍了几种经典的前置任务,如图像修复、拼图、旋转预测和着色。接着,我们深入探讨了当前主流的对比学习方法,包括MoCo、SimCLR的核心思想及其区别。最后,我们简要了解了不依赖负样本的BYOL方法。自监督学习是释放海量无标签数据潜力的关键方向,对于推动深度学习发展具有重要意义。


课后阅读建议:

  1. 阅读Lillian Weng关于自监督表示学习的优秀文章。

  2. 了解MoCo V2是如何结合MoCo和SimCLR优点的。

参考文献:

  • Context Encoders (CVPR 2016)

  • Unsupervised Learning of Visual Representations by Solving Jigsaw Puzzles (ECCV 2016)

  • Unsupervised Representation Learning by Predicting Image Rotations (ICLR 2018)

  • Colorful Image Colorization (ECCV 2016)

  • Momentum Contrast for Unsupervised Visual Representation Learning (CVPR 2020)

  • A Simple Framework for Contrastive Learning of Visual Representations (ICML 2020)

  • Bootstrap Your Own Latent (NeurIPS 2020)

076:对抗鲁棒性 🛡️

在本节课中,我们将要学习一个当代的重要话题——对抗鲁棒性。我们将探讨为什么深度学习模型在面对精心设计的微小扰动时容易出错,了解不同类型的对抗攻击方法,并学习如何通过防御技术来增强模型的鲁棒性。

概述

在监督式机器学习中,我们部署模型的测试数据分布,常常与训练模型时的数据分布不同。不幸的是,每当出现这种分布偏移时,机器学习模型,尤其是深度学习模型,其性能往往会显著下降。其中,对抗样本是这种差异变得尤为突出的一个场景。

对抗样本与数据集中其他样本在视觉上难以区分,但却会导致模型做出错误的预测。例如,一张被模型以91%置信度识别为“猪”的图片,在添加了微小的随机噪声后,同一模型会以99%的置信度将其识别为“客机”。这是一个严重的问题,因为人类通常不会受此影响。虽然原因尚不完全清楚,但这一现象催生了大量关于对抗攻击(试图“黑”掉模型)和对抗防御(保护模型免受攻击)的研究。

对抗攻击的类型

上一节我们介绍了对抗样本的基本概念,本节中我们来看看对抗攻击的不同分类方式。根据攻击者掌握的信息、攻击目标以及衡量扰动大小的方式,对抗攻击可以分为多种类型。

以下是基于威胁模型的分类:

  • 白盒攻击:攻击者假设可以访问模型的全部参数和架构。

  • 黑盒攻击:攻击者无法访问模型内部参数,只能通过查询模型的输入输出来进行攻击。

以下是基于攻击目标的分类:

  • 非定向攻击:攻击者的目标是使模型对输入样本产生任何错误分类。

  • 定向攻击:攻击者的目标是使模型将输入样本错误分类为一个特定的目标类别。这在生物识别等领域可能带来严重的安全风险。

以下是基于距离度量的分类(用于约束扰动大小):

  • L0范数:衡量干净图像与对抗图像之间不同像素的数量。

  • L2范数:衡量干净图像与对抗图像之间像素值的均方根差。

  • L∞范数:衡量干净图像与对抗图像之间单个像素的最大变化值。许多方法常用此范数。

白盒对抗攻击方法

了解了攻击的分类后,我们首先深入探讨几种经典的白盒攻击方法。这些方法假设攻击者完全了解模型。

快速梯度符号法

一种著名的白盒攻击方法是快速梯度符号法。这是一种单步方法,通过沿损失函数梯度的符号方向添加扰动来生成对抗样本。

其核心公式为:

x_adv = x + ε * sign(∇_x L(θ, x, y_true))

其中,x是原始输入,∇_x L是损失函数关于输入的梯度,sign是符号函数,ε是控制扰动大小的预算参数。

该方法试图添加一个能增加损失的扰动,从而导致模型误判。对于定向攻击,只需将梯度方向取反,并针对目标标签计算梯度即可。

投影梯度下降法

一个更强大、更完备的方法是投影梯度下降法。与FGSM的单步更新不同,PGD是一种迭代方法。

其迭代更新公式(以L∞约束为例)为:

x_adv^{t+1} = Proj_ε ( x_adv^t + α * sign(∇_x L(θ, x_adv^t, y_true)) )

其中,Proj_ε表示将扰动投影回以原始样本为中心、半径为ε的L∞球内,α是迭代步长。

PGD通过多次迭代,不断寻找能使损失最大化的微小扰动,直到成功欺骗模型。它被认为是目前最强大的攻击基准之一。

DeepFool方法

DeepFool方法的核心思想是考虑分类器的决策边界。对于一个线性分类器 f(x) = w^T x + b,将一个样本x_n误分类所需的最小扰动,就是该点到决策超平面的距离。

对于更一般的非线性分类器,DeepFool假设在样本点附近决策边界是局部线性的,并通过迭代计算将该点推向最近决策边界所需的最小L2范数扰动。

其他白盒攻击方法

除了上述方法,还有其他多种白盒攻击策略:

  • C&W攻击:将生成对抗样本的问题形式化为一个优化问题,直接最小化扰动范数,同时确保对抗样本被错误分类。

  • 基于雅可比矩阵的显著图攻击:通过计算模型输出(logits)对输入的雅可比矩阵,来识别哪些输入像素对改变分类结果最有效,然后有针对性地修改这些像素。

  • 通用对抗攻击:目标是寻找一个单一的扰动向量,当将其添加到任何输入图像上时,都能以高概率欺骗目标模型。这与为每个样本寻找特定扰动的思路不同。

非基于范数的白盒攻击

上一节我们介绍了基于添加扰动范数约束的攻击,本节中我们来看看一些不直接依赖于LP范数的白盒攻击方法。

以下是两种此类方法:

  • 空间变换对抗攻击:不是添加像素值扰动,而是学习一个流场来轻微扭曲图像中的像素位置,再通过双线性插值生成对抗图像。其目标函数同时鼓励误分类和保持流场变化最小。

  • 函数式对抗攻击:通过对输入样本应用一个函数变换(而非简单的加法扰动)来生成对抗样本。例如,将图像中所有红色像素的强度缩放为浅红色。这种方法可以与加性攻击结合使用。

黑盒对抗攻击方法

现在,我们转向黑盒攻击场景。在这种情况下,攻击者无法获取模型参数,只能通过向模型提交输入并观察输出来进行攻击。

以下是几种黑盒攻击策略:

  • 零阶优化攻击:通过多次前向传播查询来近似估计梯度。公式近似为:∇f(x) ≈ (f(x+h*e_i) - f(x-h*e_i)) / (2h)。得到梯度估计后,便可使用类似白盒的方法(如FGSM)生成对抗样本。

  • 边界攻击:一种更严格的黑盒设定,攻击者甚至无法获取模型输出的概率(logits),只能得到最终的分类标签(硬标签)。该方法通过粗粒度搜索定位决策边界,再使用二分搜索精确定位所需的最小扰动。

  • 单像素攻击:一种完全无梯度的方法。它通过随机修改一个或少数几个像素,并观察分类结果的变化,来评估像素的重要性,并迭代地选择能最大程度改变分类的像素进行修改。

对抗防御方法

我们已经看到了多种强大的攻击手段,现在来看看防御方如何保护模型。对抗攻击对使用现代深度学习模型的服务构成了严重的安全威胁,因此在诸如自动驾驶等关键应用中,防御至关重要。

以下是几种对抗防御技术:

  • 随机输入变换:在测试时,对输入图像进行随机缩放和随机填充,然后从变换后的多种版本中随机选择一个送入模型分类。这增加了攻击者预测模型实际所见输入的难度。

  • 随机噪声层:在训练和测试时,在每个卷积层前添加一个随机噪声层。通过对多个噪声扰动版本的输入进行预测并集成结果,来提高模型的鲁棒性。

  • 防御性生成器:首先训练一个生成器来建模干净图像的分布。在测试时,对于一个可能被对抗扰动的输入,通过生成器寻找与其最接近的“干净”版本,再用这个版本进行分类,从而“净化”输入。

  • 网络蒸馏:首先训练一个“教师网络”,然后训练一个“学生网络”去学习教师网络输出的软化后的概率分布(使用高温Softmax),而不是原始的硬标签。这种平滑的概率分布可以降低模型对微小扰动的敏感性。

对抗训练

目前最强大、应用最广泛的防御方法是对抗训练。这是一种计算密集型的方法,但效果显著。

其核心思想是在模型训练过程中,主动将对抗样本纳入训练集。具体来说,在每次训练迭代中,包含一个内层最大化步骤:

max_δ L(θ, x+δ, y), s.t. ||δ||_p ≤ ε

即寻找一个在约束范围内的扰动δ,使当前模型的损失最大化。然后,外层最小化步骤更新模型参数θ,以最小化这个对抗样本上的损失:

min_θ L(θ, x+δ, y)

这实质上是一种终极的数据增强。虽然计算代价高,但对抗训练能显著提升模型对各种攻击的鲁棒性。其变体包括对抗对数配对(强制干净样本和对抗样本的logits接近)和TRADES(将误差分解为自然误差和边界误差)等。

鲁棒性与准确性的权衡及前沿方向

一个重要结论是,对抗鲁棒性的提升往往伴随着标准准确率的轻微下降,这是一个需要权衡的问题。研究表明,随着训练数据量的增加,这种权衡可以得到缓解。

因此,近期的一些方法,如插值对抗训练或对抗性Vertex Mixup,尝试通过在干净样本和其对抗样本之间进行插值来扩增训练集,从而在提升鲁棒性的同时,更好地保持标准准确率。

此外,对抗鲁棒性的研究范畴也在扩展,包括:

  • 可解释性鲁棒性:攻击目标不再是预测结果,而是模型给出的解释或显著图。

  • 自然扰动鲁棒性:使模型对自然发生的损坏(如雾、模糊、雪等)具有鲁棒性,这对于自动驾驶等实际应用至关重要。

总结

本节课中,我们一起学习了深度学习中的对抗鲁棒性这一重要课题。我们从对抗样本的现象出发,系统性地了解了白盒与黑盒攻击、定向与非定向攻击等多种攻击分类与方法,例如FGSM、PGD和DeepFool。接着,我们探讨了多种防御策略,特别是目前最有效的对抗训练技术。最后,我们认识到鲁棒性与准确性之间的权衡,并展望了该领域的前沿方向。理解对抗攻击与防御,对于构建安全、可靠的深度学习系统至关重要。

077:剪枝与模型压缩

在本节课中,我们将学习如何将庞大的深度学习模型压缩成更小的尺寸,以便部署在计算资源有限的设备上。我们将探讨几种核心的模型压缩技术,包括剪枝、知识蒸馏和彩票假设。

从对抗鲁棒性的话题继续,我们现在讨论剪枝和模型压缩。这是将深度学习模型应用于现实世界应用中的另一个重要组成部分。

通常,神经网络被优化以提高预测准确性。无论是分类模型的准确率、检测模型的平均精度均值,还是分割模型的像素级分类准确率。然而,仅仅追求准确性往往导致神经网络变得非常庞大。

如今最先进的模型通常拥有数百万甚至更多的参数。回想一下,我们曾提到AlexNet有超过6000万个参数,在内存中占据约200MB的空间。VGG仅模型的权重就占据了高达500MB的内存空间。

这真的是一个问题吗?在训练模型时,拥有很高的存储和内存占用是可以接受的,并且可以使用强大的GPU来训练这些模型。然而,在测试或推理时,期望有强大的计算资源可能是受限的。

如果考虑在低计算设备上部署深度学习模型,例如手机、无人机或物联网设备,这些设备可能部署在世界的任何角落,甚至在恶劣条件下,那么庞大的神经网络模型就成为将其成功应用到这类计算平台上的限制因素。

从执行这些内存操作所消耗的能量的角度来看,这是另一种思考方式。一项有趣的分析由Song Han完成,他发表了关于深度模型压缩的最受欢迎的论文之一。分析表明,一个32位整数加法操作消耗约0.1皮焦耳(10^-12焦耳)的能量。一个32位浮点数加法操作消耗0.9皮焦耳。进一步,一个32位SRAM缓存访问操作消耗5皮焦耳,而访问动态内存(DRAM)则显著上升到640皮焦耳。访问动态内存比访问静态内存(SRAM)的成本要高得多。

为什么我们要讨论这个?这意味着当我们谈论低计算设备时,理想情况下,我们希望这些深度学习模型能驻留在SRAM中,而不必去访问DRAM,因为访问DRAM可能导致大量的能量需求,特别是在无人机或边缘设备、物联网设备等环境中,电池续航也是一个部署这些模型时需要考虑的问题。

因此,一个关键需求出现了:需要能够将这些庞大的神经网络模型剪枝成更小的内存占用,以便部署在低计算环境中。这类方法被广泛称为模型压缩方法,即一个训练好的模型被压缩成更小的内存占用,以便在低计算设备上部署。

过去几年,不同的研究人员进行了多项努力,这些方法可以大致分类为:参数剪枝与量化,这是一类专注于减少不影响性能的冗余参数的方法。第二类方法是基于低秩分解,使用矩阵和张量分解方法来估计信息丰富的参数并丢弃其余部分。转移或紧凑卷积滤波器是一类设计特殊结构卷积滤波器以节省参数的方法。最后,一个有趣的方法族称为知识蒸馏,其思想是将知识从大型神经网络模型提炼到小型学生神经网络模型中。

我们不会在本讲座中全部介绍,但会介绍几种。我们将简要介绍一种非常流行的基于剪枝的方法、一种知识蒸馏方法以及一种称为彩票假设的较新方法。

深度压缩:一种三阶段方法

一种最流行且相对较早的模型压缩方法是由Song Han在2016年ICLR上提出的深度压缩。这是一个具有变革性的方法,也将该方法推进到了硬件设计领域。它使用一个三阶段流程来减少神经网络的存储需求。

第一步是剪枝训练好的模型。然后是权重量化。最后是霍夫曼编码步骤。这提供了一个能将模型大小减少35到50倍,而精度损失极小的模型。

让我们看看这些步骤。

第一步:剪枝模型

这里的剪枝意味着什么?首先,你训练完整的模型,这是第一步。然后,将值低于某个阈值的权重从网络中移除。例如,如果任何权重的值低于10^-5,则该权重被从网络中移除。剩下的稀疏网络仅包含其他连接,然后被重新训练以获得更好的网络。这同样是一个迭代过程:在新的重新训练的稀疏网络中,如果任何权重低于某个阈值,它们会被移除,然后再次对剩余的稀疏网络进行重新训练。这个步骤被迭代执行。

仅通过这个简单的步骤,作者就展示了可以将许多流行网络的大小显著减小。例如,AlexNet的原始大小是6100万参数,使用简单的剪枝步骤后,大小下降到670万,这是9倍的压缩。当查看在ImageNet上的top-1或top-5错误率时,你会注意到由于参数减少,性能没有显著下降。对于VGG,仅剪枝就将参数数量减少了30倍。对于较小的网络如LeNet,也观察到了类似的结果。

第二步:权值共享

剪枝之后的第二步是一个称为权值共享的步骤。在每一层中,该层的权重使用简单的K均值聚类划分为K个簇,每个权重被替换为其所属簇的质心。

这里你看到一个4x4权重矩阵的例子。以及后续矩阵中的簇分配。还有每个簇的簇质心值。最后,这些蓝色权重中的每一个都被替换为这里蓝色对应的簇质心,依此类推。这有什么帮助?我们需要存储更少的值来表示这一层的权重。

随之而来的问题是,如果权重像这样改变和聚类,梯度会怎样?梯度也遵循类似的过程。因此,如果你在该特定层的这些位置有特定的梯度值,梯度也会被聚类,然后使用梯度的簇质心值从原始权重中减去,以获得新的权重。因此,即使梯度也以同样的方式参与了这个权值共享过程。

第三步:量化与霍夫曼编码

完成剪枝和权值共享后,作者使用的下一步是量化。这是基于一个经验观察:与使用32位浮点值相比,如果我们只使用8位,性能并没有降低多少。因此,这个量化步骤被用来进一步减少存储占用。

最后一步是使用霍夫曼编码。霍夫曼编码是计算机科学中一种流行的编码压缩方法,其中频繁出现的模式用较少的位数存储,而很少出现的模式用更多的位数存储以捕获其额外信息。霍夫曼编码是一种长期存在的压缩方法,在这里被用来再次减少存储占用。

通过这些方法依次应用,整体方法显示出参数减少了35到49倍,而精度损失极小。正如你在这里看到的,AlexNet从240MB变成了6.9MB,而VGG从552MB变成了11.3MB,这是49倍的存储参数减少。如果你看这里的错误率,由于这种压缩,错误率没有显著损失,这是主要目标。

因此,一旦模型达到6.9MB或10MB,它们现在就可以部署在低计算设备上了。

知识蒸馏:从大模型到小模型

我们将讨论的第二种方法是知识蒸馏。这类方法的关键直觉是将知识从一个笨重的大型模型转移到一个我们称为学生模型的小型模型中,后者的尺寸更适合部署。

这里显然的问题是,在深度神经网络中,知识意味着什么?这里使用的第一个想法是,知识可以被视为输入和softmax概率之间的映射。例如,当你看一张猫的图片时,你希望猫的类别为1,其他所有类别为0。但神经网络可能不一定给你那个输出,它可能说猫的概率是0.8,其他类别的概率可能是0.01、0.05等等。现在,这些输出代表了神经网络在训练过程中获得的知识。

因此,在知识蒸馏中,现在的想法是采用一个浅层的小型学生网络,不是用硬标签或独热编码标签来训练这个网络,而是要求学生网络以预测教师网络的softmax概率甚至logits为目标。你可以看到这里有一个笨重的模型,通过蒸馏,蒸馏模型的目标是匹配教师模型的软输出或目标。从这个意义上说,教师模型获得的知识被提炼到学生模型中,学生模型以更小的存储占用实现了相近的性能。

这里有一个简单的实验例子。给定一个笨重的模型(这是一个具有1200个ReLU节点和dropout的两层网络)和一个较小的模型(一个具有800个ReLU节点的两层网络,至少没有正则化),很容易观察到:笨重模型在MNIST上的错误数是67。如果你使用标准训练训练小模型,它在MNIST测试集上会犯146个错误,而使用蒸馏的小模型只犯74个错误,这接近笨重模型的性能。

多年来,知识蒸馏已经产生了多种变体,其中不仅仅是匹配教师模型的logits或概率输出,还可以匹配隐藏层的中间表示,可以添加一些噪声并尝试集成多个教师模型,等等。这些在参考文献中提供以供进一步阅读。

彩票假设:寻找幸运子网络

我们将在这里讨论的第三种方法是2019年ICLR上发表的一个较新的方法,称为彩票假设。顾名思义,这是基于一个观察:当你训练一个完整的庞大深度神经网络时,通常,剪枝后获得的一个非常稀疏的子网络产生的准确率接近完整模型。所以,你随机初始化权重并训练一个完整网络,得到90%的准确率;你剪枝后得到一个具有90%准确率的稀疏子网络。但是,如果你取同样的子网络,随机重新初始化并训练,你只得到60%的准确率。所以,似乎训练完整网络然后剪枝有一些特别之处。

因此,这项工作提出了一个假设:一个随机初始化的密集神经网络包含一个子网络,该子网络被初始化,使得当它被单独训练时,最多经过相同次数的迭代训练,它可以匹配原始网络训练后的测试准确率。

现在对我们来说,明显的问题是如何找到这个子网络?为此,这种方法提出了一个简单的想法,称为一次性剪枝。首先,用随机初始化训练一个完整网络。然后,剪掉完整网络中最小权重的某个百分比P。最后,将剩余权重重置为它们之前的初始化值,以创建“中奖彩票”。他们表明,遵循这个程序有助于我们找到彩票,即那个似乎能匹配完整网络准确率的随机稀疏子网络。

你也可以像我们在深度压缩中提到的迭代剪枝那样,在多轮中重复剪枝网络,这确实能得到更好的结果,但当然需要更多的计算。

这里展示了这项工作中的一些结果。在左侧,你看到的是剩余权重百分比与MNIST和CIFAR-10上早期停止迭代次数的关系。你可以看到,如果你看这两条曲线,其中虚线是随机采样的稀疏网络,粗线是通过彩票假设获得的网络。你可以看到,即使早期停止迭代次数非常低,彩票假设的剩余权重百分比仍然很高。使用这种方法,在CIFAR-10上也观察到了类似的模式。

另一方面,更重要的是,剩余权重百分比与准确率的关系图再次对彩票假设有利。你可以看到这里的虚线和这里的粗线,虚线是随机采样的稀疏子网络,粗线是使用彩票假设获得的网络。你可以看到,随着剩余权重百分比的减少(这里从100到0.2),随机子网络的准确率下降得更快,而彩票假设在更长的时间内保持了更高的准确率。在CIFAR-10上,对于不同种类的层,也看到了类似的结果。

那么,这总是一个好策略吗?并不完全是。虽然迭代剪枝能产生更好的结果,但它可能需要每轮剪枝训练网络大约15次。另一方面,寻找彩票假设虽然有时可以做到,但可能无法像原始网络那样提供同样好的性能。如果你进行迭代剪枝,研究像ImageNet这样的大型数据集将会更加困难。

因此,在过去的几年里,对原始的彩票假设工作已经有了改进,研究人员研究了这些“中奖彩票”是否可以在训练早期找到,而不是等待太多迭代;这样的中奖彩票是否能推广到新的数据集和优化器;以及这个假设是否在其他领域如文本处理或强化学习中成立。

其他方法与总结

这些方法也以几种不同的方式得到了扩展。XNOR-Net是一种流行的压缩方法,它使用二值权重。这里的理解是,你不需要用太多位来表示每个权重的精度,仅使用二值权重并用XNOR操作替换卷积,就可以使神经网络以非常小的内存占用获得合理的准确率。

TinyNet通过滤波器剪枝压缩CNN。知识蒸馏方法使用了带噪声的教师模型,其中教师模型的logits被扰动以获得多个教师的效果来训练学生。关系知识蒸馏在蒸馏中采用了度量学习。还有一些特定的架构,我们在讨论CNN时讨论过其中的一些,如MobileNets、ShuffleNet、用于检测的SSD和SqueezeNet等,它们都被用于模型压缩。

正如我们之前所说,这个领域相当广阔,还有低秩分解方法,也有以特定方式设计卷积滤波器以节省参数的方法,等等,我们在本讲座中留作阅读。

本节课中,我们一起学习了模型压缩的重要性以及三种主要技术:深度压缩通过剪枝、量化和编码来大幅减小模型尺寸;知识蒸馏通过让小型学生网络模仿大型教师网络的输出来转移知识;彩票假设则探索了在随机初始化的密集网络中寻找高性能稀疏子网络的可能性。这些技术使得将强大的深度学习模型部署到手机、物联网设备等资源受限的环境中成为可能。

你的作业是阅读一篇关于彩票假设的优秀综述,以及这篇关于深度神经网络不同模型压缩和加速方法的全面综述。

078:神经架构搜索 🔍

在本节课中,我们将学习神经架构搜索。这是本周最后一个高级主题,也是一个当代热门话题。它旨在为给定问题自动搜索合适的神经网络架构。

考虑到设计神经网络时涉及的各种超参数,这个问题的重要性不言而喻。通过本课程的学习,我们已经看到,通过更好的架构设计,模型的准确性和性能得到了提升。但这通常需要人类专家花费数百小时进行反复试验和超参数调整。即便如此,也并不意味着已经穷尽了所有可能的架构空间。研究人员通常基于先前提出的架构,并调整部分元素来适应新问题。这就引出了一个问题:我们能否采用一种系统、自动化的方式来学习高性能的模型架构?我们将要讨论的解决方案就是神经架构搜索。

神经架构搜索概述

神经架构搜索并非一个全新的概念。在早期阶段,人们会使用进化算法(如遗传算法)来设计神经网络架构。这引发了对架构的随机搜索,甚至是贝叶斯优化以及过去十年间的超参数调优的探索。近两到三年,神经架构搜索已发展成为一个独立的重要领域。它最初主要基于强化学习原理来搜索架构。

神经架构搜索的一般问题设置如下:

  • 搜索空间:定义了针对给定问题所要探索的所有可能架构的集合。

  • 搜索策略:用于在搜索空间内寻找架构的方法。

  • 性能评估策略:用于估计某个架构性能的方法。使用标准的训练和验证集进行评估可能计算成本高昂,因此需要谨慎选择。

因此,整个流程是:搜索策略在搜索空间中寻找架构,性能评估策略估计该架构的性能,然后搜索策略根据评估结果继续寻找新的架构,如此循环,直到性能达到稳定且令人满意的水平。

搜索空间的定义

NAS方法的搜索空间至关重要。那么,搜索空间是如何定义的呢?我们可以将神经网络视为通过一系列操作将输入变量转换为输出变量的函数。如果将神经网络看作计算图,每个节点代表一个张量,并与对其父节点执行的操作相关联。

节点 k 的计算可以表示为:

Z^k = operation_k (Z^i, i ∈ I_k)

这里的操作可以是卷积、池化、激活函数,也可以是连接、逐元素相加等组合操作。NAS的搜索空间通常是这个广义神经网络架构定义的一个子空间。

NAS方法使用的搜索空间大致可分为两类:全局搜索空间和基于单元的搜索空间。

全局搜索空间

在全局搜索空间中,方法允许任何类型的架构,因此在安排操作方面有很大的自由度。它包含一组允许的操作,如卷积、池化、全连接层、全局平均池化等,每种操作都有不同的超参数设置(如滤波器数量、尺寸等)。但也会指定一些约束,例如,可能不希望网络的第一层是池化操作,也不希望在卷积层之前出现全连接层等。

全局搜索的问题是,搜索空间相对固定,考虑到其广泛性,扩展和迁移可能不切实际。

基于单元的搜索空间

基于单元的搜索空间最早在2018年CVPR的一篇论文中提出,该方法被称为NASNet。其核心思想是,许多手工设计的架构实际上是通过重复特定的结构构建的。例如,ResNet中的残差块。这种方法通过定义单元结构,并通过重复该单元结构来构建网络架构,从而推进了这一思想。

一个单元可以看作是一个小的有向无环图,代表一种或一系列变换。在NASNet中,方法学习了两种单元:

  • 普通单元:输入和输出的维度保持不变(例如卷积操作)。

  • 降维单元:输出特征图的宽度和高度会减小(例如池化操作)。

以下是两种搜索空间的示例:

  • 全局搜索空间:可以是链式结构,给定输入后,有一系列操作需要从操作集合中搜索确定。另一种变体是在链式结构中包含跳跃连接。

  • 基于单元的搜索空间:定义了一个架构模板。例如,输入首先经过一个普通单元,然后是一个降维单元,接着又是普通单元、降维单元、普通单元,最后是Softmax层。在NASNet中,这些单元本身也是在操作集合中搜索得到的。

搜索策略:从强化学习到可微分搜索

正如之前提到的,神经架构搜索在2017年因基于强化学习的方法而流行起来。虽然本课程未涵盖强化学习,但我们将从高层次解释这种NAS方法的工作原理。

该方法提出使用一个控制器来提出子架构。控制器通常实现为循环神经网络,它输出一系列用于配置网络架构的标记。控制器RNN的训练类似于强化学习任务,使用一种名为REINFORCE(使用蒙特卡洛策略梯度)的流行算法。在这种情况下,动作空间是定义子网络的标记列表,奖励是子网络的准确率,损失是REINFORCE损失。

这种方法的局限性在于,当将整个搜索空间视为一个图,每条路径代表一个可能由特定操作序列构成的架构时,每个路径(架构)都需要独立训练以评估其性能,这非常耗时。

2018年ICML上提出的EfficientNAS方法提出了一个问题:我们能否将所有模型轨迹视为单个有向无环图的子图?答案是肯定的。EfficientNAS通过在子模型之间共享参数来实现这一点。在这种方法中,一个“超级图”包含了所有可能的操作序列,每个架构成为该图中的一个子图或轨迹。控制器RNN和所选路径对应的权重交替训练,最终找到合适的架构。这种方法在CIFAR-10数据集上达到了接近当时最佳水平的错误率,且搜索时间大幅减少。

一个更新的发展是2019年ICLR上提出的DARTS。其关键思想是使用一组关于操作的二元变量来定义搜索空间。层k的输出Z^k现在是操作O乘以一个二元变量再与来自前一层的输入Z^i的加权和。DARTS的洞见在于,它放松了对操作选择的硬性限制,将二元变量α变为可学习的参数,从而将选择特定操作这一分类决策,松弛为在所有可能操作上的一个Softmax分布。这使得我们可以通过反向传播来学习操作本身的选择。

在DARTS中,采用了一种交替的双层优化方法:第一步,通过最小化训练集上的损失来训练特定架构的模型参数w;第二步,通过最小化验证集上的损失来学习结构参数α(即每个操作的权重)。最终架构根据使α最大化的操作序列来选择。这是一个优雅的解决方案,使所有参数可微分,从而将整个架构搜索过程变为一个可微分的过程。

其他考虑与未来方向

NAS社区还关注架构可迁移性。研究表明,许多当代NAS方法在这方面表现良好,在CIFAR-10上学习的架构也能在ImageNet和其他数据集与任务上取得不错的效果。

尽管本次关于NAS的讨论比较简要,但让我们探讨一些未来的方向和开放性问题:

  1. 搜索效率:基于强化学习的NAS可能在标准GPU上产生长达数天的训练开销。如何提高搜索效率是一个重要问题。

  2. 减少约束的搜索空间:提高搜索效率的一种方法是更多地约束搜索空间,但我们也希望不要过度约束,以便探索更好的架构。

  3. 设计高效架构:结合剪枝、量化后的高效架构,或者通过NAS寻找“彩票假设”中的中奖网络,可能是这个领域有趣的方向。

  4. 联合优化:联合优化深度学习流程的所有组件,而不仅仅是架构,例如数据增强、激活函数、优化器等,将是未来有趣的任务。

  5. 多模态任务架构设计:为视觉-语言等多模态任务设计架构也是一个非常重要且有用的方向。

一个有趣的观察:随机搜索的案例

在结束本讲之前,有一个由一些研究人员提出的有趣观察,即“随机搜索的奇特案例”。在ICLR 2019的一项工作中,研究人员观察到,当随机搜索神经网络架构时,使用随机搜索、强化学习或进化算法之间的准确率差异并不大。这引发了一个重要讨论:我们是否真的需要神经架构搜索?其他几篇论文也支持了这一观察,表明随机搜索和神经网络的随机连接可以达到相当合理的性能。这与早期在搜索神经网络架构方面的观察形成了对比,为社区留下了一个有趣且开放的问题。

总结

本节课我们一起学习了神经架构搜索。我们了解了NAS的基本问题设置,包括搜索空间、搜索策略和性能评估。我们探讨了全局搜索空间和基于单元的搜索空间的区别。接着,我们回顾了从基于强化学习的早期方法,到更高效的EfficientNAS,再到可微分的DARTS等搜索策略的演进。最后,我们讨论了NAS的可迁移性、未来挑战,以及关于随机搜索有效性的有趣观察。NAS是一个快速发展的领域,旨在自动化神经网络设计过程,以发现更优的模型架构。


推荐阅读:

  • Lilian Weng 关于神经架构搜索的博客文章。

  • 关于神经架构搜索的两篇综述论文(如需更多信息)。

080:神经网络回顾 - 第一部分

在本节课中,我们将回顾神经网络的基础知识。我们将从神经网络的历史开始,了解其早期模型,并深入探讨感知机及其学习算法的工作原理。本节内容旨在为后续深入学习计算机视觉中的深度学习技术奠定基础。

神经网络简史

神经网络的历史或许比计算本身的历史更为悠久。它始于20世纪40年代初,当时研究人员McCulloch和Pitts致力于开发人脑的计算模型,并利用该模型模拟逻辑门等简单功能。

随后在20世纪40年代,心理学家Donald Hebb提出了至今仍在使用的Hebb学习法则。Hebb学习法则指出,在人脑中,两个同步激活的神经元之间的连接会增强,而两个不同步激活的神经元之间的连接则会减弱。

到了20世纪50年代,Frank Rosenblatt提出了第一个神经网络模型,称为感知机。感知机是最简单的神经网络,它接收一组输入,通过一组权重进行处理,然后输出一个经过阈值处理的最终决策。

在20世纪60年代初,Widrow和Hoff改进了学习规则,提出了Widrow-Hoff学习规则或Delta学习规则,这为当今使用的算法奠定了基础。然而,Rosenblatt声称感知机可以处理任何二元分类问题,但Minsky和Papert在20世纪60年代后期通过异或门的例子反驳了这一观点,这导致了神经网络在70年代的衰落。

20世纪80年代,随着1980年Neocognitron(被认为是卷积神经网络的雏形)的发展,以及1986年Rumelhart、Hinton和Williams提出的革命性反向传播算法,人们对神经网络的兴趣再次复兴。在80年代和90年代中期,卷积神经网络等变体被开发出来,并应用于手写数字识别等问题。

在90年代中期至21世纪初,支持向量机等传统机器学习算法占据了主导地位。直到21世纪中期,Geoffrey Hinton和Ruslan Salakhutdinov提出了分层特征学习算法,为深度学习的革命播下了种子。2012年,Alex Krizhevsky开发的AlexNet在ImageNet挑战赛中取得显著优势,标志着深度学习黄金时代的到来。

McCulloch-Pitts神经元模型

McCulloch是一位神经科学家,Pitts是一位逻辑学家,他们在1943年提出了一个简单的神经元计算模型。该模型的工作原理如下:

设有一个函数 G,用于聚合一组输入(当时均为二进制输入)。另有一个函数 F,根据聚合结果做出决策。输入在当时被认为是兴奋性的(1)或抑制性的(0或-1)。函数 G 表示为对各个输入的求和,最终输出 y 由 F(G(x)) 给出,其中向量 x 表示输入空间的不同维度。

输出规则为:如果 G(x) > θ,则输出为1;如果 G(x) < θ,则输出为0。这可以用以下公式表示:

y = 1 if Σ(x_i) > θ
y = 0 if Σ(x_i) < θ

以下是使用McCulloch-Pitts神经元模拟不同逻辑功能的几个例子:

  • AND函数:设置阈值 θ = 3。仅当所有三个输入 x1, x2, x3 均为1时,求和才大于3,神经元输出1。

  • OR函数:设置阈值 θ = 1。只要 x1, x2, x3 中任意一个为1,求和就大于等于1,神经元输出1。

  • NOR函数:使用抑制性输入(标记为带圈的输入)。只要有一个抑制性输入激活(为1),输出就为0。

  • NOT函数:使用单个抑制性输入,并设置阈值 θ > 0,即可模拟非门。

可以看到,使用McCulloch-Pitts神经元可以模拟大多数逻辑函数。事实上,可以证明,虽然不是单个神经元,但由McCulloch-Pitts神经元组成的前馈网络可以计算任何从n维布尔输入到布尔输出的函数。

感知机模型

Frank Rosenblatt在1958年提出了感知机模型,这是对McCulloch-Pitts神经元的推广。现在,输入不再局限于布尔值,可以是任何实数值。此外,每个输入都通过一组权重 W1 到 Wn 进行加权。

数学上,感知机可以表示为:函数 G 接收一个n维输入向量 x,其输出是权重向量 W 与输入向量 x 的内积,即 G(x) = W·x。最终输出 y = F(G(x)),规则为:如果 G(x) > θ 则输出1,否则输出0。

另一种更常见的写法是将阈值 θ 吸收到权重中。我们引入一个额外的常数值输入 x0 = 1,并对应一个权重 w0。这样,求和从 i=0 到 n,其中 w0 实际上代表了 -θ。此时,决策规则简化为:如果 Σ(w_i * x_i) >= 0 则输出1,否则输出0。这对应于一条将输入空间分为两半的决策线(或超平面),其方程为 W·x = 0。

感知机学习算法

感知机学习算法用于确定合适的权重 W。假设我们有一个数据集,包含正类样本集 P(标签为1)和负类样本集 N(标签为0)。

以下是算法的步骤:

  1. 随机初始化权重向量 W。

  2. 循环执行以下步骤,直到收敛(即所有样本都被正确分类):

    • 从 P 或 N 中随机选取一个数据点 x。

    • 如果 x 属于 P(正类),但当前感知机输出 W·x < 0(错误分类),则更新权重:W = W + x。

    • 如果 x 属于 N(负类),但当前感知机输出 W·x >= 0(错误分类),则更新权重:W = W - x。

    • 如果样本被正确分类,则权重保持不变。

算法原理直观理解

为什么这样更新权重是有效的?关键在于权重向量 W 与决策超平面 W·x = 0 垂直。对于一个属于正类 P 的点,我们希望 W·x >= 0,这意味着向量 x 与 W 之间的夹角应小于90度。

如果当前 W·x < 0(错误分类),说明夹角大于90度。通过执行 W_new = W + x,可以证明新的夹角 α_new 将小于旧的夹角 α。这是因为:

cos(α_new) ∝ (W + x)·x = W·x + x·x

由于 x·x(向量模长的平方)总是正数,因此 cos(α_new) > cos(α),这意味着 α_new < α。因此,更新后的权重使该正类样本更可能被正确分类。

对于负类样本的情况可以进行类似分析,更新规则 W = W - x 会使误分类的负类样本与 W 的夹角变大(超过90度),从而促使其被正确分类。

该算法在每次迭代中都会改善权重,并且在线性可分的数据集上可以证明是收敛的。

本节课中,我们一起回顾了神经网络的发展历程,从早期的McCulloch-Pitts神经元模型,到更通用的感知机模型及其学习算法。我们了解了感知机如何通过一个线性决策边界对数据进行分类,并学习了其权重更新的直观原理。这为后续理解更复杂的深度神经网络奠定了基础。

082:前馈神经网络与反向传播 - 第一部分

在本节课中,我们将继续讨论多层感知机,并学习如何训练它们。我们将从感知机学习算法出发,探讨如何将类似的方法应用于多层感知机,即前馈神经网络的训练。

神经网络作为函数近似器

上一节我们介绍了感知机的基本概念。本节中,我们来看看多层感知机,它也被称为前馈神经网络。之所以称为“前馈”,是因为信息从输入层开始,逐层向前传播,最终到达输出层。所有神经元都按层组织。虽然我们在上一讲中看到了只有一个隐藏层的多层感知机示例,但实际上,在输入层和输出层之间,可以根据需要设置任意数量的隐藏层。

本质上,前馈神经网络或感知机是在近似一个从输入到输出的函数。无论要近似的函数是什么,如果该函数很复杂,使用更多的隐藏层可能有助于更好地近似它。但这并非绝对必要,我们稍后会看到,添加过多的层也可能带来问题。

网络结构与函数复合

如前所述,神经网络通常用于近似某个理想函数 F*。在机器学习中,F* 是给定输入后能为其分配正确标签的理想函数,它可以是一个分类器。我们希望通过神经网络来近似这个函数。

神经元以有向无环图的形式组织。之所以是有向的,是因为所有从一层到下一层的边都是有向边,信息不能反向流动(尽管在训练时会用到反向传播,我们稍后会看到)。之所以是无环的,是因为图中没有循环,信息只从输入单向流向输出。

神经网络的层数通常被称为深度。每个神经元可以看作一个向量到标量的函数,它接收来自前一层的输入向量并计算出一个标量值。从这个意义上说,每个神经元都类似于一个感知机。

当神经网络中多层结构堆叠在一起时,可以将整个网络视为多个函数的复合。每一层都是一个特定的函数。例如,第一层函数 F1 将输入维度 R^n 映射到第一个隐藏层的维度 R^h1。类似地,F2 从 R^h1 映射到 R^h2,F3 从 R^h2 映射到输出层的维度 R^o。因此,每一层本身就是一个函数,整个神经网络可以被视为实现特定目标的函数复合。

训练目标与损失函数

在机器学习中,我们通常使用训练数据来训练算法。在监督学习设置下,训练数据同时提供了数据 x_i 和对应的标签 y_i。这里,x 是一个可能具有多个维度的向量,y 是对应输入数据点 x_i 的输出标签。训练数据集中总共有 M 个这样的样本。可以认为,这些训练样本是整体函数 F* 在不同点上的实例。

我们的神经网络定义了一个函数 y = f(x, θ),其中 x 是输入,θ 代表神经网络中所有权重和偏置参数。θ 是参数化神经网络的值,它决定了当信息通过网络传播时你将得到的输出函数。

我们的目标是找到一种方法,使 f 能最好地近似 F*。需要再次澄清的是,F* 是一个假设的、从输入映射到标签的函数。我们并不知道 F* 具体是什么,这正是机器学习的核心:我们只拥有 F* 的带噪声实例,但需要学习它本身。

现在的问题是:如何找到参数 θ(权重和偏置)的值来训练这个网络?假设给定一个特定架构的神经网络(例如,两个隐藏层,分别有10个和100个神经元)和一个训练数据集,我们的目标就是找到能使网络在训练数据上表现良好的权重。

梯度下降法简介

为了实现这个目标,我们引入优化中一个非常著名的方法:梯度下降法。梯度下降是一种简单但研究深入的优化方法,通常用于最小化目标函数,我们将用它来训练前馈神经网络。

神经网络通常通过最小化一个损失函数来训练,例如均方误差。当你给神经网络一个输入时,它会根据当前参数 θ 产生一个输出 f(x, θ)。例如,在病人诊断的例子中,输入病人属性(血型、年龄、是否吸烟等),神经网络会在输出层给出一个结果。

但在训练数据中,你已经知道对于这个特定的 x,正确的输出 F*(x) 应该是什么。因此,你可以计算网络输出 f(x, θ) 与真实标签 y 之间的误差。对所有训练数据点求和并取平均,就得到了均方误差:

MSE = (1/M) * Σ (y_i - f(x_i, θ))^2

让我们先看一个简单的一维例子来理解梯度下降。假设我们想最小化函数 f(x) = x^2。我们知道在 x* = 0 处取得最小值。

梯度下降法的思想是:给定函数 f(x),我们可以通过求导得到其在 x 点的斜率 f'(x)。如果在 x 点沿着斜率的方向(即梯度方向)前进一小步,函数值会增加。反之,如果沿着负梯度方向(即与梯度相反的方向)前进一小步,函数值则会减小。

因此,如果我们从一个随机的 x 值开始,不断沿着负梯度方向迈出一小步,迭代下去,最终就能到达函数值最小的点。这是一个寻找函数最小值的简单而有效的方法。

扩展到多变量与神经网络

现在考虑更复杂的情况:多变量设置。在训练神经网络时,我们要最小化的损失函数由网络中的许多权重参数化。我们将所有权重和偏置统一为一个量 θ,并将损失函数表示为 L(θ)。我们的目标是找到最小化 L(θ) 的权重向量 θ。

假设 u 是一个指向损失函数最小化方向的单位向量。我们想要最小化 u^T * ∇_θ L(θ)(即 u 与损失函数梯度 ∇_θ L(θ) 的点积)。由于 u 是单位向量,点积可以写成 ||u|| * ||∇_θ L(θ)|| * cos(β),其中 β 是 u 与梯度向量之间的夹角。

显然,范数总是非负的。为了最小化整个点积,我们需要让 cos(β) 尽可能小,其最小值为 -1。这意味着 u 必须指向负梯度的方向。因此,为了到达函数的最小值,我们必须沿着负梯度的方向前进。

梯度下降算法实践

现在,让我们看看如何在实践中使用梯度下降法来训练神经网络。

对于神经网络,我们从一个随机的权重向量 θ 开始。

  1. 计算网络在当前参数下,在整个训练数据上的损失函数 L(θ)(例如均方误差)。

  2. 计算损失函数相对于网络中每个需要学习的参数(每个权重、每个偏置)的梯度,记为 ∂L/∂θ_i。

  3. 根据梯度下降的迭代更新规则,更新每个参数:

    θ_i_next = θ_i_current - η * (∂L/∂θ_i)

    其中,η 称为学习率或步长,它决定了我们沿着负梯度方向前进的步幅大小。我们稍后会讨论如何选择学习率。

  4. 重复步骤1-3,直到梯度为零或接近零。当梯度为零时,我们到达了一个临界点,这很可能就是损失函数的最小值点。

神经网络中的梯度计算挑战

如前所述,前馈神经网络是组织成层的多个函数的复合。那么,在这种网络中如何实现梯度下降呢?

我们知道需要计算损失函数相对于网络中每个权重的梯度。这可以通过利用微积分中的链式法则来实现。

计算相对于某一层中某个权重 W_ij 的梯度,需要结合从该层到输出层所有涉及该权重的输出的梯度。例如,对于网络中某个特定的权重 W_ij,损失函数 L(θ) 定义在所有参数 θ(即所有权重 W 和偏置 b)上。我们需要求 ∂L(θ)/∂W_ij。

由于 W_ij 影响了它所在神经元的输出,而这个输出又作为下一层神经元的输入,影响下一层的输出,如此逐层传播直至最终影响损失函数。因此,我们必须通过链式法则,将所有层中 W_ij 的贡献累加起来,才能得到损失函数相对于该权重的梯度。这就是反向传播算法的核心思想,我们将在下一部分详细探讨其具体算法步骤。


本节课中我们一起学习了前馈神经网络的基本训练框架。我们了解到神经网络是函数的近似器,其训练目标是通过调整参数最小化损失函数。我们引入了梯度下降法作为核心优化工具,它通过沿着损失函数负梯度的方向迭代更新参数来寻找最小值。最后,我们指出了在多层复合的神经网络中计算梯度需要用到链式法则,这为下一节讲解反向传播算法奠定了基础。

083:前馈神经网络与反向传播 - 第二部分

在本节课中,我们将深入学习反向传播算法的具体细节。我们将从定义损失函数和符号开始,逐步推导出反向传播的数学公式,并最终得到一个完整的神经网络训练算法。我们将确保每一步都清晰易懂,并使用公式和代码来阐明核心概念。

概述

上一节我们介绍了前馈神经网络的基本结构。本节中,我们将探讨如何训练这个网络,核心方法是结合梯度下降和链式法则的反向传播算法。我们将学习如何计算网络中每个参数的梯度,并使用这些梯度来更新参数,从而最小化损失函数。

损失函数定义

首先,我们需要定义一个衡量网络预测好坏的函数,即损失函数。假设我们有一个包含 M 个训练样本的数据集 {(x_i, y_i)},网络的所有权重和偏置参数集合为 θ。

对于一个单独的样本 (x, y),我们使用均方误差作为损失函数。网络对输入 x 的输出记为 h_θ(x),则单个样本的损失 L(θ; x, y) 为:

L(θ; x, y) = 1/2 * (h_θ(x) - y)^2

这里引入系数 1/2 纯粹是为了后续求导时的数学简便性,它不会改变使损失最小化的参数 θ。

对于整个训练集,总成本函数 J(θ) 是所有样本损失的平均值:

J(θ) = 1/M * Σ_{i=1}^{M} L(θ; x_i, y_i)

我们的目标就是找到一组参数 θ,使得 J(θ) 最小。

符号约定与正向传播

为了清晰地描述反向传播,我们需要建立一套符号系统。

  • 假设网络共有 N_l 层。

  • 用 a_i^{(l)} 表示第 l 层第 i 个神经元的激活值(即该神经元的输出)。

  • 用 w_{ij}^{(l)} 表示连接第 l 层第 j 个神经元到第 l+1 层第 i 个神经元的权重。

  • 用 b_i^{(l)} 表示第 l+1 层第 i 个神经元的偏置。

  • 用 z_i^{(l)} 表示第 l 层第 i 个神经元的加权输入,即 z_i^{(l)} = (Σ_j w_{ij}^{(l-1)} * a_j^{(l-1)}) + b_i^{(l-1)}。

  • 激活函数记为 f(·),因此 a_i^{(l)} = f(z_i^{(l)})。

基于以上符号,一个三层网络的正向传播过程可以用向量化形式简洁地表示:

z^{(2)} = W^{(1)} * x + b^{(1)}

a^{(2)} = f(z^{(2)})

z^{(3)} = W^{(2)} * a^{(2)} + b^{(2)}

a^{(3)} = f(z^{(3)}) = h_θ(x)

这个过程从输入层开始,逐层计算,直到得到输出层的激活值,称为正向传播。

反向传播算法原理

在正向传播得到输出 h_θ(x) 后,我们可以计算损失。反向传播的目标是计算损失函数 J(θ) 关于网络中每一个权重 w 和偏置 b 的梯度(偏导数),即 ∂J/∂w 和 ∂J/∂b。有了梯度,我们就可以使用梯度下降法更新参数:

w_{new} = w_{old} - η * (∂J/∂w)

b_{new} = b_{old} - η * (∂J/∂b)

其中 η 是学习率。

反向传播的核心思想是链式法则。损失是网络输出的函数,而输出又是每一层权重和输入的函数。因此,计算损失对底层参数的梯度需要将梯度从输出层向输入层反向传递。

误差项 δ 的定义与计算

为了便于计算,我们为网络中的每个神经元引入一个中间量——误差项 δ_i^{(l)}。它定义为损失函数关于该神经元加权输入 z_i^{(l)} 的偏导数:

δ_i^{(l)} = ∂J / ∂z_i^{(l)}

这个误差项衡量了该神经元对最终损失应负的“责任”。

1. 输出层 (第 N_l 层) 的误差

对于输出层的第 i 个神经元,其误差可以直接计算。以均方误差损失为例:

δ_i^{(N_l)} = ∂J / ∂z_i^{(N_l)} = (∂J / ∂a_i^{(N_l)}) * (∂a_i^{(N_l)} / ∂z_i^{(N_l)})

其中:

  • ∂J / ∂a_i^{(N_l)} = (a_i^{(N_l)} - y_i) (对于带 1/2 系数的均方误差)

  • ∂a_i^{(N_l)} / ∂z_i^{(N_l)} = f'(z_i^{(N_l)}),即激活函数在 z_i^{(N_l)} 处的导数。

因此:

δ_i^{(N_l)} = (a_i^{(N_l)} - y_i) * f'(z_i^{(N_l)})

2. 隐藏层 (第 l 层) 的误差

对于隐藏层的神经元,其误差 δ_i^{(l)} 不能直接计算,但可以通过其后一层 (l+1 层) 的误差反向传播得到:

δ_i^{(l)} = ( Σ_{j} w_{ji}^{(l)} * δ_j^{(l+1)} ) * f'(z_i^{(l)})

这个公式的含义是:第 l 层第 i 个神经元的误差,等于所有以它为输入的下一层神经元误差的加权和(权重是连接它们的参数 w_{ji}),再乘上该神经元自身激活函数的导数。

计算权重和偏置的梯度

一旦我们有了每一层神经元的误差项 δ,计算梯度就变得非常简单。

损失函数关于权重 w_{ij}^{(l)} 的梯度为:

∂J / ∂w_{ij}^{(l)} = a_j^{(l)} * δ_i^{(l+1)}

损失函数关于偏置 b_i^{(l)} 的梯度为:

∂J / ∂b_i^{(l)} = δ_i^{(l+1)}

直观理解:权重 w_{ij}^{(l)} 连接了第 l 层的激活输出 a_j^{(l)} 和第 l+1 层的误差 δ_i^{(l+1)},因此梯度是这两者的乘积。偏置的输入可以看作恒为1的激活值,所以其梯度就是误差本身。

完整的反向传播算法

现在,我们可以将上述步骤整合成一个完整的算法,用于在单个训练批次上计算所有参数的梯度。

以下是算法的步骤:

  1. 初始化梯度累加器:将所有权重梯度 ∇W^{(l)} 和偏置梯度 ∇b^{(l)} 初始化为零。

  2. 对每个训练样本 (x, y):

    a. 正向传播:输入 x,计算并保存每一层的 z^{(l)} 和 a^{(l)},直到得到输出 h_θ(x)。

    b. 计算输出层误差:δ^{(N_l)} = (a^{(N_l)} - y) ⊙ f'(z^{(N_l)}),其中 ⊙ 表示逐元素相乘。

    c. 反向传播误差:对于 l = N_l-1, N_l-2, ..., 2,计算:

    δ^{(l)} = ( (W^{(l)})^T * δ^{(l+1)} ) ⊙ f'(z^{(l)})

    d. 计算并累加梯度:对于每一层 l,计算该样本贡献的梯度并累加:

    ∇W^{(l)} += δ^{(l+1)} * (a^{(l)})^T

    ∇b^{(l)} += δ^{(l+1)}

  3. 计算平均梯度:所有样本处理完毕后,计算整个批次的平均梯度:

    ∇W^{(l)} = (1/M) * ∇W^{(l)}

    ∇b^{(l)} = (1/M) * ∇b^{(l)}

现在,我们得到了损失函数 J(θ) 关于所有参数 W 和 b 的梯度 ∇W 和 ∇b。

参数更新与训练循环

获得梯度后,我们使用梯度下降法更新参数:

W^{(l)} = W^{(l)} - η * ∇W^{(l)}

b^{(l)} = b^{(l)} - η * ∇b^{(l)}

将“正向传播 -> 反向传播计算梯度 -> 参数更新”这三个步骤循环进行,直到损失函数收敛到最小值或达到预定的训练轮数,就完成了一个神经网络的训练过程。

总结

本节课中,我们一起深入学习了反向传播算法。我们从定义损失函数开始,引入了误差项 δ 的概念,并推导了其从输出层到隐藏层的反向传播公式。利用误差项,我们能够高效地计算出损失函数关于所有权重和偏置的梯度。最后,我们将这些步骤整合成一个完整的训练算法,该算法通过反复迭代正向传播、反向传播和参数更新,使神经网络能够从数据中学习。

反向传播是深度学习得以成功的基石,它使得训练复杂的多层神经网络成为可能。理解其原理对于后续学习更高级的模型和技巧至关重要。

延伸阅读建议

  • 深度学习(花书):阅读第4章和第6章中与本课内容相关的部分。

  • 斯坦福大学CS231n课程笔记:其中关于反向传播的讲解非常直观。

  • UFLDL教程:提供了反向传播的详细数学推导和代码实现。

建议通过动手实现一个简单的神经网络来巩固对这些概念的理解。

084:梯度下降及其变体 - 第一部分

概述

在本节课中,我们将学习梯度下降算法在训练神经网络时面临的挑战,并探讨其改进变体,特别是动量法和Nesterov加速动量法。我们将从回顾基础梯度下降开始,理解误差曲面的复杂性,并学习如何通过引入动量来加速训练并避免震荡。

梯度下降回顾

上一节我们介绍了神经网络和反向传播。本节中,我们来看看用于优化神经网络的核心算法——梯度下降。

梯度下降是一种用于寻找任何可微函数最小值的优化算法。用于训练神经网络的损失函数(例如我们目前使用的均方误差)必须是可微的。如果尚不了解其他损失函数,无需担心,我们将在后续课程中学习。

在使用梯度下降时,参数会沿着成本函数(或称误差函数、损失函数)梯度的负方向更新。参数更新规则由以下公式给出:

θ_new = θ_old - α * ∇L(θ)

其中,α 是学习率或步长,∇L(θ) 是损失函数关于参数的梯度。

我们可以在一个简单的凸函数上可视化这个过程。如果在最小值左侧取一点,该点梯度为负,沿负梯度方向(即正方向)移动将靠近最小值。如果在右侧取一点,梯度为正,沿负梯度方向移动同样会靠近最小值。这直观展示了梯度下降如何最小化目标函数。

以下是梯度下降算法的步骤:

  1. 给定学习率 α、初始参数 θ_t 和训练数据 D_train。

  2. 只要停止条件未满足,就执行以下循环(每个循环称为一个“epoch”):

    a. 初始化参数更新量 Δθ = 0。

    b. 对于训练数据 D_train 中的每个数据点:

    i. 使用反向传播计算损失函数关于每个参数的梯度。

    ii. 将梯度累加到 Δθ 中。

    c. 应用更新:θ_{t+1} = θ_t - α * (1/|D_train|) * Δθ。

当梯度变为零时,算法停止。

神经网络的误差曲面

理解了基础算法后,我们需要认识训练神经网络的核心挑战之一——误差曲面的复杂性。

神经网络的误差曲面是所有参数取值与对应损失函数值之间的关系图。现代神经网络(尤其是计算机视觉中)拥有数百万参数,这意味着误差曲面存在于一个极高维的空间中,其形状可能非常复杂。

训练神经网络的目标是从误差曲面上的某个随机初始点出发,通过梯度下降找到最小值。然而,误差曲面通常是非凸的,存在许多“局部最小值”。梯度下降最终收敛到哪个局部最小值,很大程度上取决于初始化的位置,而我们无法在训练前预知曲面的形状。

此外,高维非凸优化中,“鞍点”问题比局部最小值更为普遍。鞍点是梯度为零的点,但它在某些维度上是极小值,在另一些维度上是极大值。在高维空间中,找到一个在所有维度上都是极小值的临界点概率很低,因此鞍点大量存在。梯度下降若收敛到鞍点,训练就会过早停止。

梯度下降的挑战:震荡与平原

现在,让我们通过一个具体例子来观察梯度下降在复杂曲面上的行为。

观察梯度下降在误差曲面上的轨迹,可以发现更新速度并不均匀:在某些区域更新缓慢,在某些区域更新迅速。这引出了我们需要理解的两个关键概念:震荡与平原/平坦区域。

从等高线图视角看,参数更新的大小与误差曲面梯度的大小直接相关。在梯度小的区域,参数更新也小;在梯度大的区域,参数更新也大。

平原和平坦区域构成了误差曲面中梯度高度“非球形”的区域。这里的“非球形”是指梯度向量在各个维度上的值差异很大。在平原区域,某些维度的梯度很小,导致更新缓慢,梯度下降会在此类区域耗费大量时间。

一个自然的想法是:在平原上,能否通过增大步长(即提高学习率 α)来走得更快?这在一定情况下是有效的。然而,如果学习率设置得过高,可能会导致在梯度变化剧烈的区域更新步伐过大,从而跳过最小值,甚至导致损失上升和发散,无法收敛。

基于动量的梯度下降

为了解决上述问题,我们引入基于动量的梯度下降法。

动量法的核心思想是:结合当前梯度和过去更新的方向来决定当前步骤。类比一个盲人在喜马拉雅山脉下山:如果他一直朝某个方向下坡,即使在某一点用手杖探路发现另一个方向更陡,他可能也不会立刻完全转向,而是会结合之前的行进方向。

数学上,我们引入一个“速度”向量 v_t 来实现动量:

v_t = γ * v_{t-1} + α * ∇L(θ_t)

θ_{t+1} = θ_t - v_t

其中,γ 是动量参数,通常在0到1之间。v_{t-1} 包含了之前更新的历史信息。

当历史速度 v_{t-1} 与当前梯度方向相同时,动量会加速在这个方向上的移动,有助于快速通过平原。当它们方向不同时,动量起到缓冲作用,可以减少更新方向的剧烈变化,从而抑制震荡。

动量参数 γ 的选择:通常初始设为0.5,待训练初步稳定后,可以增加到0.9或0.99。实践中,很多人直接设置为0.9。

以下是动量梯度下降的算法步骤,主要修改在于速度向量的计算和更新:

  1. 初始化参数 θ,速度 v=0,设定学习率 α 和动量参数 γ。

  2. 对于每个epoch:

    a. 对于每个训练数据点(或小批量):

    i. 计算梯度 ∇L(θ)。

    ii. 更新速度:v = γ * v + α * ∇L(θ)。

    iii. 更新参数:θ = θ - v。

从等高线图可视化可以看出,标准梯度下降在非球形曲面上会沿之字形路径震荡前进。而动量法通过结合历史方向,有效减少了震荡,能以更直接的路径走向最小值,收敛速度更快。

Nesterov加速动量

动量法虽然加速了收敛,但在接近最小值时仍可能产生振荡。Nesterov加速动量法对此做出了改进。

Nesterov动量的核心思想是“向前看一步”:在计算当前梯度之前,先根据动量项向前走一步,在那个“向前看”的位置计算梯度,然后用这个“前瞻梯度”来更新。这相当于在决定下一步时,已经考虑了动量将要带我们去往的方向。

其更新公式如下:

v_t = γ * v_{t-1} + α * ∇L(θ_t - γ * v_{t-1})

θ_{t+1} = θ_t - v_t

与普通动量法的区别在于,梯度计算的位置是 θ_t - γ * v_{t-1},即当前参数加上一个动量项。这使得算法对未来的变化有更强的预见性。

在同一个示例中,Nesterov动量(蓝色轨迹)相比普通动量(红色轨迹),在接近最小值时的振荡更小,能以更平滑、更快的路径收敛。这是因为它利用了“向前看”的信息,提前调整了更新方向,避免了不必要的来回摆动。

总结

本节课中,我们一起学习了梯度下降法在训练深度神经网络时的局限性,包括收敛速度慢、易陷入鞍点和平原、以及在非球形误差曲面上的震荡问题。

为了克服这些挑战,我们介绍了两种重要的梯度下降变体:

  1. 动量法:通过引入速度向量,累积过去的梯度信息,从而在持续方向上加速,在不稳定方向上减速。这有助于加速训练并减少震荡。

  2. Nesterov加速动量法:动量法的改进,采用“向前看”的策略计算梯度,使优化过程更具预见性,能进一步减少收敛时的振荡,实现更快的收敛。

理解这些优化算法是有效训练深度神经网络的基础。在接下来的课程中,我们将继续探讨其他先进的优化器变体。

085:梯度下降及其变体 - 第二部分

在本节课程中,我们将继续探讨梯度下降优化算法。我们将分析不同梯度下降方法的优缺点,并介绍几种关键的改进算法,特别是用于自适应调整学习率的算法,如AdaGrad、RMSProp和Adam。这些方法旨在解决训练神经网络时遇到的各种挑战,如收敛速度慢、陷入局部极小值等问题。

梯度下降方法的比较与分析

上一节我们介绍了基于动量的梯度下降方法。现在,我们来分析一下目前所见梯度下降方法的优缺点。

以下是批量梯度下降(Batch Gradient Descent)的优缺点:

  • 优点:从优化理论角度看,其收敛条件有很好的理解。许多加速技术(如共轭梯度法)在批量梯度下降场景下效果很好。

  • 缺点:计算速度可能很慢,尤其是在训练数据集很大时(例如1万个或100万个数据点)。需要遍历整个数据集后才能进行一次参数更新。

批量梯度下降的缺点引出了另一种非常流行的方法:随机梯度下降(Stochastic Gradient Descent, SGD)。

随机梯度下降与Mini-Batch SGD

随机梯度下降的算法流程如下:首先随机打乱训练数据集,然后针对每个训练样本计算梯度并立即更新参数。这与批量梯度下降的关键区别在于,参数更新步骤位于遍历数据点的循环内部。

随机梯度下降虽然更快,但基于单个样本的更新方向可能非常随机。为了获得更稳定的梯度方向,实践中通常采用小批量随机梯度下降。

小批量随机梯度下降(Mini-batch Stochastic Gradient Descent)的做法是:每次从训练集中取一小批样本(例如20个),前向传播计算所有输出和误差,计算所有梯度后取这20个梯度的平均值,然后用这个平均梯度来更新参数。这种平均操作能提供跨多个数据点更一致的梯度方向。如今,当人们提到使用SGD时,通常指的就是小批量SGD。

以下是随机梯度下降(包括小批量SGD)与批量梯度下降相比的优缺点:

  • 优点:

    1. 训练神经网络通常比批量学习快得多,因为它利用了数据中的冗余性。

    2. 由于更新带有噪声,可能有助于逃离糟糕的局部极小值或鞍点,从而经常能得到更好的解。

    3. 易于跟踪变化,当新数据点到来时,可以轻松更新系统。

  • 缺点:

    1. 权重更新中的噪声可能导致不收敛。实践中,如果前几次迭代误差波动过大,可以尝试更改初始化或调整学习率。

    2. 需要选择合适的学习率。

学习率调整策略

学习率的选择至关重要:学习率太小,收敛缓慢;学习率太大,梯度可能爆炸。如果不使用动量,如何选择学习率呢?

以下是几种常见的学习率调整启发式方法:

  • 固定学习率:在整个训练过程中保持学习率恒定。

  • 步长衰减:每经过N次迭代,或在满足特定退化条件(如当前误差大于前一次误差)时,降低学习率。其思想是随着训练接近最小值,需要更小的步长以避免 overshoot。

  • 指数衰减:学习率按公式 α = α0 * e^(-k*t) 衰减,其中α0和k是超参数,t是迭代次数。

  • 1/t衰减:学习率按公式 α = α0 / (1 + k*t) 衰减。

这些方法都是启发式的。能否根据网络的实际行为更智能地调整学习率呢?答案是肯定的。

自适应学习率算法

一种早期的自适应方法是AdaGrad。其核心直觉是:稀疏但重要的特征(对应神经网络中的某些权重)可能梯度较小,学习在这些方向上可能很慢。AdaGrad为网络中的每个权重单独设置学习率。

AdaGrad算法为每个权重维护一个梯度平方的累积和 R_t。参数更新公式为:

θ_{t+1} = θ_t - α / (δ + √(R_t)) * Δθ_t

其中 Δθ_t 是当前梯度向量,δ 是一个小常数(如10^-7)用于数值稳定。

这个公式的效果是:对于历史梯度平方和R_t小的权重,有效学习率会变大;对于R_t大的权重,有效学习率会变小。这实现了对不同权重的差异化学习率调整。

但AdaGrad有一个问题:梯度平方累积项 R_t 会随着迭代不断增大,导致分母持续变大,最终所有权重的学习率都会变得极小,学习可能停滞。

RMSProp 方法通过修改 R_t 的计算方式来解决这个问题。它使用一个衰减率 ρ 来计算 R_t 的指数移动平均:

R_t = ρ * R_{t-1} + (1 - ρ) * (Δθ_t)^2

这确保了 R_t 不会无限增长,从而避免了学习率过早衰减的问题。

Adam:自适应矩估计

目前最流行的自适应学习率算法是Adam。它结合了RMSProp和动量的思想。

Adam同时维护两个移动平均:

  1. 梯度的一阶矩(均值)估计 s_t,类似于动量。

  2. 梯度的二阶矩(未中心化的方差)估计 r_t,类似于RMSProp中的梯度平方累积。

其参数更新公式为:

θ_{t+1} = θ_t - α * (s̃_t / (δ + √(r̃_t)))

其中 s̃_t 和 r̃_t 是经过偏差校正后的估计,用于解决初始迭代时移动平均初始化为0带来的偏差问题。偏差校正公式为:

s̃_t = s_t / (1 - ρ1^t), r̃_t = r_t / (1 - ρ2^t)

Adam算法因其良好的实践效果,已成为训练神经网络的首选优化器之一。

总结与挑战

本节课我们一起学习了梯度下降的各种变体及其改进方法。我们分析了批量梯度下降、随机梯度下降和小批量SGD的优缺点。重点介绍了自适应学习率算法,包括AdaGrad、RMSProp,以及结合了动量和自适应学习率的Adam算法。

然而,训练深度神经网络仍然面临诸多挑战:

  • 误差曲面中存在平坦区、局部极小值和鞍点。

  • 随着网络加深,会出现梯度消失或爆炸问题,导致误差曲面出现“悬崖”。

  • 权重矩阵或损失函数的病态条件数。

  • 梯度的数值计算不精确。

  • 局部结构与全局结构对应性差。

  • 选择学习率、动量系数等超参数本身也是一个难题。

深度神经网络的误差曲面通常是非二次、非凸、高维的,可能存在许多极小值和平坦区域。没有严格的保证网络一定会收敛到一个好的解,或者一定会收敛。但实践表明,使用随机梯度下降及其变体训练神经网络是有效的,并在全球众多应用中得到使用。

课后作业与思考:

  1. 如何判断自己处于损失曲面上的局部极小值点或其他临界点?

  2. 为什么使用梯度下降和均方误差训练深度神经网络是一个非凸优化问题?(这是我们本节课开始时提出的问题)

  3. 如果我们假设一个深度线性神经网络(完全没有激活函数),使用梯度下降和均方误差进行训练,是否仍然是一个非凸优化问题?

建议阅读《深度学习》书籍第8章的相关部分,以及斯坦福CS231n课程中关于优化算法的讲座。

087:神经网络正则化 - 第二部分

在本节课中,我们将继续探讨神经网络的正则化方法。我们将学习如何通过提前停止、数据增强、噪声注入、集成方法以及Dropout等技术,来防止模型过拟合训练数据,从而提升其在未知测试数据上的泛化能力。

提前停止 🛑

上一节我们介绍了通过惩罚权重范数(如L2正则化)来防止过拟合的方法。本节中,我们来看看另一种简单直接的正则化方法——提前停止。

提前停止的核心思想是:在训练过程中持续监控模型在验证集上的性能,一旦性能开始下降,就停止训练,即使此时模型在训练集上的性能仍在提升。这样可以防止模型过度拟合训练数据。

以下是提前停止的直观示例:假设Y轴是准确率,X轴是训练轮数。训练集准确率(红色曲线)可能持续上升,但验证集准确率(蓝色曲线)在达到某个峰值后开始下降。当验证集性能开始下降时,就是停止训练的最佳时机。

停止准则 ⏹️

提前停止引出了一个问题:我们如何确定何时停止训练?由于计算机的数值近似,梯度可能永远不会精确为零。因此,我们需要一些启发式准则来决定何时停止。

以下是几种常用的停止准则:

  1. 误差变化准则:在一个窗口期(如连续10个epoch)内,如果误差没有显著下降,则停止训练。停止后,可以再固定训练几个迭代以确保接近临界点。

  2. 权重变化准则:比较当前迭代(t)和之前某个迭代(t-10)的权重。如果所有权重中最大的变化量小于一个阈值(如 max(|w_t - w_{t-10}|) < ρ),则停止训练。阈值ρ也可以设置为权重本身的一个百分比。

数据增强 📈

另一种有效的正则化方法是数据增强。其思想是通过对原始训练数据进行各种变换,生成新的训练样本,从而增加数据量并让模型见识到更多样的数据,提升泛化能力。

数据增强的关键在于,某些图像变换不会改变其语义标签。例如,一只猫的图像无论大小、旋转角度如何变化,它仍然是一只猫。

以下是常见的数据增强操作列表:

  • 数据抖动:对图像进行模糊或轻微扭曲,以模拟测试数据中的噪声变化。

  • 旋转:将图像旋转一定角度。

  • 颜色变换:调整RGB通道的强度,或交换通道顺序(例如,将绿色通道当作红色通道)。

  • 注入噪声:在图像中添加高斯噪声。

  • 镜像翻转:水平或垂直翻转图像(适用于标签不变的场景)。

这些操作都是基于本课程早期介绍的图像处理技术。更现代的数据增强方法包括:

  • Mixup:通过线性插值创建虚拟训练样本。对于两个样本 (x_i, y_i) 和 (x_j, y_j),生成新样本:x̃ = λ * x_i + (1-λ) * x_j,ỹ = λ * y_i + (1-λ) * y_j。其中λ∈[0,1]。这迫使模型学习更平滑的决策边界。

  • Cutout:在训练过程中,随机将输入图像中的方形区域掩蔽(填充为黑色),让模型学习不依赖于局部特定特征。

噪声注入 🔊

除了在数据层面注入噪声,还可以在标签或梯度层面注入噪声,这同样能起到正则化的作用。

  • 数据噪声:在训练时向输入数据添加高斯噪声。可以证明,对于均方误差损失函数,向输入添加高斯噪声等价于进行L2权重衰减。这是一个有趣的关联,建议作为练习自行推导。

  • 标签噪声:以概率α随机扰动训练样本的标签,将其替换为从所有类别中均匀采样的一个标签。这可以防止模型对训练标签过于自信。

  • 梯度噪声:在反向传播更新权重之前,向梯度添加噪声。通常添加均值为0、方差为 σ_t² 的高斯噪声,并且让方差随着训练进行而衰减,例如 σ_t² = η / (1+t)^γ。

集成方法 🤝

集成方法通过训练多个不同的模型,并让它们共同决策来提升泛化性能。这是一种温和的正则化方法,因为即使某个模型过拟合,其他模型也可能纠正它。

一个标准的集成策略是模型平均。假设有K个不同的模型,对它们的输出进行平均。这些模型可以通过不同的超参数、不同的输入特征或不同的训练数据子集得到。

Bagging(自助聚合) 是一种经典的集成方法。它通过有放回抽样从原始训练集中创建K个不同的数据子集,并用每个子集训练一个模型。

从数学上分析,假设每个模型在测试样本上的误差 ε_i 来自均值为0的多变量正态分布,方差为 V,协方差为 C。那么,集成模型的均方误差为 (1/K)*V + ((K-1)/K)*C。这表明:

  • 如果模型误差完全相关(C = V),集成没有帮助。

  • 如果模型误差不相关(C = 0),集成模型的方差会降低为单个模型的 1/K。

Dropout:神经网络的集成正则化 🎲

将集成思想直接应用于大型神经网络的一个主要问题是协同适应:在迭代训练中,强大的连接会越来越强,而较弱的连接被忽略,这可能导致过拟合。

Dropout 是一种专门为神经网络设计的、高效的集成正则化方法。其工作原理如下:

  • 训练阶段:对于每个隐藏层,在每个训练样本(或小批量)的前向传播过程中,以概率 p(例如0.5)随机“丢弃”(即暂时忽略)一部分神经元。

  • 测试阶段:使用完整的网络,但将每个神经元的激活值乘以 p。

直觉理解:可以将每个神经元看作公司的员工。如果总是让固定的员工组队完成任务,他们可能会过度专业化。Dropout就像每周随机让一半员工上班,迫使所有员工都学习处理各种任务,从而建立一个更稳健的“组织”。

数学理解:一个具有H个隐藏单元的神经网络,理论上可以组成 2^H 个不同的子网络。Dropout在训练时随机采样这些子网络。可以证明,在测试时将激活值乘以 p,等价于对所有可能的 2^H 个子网络的输出取几何平均。这使得我们无需实际训练海量子网络再进行集成,只需在训练时随机丢弃,测试时简单缩放即可获得集成效果。


本节课中我们一起学习了多种防止神经网络过拟合的正则化技术。我们了解了如何通过监控验证集性能来实施提前停止,如何通过数据增强和噪声注入来丰富训练数据并提升模型鲁棒性,以及如何利用集成方法和其高效的神经网络变体——Dropout来平均多个模型的预测,从而显著降低方差并提升泛化能力。掌握这些技术对于构建强大、稳健的深度学习模型至关重要。

本节作业:

  1. 证明对于均方误差损失函数,向输入添加零均值高斯噪声等价于L2权重衰减。

  2. 阅读关于Dropout的教程以加深理解。

参考文献:

(见课程原图)

088:改进神经网络训练 - 第一部分

概述

在本节课中,我们将学习如何改进神经网络的训练过程。上一讲我们讨论了正则化及其对提升模型泛化性能的重要性。本节我们将聚焦于另一个关键组件:激活函数。我们将详细探讨不同激活函数的特性、优缺点,以及它们如何影响神经网络的训练效率和最终性能。

回顾:输入噪声与L2正则化

在深入新内容之前,我们先完成上一讲留下的练习:证明当损失函数为均方误差时,向输入添加零均值高斯噪声等价于L2权重衰减。

其核心直觉在于,当噪声通过一层网络时,其方差会被权重平方放大。对于一个简单的单层网络,放大后的噪声会直接加到输出上,并贡献到损失函数的平方误差项中,其效果类似于在损失函数中添加了一个L2惩罚项。

让我们进行数学推导。假设我们有一个输入 x,并为其添加了噪声 ε,其中 ε 服从均值为0、方差为 σ² 的高斯分布。那么带有噪声的输出 y_noisy 为:

y_noisy = Σ_i (w_i * x_i) + Σ_i (w_i * ε_i)

原始的干净输出为 y = Σ_i (w_i * x_i)。因此,新的均方误差期望值为:

E[(y_noisy - t)²] = E[(y - t + Σ_i w_i ε_i)²]

展开并利用期望的线性性质,同时注意到 ε_i 均值为0,交叉项期望为0,我们得到:

E[(y_noisy - t)²] = (y - t)² + Σ_i (w_i² * σ²)

由此可见,新的损失函数等于原始损失加上一项 Σ_i (w_i² * σ²),这正是L2权重衰减项的形式,其系数由噪声方差 σ² 决定。

激活函数详解

现在,让我们进入本节课的核心主题:激活函数。在第一周的介绍中,我们简要提到了激活函数。现在我们将花更多时间深入理解每一种激活函数及其利弊。

激活函数是应用于神经网络中每个神经元输出的非线性函数。传统上,人们在整个网络或至少同一层中使用相同的激活函数。这样做主要是为了简化计算复杂度,便于利用高效的矩阵向量运算,而非为每个神经元单独计算。

激活函数的理想特性

那么,激活函数应具备哪些特性呢?以下是一些关键点:

  • 连续且可微:这是反向传播计算梯度的基础。

  • 非递减(单调):理想情况下,我们希望输入增加时,输出不会高低波动,以保证梯度方向的稳定性。

  • 计算高效:复杂的计算会拖慢整个网络的训练速度。

常见的激活函数包括Sigmoid、双曲正切(Tanh)、线性整流单元(ReLU)等。我们该如何选择?它们对训练有何具体影响?接下来我们将逐一分析。

以下是几种常见激活函数的快速回顾:

  • Sigmoid:早期用于解决感知机阈值问题的激活函数,是逻辑函数,形状呈S型。

  • 双曲正切(Tanh):也是一种逻辑函数,形状与Sigmoid相似,但值域为[-1, 1]。

  • 线性整流单元(ReLU):2012年随AlexNet提出,形式为 max(0, x)。

  • 渗漏线性整流单元(Leaky ReLU):ReLU的变体,当输入为负时,输出为一个小的斜率(如0.01)乘以输入,而非0。

  • 指数线性单元(ELU):为解决ReLU在0点的不连续性而提出,在负输入区域是平滑的指数函数。

  • Maxout:一种不太传统的激活函数,它对一组神经元的输出取最大值,可以看作是ReLU的泛化。

接下来,让我们更详细地探讨其中几种激活函数,理解它们如何影响神经网络训练。

Sigmoid 激活函数

Sigmoid函数定义为 σ(x) = 1 / (1 + e^{-x}),它将所有输入压缩到(0,1)区间。其梯度为 σ'(x) = σ(x) * (1 - σ(x))。

尽管Sigmoid在80-90年代非常流行,但现在已不常用。原因如下:

  1. 梯度饱和:当输入值很大或很小时,Sigmoid函数的曲线变得非常平缓。这意味着输入的巨大变化只会导致输出的微小变化,梯度接近于0。这会导致权重更新缓慢,甚至停止学习,即“梯度消失”问题。

  2. 非零中心化:Sigmoid的输出值恒为正。这可能导致后续层神经元的输入全部为正,进而使得权重的梯度全部为正或全部为负(取决于共同的上一级梯度)。这种“锯齿状”的梯度更新路径会降低学习效率。

  3. 计算成本较高:涉及指数运算。

考虑一个简单网络片段:输入h1, h2,权重w1, w2,预激活a = w1*h1 + w2*h2,经过Sigmoid激活后输出y。根据链式法则,∂L/∂w1 = (∂L/∂y) * (∂y/∂σ) * (∂σ/∂a) * h1。如果h1和h2来自前一层Sigmoid的输出,则它们恒为正。同时,∂σ/∂a也恒为正。因此,w1和w2的梯度符号完全由(∂L/∂y) * (∂y/∂σ)决定,这可能导致该层所有权重同时向相同方向更新,从而减慢学习速度。

双曲正切(Tanh)激活函数

Tanh函数定义为 tanh(x) = (e^{x} - e^{-x}) / (e^{x} + e^{-x}),它将输入压缩到[-1, 1]区间。其梯度为 tanh'(x) = 1 - tanh²(x)。

与Sigmoid相比,Tanh有一个主要优势:

  • 零中心化:输出范围在-1到1之间,均值为0。这有助于缓解Sigmoid中因输出全正而导致的梯度更新问题,通常能使收敛更快。

然而,Tanh也有缺点:

  • 梯度饱和:与Sigmoid类似,当输入绝对值很大时,Tanh函数也会饱和,梯度趋近于0,导致“梯度消失”。

  • 计算成本较高:同样涉及指数运算。

线性整流单元(ReLU)激活函数

ReLU函数定义为 ReLU(x) = max(0, x)。其梯度为:当 x > 0 时,梯度为1;当 x <= 0 时,梯度通常被定义为0。

ReLU是当今最常用的激活函数,原因如下:

  • 缓解梯度饱和:在正区间,ReLU是线性的,梯度恒为1,不存在饱和问题。这使得在正输入区域,梯度可以有效地反向传播。

  • 加速收敛:在AlexNet中,使用ReLU比使用Sigmoid或Tanh使SGD的收敛速度加快了约6倍。

  • 计算极其高效:只需一个阈值判断和取值操作。

但是,ReLU有一个著名的缺陷:“神经元死亡”问题。

如果某个神经元的输入加权和(预激活a)在训练过程中始终为负,那么经过ReLU后其输出恒为0。在反向传播时,该神经元的梯度(∂ReLU/∂a)也为0。这意味着流向该神经元权重的梯度被完全切断,这些权重将永远不会得到更新。该神经元在后续训练中持续输出0,如同“死亡”,不再对网络有任何贡献。

如何缓解“神经元死亡”问题?

  1. 使用带正偏置的初始化:为正偏置设置一个较小的正值,有助于将初始预激活推向正区域。

  2. 使用ReLU的变体:如Leaky ReLU或ELU。

ReLU的变体:Leaky ReLU 与 ELU

  • Leaky ReLU:定义为 LeakyReLU(x) = max(αx, x),其中α是一个小的正常数(如0.01)。在负输入区域,它允许一个很小的、非零的梯度通过(斜率为α),从而确保权重仍然能得到微小的更新,避免了完全的“神经元死亡”。

  • 指数线性单元(ELU):定义为:当 x >= 0 时,ELU(x) = x;当 x < 0 时,ELU(x) = α(e^x - 1)。ELU具有平滑的曲线,在负区域渐近于-α,同时保持了ReLU在正区域的线性特性。其平滑性可能使优化过程更稳定。

如何选择激活函数?

在实践中,可以遵循以下指南:

  • 默认首选ReLU:它简单、高效且通常效果很好。需要监控网络中“死亡神经元”的比例。

  • 如果ReLU效果不佳:可以尝试 Leaky ReLU、ELU 或 Maxout。

  • Tanh 可以尝试,但性能通常不如ReLU,取决于具体任务。

  • Sigmoid 在现代网络中已很少用于隐藏层,除非需要特定的“门控”效果或输出概率(如二分类输出层)。Tanh 也在某些需要输出有界值的场景中使用。

一个重要的经验是:尽可能让激活函数工作在其线性区域。例如,对于Sigmoid,你希望输入值大部分落在中间近似线性的区间。有时可以通过调整函数参数(如缩放)来扩大线性区域,以适应你数据的分布,这有助于改善训练性能。关于这些技巧,可以参考一篇名为《Efficient BackProp》的经典文献。

总结

本节课中,我们一起学习了改进神经网络训练的一个重要方面:激活函数。我们首先通过数学推导,理解了向输入添加高斯噪声在均方误差下等价于L2权重正则化。然后,我们深入探讨了多种激活函数(Sigmoid, Tanh, ReLU及其变体)的特性、优缺点及其对训练动态的影响,特别是梯度饱和和神经元死亡问题。最后,我们给出了在实际应用中如何选择激活函数的实用建议。理解这些内容将帮助你在构建神经网络时做出更明智的设计选择。

089:改进神经网络训练 - 第二部分

在本节课中,我们将要学习改进神经网络训练的三个核心组件:权重初始化、Xavier/Glorot初始化方法的推导,以及批归一化技术。这些技术对于稳定训练、加速收敛和提升模型性能至关重要。

权重初始化的重要性

上一节我们讨论了优化器和学习率,本节中我们来看看另一个关键因素:权重初始化。

神经网络开始学习前,必须首先用特定值初始化权重,然后执行梯度下降。这非常重要,因为神经网络的误差曲面可能非常复杂,起始点决定了最终会收敛到哪个局部最小值。为了达到更好的局部最小值,可能需要从一个更好的初始位置开始。

一个简单的问题是:能否将所有权重初始化为零?答案是否定的。任何恒定的权重初始化方案效果都很差。

以下是一个例子来解释原因。考虑一个具有两个隐藏单元的神经网络,使用ReLU激活函数,偏置设为零,所有权重初始化为常数α。前向传播时,两个隐藏单元的输出将是 R(α*x1 + α*x2)。由于权重相同,两个隐藏单元将具有相同的输出,对损失函数产生相同的影响,进而获得相同的梯度。如果梯度相同,权重更新也将相同,导致权重在训练过程中始终保持一致。这不是我们期望的。

因此,我们需要一种比简单使用相同值初始化权重更有效的方法。

如何初始化权重

权重通常被随机初始化,例如从高斯分布中采样,以确保权重具有多样性。但需要注意,过大或过小的权重都可能导致Sigmoid或Tanh等激活函数饱和。

在2006年,权重初始化是推动深度神经网络受到关注的主要因素之一。Salakhutdinov和Hinton引入了一种称为“贪婪逐层无监督预训练”的方法。该方法建议在开始训练深度神经网络之前,先使用无监督技术(如受限玻尔兹曼机或自编码器)逐层训练网络以初始化权重。这种方法在几年内被认为是使深度神经网络在实践中工作的关键。

然而,如今人们理解到,有更简单的权重初始化方法可以达到同样好的效果,因此不再需要这种复杂的方法。

现代初始化方法的思路

为了理解新初始化方法的思路,我们考虑一个神经网络。假设所有输入都已归一化为均值为0、方差为1。同时假设权重均值为0,并具有某个未知的方差,我们的目标是找到这个方差。

考虑一个神经元 a1,其输出为 a1 = Σ_{i=1}^{n} W_i1 * x_i,其中 n 是输入数量(也称为“扇入”)。假设 W 和 x 不相关,且我们希望所有神经元具有相同的方差,那么 a1 的方差可以推导为 Var(a1) = n * Var(W) * Var(x)。

我们希望每一层激活值的方差都保持为1,以确保网络中的信号不会爆炸或消失。因此,我们要求 n * Var(W) * 1 = 1,即 Var(W) = 1 / n。

这意味着,为了良好的初始化,可以从均值为0、方差为 1/n 的高斯分布中采样权重,或者从均匀分布中采样并进行相应缩放。

基于这个思路,发展出了几种权重初始化方法。

以下是目前最流行的两种方法:

  • Xavier/Glorot初始化:从范围 [-√6/√(fan_in + fan_out), +√6/√(fan_in + fan_out)] 的均匀分布中采样权重。

  • He初始化:从范围 [-√6/√(fan_in), +√6/√(fan_in)] 的均匀分布中采样权重(针对ReLU激活函数优化)。

Xavier初始化的推导

我们之前推导出,在前向传播中,希望 Var(W) = 1 / fan_in。然而,在反向传播(误差反向传播)过程中,梯度流也会受到影响。为了保持梯度流的稳定,同样希望 Var(W) = 1 / fan_out(fan_out 是神经元输出到下一层的权重数量)。

因此,一个折中的方案是取两者的平均值:Var(W) = 2 / (fan_in + fan_out)。

现在,从范围 [-a, +a] 的均匀分布中采样,其方差为 a² / 3。

令 fan_in * (a² / 3) = 1,可解得 a = √3 / √fan_in。这是仅考虑前向传播的情况。

同时考虑前向和反向传播,我们使用平均方差 2 / (fan_in + fan_out)。令 (fan_in + fan_out)/2 * (a² / 3) = 1,可解得 a = √6 / √(fan_in + fan_out)。这就是Xavier初始化的均匀分布范围。

He初始化建立在此基础之上,但针对ReLU激活函数的特性进行了调整,其推导是留给读者的练习。

批归一化

现在,让我们进入本讲的最后一个重要组件:批归一化。这是2015年提出的一项重要发展。

协变量偏移是机器学习中的一个问题,指的是训练和测试场景之间输入分布的变化。在神经网络内部,这表现为内部协变量偏移,即数据分布在网络各层之间发生变化。即使第一层输入是归一化的,由于权重的作用,后续各层的输入分布也会不断变化,网络必须学习处理这些不同的分布。

我们知道,当网络输入经过白化或归一化(即线性变换为均值为0、方差为1)时,网络训练效果更好。那么,能否对每一层的输入都进行类似的归一化呢?

批归一化 提供了解决方案。由于我们使用小批量数据训练神经网络,可以计算每一层输出在一个小批量上的均值和方差,然后用它们来归一化该层的激活值。

具体来说,对于给定层在某个小批量的输出值,计算其均值 μ_B 和方差 σ_B²。然后进行归一化:x̂_i = (x_i - μ_B) / √(σ_B² + ε),其中 ε 是一个很小的数,用于数值稳定性。

但是,强制使每一层都具有零均值和单位方差可能会限制网络的表达能力。因此,批归一化引入了两个可学习的参数 γ 和 β。归一化后的最终输出为:y_i = γ * x̂_i + β。这样,网络可以通过学习 γ 和 β 来恢复数据的原始分布,或找到最有利于训练的新分布。

批归一化层通常插入在仿射变换层和激活函数之间。

批归一化的好处包括:

  • 允许使用更高的学习率:因为它控制了激活值的尺度,减少了梯度爆炸或消失的风险。

  • 降低对初始化的强依赖:无论初始权重如何,γ 和 β 可以在训练中调整分布。

  • 起到轻微的正则化作用:因为每个批次的均值和方差是基于该批次数据估计的,为激活值添加了噪声。

一个重要的细节是:在测试时,我们可能没有小批量数据(例如,单样本预测)。此时,我们使用在训练过程中计算得到的 μ_B 和 σ_B² 的移动平均值来进行归一化。

总结与练习

本节课中我们一起学习了改进神经网络训练的三个关键技术:权重初始化的原理与方法、Xavier初始化的推导过程,以及批归一化的动机、实现与优势。

以下是推荐的阅读材料和练习:

推荐阅读:

  1. 《深度学习》书籍第8章第8.7节。

  2. Yann LeCun 等人撰写的经典文章“Efficient BackProp”,其中包含了许多关于神经网络高效训练的深刻见解。

练习:

  1. 访问此链接,该页面提供了不同初始化方法下神经网络行为的动画演示,请尝试交互并观察。

  2. 批归一化是完全可微的。请尝试推导批归一化层中参数 γ 和 β 的梯度,即 ∂L/∂γ 和 ∂L/∂β。

  3. 根据课堂提示,尝试推导He初始化方法。

以上是一些参考资料。

090:卷积神经网络介绍 - 第一部分

在本节课中,我们将从简单的前馈神经网络过渡到计算机视觉深度学习的核心构建模块——卷积神经网络。在开始讨论之前,我们先回顾一下上周课程留下的作业或练习。

📚 批归一化层的反向传播

上一节我们介绍了批归一化算法,本节中我们来看看如何对其进行反向传播。以下是批归一化的计算图表示。

前向传播:给定输入 x1 和 x2 以及一个小批量数据,我们计算该层激活输出的均值和方差,然后使用均值和方差对数据进行归一化。归一化后,通过缩放参数 gamma 和平移参数 beta 进行重新调整,以适应具体问题。这是批归一化的核心思想。

反向传播:我们需要计算损失函数 L 对各个参数的梯度,以便将梯度传播到网络的前层。以下是关键步骤:

以下是计算梯度的主要步骤:

  1. 计算梯度 dL/dbeta:beta 是直接加到输出 y 上的。因此,dL/dbeta 等于所有输出 y_i 的梯度之和。

    dL/dbeta = Σ (dL/dy_i)
    
  2. 计算梯度 dL/dgamma:gamma 与归一化后的数据 x_hat 相乘。因此,dL/dgamma 等于 dL/dy_i 与对应的 x_hat_i 的乘积之和。

    dL/dgamma = Σ (dL/dy_i * x_hat_i)
    
  3. 计算梯度 dL/dx_hat:这是为了将梯度传播到前一层。dL/dx_hat_i 等于 dL/dy_i 乘以 gamma。

    dL/dx_hat_i = dL/dy_i * gamma
    
  4. 计算梯度 dL/dsigma^2:方差 sigma^2 用于计算 x_hat。dL/dsigma^2 等于 dL/dx_hat_i 与 dx_hat_i/dsigma^2 的乘积之和。dx_hat_i/dsigma^2 可通过 x_hat_i 的定义式求导得到。

    dL/dsigma^2 = Σ (dL/dx_hat_i * dx_hat_i/dsigma^2)
    
  5. 计算梯度 dL/dmu:均值 mu 同时影响 x_hat 和 sigma^2。dL/dmu 包含两部分:一部分来自 x_hat,另一部分来自 sigma^2。

    dL/dmu = Σ (dL/dx_hat_i * dx_hat_i/dmu) + dL/dsigma^2 * dsigma^2/dmu
    
  6. 计算梯度 dL/dx_i:这是最终需要传播到前一层输入的梯度。它由三部分组成:来自 x_hat、sigma^2 和 mu 的贡献。

    dL/dx_i = dL/dx_hat_i * dx_hat_i/dx_i + dL/dsigma^2 * dsigma^2/dx_i + dL/dmu * dmu/dx_i
    

通过以上步骤,我们可以完成批归一化层的反向传播。一个重要的启示是:在神经网络中引入任何新参数时,只要确保所有新增组件都是可微的,并仔细运用链式法则计算每一项的梯度,就能实现正确的反向传播。

🧠 卷积神经网络基础

现在,让我们进入卷积神经网络的世界。本部分内容主要基于Mish Kara的精彩讲座。

卷积回顾

在第一周的课程中,我们介绍了卷积。卷积是一种将两个信号组合成第三个信号的数学方法,是信号处理(包括图像处理)中极其重要的技术。图像是二维信号,但卷积也广泛应用于语音、加速度计数据、时间序列数据等其他类型信号。

对于二维数据(例如灰度图像),滤波器 W(尺寸为 k x k)与图像 X(尺寸为 n1 x n2)的卷积操作定义为:

Y[i, j] = Σ Σ W[u, v] * X[i - u, j - v]

其中,求和范围 u 和 v 从 -k 到 k。

更一般地,我们可以将卷积写为以像素为中心的求和形式:

Y[i, j] = Σ Σ W[u, v] * X[i + u, j + v]

其中,求和范围 u 从 -k1/2 到 k1/2,v 从 -k2/2 到 k2/2。这两种写法本质相同,只是索引方式不同。

在二维情况下,我们滑动一个二维滤波器(如Sobel边缘滤波器)在二维输入上操作。那么,三维情况呢?

三维卷积:处理彩色图像

图像本身是二维的,但彩色图像包含红、绿、蓝三个通道,这构成了第三个维度。为了同时处理所有通道,我们需要进行三维卷积。

  • 输入:一个彩色图像,是一个三维体积(宽度 W1 x 高度 H1 x 深度 D1),深度 D1=3 对应RGB通道。

  • 滤波器:也是一个三维体积(宽度 f x 高度 f x 深度 D1)。滤波器的深度必须与输入体积的深度相同。

  • 操作:将这个滤波器体积在输入体积的宽度和高度方向上滑动,在每个位置进行点乘求和,得到一个标量输出。这个输出构成了二维特征图上的一个像素点。

这里的关键在于,我们只在空间维度(宽和高)上滑动滤波器,而不在深度维度上滑动,因为深度已经匹配。因此,单个滤波器对三维输入进行卷积,产生的是一个二维输出(特征图)。

多滤波器与输出体积

如何得到三维的输出体积呢?答案是使用多个滤波器。

每个滤波器体积与输入体积卷积后,会生成一个独立的二维特征图。如果我们使用 K 个不同的滤波器,就会得到 K 个二维特征图。将这些特征图堆叠起来,就形成了一个三维的输出体积(宽度 W2 x 高度 H2 x 深度 D2),其中深度 D2 = K(滤波器数量)。

📏 卷积输出尺寸的计算

现在我们来推导卷积层输出特征图尺寸的计算公式。假设输入尺寸为 W1 x H1 x D1,滤波器空间尺寸为 f x f(深度为 D1),使用 K 个滤波器,输出尺寸为 W2 x H2 x D2。

首先,最直接的是深度:D2 = K。

接下来计算空间尺寸 W2 和 H2。这受到三个关键参数的影响:

  1. 滤波器尺寸 f:滤波器越大,在图像边界处无法进行有效卷积的区域就越多,会导致输出尺寸缩小。

  2. 填充 P:为了解决边界问题,我们可以在输入图像周围填充零(或其他值)。填充 P 是指在输入的每一边添加的像素行/列数。因此,总的宽度和高度增加量为 2P。

  3. 步幅 S:步幅定义了滤波器每次滑动的距离。当步幅 S=1 时,滤波器每次移动一个像素;S=2 时,则每次移动两个像素。步幅增大会使输出尺寸进一步减小。

综合以上因素,输出宽度 W2 和高度 H2 的计算公式为:

W2 = (W1 - f + 2P) / S + 1
H2 = (H1 - f + 2P) / S + 1

注意:计算结果必须为整数。如果不是整数,则意味着参数设置无法让滤波器恰好滑过整个输入,通常需要调整参数。

计算示例

假设我们有一个输入图像,尺寸为 227 x 227 x 3。

我们使用 96 个滤波器,每个滤波器尺寸为 11 x 11 x 3。

设置步幅 S = 4,填充 P = 0。

根据公式计算:

  • 输出深度 D2 = 滤波器数量 = 96

  • 输出高度 H2 = (227 - 11 + 2*0) / 4 + 1 = (216) / 4 + 1 = 54 + 1 = 55

  • 输出宽度 W2 = (227 - 11 + 2*0) / 4 + 1 = 55

因此,输出特征图的尺寸是 55 x 55 x 96。

🎯 本节总结

本节课中我们一起学习了两个核心内容:

  1. 批归一化的反向传播:我们详细推导了如何通过链式法则计算批归一化层中所有参数(beta, gamma, mu, sigma^2)及输入 x 的梯度,确保了梯度可以在包含批归一化的网络中有效传播。

  2. 卷积神经网络的基础:我们从二维卷积回顾延伸到三维卷积,解释了如何用三维滤波器处理彩色图像,并引入了多滤波器的概念来生成输出体积。最后,我们掌握了计算卷积层输出尺寸的关键公式 (W1 - f + 2P)/S + 1,并通过示例进行了演练。

理解输出尺寸的计算对于设计卷积网络结构至关重要,这与前馈神经网络中直接指定神经元数量的方式有所不同。在卷积网络中,你通过选择滤波器尺寸、数量、步幅和填充来间接决定下一层的尺寸。

091:卷积神经网络介绍 - 第二部分

在本节课中,我们将深入探讨卷积与神经网络的联系,分析为何在图像处理中需要卷积神经网络而非全连接网络,并详细介绍CNN的核心概念:局部感受野、权值共享和池化。我们还将了解CNN的一些变体,如空洞卷积和转置卷积。

卷积与神经网络的联系

回顾了卷积操作后,我们提出一个问题:卷积与神经网络有何联系?对于图像处理,我们能否直接使用前馈神经网络?我们将通过图像分类的例子来仔细理解这一点。

传统基于机器学习的计算机视觉解决方案,会从图像中提取一些被称为手工特征的信息,例如SIFT、LBP、HOG等。之所以称为“手工”,是因为我们通过一些思考过程和启发式方法设计出提取这些特征的程序。这些方法在一段时间内很有效,但将其扩展到更多数据集和更复杂的真实世界图像时,成为了瓶颈。

传统流程是:给定一张图像,提取原始像素、边缘或SIFT、HOG等特征,这个过程被称为静态特征工程,完全不涉及机器学习。然后,将这些特征提供给一个分类器(如神经网络)以得到最终输出。

现在我们要问的问题是:与其使用那些用于边缘检测、SIFT或其他方法的手工设计卷积核,我们能否学习有意义的卷积核或滤波器?除了学习分类器的权重参数外,能否也学习这些卷积核的权重?例如,一个拉普拉斯高斯边缘检测器(LoG)在其卷积核中有特定的权重。我们能否将这些权重作为学习算法的一部分自动学习,而不是将其分离为手工设计步骤和学习步骤?

我们想更进一步:除了学习分类器的权重,能否学习多个有意义的滤波器?既然要学习,不如学习多个。此外,能否学习多层有意义的卷积核和滤波器?我们知道卷积具有良好的数学性质,可以在神经网络的不同层之间组合卷积操作。那么,我们能否在学习分类器权重的同时或之前,利用这一点学习多层有意义的卷积核?

这是可能的。实现方法很简单:将卷积核的权重系数也视为参数,与最终分类器的权重一同学习。具体流程是:输入图像经过一个卷积核,得到一个输出特征图,这成为第二层。可以再使用另一个卷积核,得到第三层的特征图。最终,这些特征图可以作为输入,传递给一个全连接层,得到最终输出。我们可以使用反向传播来更新每个中间步骤的滤波器系数。

这样的网络被称为卷积神经网络。之所以如此命名,是因为在每一步中,我们不是使用全连接的二部图来连接前后层,而是定义一组权重(即卷积核),对输入图像进行卷积以得到输出特征图,并通过反向传播学习这些权重。

为何使用CNN而非全连接网络?

学习滤波器似乎很有趣,但为什么不直接将图像展平并使用全连接神经网络呢?为什么要把事情复杂化去学习滤波器?神经网络本身就会学习权重,为什么中间需要一个卷积操作?为什么不能让神经网络直接接收展平的图像像素作为输入,让各层学习它们该学的东西?

我们不能这样做有几个原因。让我们逐一分析。

即使使用像MNIST这样的简单数据集(一个用于手写数字识别的数据集),如果使用不带卷积或不学习滤波器的前馈神经网络,也能获得相当不错的性能(接近98%-99%)。但这种方法存在一些局限性。

第一个局限性是:这种方法忽略了图像中结构的空间相关性。通过将像素展平成一个向量,我们移除了图像不同部分或角落之间存在的空间关系。空间上分离的像素与相邻的像素被同等对待,这似乎丢失了表征图像的一些重要信息。

第二个局限性是:网络没有明显的方法来学习输入图像中不同位置的相同特征。与其他一些机器学习问题不同,在图像中,无论猫出现在左上角还是右下角,我们都期望能识别出它。如果简单地将图像展平为向量,左上角的猫和右下角的猫将具有完全不同的向量表示(猫所在的维度会不同)。我们并没有强制网络去学习“猫就是猫,无论它在图像中的哪个位置”这一特性。

第三个局限性是:计算成本可能非常高昂。举个例子,如果你有一张100万像素的彩色图像,并在第一个隐藏层使用20个神经元,那么第一层将有多少个权重?答案是6000万个。100万像素是10^6个像素,彩色图像有3个通道,所以是300万个像素。与20个神经元全连接,仅第一层就会产生6000万个权重。为了获得更好的性能,你还需要添加更多层,这将导致权重数量爆炸式增长。

CNN如何解决这些问题?

我们通过使用卷积的思想来克服这些问题。我们知道,一个滤波器只对原始图像中的局部邻域进行操作。我们称这些区域为局部感受野。具体来说,用于卷积的输入区域通常被称为感受野,它是图像的一个局部部分。因此,下一层的每个隐藏单元只连接到输入图像的一个局部部分。

这有几个目的:

  1. 捕捉空间关系:因为如果你用一个滤波器对一个图像块进行卷积,你就是在捕捉该区域的二维空间关系。这种关系可能无法被前馈神经网络有效捕捉。

  2. 极大减少模型参数数量:因为现在你只需要连接到一个局部区域,而不是拥有一个全连接图(第一层的每个神经元都连接到第二层的每个神经元)。沿用之前的例子,对于100万像素的彩色图像,假设你在第一个隐藏层学习一个大小为 K1 x K2 的滤波器,那么现在的权重数量就仅仅是滤波器本身的大小 K1 x K2。这与之前提到的全连接网络的6000万个权重形成了鲜明对比。

其次,卷积神经网络引入了权值共享的概念。权值共享意味着,无论你将滤波器与图像的右上角还是左下角进行卷积,你都使用滤波器中的相同数值。你不会因为卷积位置不同而改变滤波器权重。这与将图像展平并使用全连接层来估计该层权重(不同位置权重不同)的做法形成对比。

权值共享有两个目的:

  1. 它使神经网络对图像中物体的平移具有不变性。正如之前所说,无论猫在左上角还是右下角,都是相同的权重在起作用,因此无论猫在图像的哪个部分,网络都能识别它。

  2. 它减少了模型中的参数数量。

第三,CNN还进行池化操作。池化用于压缩来自前一层的信息。这也有两个目的:

  1. 通过压缩信息,它聚合了信息,特别是平均掉了微小的变化,从而在下一层得到一个共同的输出。

  2. 池化最终会将原始图像下采样为下一层中更小的图像。这样做之后,当卷积应用于下一层时,需要进行的卷积次数减少,从而降低了CNN后续层的计算量。

CNN核心组件详解

现在让我们更详细地看看这些组件。首先回顾一下,CNN具有局部感受野、权值共享和池化。让我们依次回顾它们。

局部感受野

如果我们使用常规的前馈神经网络,它看起来会像这样:输入是红色神经元(假设是16个像素,对应16个输入神经元),蓝色方块是中间层的神经元,最后输出层是10个绿色神经元(对应10个数字)。可以看到,这里每个输入神经元都连接到第一隐藏层的每个隐藏层神经元,这是一个密集连接。如果继续这样连接下去,就会产生非常密集的权重集合,正如我们之前提到的。

让我们对比一下进行卷积时的情况。再次观察此图,输入同样是16个输入神经元。但现在,只有像素1、2、5和6这四个神经元参与第一个卷积的计算,像素3、4、7和8不参与。因此,只有像素1、2、5、6参与计算隐藏层第一个像素 H11。类似地,随着卷积核滑动,隐藏层的每个像素都由类似的局部操作产生。

这有什么帮助?

  • 连接现在稀疏得多。这种稀疏连接自动减少了需要学习的权重数量。

  • 重要的是,我们还利用了图像的结构。如图所示,参与 H11 输出的像素1、2、5、6在空间上是相关的(例如,与像素1、2、3、4、7、8相比)。

这里可能会问一个问题:稀疏连接真的是好事吗?假设我们有无限的计算和存储带宽,让神经网络通过全连接学习它该学的东西不是更好吗?通过稀疏连接,我们难道不是禁止了某些像素相互作用并贡献给下一层的输出吗?

答案是否定的,我们并没有通过这个过程丢失信息。因为,随着我们深入这样的神经网络,如果我们看这里高亮的神经元 x1 和 x5,它们在考虑第一隐藏层时可能不直接交互,但它们可能在后续隐藏层中间接交互。因为 x1 贡献给 h2,h4 从 x5 获得输入,而 h2 和 h4 在后续层中可能相互作用。因此,只要神经网络有足够的层数,我们实际上并没有丢失这些信息。

权值共享

让我们再通过一个例子来理解这一点。假设像素1、2、5、6通过一组权重连接到第一隐藏层的第一个隐藏神经元。类似地,最后四个像素(15, 16, 11, 12)连接到该隐藏层的最后一个像素。

我们要问的问题是:我们希望这些卷积核权重不同吗?我们希望这里是不同的权重集合,那里是另一个不同的权重集合吗?答案是否定的。我们希望滤波器以相同的方式响应图像中的物体或特征,无论它出现在图像的哪个位置。这就是所谓的平移不变性。我们希望我们的神经网络对于图像中物体从一个位置平移到另一个位置具有不变性。

其次,我们也可以拥有不同的卷积核来捕捉不同类型的特征。所以,并不要求你必须为左上角部分使用一个卷积核,为右下角部分使用另一个卷积核。你可以使用相同的卷积核,并使用另一个卷积核来捕捉另一种特征(例如,一个捕捉人的眼睛,另一个捕捉人的鼻子)。

这种在整个输入中使用相同权重的思想,是卷积的默认特性,被称为权值共享。显然,这也减少了参数数量。

池化层

现在让我们看看一个完整的CNN是什么样子。这里是一个示例卷积神经网络:给定输入,你有一个卷积层1,它可以有许多滤波器(例如,步幅为1,滤波器大小 F 为 5x5,滤波器数量 K=6,填充 P=0,参数总数是150)。然后你有一个池化层,接着是另一个卷积层,再一个池化层,最后是所谓的全连接层。

现在让我们理解一下这些中间的池化层是什么。如图所示,大多数CNN都有交替的卷积层和池化层(至少早期设计的CNN是这样)。如今,你可以在CNN架构中融入其他选项和更多层,我们将在本周后续课程中重新讨论。

池化层是一种无参数的下采样操作。它是一种子采样或下采样操作,并且没有参数,你不需要在这一层学习任何权重。

让我们看一个池化操作的例子。如图所示,这可能是一个特征图,或者是卷积步骤的输出(某个隐藏层中的特征图),大小为 4x4。

如果我们现在尝试进行步幅为2的最大池化,操作如下:你取一个 2x2 的窗口,计算其中的最大值(例如第一个窗口最大值是8),并将其放在输出对应位置。然后你步进2(跳过中间一个像素),移动到下一个 2x2 窗口,取最大值(例如4),放在下一个输出位置。

如果使用步幅为1,则移动到紧邻的下一个像素(不跳过),再次取 2x2 窗口的最大值。你需要在整个特征图上重复此操作。

你所要做的就是查看那个 2x2 窗口,不涉及任何权重,只需取最大元素并将其放入下一层的对应位置。正如你所见,这变成了一种下采样操作,意味着它会根据你选择的滤波器大小和步幅,将你在此处拥有的任何图像或特征图的大小减小。

在这个例子中,我们看到了最大池化,即取 2x2 窗口中的最大值。但你也可以有其他类型的池化操作,例如平均池化(取给定 2x2 窗口中值的平均值)或L2池化(取给定窗口中所有权重的L2范数)等等。

还有更多池化的变体,例如结合最大和平均池化的混合池化,以及空间金字塔池化、谱池化等。我们将在本课程后续讲座中介绍其中一些。

CNN的变体

除了局部感受野、权值共享和池化这三个操作外,我们还可以在讨论的这些层中引入变体,特别是在卷积神经网络的卷积层中。在结束本讲座之前,让我们看看其中的几个变体。

空洞卷积

一个流行的变体是空洞卷积。它引入了一个新参数,称为膨胀率,顾名思义,它控制卷积核中值之间的间距。因此,当你在输入图像上应用卷积核时,你改变了卷积核值相对于输入的卷积间距。

让我们看一个膨胀率为2的 3x3 卷积核的例子。如图所示,你不是直接将 3x3 卷积核放置在输入的 3x3 窗口上,而是将卷积核放置在经过间隔采样的图像上。也可以说,这相当于先以某个膨胀率对原始图像进行下采样,然后应用标准卷积,两者是等价的。但我们现在一步完成。记住我们之前说过,采样和插值可以解释为卷积操作,我们现在只是利用空洞卷积来利用这一点。

注意,当膨胀率为1时,它就变成了标准卷积。空洞卷积和步幅大于1的标准卷积有一个细微差别:在步幅大于1的标准卷积中,卷积仍然是与图像中原始的相邻像素进行,只是下一个卷积移动到更远的像素。而在空洞卷积中,每个卷积本身是膨胀的,并且在输入图像中看到更大的邻域。

转置卷积

CNN中使用的另一个流行卷积变体是转置卷积。顾名思义,转置卷积允许对输入进行上采样。

在传统卷积中,你有一个输入图像,取一个滤波器,如果不进行填充,你会得到一个尺寸更小的输出。转置卷积则试图逆转这个过程:给定一个输入图像,通过卷积操作,能否得到一个更大的图像?这个过程被称为转置卷积,有时也称为反卷积或上卷积(尽管在信号处理上下文中,反卷积在技术上意味着不同的东西)。

传统上,我们会使用插值或类似规则来实现上采样。我们在第一周的课程中看到,可以使用像帐篷核这样的核与图像进行卷积,以获得更高分辨率的图像或对图像进行上采样。现在我们要问:为什么我们要使用帐篷核?为什么不能学习那个插值核的权重?

让我们通过一个例子看看如何进行转置卷积。考虑一个一维输入 [4, 2, 3, 0, 1] 和一个一维卷积核 [1, 2, -1]。我们想要得到一个比输入更大的输出。

操作如下:取卷积核 [1, 2, -1] 与输入的第一个值 4 卷积,得到 [4, 8, -4]。然后将卷积核滑动到输入的第二个值 2,得到 [2, 4, -2]。继续滑动,与 3 卷积得到 [3, 6, -3],与 0 卷积得到 [0, 0, 0],与 1 卷积得到 [1, 2, -1]。现在,在每个输出位置,我们将卷积核滑动到不同输入位置时在该位置得到的值相加。最终输出为:第一个位置 4,第二个位置 8+2=10,第三个位置 -4+4+3=3,第四个位置 -2+6+0=4,第五个位置 -3+0+1=-2,第六个位置 -1。这样就得到了比输入更大的输出 [4, 10, 3, 4, -2, -1]。你可以在二维中做同样的事情来放大图像。我们将在本课程后面看到使用这种操作的例子。

其他卷积类型

还有其他类型的卷积,我们将在讨论它们在不同上下文中的应用时详细探讨,但现在让我们简要回顾一下。

  • 3D卷积:正如我们已经提到的,进行3D卷积,输出也是一个3D体积。在本讲座讨论的场景中,我们的滤波器是3D的,但输出特征图是2D的,我们通过使用多个滤波器或特征图使其成为3D。但如果我们通过卷积本身的性质确保输出是3D体积,那就是3D卷积。

  • 1x1卷积:你只沿着体积的深度进行卷积,在输出中变成一个单一的值。因此,一个 W x H x D 的输入变成 W x H,因为你用一个深度卷积核在特定像素位置对整个深度进行卷积,得到该位置的一个标量。

  • 分组卷积:不同的滤波器与输入中不同的深度进行卷积。例如,如果你的输入有100个通道,一组滤波器可能与前10个通道交互,另一组与接下来的10个通道交互,依此类推。

  • 可分离卷积:就像我们在第一周看到的那样,我们可以将二维卷积分离为沿一个维度的卷积,然后是沿第二个维度的卷积。回想一下,我们说这有助于减少计算量。

  • 深度可分离卷积:给定一个输入体积,你有三个不同通道的三个滤波器,分别对它们进行卷积,得到三个不同的特征图。然后进行深度卷积,将它们压缩成一个单一的特征图。这可以针对不同的滤波器重复进行,以在下一层获得多个滤波器特征图。

  • 扁平化卷积:与可分离卷积非常相似,你首先有一个沿一个维度的滤波器,然后有一个沿另一个维度的滤波器,然后有沿第三个维度的第三个滤波器。在某种意义上,这是可分离卷积和深度可分离卷积的结合。

  • 空间和跨通道卷积:我们可以并行进行卷积,然后连接它们以获得输出。

我们将在本周后续的讲座中,结合它们的使用场景,看到其中几种卷积类型的例子。

总结与思考

本节课我们一起学习了卷积神经网络的核心思想及其相对于全连接网络的优势。我们深入探讨了CNN的三个关键组件:局部感受野、权值共享和池化,并介绍了几种重要的卷积变体,如空洞卷积和转置卷积。

以下是你的推荐阅读材料:

  • 关于卷积的交互式演示,请参阅此链接。

  • 关于反卷积操作的精彩讨论,请参阅Distill.pub的此链接。

  • 其他优秀资源包括《深度学习》一书第9章,以及CS231n课程的相关笔记。

最后,留给你两个思考问题:

  1. 给定一个 32 x 32 x 3 的图像,以及6个大小为 5 x 5 x 3 的滤波器,在步幅为1、填充为0的情况下,输出的维度是多少?请计算一下。

  2. 最大池化层是可微分的吗?如何通过它进行反向传播?在本节课结束时思考这些问题。

092:CNN中的反向传播

在本节课中,我们将学习卷积神经网络中的反向传播。我们将探讨如何计算卷积层和池化层的梯度,这是训练CNN的关键步骤。

卷积层反向传播

上一节我们介绍了卷积神经网络的基本概念。本节中,我们来看看如何对卷积层执行反向传播。我们将从一个简化的场景开始推导。

问题设定与符号定义

我们假设输入是单通道灰度图像,即输入通道数 C=1。同时,我们只考虑一个卷积滤波器,其尺寸为 K1 x K2。输入图像尺寸为 n1 x n2,输出特征图 y 的尺寸为 m1 x m2。

根据卷积定义,输出 y 中位置 (i, j) 的元素可以表示为:

y[i, j] = Σ_{a=0}^{K1-1} Σ_{b=0}^{K2-1} x[i-a, j-b] * w[a, b]

注意,这里为了简化表达式,我们假设滤波器从输入像素的角落开始对齐,但这不影响推导的本质。

给定损失函数 L,我们需要计算两个关键梯度:

  1. ∂L/∂w:损失相对于滤波器每个权重的梯度。

  2. ∂L/∂x:损失相对于输入图像每个像素的梯度,以便将误差继续反向传播到更早的层。

计算损失相对于滤波器权重的梯度 (∂L/∂w)

首先,我们考虑滤波器中的一个特定权重 w[a', b']。我们需要计算损失 L 相对于这个权重的梯度 ∂L/∂w[a', b']。

在卷积操作中,一个特定的滤波器权重会影响输出特征图 y 中的每一个像素。因为每次卷积运算中,该权重都会与输入图像中不同位置的像素相乘,并累加到对应的输出像素上。

因此,根据链式法则,∂L/∂w[a', b'] 需要对所有输出像素的贡献求和:

∂L/∂w[a', b'] = Σ_i Σ_j (∂L/∂y[i, j]) * (∂y[i, j]/∂w[a', b'])

其中,∂L/∂y[i, j] 是通过反向传播从后续层已知的梯度。

现在,我们需要计算 ∂y[i, j]/∂w[a', b']。根据卷积定义 y[i, j] = Σ_a Σ_b x[i-a, j-b] * w[a, b],当我们对特定的 w[a', b'] 求偏导时,求和式中只有包含 w[a', b'] 的那一项(即 a=a', b=b' 时)的导数不为零。因此:

∂y[i, j]/∂w[a', b'] = x[i-a', j-b']

将这个结果代回总梯度公式:

∂L/∂w[a', b'] = Σ_i Σ_j (∂L/∂y[i, j]) * x[i-a', j-b']

观察这个公式,它实际上是输入 x 与来自上一层的梯度 ∂L/∂y 进行卷积运算的结果。这意味着在反向传播时,计算滤波器权重的梯度可以通过一次卷积操作高效完成。

计算损失相对于输入像素的梯度 (∂L/∂x)

接下来,我们考虑输入图像中的一个特定像素 x[i', j']。我们需要计算损失 L 相对于这个像素的梯度 ∂L/∂x[i', j']。

一个输入像素 x[i', j'] 会影响输出特征图 y 中的一个局部区域 P。这个区域由滤波器的尺寸决定:当滤波器滑过输入时,像素 x[i', j'] 会与滤波器的不同部分相乘,并贡献给输出中从 (i', j') 到 (i'+K1-1, j'+K2-1) 的矩形区域内的所有像素。

因此,根据链式法则,∂L/∂x[i', j'] 需要对受其影响的所有输出像素的贡献求和:

∂L/∂x[i', j'] = Σ_{(i, j) ∈ P} (∂L/∂y[i, j]) * (∂y[i, j]/∂x[i', j'])

其中,区域 P 包含所有受 x[i', j'] 影响的输出像素位置。

现在计算 ∂y[i, j]/∂x[i', j']。考虑一个受影响的输出像素 y[i'+a, j'+b],根据卷积定义:

y[i'+a, j'+b] = Σ_{a'=0}^{K1-1} Σ_{b'=0}^{K2-1} x[i'+a - a', j'+b - b'] * w[a', b']

当对 x[i', j'] 求偏导时,只有当 i'+a - a' = i' 且 j'+b - b' = j',即 a' = a 且 b' = b 时,对应的项 w[a, b] * x[i', j'] 的导数才不为零。因此:

∂y[i'+a, j'+b] / ∂x[i', j'] = w[a, b]

将这个结果代回总梯度公式:

∂L/∂x[i', j'] = Σ_{a=0}^{K1-1} Σ_{b=0}^{K2-1} (∂L/∂y[i'+a, j'+b]) * w[a, b]

观察这个公式,它实际上是来自上一层的梯度 ∂L/∂y 与滤波器 w 进行互相关运算的结果。这等价于将滤波器旋转180度后与 ∂L/∂y 进行卷积。因此,计算输入像素的梯度也可以通过一次(互相关)卷积操作高效完成。

池化层反向传播

上一节我们推导了卷积层的反向传播。本节中,我们来看看如何对池化层执行反向传播。池化层没有可训练参数,因此我们只需要计算损失相对于其输入的梯度 ∂L/∂x,以便误差能够继续反向传播。

以下是两种常见池化操作的反向传播方法:

最大池化 (Max Pooling)

在最大池化的前向传播中,我们从每个池化窗口(例如2x2)中选择最大值。

在反向传播时,我们采用“赢家通吃”的策略:

  • 记录前向传播中每个池化窗口内最大值的位置(即“获胜神经元”)。

  • 来自上一层的梯度 ∂L/∂y 中,对应于该池化窗口输出位置的梯度,将全部传递给前一层中那个产生最大值的输入像素位置。

  • 池化窗口内其他非最大值位置的输入像素,其接收到的梯度为0。

示例:假设一个2x2窗口的前向传播输出是6(来自左上角像素),那么反向传播时,输出位置6的梯度将全部传递给输入层的左上角像素。

平均池化 (Average Pooling)

在平均池化的前向传播中,我们计算每个池化窗口内所有值的平均值。

在反向传播时,我们采用“平均分配”的策略:

  • 来自上一层的梯度 ∂L/∂y 中,对应于该池化窗口输出位置的梯度,将被均匀地分配给前一层中该窗口内的所有输入像素。

示例:对于一个2x2的窗口,输出位置的梯度将被除以4,然后平均分配给窗口内的四个输入像素。

总结

本节课中我们一起学习了卷积神经网络中反向传播的核心机制:

  1. 对于卷积层:我们推导了损失相对于滤波器权重 (∂L/∂w) 和输入 (∂L/∂x) 的梯度公式。关键发现是,这些梯度计算都可以通过卷积(或互相关)运算高效完成,这充分利用了卷积操作的数学性质和硬件优化。

  2. 对于池化层:我们学习了最大池化和平均池化的反向传播规则。最大池化将梯度路由回前向传播中的“获胜者”,而平均池化则将梯度平均分配给池化窗口内的所有输入。

理解这些原理对于深入掌握CNN的训练过程至关重要,它揭示了误差如何从网络输出层逐层反向传播,并用于更新网络参数(如卷积核权重)。

093:CNN架构演变 - 第一部分

概述

在本节课中,我们将学习卷积神经网络架构的演变历史。我们将从最早的架构开始,了解它们的设计原则、关键贡献以及如何随着时间推移而改进。我们将重点关注用于ImageNet图像分类挑战赛的几种重要架构,包括LeNet、AlexNet、ZFNet和VGGNet。


卷积神经网络的早期先驱

上一节我们介绍了卷积神经网络的基本概念和反向传播原理。本节中,我们来看看CNN架构是如何随着时间演变的。

卷积神经网络的历史可以追溯到1980年的Neocognitron。Neocognitron或许是CNN最早的先驱。它提出了特征提取的概念、池化层的概念、在神经网络中使用卷积的概念,以及最终进行分类识别的概念。

但“卷积神经网络”这一名称,是随着Yann LeCun设计的LeNet网络而出现的。LeNet主要在1989年至1998年间开发,用于手写数字识别任务。

LeNet卷积神经网络架构如下图所示。它以一个卷积层开始,接着是一个池化层,然后又是卷积层和池化层,最后是两个全连接层和输出层。

卷积滤波器的大小为5x5,步长为1。池化层大小为2x2,步长为2。你可以看到整体架构可以这样描述:Conv -> Pool -> Conv -> Pool -> FC -> FC。这表示一个卷积层后接一个池化层,再接一个卷积层和池化层,最后是两个全连接层。

直到今天,许多架构在设计CNN时仍遵循类似的原则。


ImageNet挑战赛与评估指标

CNN新架构设计和发展的主要推动力,得益于2010年开始的ImageNet分类挑战赛。ImageNet是一个数据集,他们在2010年组织了名为“大规模视觉识别挑战赛”的比赛。

ImageNet中的图像数据库根据WordNet层次结构组织。它目前包含WordNet层次结构中各种名词对应的图像,拥有数百万张图像,目前每个节点大约有500张图像。

这促使全球研究人员投入大量精力,在一个公共数据集上对他们的机器学习模型(特别是计算机视觉模型)进行图像分类性能基准测试。该数据集的性能衡量标准被称为Top-1错误率和Top-5错误率。

  • Top-1错误率:将神经网络的最佳预测与真实标签进行比较时的错误率。

  • Top-5错误率:检查真实标签是否在神经网络输出层的前五个预测标签中。这仍然是一个很好的衡量标准,因为ImageNet数据集有大约1000个类别,这意味着神经网络输出层有1000个神经元。只要你的真实标签在输出层得分的前五名中,就认为是正确匹配。否则,就会产生Top-5错误。

关于ImageNet数据集的更多详细信息,请参阅此链接。


分类任务中的损失函数与激活函数

在继续讨论CNN架构之前,让我们退一步,看看神经网络通常如何用于分类问题。

当我们讨论前馈神经网络时,我们将损失函数视为均方误差。这在当时是可以的。但当你处理分类问题时,均方误差可能不是损失函数的正确选择。均方误差更适合回归问题。

在分类问题中,最常见的损失函数被称为交叉熵损失函数。它衡量真实标签输出上的概率分布与神经网络输出的标签概率分布之间的熵。这可以让你了解神经网络所犯的错误。

以下是交叉熵损失函数的正式定义:

公式:

L = - (1/C) * Σ (y_i * log(ŷ_i))

其中,C是总类别数,ŷ_i是神经网络对特定类别i的预测(或最后一层对类别i的得分),y_i是该特定类别的正确标签。

更具体地说,y_i是一个向量。假设你有C个类别(1到C),并且第二个类别是正确的类别,那么你期望的y向量将是 [0, 1, 0, 0, ..., 0](直到第C个类别)。这就是真实标签空间中标签的概率分布,因为这是正确的类别。这就是我们在这里讨论的y_i,而ŷ_i是神经网络输出层的得分。

在二分类情况下,这可以简化为 - [y_i * log(ŷ_i) + (1 - y_i) * log(1 - ŷ_i)]。例如,当你使用Sigmoid函数时,神经网络只会输出一个得分,1 - y_i将是负类的得分。

交叉熵损失函数是可微的。如果我们使用这个损失函数,首先要检查的是它是否可微。通过观察,我们可以说交叉熵损失函数肯定是可微的。然后,我们需要计算它对最后一层权重W_j的梯度。一旦我们得到这个梯度,就可以通过我们目前讨论的方法将其进一步传播到更早的层。

如果我们考虑激活函数为Sigmoid(假设是二分类问题),交叉熵损失函数对最后一层权重W_j的导数可以推导出来。有趣的是,这个梯度项与均方误差的梯度项非常相似。记住,σ(z)就是ŷ。所以即使在均方误差中,当你对(1/2)*(ŷ - y)^2求导时,你也会得到(ŷ - y)。因此,交叉熵的梯度看起来与均方误差的梯度非常相似。

在转向CNN架构之前,还有一点需要注意:当你进行分类时,虽然我们讨论过不同类型的激活函数,如Sigmoid、Tanh、ReLU、Leaky ReLU等,但我们通常在输出层单独使用一种称为Softmax激活函数的函数。

什么是Softmax激活函数?给定最后一层的Z_1到Z_C(假设它们是最后一层C个神经元的输出),网络的最终输出由 e^{Z_j} / Σ_{k} e^{Z_k} 给出。

这有什么作用?记住,在这种情况下,最后一层所有e^{Z_k}的总和总是大于e^{Z_k},这意味着这些值肯定介于0和1之间。此外,因为分母是所有神经元指数值的总和,对最后一层的每个神经元都进行此操作,其输出之和也将等于1。这样,我们现在将神经网络输出的任何得分转换为一个概率值向量,其中每个值都在0到1之间,并且它们加起来等于1。这使我们能够将神经网络的输出解释为输出标签空间上的概率分布。

因此,Softmax是用于分类的神经网络输出层默认使用的激活函数。

这里有一个小的术语说明:概率得分之前的值通常被称为神经网络的逻辑值。因此,神经网络输出逻辑值,你应用Softmax激活函数并将其转换为概率得分。


ImageNet挑战赛的赢家:从传统方法到CNN

现在让我们回到ImageNet分类挑战赛。

我们将要讨论的所有这些CNN,都使用交叉熵损失作为损失函数,并且几乎都在最后一层使用Softmax激活函数,作为这些CNN架构中常见的选择。

正如我们几分钟前所说,ImageNet数据在2010年创建并发布,世界各地的研究人员参加了ILSVRC挑战赛。

  • 2010年,获胜的错误率是28.2%,这是在没有神经网络的情况下实现的。

  • 2011年,研究人员将错误率从28.2%提高到25.8%。

  • 2012年,Alex Krizhevsky和Geoffrey Hinton提出了一种至今仍很流行的CNN架构——AlexNet,它将错误率从25.8%大幅降低到16.4%。

因此,这是第一个基于CNN赢得ImageNet挑战赛的架构。自2012年以来,每年的挑战赛都由CNN获胜,其性能显著优于其他浅层机器学习方法。传统的机器学习方法通常被称为浅层学习,因为你没有在许多层上进行深度学习。当我们试图理解神经网络的深度为架构带来什么价值时,这一点将在接下来的课程中变得更加清晰。


AlexNet:2012年的突破

现在让我们从AlexNet架构开始,并尝试理解它。

以下是AlexNet的架构。实际上,当我们在本周第一堂关于CNN的课程中计算大小时,我们以AlexNet的第一层为例。输入是一个体积为224x224x3(RGB通道)的数据。滤波器大小为11x11(深度为3,这在所有情况下都遵循),步长为4。这导致了55x55的输出。总共选择了96个滤波器,其中48个滤波器进入一个处理单元,剩下的55x55x48进入另一个处理单元。我们将在接下来的几张幻灯片中更详细地描述这一点,但这就是2012年赢得ILSVRC的整体架构。

它的设计原则与LeNet相似,但也有许多不同的特点,并且比LeNet更深,还有一些我们将要讨论的其他重要改进。它使用带有正则化的随机梯度下降法在120万张图像上进行了训练。

让我们更仔细地看看这个架构。

它有一个输入图像,大小为224x224x3,这是ImageNet数据集中图像的大小。第一层有一个卷积加池化操作,你在这里一起看到输出。因此,55x55x48的输出是在完成卷积部分之后得到的,然后你进行最大池化,进一步减小尺寸。

然后,他们再次进行卷积加池化,接着是单独的卷积层,再是单独的卷积层,然后又是卷积加池化,接着是全连接层,再一个全连接层,最后是输出层。

让我们再看一下至少第一层的数字,你或许可以根据我们本周第一堂课讨论的原理计算出剩余层的大小。

所以,输入是224x224x3,滤波器大小为11x11,步长为4。他们还有一定程度的填充,所以最终输入网络的大小是227x227。因此,(227 - 11) = 216,除以4得到54,加1得到55,这就是我们之前讨论过的。

同样,你可以通过知道每层中的滤波器数量和核大小(图中所示)来计算每层的参数。它总共有8层:5个卷积层和3个全连接层(最后三层是全连接的)。正如我们提到的,它在ImageNet数据集上进行了训练。

AlexNet的设计者还引入了一个称为局部响应归一化层的归一化层。到目前为止,我们看到了批归一化层,类似地,局部响应归一化层对给定层中特定位置跨通道的所有值进行归一化。例如,如果你有一定的深度,你会取一个特定的像素位置,沿着深度方向,并在该特定空间位置对所有深度值进行归一化。这被称为局部响应归一化,作者认为这会产生一种称为侧抑制的效果,因此它允许其中一个值在归一化后获得更高的输出值,从而胜过其他值。

除此之外,这个特定的架构还引入了修正线性单元作为激活函数,这是使该架构成功的贡献之一。当然,ReLU现在是许多其他CNN中激活函数的默认组件或默认选择。

正如你在架构中看到的,最大池化跟随第一、第二和第五个卷积层。第三和第四层没有池化。ReLU非线性激活函数应用于整个架构中每一层的输出。

为什么当时选择这样的设计?也许这是能带来最佳经验性能的架构。

让我们尝试从参数和神经元的角度分析这个架构的复杂性。

第一层的参数由以下公式给出:滤波器大小为11x11x3,加上一个偏置,乘以第一层选择的滤波器总数96(其中48个进入一个处理单元,另外48个进入另一个处理单元)。因此,第一层的参数总数大约是35,000。

另一方面,神经元的数量由图像本身的大小给出,即55x55x96,结果大约是253,000。

同样,你可以计算其他层的数字。第二层的参数大约是300k,神经元大约是186,000。第三层的新参数是884k,神经元数量是64,000。所以你可以看到,由于应用池化后尺寸减小,神经元的数量在减少。在这里,神经元指的是下一层特征图中的每个像素。因为我们进行池化,图像尺寸从224x224变为55x55,再到27x27,再到13x13,由于尺寸减小,神经元数量减少。

但参数数量持续增加,因为初始选择的滤波器数量很大(最初是96个),下一层有128+128个滤波器,总共256个滤波器。然后是192+192,大约是384个滤波器,依此类推。你可以看到,随着网络加深,参数数量持续增加,神经元数量减少,直到进入全连接层,参数数量急剧增加,达到百万级别。在总共约6000万个参数中,大约5700万个参数仅位于全连接区域。这应该再次向你解释卷积层和池化层在参数数量方面是多么高效。

神经元总数约为63万个。

从另一个角度来看,你可以在这里看到,参数从35k增加到最后几层的数百万,而神经元从253k开始,在最后几层减少。

这个架构一个有趣的观察是:卷积层可能只包含5%的参数。记住,在AlexNet总共6000万个参数中,它们只包含300万个参数。但它们却占用了95%的计算量。为什么会这样?

尽管它们只有5%的参数,但请记住,在卷积中,你必须取相同的权重,并将其放置在原始图像的每个位置以获得输出。因此,尽管权重数量少,但卷积层的计算量很高。

所以,90%到95%的计算位于卷积层,而90%到95%的参数位于全连接层。

AlexNet使用SGD在两张NVIDIA GTX 580 GPU上进行训练,这使他们能够将特征图分配到两个不同的GPU上。训练这个架构花了他们大约一周时间。今天,由于GPU的改进,可以在更短的时间内完成,但在当时,他们花了一周时间来训练这个架构。


ZFNet:2013年的改进

在接下来的一年,2013年,ImageNet LSVRC的获胜者是一个名为ZFNet的网络。它来自提出这种CNN设计的Zeiler和Fergus的名字。

2013年,ZFNet相对于AlexNet的主要贡献是:架构大体相同(整体上是Conv-Pool-Conv-Pool-Conv-Conv-Pool-FC-FC),但在超参数上有一些变化。

以下是超参数的变化:

  • 在Conv1中,滤波器大小从11x11(步长4)改为7x7(步长2)。

  • 在Conv3、4和5中,滤波器数量从384、384、256增加到512、1024和512。

这种对超参数的精心选择,使得Top-5错误率从16.4%显著降低到11.7%。

在这个阶段,我应该澄清一个术语:超参数的概念。为什么我们称这些值(如11x11、7x7、384)为超参数?因为我们学习到的参数是神经网络的权重。所有其他值都是用户定义的,不是学习得到的,所以我们称它们为超参数。参数是权重,这才是我们实际学习的东西。

但这就是ZFNet在2013年的主要贡献。


VGGNet:2014年的深度探索

进入2014年,一个主要的贡献是一种名为VGGNet的新架构。VGGNet代表由牛津大学VGG小组开发的架构,VGG代表视觉几何组。他们在设计中提出了一种特定的理念。

他们提出了多种深度的架构。他们认为,虽然AlexNet将深度提升到一定水平,ZFNet保持了与AlexNet相同的深度,但通过使CNN更深,可以更好地解决问题,并在ImageNet分类挑战赛上获得更低的错误率。

因此,他们的做法是采用多种不同深度的架构。你可以在这里看到,输入在顶部,输出在底部。你可以看到卷积层、最大池化层、卷积层、最大池化层等等。这些64、128表示这些架构中的尺寸。

他们有趣的设计理念是:这个架构在2014年ImageNet挑战赛中获得亚军。他们的理念是,通过增加深度,你可以在函数中建模更多的非线性。关键贡献是:深度是一个关键组件。

他们从头到尾保持了一个同质的架构。这是什么意思?他们在所有层中只使用3x3卷积。我们看到AlexNet第一层有11x11滤波器,ZFNet在第二年将其改为7x7滤波器。但VGG认为,与其在设计架构时费力寻找滤波器大小,他们决定将其固定为3x3。这有什么帮助?我们稍后会讲到。他们在网络的所有池化层中使用2x2最大池化。

他们的主要思想是:较小的感受野意味着更少的参数。这意味着如果你有一个3x3滤波器,需要学习的参数更少。更重要的是,连续进行两个3x3卷积,其效果等同于一个5x5卷积。当你进行一次3x3卷积时,你会得到一个输出。如果你在第一个输出的基础上再进行一次3x3卷积,原始图像中5x5像素的邻域将对后面的输出产生影响。换句话说,两个3x3卷积具有与单个5x5卷积相同的感受野。同样,三个3x3卷积具有与7x7卷积相同的感受野,依此类推。

因此,与其尝试保持11x11或7x7等滤波器大小,他们认为可以始终将滤波器大小保持为3x3。重要的是,正如我们所说,它会有更少的参数。如果你采用单个7x7卷积,你将有7^2 * C^2个参数(7^2是49,乘以C^2)。而如果你有三个连续的3x3卷积,你将拥有3 * (3^2 * C^2)个参数,即27 * C^2,对比49 * C^2。因此,这比进行单个7x7卷积具有更少的参数。

他们发现,在这个架构中,VGG-19仅比VGG-16略好。因此,直到VGG-16,性能随着深度增加而持续提高,但在达到一定深度后似乎趋于饱和。这个问题后来得到了解决,我们将在接下来的幻灯片中看到。

他们还尝试使用不同网络的集成,以在同一任务上获得改进的性能。

然而,在参数方面,VGG具有显著的内存开销和参数开销。你可以在这里看到AlexNet与VGG-16在内存方面的比较。内存简单地是你VGG中所有层的神经元总数与AlexNet中所有层的神经元总数的比较。再次记住,当我们说神经元数量时,我们指的是特征图的大小,因为在CNN中,每个特征图中的每个像素对我们来说都是一个神经元。

因此,你可以仔细计算并发现,AlexNet总共有190万个神经元,而VGG-16有4860万个神经元。在参数方面,虽然前几个卷积层对于AlexNet和VGG来说都相对较小,但在全连接层,VGG的参数显著增加。虽然AlexNet总共有大约6000万到6100万个参数,但VGG-16最终拥有大约1.38亿个参数。

因此,虽然VGG在许多场景下表现良好,但由于其增加的参数数量和内存需求,这也是VGG的一个限制。尽管选择3x3本身减少了参数,但由于后面的层、深度、全连接层等原因,参数累积起来对他们来说还是很多。它使用了更多的内存和参数。正如我们所见,大多数参数位于第一个全连接层。大多数内存,正如他们之前所见,位于早期的卷积层,因为那时你需要最大数量的神经元来容纳所有这些特征图。


总结

本节课中,我们一起学习了卷积神经网络架构的演变历程。我们从最早的LeNet开始,了解了其基本结构。然后,我们探讨了推动CNN发展的ImageNet挑战赛及其评估指标。接着,我们深入分析了用于分类任务的交叉熵损失函数和Softmax激活函数。之后,我们详细研究了几个里程碑式的架构:2012年首次以CNN赢得ImageNet的AlexNet,2013年通过调整超参数取得改进的ZFNet,以及2014年强调深度和同质化设计(使用3x3卷积)的VGGNet。我们了解了它们的设计理念、关键贡献、参数与计算量的分布特点,以及各自的优缺点。这些架构为后续更复杂网络的发展奠定了基础。

094:CNN架构的演变 - InceptionNet与ResNet

在本节课中,我们将学习卷积神经网络(CNN)架构发展历程中的两个重要里程碑:GoogleNet(InceptionNet)和ResNet。我们将了解它们如何通过创新的模块设计来解决深层网络训练中的效率与梯度问题。

概述

随着2014年VGG网络的出现,另一个名为GoogleNet的网络也同时诞生。GoogleNet的目标是构建更深的网络,同时追求效率,旨在减少参数数量、内存使用和计算量。

GoogleNet与Inception模块

上一节我们回顾了VGG网络,本节我们来看看GoogleNet的创新之处。GoogleNet网络深度达到22层,它通过移除全连接层和引入Inception模块来达成其设计目标。

Inception模块的设计理念

以下是Inception模块的核心思想:它不再单一地选择卷积核尺寸,而是并行使用多种尺寸的卷积核,并将结果拼接起来。

原始的朴素Inception模块结构如下:

  • 并行执行1x1卷积。

  • 并行执行1x1卷积后接3x3卷积。

  • 并行执行1x1卷积后接5x5卷积。

  • 并行执行3x3最大池化后接1x1卷积。

这种设计允许网络在同一层捕捉不同尺度的特征。然而,直接进行这种并行计算非常昂贵。

1x1卷积的瓶颈作用

为了解决计算量大的问题,GoogleNet引入了1x1卷积作为瓶颈层,用于在昂贵的卷积操作之前降低通道维度。

其工作原理是:对于一个尺寸为 56x56x64 的输入张量,使用32个 1x1x64 的卷积核进行卷积操作。每个空间位置(共56x56个)经过 1x1x64 的卷积后,会输出一个标量值。因为有32个这样的滤波器,最终输出变为 56x56x32。

公式表示:输出尺寸 = (输入高度, 输入宽度, 1x1卷积核的数量)

这种方法保持了空间维度,但显著降低了深度,从而减少了后续卷积层的计算量。

最终的Inception模块

因此,最终的Inception模块在并行分支中执行3x3或5x5卷积之前,都先应用了1x1卷积来降低深度。对于最大池化分支,则先进行池化操作以保持通道数,然后再用1x1卷积降低深度。

GoogleNet的整体架构

GoogleNet的整体架构始于一个小的“主干”网络(包含卷积和池化层),之后堆叠了多个Inception模块。网络末端是分类器输出,没有全连接层。此外,网络中还有辅助分类器,它们将中间层的输出接入一个小型分类网络,计算损失并回传梯度,这有助于缓解深层网络中的梯度消失问题,确保前面层的权重也能得到有效更新。

深度网络的挑战与ResNet

随着网络逐年加深,研究人员开始探索CNN的深度极限。然而,他们发现一个反直觉的现象:更深的网络(如56层)的训练误差和测试误差反而比更浅的网络(如20层)更高。

梯度消失问题

造成这一现象的主要原因是梯度消失。在深度神经网络中,使用如Sigmoid的压缩型激活函数时,其梯度值小于1。在反向传播过程中,这些小于1的梯度值在多层之间连续相乘,会导致传递到前面层的梯度变得极其微弱,甚至趋近于零,使得前面层的参数无法得到有效更新。

公式示意:对于初始层输入 x_n 的梯度 dL/dx_n,在反向传播链式法则中会连续乘以多个Sigmoid导数 σ'(每个σ' ≤ 0.25),导致乘积迅速变小。

残差网络(ResNet)的解决方案

为了解决梯度消失问题并使深层网络至少不差于浅层网络,ResNet在2015年引入了残差块。

在普通的CNN块中,输入 x 经过一系列层(如卷积、激活函数)变换为 H(x)。而在残差块中,目标不再是学习 H(x),而是学习残差 F(x) = H(x) - x。因此,块的输出变为 F(x) + x。

核心结构:输入 x 一方面通过两到三个卷积层进行变换,另一方面通过一条恒等映射(或称“捷径连接”)直接传递到输出端,并与卷积变换的结果相加。

ResNet为何有效?

这种设计为梯度回传提供了“高速公路”。在反向传播时,梯度可以通过恒等映射这条捷径几乎无损地传递到更早的层,确保了即使网络非常深,前面层也能获得较强的梯度信号进行更新。这使得训练超过100层的超深网络成为可能。

ResNet的架构细节

ResNet由多个残差块堆叠而成。在深层ResNet(如ResNet-50/101/152)中,也使用了类似GoogleNet的1x1瓶颈层来提升计算效率。网络末端使用全局平均池化将每个特征图压缩为一个标量,然后接一个全连接层进行分类。

凭借残差连接,ResNet在2015年的ImageNet分类、检测、定位以及COCO检测与分割等多项竞赛中均取得了第一名,并成为计算机视觉领域的基础性架构。

总结

本节课我们一起学习了CNN架构演变中的两个关键网络。GoogleNet通过设计并行的Inception模块和使用1x1瓶颈卷积,在增加网络深度的同时提升了计算效率。ResNet则通过引入带有恒等映射的残差块,巧妙地解决了深度网络中的梯度消失问题,使得训练成百上千层的神经网络成为现实,并显著提升了模型性能。这两种创新思想对后续的深度学习模型设计产生了深远影响。

095:更新与近期CNN架构 🏗️

在本节课中,我们将学习自ResNet之后发展起来的一系列重要CNN架构。我们将探讨ResNet的几种变体,如宽残差网络和随机深度网络,然后介绍DenseNet、MobileNet、EfficientNet、SENet等高效网络,最后讲解受Transformer启发的ConvNeXt架构。这些架构在提升性能、降低计算成本或适应移动设备方面各有侧重。

从ResNet到其变体 🔄

上一节我们回顾了ResNet及其核心的残差连接思想。本节中,我们来看看几种基于ResNet的改进架构,它们通过不同的方式优化了网络设计。

残差连接的重要性与改进

ResNet中的恒等映射为反向传播提供了“高速公路”,缓解了梯度消失问题。ResNet的创建者进一步改进了设计,将ReLU激活函数移入残差块内部,使得恒等连接路径不再紧随ReLU之后,从而创建了更直接的信息传播路径,这被证明能带来更好的性能。

宽残差网络

宽残差网络基于一个假设:ResNet的成功主要归功于残差块结构本身,而非单纯的网络深度。因此,该架构不追求增加深度,而是增加每个残差块内的宽度(即卷积核数量)。具体做法是将基础残差块中两个3x3卷积层的滤波器数量F增加K倍,变为F*K。研究表明,一个50层的宽ResNet可以超越152层的原始ResNet。增加宽度而非深度还有计算上的好处,因为更宽的网络层更易于并行化计算。

以下是宽残差网络中一个残差块宽度增加的示意:

# 原始残差块的两个卷积层
conv1 = Conv2D(filters=F, kernel_size=3)
conv2 = Conv2D(filters=F, kernel_size=3)
# 宽残差块中宽度增加K倍
conv1_wide = Conv2D(filters=F*K, kernel_size=3)
conv2_wide = Conv2D(filters=F*K, kernel_size=3)

ResNeXt:聚合残差变换

ResNeXt将Inception模块的多分支思想引入残差网络。其核心残差块包含多个并行路径,每条路径都遵循“1x1卷积降维 -> 3x3卷积 -> 1x1卷积升维”的瓶颈结构,最后将各路径输出相加。这种设计增加了网络的“基数”(路径数量),被视为深度和宽度之外的另一个维度,能有效提升模型容量和性能。

随机深度网络

该思想可以看作是在残差块层级应用“Dropout”。由于残差连接的存在,网络不会因为随机丢弃(即跳过)某些残差块而断开。在训练时,每个小批量迭代中会按一定概率随机跳过一部分残差块。这种方法能进一步缓解梯度消失、减少训练时间(因为前向传播路径变短),并起到正则化的作用。在测试时,则使用完整的网络。

密集连接卷积网络 (DenseNet) 🌐

从ResNet及其变体对跳跃连接的探索出发,DenseNet提出了更极致的连接方式。

在DenseNet的密集块中,每一层都与其前面所有层直接连接。这与ResNet中周期性的恒等连接不同,DenseNet建立了所有层之间的直接连接。这些连接也称为跳跃连接或恒等连接(权重为1),它们确保了梯度能够直接流向更早的层,极大地缓解了梯度消失问题。研究表明,一个相对“浅”的50层DenseNet可以超越152层的ResNet。

以下是DenseNet中一个密集块内连接方式的简化描述:

第1层输出 = H1(输入)
第2层输出 = H2([输入, 第1层输出])  # [ ] 表示拼接
第3层输出 = H3([输入, 第1层输出, 第2层输出])
... 以此类推

其中,H代表一系列操作(如BN、ReLU、卷积)。

面向移动与高效计算的架构 📱

随着CNN在视觉应用中的普及,在嵌入式或边缘设备等低功耗设备上部署模型的需求日益增长。这催生了一系列旨在实现低延迟、低功耗、小模型尺寸同时保持足够精度的网络架构。

MobileNet

MobileNet的核心是使用深度可分离卷积 替代标准卷积,从而大幅减少计算量和参数量。

深度可分离卷积分为两步:

  1. 深度卷积:对输入的每个通道独立进行空间卷积(滤波器尺寸为 DK x DK x 1),产生与输入通道数相同的特征图。

  2. 逐点卷积:使用1x1卷积对深度卷积输出的所有通道进行线性组合,改变通道数。

假设输入特征图尺寸为 DF x DF x M,输出为 DF x DF x N,卷积核为 K x K。

  • 标准卷积计算成本 ≈ K * K * M * N * DF * DF

  • 深度可分离卷积计算成本 ≈ K * K * M * DF * DF (深度卷积) + M * N * DF * DF (逐点卷积)

计算量减少的比例约为:1/N + 1/(K^2)。当使用3x3卷积核时,深度可分离卷积的计算量大约为标准卷积的1/8到1/9。

此外,MobileNet还引入了宽度乘数和分辨率乘数两个超参数,允许根据不同的资源约束(如算力、存储)调整模型的宽度和输入图像大小,从而形成一个模型家族。

EfficientNet

EfficientNet系统地研究了网络缩放问题。传统做法是单独增加深度、宽度或输入分辨率,但EfficientNet发现这些维度单独缩放时收益会递减。因此,它提出了复合缩放方法,使用一个单一的复合系数 φ 来平衡地缩放网络的深度、宽度和分辨率:

  • 深度:d = α^φ

  • 宽度:w = β^φ

  • 分辨率:r = γ^φ

约束条件为:α · β² · γ² ≈ 2,且 α, β, γ ≥ 1。其中,α, β, γ 是通过在小模型上搜索确定的常数。这样,当总计算资源增加 2^φ 倍时,模型性能能得到更高效的提升。

挤压与激励网络 (SENet)

SENet关注于提升通道间特征的表征能力。它在卷积块中引入了SE模块,该模块包含两个操作:

  1. 挤压:对每个通道的特征图进行全局平均池化,得到一个标量,从而捕获该通道的全局信息。

  2. 激励:通过一个小型全连接网络(通常包含瓶颈结构)学习每个通道的重要性权重(介于0和1之间),然后使用这些权重对原始特征图的各个通道进行重新校准(缩放)。

SE模块可以灵活地嵌入到如ResNet等现有架构中,以极小的计算开销带来明显的性能提升。

受Transformer启发的CNN:ConvNeXt 🚀

近年来,Transformer模型在视觉领域大放异彩。ConvNeXt是一项将Transformer成功设计理念系统化地融入纯CNN架构的工作。它通过对ResNet进行一系列精心设计的改进,证明了传统CNN经过现代化改造后,性能可以媲美甚至超越视觉Transformer。

以下是ConvNeXt采用的一些关键设计改动:

  • 大卷积核与非重叠下采样:将初始的7x7卷积+步长2,改为使用4x4卷积、步长4。这类似于ViT中将图像分割为不重叠的图块,减少了冗余计算。

  • 倒置瓶颈块:借鉴Transformer的MLP块,将ResNet中“先压缩(1x1降维)-后扩展(1x1升维)”的瓶颈结构,改为“先扩展(1x1升维)-后压缩(1x1降维)”,并且中间使用深度卷积。

  • 减少激活与归一化层:在每个块中,将常用的ReLU激活函数替换为更平滑的GELU,并且只保留一个激活函数。同时,用层归一化 替代批归一化,并且每个块也只使用一个归一化层。

  • 分离下采样层:将下采样操作(如步长2的卷积)作为独立层,而不是在残差块内部完成,使设计更清晰。

这些改动共同作用,使得ConvNeXt在ImageNet等基准数据集上取得了优异的性能。

总结 📝

本节课我们一起学习了自ResNet以来CNN架构的重要发展脉络。我们从ResNet的多种变体(宽ResNet、ResNeXt、随机深度网络)出发,看到了对残差连接的不同利用方式。接着,我们探讨了DenseNet的密集连接思想,以及为适应移动和高效计算场景而设计的MobileNet、EfficientNet和SENet。最后,我们介绍了受Transformer启发、通过现代化设计大幅提升性能的ConvNeXt架构。这些工作展示了CNN领域在追求更高精度、更低成本和更强泛化能力方面的持续创新。

课后练习:

  1. 参考幻灯片10,计算当使用深度可分离卷积替代标准卷积时,计算量大约减少了多少倍?(假设使用3x3卷积核)

  2. 参考幻灯片14,解释为什么对于复合系数 φ,总计算量(FLOPs)大约增加 2^φ 倍?

096:微调CNN 🎯

在本节课中,我们将学习一个关键概念:微调。这是一种利用在大型数据集(如ImageNet)上预训练好的卷积神经网络模型,来帮助我们解决新领域或新任务的技术。我们将探讨其背后的原理、适用场景以及具体操作方法。

概述

到目前为止,我们已经了解了多种CNN架构的变体。这些变体首先尝试增加深度以提高性能,然后尝试在不牺牲性能的前提下提高计算效率,最后甚至出现了可以牺牲部分性能以大幅降低计算量的变体。我们看到了所有这些变体,包括Squeeze-and-Excitation网络。在这些变体中,CNN的反向传播过程本身并未受到影响,这让我们可以灵活地使用这些架构。

在本讲中,我们将讨论CNN的一个重要方面,它使我们能够为新的任务或领域设计架构。例如,如果我们知道AlexNet在ImageNet数据集上表现良好,我们如何利用这些知识来为另一个领域或任务开发模型?为了探讨这个问题,让我们从CNN本身的一些局限性开始。

CNN的局限性

CNN存在一些基本问题。首先,优化这些深度模型中的权重或参数通常很困难。这需要恰当的权重初始化,以及大量的超参数调优。这是其基本限制之一。

其次,CNN可能遭受过拟合,因为用于训练的样本数量通常远少于被训练的参数数量。我们已经看到,AlexNet有6000万个参数,VGG有1.38亿个参数,Inception V1(GoogleNet的第一个版本)有500万个参数,其改进版本Inception V3有2300万个参数,ResNet-50有2500万个参数,等等。

因此,训练这些网络从根本上需要很长时间和大量的计算能力。正如我们所说,AlexNet在两个Nvidia GTX 580 GPU上训练了大约一周,VGG在四个Nvidia Titan Black GPU上训练了两到三周,Inception V1在未共享的硬件上训练了大约一周。

当你需要为一个新领域设计架构时,所有这些会变得更加困难。在这种情况下,你必须设计一个架构,运行大约一周或几天,观察其效果,然后回去更改超参数,再运行一周,再次观察效果。这种设计新架构或CNN架构的方法无法规模化。

应对策略

那么,我们如何为新的任务和领域管理这项工作呢?

你可以尝试的一些方法是使用良好的权重初始化,这样你就能独立于架构设计。我们见过Glorot或He初始化,这是一种可能性。你也可以用手工设计的滤波器来初始化你的CNN,例如使用边缘滤波器或其他滤波器来初始化卷积层中的滤波器,然后让CNN对它们进行微调。或者,我们已经见过无监督贪婪逐层预训练作为另一种方法,它可以提供一个初始化,神经网络随后可以利用它。正如我们之前所说,由于计算能力的提高和数据集规模的扩大,这种无监督贪婪逐层预训练方法如今已不常用。

我们讨论过的另一种改进神经网络训练的方法是使用正则化方法,无论是L2权重衰减、L1权重衰减、Dropout、批归一化、添加输入噪声、添加梯度噪声,还是数据增强等。这可能缓解过拟合,但可能不会真正加快CNN的训练速度。那么,我们该怎么办呢?

CNN的一个有趣特性

CNN有一个被观察到的有趣特性,这也是我们下周讲座将重点关注的。如果你在训练CNN后,可视化卷积网络各层学习到的滤波器,你会发现较早的层拥有可能代表能够捕捉各种定向边缘、颜色斑点等的滤波器。换句话说,这些就是我们所说的低级特征,即从图像中提取的简单低级特征。

CNN中间层学习到的滤波器似乎捕捉到一些中级特征,这些特征可以被视为低级特征的组合,例如某种由两条边缘组合而成的纹理,或者颜色和边缘的某种聚合等。最后,后面层的滤波器学习高级特征,例如物体的印象,与初始层中仅仅是边缘相比,这是高级抽象。

我们现在要问的问题是,我们能利用这一点做些什么?我们能否利用对CNN滤波器如何学习的这种理解,来更好地为新的任务和领域设计架构?

迁移学习

答案是机器学习中一个称为迁移学习的设置。在迁移学习中,你有一组给定的源任务和一个你想要表现良好的目标任务,也就是你想要解决问题的新领域或新任务。例如,你可以想象你已经用ImageNet数据集训练了AlexNet,而明天你有了来自不同领域的数据,比如你想为印度的自动驾驶汽车构建一个物体识别系统。这些物体可能与ImageNet中的物体不同。

那么,你现在如何将你在ImageNet上学到的模型转移到你想在新领域开发的模型上呢?在这种情况下,你尝试利用目标领域的数据以及从源领域获得的模型或知识,来为目标领域学习一个模型。我们称之为迁移学习,即能够将知识从早期的源任务转移到我们当前感兴趣的目标任务。

迁移学习与CNN特征学习的关系

这与CNN中特征的层次化学习有什么关系?我们马上就会看到。我们现在知道,利用在不同任务上学到的知识来辅助当前任务的训练就是迁移学习。我们现在也知道,我们确实拥有预训练好的、结果良好的模型,例如,在ImageNet上预训练的AlexNet模型已经提供给我们。

我们能否以某种方式利用这一点来提高在目标任务上的性能呢?我们可以通过几种方式做到这一点。我们之所以能做到这一点,是因为我们知道AlexNet的滤波器,或者任何其他CNN模型的滤波器,其前几层具有一些通用特征,而随着你进入更深层,特征逐渐变得专门化。为什么我们这么说?如果你回到前面的一张幻灯片,你会看到较早层的滤波器似乎捕捉像边缘和颜色斑点等通用信息,而较晚的滤波器似乎捕捉一些特定于物体的信息,这可能专门针对该任务。

微调

那么,我们现在可以做什么呢?你可以将一个预训练的神经网络(如AlexNet)用于新领域,并保持AlexNet的所有权重完全不变,不为这个新任务更新它们。相反,只取最后一层,我们称之为分类层,并仅为新任务训练这些参数。我们称之为微调。在这里,你基于另一个模型的预训练权重来初始化新任务的网络权重,并且只针对新任务微调新网络中的某些层。

我们刚刚讨论了这里的第一个设置,即只训练分类层。你也可以使用预训练权重作为初始化,然后微调整个网络或CNN末端的某些层,不仅仅是分类层。你可以取最后三层或最后四层,根据你的选择进行微调,以更好地建模目标任务。选择哪一种取决于你的数据集大小以及你的目标任务与源任务的相似程度。

不同场景下的微调策略

让我们看看几种可能性,看看我们能做什么。

场景一:数据集小,且目标与源数据集相似

在这种情况下,由于目标数据集和源数据集彼此相似,你可以说在这两个数据集上训练的模型的专门化特征很可能保持不变。因此,你现在可以做的是:取源任务的通用层和专门层(中间显示的整个块可以是一个完整的预训练网络,如AlexNet或ResNet,你在另一个数据集上训练过,我们称之为源任务)。保持其不变,只随机初始化分类层,并仅为新任务的输出训练这一层。这是因为你的数据集很小,你可能没有足够的数据来重新训练或微调所有这些特征提取层,不如只专注于正确训练分类层。

在这种情况下,我们使用预训练权重初始化新网络,并且只微调分类层。

场景二:数据集小,且目标与源数据集不相似

这次意味着专门化特征可能不通用,但通用特征(如边缘检测器这类滤波器)可能仍然对源任务和目标任务通用。我们能做什么呢?我们现在考虑整个预训练的CNN,因为我们知道通用层(我们指的是CNN的早期层)的特征图。你可以从你的预训练网络中获取这些特征图作为输出,然后训练另一个分类器,例如支持向量机,也可以是神经网络,来为目标任务提供输出。

我们这样做是因为数据集小,并且目标数据集和源数据集不相似,我们不能使用在源任务上预训练网络的专门化特征。我们现在假设从前几层获得的通用特征(我们所说的表示是指每个输入的那些特征图)将帮助我们提高分类器的性能,尽管数据集很小。

场景三:数据集大

当你的数据集很大时,你有一些优势。你现在可以使用预训练网络作为良好的初始化,在目标数据集上微调整个网络。但是,你也可以做一些事情来进一步提高性能。

在微调时,你可以将学习率保持得非常低。请记住,学习率是你的梯度下降方法中梯度的系数。你可以保持学习率很低,这样你的预训练参数就不会发生显著变化。你在说:我仍然希望我的整体网络权重与我从源任务中学到的权重相似,但我有数据,我将更新它,但让我保持低学习率,这样我就不会对权重做太多偏离源任务的更新。

另一方面,如果数据集非常不同且数据量大,有明确的方法,例如所谓的传递迁移学习等方法,你可以从头开始训练。你根本不需要微调,你可以使用Glorot初始化随机初始化,并在新的目标任务上从头开始训练,因为你有一个大数据集。你在说目标任务和源任务彼此不相似,所以你不如从头开始训练,或者使用传递迁移学习等方法,这是传统机器学习中的已知方法,可以帮助你提高性能。

总结

本次讨论的主要思想是告诉你,虽然我们看到了几种不同的设计,但为给定问题设计新的CNN架构并非易事。有许多超参数,包括层数、宽度、分辨率、滤波器数量,以及学习超参数。尝试为某个领域和任务本身找到合适的组合可能是一项非常困难的任务,而使用迁移学习和微调的思想可以帮助你采用一个已解决不同问题的现有架构,并将其适应到新问题上。

这是人们将CNN应用于新领域和新任务时非常常见的流程。

延伸阅读

  • 你可以阅读《深度学习》一书的第9章。

  • 以及CS231N课程中关于迁移学习的讲座。

  • 如果你感兴趣,可以阅读最早尝试研究CNN特征在迁移学习背景下可迁移性的文章之一。

097:CNN可视化方法 🧠

在本节课中,我们将学习如何可视化卷积神经网络(CNN)的内部工作机制。我们将探讨几种不同的方法,包括可视化网络中的滤波器、理解中间层的激活模式,以及通过实验分析CNN的决策依据。这些技术有助于我们理解CNN究竟“看到”了什么,从而增强对模型的信任和解释能力。

可视化滤波器或卷积核 🔍

上一节我们介绍了CNN可视化的目标,本节中我们来看看最直接的方法:可视化CNN各层学习到的滤波器或卷积核。

在CNN的每一个卷积层中,都包含一定数量的滤波器。例如,在AlexNet的第一个卷积层中,有64个尺寸为11x11x3的滤波器(对应RGB三个通道)。我们可以将这些滤波器本身当作图像来可视化。

以下是可视化AlexNet第一层滤波器的结果:

  • 边缘检测器:许多滤波器看起来像不同方向的高斯边缘检测器,用于捕捉图像中的线条和轮廓。

  • 高阶模式检测器:部分滤波器能捕捉棋盘格、条纹等更复杂的纹理模式。

  • 颜色检测器:还有一些滤波器专门检测特定颜色之间的过渡或色块。

这种现象并非AlexNet独有。在ResNet、DenseNet、VGG等不同模型的第一个卷积层中,学习到的滤波器都非常相似,普遍呈现出类似Gabor滤波器的特性。Gabor滤波器是高斯函数与正弦函数的乘积,可以调整其尺度和方向来检测不同方向的边缘和纹理。CNN在训练过程中,自发地学习到了这类基础特征检测器,因此我们常称此现象为“类Gabor滤波器疲劳”。

可视化CNN学习到的表示空间 🌌

了解了基础滤波器后,我们进一步探索CNN如何组织其学习到的高级特征。本节我们将学习如何可视化CNN的表示空间。

以AlexNet为例,其最后一个全连接层(fc7)会输出一个4096维的特征向量。我们可以收集验证集中所有图像经过网络前向传播后得到的这些向量。

为了在二维平面上观察这些高维向量的分布,我们需要使用降维技术。以下是两种常用方法:

  • 主成分分析(PCA):一种经典的线性降维方法。

  • t-分布随机邻域嵌入(t-SNE):一种更强大的非线性降维方法,能更好地保留高维空间中的局部结构。

将MNIST手写数字数据集的特征表示通过t-SNE降至二维后,可以观察到属于同一数字的样本会聚集在一起,形成清晰的簇。这表明,在送入最后的分类层之前,CNN已经将不同类别的样本在特征空间中很好地分离开了。对于更复杂的ImageNet数据集,可视化结果也显示,语义相似的物体(如各种汽车、田野场景)在降维后的空间中也彼此靠近。这证明CNN在中间层学习到的表示确实捕获了图像的语义信息。

可视化特征图与神经元激活 🎯

除了静态的滤波器,我们还可以观察网络在处理具体图像时的动态响应。本节我们来看看如何可视化特征图和特定神经元的激活。

我们可以可视化CNN中间层(如conv5)输出的特征图。当输入一张包含两个人的图像时,某些特征图会高亮显示图像中存在两个独立实体的区域,这表明深层网络能够捕捉图像中物体的高级语义信息。

另一种方法是分析单个神经元的激活。对于一个训练好的CNN,我们可以观察哪些输入图像(或图像中的哪些区域)能最大程度地激活某个特定的神经元。通过这种方法,我们可以发现:

  • 某些神经元专门对人像(尤其是上半身)产生强烈响应。

  • 另一些神经元可能对狗、红色色块、文本或镜面反射等特定模式敏感。

这有助于我们理解CNN内部不同神经元的分工,并与Dropout正则化技术的设计初衷联系起来——即鼓励神经元学习多样化、非冗余的特征。

遮挡实验 🔦

最后,为了理解CNN的决策依据,即它到底关注图像的哪一部分来做出判断,我们可以进行遮挡实验。

遮挡实验的方法如下:使用一个灰色方块依次遮挡输入图像的不同区域,然后将遮挡后的图像输入CNN,并记录正确类别的预测概率如何变化。通过在整个图像上滑动灰色方块,我们可以生成一个热力图。

热力图中,红色表示高概率区域(遮挡后对预测影响小),蓝色表示低概率区域(遮挡后导致预测概率大幅下降)。例如:

  • 在一张博美犬图片中,当灰色方块遮挡住狗的脸部时,模型预测为“博美犬”的概率急剧下降,甚至可能错误地分类为“网球”。这说明模型确实是依靠狗脸部的特征来识别它的。

  • 在一张包含人物和一只阿富汗猎犬的图片中,遮挡住狗的脸部同样会导致对应类别的预测概率大幅降低,尽管图片中存在可能造成干扰的人物。

这个实验直观地展示了CNN在分类时真正关注的图像区域,增强了模型决策的可解释性。

总结 📝

本节课中我们一起学习了多种CNN可视化方法,这些方法都属于“不干扰模型”的分析方式,即我们直接使用训练好的模型进行分析。我们主要探讨了四种技术:

  1. 可视化卷积核:直接绘制网络学到的滤波器,这在第一层最具解释性,常表现为类Gabor滤波器。

  2. 可视化表示空间:对网络中间层(如全连接层)输出的高维特征进行降维(如t-SNE),观察不同类别样本在特征空间中的分布。

  3. 可视化神经元激活:找出能最大程度激活网络中特定神经元的输入图像或图像块,以理解神经元的功能。

  4. 遮挡实验:通过系统性地遮挡输入图像的不同部分,观察模型预测概率的变化,生成热力图以揭示模型的关注区域。

这些方法为我们打开CNN“黑箱”提供了窗口,是理解和解释深度学习模型行为的重要工具。

推荐阅读与参考资料 📚

  • CS231n 课程讲义第13讲。

  • Jason Yosinski 的深度可视化工具包演示视频和网页。

  • 关于t-SNE降维技术的详细介绍与可视化示例。

  • Zeiler, M. D., & Fergus, R. (2014) 的论文《Visualizing and Understanding Convolutional Networks》。

098:用于目标检测的CNN - 深度学习时代前和初始步骤

概述

在本节课中,我们将要学习目标检测任务,这是计算机视觉中一项非常重要的应用。我们将从深度学习时代之前的方法开始,了解早期目标检测是如何实现的,然后探讨如何将卷积神经网络(CNN)应用于目标检测任务。

从分类到检测

到目前为止,我们已经学习了神经网络最基本的形式——前馈神经网络,学习了反向传播,了解了如何使用不同变体的梯度下降法,并掌握了一些训练神经网络的实用技巧。随后,我们转向了计算机视觉的基石——卷积神经网络(CNN)。我们看到了如何使用反向传播训练CNN,了解了CNN中的各种架构。上周我们还学习了如何可视化和理解CNN正在学习什么,以及如何解释它们的预测。

然而,在迄今为止的所有讲座中,我们关注的任务都是分类。给定一张图像,我们的目标是分类图像中包含什么物体。你可以通过将交叉熵损失改为均方误差,将其扩展到回归任务,预测实数值。但是,视觉领域还有许多其他类型的任务,本周我们将开始学习这些任务。

我们将从当今最常用的任务之一——目标检测开始。

分类、定位与检测的区别

posted @ 2026-10-17 13:17  绝不原创的飞龙  阅读(6)  评论(0)    收藏  举报