图宾根深度学习笔记-全-

图宾根深度学习笔记(全)

001:课程简介与组织架构 🎓

在本节课中,我们将要学习蒂宾根大学《深度学习》课程的整体介绍与组织架构。我们将了解课程的基本信息、教学形式以及每周的学习流程。

欢迎来到蒂宾根大学的《深度学习》课程。我是安德烈亚·盖格,本门课程的讲师。深度学习是机器学习的一个分支,在过去十年中,它不仅席卷了工业界,也深刻影响了学术界。每年都有新的、更激动人心的进展出现。你们中的许多人可能都听说过它。

我想用一段你们可能熟悉的前总统的发言来开始这门课。

大家好,我是巴拉克。欢迎来到蒂宾根大学的深度学习课程。众所周知,深度学习已经彻底改变了从计算机视觉、医学、生物信息学到自然语言处理等众多研究领域。它也让智能系统(如自动驾驶汽车)更自然地与环境交互迈进了一步。深度学习是更广泛的机器学习方法家族的一部分,基于人工神经网络。深度模型从大型数据集中学习,以预测未见过的输入(如图像、文本或音频)的结果,并且它们在这方面表现得非常出色,超越了该领域大多数经典的基准方法。事实上,这段视频是由一个名为“神经语音木偶”的深度模型完全自动生成的。我的脸和声音都不是真实的。因此,我也可以在这门课上改变我的声音。安德烈亚和他的团队将告诉你开始学习深度学习所需知道的一切。希望你们喜欢这门课。

这段视频展示了深度学习在生成式人工智能领域的强大能力,它本身就是一个深度学习模型的应用实例。


这门课程采用所谓的“翻转课堂”形式进行教学。这意味着我们在现场课之前,会通过YouTube平台提前提供讲座视频和作业。然后,我们所有人会在现场课中重新聚在一起,以面对面的形式共同讨论困难的主题和你们的问题,也可能完成一些我们在现场课中提出的小任务或回答的问题。

因此,提前观看这些讲座视频对于你们从现场课中有所收获至关重要。我们也建议你们与同学组成学习小组,在课前讨论讲座视频的内容以及练习题。

为了提供一个自我评估和自我激励的机制,我们提供了在线的“讲座测验”。你们可以通过这些测验评估自己,并为期末考试赚取一些加分。这些测验也为我们提供了必要的反馈,以便更深入地讨论班上大多数同学难以理解的主题。

我们为讲座和练习都提供了这种测验。此外,对于练习,我们还提供了一个在线的Zoom帮助台,你们可以直接从本课程的助教那里获得指导和帮助来解决练习。我们还提供了一个在线聊天室或论坛,你们可以非常快速地获得关于讲座视频或练习问题的反馈。

一个典型的学期周流程如下:

  • 观看讲座视频。
  • 尝试解决练习题。
  • 咨询学习小组或练习帮助台。
  • 完成练习测验。
  • 带着所有这些知识和问题参加现场课,我们将在现场课中与其他同学一起更详细地讨论这些问题。

以上就是我现在想说的全部内容。我非常期待在我们的第一次现场课上见到你们。


本节课中,我们一起学习了蒂宾根大学《深度学习》课程的基本介绍和独特的“翻转课堂”教学模式。我们了解到课程将通过提前发布的视频、练习题、在线测验、帮助台和论坛来支持学习,最终在现场课中进行深入讨论和答疑。这种设计旨在鼓励主动学习和协作。

002:历史简介 🧠

在本节中,我们将简要回顾深度学习的发展历程。我们将看到,深度学习并非一蹴而就,而是经历了几个关键的发展阶段,每个阶段都有其独特的贡献与挑战。

深度学习的发展经历了三个主要阶段。这些阶段并非从一开始就被称为“深度学习”,而是随着时间推移逐渐形成的。

第一波:控制论时代(1940s-1960s)⚙️

第一波浪潮被称为控制论时代,也被视为深度学习的“黄金时代”。当时,研究人员使用简单的大脑计算模型来模仿生物学习,并学习简单的规则。

这些模型在分类简单模式方面取得了成功。然而,人们逐渐意识到,这些简单模型不足以解决更复杂的任务,这导致了相关研究的衰退。

早期神经元模型

1943年,McCulloch和Pitts提出了一个早期的神经激活计算模型,称为线性阈值单元或神经元。

该模型的核心是一个基于线性计算的阈值函数。其公式可以表示为:

f(x) = sign(W·x)

其中,x 是输入数据或特征,W 是权重。如果权重与特征的乘积大于0,则输出+1;如果小于0,则输出-1。这是一个二元决策模型。

研究表明,这个简单的操作比基本的“与”门或“或”门更强大,后者只是该计算的特例。但在当时,还没有有效学习权重的方法。

感知机的诞生

1958年,Rosenblatt开发了著名的感知机。这既是一种算法,也是一个实际的硬件实现,用于训练单个线性阈值神经元。

它使用感知机算法进行训练,该算法优化了所谓的感知机准则。这与我们今天使用基于梯度的反向传播优化方法有很大不同。

感知机算法关注所有被错误标记的样本集合 M,并基于真实标签(+1或-1)最小化一个损失函数,以期在算法的下一次迭代中正确分类这些样本。

对于这个简单模型,这是一个最优的做法。虽然不能保证以最少的迭代次数收敛到解,但已被证明,如果解存在(即两类数据是线性可分的),算法将收敛到正确的解。

感知机的成功引起了媒体的广泛关注,它被展示用于区分男女性别面孔等任务。然而,这种成功也导致了过度的炒作和期望。

第二波:联结主义时代(1980s)🔗

在20世纪80年代,深度学习常被称为或与“联结主义”相关联。联结主义认为,智能行为是通过大量非常简单的单元或计算细胞实现的。

正是在这个时代,反向传播、序列模型等非常重要的算法被开发出来。然而,这一发展处于阴影之中,被其他领域的发展所掩盖,因此也被称为“黑暗时代”。

被掩盖的原因是,虽然这些算法在简单例子上有效,但由于缺乏算法、计算力和数据,无法在计算机视觉等领域的复杂、具有挑战性的现实世界任务上取得重大突破。

神经认知机与卷积的灵感

1979年,福岛邦彦提出了神经认知机,它是当今我们仍在广泛使用且最成功的模型之一——卷积神经网络的前身。

福岛邦彦的神经认知机深受Hubel和Wiesel在20世纪50年代对猫视觉皮层(V1区)实验的启发。他们发现视觉皮层中有不同类型的神经细胞:对边缘方向敏感的“简单细胞”和对精确位置不敏感的“复杂细胞”。这一伟大发现于1981年获得了诺贝尔奖。

神经认知机模仿了这些简单和复杂细胞,提出了一个由S细胞(简单)和C细胞(复杂)组成的多层处理网络。这基本上实现了我们今天所知的卷积和池化——深度神经网络中两个最基本的操作。然而,当时还没有反向传播算法,学习是通过基于强化的学习规则进行的。尽管如此,该模型架构对今天非常成功的模型产生了重要启发。

反向传播算法的复兴

反向传播算法在1961年就已为人所知,但第一个经验性的成功是在1986年由Rumelhart、Hinton和Williams展示的。

反向传播算法至今仍是深度学习研究的主要工具。它之所以如此重要,是因为它能够高效地计算深度神经网络中关于网络权重的梯度。这些网络拥有数百万甚至数十亿的参数,因此在学习算法的每次迭代中,高效计算权重更新至关重要,而反向传播算法提供了这种能力。它使得基于梯度的学习能够应用于深度网络,这在当时是一个重大突破。

长短期记忆网络

1997年,Schmidhuber和Hochreiter提出了现在著名的长短期记忆网络。早在1991年,Hochreiter就在他的学位论文中阐述了梯度消失和梯度爆炸的问题,这在序列建模任务中尤为严重。

LSTM通过结合反馈循环以及“遗忘门”和“输入门”,有效地在很长的时间跨度上桥接了梯度流,从而高效地存储信息。这彻底改变了自然语言处理领域,尽管它的广泛认可和引用在2014-2015年左右才达到高峰,当时人们证明在大数据集和大算力下它效果极佳。LSTM已成为谷歌等主要科技公司机器翻译和语言识别技术的核心组件。

第三波:深度学习革命时代(2006至今)🚀

从2006年至今,深度学习才真正被称为“深度学习”,我们可以称之为革命时代。人们证明,通过更深的网络、更大的数据集和更强的计算能力,深度学习能够取得最先进的结果,几乎主导了不同领域和学科的排行榜。

卷积神经网络的崛起

1998年,Yann LeCun提出了卷积神经网络,它使用反向传播算法进行端到端训练。它通过卷积实现空间不变性,通过最大池化进行下采样,并利用权重共享的思想来减少参数量。

这篇论文真正的新意在于,首次证明了这些之前就已存在的卷积网络可以用反向传播算法非常有效地训练,并在标准机器学习任务(如MNIST手写数字分类)上取得了非常好的结果。然而,当时的结果尚未能扩展到人们感兴趣的复杂计算机视觉问题上,因此卷积神经网络在2006年至2010年左右才真正开始流行。

ImageNet与AlexNet的突破

一个重大的突破是AlexNet在ImageNet数据集上的演示。ImageNet是一个包含1000个类别、数百万张标注图像的大规模视觉识别挑战赛数据集。

2012年,多伦多大学的Hinton团队通过调整基本的卷积神经网络(他们称之为AlexNet),赢得了这个计算机视觉界非常重要的基准测试。这对计算机视觉研究者来说是一个真正的突破和启示,他们第一次看到深度学习技术不仅能用于简单的28x28像素数字识别任务,也能用于视觉社区感兴趣的、极具挑战性的任务。

这次成功的背后是多种因素的结合:大规模标注数据集(如ImageNet)的可用性、GPU计算能力的普及,以及更深、更好的神经网络架构。这真正点燃了深度学习的革命之火。

泛化能力与特征迁移

深度学习的一个成功故事在于其出色的泛化能力。研究表明,可以在一个非常通用的任务(如ImageNet分类)上训练卷积神经网络,然后只需为特定任务更新最后一层(如支持向量机或神经网络的最后一层),即使使用很少的数据,也能获得与传统技术相竞争甚至更好的性能。

这意味着,基于通用任务学习的CNN能够产生非常高层和抽象的特征,这些特征对各种各样的任务都极其有用。这展示了深度学习模型的通用性和强大能力。

网络可视化与对抗样本

2014年,Zeiler和Fergus发表了一篇关于可视化深度神经网络所学内容的论文。他们发现,网络的更高层捕获了更抽象的语义信息(如狗的脸、轮子、眼睛),而较低层则捕获基本的梯度信息。这为了解这些“黑箱”模型提供了见解。

同样在2014年,研究也展示了这些强大网络的另一面:它们可以很容易被“对抗样本”愚弄。通过对图像添加人眼难以察觉的微小扰动,就能使分类器以高置信度给出完全错误的预测。这发展成了一个独立且活跃的研究领域。

生成模型的飞速进展

从2014年左右开始,事情发展得极其迅速。深度学习在计算机视觉、自然语言处理、医学和生物信息学等多个领域占据主导地位。

深度生成模型取得了令人震惊的进展。这些模型以无监督的方式训练数据集的生成过程。在短短几年内,从生成粗糙的图像,发展到能生成以假乱真、难以与真实照片区分的高质量图像,例如StyleGAN系列模型。

强化学习与游戏AI

2015年,DeepMind展示了如何将深度神经网络与强化学习巧妙结合,让智能体通过随机探索和奖励信号,学会玩多种Atari游戏,而无需观察任何人类游戏过程。

2016年,DeepMind进一步展示了在更复杂的围棋游戏上的突破,其程序AlphaGo首次击败了职业棋手。2017年,其升级版AlphaZero无需学习人类棋谱,完全通过自我对弈,不仅掌握了围棋,还掌握了多种其他游戏。

语言模型的变革

在自然语言处理领域,2017年和2018年的两篇重要论文——Transformer和BERT——带来了变革。Transformer表明,可以用注意力机制非常有效地取代经典的循环或卷积结构。BERT则证明,可以在海量无标签文本上对模型进行有效的预训练,然后在特定任务上微调即可获得最先进的结果。

3D深度学习与多模态

从2016年起,深度学习也开始攻克更难的领域,如3D深度学习。出现了能够有效预测体素、点云、网格或隐式表示的模型,例如仅从一张2D RGB图像就能预测出3D重建结果。

大规模语言模型与未来挑战

2020年,OpenAI的GPT-3模型出现,它拥有1750亿参数,使用海量计算资源在文本库上训练。这个模型一经训练,就能解决许多不同的任务,如编写可编译的代码、创作诗歌、撰写新闻等,产生了巨大影响。

总结与未来挑战 📈

本节课中,我们一起回顾了深度学习从控制论、联结主义到现代革命的简要历史。我们看到了关键模型和算法的诞生,如感知机、反向传播、CNN、LSTM、Transformer等,以及大数据和强算力如何共同推动了这场革命。

然而,仍然存在许多挑战等待下一代研究者去解决:

  • 数据依赖:许多成功依赖于有监督学习,需要大量昂贵的标注数据。趋势正回归到无监督和自监督学习。
  • 交互学习:人类通过与物体互动来学习,而不仅仅是静态数据集。机器人学和计算机视觉社区正在融合。
  • 准确性与鲁棒性:在自动驾驶等安全关键领域,需要远超当前图像识别精度的模型。模型需要对新领域和对抗样本更具鲁棒性。
  • 归纳偏差与可解释性:理解模型的归纳偏差及其影响非常重要。
  • 计算与能效:大规模模型训练需要巨大的计算集群,而人脑仅需数百瓦。
  • 伦理与法律问题:伴随着技术进步,产生了诸多伦理和法律问题。

最后,留给我们思考的问题是:过去几年我们见证的“AI摩尔定律”(错误率每年大约减半)在未来几年是否会持续?这尚不明朗。深度学习的旅程仍在继续,充满了机遇与挑战。

003:机器学习基础

在本节课中,我们将学习机器学习的一些基础概念。我们将介绍不同类型的学习问题,并重点探讨监督学习中的回归问题,特别是线性回归和多项式曲线拟合。我们还将讨论模型选择、过拟合与欠拟合、正则化以及最大似然估计等核心概念。

学习问题的分类

上一节我们介绍了本课程的整体结构,本节中我们来看看机器学习中不同类型的学习问题。根据所使用的数据或提供的监督信号,学习问题主要可以分为以下几类:

  • 监督学习:使用由数据-标签对组成的数据集来学习模型参数。即输入 X 和输出 y,共有 N 个这样的配对。例子包括分类、回归或结构化预测问题。
  • 无监督学习:使用没有标签的数据集来学习模型参数。这里只是一个输入 X 的数据集。例子包括聚类(如K均值算法)、降维或生成模型。
  • 自监督学习:使用由数据-数据对组成的数据集来学习模型参数。即我们有 xx' 配对。例子包括自监督立体视觉和光流估计,或对比学习。
  • 强化学习:通过从稀疏奖励中进行主动探索来学习模型,而不是使用固定的数据集。即有一个智能体与环境交互。例子包括深度Q学习、策略梯度和行动者-评论家方法。

在本讲座中,我们将涵盖前三种学习问题。

监督学习:分类、回归与结构化预测

现在,让我们从监督学习问题开始。具体来说,我们来看看分类、回归和结构化预测之间的区别。

  • 分类:目标是使用一个函数 f 从输入 x 预测一个离散的类别标签。
  • 回归:目标是从任意输入 x 预测一个连续的变量。
  • 结构化预测:输入和输出都是结构化对象。

这里的区别在于,在分类或回归中,我们预测的是一个一维的量,而在结构化预测中,我们预测的是一个高维的量。今天我们将重点讨论回归问题。

以下是这些问题的可视化概述:

这是一个非常通用的机器学习模型 f 的图示,它有一些参数 W,接收输入 X 并预测输出 y。与我们机器学习从业者相关的有两个主要任务:

  1. 学习任务:在监督学习的情况下,从训练集的 xy 对中估计参数 W
  2. 推理任务:在训练或学习阶段之后,给定这个固定的 W,我们想使用这个模型进行新的预测。即对于一个新输入 X,我们希望在给定已学习的固定 w 的情况下执行该函数,并预测出对应的 y

让我们看一些具体例子:

  • 分类示例(图像分类):输入是一张图像(来自空间 R^(宽×高))。模型将其映射到一个二元变量(例如,是海滩或不是海滩)。
  • 回归示例(股票价值预测):输入是一个股票价值序列(R^n)。输出是一个单一的数字(例如,明天的股票价值)。这是一个连续的数字,因此是回归问题。
  • 结构化预测示例
    • 语音识别:输入是音频信号,输出是一个句子(单词序列)。
    • 语义分割:输入是一张图像,输出是一个标签图(为每个像素分配语义类别标签)。
    • 3D重建:输入是一组图像,输出是一个3D重建模型(例如,用体素网格表示)。

以3D重建为例,假设我们想预测一个 32^3 维的输出(即 32^3 个体素),每个体素用一个二元变量表示(占用或空闲)。那么可能的重建数量是 2(323),这是一个极其巨大的数字,甚至超过了宇宙中的原子数。因此,我们无法枚举所有可能的重建,必须采用更结构化的方法。

线性回归

现在让我们回到回归问题,看看所谓的线性回归。在线性回归中,我们希望以线性的方式对模型参数进行预测。

X 表示一个大小为 n 的数据集。设 (x, y) 是该数据集 X 中的元素,其中输入可以是高维的,输出是一维的(y 是一维变量)。目标是为先前未见过的输入 x 预测 y

我们定义一个误差函数 E,它衡量沿 y 方向的位移。例如,上图中绿色数据点与红色模型预测之间的红色短线就是误差。模型由参数 W 指定。这些基本上是模型产生的误差,我们希望最小化总体误差或所有误差的总和。

在线性回归中,我们为该函数选择一个线性模型。函数表示为线性模型,即权重向量 W 与输入特征 x 的内积:f(x; w) = w^T x

误差函数简单地是所有数据点上模型预测 f 与观测值 y 之间在 y 方向上的差值平方和。这里的小 i 索引数据集中的元素。

我们可以将其重写为:E(w) = Σ_i (w^T x_i - y_i)^2

使用线性代数,我们可以更优雅地写出这个表达式:E(w) = ||X w - y||_2^2。这里,矩阵 X 包含所有数据点特征 x_i 作为行向量,w 是权重向量,y 是包含数据集中所有 y_i 的向量。这基本上是将这些 xy 逐行堆叠成一个线性系统。

为了获得这个能量函数 E 的最小值,我们需要遵循梯度并将其设为零。计算该表达式关于 W 的梯度:∇_w E(w) = 2 X^T (X w - y)

由于这里的误差函数是关于 W 的二次凸函数,其最小化器有闭式解。设梯度为零:X^T X w = X^T y。解出 ww = (X^T X)^{-1} X^T y。这个矩阵常被称为摩尔-彭罗斯伪逆或伪逆,因为它允许我们计算非方阵的逆,是这个最小二乘问题的最小化器。

多项式曲线拟合与过拟合

线性最小二乘允许我们拟合数据的线性模型,但通过使用非线性变换将输入转换为非线性特征,我们也可以用闭式解估计例如多项式的参数,这被称为多项式曲线拟合。

现在我们选择一个 M 阶多项式来建模数据集 X。这个多项式可以简单地写成单项式 x^j 的和:f(x; w) = Σ_{j=0}^{M} w_j x^j。我们可以用与之前相同的方式将其写成权重向量转置乘以特征向量,其中特征向量由单项式堆叠而成:[1, x, x^2, ..., x^M]

这允许我们使用一个非常简单的线性表达式来表示这个多项式曲线拟合模型:它在 W 上是线性的,但在输入 x 上不是线性的。我们可以用与线性回归问题相同的形式来表述这个误差函数:E(w) = ||Φ w - y||_2^2,其中矩阵 Φ 的每一行是单个数据点的特征向量(单项式)。

让我们看看不同阶数 M 的多项式曲线拟合结果:

  • M=0(常数):欠拟合。无法很好地拟合训练集或测试集。
  • M=1(线性):欠拟合。
  • M=3:对于这个数据集,得到了相当合理的结果。
  • M=9:由于有10个自由度(10个数据点),可以将训练误差降为零。但拟合实际上是过拟合:虽然完全拟合了绿色训练点,但对蓝色新测试点的预测不一定好,曲线振荡剧烈。

选择正确的阶数 M 是一个模型选择问题。在机器学习中,我们不仅希望模型在训练集上表现良好,更希望在新的、先前未见过的输入(测试集)上表现良好,这被称为泛化

我们通常假设训练数据和测试数据是独立同分布地从数据分布 P_data(x, y) 中抽取的。

术语回顾

  • 容量:模型可以表示的函数的复杂性。
  • 欠拟合:模型过于简单,即使在训练集上误差也不低。
  • 过拟合:训练误差很小,但泛化误差(测试误差)很大。

我们希望避免这两种情况,找到一个在测试集上表现良好的模型。

以下是泛化误差随多项式阶数 M 变化的示例图:训练误差随着 M 增加持续下降,但测试误差在达到某个阶数后开始上升,这正是发生过拟合的时候。

模型选择与正则化

为了选择最佳模型,我们需要将数据集划分为训练集、验证集和测试集。我们使用验证集来选择超参数(例如多项式阶数、神经网络的学习率)。重要的是,测试集通常只评估一次,其真实标签可能不可用。

接下来我们看看岭回归。这是另一种降低模型复杂度的方法,不是直接改变多项式的阶数,而是通过添加一个正则化项。我们有一个可以调整的连续参数。

岭回归问题的误差函数如下:E(w) = ||X w - y||_2^2 + λ ||w||_2^2。与之前模型相比的变化用红色标出。

新增的项称为惩罚项或正则化器,它告诉模型在良好拟合的同时,也要让权重 w 变小(通过超参数 λ 控制强度)。过拟合行为通常发生在权重变得非常大时,正则化有助于避免这种情况。这个问题在 W 上也是二次的,因此也有闭式解:w = (X^T X + λ I)^{-1} X^T y

通过选择合适的正则化强度 λ,我们可以在欠拟合和过拟合之间找到平衡点,获得更好的泛化性能。

估计量的偏差与方差

更抽象地看,我们讨论估计量及其偏差和方差。我们将函数 g 称为估计量,如果它将数据集 X 映射到一组模型参数 W(记为 ŵ)。一个好的估计量返回的参数集接近真实值。

由于数据集是从基础数据分布中随机抽取的,因此任何关于数据的函数都是随机的,ŵ 本身也是一个随机变量。这引出了偏差和方差的术语以及所谓的偏差-方差困境。

  • 估计量的偏差:在所有可能数据集上估计值的期望与真实参数值之差。如果偏差为零,则估计量是无偏的。好的估计量偏差小。
  • 估计量的方差:估计值 ŵ 在所有可能数据集上的方差。好的估计量方差也低。

偏差-方差困境指出,我们不能同时拥有低偏差和低方差,必须进行权衡。总误差可以数学表达为偏差平方、方差和不可约误差之和。我们希望选择模型复杂度,使总误差最小化,这发生在偏差和方差之间取得平衡时。

最大似然估计

在本单元的最后部分,我们将讨论最大似然估计,这是从概率角度对之前结果的一种重新解释。

现在假设模型是一个概率分布的参数族。即从概率角度看,模型实际上给出了在给定输入 X 和参数 W 的条件下,输出 y 的分布,而不是 y 的点估计。

在这种情况下,W 的条件最大似然估计量由下式给出:ŵ_MLE = argmax_w p(y | x; w)

利用数据独立同分布的假设,这等于最大化所有数据点个体概率的乘积。取对数(单调函数不改变最大值点),将乘积转化为求和,得到对数似然函数。

如果我们假设模型分布是高斯分布,其均值是 xW 的线性函数(即之前的线性表达式),方差为 σ^2。将其代入最大似然估计表达式,经过推导,我们发现最大化对数似然等价于最小化之前的最小二乘误差函数。因此,选择高斯分布作为模型分布会使最大似然估计量产生与之前推导的最小二乘估计量完全相同的结果。

最大似然估计器是统计学习理论中一个非常重要的工具,因为在温和的假设下,已知最大似然估计器是一致的(当训练样本数趋于无穷时,收敛到真实参数)和高效的(收敛速度最快)。它在模型的概率解释和最小二乘公式之间提供了很好的联系。

总结

本节课中,我们一起学习了机器学习的基础知识。我们首先了解了监督学习、无监督学习、自监督学习和强化学习等不同类型的学习问题。然后,我们深入探讨了监督学习中的回归问题,从简单的线性回归出发,扩展到多项式曲线拟合,并指出了过拟合与欠拟合的现象。为了解决过拟合,我们介绍了通过验证集进行模型选择以及使用岭回归进行正则化的方法。接着,我们从统计角度探讨了估计量的偏差与方差困境。最后,我们介绍了最大似然估计,将其与最小二乘法联系起来,并理解了其作为概率框架基础的重要性。这些概念为后续深入学习神经网络和深度学习奠定了坚实的基础。

004:图上的机器学习 🧠

在本节课中,我们将要学习图神经网络。这是一种以图上的信号作为输入,并产生图上信号作为输出的神经网络类别。我们将从学习图神经网络的动机和应用开始,然后深入其核心构建模块——图卷积滤波器,最后介绍图卷积网络这一具体实例。

动机:为什么需要图上的机器学习?🤔

我们已经知道,多层感知机是非常灵活的函数逼近器。事实上,一个仅包含单个隐藏层(假设该层足够宽)的MLP,就能够逼近我们想要的任何函数。

然而,我们也看到MLP的规模难以扩展。例如,当输入维度非常高时(比如一张百万像素的图像),模型中的参数量会急剧膨胀,从而导致模型过拟合。

对于生活在规则网格上的结构化信号(如一维时间网格或二维图像网格),卷积神经网络可以解决这个问题。但CNN的问题在于,它们只适用于这种规则的网格结构数据。

许多数据无法轻易地用这种规则格式来描述。例如,具有图结构的分子就很难被放入规则的网格格式中。因此,我们在这节课中试图寻找一种模型类别,它比MLP更具扩展性,同时又比CNN更灵活。我们想要一种介于两者之间的模型:既能泛化CNN使其更灵活,又能处理大型、高维的数据集。

下图是同一个论点的另一种说明。我们知道,我们可以用卷积网络很好地处理左侧的网格结构数据,因为假设我们具有平移等变性,就可以用相对较少的参数处理此类数据。现在的问题是,我们如何为右侧所示的通用图构建神经网络?答案就是图神经网络,或者其一个具体实例——图卷积网络。我们的目标与处理左侧CNN时一样,也要利用图的局部结构。我们希望利用图的局部连接结构作为先验信息,以构建泛化能力良好的模型。

图是信号结构的描述符,信号存储在节点中。图中深色代表节点,浅色代表边,边表达了信号分量之间的相似性。在二维卷积网格(如图像网格)中,边也表达了邻近关系,因为从一个点到另一个点需要经过若干“跳”。

然而,在这种更通用的图表述中,网格不需要是规则的,边的权重甚至可以彼此不同。我们将要定义的图卷积,就像在二维网格上定义的卷积一样,将是基于图结构编码的矩阵所构成的多项式。直观上,我们就像在二维网格结构上应用卷积滤波器一样,在图结构上应用滤波器。

在下图的说明中,我们想为红点计算一个值。我们通过计算绿色和红色元素与卷积滤波器的点积来实现。现在,我们试图为右侧的通用图寻找等效操作,通过利用局部性来实现。如图所示,绿点也仅距离红点一跳之遥。如果我们构建一个更大的滤波器(我们可以用即将开发的公式轻松做到),我们也可以覆盖更大的邻域。例如,这是一个覆盖两跳的邻域,而这里我们也有一个距离目标节点两跳的滤波器。

在深入细节之前,我们先看一些应用,了解这为什么有用。

应用:图神经网络能做什么?🚀

以下是图神经网络的一些可能应用:

  • 分子性质预测:图神经网络是绝佳的工具,因为分子本身就是图结构或可以轻松表示为图。
  • 生物物种分类:可以在图(此处是树)上进行推理,这也可以用图神经网络轻松建模。
  • 语言处理:经常需要处理解析树,而解析树也是图,因此可以纳入图神经网络范式。
  • 场景图生成:输入是图像,运行物体检测器得到物体提议,但这些物体是相互关联的。通过学习它们之间的关系(例如,两个特定语义类别如鸟和树枝的相对位置),我们可以做出更好的预测。右侧展示了一个可能的场景图,它不仅包含物体,还推断出了它们之间的关系,这是比仅代表物体的一组边界框更丰富的表示。
  • 视频多目标跟踪:可以将此问题表示为图,节点是每一帧中的检测结果,目标是跨时间关联检测结果,找出最可能的轨迹。我们可以将其表述为一个图,将所有可能的连接建模为图,并找出哪些连接能导致正确的跟踪结果。
  • 作者归属:观察来自两位不同作者的大量文本,构建一个关于频繁共现功能词的图。功能词是能反映作者特定写作风格的非重要词汇。根据作者使用两个功能词紧密相邻的频率,我们可以推断其写作风格。处理这个图(节点是功能词)可以让我们区分文本的可能作者。
  • 推荐系统:节点代表客户,边代表他们在产品评分方面的平均相似性。当我们想推断某个客户对某个产品的可能评分时,可以利用与该客户评分相似的其他客户的已知评分来进行推断。
  • 学习分子指纹:由于分子固有的图结构,这是图神经网络的另一个重要应用。例如,在特定实验中,目标是预测分子的毒性,这自然可以用图语言来表述。
  • 蛋白质界面预测:蛋白质中的每个残基是图中的一个节点,节点的邻域是蛋白质结构中的相邻节点集。然后可以再次应用图神经网络范式对这些蛋白质进行预测。
  • 交互网络:目标是学习物理属性,而无需将这些知识注入模型。例如,观察多个物体如何随时间相互吸引或绕轨道运行(想象行星),使用图神经网络对这些物体之间的关系建模,以产生与真实情况相似的预测。还有台球场景,球可能相互碰撞或与墙壁碰撞,我们想预测此类序列的未来状态;或者右侧的布料落在红色刚性物体上并因此变形,我们可以通过将布料建模为一组通过图连接的节点来学习物理规律。
  • 物理控制学习:例如,一个参数化不同身体关节的小型行走物体,某种图神经网络推断该物体的底层物理规律,使其能够行走。
  • 无人机集群控制:图神经网络非常擅长于此,因为我们可以非常本地化地处理信息,然后通过无线方式将信息发送给其他智能体,接着以去中心化的方式,使用图神经网络在图之上实现推理,例如,以获得特定的编队飞行。




关于动机的最后一点说明:本讲座基于宾夕法尼亚大学 Alejandro Ribeiro 的《图神经网络》课程,特别是涵盖了该课程第三和第四讲的部分内容。这是一个非常棒的讲座,如果你有兴趣更深入地研究图神经网络这个主题,我强烈建议你进一步观看。

总结 📝

本节课中,我们一起学习了图神经网络的基本概念。我们首先探讨了在图数据上进行机器学习的动机,认识到对于非规则网格结构的数据(如分子、社交网络、知识图谱等),传统的MLP和CNN存在局限性,而图神经网络提供了一种更灵活且可扩展的解决方案。接着,我们浏览了图神经网络在多个领域的广泛应用,包括分子性质预测、场景理解、推荐系统、物理模拟和机器人控制等,这些应用展示了图神经网络的强大能力和通用性。最后,我们了解到本课程内容基于更深入的专题课程,为有兴趣的学者提供了进一步学习的路径。在接下来的章节中,我们将深入探讨图神经网络的核心构建模块——图卷积滤波器。

005:图卷积滤波器 📊

在本节课中,我们将学习图卷积滤波器的核心概念。图卷积是图神经网络的基础构建模块,它允许我们在图结构数据上执行类似卷积的操作。我们将从图的正式定义开始,逐步介绍图信号、图移位算子,并最终理解图卷积滤波器的工作原理。


图的定义

我们首先通过观察所谓的有向图来正式定义图。

有向图是一个三元组 G = (V, E, W)

  • V 代表顶点集,有时也称为节点。它被定义为一组 n 个标签,即从 1n 的整数集合,其中 n 是顶点数量(例如,图中为5)。
  • E 是边集,由标签的有序对 (i, j) 组成。在图卷积的上下文中,我们将边 (i, j) 解释为节点 j 对节点 i 产生影响。这种直觉与图通常的定义方式略有不同。
  • W 是权重集,与每条边 (i, j) 关联的权重 w_ij 是一个实数。它决定了节点 j 对节点 i 的影响强度,但不决定节点 i 对节点 j 的影响强度(后者由 w_ji 决定)。

我们通过顶点之间的箭头来绘制这样的图。箭头总是从施加影响的节点 j 指向被影响的节点 i。例如,边 (1, 2) 在图中表示为从节点2指向节点1的箭头。边 (i, j) 与边 (j, i) 不同。在有向图中,我们可以只有 (i, j),也可以两者都有,并且它们的权重可以不同。

一个图被称为对称的无向的,如果它的边集和权重是对称的。这意味着如果边 (i, j) 在边集中,那么边 (j, i) 也必须在边集中,并且 w_ij = w_ji。我们在图中用双向箭头表示这种连接。

一个图被称为无权重的,如果它没有权重,或者等价地说,所有权重都为1。在这种情况下,图结构仅告诉我们哪些节点在边连接性上是“接近”的,但每条边不包含关于两个节点彼此之间“接近程度”的任何额外信息。

在实践中,我们遇到的大多数图实际上都是加权的。如果我们拥有这种加权信息,我们应该将其考虑在内,因为它是先验知识的一部分。我们能够纳入计算的先验知识越多,我们的预测效果就越好。


图移位算子

现在我们来讨论定义图的变换,即图的数学表述。有多种选项可以定义此变换,它们都源自图的结构(顶点、边和权重集)。

我们在这里定义的所有算子都是移位算子。第一个定义的算子是最简单的,即邻接矩阵。

邻接矩阵
对于一个图 G,其邻接矩阵 A 是一个稀疏矩阵,其非零元素恰好定义在有边(或有权重)的位置。具体来说,A_ij = w_ij 当且仅当边 (i, j) 在边集 E 中。如果图是对称的,那么邻接矩阵也是对称的,即 A = A^T

接下来,我们定义邻域和度。

邻域与度
节点 i邻域 N(i) 是影响节点 i 的所有节点的集合。数学上,N(i) = { j | (i, j) ∈ E }。节点 i d_i 是其所有入射边权重的总和。在无权图的情况下,度就是入射边的数量。度衡量了有多少节点以及它们对特定节点的影响强度。

度矩阵
度矩阵 D 是一个对角矩阵,其对角线元素是各个节点的度,即 D_ii = d_i。度矩阵也可以用邻接矩阵表示为 D = diag(A * 1),其中 diag 操作将向量转换为对角矩阵,1 是全1向量。

下一个图移位算子是拉普拉斯矩阵。

拉普拉斯矩阵
具有邻接矩阵 A 的图的拉普拉斯矩阵 L 定义为度矩阵减去邻接矩阵:L = D - A。其元素可以明确地写为:

  • 非对角线元素:L_ij = -w_ij
  • 对角线元素:L_ii = Σ_{j∈N(i)} w_ij

图上的拉普拉斯矩阵称为图拉普拉斯算子,它在图上具有与规则域上的拉普拉斯算子(梯度的散度)相似的性质。它衡量信号在图上变化的平滑度或曲率。

我们可以对邻接矩阵和拉普拉斯矩阵进行归一化,这在图非常不均匀时特别有用。

归一化算子
归一化邻接矩阵定义为:Ā = D^{-1/2} A D^{-1/2}
归一化拉普拉斯矩阵定义为:L̄ = D^{-1/2} L D^{-1/2} = I - Ā
使用这些归一化矩阵作为移位算子,可以归一化节点间的影响,在处理异构图时具有优势。

图移位算子
图移位算子 S 代表我们学过的任何图矩阵表示。具体来说:

  • 对于邻接矩阵,S = A
  • 对于拉普拉斯矩阵,S = L
  • 对于归一化邻接矩阵,S = Ā
  • 对于归一化拉普拉斯矩阵,S = L̄

如果图是对称的,移位算子也是对称的。我们将图移位算子定义得如此宽泛的原因是,虽然 S 的具体选择在实践中很重要,但我们将要进行的分析适用于任何选择。因此,从现在开始,我们将只考虑通用的 S


图信号与扩散

我们已经讨论了图移位算子,现在来看看图信号。

考虑一个具有 n 个节点的图 G,由其移位算子 S 表示。图信号 x 是一个 n 维向量,它为每个节点分配一个实数值标量。S 编码了 x 各分量之间预期的邻近性或相似性。

输入信号 x 与图移位算子 S 的乘法会在图上产生一个扩散信号。数学上,我们写作 y = Sx。这实现了一个局部操作,其中节点的值与其邻居节点的值混合。每次应用移位算子,信息仅沿着一条边(一跳)传播。通过多次应用 S,我们可以获得一个扩散序列。

扩散序列
扩散过程可以递归地写为:x_{k+1} = S x_k,其中 x_0 是输入信号。等价地,我们可以将其写为幂序列:x_k = S^k x_0。在实践中,递归形式(矩阵-向量乘法)计算效率更高,而幂序列形式在数学分析上有时更方便。


图卷积滤波器

现在,我们准备定义图卷积滤波器。如前所述,图卷积滤波器是图卷积网络的基本构建模块。

给定代表所考虑图的移位算子 S 和一些滤波器系数 h_k(标量),一个图卷积滤波器是移位算子 S 上的一个多项式。

图卷积滤波器定义
图卷积滤波器 H(S) 定义为:
H(S) = Σ_{k=0}^{∞} h_k S^k

将此滤波器应用于信号 x 的结果是:
y = H(S)x = Σ_{k=0}^{∞} h_k S^k x

这意味着滤波器系数 h_k 决定了扩散序列 S^k x 中特定扩散状态的重要性。这些系数可以在图卷积滤波器中学习。与卷积神经网络中的卷积核不同,在图卷积中,我们无法关联相对位置(如图像中的左上、右上像素),因为图结构是置换不变的。因此,我们将滤波器定义为移位算子幂序列 S^k 上的权重。

在实践中,我们无法计算无限项,因此使用有限滤波器。

有限图卷积
考虑一个由移位算子 S 支持的信号 x 和一个有限滤波器 H(截断到 K-1 阶)。图卷积的输出为:
y = H(S)x = Σ_{k=0}^{K-1} h_k S^k x

我们看到,图卷积实际上是扩散序列元素 S^k x 的加权线性组合。每个 S^k x 作为一个特征,我们对它们进行加权线性组合。

我们可以使用框图来可视化此操作。图卷积迭代地应用移位、加权和求和。从输入 x 开始,我们将其与 h_0 相乘得到 h_0 x。然后,将 x 移位得到 Sx,与 h_1 相乘后加到之前的输出上。重复此过程,每次将信号再次移位,与相应的系数相乘并累加,最终得到卷积输出。


作为特例的时间卷积

有趣的是,我们定义的最一般形式的图卷积,实际上包含了重要的特例,如一维时间卷积。

时间卷积只是移位输入的线性组合。如果我们考虑一个简单的线图,其中节点 i 影响节点 i+1(即边 (i+1, i)),那么该图对应的邻接矩阵(假设为无权图)是一个次对角线为1的矩阵。用这个矩阵乘以输入向量,会产生一个移位后的向量。

因此,时间卷积是线图邻接矩阵上的一个多项式,可以使用我们之前定义的相同图卷积操作来表达。如果我们让移位算子成为任意图的移位算子,而不仅仅是线图,那么我们就回到了更一般的图卷积。


图傅里叶变换简介

最后,简要提一下图傅里叶变换。这是一个更大的主题,但它是分析图信息处理系统的重要工具。

给定图移位算子 S 的特征分解 S = U Λ U^T,图信号 x 的图傅里叶变换定义为将信号投影到移位算子的特征空间上:x̂ = U^T x

在傅里叶域中工作的好处是,类似于常规傅里叶域,图卷积在傅里叶域中变为逐点乘法。整个卷积算子变成了一个逐点算子。如果你对这个主题感兴趣,可以进一步查阅相关讲座。


总结

在本节课中,我们一起学习了图卷积滤波器的核心内容。我们从图的正式定义开始,介绍了顶点、边和权重的概念。然后,我们探讨了多种图移位算子,包括邻接矩阵、拉普拉斯矩阵及其归一化形式,并将它们统一为通用的图移位算子 S

接着,我们定义了图信号以及通过 S 进行信号扩散的过程。基于此,我们引入了图卷积滤波器,它被定义为移位算子 S 上的多项式,其输出是扩散序列的加权线性组合。我们还看到,经典的一维时间卷积是图卷积在特定图结构(线图)下的一个特例。

最后,我们简要提及了图傅里叶变换的概念,它将卷积运算转换到频域,简化了分析。图卷积滤波器是构建更复杂图神经网络模型的基础,理解其原理至关重要。

006:图卷积网络

在本节课中,我们将要学习图卷积网络。我们将从如何定义图上的卷积滤波器开始,逐步深入到如何学习这些滤波器,并将其堆叠成多层网络。我们还将探讨如何将单输入单输出的网络扩展到多输入多输出的情况,并理解图卷积网络相较于多层感知机的优势与特点。

图滤波器的学习

上一节我们介绍了如何用系数 H 定义图上的卷积滤波器。本节中我们来看看如何学习这些图滤波器,即学习这些滤波器的系数。

我们想要学习这些图滤波器的系数,并将这些图滤波器像多层感知机那样堆叠成层状网络。我们希望将多个线性变换与非线性激活函数组合在一起,形成多层结构。首先,让我们观察单个图滤波器,它相当于多层感知机中的单个线性层,并了解如何学习这些滤波器。

我们用 D 表示数据集,它包含图上的输入和输出信号,以及图表示本身。这个数据集由三元组 (X_i, Y_i, S_i) 组成。这个数据集可能包含来自不同图的元素。我们可以训练一个图滤波器,使其对不同的图表示具有鲁棒性,只要测试集中的图来自相同的分布。尽管这些图并不完全相同,我们仍然可以用这样的图神经网络处理它们,因为训练图和测试图的 S 都是已知的,并且图滤波器已经学会了在整个图分布上良好工作。这就是图神经网络的美妙之处:你可以在一个图集合上训练图神经网络,并将其应用于不同的图上。如果只是在顶点上定义多层感知机,这是不可能实现的。

现在,让我们定义图滤波器 F_H,它以输入信号 X 和图移位算子 S 为参数。用我们之前讲座中的符号表示,预测输出 Ŷ 就是这个图滤波器的输出:Ŷ = F_H(X, S)。这仅仅是图移位算子 SK 次幂多项式与 X 的乘积,系数为 H_k,这些系数现在是模型的参数,因此我们在函数 F 中将其标为下标。

在训练期间,我们像在经验风险最小化中通常做的那样,寻求最小化一个损失函数。我们试图找到最优系数 H*,以最小化这个目标函数。该目标函数简单地是整个数据集上某个损失函数的总和,该损失函数用于比较模型的预测信号 Ŷ 和真实的输出信号 Y。重要的是,这个优化仅针对滤波器系数 H。输入 X、输出 Y 以及以图移位算子形式提供的图表示 S,都是作为先验信息给出的。

在某些情况下,我们可能不关心推断与输入信号形状相同的输出信号。当输出 Y 的维度与输入信号不同,或者本身不是一个图信号时,我们只需向模型添加一个读出层。我们也会为稍后将看到的模型这样做。

我们使用 n 表示图中顶点或节点的数量,这意味着输入信号是一个 n 维向量。现在我们假设输出是一个 m 维向量,其中 m ≠ n。因此,我们可以取这个 n 维图滤波器的预测,并将其乘以一个 m × n 维的矩阵,即所谓的读出矩阵 R,以匹配维度。通常,R 不是学习得到的,而是算法或算法设计者的设计选择。

以下是读出层的典型示例:

  • 如果我们只想读取单个节点的值,那么这个读出层就对应一个单位向量,其中“1”位于我们想要读取的对应分量上。
  • 如果我们想对所有图信号元素进行求和或平均池化,那么我们可以取全1向量的转置,并将其与输出相乘,从而对所有元素求和。

读出层用于产生与输入图信号维度 n 不同的输出。例如,求和读出层对于处理分类问题很有用,其中输入是图和图信号,输出是一个特定的类别。

图卷积网络

以上是关于学习图滤波器的内容,你可以将其视为单层图网络。然而,还缺少一样东西,这让我们更接近图卷积网络,那就是图感知机。图感知机也是单层图网络,但它们将我们之前看到的图滤波器与一个非线性函数 σ 结合起来,这个非线性函数可以是逐点的 sigmoid、tanh 或 ReLU,就像我们在多层感知机中应用它们一样。

通过添加这样的非线性,我们向使模型更具表达能力迈进了一步。之前的模型只能学习线性映射。现在,在这个线性映射之上加上一个非线性函数,我们可以学习至少是非线性的、表达能力稍强的映射,从而捕捉更大的函数类别。它相当于神经网络的感知机。如果在这里使用 sigmoid,它基本上可以进行分类。

现在,为了定义图卷积网络,我们取这些图感知机,并像多层感知机那样,递归地将这些图感知机组装成层。我们这里有一个图感知机,其中 H 现在有一个额外的层索引 LX 也有一个层索引。我们取前一层的信号,将其与 SK 次幂相乘,然后乘以标量系数 H_{L, k},接着应用非线性激活函数,得到下一层的信号。然后我们递归地添加更多这样的层,最后可能还有一个额外的读出层。当然,我们假设 X_0 是输入信号,所以我们从 X 开始。这只是一个递归定义,即图感知机的递归应用。

作为具有 L 层的图卷积网络的简写符号,我们简单地写成预测是 F_{ℋ}(X, S),其中 现在包含所有层的系数向量。这等于这个递归的最后一个元素 X_L。我多次提到这一点,因为图移位算子 S 是作为先验信息提供给模型的,它编码了图结构。

这是一个三层模型的框图,展示了其工作原理:我们有第1、2、3层,输入信号 X,然后我们将该信号与此处的幂级数(系数为 H_{1, k})相乘,得到 C,接着将其通过逐点非线性函数(本例中为 sigmoid),得到 X_1。然后对 X_1 进行相同操作得到 X_2,再对 X_2 操作得到 X_3。你可以看到 S 在每一层总是相同的,我们使用相同的幂级数,但系数会变化。这里的红色系数是这个模型的可学习参数。这就是一个图卷积网络。更具体地说,它是一个处理信号的图卷积网络,其中每个节点有一个实数标量。

学习图卷积网络与学习图滤波器的工作原理相同。我们再次有数据集 D,其中该数据集的不同元素可以对应不同的图。然后我们有图卷积网络 F_{ℋ}(X, S)。在训练期间,我们寻求最小化某个损失函数,该函数衡量预测 F_{ℋ}(X, S) 与真实输出信号 Y 之间的差异。这个优化是针对所有滤波器系数 进行的。图移位算子 S 作为先验信息给出,数据集可能包含多个不同的图实例。经验表明,与多层感知机类似,更深的图卷积网络由于卷积和非线性的分层堆叠而效果更好。这与卷积神经网络完全相同,在卷积神经网络中,你也有卷积和非线性堆叠在一起。

与多层感知机的比较

如前所述,图卷积网络比卷积神经网络更灵活,但比多层感知机灵活性差,因为它们是多层感知机的一个特例。

如果我们考虑一个非常简单的多层感知机形式,它具有逐点非线性函数 σ(例如 sigmoid),并暂时忽略偏置,我们可以使用此表示递归地定义它。现在,如果我们将其与图卷积网络进行比较,我们看到以红色突出显示的可训练参数。我们看到这个模型更灵活,因为我们可以使用这个模型来表示右边的模型。所以图卷积网络是这种多层感知机的一个特例。我们也有一个线性变换,但在图卷积网络的情况下,我们有一个投影到 S^K 矩阵上,然后只有一个标量系数 H;而在多层感知机中,我们可以学习一个任意的矩阵 W,它可以等价于此处的幂级数,也可以不等价。

因此,图卷积网络是多层感知机的一个特例。换句话说,它们在训练集上会达到更高的训练损失。在这种情况下,我们为所有数据点使用相同的 S,即相同的图和图表示,因为在多层感知机中,我们无法在不同的图上执行,因为多层感知机根据定义不具备图卷积网络所具有的置换等变性。

所以,图卷积网络是多层感知机的一个特例。假设我们有一个足够好的训练算法,多层感知机看到的训练误差总是小于或等于图卷积网络的训练误差。乍一看这可能有点违反直觉,但这里重要的是,图卷积网络对图信号的泛化能力更好,因为它们能够通过使用图移位算子 S 来利用编码在图移位算子中的图信号的内部对称性,而多层感知机则不能。模型在训练集上的表现可能稍差,但对同一图上的新信号或新图的泛化能力却要好得多。

此外,图卷积网络是此比较中唯一可以处理不同图的模型。在训练期间和推理时,我们都可以将此模型应用于不同的图。我们可以对不同的图进行卷积,因为 S 向模型告知了图的结构。我们总是通过 S 进行投影,所以 X 的维度与 S 相关联,而不是与某些全局训练的参数矩阵 W 相关联。这就是图卷积的美妙之处。

这里有一个关于泛化特性的小图示。假设训练示例都与左边所示的示例信号有些相似。顺便说一下,信号强度在这里用蓝色的深浅表示。我们在顶点9处有强信号,在顶点10处有较弱信号,在顶点5和6处没有信号。现在,多层感知机会学会相当正确地预测看起来像中间信号的信号。但是,如果我只见过看起来像左边这样的训练示例,要泛化到右边这样的信号是不可能的。因此,如果在测试时将此作为多层感知机的输入,而训练时只观察到类似左边的示例,预测很可能非常糟糕。然而,图卷积网络在这种情况下会成功,因为它知道图的底层结构。它基本上通过了解图的结构沿着图执行卷积,因此可以推断出在节点6的输入附近有具有强激活的节点。所以这里的模式与这里的模式相同,只是发生在图中的不同节点上。这就是等变性,我们在卷积神经网络中看到的平移等变性在这里表现为图卷积的等变性。

多输入多输出图卷积网络

到目前为止,我们看到的图卷积网络在其输入特征数量上是有限的,特别是我们假设信号是一维的,即图中每个顶点的输入信号有一个标量。这既限制了输入,也限制了中间表示,因此我们想将其推广到允许多个特征,即允许向量,现在每个顶点都有一个特征向量。所以我们首先定义滤波器组。

滤波器组将一个图信号以矩阵形式输出。这里我们有一个图信号,这是图中每个节点的一个标量。而滤波器组输出每个节点的一个特征向量。例如,在这种情况下,在节点6处,我们在这个滤波器组的输出中有三个值。它是如何做到的呢?它基本上只是将三个不同的滤波器独立地应用于输入信号 X。我们取输入信号 X,通过第一个滤波器得到 C_1,通过第二个滤波器得到 C_2,通过第 F 个滤波器得到 C_F。这里的每个 C 都是一个一维图信号,我们为每个顶点指定了一个实数。但现在我们有 F 个这样的信号,因此我们实际上为每个顶点指定了一个特征向量。你可以将其想象成一本书,在书的每一页上,我们指定了一个一维信号,这样如果我们看整本书,每个节点就有一个向量。这就是一个滤波器组。

现在,如果我们假设每个节点有 F 个输入特征,并且我们有一个包含 G 个滤波器的滤波器组。那么我们使用每个滤波器 G 处理每个输入特征 F,以产生 F × G 个输出。这意味着这个多输入多输出图滤波器会生成每个节点具有 F × G 个特征的输出。因此,如果我们在多个层上应用这个,每个节点的特征数量会呈指数增长,这是不可取的。

我们可以这样看:每个节点有多个输入特征,然后在输出端,因为我们有多个滤波器,我们得到这个维度,然后我们有多个输入特征作为第二个维度,所以我们得到每个节点 F × G 个特征。在这个图示中,这里的每个 U 都是一个图信号,我们为每个节点指定了一个标量。所以在这种情况下,每个节点会有 F × G 个标量。

解决这个问题的方法很简单,就是对 F 求和。我们对这里的 F 求和,以获得恰好 G 个特征每个节点。然后我们得到一个操作,将每个节点的 F 个特征映射到 G 个特征。但这里是一个求和操作。

这看起来有点复杂,但我们可以利用线性代数的优势,用非常紧凑的矩阵符号来表示,这就是你会在许多论文中看到的多输入多输出图滤波器。这可以简单地写成一个矩阵 C,等于这个多项式求和:Σ_{k=0}^{K} S^k X H_k。其中,矩阵 C 的列对应 G 个特征值,行对应 n 个顶点。然后我们有这个 n × n 的图移位算子。接着,我们有一个 n × F 的输入信号,即对于图中的每个节点,我们有 F 个特征。然后我们有这个特征矩阵 H_k,它是 F × G 维的。总的来说,我们得到一个 n × G 维的矩阵。这与上述公式完全等价。

现在,为了定义一个多输入多输出图卷积网络,我们可以像之前为标准图卷积网络所做的那样,递归地组合这些 L 个多输入多输出滤波器层,在标准图卷积网络中,图的每个节点只有一维特征。我们得到这里的表达式,其中 X_0X,即第一层的输入。这里的 H_{L, k} 矩阵是我们想要优化的参数,它们指定了在每一层中,我们从多少个特征减少或增长到下一层的多少个特征。

我们也可以用一个更紧凑的表达式来写:矩阵预测 YF_{ℋ}(X, S),它是该网络最后一层或读出层的输出。其中,这个 只是出现在此计算中的所有这些矩阵的集合。所以,矩阵 H 有两个索引,一个是对层的索引,一个是对 k 的索引。图移位算子 S 再次只出现一次,并作为先验信息给出,在所有层中都是相同的。

这里我们比较了第一个更简单的图卷积网络和我们看到的多输入多输出图卷积网络。你可以看到操作几乎相同,只是我们用矩阵 H 替换了标量 h,并且输入和输出现在是矩阵而不是向量了。

总结

本节课中我们一起学习了图卷积网络的核心概念。我们从一个简单的图滤波器开始,学习了如何将其系数作为参数进行优化。通过引入非线性激活函数,我们得到了图感知机,这是构建更复杂网络的基础。通过递归堆叠图感知机,我们最终构建了图卷积网络,它能够处理图结构数据并利用其内在对称性进行有效的特征学习。

我们进一步将模型扩展到多输入多输出的情况,引入了滤波器组的概念,并使用矩阵形式简洁地表示了多特征图卷积操作。最后,我们比较了图卷积网络与多层感知机,认识到图卷积网络虽然在训练集上可能表现稍逊,但其通过利用图结构先验知识,在泛化到新图或同一图的新信号方面具有显著优势。

需要指出的是,我们在这节课中仅仅触及了图神经网络的表面。这是一个非常广阔的领域,有专门的完整课程。如果你想深入了解,我推荐阅读关于图神经网络的综述文章,这些文章回顾了过去10到20年该领域的工作。


核心概念公式/代码摘要

  • 单特征图滤波器Ŷ = F_H(X, S) = Σ_{k=0}^{K} h_k * S^k * X
  • 图感知机(单层)X_{l+1} = σ( Σ_{k=0}^{K} h_{l,k} * S^k * X_l )
  • 多特征图滤波器(矩阵形式)C = Σ_{k=0}^{K} S^k * X * H_k,其中 X ∈ R^{n×F}, H_k ∈ R^{F×G}, C ∈ R^{n×G}
  • 多特征图卷积网络(递归层)X_{l+1} = σ( Σ_{k=0}^{K} S^k * X_l * H_{l,k} )X_0 = X

007:潜在变量模型与自编码器

在本节课中,我们将要学习生成模型的一个重要分支——潜在变量模型。我们将从基本概念入手,逐步介绍主成分分析、深度自编码器,并最终触及生成式概率建模。这些模型能够从无标签数据中自动学习数据的压缩表示,是深度学习的核心组成部分。

潜在变量模型概述

上一节我们介绍了本课程的结构。本节中,我们来看看什么是潜在变量模型。

在之前的课程中,我们始终专注于监督学习。监督学习使用包含输入和期望输出的数据对 (x_i, y_i) 来训练模型,例如图像分类和回归任务。

相比之下,无监督学习仅使用没有标签的数据集 {x_i} 来训练模型。其目标是仅从原始数据中学习有用的表示,这非常有益,因为获取标签通常很困难。无监督学习技术包括聚类、降维和生成模型等。

一些无监督学习技术使用潜在变量来捕获数据结构。潜在变量模型允许在观测空间和潜在空间之间进行映射。

以下是潜在变量模型的核心定义:

  • 设观测空间 x ∈ R^D,例如一张 W×H 维的图像。
  • 设潜在空间 z ∈ R^Q,通常 Q << D,以实现压缩。
  • 模型通常包含两个映射函数:
    • 编码器 f_w: 将观测 x 映射到低维潜在表示 z
    • 解码器 g_w: 将潜在表示 z 映射回重建的观测
  • 目标是最小化原始输入 x 与重建输出 之间的差异。

每个训练数据点都关联一个潜在变量。潜在变量模型可以按以下方式分类:

  1. 线性 vs 非线性:映射函数是线性还是非线性。
  2. 确定性 vs 随机性:映射是确定性的还是概率性的。
  3. 仅解码器 vs 编码器-解码器:是否同时具备编码和解码功能。

以下是本课程涉及的重要技术分类:

  • 确定性模型
    • 线性:主成分分析。
    • 非线性:自编码器。
  • 概率性模型
    • 线性:概率主成分分析。
    • 非线性:变分自编码器。
    • 仅解码器:生成对抗网络。

自编码器

上一节我们定义了潜在变量模型。本节中我们来看看其中一种最常见的形式——自编码器。

自编码器是一种同时包含编码器 f_w 和解码器 g_w 的确定性潜在变量模型。它被称为“自”编码器,因为其目标是重建自己的输入。

以下是自编码器的工作流程:

  1. 输入向量 x 通过编码器神经网络 f_w,生成潜在编码 z
  2. 潜在编码 z 通过解码器神经网络 g_w,生成重建的输出
  3. 通过压缩和重建, 通常与 x 不完全相同。训练目标是最小化 x 之间的差异(如均方误差)。

以MNIST手写数字为例:输入一张数字图像,编码器将其压缩为一个低维向量,解码器再将该向量重建为一张与原始输入相似但不完全相同的图像。

生成模型

上一节我们介绍了确定性的自编码器。本节中我们来看看概率性的生成模型。

生成建模是机器学习中一个广泛的领域,旨在对数据点 x 的概率分布 P(x) 进行建模。其任务是捕获数据中的结构规律和依赖关系,例如图像像素间的关联。

我们主要关注生成式潜在变量模型。与自编码器不同,这类模型中的映射 fg 不是确定性的,而是对概率分布进行建模。它们允许从学习到的分布中采样新的数据点,因此被称为“生成”模型。

直观地说,生成模型试图为观测数据建立一个“理论”。例如,对于音频波形,潜在变量可能代表说话者的性别或所说的词语序列,用以解释观测到的信号。

一些生成模型(如标准化流)可以计算数据的精确似然 P(x)。但大多数模型,如变分自编码器,只能近似 P(x)。不过,它们都支持从模型分布中采样生成新的数据样本。

生成式潜在变量模型通常考虑一个简单的贝叶斯过程:

  1. 首先,从潜在空间的先验分布 P(z) 中采样一个潜在编码 z
  2. 然后,给定 z,从条件似然分布 P(x|z) 中采样一个数据点 x
    通过边缘化 z,我们可以得到数据的边际分布:P(x) = ∫ P(x|z)P(z) dz
    模型的目标是最大化训练数据在该分布下的似然。

我们可以使用图模型(板表示法)来直观表示这个过程。参数 w 是全局的,而每个数据点 x_i 都关联一个特定的潜在变量 z_i

潜在变量模型示例

上一节我们介绍了生成模型的理论框架。本节中我们通过一些具体示例来看看潜在变量模型能做什么。

线性示例(主成分分析)
假设有一个二维数据集(蓝点)。应用主成分分析(一种线性潜在变量模型)后,数据被线性投影到一条一维直线(流形)上,得到潜在表示。然后,再从这些潜在表示重建出数据点(橙点)。目标是找到最优的线性投影,使重建点尽可能接近原始点。

非线性示例(图像流形)
在更真实的场景中,如MNIST或人脸图像上应用非线性潜在变量模型(如自编码器),可以学习到有意义的潜在空间。在该空间中遍历时,重建的图像会平滑过渡。例如,在MNIST的潜在空间中,数字“7”可以逐渐变为“1”;在人脸潜在空间中,x轴可能对应面部朝向,y轴可能对应表情变化。所有这些结构都是从无标签数据中自动发现的。

3D形状生成
一项关于“占用网络”的研究将变分自编码器应用于3D形状表示。这使得模型能够生成训练集中未出现过的新形状,例如通过插值生成介于两种汽车或沙发之间的新物体。

文本句子插值
同样可以学习句子序列的潜在空间。在潜在空间中对两个句子进行插值,可以生成一系列语义和句法上连贯过渡的句子。例如,从“I want to talk to you”平滑地过渡到“I want to be with you”。这对于离散的文本数据而言是一项非常具有挑战性的任务。

总结

本节课中我们一起学习了潜在变量模型的核心概念。我们首先对比了监督学习与无监督学习,并定义了潜在变量模型的基本框架,即通过编码器和解码器在观测空间与低维潜在空间之间建立映射。接着,我们详细介绍了确定性的自编码器及其工作原理。然后,我们探讨了概率性的生成模型,特别是生成式潜在变量模型所遵循的贝叶斯过程。最后,通过从线性投影到图像、3D形状乃至文本生成的多个示例,我们看到了潜在变量模型在捕获数据内在结构和生成新样本方面的强大能力。这些模型为从无标签数据中学习丰富表示奠定了基础。

008:主成分分析 (PCA) 🧮

在本节课中,我们将要学习主成分分析。这是一种在科学领域,特别是高维数据分析中,最常用的统计工具之一。许多同学可能已经了解PCA,本节将是一个简要回顾。在本课程的语境下,PCA之所以重要,是因为它是一个确定性的线性潜变量模型,是数据分析和降维可视化最基本的方法之一。

符号与问题设定

首先,我们来明确符号。我们将数据集表示为 X,它是一个矩阵。矩阵的每一行是一个数据向量。因此,数据矩阵有 N 行(每个数据点一行)和 D 列(数据的维度)。所以 X 是一个 N × D 的矩阵。

类似地,我们引入一个矩阵 C 来表示潜变量。它的每一行存储一个潜码向量。这个矩阵也有 N 行,但只有 Q 列(Q 是一个较小的数,例如2或3)。X 是观测到的数据,而 C 是未观测到的潜变量,需要从 X 中推断出来。

通常,我们假设 Q 小于 D,因为我们希望模型能产生一个压缩的表示。

在PCA中,我们的目标是学习一个双向映射,即同时拥有一个编码器和一个解码器,因此它是一个自编码器模型。我们希望有一个从潜空间 C 到观测空间 X 的映射,以及一个从 X 回到 C 的映射。我们希望这个映射能尽可能多地保留高维空间 X 中的信息。

例如,对于图像数据,我们将图像展平为向量,得到一个700维的观测空间。我们希望将维度降低到2或3维。换句话说,我们希望将 X 编码成一个潜码 C,使得如果我们把 C 解码回 (其中 是重建结果),那么这个重建结果能很好地近似我们观测到的原始 X。在大多数情况下, 不等于 X,因为对于真实数据集,压缩总是一个有损过程。

线性模型与目标函数

现在,我们假设从潜空间到观测空间存在以下线性映射:

X̂ᵢ = x̄ + Σⱼ Cᵢⱼ Vⱼ

其中:

  • X̂ᵢ 是第 i 个数据点的预测(重建)向量。
  • 是整个数据集的均值向量。
  • Vⱼ 是第 j 个基向量。
  • Cᵢⱼ 是潜变量矩阵 C 中第 i 行、第 j 列的元素。
  • 求和从 j=1 到 Q。

我们假设偏移量是均值 ,基向量 V 构成一组标准正交基。均值是在没有任何潜变量信息时最好的预测起点。而选择标准正交基是为了能很好地张成空间,并且我们希望这个基的朝向能最大程度地解释数据集。

我们模型的目标是最小化 L2 重建损失,该损失是关于潜变量 C 和正交基 V 的函数:

L(C, V) = Σᵢ || X̂ᵢ - Xᵢ ||² = Σᵢ || (x̄ - Xᵢ) + Σⱼ Cᵢⱼ Vⱼ ||²

我们的目标是找到最优的 CV 来最小化这个损失。

优化潜变量 C

首先,我们优化潜变量 C。将损失函数展开,并利用 V 是标准正交基的性质(即 Vⱼᵀ Vⱼ = 1),我们可以得到损失函数的表达式。

为了最小化损失函数关于 C 的部分,我们对 Cᵢⱼ 求导并令其为零。这可以得到一个闭式解:

Cᵢⱼ = - Vⱼᵀ (x̄ - Xᵢ)*

这里,星号 () 表示最优值。将这个最优解代回损失函数,我们得到在 C = C 时的损失值。

优化基向量 V 与特征值问题

上一节我们找到了最优的潜变量 C*,本节我们来看看如何优化基向量 V

C* 代入损失函数后,损失可以重写为:

L(C*, V) = - Σⱼ Vⱼᵀ S Vⱼ + 常数项

其中 S 是所谓的散布矩阵,它是数据协方差矩阵的非归一化版本:

S = Σᵢ (x̄ - Xᵢ)(x̄ - Xᵢ)ᵀ

现在我们需要优化关于 V 的损失。为了避免平凡解(例如 V=0),我们需要加入约束条件:基向量必须是标准正交的,即 Vⱼᵀ Vⱼ = 1。我们使用拉格朗日乘子法将这个约束整合到优化问题中。

构造拉格朗日函数并关于 Vⱼ 求导,令其为零,我们得到一个熟悉的方程:

S Vⱼ = λⱼ Vⱼ

这正是特征值问题!我们发现,λV 是散布矩阵 S 的特征值和特征向量。

此外,我们观察到 Vⱼᵀ S Vⱼ = λⱼ。这意味着损失函数 L(C*, V) 可以通过选择 S 的前 Q 个最大特征值所对应的特征向量作为基向量 V 来最小化。这些特征向量解释了数据中最大的方差。

PCA 的编码器与解码器

通过上述推导,我们得到了PCA的线性模型。我们可以非常简洁地写出PCA的解码器和编码器。

解码器(从潜码 C 重建数据 X):
X̂ = x̄ + V C

编码器(从数据 X 得到潜码 C):
C = Vᵀ (X - x̄)

这里,矩阵 V 的列就是对应于前 Q 个最大特征值的特征向量。

以下是PCA的完整步骤:

  1. 给定数据集 X,计算数据均值 和散布矩阵 S
  2. 计算 S 的特征分解。
  3. 选取前 Q 个最大特征值对应的特征向量,组装成矩阵 V
  4. 使用上述线性映射进行编码和解码。

方差最大化视角

我们之前从最小化重建误差的角度推导了PCA。然而,PCA也可以从最大化潜变量方差的角度来理解,这为我们提供了另一种直观解释。

考虑一维编码情况:c = vᵀ (x - x̄)。我们的目标是最大化潜变量 c 的方差。可以证明,c 的方差正比于 vᵀ S v

因此,在标准正交基的约束下,最大化所有潜变量维度上的方差之和,同样会引导我们求解特征值问题 S v = λ v,并选择最大特征值对应的特征向量。Vⱼᵀ S Vⱼ 的值就是沿着第 j 个主成分方向的方差(如果 S 经过数据点数量归一化,就是协方差)。

实例与应用

让我们看一些例子来理解PCA的实际效果。

二维数据示例:我们有一个二维数据集,假设使用一维潜空间(Q=1)。PCA会找到数据散布最大的方向(第一主成分),并将所有数据点投影到这条线上。重建点则位于这条投影线上。如果使用二维潜空间(Q=2),则重建误差为零,因为没有进行压缩,但这失去了降维的意义。

MNIST 手写数字:在MNIST数据集上,使用不同数量的主成分进行重建。仅用2个基向量时,重建图像非常模糊。随着基向量数量增加(例如50个),重建图像细节更加丰富。底部展示了均值图像和前几个主成分(特征向量),可以看到算法自动从数据中学到了最主要的变换模式(如旋转、拉伸、平移)。

人脸图像 (Eigenfaces):PCA也可以应用于经过良好裁剪的人脸图像。仅用3个主成分就能获得不错的重建效果。更重要的是,我们可以在低维潜空间中对数据点进行分类,这通常更高效。例如,使用3个PCA成分进行人脸/非人脸分类可以达到79%的准确率。同时,特征向量(“特征脸”)被重新排列成图像后,可以直观看到前几个成分主要捕捉光照和低频变化,而后面的成分则包含更多高频细节。

总结

本节课中,我们一起学习了主成分分析。我们首先介绍了PCA作为一种线性自编码器模型,其目标是最小化L2重建误差。我们详细推导了如何通过求解散布矩阵的特征值问题,来获得最优的编码器和解码器。我们还探讨了PCA的另一种视角:最大化潜变量空间的方差。最后,我们通过二维数据、手写数字和人脸图像的实例,直观展示了PCA在降维、重建和特征提取方面的应用。PCA是理解数据结构和进行预处理的一个强大而基础的工具。

009:自动编码器

在本节课中,我们将要学习自动编码器。这是一种确定性的非线性潜变量模型,可以看作是主成分分析的非线性扩展。我们将探讨其基本原理、与PCA的关系,并通过实例观察其效果。

从PCA到自动编码器

上一节我们介绍了PCA,它是一种确定性的线性模型。本节中我们来看看自动编码器,它是一种确定性的非线性模型。

上图展示了自动编码器的结构。它与我们之前见过的模型图非常相似,区别在于编码器函数和解码器函数不再被假设为线性,而是由参数为 W 的神经网络表示。这个网络可以是卷积神经网络、多层感知机、循环网络或图神经网络等。

在通用自动编码器中,整个模块可以看作一个神经网络,其输出就是自身的输入。中间存在一个瓶颈层,旨在压缩信息,但与PCA不同,这种压缩是以非线性方式进行的。与PCA一样,其目标是最小化重构误差,最常用的重构误差是L2损失。

自动编码器与PCA的关系

自动编码器与PCA之间存在特定关系。PCA是自动编码器的一个特例。

如果我们假设从 XC 的编码器映射 F,以及从 C 的解码器映射 G,都是线性的,且没有任何激活函数。换句话说,我们有:

C = A * X + a

X̂ = B * C + b

在这种情况下,整个映射变为线性。将 C 代入解码器表达式,得到 X̂ = B * (A * X + a) + b,可以简化为另一个线性矩阵 C 的映射:X̂ = C * X + c。我们知道,如果最小化L2重构误差,该映射的最优解 W* 由PCA给出。因此,如果我们假设编码器和解码器都是没有激活函数的线性层,那么两者是等价的。

实验结果对比

接下来,我们通过一些实验结果来比较自动编码器的不同变体与PCA。以下是几种模型的对比:

  1. 简单PCA模型:在示例中,将二维空间映射到一维空间。
  2. 线性自动编码器:仅使用线性层执行相同操作,应得到与PCA相同的结果。
  3. 非线性自动编码器:通过非线性方式映射到一维空间。具体路径为:二维空间 -> 32维隐藏层 -> 一维潜空间 -> 32维解码器隐藏层 -> 重构输出。
  4. 二维潜空间非线性自动编码器:将潜空间维度从一维改为二维,观察其变化。

使用教育框架实现了这三种模型,并获得了以下可视化结果。

图中蓝色点表示二维玩具数据集,它是一个从0到π的余弦曲线。黄色点是重构点,连接蓝点和对应黄点的黄色线段表示重构误差。

以下是编码器和解码器均只有一个线性层、潜空间维度为一的自动编码器的训练过程:

  • 第1次迭代:模型随机初始化,重构是随机投影。
  • 第10次迭代:模型开始收敛,重构误差低于初始值。
  • 第50次迭代:重构效果继续改善。
  • 第1000次迭代:重构结果稳定。

将最终结果与使用一维潜空间的主成分分析结果对比,发现两者无法区分。这从经验上证明了具有线性编码器和解码器层的自动编码器与PCA是等价的。然而,重构误差仍然很大,因为这个二维空间中的流形是弯曲的,而非线性的。

接下来,我们实现并训练了之前提到的非线性自动编码器,其编码器和解码器均有32维隐藏层。

  • 初始化:随机初始化。
  • 第10次迭代:重构误差减小,但重构点仍接近一条线。
  • 第50次迭代:这条线开始分裂成多个线段,以更好地逼近流形形状。
  • 第300次迭代:重构误差已大幅减小。由于数据点本身带有噪声,即使使用一维潜空间,误差也无法降至零,但会变得非常小。
  • 第10000次迭代:非线性自动编码器已经捕捉到了该流形的近似形状。

出于好奇,我们使用相同模型但将潜空间维度改为二维进行实验。

  • 经过训练后,自动编码器能够精确地重构数据集。这是因为使用二维空间时,我们实际上不需要压缩信息。虽然在这种情况下,自动编码器仍需通过迭代算法(而非封闭解)来发现这个映射关系,但它最终能够实现完美重构。

在MNIST数据集上的应用

我们可以在MNIST数据集上做同样的实验。下图展示了一些重构结果。

  • 第一行:真实数据。
  • 第二行:深度自动编码器的重构结果。
  • 第三行:逻辑PCA模型的重构结果。
  • 第四行:标准PCA的重构结果。

可以看到,在相同的潜空间维度下,这种非线性自动编码器能产生更清晰、更锐利的重构图像。相比之下,PCA总是学习最佳的线性嵌入,但线性嵌入不足以很好地捕捉数据集的复杂结构。

去噪自动编码器

最后,介绍一种称为去噪自动编码器的变体。

去噪自动编码器接收带噪声的输入,并试图预测原始的无噪声数据。例如,我们有一个像MNIST这样的无噪声数据集,然后为其添加一些高斯噪声。去噪自动编码器的思想是,通过添加这种噪声,模型能够学习到更稳定、更高层次的表示,这些表示对这些输入损坏具有鲁棒性。这鼓励模型更好地泛化,捕捉数据中更有用的结构和统计特征,并且不会像普通自动编码器那样容易过拟合。其思想与数据增强非常相似,本质上就是对输入进行数据增强,只不过这里我们处理的是无监督学习问题,其标签就是无噪声的输入本身。结果显示,这些数字被很好地去噪了。

总结

本节课中我们一起学习了自动编码器。我们了解到自动编码器是PCA的非线性推广,通过神经网络实现编码和解码。当编码器和解码器为线性时,自动编码器等价于PCA。通过实验,我们观察到非线性自动编码器能够更好地捕捉复杂、弯曲的数据流形,并在MNIST数据集上产生比PCA更清晰的重构结果。此外,我们还介绍了去噪自动编码器,它通过向输入添加噪声来学习更鲁棒的特征表示,其思想类似于无监督学习中的数据增强。

010:变分自编码器 (VAE) 🧠

在本节课中,我们将要学习第一个概率性隐变量模型——变分自编码器。这是当今最流行的生成模型之一。之前我们讨论了确定性模型,现在我们将从概率视角来看待具有自编码特性的隐变量模型。

概述

首先,我们回顾一下数据的贝叶斯模型。这是本讲介绍中的公式,它描述了一个生成过程:对隐变量 z 有一个先验分布 P(z),以及给定 z 时数据 x 的似然 P(x|z)。对 z 积分,就得到了数据 x 的边缘似然 P(x),这正是我们想要最大化的目标。

当然,由于我们有 P(z) 并对 z 积分,这个表达式就是条件似然 P(x|z) 关于从先验分布中抽取的变量 z 的期望。

为了构建这个模型,我们需要做一些假设,使其易于处理。

模型假设

以下是使模型可行的关键假设:

  • 我们首先假设先验模型 P(z) 是可采样且可计算的。
  • 我们还假设似然模型 P(x|z) 也是可计算的。

这意味着我们可以从 P(z) 中采样,并且对于任何给定的 xz,我们都可以计算 P(z)P(x|z) 的概率质量或密度。

这些假设对于自回归模型(如语言模型)成立,但对于许多其他模型(如带环的图模型)则不成立,后者需要使用信念传播等近似方法。在变分自编码器的背景下,我们将考虑非常简单的参数化分布(如高斯分布),并通过神经网络进行参数化,以实现易于采样的特性。

目标函数与挑战

为了找到最佳的模型参数 W,一个合理的方法是最小化负对数似然(或交叉熵)。我们希望找到 W* 作为在整个数据分布 x 上的期望的最小化者,或者用我们的数据集(来自数据分布的样本)来近似这个期望。

然而,不幸的是,即使有了我们的假设,计算 P_W(x) 仍然是难以处理的。计算这个期望之所以困难,是因为搜索空间太大。我们是在一个可能高达数十维的隐空间上操作,虽然它比观测空间小,但仍然很大。为了准确估计这个量,我们需要抽取大量样本,这对于单次梯度步长来说计算量过大。

因此,直接优化是难以处理的。

变分自编码器的核心思想

VAE 的核心思想是通过引入另一个模型组件来规避这个问题,这个组件称为识别模型(或编码器),同样由参数 W 参数化,记作 Q_W(z|x)。给定一个数据点 x,我们试图用一个概率模型来编码它,预测出给定数据点时 z 的分布。

在自编码器的语境中,P(x|z) 是解码器,而 Q(z|x) 是编码器。我们尝试训练这个近似模型,使其逼近真实但未知的后验 P_W(z|x)。这个识别模型不需要完全精确,只要它能比一个非常宽泛的先验分布提供更好的指引,它就是有用的。联合训练这个模型会得到一个收敛更快且变得可处理的自编码器。

证据下界 (ELBO)

我们使用识别模型 Q 来寻找似然的一个易于处理的下界,这个下界被称为证据下界,因为它是对数似然(有时被称为证据)的一个下界。

通过对数似然的推导,我们可以得到负对数似然的上界。这个上界表达式包含一个 KL 散度项(衡量识别模型 Q 与先验 P(z) 的差异)和一个重构项(衡量给定 z 时重构 x 的好坏)。

注意,Q(z|x)P(x|z) 在这里扮演了自编码器的角色:Q 作为编码器将 x 映射到 zP 作为解码器将 z 映射回 x。这就是它被称为变分自编码器的原因——从这些方程中自然涌现出了自编码结构。

VAE 的实践设定

在 VAE 中,识别模型 Q(z|x) 通常被选择为一个由神经网络参数化的多元高斯分布。这使得 KL 散度项的计算变得非常简单。同样,似然 P(x|z) 也由神经网络参数化:对于二值观测,通常选择伯努利分布;对于实值观测,则选择高斯分布或拉普拉斯分布。

具体来说,对于识别模型,我们有一个神经网络,它接收输入 x,经过若干层(如 MLP)后,输出高斯分布的均值向量 μ 和对角协方差矩阵的对角元素 σ²。先验 P(z) 通常简单地选择为标准高斯分布(均值为0,协方差矩阵为单位矩阵)。

在这种高斯假设下,KL 散度项有一个简单的解析解,易于微分和优化。

重参数化技巧

然而,对于重构项,虽然前向传播可以通过采样轻松计算,但反向传播需要对采样操作进行微分,而采样操作本身依赖于需要优化的参数。直接处理会得到高方差的梯度估计器,效果不佳。

Kingma 和 Welling 在他们的 VAE 论文中提出了重参数化技巧来解决这个问题。该技巧将采样操作从计算图中移出,放到输入层。对于高斯分布,我们可以先从标准高斯分布中采样一个噪声变量 ε,然后通过一个确定性的变换 z = μ + σ ⊙ ε 得到 z。这样,μσxW 的确定性函数(通过神经网络),而随机性仅来自输入噪声 ε。这使得我们可以通过这个确定性路径进行反向传播,从而获得低方差的梯度。

在实践中,对于每个数据点 x,通常一个 ε 样本就足够了,特别是在使用合理大小的迷你批次(如64或128)时。

VAE 的表达能力

VAE 的表达能力非常强。它本质上实现了一个从简单隐分布(如标准高斯)到复杂数据分布的映射。通过神经网络的非线性变换,我们可以将简单的隐分布转换成输出空间中非常复杂的分布。例如,通过精心设计的非线性映射,可以将高斯分布的点映射成一个环状分布。

应用与结果

VAE 已被广泛应用于众多领域:

  • 图像生成:可以学习人脸、手写数字等数据的内在流形,并能从隐空间随机采样生成新样本。
  • 序列建模:与循环神经网络结合,用于序列数据的生成。
  • 解耦表示学习:通过特殊的训练方案,可以在弱监督下解耦潜在因子,如姿态、光照、纹理等。
  • 运动预测:从静态图像预测可能的多模态未来运动。
  • 3D形状学习:学习3D物体的隐空间表示。
  • 环境嵌入:在自动驾驶等领域,通过观察环境学习其重建表示,所得的隐编码可作为解决驾驶任务的有用线索。

总结

本节课我们一起学习了变分自编码器。我们从概率视角出发,理解了直接优化数据似然面临的挑战,并引入了识别模型来构造易于优化的证据下界。我们详细探讨了 VAE 的模型假设、目标函数(包含 KL 正则项和重构项),以及使训练可行的关键技术——重参数化技巧。最后,我们看到了 VAE 强大的表达能力及其在图像生成、表示学习等多个领域的成功应用。VAE 作为一种重要的深度生成模型,为我们提供了连接概率图模型与深度学习的优雅框架。

011:生成对抗网络(GANs)📊

在本节课中,我们将要学习生成对抗网络(GANs)。这是一种非常流行的生成模型,它使用了一种全新的、对抗性的训练范式来学习数据分布。与上节课介绍的变分自编码器(VAE)不同,GANs没有编码器,并且不直接对似然函数进行建模。

上一节我们介绍了变分自编码器,本节中我们来看看另一种强大的生成模型——生成对抗网络。

生成模型回顾

在开始之前,让我们简要回顾一下第11讲中介绍的隐变量模型。隐变量模型在观测空间 x 和维度通常低得多的隐空间 C 之间建立映射。每个训练数据点都与一个隐变量相关联,因此 C 被称为隐变量。模型可以是线性或非线性、确定性的或随机的,并且可能包含也可能不包含编码器 F

以下是部分已提出的隐变量模型分类:

  • 主成分分析 (PCA):线性,确定性,无编码器。
  • 自编码器 (AE):非线性,确定性,有编码器。
  • 变分自编码器 (VAE):非线性,随机性,有编码器。
  • 生成对抗网络 (GAN):非线性,随机性,无编码器。

生成模型通常指任何从数据分布 P_data 中学习一个模型分布 P_model 的模型。其目标可能是评估特定数据点的似然、进行推断,或者从该分布中采样。

  • 显式密度模型:显式地表示或建模密度。
    • 可处理的密度:例如 PixelRNN。
    • 近似密度:例如变分自编码器(VAE),使用变分近似。
  • 隐式密度模型:仅能从 P_model 中生成样本,无法评估密度。生成对抗网络(GANs) 是这类模型中最著名的代表。

GANs 基本原理

GANs 放弃了显式建模密度或似然。相反,它们使用一个对抗过程,其中两个模型(有时也称为“玩家”)同时被训练以竞争一个目标,因此这通常也被称为一个“两人博弈”。具体来说,这两个玩家是生成器 G判别器 D

  • 生成器 G:捕获数据分布。它负责学习一个模型分布 P_model,以近似数据分布 P_data,从而能够生成看起来像来自数据分布的新数据点。
  • 判别器 D:估计一个样本是来自真实数据分布还是生成器。它输出一个概率:如果认为输入样本来自真实数据分布,则输出接近 1;如果认为来自生成器(假样本),则输出接近 0。

生成器 G 的目标是最大化判别器 D 犯错的概率,即“欺骗”判别器。判别器 D 的目标是更好地区分真实与伪造的样本。通过让这两个玩家进行博弈,我们可以得到一个能近似数据分布的生成器。

GANs 的一个优点是训练只需要反向传播,而许多其他生成模型需要更复杂的算法。它不需要近似推断,也不需要通常存在混合问题的缓慢马尔可夫链。虽然有一些关于最优性和收敛性的理论结果,但它们需要很强的假设。

GANs 的形式化定义

让我们更正式地定义 GAN。沿用我们的常用符号,令 x ∈ R^D 表示一个观测值(例如一张图像),其中 D 是维度。令 P(C) 是隐变量 C ∈ R^Q 上的先验分布,其中 Q 是隐空间的维度。

我们有一个参数为 W_G 的生成器网络 G,它将隐代码映射到数据空间,即 G: C → x。由隐空间先验 P(C) 和确定性函数 G 共同诱导出的分布,我们称之为模型分布 P_model

令参数为 W_D 的判别器网络 D 从观测空间映射到一个概率值,即 D: x → [0, 1]。判别器网络的最后一层通常使用 Sigmoid 激活函数将输出映射到 [0, 1] 区间。

判别器 D 和生成器 G 进行以下两人极小极大博弈,价值函数为 V(G, D)

min_G max_D V(D, G) = E_{x∼P_data}[log D(x)] + E_{c∼P(C)}[log(1 - D(G(c)))]

这里,E 表示期望值。

  • 判别器 D 的目标 (max_D):对于来自 P_data 的真实样本 x,希望 D(x) 接近 1(最大化 log D(x));对于生成器生成的假样本 G(c),希望 D(G(c)) 接近 0(最大化 log(1 - D(G(c))))。
  • 生成器 G 的目标 (min_G):希望生成的样本 G(c) 能欺骗判别器,使其输出接近 1(即最小化 log(1 - D(G(c))),因为当 D(G(c)) 接近 1 时,此项值很小)。

训练算法与梯度技巧

在实践中,我们使用迭代数值优化算法。在内部循环中将 D 优化到收敛在计算上是不可行的,并且会导致在有限数据集上过拟合。因此,通常的做法是交替优化这两个目标:先进行 K 步(例如 1 到 5 步)优化判别器 D,然后进行一步优化生成器 G,并使用足够小的学习率。这有助于平衡判别器和生成器的重要性,对于模型收敛至关重要。

以下是基本的 GAN 训练算法:

while 未收敛:
    for k 步:
        从数据分布 P_data 中采样一个批次(大小 B)的真实样本 {x}
        从隐变量先验 P(C) 中采样一个批次(大小 B)的噪声样本 {c}
        通过**上升**其随机梯度来更新判别器 D:
            ∇_{W_D} (1/B) Σ [log D(x) + log(1 - D(G(c)))]
    从隐变量先验 P(C) 中采样一个批次(大小 B)的噪声样本 {c}
    通过**下降**其随机梯度来更新生成器 G:
        ∇_{W_G} (1/B) Σ [log(1 - D(G(c)))]

在训练早期,生成器性能很差,判别器可以轻易地以高置信度将生成样本判别为假(D(G(c)) 接近 0)。这会导致 log(1 - D(G(c))) 的梯度饱和,变得非常小,从而生成器 G 难以获得有效的梯度进行更新。

为了解决这个问题,可以使用一个梯度技巧:不训练 G 去最小化 log(1 - D(G(c))),而是改为训练 G最大化 log(D(G(c)))。这会导致相同的纳什均衡点,但在训练早期能提供更强的梯度。

理论结果简述

原始 GAN 论文提出了一些理论结果:

  1. 最优判别器 (Proposition 1):对于任意固定的生成器 G,最优判别器 D*_G 为:
    D*_G(x) = P_data(x) / (P_data(x) + P_model(x))
  2. 全局最优性 (Theorem 1):当且仅当 P_model = P_data 时,虚拟训练准则 C(G) = V(G, D*_G) 达到全局最小值 -log 4,且此时 D*_G(x) = 1/2
  3. 收敛性 (Proposition 2):如果 GD 有足够的容量,并且在每次更新中判别器都能达到其最优值,同时生成器(或模型分布)的更新能改进虚拟训练准则,那么 P_model 会收敛到 P_data

这些理论结果基于很强的假设(如无限模型容量、判别器每次都能达到最优),在实践中难以完全满足。神经网络参数化的 G 会引入临界点,交替优化只能保持 D 接近其最优解,因此实践中 GANs 可能不收敛甚至振荡。然而,经验表明,通过精心平衡 GD 的训练,GANs 通常能产生非常好的结果。

模式崩溃问题

GANs 的一个主要失败模式是模式崩溃。它源于极小极大博弈使得训练比简单地最小化损失函数更困难。在模式崩溃中,生成器学会了产生高质量但多样性很低的样本,只覆盖数据分布的一部分模式。直观上,这可能是因为生成器发现只专注于欺骗当前判别器的一种“有效”模式更容易,而忽略了其他模式,导致生成样本缺乏多样性。

以下是几种应对模式崩溃的策略:

  • 鼓励多样性:例如,让判别器考察整个小批量样本的统计特性,而不是单个样本。
  • 预见性对抗:例如,通过“展开”判别器未来几步的更新,让生成器在更新时能预见到判别器的应对策略。
  • 经验回放:不时地向判别器展示旧的生成样本,减少其在模式间跳跃。
  • 训练多个 GAN:希望多个生成器能覆盖更多模式(计算成本高)。
  • 使用改进的目标函数和正则化:例如 WGAN(Wasserstein GAN)及其梯度惩罚(Gradient Penalty)变体,能带来更好的训练稳定性。

优势与劣势

最后,我们来总结一下 GANs 的优势与劣势。

优势

  • 能够建模非常广泛和复杂的函数与分布,非常灵活。
  • 训练仅需反向传播,无需采样或近似似然(如 MCMC 或 VAE 中的变分推断)。
  • 生成的样本质量通常非常高,视觉上更逼真。

劣势

  • 没有模型分布或似然的显式表示,无法评估样本的似然。
  • 训练框架更复杂, notoriously hard to train。需要仔细平衡生成器和判别器的训练,以确保收敛并避免模式崩溃等问题。
  • 调整超参数和正则化项通常需要大量经验。

本节课中我们一起学习了生成对抗网络(GANs)的基本原理、形式化定义、训练算法以及相关的理论概念和挑战。GANs 通过一个巧妙的对抗博弈框架,绕过了直接对复杂数据分布建模的困难,成为当前最强大的生成模型之一。尽管训练存在挑战,但其在图像生成等领域产生的惊人效果,推动了深度学习生成模型的飞速发展。

012:生成对抗网络(GAN)的发展 🚀

在本节课中,我们将学习生成对抗网络(GAN)自诞生以来的关键发展历程。我们将从最初的架构探索开始,逐步了解如何通过改进训练稳定性和网络设计,最终生成以假乱真的高质量图像。


概述

生成对抗网络(GAN)自其开创性论文发表以来,在领域内引发了一场真正的革命,催生了大量关于其应用和改进的后续研究。在本单元中,我们将简要概述该领域的一些近期发展。这堪称人工智能领域的“摩尔定律”。下图展示了四年半时间里,GAN在人脸图像生成质量上的惊人进步。

你可以看到原始GAN论文中的结果,并将其与今天获得的结果进行比较。如今的结果令人印象深刻,社区中的许多人都对能够生成如此高保真度和丰富细节的图像感到震惊。


DCGAN:迈向高质量图像生成的第一步

上一节我们提到了GAN的诞生,本节中我们来看看第一个重要的改进——DCGAN。它本质上是对可能架构的搜索空间进行系统性探索,以找到在“最小最大”双玩家博弈框架下表现良好的结构。

以下是DCGAN发现的一组基本设计准则,这些准则能产生高质量的图像模型:

  • 卷积替代池化:在判别器中使用步幅卷积(stride convolutions)替代池化层,在生成器中使用分数步幅卷积(fractional stride convolutions)进行上采样。
  • 批量归一化:在生成器和判别器中都使用批量归一化(batch normalization)。
  • 移除全连接层:对于更深的架构,移除了全连接隐藏层。
  • 激活函数选择:在生成器中,除输出层使用Tanh外,其余层使用ReLU激活函数;在判别器的所有层中使用Leaky ReLU激活函数。

这是一项非常彻底和系统的实证研究,旨在找出哪些架构表现良好。由于GAN训练 notoriously 困难,正确设置这些超参数至关重要。他们展示了在更具挑战性的数据集(如卧室场景)上生成的样本,这些样本比原始GAN论文中的结果看起来真实得多。卧室场景对于在图像域操作的2D生成模型来说非常复杂,因为其中物体的布局、视角和外观存在巨大差异。

此外,他们还展示了随机潜在代码插值(random latent code interpolations)的示例。例如,在底部的序列中,一个看起来像电视的物体逐渐变形为窗户。他们还在论文中展示了可以进行简单的向量算术运算。具体做法是:取带有眼镜的男性样本的平均潜在向量,减去不带眼镜的男性样本的平均向量,再加上不带眼镜的女性样本的平均向量,然后对结果向量添加一点高斯噪声。最终他们得到了类似的结果:可以“拿”一个戴眼镜的男人,“去掉”眼镜,然后“加上”一个女人,最终得到一个戴眼镜的女人。这表明模型学习到了有语义意义的信息。


GAN的评估指标:FID

随着GAN模型的爆炸式增长,如何评估生成模型变得尤为重要。由于许多高性能的生成图像模型无法进行精确的似然计算,因此需要一个标准化的评估指标。目前一个常用(尽管不完美)的指标是Fréchet Inception Distance

FID是一种用于评估生成对抗网络(GAN)生成图像质量的指标。它通过比较生成图像与真实图像在预训练的Inception V3网络深层特征上的分布差异来实现。FID度量的是两个多维高斯分布之间的Fréchet距离。

公式
假设模型特征分布为高斯分布 ( N(\mu_m, \Sigma_m) ),真实数据特征分布为高斯分布 ( N(\mu_r, \Sigma_r) ),则FID距离为:
FID = ||μ_m - μ_r||^2 + Tr(Σ_m + Σ_r - 2(Σ_m Σ_r)^{1/2})

其核心思想是,在预训练的Inception V3网络的深层,特征被认为具有更丰富的语义信息。比较这些特征的分布,比比较网络浅层的局部、低级特征更有意义。在提出该指标的原始论文中,作者证明了它的有效性:例如,向图像添加噪声、模糊图像或混入其他分布的图像,都会导致FID值上升。

然而,FID是一个衡量图像保真度和与数据分布相似性的指标,它无法测量或防止模式崩溃,也不能很好地判断模型生成的图像是否很好地覆盖了整个数据分布。


提升训练稳定性:WGAN与梯度惩罚

GAN领域的一个重大进展是Wasserstein GAN。在原始GAN中观察到,高维空间中的低维流形通常重叠很少,如下图所示的两条线。当两个分布的支持集没有重叠时,原始GAN的判别器会饱和,导致梯度消失。

WGAN使用Earth Mover‘s距离(又称Wasserstein距离)替代Jensen-Shannon散度,这种距离度量能够处理支持集不重叠的情况,并提供有意义的梯度,从而有助于稳定GAN的训练。

另一项使GAN训练更可行、更稳定且减少超参数调整需求的主要进展是引入了正则化器,例如梯度惩罚。例如,在一篇论文中引入了一个非常简单的梯度惩罚正则项,即对判别器关于输入x的梯度进行惩罚。理论上可以证明,这能局部地促进收敛。下图对比显示,标准GAN的参数在振荡而不收敛,而使用梯度惩罚的两个版本则随时间推移收敛到均衡点。

使用此类梯度惩罚的DCGAN已被证明可以产生高质量样本,其质量远高于原始DCGAN论文中的结果,且无需依赖许多为经验性稳定训练而引入的技巧。仅使用一个非常朴素的DCGAN网络并加上梯度惩罚,就能获得如下图所示的卧室或教堂样本。

值得注意的是,在某些教堂样本中,你甚至可以看到网络建模了一个小小的“Shutterstock”水印,因为许多训练图像中都有这个水印,因此它被模型记住并作为数据分布的一部分生成了出来。


图像到图像的翻译:CycleGAN

GAN不仅可以用于从潜在代码生成图像,还可以用于在两个域之间进行翻译,即将一个域(例如X)中的图像转换到另一个域(例如Y)中的图像。在一篇名为《使用循环一致对抗网络进行非配对图像到图像翻译》的论文中,就应用了这种跨域翻译的思想。

该模型联合训练两个域之间的前向和后向映射(此时域本身成为了潜在变量),使用了不同的损失函数:一个循环一致性损失,用于衡量一个样本从一个域映射到另一个域再映射回原始域后,与原始样本的对齐程度;以及GAN中经典的判别器损失。

利用这类模型,我们可以将一张输入照片转换成特定风格的画作,只需在未配对、未对齐的真实世界照片和特定画家风格的画作集上进行训练即可。然后,我们还可以将生成的画作映射回原始图像域,并使用重建损失来使两者尽可能一致。当然,这其中存在一些模糊性,例如下图中的红花被转换成了黄花。

这种模型的应用非常有趣,可以实现马和斑马之间的转换、冬季和夏季场景的转换、航拍照片和地图的转换、建筑立面图与其实语义标签的转换,以及线条画和实物照片之间的转换等。你甚至可以在一些网络演示中交互式地体验它。


迈向高分辨率:渐进式增长GAN

为了进一步提升GAN的性能,渐进式增长GAN提出了一种新思路:从低分辨率(如4x4像素)开始训练GAN,然后在训练过程中逐步为生成器和判别器添加更多层,以达到越来越高的分辨率。

通过这种渐进式增长的过程,他们能够生成非常逼真的图像,分辨率高达1024x1024像素(1兆像素),这在之前是无法实现的。这也是当今最先进的GAN模型所使用的基本方法之一。


挑战复杂数据集:大规模条件GAN

另一个研究小组分析了GAN如何扩展到更复杂的数据集,特别是ImageNet。由于其巨大的多样性,ImageNet很难建模。他们尝试将类别条件GAN(其中类别标签也作为GAN的输入)扩展到ImageNet,并在512x512像素的较大分辨率下进行了实验。

他们证明,即使没有渐进式增长,只要拥有非常大的模型、非常大的小批量大小,以及随之而来的大型计算集群(这是在谷歌规模上完成的),这也是可能的。他们引入了一些使这项工作成功的重要技巧,例如生成器的正交正则化和潜在空间裁剪。他们探索了不同正则化策略的变体,并特别分析了通过这些正则化策略实现的稳定性与性能之间的权衡。他们发现了一个权衡点:加入这些正则化器可以获得更稳定、更收敛的行为,但另一方面,会损害FID指标衡量的性能。

因此,他们提出监控生成器和判别器权重矩阵的(奇异值),以确定模式崩溃何时发生(如下图所示),并在这些位置停止训练。他们发现,与判别器的正则化策略相比,这种早停策略能带来更好的FID分数。


当前最先进技术:StyleGAN

StyleGANStyleGAN2被认为是当今最先进的GAN模型。我强烈建议你观看相关的演示视频,效果令人印象深刻。

StyleGAN在训练中使用了正则化策略,但采用了一种新颖的网络架构。与传统架构中潜在代码直接输入生成模型不同,在StyleGAN中,潜在代码通过全连接层映射成不同层次的风格代码,这些风格代码被输入到生成器架构的不同层中。生成器从一个常量代码开始,此外还有一个额外的噪声向量也被注入到不同层中。这种设计允许在架构的不同分辨率上控制细粒度的风格

通过这种方式,他们合成的图像比以往任何生成模型合成的图像都更加逼真。通过改变刚刚提到的输入噪声(即采样不同的输入噪声),他们可以获得非常复杂的随机变化,例如在下图放大的绿色方框中,每根发丝的变化都各不相同,而每一种变化本身又是连贯且自然的。

这个模型的一些结果可以在“This Person Does Not Exist”网站上看到。每次刷新该网站,你都会从该模型得到一张新的图像。许多人正是因为这个StyleGAN2模型生成图像的质量而感到兴奋。它生成的图像非常逼真,人类很难将其与真实图像区分开来,除非你非常熟悉这类模型,否则很难看出它的缺陷。它可以生成不同类型的人类,包括男性和女性,不同的发色,有胡子或没胡子,卷发或直发,戴眼镜或不戴眼镜等。


总结

本节课中,我们一起学习了生成对抗网络(GAN)的关键发展脉络。我们从奠定基础的DCGAN开始,了解了如何通过架构改进生成更真实的图像。接着,我们探讨了评估生成模型质量的FID指标,以及解决训练不稳定问题的WGAN和梯度惩罚技术。然后,我们看到了GAN在图像翻译(CycleGAN)和高分辨率生成(渐进式增长GAN)方面的强大能力。最后,我们介绍了当前最先进的StyleGAN系列模型,它们通过创新的架构设计,能够生成几乎无法与真实照片区分的高质量人脸图像。GAN的发展历程展示了通过持续的研究和创新,生成模型的性能如何实现了质的飞跃。

013:AVG的研究成果

在本节课中,我们将学习蒂宾根大学自主视觉研究小组如何将深度学习应用于日常研究,涵盖3D重建、生成模型、自动驾驶等多个前沿领域的具体研究成果。

概述

上一节我们介绍了深度学习的基础知识。本节中,我们将通过自主视觉研究小组的实际研究案例,展示深度学习在复杂3D环境理解与交互中的应用。这些案例包括从单张图像进行3D重建、生成新形状、处理动态4D数据,以及构建自动驾驶系统。

3D重建与隐式表示

为了理解复杂的3D环境,一个核心任务是进行3D重建,即从一张或多张图像中预测物体的三维形状。传统方法使用体素、点云或网格等显式表示,但这些方法在细节表现上存在局限。

我们的关键思路是采用隐式表示。在CVPR 2019发表的论文《Occupancy Networks》中,我们提出将3D表面视为一个神经分类器的决策边界,而非显式地表示形状。

具体而言,我们训练一个神经网络分类器。该网络的输入是一个3D坐标点 x 和一个条件(例如一张图像),输出是该点位于物体内部的占用概率 p。决策边界(例如概率为0.5的等值面)就定义了物体的表面形状。

以下是该模型的核心公式描述:

f_θ(x, c) → p,其中 p ∈ [0, 1]

利用这个模型,我们可以完成多种任务。

以下是该模型的一些应用示例:

  • 单图像3D重建:仅输入一张2D图像(左),模型能重建出细节丰富的3D形状(右),其效果优于基线方法,并接近真实情况。
  • 从噪声点云重建:即使输入是稀疏且带有噪声的点云(左),模型也能学习并重建出正确、平滑的水密表面(右)。
  • 无条件生成模型:该隐式表示也可用于变分自编码器,在潜在空间中进行插值,生成训练集中未出现过的新样本。

扩展到4D与外观建模

上述隐式模型可以进一步扩展。

首先,我们将其扩展到4D(3D空间+时间)。在ICCV 2019的工作中,我们提出了一种方法:模型在特定时间点表示形状的占用情况,同时学习一个4D速度场。通过求解一个常微分方程(ODE)来获得物体的运动轨迹,并可以通过ODE反向传播来更新模型参数,从而学习4D重建。

其次,我们可以用这些技术对物体表面的光场进行建模。这本质上是建模物体表面每个点的外观,该外观取决于观察视角和光照位置。给定一张输入图像和一个3D几何模型(如CAD模型),我们可以训练神经网络来推断一个模型,之后可以任意改变光照和视角进行渲染。模型能够准确捕捉阴影变化、处理多个光源(环境光照)并生成物体表面的镜面反射效果。

大规模场景与无监督学习

最初的隐式模型难以扩展到大型场景。为此,我们开发了将隐式模型与3D卷积思想相结合的方法,使其能够处理房间级别的大规模场景重建,例如在Matterport数据集上的表现。

更进一步的挑战是无监督学习。我们探索了是否能够仅从无结构的图像集合中学习这些隐式模型(这里特指一种称为辐射场的隐式模型),其中每个场景只有一张图像,且我们不知道相机的参数和姿态。我们提出的模型证明这是可能的。仅从一组RGB图像出发,我们可以恢复出一个解耦的表征,之后可以分别操纵相机视角、物体形状或外观,并生成新的RGB渲染图、深度图或3D几何。

语义解耦与生成模型

3D建模的另一个方面是从无监督的3D点云中恢复语义信息。给定一组物体的3D点云,我们的目标是恢复出有语义意义的部件,这些部件组合起来能构成合理的形状。我们使用了一种基于流的生成模型(一种具有显式似然表示的深度生成模型)来实现这一目标。该模型在人体、手部以及各种ShapeNet物体上都成功恢复了有意义的部件。

解耦是当前重要的研究方向。给定一组图像,我们试图分离出生成图像的根本因子,例如物体的形状、纹理或背景信息。我们通过向模型引入弱偏置,使其能够自动学习将这些因果因子解耦。一旦获得这种解耦表征,我们就可以任意组合形状、纹理和背景。研究表明,使用这种干预数据训练的分类器,比仅在原始数据集上训练的分类器更加鲁棒。我们还可以在潜在空间中进行遍历,探索解耦因子的变化。

感知与控制的结合:自动驾驶

我们不仅对感知感兴趣,也对将感知与控制相结合感兴趣。我们认为,只有在实际反馈循环(如机器人系统、自动驾驶汽车)中审视控制问题,才能学习如何开发更好的视觉系统。

以下是我们在该领域的一些探索:

  • 学习端到端驾驶策略的中间表示:我们发现,为学习驾驶策略标注正确的部分,比提供非常详细的语义标注更重要。过于详细的类别标签可能会使视觉抽象模型感到困惑,反而导致策略驾驶效果变差,尽管标注花费了更多时间。
  • 更好的自动驾驶模型:自动驾驶是一项复杂的任务,需要将高维输入信息(多传感器图像、激光雷达、RGB信息、地图等)整合成一个紧凑的表征以做出驾驶决策。为此,我们开发了一个模型,将本课程中学到的Transformer架构与上述隐式表示相结合,用于预测自动驾驶的路径点和语义信息。

    训练出的驾驶策略仅以多个图像作为输入,能够学习遵守交通规则、为行人和车辆停车、正确转弯并驶向目标地点。

总结

本节课中,我们一起学习了自主视觉研究小组如何利用深度学习解决3D视觉中的核心问题。我们从隐式表示的3D重建出发,看到了其在生成模型、4D动态建模和外观建模上的强大能力。接着,我们探讨了如何将其扩展到大规模场景以及进行无监督学习。然后,我们了解了语义解耦在理解物体构成方面的重要性。最后,我们看到了如何将先进的感知模型与控制系统结合,应用于像自动驾驶这样的复杂现实任务中。这些研究展示了深度学习在推动机器感知与交互边界方面的巨大潜力。

如果您对我们的研究感兴趣,推荐您访问我们的研究博客,那里有关于我们近期工作的简短介绍。

非常感谢您的聆听,也感谢您参与本课程的学习。祝您考试顺利!

014:逻辑回归与计算图

在本节课中,我们将要学习分类问题,并引入逻辑回归模型。我们还将初步了解计算图的概念,这是理解深度神经网络工作原理的重要一步。

上一讲我们讨论了线性回归及其最大似然解释。本节中,我们来看看分类问题,特别是二分类问题。

逻辑回归问题

以下是监督学习问题设置的简要回顾。

我们有一个模型,它试图从输入 X 预测输出 Y。该模型 F 由参数 W 参数化。在机器学习中,我们需要解决两个基本问题:学习问题和预测问题。学习问题是从训练集中估计模型的参数 W。训练集由 X 和 Y 对组成。学习之后,我们固定参数 W,然后可以使用该模型对未见过的输入 X 进行新的预测 Y。

上次我们讨论了回归问题,其中输出是一维实数。今天,我们将考虑分类问题。输入同样可以是任何结构化对象。输出是一个二元变量,因为它只有两种可能性。例如,我们试图对图像进行分类,判断它是否描绘了海滩。

我们已知条件最大似然估计器。更准确地说,我们通常使用对数似然估计器,这出于数值稳定性、数学原因以及与信息论概念的联系等考虑。我们将其估计值记为带“帽子”符号的 W_hat。

现在我们要执行二分类。输出只能取两个可能的离散标签,为简化起见,设为 0 或 1。问题在于,在这种情况下我们应该如何选择模型分布?高斯分布不是一个好的选择,因为它建模的是连续分布。伯努利分布是适合此问题的分布,它精确地建模了这个二分类问题。

伯努利分布如下所示。其中 y_hat 是某个模型的预测值。y 是来自我们数据集的真实标签。如果 y 是 0,则选择 (1 - y_hat) 项。如果 y 是 1,则选择 y_hat 项。可以将 y_hat 视为预测正类(类别 1)的概率。

y_hat 是依赖于输入 X 和可训练参数 W 的预测值,我们将其记为函数 F_W(x)。为了简化公式,我们暂时继续使用 y_hat 这个符号。

总之,我们假设伯努利分布,其中 y_hat 是 F_W(x) 的简写。另一个问题是我们应该如何选择 y_hat 或 F_W(x)。模型分布是一个离散分布,因此 F_W 必须在 0 和 1 之间,必须是一个适当的概率。

我们如何有效地实现这个要求?我们可以使用所谓的压缩函数或 sigmoid 函数。我们选择 F 作为线性函数的 sigmoid 函数。Sigma 表示 sigmoid 函数,其表达式为 1 / (1 + e^{-x})。

我们取权重和 x 的线性组合,然后通过一个非线性变换 sigma。我们选择这种形式的 Sigma 是因为它是一个特殊的形式。sigmoid 函数 sigma 将无界的实数范围映射到 0 到 1 的区间,这正是我们需要的,因为该函数的输出必须在 0 和 1 之间才能成为一个概率。

这也被称为逻辑回归的原因,因为这是逻辑函数,有时也称为逻辑传递函数。这是处理分类问题时的要求。

现在让我们把它们结合起来。我们使用之前的最大似然估计器。对于模型分布,我们在此处插入伯努利分布,其中 y_hat 是 W^T x_i 的 sigmoid 函数。

由于我们有这个表达式的对数,我们可以稍微转换一下。我们可以将乘积的对数拆分为对数的和。然后,我们可以通过在前面加上负号,将最大化问题转化为最小化问题。在机器学习中,我们通常不最大化似然,而是最小化损失函数,这就是我们这样写的原因。

后面这个项在机器学习社区中有一个特定的名称,称为二元交叉熵损失。我们用符号 L 表示损失。高损失是不好的,低损失是可取的,因此我们希望最小化损失。我们希望计算所有数据点上这些个体损失总和的最小值。

在机器学习中,我们经常使用更通用的术语“损失”而不是线性回归中使用的“误差”。原因是损失项更通用。它不一定是观测值之间的误差,它也可以是,例如,我们想要编码的归纳偏差,或者是对网络权重的正则化器,或者是一些更难以描述为误差的复杂对象。损失函数是一个更通用的术语。

这个公式有以下解释:我们想要最小化的是经验数据分布 P_data(由训练集定义)和模型分布之间的差异。我们想找到模型的参数,使得模型的预测与我们的数据最相似,实际上两个分布是相同的。

二元交叉熵损失

现在我们只看单个数据点 i 的这个表达式,这就是我在这里写下的二元交叉熵损失。

我们可以将二元交叉熵损失重写如下:如果 y_i 等于 1,那么我们考虑第一项,因为第二项消失了(1-1=0)。如果 y_i 等于 0,那么第一项显然消失。如果真实标签是 1,我们考虑蓝色曲线;如果真实标签是 0,我们考虑红色曲线。

我们希望最小化这条曲线。这些曲线何时最小?对于 y 等于 1 的蓝色曲线,如果预测值 y_hat 也接近 1,我们得到一个较小的值。相反,如果预测值接近 0 而真实标签是 1,则损失很高。类似地,对于真实标签为 0 的情况,我们希望最小化损失,使得预测值 y_hat 也接近 0。这非常直观。

实际上,这意味着如果对于数据点 i,预测的 y 接近或等于真实的 y,则损失 L 最小。这个损失函数可以扩展到两个以上的类别。我们可以将其视为概率,在有两个类别的情况下,因为这是一个二维单纯形,我们这里有 1 减去第一项,但我们可以考虑更高阶的单纯形,其中有多个输出,是超过两个类别的离散分布。我们将在后面的讲座中讨论这个问题。

让我们更直观地看一下这个结果。一个简单的一维例子:在 x 轴上是我们的一维特征输入。然后我们想在正类和负类之间进行分类。我们从数据中观察到的只是 x 值,然后我们想判断特定的 x 值是属于正类还是负类。可以看到它们是离散的,只取值 1 或 0。

这是数据集:我们有一组绿色的正样本和一组红色的负样本(负样本对应 y_i = 0)。我们想要拟合一个逻辑回归模型,它具有这样的逻辑曲线。这是一个非常简单的模型,基本上只采用偏置加上权重乘以这里的 x 值,这是一个非常简单的线性项,然后通过图中所示的非线性变换。

如何用概率和损失函数来解释这一点?分类器的概率就是这条曲线。对于正样本,用这些绿色条显示,每个绿色条基本上一直延伸到曲线,因为这是模型分配给这些正值的概率。

对于正值,这并不理想。即使总体上这是理想的分类器,例如,这个数据点的样本仅以大约 0.4 的概率被分类为正值,因此根据我们的分类器,它属于负类的机会更高。但我们想要优化的标准是,对于所有数据点,我们希望最大化这些被正确分配的概率。

不幸的是,仅凭这些特征,不可能完美地做到这一点,因为我们不仅有正点,还有负点,这些是负点的概率。在左侧,我们相当有信心。对于这个异常值,我们实际上有更高的概率属于正类区域。我们必须保持这种平衡,我们想要训练一个代表整个数据集的分类器。这就是我们计算的交叉熵的总和,它平衡了这些数据点的影响。

现在我们可以把所有这些概率放在一起。我们可以把它们放在底部。同样,红色是负类,绿色是正类。我们现在想要优化的基本上是这些概率,我们希望最大化它们,或者等价地,我们希望最小化负对数,这就是这里的负对数。例如,这个被错误分类的红点,其正确类别的概率很低,损失也很高。这是有道理的。然后我们要做的是最小化所有这些负对数概率的平均值或总和。这基本上就是我们在做的事情的直观理解。

模型优化

现在,这是我们一直在研究的逻辑回归的完整最大似然模型。我们试图找到最优权重 W,它是所有数据点上的二元交叉熵损失,其中模型通过线性函数的逻辑函数来预测 y_hat。

现在的问题是,我们如何实际优化这个?在线性回归的情况下,这很容易,因为损失是 W 的二次函数。现在,在这种情况下,损失不再是 W 的二次函数。如果你看这个损失,W 出现在 sigmoid 内部,这已经是一个非线性函数,然后我们通过这个非线性函数,再取这个非线性函数的对数,这也是一个非线性函数,所以这是线性模型的高度非线性变换。因此,我们没有像在线性回归任务的二次情况下那样简单的表达式。

在这种情况下,如果没有封闭形式的解析解,我们需要应用一些基于梯度的迭代优化技术。幸运的是,我们可以计算梯度。因此,我们可以使用基于梯度的优化器,从某个初始化开始,沿着梯度缓慢地到达局部最小值。实际上,在这种情况下,情况很好,因为尽管这不是一个容易最小化的二次函数,我们需要应用这个迭代算法,但它仍然是一个凸函数。可以证明这个问题是一个凸问题,所以我们知道如果我们应用基于梯度的优化,我们不会陷入局部最优,最终会得到全局最优,这很好。但我们不能一步或解析地达到它,我们需要应用迭代的基于梯度的算法。

对于所有需要梯度的基于梯度的优化算法,我们需要计算这个梯度。这是 Nabla 算子,简单地说,我们想计算这个函数相对于 W 的梯度向量。

好在机器学习中,这些机器学习问题的损失函数通常实际上是一个一维量,这里也是如此。这意味着这不是一个雅可比矩阵,而只是一个向量,它是一个一维量,我们为其计算相对于多个权重的导数。关于这个特定目标函数的好处是,尽管第一步求梯度看起来相当复杂,但当你进行解析推导时,它会大大简化,这是二元交叉熵损失对于这个逻辑回归模型的梯度的最终形式。它有一个非常简单的形式:(y_hat - y) * x。

既然我们能够解析地计算这个梯度,我们就可以将其应用于迭代的基于梯度的优化器中,该优化器试图逐步走向最优值。我们可以使用的最简单的优化器,也是如今在深度学习中大量使用的,称为梯度下降。我们选择步长 eta 和容差值 epsilon,然后需要初始化我们优化的权重,记为 W_0。上标是时间或迭代索引。然后我们重复直到这个向量 V 小于某个 epsilon。我们迭代一个计算梯度的过程。这个向量 V 就是整个数据集上函数的梯度。我们可以将这个梯度算子拉到数据点的求和内部。所以我们需要做的实际上是计算损失的总和。当我们计算那个梯度时,我们可以把这个梯度拉到求和内部,这就是这里发生的情况。然后我们优化这个,直到梯度变小,直到我们收敛到这个函数的一个极值。

有几种变体效果更好。一种是线搜索,你沿着梯度的方向前进,但搜索最小值。如果你能高效地评估函数,那么这样做是很好的。对于某些问题,你甚至可以通过使用共轭梯度法做得更好,你不是沿着梯度的方向,而是沿着某个共轭方向,这能更直接地带你到最优值。还有其他技术,如 LBFGS 等。但所有这些技术的共同点是它们都需要梯度。这就是我们为此问题解析推导出的结果。

逻辑回归拟合结果

现在让我们看看使用这种迭代梯度优化器进行逻辑回归拟合的一些结果。这是一个二维示例。底部的公式显示,函数 F_W 现在基于两个输入 x1 和 x2,是一个二维域。我们有一个基于这些变量的线性组合加上某个偏置项 w0 的逻辑函数。当我写粗体 X 时,我指的是一个 x 向量,通常我也在那里包含偏置值。这意味着如果我写一个粗体,第一个元素总是 1,然后其他元素是 x1, x2 等。对于这个简单的二维情况,我把它写得很明确,所以我们有偏置,我们有三个权重,但有两个输入。

我们有两个不同的数据集。在左侧,蓝点是正类,红点是负类。我们尝试拟合这个逻辑回归模型。可以看到这产生了一个线性决策边界。但我们也能优化斜率,这给了我们一些关于混淆的指示。白线表示 0.5 的位置,我们会将一个新点分类为恰好可能属于两个类别。如果它稍微在白线右边,我们的分类器更可能将其分类为负类;如果稍微在左边,则更可能分类为正类。

在右侧,情况略有不同,因为这两个数据集、这两个簇重叠更多,所以混淆更多。它们的排列也略有不同,中心与这里不同。你可以看到现在决策边界有不同的斜率,因为中心位于不同的位置,也因为点之间有更多的混淆,分类器也通过具有较小的斜率表现出相同的更多混淆行为。因此,蓝类和红类之间的过渡不像这里那样迅速。这可以通过权重 w0, w1, w2 来建模,即这个 sigmoid 函数的斜率。

与信息论的联系

最后,我想强调一下与信息论的联系,尽管我们时间不够,无法深入细节,但联系非常紧密且有趣。特别是,最大化对数似然实际上等价于最小化交叉熵或 KL 散度,这两者都是信息论中的基本量。

在顶部,我们有我们的对数似然最大化问题。这与之前相同,我们有模型分布。我们想找到参数 w,最大化所有数据点上模型概率的对数和。这被称为对数似然。

我们可以使用期望来重写这个。我们在这里所做的实际上是查看数据分布上的经验估计。如果我们在这里写 1/N,那就是均值,那么我们将查看数据分布的经验估计。但 1/N 相对于 W 是常数,所以它被吸收到 argmax 算子中。但暂时把这个方程看作开头有 1/N。那么这将完全对应于期望算子的定义,这基本上是对数据点概率的期望。每个数据点都是基于抽样的估计。所以我们现在有 y 和 x。如果我在这里写一个期望,因为空间不够,我只写 P_data 的期望,因为我们知道期望是关于什么的,是关于数据分布的,即 (x, y) 对。更正确地说,我本应在这里写一对括号 (x, y),从数据分布中抽样,这实际上就是这里发生的情况,我们有来自数据分布的 n 个样本,这就是为什么它等价于为这些样本 x 和 y 写关于数据分布的期望。然后在参数中是与这里相同的项。

所以我们有关于数据分布的模型分布的期望。当然,我们可以用 argmin 来写这个 argmax,如果我们在这里把加号换成减号,和之前一样。我们这样做是因为现在这变成了信息论中一个非常著名的量,称为交叉熵,它是数据分布和模型分布之间的交叉熵,并且与熵和 KL 散度有紧密联系。

我们也可以做的是,我们可以取这个项,并在这个参数内部添加对数数据概率 P_data(y|x)。为什么我们可以这样做?因为数据概率,这个数据分布不依赖于 w,所以它改变了这个函数的值,但相对于 W 是常数,所以我们可以直接加上它。这不会改变这个表达式的最小化器。所以这样写是正确的。现在,我们这里的最后一项与这个期望一起,如果你把它分解进去,它也是一个线性算子。是交叉熵,第一项只是数据分布的熵。

所以我们看到,我们有效地有交叉熵减去熵。这在信息论术语中称为 Kullback-Leibler 散度(KL 散度)。KL 散度是一种散度,它是两个分布相似性的度量,这就是我们在本单元前面已经提到的。所以我们试图最小化的,这里有一个很好的直观理解,通过计算参数的最大似然估计,我们试图最小化两个分布之间的距离,更准确地说,是由数据集给出的经验数据分布与给定其参数 w 的模型分布之间的距离。

这是一个例子。假设绿色的是数据分布,红色的是模型分布(对于实数值的高斯分布,而不是分类)。那么我们想让它们尽可能相似。所以这种情况下 KL 散度会相当大,因为重叠很小;而这种情况下的 KL 散度会很小。这就是我们想要实现的:我们希望我们的模型能很好地表示数据,我们希望调整模型的参数,使其尽可能好地拟合数据。

本节课中我们一起学习了逻辑回归模型,它用于解决二分类问题。我们引入了 sigmoid 函数将线性输出映射为概率,并定义了二元交叉熵损失函数。我们还探讨了如何使用梯度下降等迭代方法来优化模型参数,因为逻辑回归的损失函数没有封闭解。最后,我们了解了最大似然估计与最小化数据分布和模型分布之间的 KL 散度是等价的,这建立了与信息论的联系。这些概念是理解更复杂神经网络模型的重要基础。

015:计算图 🧮

在本节课中,我们将学习计算图这一核心概念。计算图是理解复杂计算、以及在这些长计算链中高效计算梯度的基础工具。

概述

上一节我们讨论了逻辑回归模型的最大似然估计。本节中,我们来看看如何通过计算图来分解复杂计算,并高效地计算梯度。

计算图简介

在之前的幻灯片中,我们看到了逻辑回归模型的最大似然估计量。

最小化这个估计量需要求解一个没有解析解的非线性目标函数,因此必须使用基于梯度的优化器。这意味着我们需要计算这个表达式的梯度。

幸运的是,在上述逻辑回归的例子中,梯度计算相对简单。其公式为:预测值减去真实标签,再乘以输入数据点 i。公式可以表示为:

梯度公式: ∇ = (ŷ_i - y_i) * x_i

然而,对于本课程感兴趣的更复杂模型(尤其是深度神经网络),梯度计算通常不会如此简单。这些模型包含大量计算层,我们无法通过纸笔手动推导出数百万乃至数十亿参数的梯度。

因此,核心问题在于:我们如何高效地计算一般情况下的梯度?这个问题在深度学习历史上曾长期存在。直到20世纪80年代,人们首次证明了高效计算梯度的可行性,这推动了深度学习的突破。

计算图的核心思想

计算图的核心思想,是将复杂的计算分解为一系列非常简单的、原子级的赋值操作序列。我们称这个序列为计算图,也有人称之为“源代码”,因为它就像一行行执行的计算机程序或汇编代码。

前向传播接收训练点 XY 作为输入,并计算出一个损失值。正如我们将看到的,这个损失的梯度可以通过反向传播(或称后向传播)来计算,这也就是下一单元要讲的反向传播算法

本幻灯片的关键信息是:前向传播和反向传播都可以高效计算。这是因为我们可以利用动态规划的思想,即存储并重用中间结果,而不是在每次前向或反向传播时都重新计算它们。这种计算的分解与重用,是反向传播算法成功的关键。没有它,我们将无法将算法应用于如今规模庞大的模型。优化拥有数百万或数十亿参数的深度网络将是不可想象的。正因如此,能够调整深度模型中海量参数的反向传播算法,至今仍是深度学习的核心驱动力。

计算图的构成

现在,让我们具体看看计算图是什么。在本课程中,我们将计算图定义为包含三种节点的结构:

  • 输入节点(绿色):没有参数,是学习问题的输入,即数据集进入计算图的地方。例如,图中的 xy
  • 参数节点(橙色):存储模型的参数。这是我们想要更新的部分。在反向传播梯度时,我们最终感兴趣的就是这些参数的梯度,因为更新将应用于此。
  • 计算节点(红色):执行计算操作。在我们的设定中,损失函数也是一个计算节点。中间的计算节点接收输入(例如输入节点、之前的计算节点或参数),并产生一个结果,完成一次变量赋值。

通过这种图形化的表述,我们可以将复杂计算分解为一系列更简单的计算。

计算图实例:线性回归

这里我们看一个线性回归的具体例子。在线性回归中我们需要做什么?右侧的“源代码”展示了我们需要计算的四行赋值代码,可以将其视为汇编代码。

以下是计算步骤:

  1. 首先,将一维输入 x 与一维参数 w1 相乘,结果赋值给变量 u。代码表示为:u = w1 * x
  2. 接着,加上偏置项 w0,将 uw0 相加,结果赋值给预测值 ŷ。代码表示为:ŷ = u + w0
  3. 然后,从预测值 ŷ 中减去真实值 y,得到差值并赋值给变量 c。代码表示为:c = ŷ - y
  4. 最后,为了计算L2损失,我们对差值 c 取平方,并将结果赋值给损失变量 L。代码表示为:L = c²

这就是我们将线性回归问题(本可以写成一行)分解为一系列原子操作序列的方式。

计算图的粒度

我们可以选择不同级别的粒度。上面的例子使用了非常精细的粒度。但我们也可以使用更粗的粒度,这取决于我们希望这些原子单元有多大,以及我们能处理何种级别的原子操作。

例如,在您将使用的教育框架中,可能有一个负责仿射变换的单元,它同时包含了乘法和偏置加法。在这种情况下,我们可以将步骤1和2合并,计算图随之改变。现在,步骤1和2坍缩为一步,直接从 x 计算 ŷ

我们还可以将损失计算也合并,例如直接计算 (ŷ - y)²。这样,整个计算图可能只剩下两个计算步骤。因此,书写计算图有多种方式,具体取决于我们想如何实现以及我们能高效实现多复杂的操作。

更多示例:逻辑回归与多层感知机

以下是逻辑回归的另一个例子。输入 x 首先经过一个仿射变换(参数为 w0, w1)得到 u,然后 u 通过一个非线性σ函数(如Sigmoid)得到预测值 ŷ。最后,使用二元交叉熵损失函数比较 ŷ 与真实标签 y

我们也可以用向量形式表示,用一个权重向量 w 和一个扩展的输入向量 [1, x](以包含偏置)来简化表示。在实际的教育框架中,我们将使用向量操作。

我们还可以将两个这样的操作堆叠起来。这已经是对下一讲(第3讲:多层感知机)的初步预览。多层感知机是我们考虑的第一个深度模型,它之所以“深”,是因为它拥有超过一层(这里指除输入层外的层)。在本例中,我们有一个隐藏层和一个输出层,共两层。而逻辑回归模型只有输出层一层。

在这个计算图中:

  1. 输入 x 通过权重矩阵 W1 进行仿射变换。
  2. 结果向量经过元素级的非线性函数(如ReLU),生成隐藏特征向量 h
  3. 向量 h 再与权重向量 w2 相乘得到一个标量。
  4. 该标量通过Sigmoid非线性函数得到预测值 ŷ
  5. 最后应用二元交叉熵损失。

通过这个例子,你已经可以看到我们如何优雅地使用计算图来建模不同粒度,以及如何堆叠更大的计算。这仍然是一个非常小的模型。在本课程后面,我们将讨论拥有50、100甚至更多层的深度网络,但你可以想象我们如何将它们连接在一起。

总结

本节课中,我们一起学习了计算图的基本概念。我们了解到,计算图通过将复杂计算分解为原子操作的序列,并利用动态规划思想存储和重用中间结果,使得前向传播和反向传播都能高效进行。这是现代深度学习能够训练海量参数模型的基础。我们还通过线性回归、逻辑回归和多层感知机的例子,直观地看到了计算图的构建方式和不同的粒度选择。理解计算图是掌握后续反向传播算法的关键一步。

016:计算图与反向传播 🧠

在本节课中,我们将要学习深度学习中的一个核心算法:反向传播。我们将看到如何通过计算图来高效地计算复杂损失函数关于数百万参数的梯度,这是训练神经网络的基础。

概述

在第三单元中,我们终于要介绍反向传播算法。

请记住,大多数最大似然问题没有闭式解。因此,我们需要利用基于梯度的优化技术。为了使用这种基于梯度的优化器,我们需要找到模型负对数似然关于模型参数 W 的梯度。

更一般地说,我们想要做的是关于参数 W 优化一个损失函数。这里我们有一个用花体 L 表示的损失函数,它依赖于一个数据集。粗体的 yx 再次表示整个数据集,按行堆叠,以及参数向量 W

如果我们根据单个数据点将其写出来,我们得到所有数据点之和的梯度,因为我们假设数据是独立同分布采样的,所以这是每个数据点的损失函数项 L(y_i, x_i, W)n 表示我们数据集中的数据点数量。

因为梯度算子是线性的,我们可以将其提取到求和符号之外。所以这等于所有数据点的损失函数关于参数 W 的梯度之和。在这个符号中,我使用了 x_iy_i 作为数据集元素。

在接下来的内容中,我们将考虑关于单个数据点的梯度计算,所以我们只关注这里的这个表达式。如果我们想找到关于整个数据集的梯度,我们只需要对所有为每个数据点估计的梯度进行求和。

所以基本上,从这个表达式,我们得到一个向量,它是这个标量值损失函数关于所有参数的梯度,在我们的神经网络中可能有数百万个参数。然后我们为每个数据点 i 计算这样一个梯度向量,然后我们对所有数据点的这些梯度向量进行逐元素求和,以获得关于整个数据集总损失函数的梯度向量。

一旦我们有了这个梯度,我们就可以沿着梯度方向迈出一步,这被称为梯度下降。因此,关于整个数据集 X 的梯度是通过对所有单个梯度求和得到的。在接下来的幻灯片中,我们将看看如何实际高效地计算这些梯度,这并非易事,因为在大型神经网络中,我们有数百万个这样的参数。因此,这个计算必须非常快,因为我们需要进行很多梯度下降步骤才能到达局部最小值。

链式法则:反向传播的基石

为了理解反向传播算法,我们基本上只需要理解一个规则,这个规则我们在高中就已经知道了,那就是链式法则。

链式法则告诉我们,如果我们有两个函数 FG 的串联,这里我们有一个函数 F,它依赖于函数 G 的结果,而函数 G 依赖于某个值 x。那么函数 F 关于变量 x 的导数等于函数 F 关于函数 G 的导数乘以函数 G 关于 x 的导数。

所以这是链式法则的最简单形式。现在,我们在这节课中还需要用到所谓的多元链式法则。

多元链式法则关注的是函数的组合,其中在一个函数的参数中,我们有一组函数,这些函数中的每一个都依赖于同一个变量 x。这就是这两者之间的区别:这里我们只有 f(g),而这里有 f(g1, ..., gm),所有这些函数 g1gm 都依赖于同一个变量 x。我们想要计算的是这个函数组合关于变量 x 的导数。

多元链式法则告诉我们,这个导数就是所有单个链式法则的和。所以是 F 关于 g_i 的导数乘以 g_i 关于 x 的导数,然后对所有函数 i 求和。

这两个规则真的是我们在整个讲座中将要应用的唯一内容。

一个简单的例子:链式法则的应用

让我们看看反向传播算法的最简单形式,这是最基本的例子之一,基本上只是链式法则的一种略微不同的形式。

目前,我们不对节点类型做任何区分。请记住,在计算图中,我们定义了输入节点、参数节点和计算节点。现在,我们只考虑节点,所以这里所有的节点都是红色的。这是我们之前用于计算节点的颜色,但此刻我们不打算做任何区分。我们唯一想做的就是计算关于这些节点中任何一个的梯度。

所以我们想计算这个最终输出节点的梯度,我们在这里称之为 L 表示损失。实际上,在这种情况下,它不一定是一个损失函数,也可以是 C 等。在这个简单表达式中,它不一定是损失,但因为我们在后面总是将损失视为输出节点,所以我也在这里使用了损失符号。我们感兴趣的是这个损失关于损失本身、变量 y 或变量 x 的梯度。

这是一个非常简单的计算图,其中 y 依赖于 xL 依赖于 y。我们想要在这里表达的表达式就是 L = 2 * x^2,并且我们按照上一讲讨论的方式,将其分解为计算图中的原子操作,如下所示。

第一步,我们计算 y 作为 x 的平方。第二步,我们计算 L 作为 2 * y。将两者组合,我们得到 2 * x^2

现在我们要做的是,在运行这个前向传播之后,也就是在计算了特定输入 xL 值之后,我们将运行一个所谓的反向传播。这就是反向传播名称的由来。所以我们将首先沿着黑色箭头的方向运行前向传播,以便从输入 x 计算 L

假设 x 为 1,那么我们将有 L = 2。然后我们将运行反向传播,我将在接下来的幻灯片中用蓝色箭头表示,同时我们从输出节点(在我们的例子中总是 L)反向传播梯度到每个单独的节点,这样在每个单独的节点,我们都可以读出关于该节点的梯度,即 L 关于这些节点中每一个的梯度。

我将在这里使用两种不同的颜色来表示两种不同类型的量。我将使用红色表示反向传播的梯度。我将使用蓝色表示局部梯度,即基于这里的赋值公式、基于特定节点存在的局部函数形式局部计算的梯度。

反向传播从输出节点 L 开始。它向后计算梯度。它做的第一件事是计算 L 关于自身的梯度,这显然是 1。所以 L 关于 L 的偏导数是 1。

请注意,在这个符号中,我们总是在某种程度上滥用导数运算符。所以我们总是使用偏导数来表示我们正在计算关于该整个表达式中任何可能参数的导数。即使那个参数可能只出现在该表达式的内部函数中。所以每当我写 L 关于任何变量的偏导时,我指的是输出节点关于这些内部变量中任何一个的梯度。

为了现在计算 L 关于 y 的梯度(这是我想先做的,我把它写在 y 下面的底部),我需要做的是应用链式法则,即我需要计算 L 关于 L 的梯度乘以 L 关于 y 的梯度。

在这里看起来有点荒谬,因为我们真正感兴趣的只是 L 关于 y 的梯度。但你会看到,如果我们在这个简单的计算图中进一步向后走,以这种形式书写是有意义的。

显然这是正确的,如果我们把这里的 L 消掉,那么这和 L 关于 y 的梯度完全一样。那么 L 关于 y 的梯度是多少呢?它是 2,我们可以从这个表达式中看到。所以 L 关于 y 的梯度是 L 关于 L 的梯度乘以 L 关于 y 的梯度,即 1 乘以 2,等于 2。所以我们现在已经将梯度反向传播到这里。

现在我们可以再向前一步,我们从第二个表达式这里向后走,现在我们在计算第一个表达式,关于第一个变量的梯度,这是我们在前向传播中首先计算的。

我们做完全相同的事情。所以现在我们计算 L 关于 x 的梯度。这就是我们想要计算的。这正好是我们发送到这里的信息,再次是链式法则:它是 L 关于 y 的梯度乘以 y 关于 x 的梯度。显然这是正确的,这和 L 关于 x 的梯度相同,因为这里的偏 y 消掉了。

现在,L 关于 y 的梯度我们已经计算过了,这是从这个节点反向传播的梯度,我们已经将其反向传播到这里,所以这是我们在前一次迭代中已经计算好的东西。

所以我们现在唯一需要做的就是乘以局部梯度,即 y 关于 x 的梯度,也就是 2x。这就是这里发生的情况:L 关于 x 的梯度等于 L 关于 y 的梯度乘以 y 关于 x 的梯度。而 L 关于 y 的梯度是我们在这一点计算的值乘以 2x,这是一个局部梯度。现在我们已经找到了 L 关于 x 的梯度。

你可以想象,我们可以在这样的链或树中进一步向后走,以便将梯度反向传播得更远。我们最终感兴趣的当然是反向传播梯度到参数函数的权重,比如逻辑回归器或神经网络。我们将在接下来的幻灯片中看到这一点。

具体数值示例

这也许有点抽象,所以让我们把它变得非常具体。

我们在这里做的是插入实际的数字。所以 L 关于 L 的梯度是 1。我们把它插在这里,然后乘以 L 关于 y 的梯度,即 2。1 乘以 2 是 2。所以这里的梯度,L 关于 y 的梯度,就是我们在这里计算的值。我们已经将其反向传播到这里。可以想象这些箭头反向传播了从先前节点和局部梯度计算出的梯度到这个位置,所以我们只是把它复制到这里。

现在这个值来到这里,它是这里相同的表达式。我们再次乘以它,我们将反向传播的梯度(在这种情况下不是 2)乘以局部梯度,即 2x。所以我们得到 4x。我们将这个梯度 4x 发送到这个位置,所以我们在这里得到 4x

正如你所看到的,L 关于 x 的梯度显然是 4x。当然,当你在计算机上实现这个以进行实际的前向和反向传播时,你会为 x 填入实际的值,即当前输入和输出的值。你也知道参数向量 W 的当前状态值。对于 xy,以及对于 W(我们这里还没有 W,我将在下一张幻灯片中看到),但在特定迭代中的所有值,我们都有已知的值。所以我们总是会插入实际的梯度,而不是这里的这些符号梯度,但我们实际上会有我们在这里输入的真实值。

抽象形式与分支情况

我们已经让它更具体了一些,现在让我们再次让它更抽象一些。

我在这里所做的是用抽象函数替换了这些函数的具体定义。所以我在这里写的是,不是 y,而只是函数 y(x);不是 L,而只是损失函数 L(y)。所以表达式读起来像这样:L(y(x))。这是我能表达这个特定计算图的最通用形式。

在这个小的通用形式中,我有反向传播:L 关于 y 的梯度是节点 L 处的梯度乘以局部梯度。因为这总是 1,所以我这里只有局部梯度。然后 L 关于 x 的梯度,这里的这个值,是 L 关于 y 的梯度(即我之前计算的)乘以 y 关于 x 的梯度(这里的局部梯度),通过反转这个箭头。

所以这是最简单的例子,基本上只是应用了两次链式法则。现在让我们看一个稍微困难一点的例子。这是一个我们有一个出度大于 1 的情况的例子。出度指的是从一个特定节点出发的连接数量。之前我们有一个链,所以每个节点都有一个出站连接,现在在这种情况下,y 有多个出站连接。y 发送到 uv

所以我们在这个特定情况下看这个损失函数:L(u(y(x)), v(y(x)))。这个例子中的前向传播如下所示。

y 被计算为 y(x)。所以这是我们在输入 x 处评估的关于 x 的函数。然后我们评估 u(y)v(y),就是这两个。我在这里使用了相同的迭代编号,因为它们基本上是同时处理的,所以它们是分支出来并同时处理的。然后我们有 L,它同时依赖于 uv

现在让我们计算反向传播。再次从输出节点 L 开始。我们有 L 关于 u 的梯度,是 L 关于 L 的梯度(即 1)乘以 L 关于 u 的梯度。类似地,L 关于 v 的梯度是 L 关于 L 的梯度乘以 L 关于 v 的梯度。

到目前为止一切顺利。这是第三步。现在让我们看第二步,再向前一步。L 关于 y 的梯度是多少?现在情况与之前不同了。在这种情况下,uv 都将梯度信息发送到这个变量 y。即,u 发送 L 关于 u 的梯度(这是在 u 位置计算的值)乘以从 uy 的局部梯度。类似地,v 发送 L 关于 v 的梯度(这是在先前反向传播算法迭代中存储在这里的值)乘以从 vy 的局部梯度。

那么这个表达式应该读作什么?回想一下我们之前看到的多元链式法则,我们这里的情况完全相同。如果你看这个损失函数,我们有一个损失,它依赖于 y,而 y 是两个函数 uv 的参数,uv 都依赖于 y。所以如果我忽略 y 的参数 x,这个损失函数将读作 L(u(y), v(y))

我们从多元链式法则知道,L 关于 y 的梯度是 L 关于 uu 关于 y 的梯度之和,加上 L 关于 vv 关于 y 的梯度之和。所以这仅仅是在一个特定节点处应用多元链式法则。这就是我在这里写的内容。当然,这两项都有蓝色的局部贡献和红色的反向传播全局贡献。

重要的是要记住,每当我们有这样一个出度大于 1 的情况,每当一个变量发送到多个变量,每当我们有像这样的表达式时,我们需要在反向传播过程中对梯度求和。这就是这里发生的情况。基本上就是这样。

然后在最后一步,因为我们还没有完成,我们计算 L 关于 x 的梯度,正如我们已经知道的,是 L 关于 y 的梯度(这基本上是我们在上一步中计算的和)乘以局部梯度 y 关于 x 的梯度。当然,这是一个抽象的例子,我们不知道每个单独函数的确切形式,所以除非我们详细指定这个函数,否则我们无法进一步研究。

算法实现思路

现在我们基本上已经准备好实现反向传播算法了。

我们如何在 Python 中实现这个反向传播算法?一个方便的方法,也是教育框架所做的,是将每个变量或节点视为具有某些属性的对象。例如,值和梯度。

假设我们有一个变量 x,它有一个值,这些值是在前向传播中计算的。它还有梯度,这些梯度在反向传播中被更新。

值是在前向传播中计算的,这基本上只是用 Python 写出这个程序,即赋值序列。在那里,x 的值基本上是输入,比如图像或任何作为输入进入模型的信号。然后我们有 y 的值,是函数 Yx 值处的评估。然后 u 的值是函数 Uy 值处的评估。v 的值是函数 Vy 值处的评估,而 y 值已经在这里计算过了。最后,我们有损失函数,它简单地依赖于对损失函数 L 的评估,而 L 依赖于已经在这里计算出的 u 值和 v 值。

类似地,我们可以再看一下反向传播,我在这里使用了红蓝符号来表示它是梯度的反向传播全局分量还是梯度的局部贡献。

我们如何实现反向传播?首先,我们会将梯度设置为 0,因为我们将以加法方式更新它们,所以我们从将所有梯度(x, y, u, v 的梯度)设置为 0 开始。

然后我们从 L 关于 L 的梯度开始,我们知道它是 1。所以这是这里的这一项。每当我使用符号 L.gradU.grad 时,我的意思基本上是损失(输出节点)关于该变量的梯度。例如,y.grad 是损失 L 关于变量 y 的梯度。

现在我们已经用 L.grad = 1 初始化了,现在我们可以计算 U.grad,所以这是这个方程:L.grad(即 1)乘以局部梯度 ∂L/∂U,在前向传播中计算出的 UV 值处评估。所以这里需要注意的是,我们需要前向传播中计算出的值,因为我们需要在这些特定位置评估梯度。所以这真的是一个具体的值,梯度函数在特定值处的评估返回的梯度值。梯度本身是一个函数,在这种情况下它在 UV 处评估。

类似地,对于 V,它是 L 关于 v 的梯度,这是一个函数,在 UV 的值处评估。

然后是 y,现在这是一个特殊情况,由于我们之前看到的多元链式法则,我们有一个和。所以我们在这里简单地执行这行代码两次:一次我们添加 u 的梯度贡献,一次我们添加 v 的梯度贡献。两者都是全局分量乘以局部分量,局部分量在 y 的值处评估。

最后,我们更新 x 的梯度,作为 y 的梯度乘以 y 关于 x 的局部导数,在 x 的值处评估。

机器学习实例:逻辑回归

让我们看一个更接近机器学习的真实例子,我这里展示的例子是我们之前已经见过的逻辑回归例子,也是在计算图前向传播的背景下。

这是我们之前已经见过的公式。BCE 表示二元交叉熵损失。

这里发生的基本上就是,我们有一个线性函数 u,它线性组合 w0, w1x。所以是这个线性函数 u。然后我们有 sigmoid 操作,它返回 ŷ,并以 u 作为输入。最后,我们使用二元交叉熵函数将 ŷy 进行比较,该函数定义如下。

在顶部,由于空间原因,我只使用了缩写 BCE,所以这基本上就是左边整个表达式写成一行。

这是前向传播。现在让我们看看反向传播。特别是,我们想要做的是,在这种情况下,与之前不同的是,我们想要计算梯度,但我们并不对这个图中任何变量的梯度都感兴趣。例如,我们对这些绿色输入变量的梯度不感兴趣。我们可以计算它们,有时我们确实会计算,比如在风格迁移的情况下,输入实际上被修改了,这就是反向传播算法的美妙之处,它可以计算关于任何变量的梯度,甚至可以通过执行两次来计算二阶导数。

但在这里的情况下,当我们对训练参数和学习逻辑回归模型的参数感兴趣时,我们真正感兴趣的只是将梯度从 L 反向传播到 w1w0,这样我们就得到了 L 关于 w1w0 的梯度,然后这些梯度被用于我们迭代循环中基于梯度的优化,以更新模型的参数。

所以在这里你可以看到,我们现在使用了我们在前一单元中定义的相同符号,其中绿色是输入变量,橙色是参数变量。我们感兴趣的是关于参数的梯度。红色是计算节点。

让我们将反向传播算法应用于这个例子。再次,我们从 L 关于 L 的梯度开始,在输出节点处是 1。但我们不是反向传播到 y,我们只是反向传播到 ŷ。那么 L 关于 ŷ 的梯度是多少?它是 L 关于 L 的梯度(即 1,所以这部分消掉了)乘以 L 关于 ŷ 的梯度,它就是这个表达式。所以如果你计算这个项的梯度,你会得到这个表达式。

然后在第二步,我们计算 L 关于 u 的梯度,它是 L 关于 ŷ 的梯度(我们在上一步计算的)乘以 ŷ 关于 u 的局部梯度。而 ŷ 关于 u 的梯度就是 sigmoid 函数关于其输入的导数,即 σ(u) * (1 - σ(u))

我建议将这两个推导作为练习,它们非常简单但也很有启发性,这样你就能看到如何从这个表达式得到这个表达式,以及从这个表达式得到这个表达式,它们都是这些表达式的梯度。

现在我们有了这些局部梯度相对紧凑的项。最后我们做的是,我们计算 L 关于 w0 的梯度,作为全局贡献,即从算法前一次迭代反向传播的梯度 L 关于 u,乘以 u 关于 w0 的梯度。而 u 关于 w0 的梯度是 1。所以我们最终得到 L 关于 u 的梯度。

我们可以对 w1 做同样的事情:关于 w1 的梯度是 L 关于 u 的梯度(从前一次迭代)乘以 u 关于 w1 的梯度。u 关于 w1 的梯度是 x。所以我们最终得到 L 关于 u 乘以 x

当然,当我们在计算机上应用反向传播算法时,我们在前向传播中计算值,然后在反向传播中计算梯度,并立即在前向传播中计算出的值处评估它们。

总结

在本单元中,我们学到的是,我们可以将数学表达式写成计算图。这将复杂的表达式分解为更简单的计算,这些计算是可处理的,并且我们可以应用高效的动态规划来求解前向传播中的值和反向传播中的梯度。

因此,值在前向传播中被高效计算,梯度在反向传播中被高效计算。这确实是为什么反向传播至今仍然是深度学习的主要推动力的原因,因为它允许我们计算梯度,特别是使用并行计算硬件。这是一个高度可并行化的算法。

我们可以在几秒或几分钟内计算一个目标函数(损失函数)关于数百万甚至有时数十亿参数的梯度。

前向传播是前向方向,反向传播是反向方向。我们还看到,在反向传播中,如果有这种头对头的关系,多个传入的梯度会被求和。这里没有发生,但之前在这个例子中发生了,在多层感知机的情况下也会发生,在神经网络中相邻层之间有全连接关系,因此有很多节点发送到下一层的多个节点。这种求和是应用多元链式法则的结果。

关于这一点真正美妙的是链式法则的应用和这种模块化,即每个节点只需要知道如何计算关于其自身参数的梯度。这基本上就是我们在前面的例子中以蓝色显示的内容:我们只需要计算这些局部梯度,我们不需要知道图中任何其他节点的任何信息。它是高度分解的。如果我们能分解事物,这总是好的,它使事情变得更简单。

最后一点说明,当我们现在想要应用这个前向-反向传播(反向传播算法)来优化模型的参数(如逻辑回归器)时,我们当然需要计算整个数据集的梯度。我们刚刚讨论的只是关于单个数据点的梯度计算。我们取了一个数据点,并将其通过这个计算图,以便计算该单个数据点的损失。在这种情况下是 x_i,然后与 y_i 比较。但我们需要的是整个目标函数的梯度,假设数据是独立同分布的,它只是所有数据点的所有梯度之和。

所以我们需要有效地做的是多次应用这个反向传播算法,即应用与我们数据集中数据点数量相同的次数,以获得整个数据集的损失函数关于参数 W 的梯度。

因此,我们应用反向传播算法 n 次,得到梯度,然后利用这个梯度,我们可以在基于梯度的优化算法的内循环中执行一次梯度更新步骤。

一个小小的免责声明:到目前为止,我们只讨论了标量值的反向传播算法,但在下一讲中,我们将看到如何将反向传播算法应用于数组和张量。

本节课中,我们一起学习了反向传播算法的核心思想:通过计算图分解复杂计算,利用链式法则和多元链式法则,从输出层向输入层反向传播梯度,从而高效地计算出损失函数关于所有参数的梯度,为后续的梯度下降优化奠定了基础。

017:计算图与教育框架 🧠

在本节课中,我们将学习如何通过一个名为“教育框架”的简洁Python实现,来具体实践梯度下降算法。我们将深入理解计算图的内部工作原理,并看到如何用代码实现前向传播与反向传播。

在上一节中,我们介绍了在计算图上执行梯度下降的完整算法流程。本节中,我们来看看如何用Python代码实现这个算法。

算法回顾与实现目标

首先,让我们用计算图上的梯度下降简单训练流程来总结之前单元的内容。

使用反向传播算法,我们首先选择步长 η 和容差值 ε。然后,我们在迭代0时将参数 W 初始化为某个值(例如随机值)。这是参数的初始状态。

现在,我们重复以下步骤,直到梯度的大小(即 ||∇L||)低于容差阈值 ε

  1. 对于每个数据点,计算前向传播和反向传播,以计算损失函数关于参数 W 的梯度。
  2. 通过累加所有单个数据点的梯度来计算总体梯度。
  3. 沿着负梯度方向,以步长 η 更新参数。

这就是计算图上的梯度下降算法。问题是,我们如何在Python中实现它?

引入教育框架 (EDF)

在本系列讲座的前半部分,我们将使用所谓的“教育框架”。这是一个由150行Python/NumPy代码实现的深度学习框架,由人工智能先驱之一、芝加哥TTI的David McAllister在其授课背景下建立。

这个教育框架让我们能够深入理解深度学习框架的内部运作机制,这非常重要。在听讲座或报告时,很容易错过深入理解概念实际如何工作所需的细节。只有通过自己实现这些概念,才能很好地理解之前遗漏了什么。实现过程能让我们精确地定位自己不理解或仍有欠缺的地方,这就是其重要性所在。

我们使用这个教育框架的另一个原因是它非常紧凑,让我们能够理解每一行代码。它只有不到150行的Python/NumPy代码,没有使用任何其他库,因此我们可以轻松地逐行查看并尝试理解每一行代码的作用。

框架设计概览

让我们更详细地看看这个教育框架。它使用Python,这意味着变量绑定到对象。例如,我们有描述输入、标签或计算图中特定计算节点的变量,也有作为这些变量属性的值。

计算图中不同类型的节点被实现为类。我们有输入类、参数类和计算节点类。之前介绍计算图时,我们为图中不同类型的节点关联了不同的颜色:输入节点为绿色,参数节点为橙色,计算节点为红色。现在,我们为这些节点中的每一个定义类模板,以便我们可以从这些类模板继承更具体的实现。

例如,对于计算节点,我们可以继承一个名为 Sigmoid 的类,它实现了逻辑回归模型或深度神经网络中的S型非线性激活功能。

核心类定义

以下是基类的抽象定义:

输入类 有一个初始化函数和一个用于添加梯度的函数(为空),因为我们不关心向输入变量添加梯度,也因为输入变量不依赖于任何其他变量。

参数类 有一个初始化函数,用于复制并存储该参数的值,并将这些参数追加到一个全局参数向量中,因为稍后我们需要对这些参数求和以获得计算结果。它还有一个用于累加梯度的函数,该函数简单地沿数据维度对所有梯度求和(我们稍后会解释)。参数类还有一个更新参数函数,用于沿梯度方向更新参数,这是在梯度下降中使用的。

计算节点类 也有一个添加梯度的函数,因为我们也需要更新计算节点本身的梯度。

前向与反向传播执行

为了执行计算图,我们需要定义前向函数和反向函数。

前向函数接收输入 xy 以及参数的当前状态 W,并从左到右遍历计算图,计算所有中间值,直到头节点 L(本例中为损失)。我们将所有这些计算节点存储在一个列表 comp_nodes 中,并以从左到右的方式存储它们。这意味着当我们遍历这个排序列表中的任何特定节点时,我们知道所有先前的节点都已被计算,因此我们可以利用它们的值。

因此,在反向传播算法的前向传递中,我们只需遍历排序后的计算节点列表,并对每个计算节点应用在该节点内部实现的前向函数。

对于反向传递,我们首先将所有计算节点和所有参数的梯度设置为0。然后我们反向遍历(这是Python中反向遍历排序列表的表示法),从 L 开始,通过迭代调用每个计算节点的反向函数,将梯度向后传播,向其父节点发送梯度更新。我们将在下一张幻灯片中看到这一点。

最后,我们有一个更新参数函数,它遍历参数列表,并为每个参数调用更新参数函数。

向量化计算的重要性

需要说明的是,前向和反向函数分别在整个数据集上计算前向和反向传递。这样做的原因是矩阵运算非常高效,因为它们在Python中得到了非常高效的实现。而如果我们使用循环遍历数据集,速度会慢得多。因此,利用这种向量化非常重要。

此外,如果我们有GPU硬件可用,我们甚至可以并行化这个计算,因为每个单独数据点的前向传递是相互独立的,每个单独数据点的反向传递也与其他数据点的反向传递独立。因此,我们可以使用并行计算硬件并行运行所有这些前向和反向传递。

计算节点实例:Sigmoid函数

让我们看一个计算节点的具体例子:Sigmoid函数。其公式为 σ(x) = 1 / (1 + e^{-x}),Sigmoid函数的梯度可以简单地用Sigmoid函数本身来表示:σ'(x) = σ(x) * (1 - σ(x)),这带来了一种高效的实现。

右侧可以看到Sigmoid类的Python定义,它继承自 ComputationNode 类。它有三个函数:初始化函数、前向函数和反向传播函数。

在初始化时,我们简单地将节点本身添加到计算节点列表中,并在该类中存储其父节点(self.x = x)。在反向传递中,我们实现上述梯度函数,并将其与节点本身的反向传播梯度(self.grad)相乘,然后将此结果作为消息进一步传递给父节点。这就是我们在上一单元讨论的内容:我们不是对类本身执行操作,而是查看存储在 self.x 中的父节点,并向该计算节点 X 添加一个梯度。我们添加的正是我们发送的这个消息,也就是我们在上一单元用红色和蓝色说明的梯度的全局分量和局部分量。

全局分量是 self.grad(Sigmoid计算节点本身的梯度),局部分量是Sigmoid关于 x 的导数(即 σ(x) * (1 - σ(x)))。这里我们可以利用导数可以用Sigmoid本身表示这一事实,因此我们可以简单地利用前向传递中计算好的值。

需要注意的是,在这个反向路径中,梯度被发送到了父节点 self.x

完整示例:组装与运行

现在让我们把所有内容整合起来,执行一个具体的最小示例。这是一个非常小的多层感知机(我们实际上会在下一讲讨论),但你可以将其视为两个堆叠在一起的逻辑回归模型。

右侧再次可以看到源代码,左侧是描述性说明。

我们首先导入教育框架,然后清空计算图(在执行任何计算图操作之前,我们都应该这样做)。接着加载数据:对于 x.value(输入类的一个实例),我们加载数据;对于 y.value,我们加载标签。

然后我们初始化两组参数,更具体地说,是两组仿射变换参数 params1params2。这些参数接收输入节点数和该特定层的输出节点数作为输入。在这个简单的模型中,我们从 N_input 个输入节点到 N_hidden 个隐藏节点,再到 N_labels 个输出标签。

根据这些定义,我们可以定义计算图本身,它简单地表示为函数的串联,非常优雅。在这个特定示例中,我们有一个依赖于参数和输入 x 的仿射变换,后跟一个Sigmoid非线性激活,这产生了隐藏层 h。隐藏层 h 是另一个仿射变换的输入,后跟一个Softmax变换(我们尚未在本讲座中讨论Softmax变换,这将在讨论多类分类问题时介绍)。Softmax的输出是一个概率向量 p,然后我们可以将其输入到对数损失(即 p 和标签 y 之间的交叉熵函数)L 中。

这样就指定了计算图。然后,我们可以通过循环所有迭代来执行梯度下降算法:运行前向传递,然后从头节点 L(损失)开始运行反向传递,最后更新参数,并持续迭代,直到我们观察到参数的变化小于某个阈值。


本节课总结:在本节课中,我们一起学习了如何通过一个精简的教育框架(EDF)在代码层面实现计算图上的梯度下降。我们了解了框架的核心类结构(输入、参数、计算节点),明确了前向传播与反向传播的执行流程,并强调了向量化计算的重要性。最后,我们通过一个简单的多层感知机示例,看到了如何将这些组件组装起来并运行完整的训练循环。这个实践过程有助于我们深入理解深度学习框架底层的运作机制。

018:张量反向传播 🧮

在本节课中,我们将学习深度神经网络的基础。我们将从回顾反向传播算法开始,并将其扩展到处理向量和矩阵(统称为张量)的运算。理解张量上的反向传播是构建和训练复杂神经网络模型的关键。

上一节我们介绍了标量计算图上的反向传播,本节中我们来看看如何将相同的原理应用于处理向量和矩阵的运算。

计算图回顾 📊

反向传播算法的目标,是计算损失函数相对于计算图中任何变量的梯度。其过程分为两步:

  1. 前向传播:按照计算顺序(黑色箭头方向)计算每个变量的值。
  2. 反向传播:从损失函数节点开始,沿反向应用链式法则,将梯度传播回图中的所有节点。

在实现上,我们将计算图中的每个变量视为一个对象,它拥有 梯度 两个属性。前向传播计算 ,反向传播计算 梯度

从标量到张量 🚀

之前我们处理的是标量运算。例如,一个简单的变换:y = sigmoid(w*x + b),其中 x, w, b, y 都是标量。

然而,在深度神经网络中,我们更常处理向量和矩阵。例如,一个仿射变换接一个Sigmoid激活函数:
y = sigmoid(A * x + b)
这里,x 是输入向量,A 是权重矩阵,b 是偏置向量,y 是输出向量。Sigmoid函数是逐元素应用的。

一个重要的观察是:在深度学习中,计算图的最终输出(损失函数)几乎总是一个标量。这意味着,损失函数 L 相对于图中任何变量 V 的梯度 dL/dV,其形状(维度)与 V 本身相同。例如,如果 A 是一个 3x3 的矩阵,那么 dL/dA 也是一个 3x3 的矩阵。

核心原理:分解为标量运算 🔍

处理张量反向传播的关键在于认识到:任何向量/矩阵运算都可以写成标量运算的循环

以前面的仿射变换为例,其前向传播可以分解为以下标量运算的循环:

# 假设 u = A * x, y = sigmoid(u + b)
# 初始化向量 u
for i in range(u_dim):
    u[i] = 0

# 计算矩阵向量乘法 A*x
for i in range(A_rows):
    for j in range(A_cols):
        u[i] += A[i, j] * x[j]

# 计算 y = sigmoid(u + b)
for i in range(y_dim):
    y[i] = sigmoid(u[i] + b[i])

既然所有运算都已被分解为标量操作,我们就可以直接应用之前为标量推导的链式法则和反向传播规则。梯度计算被简化为对每个标量索引应用相同的规则。

例如,在反向传播中计算 dL/dA[i,j]

  1. 它只依赖于那些在计算 u[i] 时使用了 A[i,j] 的路径。
  2. 根据链式法则:dL/dA[i,j] = (dL/du[i]) * (du[i]/dA[i,j])
  3. 其中 du[i]/dA[i,j] = x[j]
  4. 因此,dL/dA[i,j] = (dL/du[i]) * x[j]

通过对所有索引 i, j 进行循环,我们就可以计算出整个梯度矩阵 dL/dA。这种方法可以推广到任意维度的张量。

小批量处理:提升效率的关键 ⚡

在深度学习中,我们希望尽可能利用硬件(如CPU的SIMD指令或GPU的并行核心)来加速计算。直接使用Python循环执行标量运算效率极低。

因此,我们引入了小批量处理。核心思想是:一次性将多个数据样本(一个批次)输入计算图,并并行计算它们的损失和梯度

对于之前的仿射变换例子,假设我们有 N 个数据点。我们可以将输入组织成一个矩阵 X,其中每一行是一个数据样本:
X 的形状为 [N, input_dim]

那么,对整个批次的变换可以写为高效的矩阵运算:
Y = sigmoid(X @ A.T + b)
这里 @ 表示矩阵乘法,b 通过广播机制加到每个样本上。Y 的形状为 [N, output_dim]

在反向传播时,我们一次性计算损失函数 L 对整个批次数据的平均梯度。根据标量循环原理推导出的梯度公式,现在需要对应地使用矩阵运算来实现:

  • dL/dX = dL/dY @ A
  • dL/dA = X.T @ dL/dY
  • dL/db = sum(dL/dY, axis=0) (对批次维度求和)

这些矩阵运算能被NumPy、PyTorch等框架极度优化,在GPU上并行执行,速度比纯Python循环快数个数量级。

总结 📝

本节课中我们一起学习了张量上的反向传播。

  1. 基本原理:通过将张量运算分解为标量运算的循环,我们可以将标量反向传播的链式法则直接应用过来。
  2. 维度保持:由于损失函数是标量,任何变量的梯度都与该变量本身形状相同。
  3. 高效实现:小批量技术允许我们使用高效的矩阵/向量库(如NumPy、CUDA)并行处理多个样本,这是现代深度学习得以快速训练的核心。
  4. 实践注意:虽然从标量循环的角度理解梯度计算很简单,但将其转化为最高效的张量运算实现(如特定的矩阵乘法、转置和求和)需要专业知识。深度学习框架为我们封装了这些复杂的细节。

理解张量反向传播是打开深度神经网络大门的第一把钥匙。在接下来的章节中,我们将利用这个工具来构建第一个真正的深度网络——多层感知机。

019:XOR问题与深度神经网络 🧠

在本节中,我们将探讨线性分类器的表达能力及其局限性。我们将从一个简单的逻辑回归模型出发,分析它能解决哪些问题,并最终揭示其核心限制——无法解决线性不可分问题,例如经典的XOR(异或)问题。为了解决这个问题,我们将引入多层感知机(MLP)的概念,这是构建深度神经网络的基础。

线性分类器的决策边界

首先,回顾上一讲中的逻辑回归模型。该模型在权重向量 w 和输入向量 x 之间进行线性(或仿射)变换,得到一个标量。这个标量随后通过一个Sigmoid非线性函数,将其从实数轴映射到0到1的范围内。

公式z = w^T x + w_0y_hat = σ(z) = 1 / (1 + e^{-z})

这个介于0和1之间的结果被解释为输入 x 属于类别1的概率。现在的问题是:如此简单的线性分类器能解决哪些问题?

为了理解这一点,我们考虑一个二维逻辑回归问题。模型有三个参数:权重向量 w 的两个维度分量 w_1w_2 以及偏置项 w_0。模型的决策边界位于 z = 0 处,因为此时Sigmoid函数的输出恰好为0.5。换句话说,当 w^T x + w_0 > 0 时,我们判定为类别1;反之,则判定为类别0。

公式

  • 决策边界:w^T x + w_0 = 0
  • 判定为类别1:w^T x + w_0 > 0
  • 判定为类别0:w^T x + w_0 < 0

在二维输入空间中,这个决策边界是一条直线,它将空间一分为二。

线性分类器能解决的简单问题

现在,让我们看一些这个简单分类器可以解决的具体问题,例如一些基本的逻辑(布尔)运算符。我们使用二维输入,共有四种可能的输入组合:(0,0), (0,1), (1,0), (1,1)。

以下是OR(或)运算符的真值表及其在二维空间中的可视化:

  • OR运算符:当任一输入为1时,输出为1。

    • 目标:找到参数 ww_0,使得分类规则能正确分离类别0(红色点 (0,0))和类别1(绿色点 (0,1), (1,0), (1,1))。
    • 通过观察,我们可以找到一条直线(决策边界)来分离它们。例如,选择 w = [1, 1]^T,-w_0 = 0.5(即 w_0 = -0.5)。可以验证,这个设置能正确分类所有点。
  • AND(与)运算符:仅当两个输入均为1时,输出为1。

    • 同样,决策边界的法线方向与OR类似,但偏置项需要改变。可以选择 w = [1, 1]^T,-w_0 = 1.5(即 w_0 = -1.5)。
  • NAND(与非)运算符:AND运算结果的取反。

    • 这可以通过反转AND运算符的决策边界来实现。例如,选择 w = [-1, -1]^T,-w_0 = -1.5(即 w_0 = 1.5)。

由此可见,简单的线性分类器可以表示一系列基本的逻辑运算。

线性分类器的局限:XOR问题

然而,当我们面对XOR(异或)运算符时,情况变得棘手。XOR的定义是:当两个输入不同时,输出为1;相同时,输出为0。

从可视化中可以看出,两个红色点((0,0)和(1,1))彼此对立,两个绿色点((0,1)和(1,0))也彼此对立。无法找到一条直线将红色点和绿色点完全分开。无论是OR还是NAND的决策边界,都只能正确分类三个点,而错误分类第四个点。

这引出了一个关键结论:XOR问题是线性不可分的

线性不可分的正式证明

我们可以更正式地理解这一点。首先,回顾一下凸集的定义:一个集合S是凸集,如果连接其中任意两点的线段上的所有点也都属于S。

公式:若 x1, x2 ∈ S,则对任意 λ ∈ [0, 1],有 λx1 + (1-λ)x2 ∈ S

线性分类器的决策区域(即判定为某个类别的所有点构成的区域)是半空间。半空间是由一条直线界定的无限区域,它是一个凸集。

现在,假设存在一个线性分类器能解决XOR问题。那么所有绿色点(类别1)必须位于同一个半空间(正半空间)内,所有红色点(类别0)位于另一个半空间(负半空间)内。由于半空间是凸集,连接两个绿色点的线段上的所有点也必须是绿色(类别1)。同样,连接两个红色点的线段上的所有点也必须是红色(类别0)。

观察XOR的四个点,连接(0,1)和(1,0)的线段,与连接(0,0)和(1,1)的线段,会相交于中点(0.5, 0.5)。根据凸集性质,这个交点必须同时属于“绿色线段”和“红色线段”,即它必须同时被分类为类别1和类别0,这是矛盾的。因此,这样的线性分类器不存在。

历史背景与启示

这一局限性在历史上具有重要意义。尽管线性分类器(如感知机)在20世纪50-60年代在图像分类等问题上显示出潜力,但其无法解决XOR这类简单非线性问题的缺陷很快暴露出来。明斯基和帕佩特在1969年的《感知机》一书中详细论述了这些限制,导致神经网络研究在70年代陷入低潮。

然而,现实世界中的问题大多是非线性的。那么,我们该如何解决非线性问题呢?

解决方案:特征变换与多层感知机

方法一:手工特征工程

一种思路是模仿多项式曲线拟合,将数据映射到更高维的特征空间。例如,对于XOR的二维输入 x = [x1, x2]^T,我们可以构造一个三维特征向量 ψ(x) = [x1, x2, x1*x2]^T

在这个三维空间中,原本线性不可分的点变得可以用一个平面(三维空间中的线性决策边界)来分离。这类似于支持向量机(SVM)中使用核函数的思想。

另一个例子是,在原始笛卡尔坐标 (x, y) 中线性不可分的数据,转换到极坐标 (r, θ) 空间后,可能变得线性可分。这说明了数据表示的重要性

然而,手工设计有效的特征变换非常困难,且依赖于领域知识。在深度学习兴起之前,这曾是计算机视觉等领域的主流方法(如设计SIFT特征描述子)。

方法二:表示学习与多层感知机

更强大的方法是让算法自动学习合适的特征表示,这就是表示学习的核心思想。我们不再手动指定特征变换函数,而是设计一个包含可学习参数的函数族(例如神经网络),并通过优化算法(如梯度下降)从数据中学习出最佳的函数。

回到XOR问题,我们如何用学习的方式解决它呢?观察发现,XOR运算可以通过组合我们已经能用线性分类器实现的OR、NAND和AND运算来表达:

公式XOR(x1, x2) = AND( OR(x1, x2), NAND(x1, x2) )

这意味着我们可以构建一个计算图(神经网络)来实现这个逻辑:

  1. 第一层(隐藏层):并行计算 h1 = OR(x1, x2)h2 = NAND(x1, x2)。这里 h1h2 是隐藏单元,我们观测不到它们的真实值,但网络会计算它们。
  2. 第二层(输出层):计算 y_hat = AND(h1, h2),得到最终的XOR预测。

由于OR、NAND、AND都可以用逻辑回归单元(线性变换+Sigmoid激活)实现,我们可以将这个计算图具体化为:

代码/公式描述

  • 隐藏层:h = σ(W * x + b)。其中 W 是一个2x2矩阵(堆叠了OR和NAND的权重向量),b 是偏置向量。σ 是Sigmoid函数,按元素作用。
  • 输出层:y_hat = σ(w_out^T * h + b_out)。其中 w_outb_out 是AND运算的参数。

这就构成了一个多层感知机(MLP)——我们第一个真正的深度神经网络模型!它包含一个输入层、一个隐藏层和一个输出层。关键在于,隐藏层 h 是输入 x 的一个非线性变换,它学习到了一个使原始问题(在 x 空间线性不可分)在新空间(h 空间)中变得线性可分的表示。

这个模型的优美之处在于它是完全可微的(使用了Sigmoid而非不可微的阶跃函数)。因此,我们可以随机初始化所有参数(W, b, w_out, b_out),然后利用训练数据(XOR的四个样本),通过梯度下降算法和反向传播算法来优化这些参数,从而自动学习到解决XOR问题的网络权重,而无需我们手动设置。

总结

本节课我们一起深入探讨了线性分类器的能力与局限。我们首先看到线性模型可以解决如OR、AND等线性可分问题。随后,我们遇到了经典的XOR问题,并从几何和凸集理论的角度理解了其线性不可分的本质。为了突破这一限制,我们介绍了两种思路:一是手工进行特征空间变换,二是构建多层感知机进行表示学习。我们重点展示了如何通过组合简单的线性分类器(逻辑回归单元)来构建一个能解决XOR问题的多层神经网络,并指出这种模型可以通过梯度下降进行端到端训练,自动学习有效的特征表示。这为后续深入学习复杂的深度神经网络奠定了重要基础。

020:多层感知机 🧠

在本节课中,我们将正式介绍多层感知机。这是一种前馈神经网络,通过堆叠多个非线性层来学习数据的复杂表示,是深度学习的核心架构之一。

什么是多层感知机?

上一节我们介绍了神经网络的基本单元。本节中,我们来看看如何将这些单元组合成更强大的模型。

多层感知机是一种前馈神经网络。“前馈”意味着网络结构是分层的,且层与层之间没有反馈连接。信息从输入层开始,单向地向前传递,经过一系列隐藏层,最终到达输出层。

它由多个非线性函数组合而成。例如,一个函数 ( F(x) ) 可以被分解为多个隐藏层函数的组合:
[
F(x) = f_3(f_2(f_1(x)))
]
其中,( f_1, f_2, f_3 ) 代表不同的隐藏层。

  • 隐藏层:所有非输出层的层都被称为隐藏层。它们不直接接收训练标签(监督信号),其目标是学习对最终预测或分类有用的特征表示。
  • 神经元:每一层都由多个神经元组成。每个神经元执行一个“仿射变换 + 非线性激活函数”的操作。
  • 全连接:在多层感知机中,每一层的每个神经元都与前一层的所有神经元相连接。这是其架构的一个关键特征。
  • 深度:模型中计算链的长度称为模型的深度。堆叠多个隐藏层就得到了“深度”模型,这也是“深度学习”一词的由来。

注意:“多层感知机”这个名称可能有些误导。现代神经网络使用反向传播训练,并采用可微的非线性激活函数(如Sigmoid、ReLU),这与早期不可微的阈值单元“感知机”算法不同。因此,它更像是阈值单元的平滑近似。

网络架构解析

理解了基本概念后,我们来具体看看一个多层感知机的结构是什么样的。

下图展示了一个典型的多层感知机架构示例:

以下是该架构的关键组成部分说明:

  • 输入层:位于最左侧,接收原始数据。
  • 隐藏层:图中展示了三个隐藏层。同一层内的神经元之间没有连接。
  • 输出层:位于最右侧,产生最终预测。
  • 连接方式:每一层的神经元都与前一层的所有神经元全连接。例如,第二个隐藏层的某个神经元会接收第一个隐藏层所有三个神经元的输入。
  • 深度:深度指计算层的数量(不包括输入层)。图中网络有3个隐藏层和1个输出层,因此深度为4。
  • 宽度:宽度指一层中神经元的数量。例如,第一个隐藏层宽度为3,第二个为2。

网络的深度和宽度是核心的设计参数,我们可以根据具体任务(如数据量大小、问题复杂度)自由选择。对于简单任务和小数据,通常使用参数更少的浅层或窄网络以防止过拟合。

向量化表示与表示学习

从计算视角看,每一层实际上是在对数据表示进行变换。

在向量表示下,一个隐藏层 ( \mathbf{h}^{(l)} ) 的计算可以写为:
[
\mathbf{h}^{(l)} = g{(l)}(\mathbf{A} \mathbf{h}^{(l-1)} + \mathbf{b}^{(l)})
]
其中:

  • ( \mathbf{h}^{(l-1)} ) 是上一层的输出向量。
  • ( \mathbf{A}^{(l)} ) 和 ( \mathbf{b}^{(l)} ) 是该层的权重矩阵和偏置向量(参数)。
  • ( g^{(l)}(\cdot) ) 是逐元素应用的非线性激活函数。

这种层级变换的视角引出了表示学习的核心思想。假设我们将最后一个隐藏层的输出记为 ( \psi(x) ),即输入 ( x ) 学习到的新表示。

在分类任务中,输出层通常就是一个逻辑回归分类器(线性分类器)。因此,前面所有隐藏层的目标,就是将原始数据 ( x ) 变换成一个新的表示空间 ( \psi(x) ),使得在这个新空间中,不同类别的数据变得线性可分

理想情况下,随着数据通过每一层,其表示会变得越来越易于区分,最终在最后一个隐藏层形成线性可分的格局,以便输出层轻松完成分类。下图直观展示了这一过程:通过多层变换,原始空间中复杂的决策边界(红色曲线)在表示空间中变成了一个简单的线性边界。

激活函数

激活函数的非线性是网络获得强大表达能力的关键。以下是一些常用的激活函数:

  • Sigmoid:早期受神经科学启发,但容易饱和,导致梯度消失,训练缓慢。
  • ReLU(整流线性单元):目前最流行的激活函数之一。公式为 ( f(x) = \max(0, x) )。它在正区间梯度恒定,能有效缓解梯度消失问题,加速训练。虽然它在 ( x=0 ) 处不可微,但在实践中表现良好。
  • Leaky ReLU 等:ReLU 的变体,旨在解决神经元“死亡”问题。

选择不同的激活函数会显著影响网络的训练动态和最终性能。

神经科学启发(简要)

这些模型被称为“神经网络”是因为其设计受到了大脑的启发。大脑中的神经元也是分层结构,每个神经元接收来自其他多个神经元的输入信号,进行整合(类似线性组合),然后通过轴突产生输出(类似激活函数)。突触的强度决定了信号传递的权重。

然而,本课程的核心目标不是模拟大脑。我们关注的是如何构建具有强大统计泛化能力的模型。现代深度学习的架构和训练方法(如反向传播)与大脑的实际运作方式有根本性不同。我们只是从大脑的结构中获得灵感,旨在开发出在任务上表现优异的实用模型。

如何训练多层感知机?

训练多层感知机,我们使用随机梯度下降及其变种。

  • 动机:对于大型数据集(如数百万张图片),计算整个数据集的梯度(批量梯度下降)效率极低。随机梯度下降通过每次迭代随机抽取一小部分数据(小批量)来计算梯度,这是一种对真实梯度的有噪估计,但计算效率高,且随机性有时有助于跳出局部极小值。
  • 算法步骤
    1. 初始化:随机初始化所有权重参数,设定学习率和小批量大小。
    2. 循环
      a. 采样:从数据集中随机抽取一个小批量数据。
      b. 前向传播:将小批量数据输入网络,计算每一层的激活值以及最终的输出 ( \hat{y} )。
      c. 反向传播:计算损失函数关于所有参数(各层的 ( \mathbf{A} ) 和 ( \mathbf{b} ))的梯度。
      d. 参数更新:沿着梯度负方向,以学习率为步长,更新所有参数。
    3. 终止:在独立的验证集上监控性能。当验证误差不再下降(可能开始过拟合)时停止训练。

关于小批量的说明:由于GPU内存限制,小批量大小通常远小于数据集总大小。但在后续演示的小型例子中,我们可以使用整个数据集作为“批量”。

实践演示:解决XOR问题

现在,让我们运用所学知识来解决经典的XOR(异或)问题。我们使用一个在线演示工具来训练一个简单的MLP。

我们构建一个具有以下结构的网络:

  • 输入层:2个神经元(对应XOR的两个输入)。
  • 隐藏层:1层,包含2个神经元(使用 tanh 激活函数,因其收敛性优于Sigmoid)。
  • 输出层:1个神经元(使用 softmax,在此二分类情况下等价于Sigmoid)。

我们并不手动设置权重来模拟AND/OR门,而是随机初始化权重,然后让随机梯度下降算法自动学习。经过训练,网络成功地学习到了一个决策边界,将XOR问题的四个点正确地分为两类(红 vs 绿),验证了MLP解决非线性可分问题的能力。

进一步,我们可以尝试更复杂、数据点更多且更不易分离的数据集。通过调整隐藏层的宽度(神经元数量),可以观察到模型复杂度的变化:

  • 神经元较少时,决策边界相对简单。
  • 随着神经元数量增加(例如增加到50个),决策边界变得非常复杂,几乎可以完美拟合训练数据,但很可能导致过拟合

这说明网络架构本身(如层宽、层深)就是一种正则化手段。除了显式的权重衰减(后续会讲),简单地减少神经元数量也是控制模型复杂度、防止过拟合的有效方法。

表达能力与非线性激活的重要性

最后,我们来探讨多层感知机的表达能力。一个两层MLP的方程可以写为:
[
\hat{y} = g_2(\mathbf{A}_2 \cdot g_1(\mathbf{A}_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2)
]

关键问题:如果我们使用线性激活函数(即 ( g(x) = x ) )会怎样?更深的网络会比单层线性分类器表达能力更强吗?

答案是否定的。如果所有激活函数都是线性的,那么无论网络多深,整个模型都可以被简化为一个单一的线性变换:
[
\hat{y} = \mathbf{A}_2 (\mathbf{A}_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2 = (\mathbf{A}_2 \mathbf{A}_1) \mathbf{x} + (\mathbf{A}_2 \mathbf{b}_1 + \mathbf{b}_2) = \mathbf{A}’ \mathbf{x} + \mathbf{b}’
]
这并没有引入任何新的表达能力,只是增加了冗余参数,可能使优化更困难。

结论非线性激活函数是深度网络获得强大表达能力的根本原因。没有它们,深层网络等价于浅层线性模型。

那么,对于具有非线性激活函数的MLP,其模型能力如何?是更深更好,还是更宽更好?需要多深才能表达任意复杂的函数?这将是下一单元要讨论的核心内容。


本节课总结

本节课中,我们一起学习了:

  1. 多层感知机的基本定义和核心特性:前馈、全连接、包含隐藏层。
  2. 网络的深度宽度是重要的设计参数。
  3. 表示学习的角度理解MLP:每一层都在学习将数据向更线性可分的方向变换。
  4. 常用激活函数(如ReLU)的作用及其重要性。
  5. 使用随机梯度下降反向传播来训练MLP的流程。
  6. 通过实践看到MLP可以解决XOR等非线性问题,并理解了架构本身是一种正则化
  7. 明确了非线性激活函数是深度模型超越线性模型表达能力的必要条件。

021:通用逼近定理 🧠

在本节课中,我们将要学习多层感知机的表达能力。具体来说,我们将探讨一个被称为“通用逼近定理”的重要理论结果,它解释了为什么即使只有一个隐藏层的神经网络,也能以任意精度逼近任意连续函数。我们还将讨论该定理的局限性,以及为什么在实践中,深度(即更多层)比宽度(即每层更多神经元)更为重要。


通用逼近定理 📜

上一节我们介绍了神经网络的基本结构。本节中,我们来看看一个关于其表达能力的核心理论:通用逼近定理。

该定理指出,一个仅包含单个隐藏层的多层感知机,只要其隐藏层神经元数量足够多,就能够以任意精度逼近任意定义在紧致集上的连续函数。这是一个非常深刻的结果,也是深度学习的理论基础之一。

该定理最早由Cybenko等人证明,随后被其他研究者在更一般的情况下推广。其数学表述如下:

设 σ 是任意连续且有界的“挤压”函数(例如Sigmoid函数)。那么,形如下式的有限和:

G(x) = Σᵢ [ cᵢ · σ( Aᵢ x + bᵢ ) ]

在连续函数空间 C(Iⁿ)(定义在n维单位立方体 Iⁿ 上)中是稠密的。换言之,对于该空间中的任意连续函数 f(x) 和任意精度 ε > 0,总存在一个如上定义的函数 G(x),使得对于单位立方体中的所有 x,都有:

| G(x) - f(x) | < ε

该定理的严格证明涉及测度论,超出了本课程的范围。因此,我们将跳过形式化证明,转而通过一些具体案例来直观理解其为何成立。


从离散(二进制)情况理解 🧩

为了获得直观理解,我们首先观察一个更简单的情况:离散的二进制输入/输出函数。

假设我们有一个三维二进制输入 x = (x₁, x₂, x₃),每个分量取值0或1,因此共有 2³ = 8 种可能的输入组合。我们想用一个单隐藏层的MLP来预测一个一维的二进制输出 y

我们暂时假设隐藏层神经元是线性阈值单元,其函数形式为:

h(x) = 𝟙[ A·x + b > 0 ]

其中,𝟙[·] 是指示函数(条件为真时输出1,否则输出0),A 是权重向量,b 是偏置。这本质上是一个阶跃函数。

以下是关键思路:我们可以设计网络,让每个隐藏神经元专门识别一种特定的输入组合。

例如,要识别输入向量 (0, 1, 1) 并输出1,我们可以设置一个神经元的参数如下:

  • 权重 A = [-1, 1, 1]
  • 偏置 b = -0.5

计算过程如下:

  • 当输入为 (0,1,1) 时:A·x + b = (-10)+(11)+(1*1)-0.5 = 1.5,大于0,神经元激活(输出1)。
  • 当输入为 (1,1,1) 时:(-11)+(11)+(1*1)-0.5 = 0.5,仍大于0。为了确保只对(0,1,1)激活,我们需要更精细地调整偏置,例如设为 -1.5,这样(1,1,1)的输入和为 0.5,将不大于0。

通过这种方式,我们可以为8种可能的输入组合各设置一个神经元。然后,输出层只需根据这些神经元的激活状态,组合出最终结果 y

然而,这种方法存在一个明显问题:对于 d 维二进制输入,我们需要 2ᵈ 个隐藏神经元。这是一个指数级的增长,当输入维度稍高时,模型将变得不可行。这引出了该定理的第一个重要局限。


从硬阈值到软阈值 🔄

上一节我们使用了不可微的硬阈值单元,这不利于基于梯度下降的学习。本节中我们来看看如何用可微的“软”阈值单元来解决这个问题。

解决方案是使用如Sigmoid这样的可微激活函数来近似硬阈值函数。观察Sigmoid函数 σ(x) = 1 / (1 + e⁻ˣ),我们可以通过调整其输入尺度来使其变得“更陡峭”。

以下是不同尺度参数下的Sigmoid函数:

  • σ(x):标准的S形曲线。
  • σ(5x):曲线变得更陡,更接近阶跃函数。
  • σ(50x):曲线几乎与阶跃函数无法区分。

因此,我们拥有一个可微的“软”阈值替代品。通过使用足够大的权重,我们可以让Sigmoid函数在训练中表现得像一个可微的阶跃函数,从而既能利用通用逼近定理的理论保证,又能使用梯度下降算法进行有效训练。


通用性的局限与深度的重要性 ⚠️➡️🚀

虽然通用逼近定理表明宽而浅的网络(单隐藏层)是“万能”的,但这在实践中存在重大局限。

以下是通用逼近定理的几个关键局限:

  1. 需要指数级宽度:如上文二进制例子所示,逼近复杂函数可能需要隐藏层神经元数量随输入维度指数增长,导致计算和内存成本不可接受。
  2. 无法保证泛化:“万能”仅代表表示能力,不代表学习能力。一个拥有海量参数的宽网络很容易简单地记忆训练数据,导致严重的过拟合,而无法在未见过的测试数据上(即泛化)表现良好。

那么,什么才是更有效的路径呢?近二十年的研究揭示,网络的深度(层数)比宽度(每层神经元数)更为关键

深度网络引入了一个强大的归纳偏置复杂的函数可以由一系列更简单的函数组合(嵌套)而成。这与我们对自然世界的认知相符(复杂系统由简单模块层级化构成),也使得深度网络能够:

  • 更紧凑地表示复杂函数:用更少的参数实现相同的功能。
  • 获得更好的泛化性能:经验表明,深度网络在测试集上的表现通常远优于同等参数量的浅层网络。

一个经典的例子是奇偶校验函数(判断二进制向量中1的个数是否为奇数)。可以证明,用浅层网络实现该函数需要指数级数量的神经元,而用一个深层网络则只需要线性数量的神经元。


深度为何有效:空间折叠的直观解释 📐

为什么深度比宽度更有效?这里提供一个基于特定激活函数(绝对值整流单元)的几何直观解释,称为“空间折叠”。

想象输入数据所在的空间是一张白色的纸(以二维为例)。每个使用绝对值激活的神经元,其作用就像沿着由权重和偏置定义的“镜面轴线”对空间进行镜像折叠

通过叠加多个这样的层(即增加深度),我们可以对这张“纸”进行多次折叠。每一次折叠都在空间中创建新的对称轴。仅仅通过几层这样的操作,就能快速创造出非常复杂的对称模式,而要使用单层网络(仅增加宽度)来描绘同样的复杂模式,则需要指数级多的神经元。

这个直觉表明,深度网络通过层级化的“折叠”或“变换”,能够高效地构建出复杂的函数形态。对于ReLU等更常用的激活函数,也存在类似但更复杂的几何解释。


实证结果 📊

理论需要实证支持。在Goodfellow等人的论文中,在一个多位数分类任务上进行了实验。

实验结果清晰地显示:

  • 随着隐藏层数量的增加(深度增加),测试准确率持续提升。
  • 更深网络的性能上限显著高于浅层网络。

当然,训练深度网络也存在挑战,例如梯度消失/爆炸问题,这需要通过归一化、残差连接等技术来解决(将在后续课程讨论)。但总体而言,只要设计得当,从深度获得的收益是仅增加宽度无法比拟的。

另一项对比实验进一步证实了这一点。在卷积神经网络(一种具有强大归纳偏置的深度网络架构)中:

  • 对于一个3层的网络,当参数数量增加到约2000万时,由于过拟合,测试准确率开始下降。
  • 对于一个11层的更深网络,即使参数数量继续增加,测试准确率仍然在提升,表现出更好的泛化能力。

这些实证结果强有力地表明,函数的可组合性(深度所强化的偏置)是模型所能学习函数空间上一个非常有用的先验假设


总结 ✨

本节课中我们一起学习了:

  1. 通用逼近定理:单隐藏层的多层感知机,在宽度足够时,可以逼近任何连续函数。
  2. 定理的直观理解:通过二进制例子,我们看到了如何用隐藏神经元“记忆”特定模式,但也揭示了其需要指数级宽度的局限。
  3. 从硬阈值到软阈值:使用Sigmoid等可微激活函数,可以在保持逼近能力的同时实现梯度下降训练。
  4. 深度的重要性:通用逼近定理在实践中受限于过拟合和计算成本。深度网络通过引入“函数可组合”的强归纳偏置,能够用更少的参数更紧凑地表示函数,并获得更优的泛化性能。
  5. 深度有效的直观解释:“空间折叠”等概念为深度网络的高效性提供了几何视角。
  6. 实证证据:实验结果表明,增加网络深度通常比增加宽度更能有效提升模型性能。

总而言之,虽然浅层网络在理论上是“万能”的,但正是深度赋予了现代神经网络解决复杂现实问题的强大能力和效率。

022:输出与损失函数 📚

在本节课中,我们将继续学习前馈神经网络,即多层感知机。我们将深入探讨输出层和损失函数的设计,这对于构建有效的神经网络模型至关重要。上一节我们介绍了多层感知机的基本结构,本节中我们来看看如何根据不同的任务(如回归或分类)来设计合适的输出层和损失函数。

概述 📖

在上一讲中,我们讨论了多层感知机,这是前馈神经网络最基本的形式。我们看到,即使是非常简单、非常浅的(仅有一个隐藏层)网络,即基本的两层神经网络,也能够表示几乎任何函数。但我们也认识到,这不一定是个好主意。为了获得既具有强大表达能力,又能良好泛化的模型,我们需要更深的模型,这些模型利用计算的组合性,从而带来更稳健的行为。

今天,我们将继续讨论这种基本的前馈网络形式——多层感知机,并更深入地研究输出和损失函数、激活函数,以及数据预处理和权重初始化。这些都是使神经网络良好工作的重要因素。

让我们从本单元的第一部分——输出和损失函数开始。

输出与损失函数 🎯

这里我们看到了一个非常简单的前馈神经网络。我们有一个二维输入 x1x2,它输入到两个隐藏层,每个隐藏层有两个节点。这些都是一维计算节点,因此在非线性变换后,输出是一个一维量。然后我们有一个输出层,计算预测值 y_hat。接着,我们有一个损失函数,用于比较预测值 y_hat 与数据提供的目标值 y

在本单元的第一部分,我们将重点讨论输出层和损失函数。它们的选择很大程度上取决于我们希望解决的任务。例如,根据我们处理的是回归问题还是分类问题(即预测连续值还是离散值),两者都会发生变化。当然,这里是一维输出的例子,也可能预测更高维的输出,损失函数需要相应地比较预测向量和目标向量。

首先,让我们思考一下优化过程中的目标是什么?在非常抽象的层面上,我们的目标很简单:尝试使模型的输出(即我们的预测,用红色表示)与来自数据集的目标(用绿色表示)相似。这里我们看一个二维输出空间 (y1, y2) 的例子,有一个红色的预测点和一个绿色的目标点。如果我们使用欧几里得距离作为损失度量,优化器在最小化绿点和红点之间距离的过程中,会通过更新神经网络的参数,使红点逐渐靠近绿点。

从分布的角度来思考也很有用,我们将在接下来的几张幻灯片中大量使用这个概念,这也是推导特定损失函数的一个良好动机。在分布的角度下,模型可能预测一个简单的分布,例如红色的高斯分布。数据分布用绿色表示。我们试图做的是使模型分布和数据分布对齐。这里我们有一个条件预测器,我们展示的是给定某个输入 xy 的概率 P(y|x)。我们可以用例如 KL 散度来衡量这两个分布之间的差异。如果这两个高斯分布重叠很小,KL 散度会相当大;反之,如果重叠很大,KL 散度就小,这意味着分布对齐得很好。在优化过程中,我们希望从模型参数的初始状态出发,最终得到一个预测与数据对齐得很好的模型。

现在的问题当然是:如何设计一个好的损失函数?到目前为止,我们只看到了非常抽象的例子,没有明确或正式地陈述损失函数。首先,损失函数可以是任何我们希望优化的可微函数,并且能够表达我们对特定预测状态或决策所产生成本的信念。然而,我们今天要做的事情更加优雅:我们不会基于直觉来定义损失函数,而是基于最大似然原理来推导它们。这很好,因为它消除了为每个模型手动设计成本函数的负担。我们总是可以回到最大似然原理,为我们的问题推导出损失函数。这也为我们提供了与模型概率解释的良好联系。

当我们从最大似然原理考虑或推导成本函数时,我们实际上是将神经网络的输出不是看作一个点估计(预测单个值),而是看作预测一个分布。换句话说,神经网络对分布的参数进行点估计,它预测具体的值,但我们不将这些值直接视为目标 y,而是将它们视为输出 y 上某个分布的参数。这就是我们所说的模型分布 P_model

这是最大似然公式。我们想要找到参数 W_hat(ML 代表最大似然),使得模型在整个数据集上的概率最大化。这里 yX 表示整个数据集,W 是模型参数的通用表示。我们可以使用独立同分布假设来简化这个表达式为对每个输入 X 的单个预测的乘积。然后,当然,我们可以取这个表达式的对数,将乘积转换为和。因为对数函数是单调的,它不改变最大值,所以仍然是同一个问题。这被称为对数似然。在实践中我们使用对数似然,因为它使我们的实现更容易。

在上一张幻灯片中,模型是模型分布的通用描述。但现在我们如何用神经网络来表示它呢?正如我之前提到的,我们可以将神经网络视为预测分布的参数,而不是点估计 y。例如,这里的模型分布 P(我省略了下标 model)是一个高斯分布。这个高斯分布的均值 μ 被一个带参数 W 的函数所取代,该函数以 X 为输入,正是前馈神经网络或多层感知机 f_W(x)。神经网络通过这条虚线预测分布的均值 μ。然后我们想要做的是,在这个分布下最大化目标值 y(来自数据集)的概率。在这个具体例子中,我们看到目标的概率不是 0,但也不是最大。因此,模型必须改变神经网络的参数,使得对应于该目标 y 的特定输入 x 的预测更接近目标,从而使该分布下目标的概率变得更大。

让我们快速回顾一下不同类型的问题,这就是我们今天要看的。我们不会看复杂的结构化预测问题,而是看更简单的回归和分类问题。

  • 回归:输入是任意对象,输出是一个实数(例如,股票的价值)。
  • 二元分类:输入是任意对象(例如图像),输出是一个二元变量(例如,是海滩或不是海滩)。
  • 多类分类:输入是结构化对象(如图像),输出是一个离散随机变量,有多个可能的状态或标签(例如,图像被分类为描绘海滩、山脉、城市或森林)。

当我们谈论超过两个类别的分类时,我们称之为多类分类。因此,我们今天将进行这种区分,并学习如何为这些不同类型的问题定义损失函数和输出层。

回归问题 🔄

我们从回归问题开始。我们已经见过高斯分布。高斯分布由均值 μ 和标准差 σ(或方差 σ²)参数化。高斯分布的一个特点是它具有薄尾,这意味着随着 y 趋向正负无穷,y 的概率非常快地趋近于 0。这意味着该分布对异常值惩罚强烈。如果我们有一个大致遵循高斯分布的数据集,但有一个点离群很远,那么这个点会对分布的估计产生很强的影响,因为如果分布只关注所谓的“内围”正确观测值,这个异常值将具有极低的概率。这就是为什么我们也会在接下来的幻灯片中讨论一个没有这个问题的不同分布。

但让我们暂时停留在高斯分布上。这与第一讲中最大似然估计的上下文实际上是一样的,唯一的区别是现在我们有一个神经网络来预测参数 μ,而不是一个非常简单的线性模型,而是一个任意复杂的非线性变换(例如 MLP)。因此,我们假设模型分布是一个高斯分布,其中 MLP 或前馈网络预测该高斯分布的均值,而 σ 保持为某个常数值(具体哪个值实际上并不重要)。

然后我们得到,正如我们在第一讲中已经看到的,如果我们进行这些方程推导,最终会得到一个损失函数。通过改变符号,我们将最大化问题转化为最小化问题。这个量被称为损失函数,我们可以从这个表达式中读出它的样子:它是一个平方损失,因为它是模型预测 f 减去目标 y 的平方。我们对每个数据点都这样做,所以实际上是平方和。这个表达式有时也被称为 L2 损失。我们已经看到,这是一个受异常值、错误数据点强烈影响的损失。

因此,在实践中,人们经常使用不同的分布。这是一个受异常值影响较小的分布的例子,称为拉普拉斯分布。它的形式与高斯分布相似,只是现在在指数表达式中,我们有 yμ 之间的绝对差(对于拉普拉斯分布称为位置参数 μ)。与高斯分布相比,这个分布具有重尾,这意味着 P(y) 随着 y 达到正负无穷而趋近于 0 的速度要慢得多。因此,它在尾部有更多的概率质量,对异常值的惩罚较弱,通常是实践中回归问题的首选之一。

如果我们对拉普拉斯分布进行与高斯分布相同的计算,我们会得到一个损失函数。现在这个损失函数看起来与之前非常相似,只是平方被绝对值括号所取代。换句话说,我们最小化绝对误差 L1 损失,这比我们之前看到的 L2 损失更稳健。

到目前为止,我们只预测了这些分布的两个参数中的一个,但也可以预测两个参数。在这种情况下,我们再次考虑拉普拉斯分布,但我们不仅预测位置 μ,还预测尺度 b。我们用一个神经网络同时预测 μbfg 基本上是一个具有两个可能略有不同的输出头的网络。如果我们这样做,那么得到的表达式不再那么简单,因为当然我们不能忽略依赖于 w 的项,所以损失函数是更复杂的形式。但同样重要的是要强调,我们通过假设数据上的某种分布,从最大似然原理中以原则性的方式推导出了这个损失函数。如果我们对数据的分布有了解(通常情况如此),我们可以像这里一样提出一个损失函数,否则很难指定。

预测位置和尺度两个参数的优点是,我们现在可以显式地建模或要求我们的模型估计偶然不确定性。偶然不确定性基本上是观测噪声。例如,从某个观测中根本不清楚图像上是什么。如果我们预测尺度参数,模型可以学习这一点:当它确定时,模型最好预测一个小的 b,因为它可以将更多的概率质量放在正确的位置;但如果从输入中不清楚是什么,预测一个小的 b 就不是一个好主意,因为如果分布太窄而我们稍有偏差,目标 y 的概率就会非常低。这就是其背后的直觉。因此,模型现在可以在位置和尺度之间进行权衡。

最后,如果我们谈论回归和分布,我们迄今为止看到的所有分布都是单峰的,这意味着它们不能表示多模态数据分布。例如,如果你想进行深度预测,在物体边缘,你不知道该像素是属于前景还是背景,但你很确定它要么是前景要么是背景。如果你只有一个具有单峰(单个峰值)的拉普拉斯或高斯分布,就很难表达这一点,但你可以建模混合密度。这里是一个混合模型的例子,在这种情况下是两个分量的混合。它是一个简单的拉普拉斯分布,现在在分量数量上求和。因此,我们现在对 M 个分量(这里 M=2)中的每一个都预测一个均值和一个尺度,然后将它们相加,并用权重(这是模型的另一个预测,或者也可以是固定的)对它们进行加权。这被称为混合密度网络

我们还没有讨论的是,在处理回归问题时,输出层实际上应该是什么。隐藏层,到目前为止我们讨论的是仿射变换加上一些激活函数(如 sigmoid 激活函数)的简单组合。在接下来的单元中,我们将了解更多的激活函数。但是输出层应该是什么呢?当然,输出层也是其输入的组合,所以我们有一个仿射变换。对于大多数输出,例如我们想预测高斯分布的均值,它位于实数空间,那么这个输出层只是一个线性层(仿射变换),没有其他东西,没有激活函数,或者如果你愿意,可以有一个恒等激活函数、线性激活函数。不需要非线性,因为线性预测已经进入了正确的空间(实数空间)。这与我们稍后将看到的分类问题非常不同。

如果我们想预测一个不在实数空间内的变量,例如拉普拉斯分布的尺度参数 b 或高斯分布的标准差参数,它们属于 R+(正实数),不能为负,只能为正。因此,我们需要在线性层之上使用一个激活函数,以将输出范围压缩到 R+。例如,我们可以使用今天将看到的 ReLU 或 softplus 函数。这些都是压缩函数,类似于 sigmoid(sigmoid 是将实数压缩到 0 和 1 的压缩函数)。但在这种情况下,我们希望压缩到 0 到无穷大,你可以使用 ReLU 或 softplus 来实现,但不能使用 sigmoid,因为它压缩到 0 和 1。

总结一下,对于回归问题,损失函数应该是什么样子?如果我们假设高斯或拉普拉斯模型分布,那么损失函数对应于 L2L1 损失,我们可以直接实现这个 L1 或 L2 损失来优化我们的网络。但也可以预测不确定性(例如方差或尺度)或多个模式,使用混合密度网络

分类问题 🏷️

在分类中,我们感兴趣的是预测离散结果(二元结果或多类结果)。在接下来的两周里,我们将进行相关的练习。我们可能有一个多类分类问题,例如将 MNIST 手写数字分类为 0 到 9 这 10 个不同的数字。MNIST 实际上是机器学习中最流行的数据集之一。尽管它简单(或者也许正因为其简单),它有大量的变体,并且即使在今天的研究中仍在使用。它基于美国国家标准与技术研究院的数据,包含手写数字。每个数字的分辨率为 28x28 像素。总共有 60,000 个带标签的训练样本和 10,000 个带标签的测试样本。

关于流形维度再多说一句。如果你看所有可能图像的空间,我们谈论的只是这些微小的 28x28 像素 MNIST 数字。假设它们是二值图像(实际上它们是灰度图),我们可以生成 2^784 种不同的图像。这是我们可以生成的整个空间的大小。换句话说,如果我们考虑 MNIST 是灰度的,我们甚至会有 256^784 种组合。要枚举所有可能的图像是真正不可能的,图像空间大得惊人。那么问题可能就出现了:为什么仅用 60,000 个带标签的训练图像进行图像分类甚至是可能的?为什么我们有希望成功完成这个任务?答案是,图像集中在这个非常高维空间的一个低维流形上。这就是为什么我们不需要搜索整个空间,只有这个极大空间中非常小的一部分看起来像真实的图像,而其中只有一小部分看起来像数字,其中又只有非常小的一部分看起来像手写数字。因此,我们生活在这个高维空间的一个非常小的流形上。

现在让我们回到分类。我们已经看过的一个东西是伯努利分布,我们将再次从伯努利分布开始。伯努利分布是关于两个类的分布,所以我们现在看的是两类(二元分类)问题。它可以写成这样:y 可以取 0 或 1,概率是 μ^y * (1-μ)^(1-y)μy=1 的概率。我们可以再次做之前做过的事情:我们可以假设模型分布为伯努利分布,并将其放入我们的最大似然估计器中。正如我们已经知道的,由此推导出的损失函数是我们已经知道的二元交叉熵损失函数。换句话说,在模型分布为伯努利分布的情况下最大化对数似然,等价于最小化二元交叉熵损失。作为备注,f_W 的最后一层可以是一个 sigmoid 函数(或任何其他压缩函数),但必须是一个将实数范围压缩到 0 和 1 的压缩函数。这就是为什么我们经常使用 sigmoid 函数,就像在逻辑回归中一样,以获得一个介于 0 和 1 之间的概率 μ

接下来我们将讨论如何将其扩展到多个类别,超越两个类别,例如 MNIST 的 10 个类别。在多类的情况下,我们再次使用最大似然原理和相应的分布,称为分类分布。它是多项分布的一个特例,其中只抽取一个事件。它的定义如下:Y 取类别集合中任何类别的概率是 μ_c(这是类别 c 的概率)。所有概率之和必须为 1。离散分布根据其定义自动适应多个模式,我们不需要使用混合模型来定义多峰分布。

在本课程中,我们将看一种替代表示法。这种索引表示法对于许多机器学习问题来说并不方便。相反,我们将把这个分布视为一个分布,不是在一维分类标签变量上,而是在一个独热向量 y 上,该向量包含 0 和 1 的元素。该向量的长度是类别数(例如 4),并且向量元素之和为一,这意味着只有一个元素是激活的(为 1)。这就是为什么它被称为独热向量。例如,我们将类别 2 表示为 [0, 1, 0, 0]。有了这个定义,我们可以像这样重写表达式。这个向量也可以解释为一个离散分布,其中所有概率质量都集中在真实类别上。这是一个很好的观点,将向量视为表示离散分布。当然,在最大似然过程中,我们想要做的是使这个分布(真实分布)与模型的预测分布尽可能相似。

现在让我们继续。让我们将分类分布作为模型分布放入我们的最大似然公式中。我们得到了什么?这是我们的最大对数似然公式。这里我们有我们的分类分布,所以我将把它代入这个表达式。因为我有一个乘积的对数,这等于对数的和。然后根据对数法则,我也可以把 y 拉到前面。同样,我可以通过在前面加上负号,将最大化问题转化为最小化问题,从而得到所谓的交叉熵损失。交叉熵损失就是这个表达式,它遍历所有类别,计算 -y_c * log(预测_c)。换句话说,我们现在最小化交叉熵损失。目标 y 就是这样一个独热向量。

现在问题当然出现了:我们应该如何制定输出层?我们如何制定输出层,使得 f_Wc 输出一个合适的离散分布?我们如何确保 f_Wc 预测一个有效的离散或分类分布?要求是什么?对于一个离散概率分布的要求是:向量的每个元素(预测的概率)介于 0 和 1 之间;同时,所有类别或类的总和必须等于 1,即分布必须和为 1。例如,如果我们为每个类别选择一个逐元素的 sigmoid 函数,我们将确保第一个条件,但不会确保第二个条件,因为如果我们对每个预测有独立的 sigmoid,则不能保证 sigmoid 后的输出之和为 1。

现在的解决方案是,在每类的仿射预测之上(我们现在有多个输出),我们定义一个 softmax 函数,它保证两个条件:各个元素在 0 和 1 之间,并且分布归一化到 1。softmax 函数看起来像这样:softmax(x)_i = exp(x_i) / sum_j(exp(x_j))。你可以看到,如果我对每个分量求和,分子和分母相同,所以和为 1。你还可以看到,由于指数函数,值总是大于零,并且总是小于一,因为我除以一个包含自身的和。所以两个条件都满足了,这很好。这是保证这两个条件的最简单函数。

现在让 S(我们称之为分数)表示网络在最后一个仿射层之后的输出。我们产生多个分数,产生的分数数量与类别数相同。然后,我们将神经网络的输出或输出层的函数定义为 softmax 函数。如果我们取 softmax 的对数,由于指数和对数抵消,我们得到这个表达式。这有优势,因为我们现在看到 S_c 直接贡献给损失函数,它们不会饱和。当我们进行更改时,我们确保这会传播到梯度。

让我们对这个我们想要最大化的对数 softmax 给出一些直觉。假设 c 现在是正确类别。这个表达式右边的第一项鼓励正确类别 c 的分数增加(我们想最大化这个表达式,所以想增加这个)。第二项鼓励 S 中的所有分数共同减少(因为我们这里有一个负号)。现在,这两项相互对抗:如果一个分数增加,那么另一个分数必须减少,因为输出总是一个分布。如果我增加正确的那个,我必须减少不正确的那些。这里的第二项可以用一个最大算子来近似。为什么?因为求和中的所有项对于几乎所有项来说都是微不足道的,因为我们有一个指数函数,所以这些是非常大的数字。如果我有一个 s 比其他 s 大一点,它将主导整个和。所以所有其他的 s 在这个表达式中都将变得微不足道,这意味着我们几乎只剩下最大分数。如果我只看这里的一项(最大值),那么对数和指数会抵消,我就得到了最大项。这就是为什么这大致是最大值的直觉。因此,损失总是强烈地惩罚最活跃的错误预测。如果正确类别已经有最大的分数,那么两项抵消,然后该样本对整体训练成本的贡献很小。

这里有一个例子。我们有四个类别,这些是神经网络预测的分数。在右边,我展示了这些分数的指数。你可以看到,这个图与这个图已经非常不同。你可以看到,所有不是最高分数的类别,在取指数后,与最高分数类别相比几乎微不足道。第二项(对数求和项)在这种情况下是 4.06。这非常类似于这些分数的最大值(4)。现在假设 c=2 是正确类别。如果我们的模型预测这些分数,我们将得到以下对数 softmax:S_c 是 1 减去 4.06,等于 -3.06。另一方面,如果正确类别是类别三,我们将得到 S_c 是 4 减去 4.06,大约等于 0。你可以看到,如果正确类别对应于我们神经网络分配了最高分数的类别,我们得到的值要大得多;而如果我们没有分配正确类别,我们得到的值要低得多。

现在我想简要谈谈 softmax 与 sigmoid 函数的联系,以及过参数化。正如你可能已经注意到的,要求分布归一化到一的约束实际上移除了一个自由度,所以我们甚至不需要预测 C 个类别,预测 C-1 个就足够了,因为最后一个我们知道一定是 1 减去前几个的和。这是真的。例如,我们可以考虑这个例子,我们有两个类别,我们固定一个自由度,比如固定第二个为 0。那么 softmax 就变成了 sigmoid 函数。所以你可以看到,我们可以通过固定一个自由度并假设两个类别,从 softmax 函数推导出 sigmoid 函数。这意味着 softmax 实际上是 sigmoid 函数的多类泛化。

现在问题当然是:哪个更好?总是预测类别数量的分数更好,还是应该只预测一个分数(或更少)并计算剩余的那个?答案是:在实践中没有太大区别,并且实现起来通常更简单的是预测这个过参数化版本,即预测所有值(例如 MNIST 的 10 个分数)并对其应用 softmax,因为 softmax 负责归一化。

另一个我想做的评论是,名字 softmax 实际上有点令人困惑,softargmax 会更精确。因为 softmax 是 argmax 的连续和可微版本,如果你用独热表示来考虑的话。softmax 对输入之间的差异做出响应。需要注意的一点是,softmax 对其所有输入加上相同的标量是不变的。因此,我们可以推导出 softmax 的一个数值更稳定的变体:softmax(x) = softmax(x - max(x))。这样做的优点是,即使在有限固定精度下,即使 x 变得很大,也能进行精确计算。这说明了 softmax 只依赖于各个分数之间的差异,而不依赖于我们减去的某个全局项。

现在让我们把它们放在一起。我们之前看到的单个训练样本的交叉熵损失是这个。到目前为止,我们已经看过了对数 softmax,它是这个表达式的一部分。交叉熵损失是所有类别的和:-目标_c * softmax(网络分数)_c。假设我们有四个类别和四个训练样本 X 及对应的标签 y,标签表示为独热编码。类别是狗、猫、老鼠、大象。假设我们有一个模型做出如下预测。对于第一个,它做得很好;对于第二个,它有点不确定;对于第三个,它甚至对所有类别都不确定;在最后一种情况下,模型实际上预测了错误的类别。如果你取这些分数并计算 softmax,我们得到这些概率分布。从这些概率分布中,我们可以使用这个表达式计算交叉熵损失。我们在这里看到的是,第一个示例预测的交叉熵损失相对较小,因为我们以高概率将正确类别(狗)分配给了该图像。对于第二个,交叉熵损失增加了,因为我们更不确定。对于第三个例子,损失进一步增加,因为我们更加不确定。对于最后一个例子,我们做出了错误的预测,所以我们给类别大象分配了非常低的概率,而给其他类别(如狗和老鼠)分配了高概率。因此,在这种情况下交叉熵损失最高,这非常直观。如果这是一个小批量,那么在基于梯度的随机梯度下降优化算法中,样本 4 将对损失函数的贡献最强,梯度将最强烈地试图扭转这个样本,使错误的预测变得正确。

总结一下,对于分类问题,输出层和损失函数是什么?对于两类情况,输出层可以过度预测一个值并使用 sigmoid,或者使用过参数化表示并预测两个值,然后应用 softmax 以获得这两个值的归一化分布。对于超过两个类别的情况,我们通常预测 C 个分数,然后在上面使用 softmax 非线性。对于损失函数,在两类情况下,我们有二元交叉熵损失,它实际上只是交叉熵损失的特例,我们将其用于超过两个类别的情况。

总结 ✨

本节课中我们一起学习了深度神经网络中输出层与损失函数的设计。我们从最大似然原理出发,推导了适用于回归问题的 L2(平方损失)、L1(绝对损失)损失函数,以及可以预测不确定性的更复杂形式。对于分类问题,我们学习了如何利用伯努利分布推导二元交叉熵损失,以及利用分类分布和 softmax 输出层推导多类交叉熵损失。理解这些输出与损失函数的选择,是构建有效神经网络模型解决回归与分类任务的基础。

023:激活函数详解 🧠

在本节课中,我们将学习深度神经网络中一个至关重要的组成部分:激活函数。我们将探讨激活函数的作用、为什么它们必须是非线性的,并详细介绍几种常见的激活函数及其优缺点。理解这些内容对于构建和训练有效的神经网络至关重要。

上一节我们讨论了神经网络的输出和损失函数,本节我们将聚焦于隐藏层。隐藏层的关键超参数不仅包括层数和每层的节点数,还包括我们使用的激活函数类型

激活函数的作用

回忆一下多层感知机(MLP)的定义。一个隐藏层可以定义为对前一层输出的变换:首先通过一个由矩阵 A 和向量 b 表示的线性函数,然后是一个非线性函数 g,即激活函数,例如 Sigmoid 函数。

激活函数通常按元素应用于输入(但并非总是如此,我们稍后会看到一个例外)。激活函数必须是非线性的,只有这样,网络才能学习非线性映射和有用的数据表示。

有些激活函数并非处处可微,我们也会看到一些例子。但只要不可微的点非常少,这通常不会影响训练。

常见激活函数分析

以下是几种在深度学习中常见的激活函数,我们将逐一分析它们的特性。

Sigmoid 函数

我们首先来看在逻辑回归中已经见过的 Sigmoid 激活函数。

公式g(x) = 1 / (1 + exp(-x))

Sigmoid 函数的特殊之处在于,它将实数轴上的输入映射到 (0, 1) 区间。这可以解释为概率,或者在神经科学中解释为神经元的饱和放电率:低放电率对应低输出,高放电率对应高输出。

然而,Sigmoid 函数存在两个主要问题:

  1. 梯度消失:当输入值非常大或非常小时(即函数饱和区),其梯度接近于零。在反向传播中,这会导致梯度“停止流动”,使得该激活函数之前的网络层参数无法得到有效更新。
  2. 非零中心化:其输出值始终为正。这会在网络中引入系统性偏差,导致后续所有层的输入都为正。这种“同号性”会限制梯度更新的方向,使得优化路径变得低效曲折(类似于逆风航行),在高维空间中问题尤为严重。

Tanh 函数

为了解决非零中心化问题,人们提出了 Tanh(双曲正切)函数。

公式g(x) = 2 * sigmoid(2x) - 1

Tanh 函数将输入映射到 (-1, 1) 区间,是一个以零点为中心的奇函数。这缓解了 Sigmoid 的非零中心化问题。然而,它同样存在饱和区,因此梯度消失的问题依然存在。

ReLU 函数

目前最常用的激活函数是 ReLU(修正线性单元)。它受神经科学的启发较少,但在实践中效果非常好。

公式g(x) = max(0, x)

ReLU 是一个“修正”的线性单元:对于正输入,输出为线性;对于负输入,输出为零。它是一个非线性函数,且在 x=0 处不可微,但这在训练中几乎不会造成问题。

ReLU 的优点包括:

  • 缓解梯度消失:对于所有正输入,其梯度恒为 1,不存在饱和问题,通常能带来更快的收敛速度。
  • 计算高效:只需一个 max 操作,无需计算指数。

ReLU 的缺点包括:

  • 非零中心化:输出同样非负。
  • 神经元“死亡”:如果某个神经元的输入持续为负,其梯度将始终为零,该神经元将停止学习,成为“死神经元”。实践中,网络中常有约 10% 的神经元处于这种状态。为了缓解此问题,通常会在 ReLU 层之前的线性层中使用一个小的正偏置初始化。

Leaky ReLU 与 PReLU

为了解决“死神经元”问题,Leaky ReLU 被提出。

公式g(x) = max(αx, x),其中 α 是一个很小的固定值(如 0.01)。

它在负输入区域引入了一个小的、非零的斜率。其优点是不饱和、不会“死亡”、输出更接近零中心,且计算高效。

PReLU(参数化 ReLU)是 Leaky ReLU 的推广,其中的斜率参数 α 本身是可学习的,可以从数据中习得。

ELU 函数

ELU(指数线性单元)是另一种变体。

公式g(x) = x if x > 0 else α*(exp(x)-1)

它类似于 Leaky ReLU,但在负区域是平滑且饱和的。这被认为能为模型在处理某些问题时带来对噪声的鲁棒性,同时继承了 Leaky ReLU 的大部分优点。

Maxout 函数

Maxout 函数是 ReLU 族的一个广义形式,它不是逐元素应用的。

定义g(x) = max(w1^T x + b1, w2^T x + b2, ...)

它取多个线性变换结果的最大值。通过选择不同的权重,它可以表示 ReLU、Leaky ReLU 甚至更复杂的函数(如绝对值、近似二次函数)。其缺点是显著增加了每个神经元的参数量。

如何选择激活函数?🤔

不幸的是,没有“一刀切”的解决方案。激活函数的选择高度依赖于具体问题。在本单元中,我们只探讨了最常见的几种,实际上还有很多其他变体。最佳选择通常需要通过实践中的试错来确定。

以下是一些通用的指导原则:

  • 关注梯度流:确保在优化过程中梯度能顺畅地反向传播到所有需要更新的参数。
  • 经验法则:将 ReLU 作为默认选择。它最通用、高效且收敛快。可以尝试 Leaky ReLU、Maxout 或 ELU 以获得潜在的小幅性能提升。
  • 特定场景:在循环神经网络(RNN)中,Tanh 仍被经常使用。通常应优先选择 Tanh 而非 Sigmoid。

实现注意事项:梯度检验 ✅

在实现计算图中的任何节点(如激活函数)时,确保梯度计算的正确性至关重要。如果你手动实现梯度(而非依赖现代框架的自动微分),进行梯度检验是标准做法。

梯度检验的核心思想是通过数值方法近似梯度,并与你实现的解析梯度进行比较。常用的方法是使用对称差分商

公式(f(x+h) - f(x-h)) / (2h)

关键在于如何选择步长 h

  • h 太小:会因数据类型的有限精度而产生舍入误差。
  • h 太大:会因近似公式不准确而产生近似误差。

一个良好的经验法则是选择 h = ε^(1/3),其中 ε 是机器精度。例如,对于单精度(32位)浮点数,ε ≈ 6e-8,其立方根约为 8e-3。在这个量级附近选择 h,通常能获得可靠的数值梯度估计。

通过绘制解析梯度与数值梯度的对比图,可以直观地验证实现的正确性。


本节课中,我们一起深入学习了深度神经网络中的激活函数。我们了解了激活函数的必要性,分析了 Sigmoid、Tanh、ReLU 及其变体(Leaky ReLU, PReLU, ELU)以及 Maxout 函数的特性、优缺点。最后,我们讨论了选择激活函数的实用指南,并强调了在实现时进行梯度检验的重要性。记住,良好的梯度流动是成功训练深度网络的关键,而 ReLU 通常是你的可靠起点。

024:数据预处理与权重初始化 🧠

在本节课中,我们将要学习深度神经网络训练前的两个关键步骤:数据预处理和权重初始化。正确的处理与初始化对于网络能否成功学习至关重要。

数据预处理 📊

上一节我们讨论了激活函数需要零中心化的重要性。本节中我们来看看为什么输入数据本身也需要进行类似的预处理。

考虑一个线性层和激活函数 G。如果所有输入 X 都是正数,并且大多数激活函数的梯度 G' 也为正,那么所有参数的梯度将具有相同的符号(即上游梯度 ∂L/∂G 的符号)。这会导致梯度更新效率低下。因此,不仅每一层的激活需要零中心化,输入数据本身也应如此。

这就是为什么在将数据输入神经网络之前,通常需要进行预处理。最常见的预处理是零中心化。

以下是常见的数据预处理步骤:

  1. 零中心化:计算数据集中每个特征维度(例如,图像的每个颜色通道)的均值,然后从每个数据点中减去该均值。公式表示为:X_centered = X - mean(X)
  2. 归一化:在零中心化之后,有时还会将数据除以每个特征维度的标准差,使得数据的标准差为1。公式为:X_normalized = X_centered / std(X)
  3. 去相关与白化:更高级的预处理包括通过乘以经验协方差矩阵的特征向量来去除数据相关性,或通过除以协方差矩阵特征值的平方根来进行白化,使数据在各个维度上具有单位方差且不相关。这类似于对数据应用PCA(主成分分析)。

直观上,零中心化数据的好处在于,它能使决策边界的小幅调整不会立即导致巨大的分类错误,从而使优化过程更加稳定。

在实际应用中,白化并不常用,人们发现零中心化是真正必要的。不同网络架构的具体做法略有不同:

  • AlexNet:减去在整个训练集上计算出的平均图像。
  • VGGNet:仅减去每个颜色通道的均值(共3个值)。
  • ResNet:减去每个通道的均值并除以每个通道的标准差。

权重初始化 ⚖️

现在,让我们转向另一个关键话题:如何初始化网络中的权重参数。回想一下,我们使用随机梯度下降算法进行训练。该算法简单,但一个核心问题是:如何初始化数量可能高达数百万的权重?

最简单的想法是将所有权重初始化为一个常数(例如0)。但这会导致严重问题:网络每一层中的所有神经元在初始时完全对称。在正向和反向传播中,它们会进行完全相同的计算和更新,从而无法学习到有意义的、多样化的特征表示。因此,这种初始化方式不可行。

接下来,我们以一个多层感知机为例,其单层可表示为带参数 AB 的仿射变换与激活函数 G 的组合。我们将重点分析使用 tanhReLU 激活函数的情况。

初始化不当的问题

一种天真的改进方法是从小标准差(如 sigma = 0.01)的高斯分布中随机采样权重。但这会导致深层网络的激活值迅速向0收缩。在反向传播时,梯度也会随之消失,无法有效地更新浅层网络的权重。

相反,如果从较大标准差(如 sigma = 0.2)的高斯分布中采样权重,则可能导致大多数激活值进入 tanhsigmoid 函数的饱和区。在饱和区,激活函数的梯度接近于0,同样会造成梯度消失,阻碍学习。

正确的初始化方法

因此,我们需要为权重设置合适的初始化方差。Xavier Glorot 等人提出,对于零中心化的激活函数(如 tanh),应从方差为 1 / D_in 的分布中采样权重,其中 D_in 是该层输入的维度。这被称为 Xavier初始化

其原理是,假设输入 X 和权重 W 独立同分布、均值为零,且激活函数在零点附近的导数为1(tanh 符合此条件)。为了确保信号在通过网络层时方差不发生剧烈变化(避免爆炸或消失),需要令 Var(W_i) = 1 / D_in。这样,输出 y 的方差将近似等于输入 X_i 的方差。

然而,Xavier初始化假设激活函数是零中心化的。对于 ReLU 激活函数,由于其输出始终非负,不再满足零均值假设。直接使用Xavier初始化会导致激活分布逐渐向零偏移。

针对 ReLU,Kaiming He 等人提出了 He初始化。其解决方案很简单:将权重的方差加倍,即从方差为 2 / D_in 的分布中采样。这补偿了ReLU函数“砍掉”一半负值区域所损失的方差,从而在多层传播中保持激活值的方差稳定。

总结 📝

本节课中我们一起学习了深度神经网络训练前的两项基础且重要的工作:

  1. 数据预处理:对输入数据进行零中心化是高效学习的关键,它有助于稳定优化过程。去相关和白化等技术使用频率较低。
  2. 权重初始化:正确的初始化对于保证梯度在网络中有效流动至关重要。它确保了所有参数都能在训练初期获得有意义的梯度更新。
    • 对于 tanh 这类零中心化激活函数,推荐使用 Xavier初始化(方差为 1 / D_in)。
    • 对于 ReLU 激活函数,推荐使用 He初始化(方差为 2 / D_in)。

权重初始化本身是一个活跃的研究领域,有更多深入的文献,本课程无法全面覆盖。掌握这些基础知识将为构建和训练有效的深度学习模型打下坚实基础。

今天的内容就到这里。谢谢!

025:正则化与参数惩罚 🛡️

在本节课中,我们将要学习深度学习中一个至关重要的概念:正则化。我们将从最基础的参数惩罚方法开始,探讨如何通过约束模型参数来防止过拟合,从而提升模型的泛化能力。

概述

在之前的课程中,我们简要地在线性回归的背景下讨论过正则化。今天,我们将更广泛地探讨这个主题,并特别关注其在深度神经网络中的应用。正则化对于深度神经网络尤为重要,因为这类模型通常拥有大量参数,有时甚至超过了训练样本的数量。因此,正则化是控制模型复杂度的关键工具。

本讲座分为五个单元。在第一单元中,我们将讨论参数惩罚。在第二单元中,我们将介绍早停法。接着,我们将探讨集成方法,以及深度学习领域流行的一种特定集成方法——Dropout。最后,我们将讨论数据增强,它也可以被视为一种特殊的正则化形式。

在开始之前,让我们简要回顾第一讲中关于模型容量、过拟合和欠拟合的内容。下图展示了用不同次数的多项式拟合一组绿色数据点的结果。

左侧是一次多项式拟合,中间是三次多项式拟合,右侧是九次多项式拟合。我们可以看到,如果模型容量过低,即模型不够灵活、参数过少,训练误差和测试误差都会很高,这被称为欠拟合。另一方面,如果模型容量过高,例如九次多项式有10个参数,模型可以完美拟合训练集,甚至将训练误差降至零,但测试误差或泛化误差会非常大,模型无法很好地泛化,这被称为过拟合。

在深度神经网络的背景下讨论正则化时,我们的目标是将一个过于灵活的模型从第三种状态(过拟合)调整到第二种状态(容量适中)。这是因为对于任何现实世界的问题,手动指定具有少量参数的“正确”表示形式非常困难。相反,指定一个能够表示更广泛函数类的大型模型(如多层神经网络)要容易得多,然后通过软约束来限制这个函数类,从而实现正则化。这就是为什么我们要从第三种状态转向第二种状态。

下图左侧展示了不同次数多项式拟合的训练误差和泛化误差。我们可以看到,泛化误差的最小值出现在中间区域。

因此,如果我们从右侧的过拟合状态转向实现良好泛化的点,我们实际上是在付出代价,即增加偏差以减少方差。在过拟合状态下,参数估计器具有较大的方差和较低的偏差。在实践中,我们当然希望两者都小。但我们的期望是,如果我们愿意支付稍高的偏差代价,就可以显著减少方差,从而获得更好的泛化效果。目标始终是最小化泛化误差,尽管我们使用了像深度神经网络这样指定了非常灵活的大型函数族的模型。

下图从非常直观的层面很好地阐释了这一点。

它说明了数据和正则化器都是对函数空间的约束。它们共同约束了我们估计器的可能输出,即可能的解空间。红色区域代表这个神经网络可以表示的所有映射(函数)。正则化器通过对函数空间施加软约束来限制空间,使得接近该函数空间中特定位置的函数更有可能被选择。同样,训练数据也通过最小化负对数似然误差(最大化数据似然)来约束函数空间。我们寻求的解位于函数空间、正则化器和数据约束的交集附近。即使我们使用非常大的深度网络,自然界的复杂性仍然可能超过我们能表示的所有函数,因此我们总是在做近似,但正则化器鼓励我们做出一个既能很好拟合数据又不太复杂的近似。

参数惩罚

现在,让我们从参数惩罚开始。

我们使用之前的通用符号,用 X 表示数据集,其中所有行是输入,所有行是输出(本例中是一维输出,但可以更多)。用 W 表示模型参数。在多层感知机中,W 是一个将所有权重矩阵展平并拼接起来的大向量。

我们可以通过向损失函数添加一个参数范数惩罚项 R(W) 来限制有效模型容量。因此,数据集 X 和参数 W 上的总损失等于原始未正则化的损失加上对参数 W 的正则化项:

L̃(X, W) = L(X, W) + α * R(W)

其中 α 是一个控制正则化强度的超参数,可以通过交叉验证等方法选择。从这个方程可以看出为什么这被称为参数范数惩罚,因为惩罚项不依赖于数据集,只依赖于参数,并且通常实现为参数 W 的简单范数。

我们的正则化器应该量化参数的大小或模型容量。我们希望只有少数参数是活跃的,即拥有一个容量小但仍能很好拟合数据的模型。最小化总损失 会同时减小 LR,因此这里存在 LR 之间的竞争。我们希望通过将它们组合成一个单一目标来同时优化这两项,从而鼓励模型既拟合数据又不太复杂。

通常,R 只应用于仿射层的权重,而不应用于偏置。原因是我们希望偏置保持灵活,鼓励偏置接近零通常不是一个好主意。其次,权重的数量远多于偏置,因此约束偏置不那么重要。在没有关于问题的任何先验知识的情况下,我们希望使权重向量 W 更接近零,因为小权重会导致更简单的解,这对于深度神经网络也是如此。这是一个鼓励模型简单性的非常简单的正则化器。

下图提供了一个关于此的简单直觉。

假设有两个非常简单的网络对输入 xy 进行预测。两个网络的输入 x1x2 几乎相同且接近1,它们都预测输出为2。然而,第二个网络具有更高的权重。因此,如果测试分布与训练分布略有不同,第二个网络的预测会更差。它表示的是相同的东西,但函数更复杂。

下图是参数空间的几何视图。

我们有一个二维参数空间 W1W2。绿色等高线是目标函数,红色等高线是正则化器。目标函数在这里有一个最小值。当我们应用参数惩罚时,我们添加一个正则化器(本例中是L2正则化器,其等高线是同心圆)。由于这两个相互竞争的目标,如果我们优化联合目标(目标函数与正则化器之和),最优解不再位于原始目标函数的最小值点,而是位于两者之间的某个点。在这个例子中,正则化器显著减小了 W2 的值,同时略微增加了 W1 的值,但总体上确保了参数向量的L2范数较小。

L2正则化(权重衰减)

L2正则化是深度学习和机器学习中最常见的正则化形式之一。在深度学习中,它通常也被称为权重衰减。在线性回归的背景下,我们也使用过“岭回归”这个术语。

权重衰减或L2正则化简单来说就是L2惩罚项。正则化项 R(W) 是参数 W 的平方L2损失:

R(W) = (1/2) * ||W||₂²

因此,总目标函数(包含未正则化损失和正则化器)可以写成:

L̃(X, W) = L(X, W) + (α/2) * WᵀW

现在,让我们尝试理解这对梯度下降优化有什么影响。在随机梯度下降等算法中,参数更新由以下方程给出:

W_{t+1} = W_t - η * ∇_{W_t} L̃

其中 η 是步长。我们可以代入 的表达式,其梯度是原始目标函数的梯度加上正则化器的梯度(即 αW_t)。因此:

W_{t+1} = W_t - η * (∇_{W_t} L + αW_t)

我们可以将其改写为:

W_{t+1} = (1 - ηα) * W_t - η * ∇_{W_t} L

从这个形式可以非常直观地看出权重衰减的作用:在每次优化迭代中,我们在进行梯度更新之前,先使前一次迭代的权重衰减。ηα 都是小的正数,所以 (1 - ηα) 是一个略小于1的数。我们实际上是在每次梯度更新步骤中使权重变小、更接近原点。

以上是针对单次梯度更新步骤。在整个训练过程中会发生什么?为了分析这一点,让我们做一个简化。设 W* 是未正则化目标函数的最优解。考虑在 W* 附近对未正则化损失 L 进行二次近似

L̂(W) ≈ L(W*) + (W - W*)ᵀH (W - W*) / 2

其中 H 是海森矩阵。由于 W* 是最优解,梯度为零,所以线性项消失。现在,将正则化项加入这个近似中,得到正则化后的近似目标:

L̂_reg(W) ≈ L(W*) + (1/2)(W - W*)ᵀH (W - W*) + (α/2) WᵀW

是这个正则化近似目标的最小值。通过求解梯度为零的点,我们可以得到:

W̃ = (H + αI)⁻¹ H W*

从这个表达式可以看出,当 α 趋近于0时, 趋近于 W*。当 α 增大时,为了更好理解其影响,我们对海森矩阵 H 进行特征值分解:H = QΛQᵀ。代入上式可得:

W̃ = Q (Λ + αI)⁻¹ Λ Qᵀ W*

这意味着,W* 中与 H 的特征向量对齐的分量被一个因子 λ_i / (λ_i + α) 重新缩放。正则化主要影响特征值小的方向。如果特征值 λ_i 远小于 α,则该方向上的分量会被显著缩小。如果特征值 λ_i 远大于 α,则缩放因子接近1,正则化影响很小。

从之前的几何图可以直观看出:在 W1 方向(本例中曲率低、特征值小),正则化器效果很强,将最优值拉向0。在 W2 方向(曲率高、特征值大),原始目标函数的代价很高,因此L2正则化器(具有各向同性的同心圆)的影响要小得多。

L1正则化

我们也可以看看另一种正则化器,例如L1正则化器。L1正则化器是添加一个惩罚项,其中范数是L1范数,即对参数向量 W 的每个元素取绝对值然后求和:

R(W) = ||W||₁ = Σ_i |w_i|

与L2正则化器相比,L1正则化器会导致解更加稀疏。从下图的等高线可以看出,L1正则化器(红色菱形等高线)试图将参数 W1 挤压到接近0,而另一个参数则相对自由。

让我们通过一个简单的具体数字例子来看这个现象。假设有三个输入特征 x = [1, 2, 1]。考虑两个不同的线性分类器(权重向量):

  • W1 = [0, 0.75, 0]
  • W2 = [0.25, 0.5, 0.25]

两者与 x 的点积结果相同,都是1.5。但L2和L1正则化器对这两个解的偏好不同:

  • L2正则化值:||W1||₂² = 0.5625||W2||₂² = 0.375。L2正则化器偏好 W2
  • L1正则化值:||W1||₁ = 0.75||W2||₁ = 1.0。L1正则化器偏好 W1

因此,L1正则化器倾向于稀疏解(许多值接近0),而不是值更均匀的解。

下图是一个简单的图形化示例。如果我们要将一张图片分类为“狗”,分类器可能会考虑不同的特征,如“毛茸茸的”、“有两只眼睛”、“有尾巴”等。L2正则化器会考虑所有信息来做决策,而L1正则化器会将所有注意力集中在少数关键特征上,试图通过关注少数特征来对图像进行分类。根据具体应用,可能更倾向于使用L2或L1正则化。

与贝叶斯MAP推断的联系

最后,参数惩罚也与贝叶斯最大后验概率推断有联系。例如,L2正则化可以解释为对网络参数 W 应用高斯先验的MAP估计。

最大后验概率目标是最大化给定数据后参数的后验概率:P(W|X)。根据贝叶斯规则,这等价于最大化似然 P(X|W) 乘以先验 P(W)。取对数后,得到对数似然加对数先验。如果先验是零均值、协方差矩阵为 (1/α)I 的高斯分布,那么对数先验项就变成了 - (α/2) WᵀW(忽略常数项)。将最大化问题转化为最小化问题(取负号),我们就得到了L2正则化项。因此,L2正则化等价于假设参数服从高斯先验的MAP估计。

类似地,L1正则化可以解释为对参数 W 应用拉普拉斯先验的MAP估计。

在计算图中的实现

在代码实现中,例如在深度学习框架里,损失函数只是计算节点。正则化的集成非常直接,就是计算节点的简单组合。未正则化的目标函数 L 是一个节点,依赖于参数 W1W2 的正则化器 R(W) 是另一个节点。总损失函数就是这两个节点的加权和,其中超参数 α 控制权重。

总结

在本节课中,我们一起学习了正则化的核心概念,特别是参数惩罚方法。我们了解到,正则化的目的是通过向损失函数添加惩罚项来约束模型参数,从而在拟合数据和保持模型简单性之间取得平衡,最终目标是提高模型在未见数据上的泛化性能。

我们重点探讨了两种最常见的参数范数惩罚:

  1. L2正则化(权重衰减):它惩罚参数的平方和,倾向于产生较小但非零的权重,使解更平滑、稳定。
  2. L1正则化:它惩罚参数的绝对值之和,倾向于产生稀疏解,即许多权重恰好为零,这可以用于特征选择。

我们还从几何视角和贝叶斯推断的角度理解了这些方法,并简要提到了它们在计算图中的实现方式。参数惩罚是正则化工具箱中最基础且强大的工具之一,为后续学习其他正则化技术奠定了基础。

026:正则化之早停法 🛑

在本节中,我们将学习一种非常有效且常用的正则化策略——早停法。这是一种简单、计算高效的方法,几乎总是被用于深度学习实践中。

概述

上一节我们讨论了权重衰减等正则化方法。本节中,我们来看看另一种策略:早停法。它通过监控验证集性能,在模型开始过拟合之前停止训练,从而防止模型在训练集上表现过好而在新数据上表现变差。

训练与验证损失

在训练过程中,我们会迭代整个数据集多次。完整遍历一次数据集称为一个周期。一个迭代通常指处理一个迷你批次。

如果我们观察训练损失和验证损失随周期或迭代次数的变化,通常会看到以下模式:

  • 训练损失(即负对数似然损失)会持续下降,逼近越来越小的值。
  • 然而,验证损失在初始快速下降后,会在某个点开始再次上升。

我们关心的是最佳的泛化性能,这通常通过验证误差来近似。因此,我们不希望无限期地训练下去。

早停法原理

早停法的核心思想是:我们训练一段时间,直到验证误差停止下降并开始上升,然后返回具有最低验证误差的参数。

具体做法是:在训练期间,我们需要定期在预留的验证集上评估模型性能(进行一次前向传播以计算验证损失)。我们不等待训练误差变得任意小,而是在验证误差一段时间内不再下降(或开始上升)时,提前停止训练。

以下是早停法的基本步骤:

  1. 将数据集划分为训练集和验证集。
  2. 开始训练模型,并定期(例如每个周期后)在验证集上评估性能。
  3. 持续跟踪到目前为止观察到的最佳验证误差及其对应的模型参数。
  4. 当验证误差在连续多个评估周期内不再改善(或开始上升)时,停止训练。
  5. 恢复并返回具有最佳验证误差的模型参数。

与L2正则化的关系

简单的早停策略与L2正则化存在类比。事实上,在某些假设下,两者是等价的。

  • 在早停法中,参数优化的轨迹会在到达未正则化目标的最小值 W* 之前,于 点停止。
  • 在L2正则化中,正则化项惩罚力 λ 会迫使正则化损失的最小值 比未正则化目标的最小值 W* 更接近原点。

因此,这两种正则化策略具有非常相似的效果。想深入了解,推荐阅读《深度学习》教材第7.8章。

早停法的优势与实施要点

早停法是深度学习中最常用的正则化形式,因为它有效、简单且计算高效。

在早停法中,训练时间(迭代次数或周期数)本身可以被视为一个超参数。但它的独特之处在于其超高效性:它只需要一次训练运行就能测试所有的“超参数”(即不同的停止点)。因为我们在每次评估验证集时,都能得到一个验证误差。这与权重衰减等需要为不同超参数值重复训练模型的方法不同。

唯一的额外成本是需要定期在验证集上评估验证误差。但这在机器学习中非常普遍,应该是你首先要做的事情。你可以通过以下方式减少成本:

  • 使验证集小于训练集(这通常是标准做法)。
  • 降低评估频率(例如,每个周期评估一次)。

如果可用的训练数据很少,可以进行第二阶段的训练以利用之前用于评估的验证集。具体做法是:

  1. 进行第一次训练运行,以确定通过早停法得到的最佳训练步数(迭代次数或周期数)。
  2. 从头开始第二次训练,使用所有训练数据(包括之前的验证集),并按照第一步确定的步数进行训练。

当然,这需要进行两次训练运行,通常只在数据量确实很少,需要验证集数据来获得良好性能时才值得这样做。

总结

本节课我们一起学习了早停法这种重要的正则化技术。我们了解到,它通过监控验证集性能并在其开始恶化时提前终止训练,来有效防止过拟合。早停法计算高效,通常只需单次训练即可确定最佳停止点,并且与L2正则化存在理论上的联系。在实际应用中,应养成监控训练与验证损失曲线的习惯,这是模型调试和获得良好泛化能力的关键一步。

027:正则化之集成方法 🧠

在本节课中,我们将学习一种提升模型泛化能力的技术——集成方法。我们将了解其核心思想、背后的数学原理以及几种常见的构建策略。

集成方法的核心思想是,通过训练多个独立的模型来完成同一任务,并在推理时对它们的预测结果进行平均。这种方法通常也被称为模型平均。

为什么集成方法有效?🤔

上一节我们介绍了集成方法的基本概念,本节中我们来看看其背后的数学原理。直觉在于,不同的模型在测试集上会犯略有不同的错误。通过对这些预测进行平均,我们可以得到一个总体上更稳健的估计,而无需真正开发一个更好的单一模型。

让我们以K个不同的回归模型为例进行说明。假设每个模型产生的误差为 ε_k,其方差为 V,不同模型误差之间的协方差为 C。

集成预测器的期望平方误差如下:

$$
E[(\frac{1}{K}\sum_{k=1}^{K} \epsilon_k)^2] = \frac{1}{K}V + \frac{K-1}{K}C
$$

这个公式揭示了集成方法有效的关键:

  • 如果误差完全相关(C = V),那么集成误差就等于单个模型的误差 V,没有获得任何增益。
  • 如果误差完全不相关(C = 0),那么集成误差将降低为单个模型误差的 1/K,获得了巨大的增益。

因此,当集成中各个模型的预测误差尽可能不相关时,集成方法的效果最好。

如何构建集成模型?🔨

理解了集成方法的原理后,我们来看看几种常见的构建策略。以下是几种常用的方法:

  1. 不同的初始化与随机性:这是最简单的方法。使用不同的随机初始化权重,或在随机梯度下降中使用不同的迷你批次顺序来多次训练相同的模型架构。即使对于简单的模型,这也能引入一定的独立性,从而被集成技术所利用。

  2. 不同的模型架构:我们可以使用略微不同的模型架构,例如更多或更少的层、不同数量的隐藏单元、不同的损失函数或超参数(如不同的L2惩罚系数α值)。

  3. Bagging(自助聚合):Bagging是指通过有放回地从原始数据集中随机抽取样本来创建K个新的数据集,并在每个新数据集上训练一个模型。这意味着每个新数据集中可能缺少原始数据集中的某些样本,也可能包含一些重复样本。

为了更直观地理解Bagging,请看以下示例:假设我们的任务是分类数字“8”。原始数据集中有“9”、“6”和“8”。通过Bagging,我们可能得到两个新的数据集:

  • 数据集1:包含“8”、“6”、“8”
  • 数据集2:包含“9”、“9”、“8”

在这种情况下,第一个集成成员(在数据集1上训练)可能学会主要根据数字顶部是否有环来区分“8”和“6”。第二个集成成员(在数据集2上训练)则可能学会主要根据数字底部是否有环来区分“8”和“9”。这样,我们就得到了两个更独立、更简单的预测器,最终通过平均它们的预测来获得更鲁棒的结果。

集成方法的优缺点 ⚖️

集成方法的主要优势在于,它通常能在不改变基础模型架构的情况下,稳定地提升模型性能(例如1-2%),许多竞赛的获胜方案都使用了集成技术。

然而,其缺点也很明显:在推理时,需要评估多个模型。对于深度学习模型,这意味着我们需要对所有模型进行前向传播,然后才能平均它们的预测结果,这会显著增加计算成本和推理时间。

总结 📝

本节课中我们一起学习了集成方法。我们了解到,集成方法通过组合多个模型的预测来提升泛化能力,其有效性依赖于模型预测误差之间的低相关性。我们探讨了其数学原理,并介绍了通过不同初始化、不同模型架构以及Bagging等策略来构建集成模型。虽然集成方法能有效提升性能,但也需要权衡其带来的计算开销。

028:正则化之Dropout 🎯

在本节课中,我们将要学习一种非常具体且高效的集成方法——Dropout。这是一种在现代深度学习中极为流行的正则化技术,因其简单、高效且在许多模型上效果显著而广受青睐。


核心思想 🧠

上一节我们介绍了经典的集成方法,本节中我们来看看一种特殊的“即时”集成技术——Dropout。

Dropout的核心思想是:在训练过程中,我们以一定的概率(例如0.5)随机将网络中的神经元“丢弃”(即将其输出设置为0),从而在每次迭代中动态地创建一个不同的简化网络。

考虑上图顶部的例子。这是一个简单的多层感知机,底部是输入,顶部是输出。以概率0.5随机丢弃神经元,意味着移除该神经元的所有输入和输出连接。右侧展示了被移除的神经元及其连接。

实现机制 ⚙️

这种机制可以通过一个二进制掩码来实现。掩码是一个二进制向量,长度等于网络中神经元的总数。在训练的每一次迭代中,我们都会随机生成一个新的掩码,每个神经元以概率p(如0.5)被保留(值为1)或丢弃(值为0)。然后,我们在这个被“瘦身”的网络上进行前向传播和反向传播。

因此,Dropout有效地从一个共享参数的单一网络中,动态地创建了一个庞大的模型集成。这与传统集成方法不同,传统方法中每个模型的参数是独立的,而Dropout中所有“子模型”共享同一套参数。

为何有效? 🤔

以下是Dropout被认为是一种好方法的主要原因:

首先,Dropout是一种有效的正则化手段。它迫使网络学习冗余的表示。因为模型无法依赖某些特定的特征来做预测(这些特征可能在某个前向/反向传播中被丢弃),所以它必须以分布式和冗余的方式在网络中表示信息。这降低了模型容量的影响,增强了泛化能力。当然,这也意味着我们通常需要更大的模型更长的训练时间

其次,它能防止特征的协同适应。在经典深度学习中,神经元可能会学会“抵消”其他神经元的作用(例如,一个神经元激活很高,另一个激活很低,组合起来产生零值)。这种协同适应通常不是我们希望建模的真实效应。Dropout通过随机丢弃神经元,使得模型学到的表示必须对这种随机性保持鲁棒,从而打破了这种不必要的依赖。

最后,也是其一大优势:与之前介绍的集成方法相比,Dropout在推理(预测)时只需要一次前向传播,效率极高。

推理时的挑战与解决方案 🧮

然而,Dropout在推理时带来了一个问题:由于随机掩码的存在,模型的输出变得随机。形式上,一个应用了Dropout的神经网络可以写成:
f_w(x, c)
其中,c 是一个二进制掩码向量,每个元素独立地从伯努利分布中采样(例如,以概率p=0.5取值为1)。

在推理时,我们希望计算整个集成的预测,即对所有可能的掩码 c 对应的输出求期望:
E_c [f_w(x, c)]
对于有M个神经元的网络,可能的掩码组合有 2^M 种,这个求和是难以直接计算的。

那么该怎么办呢?让我们考虑一个简单的线性模型(可以看作是神经网络中的一层):
f_w(x) = w1*x1 + w2*x2
其Dropout版本为:
f_w(x, c) = c1*w1*x1 + c2*w2*x2, 其中 c1, c2 独立地以概率p=0.5取0或1。

在训练时,我们优化的是对所有可能掩码的期望损失。通过计算可以得出,这个期望值等于 p * f_w(x)(本例中p=0.5,所以是 0.5 * f_w(x))。

这意味着,为了在训练时达到与原始模型 f_w(x) 相同的输出水平,Dropout模型中的权重 w 必须学习得更大(大约是原来的 1/p 倍)。因此,在推理时,如果我们使用完整的网络(不丢弃任何神经元),为了得到近似的集成预测结果,一个简单有效的方法是进行权重缩放:将训练好的所有权重乘以Dropout概率 p(或者说,在推理时,每个神经元的输出要乘以 p)。

注意:这种权重缩放推理法对于非线性模型只是一种近似,但在实践中被证明效果很好。

效果展示 📊

下图展示了一个在MNIST数据集上训练的单隐藏层自编码器的神经元激活可视化。左侧是标准自编码器,右侧是使用了Dropout的自编码器。

我们可以看到:

  • 左侧(无Dropout):存在大量协同适应,许多神经元的激活模式嘈杂,它们相互响应并试图纠正彼此的错误。
  • 右侧(有Dropout):协同适应显著减少。特征更加清晰、有意义,它们分布在图像的不同位置并对不同类型的输入信号做出反应。

这种更健壮的表示直接带来了更好的泛化性能。从该数据集的测试分类错误率图中也能看出,结合了Dropout和其他正则化技术的模型(最后两个),其性能优于未使用Dropout的模型。


总结 📝

本节课中我们一起学习了Dropout正则化技术。我们了解到:

  1. Dropout通过在训练时随机“关闭”神经元,动态创建庞大的模型集成,是一种高效的正则化方法。
  2. 它迫使网络学习冗余且鲁棒的表示,防止特征间的协同适应。
  3. 在推理时,通过简单的权重缩放(乘以Dropout保留概率p),可以近似得到集成预测的结果,且只需一次前向传播。
  4. 虽然可能需要更大的模型和更长的训练,但Dropout通常能显著提升模型的泛化能力,是现代深度学习工具箱中不可或缺的工具之一。

029:数据增强 🎨

在本节课中,我们将要学习一种重要的正则化技术——数据增强。数据增强通过对训练数据进行一系列简单的变换,来生成新的、多样化的训练样本,从而帮助模型学习到更鲁棒的特征,提高其泛化能力。

概述

假设我们要构建一个海獭检测器。从网络上收集的海獭图片中,我们可以看到海獭的姿态、颜色、光照和个体外观都存在巨大差异。我们的深度神经网络需要对这些输入变化具有不变性,才能正确识别所有海獭。

实现这种泛化能力的最佳方式是使用更多数据进行训练。然而在实践中,数据通常是有限的,因为数据的采集和标注成本都很高。数据增强的目标就是:在现有数据集的基础上,通过应用简单的变换,在训练过程中动态地生成新的“伪数据”,从而有效地扩充训练集。

重要的是,这些变换必须保持图像原有的语义。例如,我们不能把海獭变换成看起来像猫的图像。令人惊讶的是,即使是非常简单的操作,如轻微的平移或添加少量像素噪声,通常也能显著提升模型的泛化性能。

几何变换

以下是几种常见的几何变换方法,它们通过改变图像的空间结构来生成新数据。

随机裁剪

一种简单的数据增强方法是随机裁剪图像。将裁剪后的区域重新缩放到原始图像大小,以确保所有图像尺寸一致。需要注意的是,裁剪区域不宜过小,否则会丢失语义信息,导致无法识别物体。

# 示例:使用库进行随机裁剪
transform = RandomCrop(size=(224, 224))
augmented_image = transform(original_image)

裁剪与填充

这种方法与随机裁剪类似,但在裁剪后,会对图像的边界进行填充。填充可以使用恒定颜色、复制图像边缘颜色或其他策略。

水平翻转

水平翻转图像是另一种常用方法。通常以一定的概率(例如0.5)决定是否对图像进行翻转。但这种方法是否适用取决于具体任务。例如,在涉及交通规则(如靠右行驶)的场景语义分割任务中,水平翻转可能会产生不符合现实的数据。

仿射变换

仿射变换是一种线性变换,可以对图像进行旋转、缩放、剪切等操作。对于变换后出现的空白区域,可以采用恒定颜色填充或从图像边界复制颜色等策略。

分段仿射变换与透视变换

分段仿射变换在图像局部网格上应用不同的仿射变换,实现更局部的扭曲效果。透视变换则自由度更高,能模拟图像平面上的透视效果,使某些区域被挤压而其他区域被放大。

局部滤波与噪声

除了几何变换,我们还可以对图像应用局部滤波或添加噪声,以模拟不同的拍摄条件。

高斯模糊

对图像应用高斯模糊可以模拟不同对焦或运动模糊的情况。这有助于模型学会在不同清晰度下识别物体。

# 示例:随机强度的高斯模糊
transform = GaussianBlur(kernel_size=(5, 5), sigma=(0.1, 2.0))
augmented_image = transform(original_image)

锐化与边缘增强

与模糊相反,我们也可以锐化图像或将其与边缘检测版本结合。但需注意,过度增强可能会使图像偏离合理的语义范围。

添加噪声

向输入图像添加像素噪声是一种非常通用且流行的数据增强技术。深度神经网络对噪声相当敏感,在训练中加入噪声有助于提升模型鲁棒性。

噪声类型包括:

  • 高斯噪声:向每个像素独立添加高斯噪声。
  • 椒盐噪声:以一定概率将像素设置为纯黑或纯白。
  • 随机遮挡噪声:随机将图像中的某些区域置为黑色(如Cutout方法)。

此外,噪声不仅可以添加到网络输入,也可以添加到网络中间层(类似于Dropout的思想),从而在更高层次的表征上引入鲁棒性。

颜色变换

颜色变换对于许多数据集的成功至关重要,因为相机传感器、白平衡和光照条件都会导致颜色差异。

对比度与亮度调整

可以随机改变图像的整体对比度和亮度。也可以分通道调整,制造出色彩偏移的效果。

局部亮度调整与色调/饱和度变换

基于频率噪声局部调整亮度,或改变图像的色调和饱和度。这些变换可以模拟不同光照和色彩环境。

颜色反转与灰度化

将颜色反转或转换为灰度图像。虽然反转后的图像与现实世界差异较大,但仍可能对模型学习有益。

添加特效

可以添加一些简单的视觉特效来模拟不同天气条件,例如雪、云、雾等。这些效果通常可以高效计算,无需预先生成。

组合变换与注意事项

在实际应用中,我们通常会将多种变换随机组合,并应用于整个数据集的每一张图像,且最好在训练过程中动态生成。

随机组合变换

从定义好的变换集合中随机选择并组合多种效果,应用于每一个训练样本。

输出空间的变换

对于分类任务,通常不需要变换标签。但必须小心避免那些会改变语义的变换(例如,水平翻转手写字母“D”会得到“B”,旋转数字“6”会得到“9”)。

对于具有结构化输出的任务(如语义分割、关键点检测、边界框预测),如果对输入进行了几何变换,则必须相应地变换真实标签(Ground Truth)。对于深度预测等任务,变换则更为复杂。

实践要点与总结

  • 公平比较:在比较不同模型时,必须使用完全相同的数据增强策略,否则性能提升可能归因于增强策略而非模型本身。
  • 网络设计的一部分:将数据增强视为模型设计的一部分,选择计算高效的变换以便在GPU上动态进行。
  • 分布选择:为变换参数(如裁剪范围、噪声强度)指定合适的随机分布范围,这通常需要通过实验和经验来确定。
  • 与集成学习结合:数据增强可以与集成学习思想结合。例如,在测试时对同一张图片的不同裁剪区域进行预测并平均结果,可以提升性能。
  • 自动化搜索:已有研究尝试使用强化学习等方法,自动地为特定数据集寻找最优的数据增强策略组合。

本节课中,我们一起学习了数据增强这一强大的正则化技术。我们介绍了包括几何变换、滤波噪声、颜色变换在内的多种增强方法,并讨论了在实际应用中组合使用这些方法以及需要注意的要点。正确使用数据增强能显著提升深度学习模型的泛化能力和鲁棒性。

030:优化挑战 🎯

在本节课中,我们将要学习深度神经网络优化过程中面临的主要挑战。我们已经了解了梯度下降和随机梯度下降这两种最基本的优化方法。本节将深入探讨在优化深度模型时可能遇到的各种困难,例如局部最小值、学习率选择、悬崖、鞍点和高原等问题。

优化基础回顾

上一节我们介绍了梯度下降的基本概念,本节中我们来看看它在实际应用中可能遇到的问题。梯度下降的标准公式如下:

公式: W_{t+1} = W_t - η * ∇L(W_t)

其中:

  • W_t 是第 t 次迭代时的参数。
  • η 是学习率(步长)。
  • ∇L(W_t) 是在 W_t 处计算的损失函数梯度。

我们从某个初始参数 W_init 开始,然后根据上述公式迭代更新参数。参数通常不会初始化为零,例如,基于零均值高斯分布的初始化是更好的选择。但在讨论简单低维优化问题时,初始化不那么关键。

主要优化挑战

以下是深度学习中常见的几种优化挑战。

1. 非凸性与局部最小值

深度神经网络的损失函数几乎总是非凸的。这意味着存在多个局部最小值,而不仅仅是单一的全局最小值。

图示说明: 在一个一维非凸函数中,根据梯度下降的起始点不同,优化器可能收敛到不同的局部最小值,而非全局最优解。

在神经网络中,一个有趣的事实是:许多局部最小值实际上是同等优秀的。例如,在一个多层感知机中,随机置换某一隐藏层中的神经元及其连接,会得到一个参数化不同但性能完全相同的网络。因此,尽管梯度下降可能无法找到全局最小值,但它找到的许多局部最小值在实践中仍然非常有用。

2. 学习率选择

学习率 η 的选择至关重要,它直接影响优化的速度和稳定性。

  • 学习率过低: 优化过程会变得非常缓慢。越接近最小值,梯度越小,步长也越小,导致收敛速度极慢。
  • 学习率过高: 可能导致发散。在凸函数(如抛物线)中,过高的学习率会使参数更新“越过”最小值,落到损失值更高的点。后续迭代中,梯度可能变得更大,导致参数在最优解两侧来回震荡,最终远离最优解。

3. 悬崖

损失函数景观中可能存在非常陡峭的区域,即“悬崖”。这些区域具有极高的梯度值。

问题: 当优化器进入悬崖区域时,巨大的梯度会将参数“弹射”到很远的地方,严重破坏优化进程,其效果类似于学习率过高导致的发散。

应对策略: 一个常用的启发式方法是梯度裁剪。它将梯度向量的每个元素限制在一个预先设定的范围内,防止过大的梯度对优化产生不利影响。

4. 鞍点

鞍点是梯度为零的点,但它既不是局部最小值也不是局部最大值。在某个方向上函数值上升,在另一个方向上函数值下降。

图示说明: 在一个三维曲面中,鞍点在一个方向上是谷底,在垂直方向上是峰顶。

在深度学习中,鞍点的数量相对于局部最小值呈指数级增长。这是因为在超高维参数空间中,所有维度同时朝同一方向(都向上)的概率极低,而部分向上、部分向下的组合则多得多。

影响: 鞍点本身只有在参数精确位于该点时才会导致优化停滞。但实践中,由于随机性,精确命中鞍点的概率很低。只要稍微偏离,梯度就会引导参数继续下降。

5. 高原

高原是指梯度接近于零的平坦区域。

问题: 在高原上,优化进展非常缓慢,甚至完全停止。这会导致训练时间大幅延长。

常见原因: 激活函数饱和(如Sigmoid函数在两端梯度接近零)或“死亡”的ReLU神经元(输出恒为零,梯度为零)。

6. 病态曲率

某些函数(如经典的Rosenbrock函数)具有狭窄而弯曲的峡谷状景观。

挑战: 沿着峡谷底部前进的方向梯度很小,但峡谷两侧的墙壁非常陡峭。优化器很容易在更新时“ overshoot”(越过)到峡谷壁上,从而被巨大的梯度弹射出峡谷,难以稳定地沿着最优路径前进。

总结

本节课中我们一起学习了深度神经网络优化面临的主要挑战。我们回顾了非凸损失函数中的局部最小值问题,探讨了学习率设置不当(过低或过高)的影响,并认识了悬崖、鞍点、高原和病态曲率等复杂地形给优化带来的困难。理解这些挑战是选择和设计更高级优化算法的基础。在下一节,我们将介绍能够更好应对这些挑战的优化算法变体。

031:优化算法 🚀

在本节课中,我们将要学习深度学习中的核心优化算法。我们将从最基础的梯度下降法开始,逐步探讨其变体,如随机梯度下降、带动量的随机梯度下降、RMSProp以及目前最流行的Adam优化器。理解这些算法如何工作,以及它们各自的优缺点,对于高效训练神经网络至关重要。

回顾:标准梯度下降法 📉

上一节我们介绍了优化的基本概念,本节中我们来看看最基础的优化算法——标准(或“朴素”)梯度下降法。

该算法的步骤如下:

  1. 初始化权重参数 W00 代表时间步或迭代次数)。
  2. 选择一个标量学习率 η
  3. 对于数据集中的每一个数据点,执行以下操作:
    • 前向传播输入,计算预测值。
    • 从损失节点开始反向传播,计算损失函数相对于模型参数 W 的梯度。这是单个数据点的梯度。
  4. 将所有数据点的平均梯度取负方向,移动一小步来更新参数。
  5. 如果验证误差下降,则回到步骤2重复;否则停止。

然而,在深度学习中,我们通常要处理数百万个参数和大量的训练数据点,这使得上述更新步骤的计算成本非常高。同时,深度学习通常需要很多次更新,因此这种方法会变得非常慢,甚至无法放入GPU内存。

随机梯度下降法 🔄

为了解决标准梯度下降法效率低下的问题,在实践中我们使用随机梯度下降法。

其核心洞察是:在整个训练集上的总损失可以表示为一个期望值。这个期望值可以用数据的一个较小子集(即小批量)来近似。因此,真实的梯度也可以用基于这个小批量的梯度来近似。

以下是关于SGD的一些要点:

  • 这个较小的数据集被称为小批量。在本课程中,我们通常用大写 B 表示批量大小。
  • 应选择GPU内存允许的最大批量大小 B。通常 B 远小于数据集大小,例如 8, 16, 32, 64。
  • 较小的批量大小会导致梯度方差更大,更新更“嘈杂”,但随着我们进行许多步更新,这种效应会随时间被平均掉。
  • 批量可以随机选择,也可以在优化前对数据集进行分区。需要注意,在分区时应先打乱数据集,因为原始数据通常是顺序记录的,元素间可能存在相关性。

我们还需要明确两个术语:

  • 一次迭代:基于单个小批量进行一次梯度更新。
  • 一个周期:完整遍历一次训练集。如果小批量大小为 B,数据集大小为 N,那么一个周期包含 N/B 次迭代。

以下是SGD算法步骤:

  1. 初始化权重 W,选择学习率 η 和小批量大小 B
  2. 随机抽取小批量数据。
  3. 对于小批量中的每个元素:
    • 前向传播输入。
    • 反向传播梯度。
  4. 沿小批量平均梯度的负方向移动一小步来更新参数向量。
  5. 如果验证误差下降,则继续;否则停止。

这种方法允许在有限内存(如GPU内存)下进行训练,但同时也引入了随机性,因为批量梯度只是真实梯度的近似。

优化算法可视化示例 📊

为了直观地比较不同优化器,我们将使用一个简单的二维二次函数作为损失函数示例:
L(W1, W2) = 0.1 * W1^2 + W2^2
该函数在 W1 方向变化较缓,在 W2 方向变化较陡。其梯度为:
∇L = [0.2 * W1, 2 * W2]
为了模拟小批量带来的随机性,我们会在梯度上添加少量噪声。

现在,让我们将SGD更新规则 W_{t+1} = W_t - η * ∇L_t 应用于此问题。

  • 学习率过小(如 η=0.1):初期梯度较大时,步长尚可;但当接近谷底梯度变小时,步长也随之变小,收敛缓慢。
  • 学习率过大(如 η=1.01):模型剧烈振荡,甚至可能发散。
  • “良好”学习率(如 η=0.99):振荡着接近最小值,但效率仍然不高,且由于固定学习率和梯度噪声,永远无法真正收敛到最优点并保持稳定。

学习率选择与SGD收敛性 ⚖️

如何选择合适的学习率?一种方法是线搜索,即计算当前小批量梯度后,通过求解一个优化问题来找到沿该方向最优的步长。然而,对于深度学习,每一步都需要解决大规模系统问题,计算成本高昂,且找到的步长仅对当前小批量最优,因此实践中并不常用。

关于SGD的收敛性,即使从最优解开始,由于梯度噪声,SGD也会步离最优点,始终在其周围振荡。这意味着使用固定学习率 η,SGD实际上无法收敛

数学上,序列的收敛意味着存在一个极限,使得序列项最终能无限接近该极限。SGD的参数更新序列 W_0, W_1, W_2, ... 也类似。Robbins-Monro定理指出,如果学习率序列 {η_t} 满足:

  • ∑ η_t = ∞ (学习率衰减不能太快)
  • ∑ η_t^2 < ∞ (学习率衰减必须足够快)
    并且梯度估计是无偏的,那么SGD更新序列将收敛到损失函数的一个局部最小值。
    例如,满足条件的序列可以是 η_t = η / t。因此,通过衰减学习率,可以保证SGD收敛

但SGD仍存在问题:所有维度的梯度被同等缩放,这要求我们选择非常保守(小)的学习率来避免发散,但这又会导致更新步长过小,进展缓慢。找到合适的学习率很困难。

带动量的随机梯度下降法 🏃♂️

为了解决SGD的振荡和缓慢问题,我们引入带动量的SGD

观察SGD在示例中的表现:沿 W2 方向(陡峭维度)振荡剧烈,沿 W1 方向(平缓维度)进展缓慢。动量法的思想是:更新权重时,不仅考虑当前梯度,还引入过去梯度的指数移动平均,起到“阻尼”振荡和“加速”平缓方向的作用。

传统动量更新公式为:
v_{t+1} = β * v_t - η * ∇L_t
W_{t+1} = W_t + v_{t+1}
其中 v 是速度(动量),β 是动量系数(通常取0.9)。若 β=0,则退化为SGD。

更清晰的参数化(解耦学习率与动量)为:
v_{t+1} = β * v_t + ∇L_t
W_{t+1} = W_t - η * v_{t+1}

这实质上是在计算梯度的指数移动平均 v_tβ 越大,对过去梯度的记忆越长,平滑效果越强。动量法使SGD更高效,并允许使用稍大的学习率。

Nesterov 加速梯度法 🚄

Nesterov 动量是动量法的一个改进变体。其核心区别在于:计算小批量梯度时,不是在当前位置 W_t,而是在一个“前瞻”位置 W_t + β * v_t 处计算。

更新公式为(解耦形式):
v_{t+1} = β * v_t + ∇L(W_t + η * β * v_t)
W_{t+1} = W_t - η * v_{t+1}

这相当于先根据当前动量“跳一步”到预测的未来位置,然后在该位置计算梯度并进行修正。这种“前瞻性”更新使优化器响应更快,能更有效地减少振荡。它在训练循环神经网络等复杂模型时表现尤其出色。

RMSProp 优化法 📈

RMSProp 的动机与动量法类似,但策略不同。它观察到梯度分布通常不均匀(如示例中 W2 方向梯度大,W1 方向梯度小),这迫使SGD使用保守的学习率。

RMSProp 的想法是:根据每个参数维度上梯度平方的移动平均,来单独调整该维度的学习率。对于梯度大的维度,除以较大的数来减小步长;对于梯度小的维度,除以较小的数来增大步长。

更新公式为:
s_{t+1} = β_2 * s_t + (1 - β_2) * (∇L_t ⊙ ∇L_t) // ⊙ 表示逐元素乘法
W_{t+1} = W_t - η * ∇L_t / (√(s_{t+1}) + ε)
其中 s 是梯度平方的指数移动平均(估计未中心化的方差),β_2 通常取 0.99,ε(如1e-8)是为防止除零的小常数。

RMSProp 能更快收敛。但一个问题是在初始迭代时,移动平均 s 偏向于初始值(通常为0),导致初期更新步长异常大(因为除以一个很小的数),产生“跳跃”行为。Adam 优化器将解决这个问题。

Adam 优化法 🤖

Adam 是当前最常用、默认的优化器,因为它结合了动量法和RMSProp的思想,并具有鲁棒性和通用性。它同时保持梯度的一阶矩(均值,类似动量)和二阶原始矩(未中心化的方差,类似RMSProp)的指数移动平均,并进行偏差校正。

完整Adam更新公式如下:

  1. 计算有偏一阶矩估计:m_t = β_1 * m_{t-1} + (1 - β_1) * g_t
  2. 计算有偏二阶矩估计:v_t = β_2 * v_{t-1} + (1 - β_2) * g_t^2
  3. 计算偏差校正后的一阶矩估计:m̂_t = m_t / (1 - β_1^t)
  4. 计算偏差校正后的二阶矩估计:v̂_t = v_t / (1 - β_2^t)
  5. 更新参数:W_t = W_{t-1} - η * m̂_t / (√(v̂_t) + ε)

偏差校正步骤 34 至关重要,它消除了初始迭代时 mv 偏向于零的影响。数学上,可以证明未经校正的 m_t 的期望是 E[g_t] * (1 - β_1^t),除以 (1 - β_1^t) 后即可得到无偏估计。Adam 通常能快速、稳定地收敛。

也存在结合 Nesterov 动量的 Adam 变体(Nadam),但在大规模问题上相比 Adam 优势不明显,因此使用不广泛。

优化器对比与总结 🏁

不同优化器在不同地形上的表现各异:

  • 多局部极小值问题:不同优化器可能收敛到不同的局部极小点,且速度和振荡频率不同。
  • 陡峭地形:梯度下降法可能振荡剧烈。
  • 平坦高原区域:只有部分优化器(如Adam、RMSProp)能够有效逃离。

在实践中,深度学习框架实现了众多优化器(SGD, Momentum, RMSProp, Adam, Adagrad, Adadelta, Adamax 等)。Adam 由于其鲁棒性和通用性,通常是首选的默认优化器。

需要指出,在经典优化中常使用二阶方法(如牛顿法、拟牛顿法L-BFGS),它们计算或近似海森矩阵(二阶导数)。然而,这些方法不适用于深度学习中的小批量场景(估计不准确),且需要计算和求逆参数数量平方级别的矩阵,计算量巨大,因此通常不用于训练深度神经网络。


本节课中我们一起学习了深度学习的核心优化算法。我们从计算成本高昂的标准梯度下降法出发,引入了基于小批量的随机梯度下降法以提高效率。为了改善SGD的振荡和收敛慢的问题,我们学习了带动量的SGD及其改进版Nesterov动量。接着,我们探讨了通过自适应调整每个参数学习率的RMSProp方法。最后,我们详细介绍了结合动量和自适应学习率,并包含偏差校正的Adam优化器,它因其卓越的鲁棒性成为当前实践中的主流选择。理解这些算法的原理和适用场景,将帮助你为不同的深度学习任务选择合适的优化工具。

032:优化策略 🚀

在本节课中,我们将学习一系列实用的优化策略。这些策略能帮助我们在使用优化器的基础上,进一步提升深度神经网络的训练效果和稳定性。

上一节我们介绍了几种常见的优化器。本节中,我们来看看如何通过调整学习率、监控训练过程、选择超参数等策略来优化整个训练流程。

选择合适的学习率计划 📉

选择正确的学习率计划非常重要。我们已经知道,学习率应该逐渐降低。否则,模型会在最优值附近持续振荡。然而,学习率也不应下降过快,否则模型可能会被困在远离优质解的区域。

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

  • 固定学习率:这不是一个好方法。它无法收敛,并且在训练初期下降太慢,在后期又下降太快。
  • 反比例衰减:这是一种流行的学习率计划,其动机源于罗宾斯-门罗定理。公式为:η_t = η / (1 + βt)。然而,这种衰减速度非常快,通常过快。此外,衰减系数 β 仍需手动选择和调整。
  • 指数衰减:公式为 η_t = η * α^t,其中 α 是一个超参数。这同样需要手动设置初始学习率 η 和衰减率 α
  • 阶梯衰减:这是实践中最常见的方法。我们让模型在特定学习率下收敛,一旦发现其变化不大,就降低学习率。例如,将学习率减半。阶梯的位置(即何时降低学习率)通常基于实验来确定。

主动监控训练过程 📊

在训练过程中主动监控非常重要。这有助于我们直观理解训练进程和模型行为,从而判断哪些超参数需要调整、模型或数据是否存在普遍问题,或者是否应选择不同的优化器。

以下是观察学习曲线的一些示例:

  • 欠拟合:如果训练损失和验证损失从一开始就无法显著下降,只有微小的线性减少,而没有初期急剧下降然后收敛的典型曲线,则可能表明模型容量不足,处于欠拟合状态。
  • 训练不足:如果观察到损失在下降,但模型可能尚未收敛,那么通常只需要延长训练时间。某些模型可能需要训练数天甚至数周。
  • 过拟合:如果训练损失持续下降,但验证损失开始再次上升,这就是我们之前见过的典型过拟合情况。此时可能需要使用正则化(如早停)或调整模型容量。
  • 验证集噪声过大:如果训练损失收敛且曲线平滑,但验证曲线非常嘈杂且振荡,这可能意味着验证集选择得过小。验证集太小会导致评估结果噪声过大,失去意义。
  • 验证集比训练集更简单:在某些情况下,验证集可能实际上比训练集更容易,这也会导致曲线异常。

为了有效监控,强烈建议使用社区提供的工具。一个几乎人人都在使用的优秀工具是 TensorBoard。它最初来自 TensorFlow 社区,但也有许多适配 PyTorch 的版本。它允许你在训练过程中轻松监控学习和验证曲线,以及其他指标如准确率。你还可以查看单个小批次、使用各种可视化方法查看样本、显示神经网络图以及激活的分布直方图。这对于调试神经网络至关重要。

选择超参数 ⚙️

超参数是所有不由梯度下降优化算法本身优化的参数。例如,神经网络的权重是参数,而不是超参数。但网络架构本身(不由梯度下降优化)、迭代次数、批次大小、学习率计划或不同正则化项的权重,这些都是超参数。

我们的模型中超参数的数量远少于参数数量,这很好,但仍有相当数量的超参数需要高效搜索。

高效搜索超参数空间是一个难题。目前最常见的情况仍然是基于直觉手动搜索:建立直觉、查看当前参数附近的局部区域、查阅文献(论文通常有专门章节说明如何选择这些参数)。从一个好的模型或相关模型开始,并据此选择超参数,是一个好策略。

当然,如果你有足够的计算资源,可以进行网格搜索随机搜索。你定义一些范围,系统地尝试这些值,直到获得较低的验证误差。但这需要大量昂贵的训练运行。随机搜索与网格搜索类似,只是搜索点不是规则的网格,而是更随机。你还可以进行多尺度搜索:从粗糙的网格开始,然后逐步缩小范围。

结合人类建立的直觉仍然是个好主意,因为参数搜索空间仍然太大,难以高效地进行纯自动化搜索。值得一提的是,有一个完整的研究领域致力于自动化这个过程,即神经架构搜索。但许多此类算法需要大量计算资源和大型 GPU 集群,因此通常只有愿意投入这些资源的大公司才能使用。也有一些 NAS 算法尝试基于梯度的方法,但这要困难得多,而且这些算法通常只能操纵架构的较小组件,无法探索整个超参数搜索空间。

如何开始:从小处着手 🛠️

当你实现模型并想要训练和测试时,如何开始?一个糟糕的策略是构建整个模型,然后直接在完整数据集上运行。这几乎总是行不通,或者即使行得通,也可能给你一种它工作正常的错觉,但实际上并非如此。

一个好策略是从小处着手。甚至可以从单个训练样本开始,使用一个非常小的网络(可能只有你最终想开发模型的五分之一层数)。首先验证模型的输出是否正确。然后通过过拟合这一个训练样本,你可以看到训练过程是否有效,因为准确率应该达到 100%。你的模型在几乎所有情况下都应该足够灵活,可以精确拟合这个样本。如果这都做不到(你会惊讶于这种情况经常发生),那么肯定有问题,你需要先找到并解决它,否则所有后续结果都将毫无意义。

此外,当你在单个训练样本上用小网络过拟合时,调试周期非常快,这当然很重要。如果你训练模型三周后才发现错误,那就损失了三周时间;但如果能在几分钟内完成,你就能快速得到答案。

你需要选择一个好的学习率,应该选择实践中常用的值。一旦这步工作正常,你可以开始增加数据集规模,增加到 10 个训练样本(而非完整数据集),并再次验证输出是否正确。过拟合 10 个样本仍应产生接近 100% 的准确率。你还可以测量单次迭代的时间,以识别模型中的瓶颈(例如数据加载瓶颈)。一旦这些都正常工作且速度满足要求,你就可以将数据集规模从 100 增加到 1000、10000 个样本,同时开始增加网络规模。通过观察训练和验证曲线,你应该在某个时刻看到泛化效果。重要的是,一次只做一个更改,以便识别问题的潜在原因。

改善梯度流 🌊

梯度流非常重要。如果在反向传播期间没有良好的梯度流,某些参数就无法得到适当更新。因此,保持良好的梯度流至关重要,你应该可视化梯度流并确保它始终正常。

我们已经知道初始化非常重要。你应该使用适当的初始化方法,例如高斯初始化,使所有层的激活分布保持恒定(相同的均值和标准差)。

另一种在优化期间(不仅仅是初始化时)强制执行激活分布跨层恒定的技术叫做批归一化。批归一化的做法是,对于单个批次(B 是批次大小),分别计算每个通道激活的均值和方差(C 是通道或神经元)。然后减去均值并除以方差,得到在小批次上白化的信号。因为这不够灵活(任何之前添加的偏置都会被此操作消除),所以这里又添加了一个额外的可学习偏置。这样,你通过批次的均值和方差对每个通道进行归一化。这在训练时有效,因为我们可以对批次进行平均。在测试时,你只需取训练期间估计的均值和方差的运行平均值,并保持固定。这样,整个操作实际上就变成了一个线性操作,我们有了这个可学习的尺度和位移参数。我们可以在每个全连接层之后、激活函数之前添加这样一个批归一化层,这将有助于训练期间激活的分布。

批归一化有多种变体。除了默认的批归一化(在批次上归一化),还有层归一化(在层上归一化)、实例归一化,以及组归一化。这些主要适用于图像和卷积网络。

残差网络与深度模型 🧱

对于深度网络,一个特别棘手的问题是梯度传播非常缓慢。因为梯度是反向传播的,如果你需要反向传播很多层(可能数百或数千层),那么一旦到达早期层,梯度会变得非常小。有论文观察到,在 ImageNet 挑战赛中,随着网络越来越深,更深的网络性能往往比更浅的差。这与直觉相悖,因为如果你有一个浅层网络,并添加恒等变换作为层,那么深层模型在理论上至少应该和浅层模型一样好。

残差网络中提出的解决方案是,学习残差映射而不是经典的卷积或全连接层。这里有一个跳跃连接,这是残差网络中的一个块,网络中有许多这样的块。跳跃连接将输入添加到这两层的输出,使得这两层只需要学习 X 的增量,这要容易得多。因为现在实现恒等变换要容易得多:恒等变换就是让这两层基本上预测零,这样输入就直接作为输出传递。事实证明,这在模型中使用这些残差块来学习深度模型非常有帮助,如今这也已成为常见做法。

训练计划与课程学习 📚

如果你有一个小数据集,实践中通常通过预训练来提高性能。预训练意味着你预训练你的主干网络(即除最后几层特定于任务层之外的所有层)。例如,在 ImageNet 这样的大型带标签数据集上预训练主干网络。网络通过这个过程已经学习了很多关于图像如何形成的有用信息。然后,你可以在目标任务和数据集上微调最后几层或整个架构,这些数据集标注样本较少。事实证明,这能显著提高性能。因此,许多人从在某些任务上预训练网络开始,然后针对感兴趣的任务进行微调。这是一种迁移学习。

另一种策略是使用自监督学习,你在一个任务上预训练主干网络,该任务的监督信号来自数据本身(例如去噪或修复任务)。我们将在本课程后面更详细地讨论其中一些内容。

还有课程学习,对于某些任务(如光流估计)也极其重要。课程学习的理念是,从在一些简单的数据集上学习开始,然后逐步增加数据的难度。例如,在光流估计中,你从一些非常简单、复杂度不高的场景开始,为它们学习一个光流估计器。然后,你继续用越来越难的数据集进行训练,直到达到你感兴趣的真实数据集。事实证明,这对于某些任务获得良好性能非常有帮助。因此,让模型先学习简单的东西,然后再处理更难的问题,这很重要。这也与人类学习有关,实际上也观察到这种渐进过程很重要。

本节课中,我们一起学习了多种优化策略,包括学习率调整、训练监控、超参数选择、从小规模开始的调试方法、改善梯度流的技术(如批归一化)、用于训练深度网络的残差连接,以及预训练和课程学习等高级训练计划。掌握这些策略将帮助你更高效、更稳定地训练深度学习模型。

033:优化与调试策略 🐛

在本节课中,我们将学习如何识别和解决深度神经网络训练过程中常见的故障。我们将探讨一系列实用的调试策略,帮助你系统地定位问题根源,确保模型能够成功训练。

上一节我们介绍了优化算法的理论基础,本节中我们来看看如何将这些知识应用于实际问题排查。

数据集问题

检查输入数据是调试的第一步。确保馈送给网络的数据是正确且有意义的至关重要。

以下是常见的数据集相关问题及检查方法:

  • 检查输入数据:打印并可视化几个批次的数据,确保图像尺寸、通道顺序、标签索引等正确无误。一个常见的错误是混淆了图像的宽和高。
  • 尝试随机输入:用随机数代替真实数据进行前向传播。如果错误表现相同,则表明网络在某个环节将数据变成了无意义的垃圾。
  • 检查数据加载器:数据在磁盘上可能完好,但加载到内存时可能因加载器错误而损坏。确保第一层之前的输入是正确的。
  • 确保输入与输出对应:检查一些输入样本是否具有正确的标签。在打乱数据时,确保输入和标签以相同的方式被打乱。
  • 验证数据集噪声:手动检查一批输入样本,查看标签是否错误。数据集中存在大量错误标签会阻碍网络学习。
  • 打乱数据:创建小批量数据前打乱数据集,避免同一批次内的样本高度相关,影响学习效果。
  • 处理类别不平衡:对于分类任务,如果某个类别的样本远多于其他类别,可能需要调整损失函数以平衡各类别的重要性。
  • 验证训练样本数量:从头开始训练网络通常需要大量数据。对于图像分类,通常每个类别需要约1000张或更多图像。若数据不足,需加强数据增强或进行预训练与微调。
  • 确保批次随机性:在分类任务中,确保每个小批量包含不同标签的混合样本,而非单一标签。
  • 从标准数据集开始:测试新网络架构或代码时,首先使用MNIST、CIFAR-10或ImageNet等标准数据集。这些数据集有已知的参考结果,能帮助你确认模型基础能力。

数据标准化与增强问题

数据预处理不当也会导致模型无法收敛或性能不佳。

以下是相关的注意事项:

  • 标准化输入:如果输入值范围差异很大,将其标准化(例如,零均值、单位方差)通常有助于训练。公式示例:x_normalized = (x - mean) / std
  • 避免过度数据增强:数据增强具有正则化效果,但过度增强结合其他正则化方法可能导致网络严重欠拟合。
  • 检查预训练模型的预处理:使用预训练模型时,确保在训练和测试阶段采用与原始模型完全相同的归一化和预处理策略(例如,图像像素值范围是[0,1]还是[0,255])。
  • 统一预处理流程:任何预处理统计量(如数据均值)应仅基于训练数据计算,然后应用于验证集和测试集。错误做法:在整个数据集上计算均值并减去,然后再划分训练、验证和测试集。

实现问题

代码实现中的细微错误常常难以察觉,需要系统性地检查。

以下是一些关键的实现检查点:

  • 先解决简化问题:尝试解决一个更简单的任务版本,这有助于抽象理解问题,更快定位错误。例如,若目标输出是物体类别和坐标,先尝试仅预测类别。
  • 确保训练流程正确:检查是否在训练和验证模式间正确切换;确保使用正确的输入;在反向传播前不要忘记将梯度置零。代码示例(PyTorch风格):
    optimizer.zero_grad() # 梯度清零
    output = model(input) # 前向传播
    loss = criterion(output, target) # 计算损失
    loss.backward() # 反向传播
    optimizer.step() # 更新参数
    
  • 检查损失函数:如果实现了自定义损失函数,仔细检查其中是否有错误。可视化损失,并尽可能为其添加单元测试(例如,计算导数的数值差分进行验证)。
  • 验证损失函数输入:使用框架提供的损失函数时,确保传入它期望的输入。例如在PyTorch中,负对数似然损失(NLLLoss)需要输入经过LogSoftmax的值,而交叉熵损失(CrossEntropyLoss)则直接输入原始逻辑值(logits)。
  • 调整损失权重:如果总损失由多个子损失函数组成,确保它们之间的相对权重设置正确。通常应尽可能减少损失函数的数量以降低超参数复杂度。
  • 监控其他指标:不要只看损失函数。使用准确率等其他指标来更全面地评估网络训练状况。
  • 测试自定义层:如果网络中有自定义实现的层,务必反复检查,确保其按预期工作。
  • 检查输出格式:确保网络输出格式与损失函数期望的输入格式匹配。
  • 检查冻结的层或变量:确认是否无意中通过分离梯度等方式,禁用了某些本应可学习层的梯度更新。
  • 增加网络容量:如果网络表现欠拟合,可能是其表达能力不足。尝试增加层数或隐藏单元数量。
  • 检查隐藏层维度错误:使用张量时,很容易弄错维度。一个技巧是使用不同的质数作为输入维度,观察它们在网络中传播时,各中间层的维度是否正确。
  • 梯度检查:如果手动实现了梯度下降或特定计算节点,并且没有使用自动微分,请使用数值微分等方法确保梯度计算正确。

训练问题

训练过程中的超参数设置和监控同样关键。

以下是训练阶段需要关注的重点:

  • 从小数据集开始:这是非常重要的第一步。尝试在极小的数据子集(如一两个样本)上过拟合,以确保训练机制本身能够工作。这是一个必要条件。
  • 检查权重初始化:如果不确定,使用Xavier或He初始化。糟糕的初始化可能导致陷入不良的局部最小值,尝试不同的初始化方式可能会有帮助。
  • 调整超参数:可能当前使用的超参数组合不佳。尝试修改它们,如果可能,进行小范围的网格搜索。
  • 减少正则化:过多的正则化(如Dropout、L2正则化)会导致网络欠拟合。尝试减少正则化强度。
  • 给予足够时间:深度学习优化可能非常缓慢。如果损失在稳步下降,让模型训练更长时间,结果可能会持续改善。
  • 切换训练/测试模式:确保在预测(测试)时,将模型切换到正确的模式。像Batch Normalization和Dropout这样的层在训练和测试时的行为是不同的。
  • 可视化训练过程:监控每一层的激活值、权重和更新量。例如,参数更新的量级通常应在 1e-3 左右。可以使用TensorBoard等工具,或直接打印权重、偏置和激活值进行观察。
  • 留意激活值:注意那些均值远大于0的层激活。在使用Batch Norm后,权重直方图应随时间呈现近似高斯分布。偏置的直方图通常从0开始,最终也趋于高斯分布。警惕那些变得异常大(发散)的参数。
  • 尝试不同的优化器:优化器的选择通常不会阻止网络训练,除非超参数设置极差。但针对特定任务选择合适的优化器,有助于在最短时间内获得最佳训练效果。
  • 梯度爆炸/消失:检查层更新值,过大的值可能表明梯度爆炸,此时梯度裁剪可能有帮助。同时检查激活值的标准差,理想范围通常在0.5到2之间。显著超出此范围可能表明梯度消失或爆炸。
  • 调整学习率:过低的学习率会导致模型收敛极慢;过高的学习率初期损失下降快,但可能难以找到好的解甚至发散。尝试将当前学习率乘以0.1或10进行调节。
  • 处理NaN(非数值):NaN的出现通常源于除零、对零或负数取自然对数等操作。如果训练初期出现NaN,可能是模型发散了。尝试逐层评估网络,找出NaN首次出现的位置。

本节课中我们一起学习了深度神经网络训练中常见的故障类型和一套系统的调试策略。从检查数据、验证实现到监控训练过程,每一步都至关重要。记住核心原则:简化问题、从小开始、逐步增加复杂性、并充分利用可视化工具。通过耐心和系统性的排查,你将能更有效地解决训练难题,构建出性能优异的模型。

034:卷积 👁️

在本节课中,我们将要学习卷积神经网络的基础——卷积层。卷积神经网络是现代几乎所有处理图像输入的深度神经网络架构的骨干。我们将从卷积层的基本概念开始,理解其工作原理、数学表达以及它为何如此高效。

概述 📋

本节课是深度学习课程的第七讲,我们将聚焦于卷积神经网络。卷积层是CNN的核心组件,它通过局部连接和权值共享,极大地减少了参数数量,并引入了平移等变性这一重要的归纳偏置,使得网络能够高效地处理图像数据。

卷积层的基本概念 🧠

上一节我们介绍了卷积神经网络在图像分类任务中的成功应用。本节中,我们来看看构成这些网络的基础模块——卷积层。

在图像分类任务中,目标是构建一个模型,输入一张图像,输出其所属的类别(例如“公交车”)。传统方法依赖于手工设计的特征(如梯度、直方图),然后输入到分类器(如支持向量机)中。然而,这种方法在2008-2009年左右性能达到瓶颈。

直到2012年,随着现代卷积深度神经网络的出现,性能才得以大幅提升。深度神经网络无需手工特征,而是通过反向传播自动学习所有中间表示。对于图像输入,标准的多层感知机并非理想选择,而卷积层则为此量身定制。

一个典型的卷积神经网络(如VGG)包含三种层:

  • 卷积层(白色):提取特征。
  • 池化层(红色):降低特征图的空间维度。
  • 全连接层(蓝色):最终进行分类。

与MLP不同,卷积网络在操作中会保持特征图的空间维度,并逐步通过池化层缩小尺寸,同时增加特征通道数(即每个空间位置的神经元数量),最终通过全连接层输出类别概率。

卷积层的数学描述与实现 🔢

上一节我们了解了卷积层的直观作用。本节中,我们来看看如何用数学语言精确地描述它。

首先,我们引入贯穿本讲的符号约定:

  • B:批量大小。
  • W, H:特征图的宽度和高度。
  • C:特征通道数(每像素的神经元数)。
  • C_in, C_out:特定层的输入和输出通道数。
  • K:卷积核(滤波器)的大小(假设为K×K像素)。

与MLP的二维张量(B, C)不同,卷积层处理的是四维张量(B, C, H, W),它保留了空间维度。

全连接层 vs. 卷积层

以下是全连接层与卷积层的核心区别:

  • 全连接层:输出层的每个神经元与输入层的所有神经元相连。参数数量为 (W*H*C_in + 1) * (W*H*C_out),对于高分辨率图像,参数量巨大。
  • 卷积层:输出层的每个神经元只与输入层中一个局部区域(如3×3)的神经元相连,并且相同的滤波器权重被应用于输入的所有空间位置(权值共享)。参数数量为 (K*K*C_in + 1) * C_out,参数量显著减少。

权值共享的直觉是:在图像中,无论物体出现在哪里,检测边缘或纹理等特征的滤波器应该是相同的。这大大提升了网络的数据效率。

爱因斯坦记号

为了简洁地描述卷积运算,我们使用爱因斯坦记号。在这种记号中:

  • 小写索引(如 i, j)表示张量的单个元素。
  • 大写索引(如 I, J)表示张量的整个维度(切片)。
  • 乘积中重复的大写索引意味着沿该维度求和。

例如,矩阵乘法 y = Ax 可以写为 y_I = A_I^J x_J,其中对 J 的求和是隐含的。

卷积运算公式

使用爱因斯坦记号,卷积层的前向传播可以形式化地表示为:

H_{b, x, y, c_out}^{(l+1)} = g( A_{c_out}^{c_in, \Delta x, \Delta y} H_{b, x+\Delta x, y+\Delta y, c_in}^{(l)} + b_{c_out} )

其中:

  • H^{(l)}H^{(l+1)} 是第 l 层的输入和输出张量。
  • g(·) 是激活函数(如ReLU)。
  • A 是卷积核权重,维度为 (C_out, C_in, K, K)
  • b 是偏置项,维度为 (C_out,)
  • 求和是隐含的,对索引 c_inΔxΔy 进行。

这个公式清晰地表明:为了计算输出位置 (x, y) 处通道 c_out 的值,我们取输入中以 (x, y) 为中心的 K×K 局部区域,与对应的滤波器权重相乘并求和,加上偏置,最后通过激活函数。

技术备注:实践中,深度学习库实现的是互相关,而非严格的数学卷积(即滤波器不需要翻转)。这对网络的表达能力没有影响。

卷积核示例

虽然CNN的滤波器是学习得到的,但它们常常会学到类似传统图像处理中的滤波器。例如:

  • 高斯模糊核:使图像变得平滑。
  • Sobel边缘检测核:突出图像中的边缘。

卷积的性质:平移等变性 ⚖️

上一节我们形式化地定义了卷积运算。本节中,我们探讨它的一个关键性质——平移等变性,这是CNN成功的重要归纳偏置。

首先区分两个概念:

  • 不变性:对输入进行某种变换(如平移)后,函数的输出保持不变
  • 等变性:对输入进行某种变换后,函数的输出以相同的方式被变换

卷积运算具有平移等变性。这意味着:如果我们将输入图像平移 t 个单位,那么输出的特征图也会被平移 t 个单位。用公式表达,对于平移操作 T,有:

** (A ⋆ T(H)) (x) = T(A ⋆ H) (x) **

直观理解:因为相同的滤波器被滑动应用于图像的每个位置,所以输入图像的移动会直接导致输出特征图的相应移动。

重要性:这种等变性是一种强大的归纳偏置。它告诉网络:“在图像中某个位置有用的特征,在其他位置也同样有用。”这极大地减少了需要学习的参数数量,提高了数据效率,是卷积网络在处理图像任务上取得成功的关键原因之一。

实现细节:计算图与填充 🛠️

上一节我们讨论了卷积的理论性质。本节中,我们来看看它在实现中的两个重要细节:如何在计算图框架中处理权值共享,以及如何处理边界问题。

权值共享与反向传播

在卷积层的计算图中,由于权值共享,同一个滤波器参数会被用于计算输出特征图的每一个位置。这意味着在反向传播时,损失函数相对于该参数的梯度,来自于所有应用了该滤波器的位置。

根据全导数法则,我们需要将所有流入该参数节点的梯度累加起来。这与普通全连接层中一个参数只接收一个梯度的情形不同。正是这种来自图像中大量位置的梯度汇聚,使得每个滤波器参数的更新信息非常丰富,从而实现了更高的数据效率。

填充

卷积运算在边界处会遇到问题:当滤波器中心位于输入图像的边缘时,滤波器的一部分会落在图像外部,导致无法计算。

直接忽略边界会导致特征图尺寸随着网络加深而不断缩小,这通常是不希望的。为了解决这个问题,我们引入了填充

填充 是在输入特征图的边界周围添加额外像素(通常是0)的过程。假设使用 K×K 的滤波器,为了保持输入输出尺寸一致,通常需要在每边填充 P = floor(K/2) 个像素。

例如,对于一个 3×3 的滤波器(K=3),我们设置填充 P=1。这样,输入尺寸为 (H, W),填充后变为 (H+2, W+2),卷积后输出尺寸恢复为 (H, W)

实现时,我们可以先创建一个更大的零张量 H‘,其尺寸为 (H+2P, W+2P),然后将原始输入 H 复制到 H‘ 的中心区域,最后对 H‘ 应用标准的卷积公式。

总结 🎯

本节课中,我们一起学习了卷积神经网络的核心——卷积层。

  1. 我们了解了卷积层通过局部连接权值共享,以远少于全连接层的参数量来处理图像数据。
  2. 我们使用爱因斯坦记号形式化地定义了卷积运算。
  3. 我们探讨了卷积的平移等变性,这是CNN强大的归纳偏置,使其对图像中物体的位置变化具有鲁棒性。
  4. 我们讨论了实现细节,包括权值共享在反向传播中的梯度累积,以及使用填充来处理卷积的边界问题,以保持特征图的空间尺寸。

卷积层是构建现代深度视觉模型的基石。在接下来的章节中,我们将以此为基础,探讨如何通过池化等操作构建更完整的网络架构。

035:下采样

在本节中,我们将学习卷积神经网络中的下采样操作。我们将了解下采样的目的、常见的实现方式(如池化),以及如何计算卷积层输出尺寸。最后,我们会看到如何将卷积层的输出转换为全连接层所需的向量形式。

下采样的目的 🎯

上一节我们介绍了卷积操作,它保持了输入的空间分辨率。然而,下采样操作,例如最大池化,会降低空间分辨率。在下图中,空间分辨率从224x224降低到了112x112。

下采样在卷积网络中非常有用,主要有两个目的:

  1. 逐步降低空间分辨率,最终得到描述整张图像的单一输出(例如一个分类标签)。
  2. 通过观察图像中越来越大的区域,逐步聚合更多关于图像的全局信息。

图中的红色操作标记了池化操作,它逐步降低了空间分辨率。同时,它也增大了感受野,即影响特定神经元的输入区域范围。最终,网络末端的神经元会受到图像中每个像素的影响;而在网络开始的卷积层,只有非常局部的邻域会影响单个激活值。

池化操作 🧊

最简单且最常见的下采样操作之一是池化。池化有多种变体,例如最大池化、最小池化和平均池化。

池化操作没有参数。它对每个通道单独操作,在图像中取一个由池化核定义的特定区域(本例中为3x3),计算该区域的池化值(例如最大池化就是取最大值),并将该值放在输出对应的位置。然后,它以一定的步长移动,而不是查看输入的直接相邻像素。通常步长大于1,例如本例中步长为2,意味着它会跳过一些输入像素。

以下是池化操作的数学表达式。对于输出张量在位置 (x, y) 的值,我们计算输入张量在对应区域的最大值:

公式:
输出[b, x, y, c] = max_{Δx, Δy} ( 输入[b, s*x + Δx, s*y + Δy, c] )

其中,s 是步长,(Δx, Δy) 在池化核范围内遍历。可以看到,这个操作没有参数,只是一个最大值运算,并且对每个输入通道独立进行,因此输出通道数与输入通道数相同。

以下是一个具体示例,我们使用了步长为2、核大小为2x2的最大池化操作,它将空间维度缩小了2倍。

你可以看到,红色输出单元格对应红色输入单元格区域,其值6是红色输入区域中的最大值;绿色输出单元格对应绿色输入区域,其值4是绿色输入区域中的最大值,依此类推。

需要说明的是,最大池化对输入的小幅平移具有一定的不变性,这常被用作解释其有效性的理由。但在实践中,如今人们常用步长卷积来替代最大池化。

步长卷积 🚶‍♂️

步长卷积是一种标准的卷积算子,但其步长大于1。与没有参数的池化操作不同,步长卷积像普通卷积层一样拥有可学习的参数。它可以在不额外添加池化层的情况下降低空间分辨率。

其公式与标准卷积几乎相同,唯一的区别在于对输入位置的索引方式:

公式:
输出[b, x, y, c_out] = σ( Σ_{Δx, Δy, c_in} ( 权重[Δx, Δy, c_in, c_out] * 输入[b, s*x + Δx, s*y + Δy, c_in] ) + 偏置[c_out] )

其中 s 是步长。我们有效地将卷积滤波器在输入张量上移动 s 个像素,而不是1个像素。这是一种可学习的下采样方式,被用于许多现代网络架构(如ResNet)中。

感受野与尺寸计算 📐

感受野被定义为所有能影响特定神经元的输入像素的集合。例如,在一个两层网络中,如果我们想知道第二层某个神经元受哪些输入像素影响,我们可以从该神经元反向追溯。如果两层都使用3x3卷积,那么第一层中只有对应3x3区域的神经元会影响它。再进一步追溯,输入层中影响这些第一层神经元的区域,就是最终的影响区域(本例中为5x5)。这个区域的并集就是该神经元的感受野。

添加更多层或使用池化操作(其步长能更快扩大覆盖范围)会使感受野增长得更快。如果仅使用卷积层来缩小224x224图像的空间分辨率,速度会很慢(线性增长);而使用池化下采样,速度则快得多。

在实现卷积神经网络时,正确计算每个张量的通道数和空间维度非常重要。空间维度取决于输入张量大小、滤波器大小、填充和步长。以下是计算输出空间维度的公式:

公式:
输出宽度 = floor( (输入宽度 + 2*填充 - 核宽度) / 步长 ) + 1
输出高度 = floor( (输入高度 + 2*填充 - 核高度) / 步长 ) + 1

其中 floor 是向下取整运算。我们给输入宽度加上两倍的填充(因为两边都填充),减去核宽度(因为卷积核会消耗一些空间),然后除以步长。如果结果是分数,则向下取整,因为无法处理缺失的输入,最后再加1。

例如,一个5x5的输入,填充为1,步长为4,核大小为3x3。计算过程:(5 + 2*1 - 3) / 4 = 1.0,取整后为1,再加1得到2。因此输出是2x2的张量。

全连接层 🔗

全连接层也是某些卷积网络架构的一部分。实际上,它们是卷积网络架构中内存消耗最大的部分,因为它们是全连接的(例如,一层4096个神经元连接到另一层4096个神经元),相比之下,卷积核的内存占用要轻量得多。

为了应用全连接层,我们需要将空间维度压缩(例如从7x7压缩到1x1)。具体做法与我们之前在MLP练习中将MNIST数字图像压缩为向量的方式相同:展平输入。

假设我们有一个大小为3x3x3的特征图(3个通道,3x3空间尺寸)。我们通过一个简单的重塑操作,将四维张量转换为二维张量。具体来说,我们将每个空间位置的所有通道特征拼接成一个大向量。

代码/操作示意:
展平后的向量 = 将输入张量 [批量大小, 高度, 宽度, 通道数] 重塑为 [批量大小, 高度 * 宽度 * 通道数]

这样,X和Y维度就被重塑到了特征通道维度C中。然后,我们就可以对这个向量应用标准的全连接层了。

网络示例与总结 🏁

下图展示了一个用于图像分类的简单卷积网络示例。你可以看到,第一列是卷积层激活前的输出,第二列是激活后的输出。接着是最大池化操作,它降低了空间分辨率。之后是更多的卷积和池化层。最后,通过一个softmax层来预测输出类别的离散分布。

在整个过程中,特征在空间维度上变得越来越粗糙,但在特征通道维度上则希望变得越来越具有表现力,以最终预测出正确的输出。

本节总结:
本节课我们一起学习了卷积神经网络中的下采样。我们了解了池化(如最大池化)和步长卷积这两种降低空间分辨率的主要方法,并探讨了感受野的概念。我们还学习了如何计算卷积层输出张量的空间尺寸,以及如何将卷积层的输出展平以输入到全连接层。下采样是构建深层、高效卷积网络的关键步骤,它帮助网络逐步从局部特征中提取出全局信息。

036:上采样

在本节中,我们将学习卷积神经网络中的上采样技术。上采样是许多像素级预测任务(如语义分割、深度估计)中的关键步骤,用于将低分辨率特征图恢复到输入图像的高分辨率。

在许多情况下,我们可能不满足于图像级别的预测,而是需要像素级别的预测。例如,我们可能希望预测每个像素位置的深度图、运动向量或语义标签图。就像这里的例子,对于输入图像的每一个像素,我们都需要确定其对应的语义类别标签,例如树木、水池、汽车、道路、人行道或建筑物。

如果目标是像素级预测,那么纯粹的下采样是不够的,我们还需要再次进行上采样。上图展示了一个典型的架构示例,它同时包含用于下采样的卷积和池化操作,以及用于从低分辨率恢复到高分辨率的上采样和卷积操作。

为何需要下采样与上采样?🤔

上一节我们介绍了下采样的必要性,本节中我们来看看为何需要上采样。首先,为什么需要下采样?如果不进行下采样,网络将无法快速聚合大范围图像区域的信息,也就无法获得具有大感受野的强特征。在网络参数数量有限的前提下,下采样是必要的。

上采样则能产生与输入图像相同分辨率的输出。为了保持高精度,特别是在物体边界处,通常会引入类似跳跃连接的结构,将来自网络浅层的信息直接传递到上采样层。这种编码器-解码器网络非常常见,因其形状常被称为U-Net架构。

上采样的实现方法 🛠️

有多种方法可以实现上采样。以下是几种常见的示例:

  • 最近邻上采样:将输出中的每个元素简单地设置为输入特征张量中最近邻的值。
  • 双线性上采样:基于输入像素的加权平均进行插值,通常会产生更平滑的结果。
  • “钉床”上采样:将输入值稀疏地插入到输出张量的对应位置,其余位置填充零,然后通过一个卷积层来填充这些缺失的部分。这种方法效果通常很好。

选择哪种上采样方法取决于具体应用,因为不同类型会产生不同的伪影。例如,双线性上采样结果可能过于平滑;“钉床”上采样中那些突出的非零值(“钉子”)有时在输出中仍然可见。每种技术都有其优缺点。

特殊的反池化:最大反池化 🔄

还有一种特殊的上采样方法叫最大反池化。在最大池化操作时,我们会记录下每个池化区域内最大值的位置。当在上采样阶段需要恢复某个特征图时,我们采用“钉床”表示法,但只在之前记录的最大值位置插入该值。

这种方法旨在保留一些高层次细节和结构,但它要求下采样层和上采样层必须成对对应,因此无法在任意的U-Net架构中随意实现。然而,这种方法曾被用于语义分割领域的奠基性网络之一——SegNet中。

上采样的替代方案:空洞卷积 🌐

除了先下采样再上采样,另一种替代方案是使用空洞卷积。使用空洞卷积,你无需进行下采样,因为它是一种在保持特征图空间分辨率不变的同时,增大感受野的技术。

在空洞卷积中,有一个乘数作用于输入张量的偏移量 delta x,这有效地分散了卷积核在输入上的采样位置。通过不同的空洞率 d,你可以获得一个非常大的有效感受野,而实际上并不需要更多的参数,因为它更稀疏地查询输入。

这种方法在一定程度上是有效的。但即使在原始论文中,为了使其真正有效,仍然需要一些标准的主干网络。其核心思想是,你可以在不增加参数或降低空间维度的情况下增大感受野。你可以在网络的末端,用极少的参数,在同一分辨率下细化你的输出,这通常效果很好。

下图展示了空洞卷积如何逐步增大感受野。

应用实例 📈

这种方法可以应用于语义分割、光流估计等任务。

上图是一个语义分割的例子。可以看到,标准的U-Net架构存在的问题,在一定程度上可以通过空洞卷积来缓解,因为它能更好地保留一些细节。需要说明的是,这张图来自2016年的论文,语义分割领域的最新技术成果自那时起已经取得了长足的进步。

总结 📝

本节课中,我们一起学习了卷积神经网络中的上采样技术。我们首先了解了为何在像素级预测任务中需要结合下采样与上采样,并介绍了经典的编码器-解码器(如U-Net)架构。接着,我们探讨了几种具体的上采样实现方法,包括最近邻、双线性、“钉床”上采样以及特殊的最大反池化。最后,我们学习了上采样的一种重要替代方案——空洞卷积,它能在不损失分辨率的情况下扩大感受野。理解这些技术是掌握语义分割等高级计算机视觉任务的基础。

037:卷积神经网络架构 🏛️

在本节中,我们将学习一些经典的、更具历史意义的卷积网络架构,以及一些更近期的、最先进的卷积网络架构。我们将看到,在过去十年中,网络架构如何演变以实现更高的性能。

概述

我们将回顾从早期突破到现代设计的几种关键卷积神经网络架构。理解这些架构的演变,有助于我们把握设计高效、强大模型的核心思想。


经典架构的演变

上一节我们介绍了卷积神经网络的基本组件。本节中,我们来看看这些组件是如何被组合成具有影响力的网络架构的。

LeNet-5:起点 🚀

卷积网络的起点可以追溯到LeNet-5。这是一个由Yann LeCun等人开发的、相对较浅的网络架构,它奠定了现代卷积网络的基础。

以下是LeNet-5的主要结构:

  • 它仅包含两个卷积层、两个池化层和两个全连接层。
  • 在ImageNet时代之前,它是在MNIST数据集上取得领先性能的架构。
  • 其设计思路是使用卷积和下采样来降低空间维度,最后通过全连接层完成分类。

AlexNet:深度学习的突破 💥

2012年,AlexNet的出现标志着深度学习的重大突破。它首次证明了结合多项技术(如ReLU、Dropout和数据增强)并利用GPU训练,可以将深度学习成功应用于像ImageNet这样的大规模数据集。

以下是AlexNet的关键特点:

  • 它包含8层,由于当时GPU内存限制(仅3GB),其计算被分布在两个GPU上。
  • 网络中出现了特征通道数随深度增加、空间分辨率随深度降低的趋势。
  • 它结合了卷积层、最大池化层、ReLU激活函数,并使用了Dropout和数据增强来正则化训练。

VGGNet:小卷积核的威力 🔍

2015年提出的VGG网络架构强调了使用小卷积核的重要性。它表明,使用堆叠的多个3x3小卷积核,可以替代单个大卷积核(如7x7),并获得相同甚至更大的感受野。

以下是VGG网络的核心思想:

  • 使用更小的卷积核(3x3)可以减少参数数量,同时因为层数更多而引入了更多非线性。
  • 它提出了两个主要变体:VGG-16和VGG-19。
  • 其设计理念是构建更深但参数更少的网络,从而获得更好的泛化能力。公式上,两个3x3卷积层的感受野等同于一个5x5卷积层:receptive_field = 2 * 3 - 1 = 5

GoogLeNet (Inception):并行与高效 🧩

同样在2015年,Google提出了GoogLeNet(或称InceptionNet)。这是一个22层的网络,比VGG更深,但参数却少了27倍,效率极高。

以下是GoogLeNet的创新之处:

  • 它避免了使用庞大的全连接层,快速收敛到Softmax分类。
  • 核心创新是Inception模块,该模块并行使用不同尺度的卷积(1x1, 3x3, 5x5)和池化操作,然后将结果拼接起来,类似于一个集成模型。
  • 它使用了辅助分类器(侧枝输出)来帮助深度网络中的梯度传播。
  • 广泛使用1x1卷积来灵活调整特征通道数,控制计算复杂度。1x1卷积的运算可以表示为对特征图在通道维度上的线性组合。

ResNet:残差连接解决梯度问题 🌉

2016年的ResNet是另一个真正的突破,它使得训练极深的网络(如152层)成为可能,并成为当今大多数先进图像分类架构的基础。

ResNet成功的关键在于解决了深度网络训练中的梯度消失/爆炸问题。作者发现,简单地堆叠更多层数,即使模型有能力学习恒等映射,性能也会下降。

以下是ResNet的解决方案:

  • 引入了残差块。在残差块中,层学习的是输入x与目标输出H(x)之间的残差F(x),即 F(x) = H(x) - x。最终输出为 H(x) = F(x) + x
  • 这种设计让模型更容易学习恒等映射(当F(x) = 0时),极大地缓解了深度网络的优化困难。
  • 残差连接已成为当今许多神经网络架构的标准组件。

U-Net:用于图像分割的编码器-解码器结构 🎯

最后,我们看一下在生物医学图像分割领域流行起来的U-Net架构。它是一种编码器-解码器结构,专门用于产生像素级的输出(如图像分割)。

以下是U-Net的核心设计:

  • 网络呈“U”形,左侧是下采样(编码)路径,用于捕获上下文信息;右侧是上采样(解码)路径,用于精确定位。
  • 关键创新在于引入了跳跃连接,将编码器路径中高分辨率的特征图与解码器路径中相应层的特征图进行拼接。这帮助解码器在恢复空间细节时,同时利用高级语义信息和低级细节信息。

架构对比与总结

以下是不同网络架构在复杂度和性能上的对比概览:

  • VGG-19:参数多,模型大,计算慢,但性能扎实。
  • GoogLeNet:模型更小、更快,通过Inception模块和1x1卷积实现高效计算。
  • ResNet及变体:在模型大小、速度和准确率之间取得了更好的平衡,通过残差连接成功训练了极深的网络。

总结:本节课我们一起学习了卷积神经网络架构的演变历程。我们从简单的LeNet-5出发,见证了AlexNet带来的深度学习革命,理解了VGGNet中小卷积核的优势,领略了GoogLeNet的并行高效设计,掌握了ResNet通过残差连接解决深度训练难题的核心思想,最后了解了U-Net在图像分割任务中的编码器-解码器结构。这些架构的创新思想,如使用小卷积核、残差连接、跳跃连接等,至今仍是构建高效深度学习模型的基石。

038:卷积神经网络可视化 🎨

在本节课中,我们将学习几种用于理解卷积神经网络内部工作机制的可视化技术。通过可视化,我们可以更好地洞察网络如何“看待”和处理输入数据,这不仅能帮助我们调试和改进模型,有时还能创造出有趣的艺术效果。

可视化的重要性

上一节我们讨论了卷积神经网络的结构与训练。本节中,我们来看看如何“打开”这个黑箱,理解其内部各层究竟在做什么。可视化对于理解网络学习到的特征至关重要。

浅层线性分类器的可视化

让我们从一个简单的模型开始,例如逻辑回归。这类模型的可视化相对容易,因为它本质上是一个单层网络。

以下是其工作原理:输入像素与一个权重矩阵 W 相乘,加上偏置 b,然后通过一个逻辑函数得到分数。公式可以表示为:

score = σ(W * x + b)

在这种情况下,我们可以直接查看权重矩阵 W。对于一个在特定数据集上训练好的分类器,如果我们将其权重矩阵可视化,会发现它看起来像是该类别物体的某种“平均图像”。这解释了为什么浅层模型分类效果有限:它仅仅是将输入图像与这个平均模板进行相关性匹配。

深层卷积网络的可视化

现在,让我们转向更复杂的模型,如AlexNet、ResNet或DenseNet。对于这些深层网络,最容易理解和可视化的是第一层。

以下是第一层卷积滤波器(filters)的可视化结果。可以看到,不同网络的第一层滤波器大小可能不同(例如AlexNet使用较大的核,ResNet-18使用较小的核),但它们都有一个共同点:第一层学习到的都是非常基础的图案,如边缘、颜色梯度或斑点检测器。这与人类大脑初级视觉皮层(V1区)提取基础特征的方式有相似之处。

然而,这种方法对于更深的网络层效果不佳,因为高层滤波器的功能不再直观。

理解高层特征空间

为了理解网络高层在做什么,我们可以分析其生成的特征表示。以AlexNet为例,我们可以取一张测试图像(如一朵花),让它通过网络,并提取倒数第二层(分类层之前)的4096维特征向量。

接下来,我们可以计算数据集中所有其他图像的特征向量,并在这个4096维的特征空间中,为测试图像寻找最邻近的图像(例如,使用L2距离)。结果显示,在特征空间中距离近的图像,在语义上也高度相似(例如,花对应花,大象对应大象)。相比之下,在原始像素空间中进行最近邻搜索,得到的结果在语义上则混乱得多(例如,船可能匹配到马)。

这表明网络成功地将图像映射到了一个语义丰富的特征空间。我们可以使用降维技术(如主成分分析PCA或t-SNE)将这个高维空间压缩到二维进行可视化。在MNIST数据集上的可视化结果显示,相似的数字在特征空间中确实聚集在一起。

这种语义丰富的特征表示也是迁移学习成功的原因:我们可以先在一个大型数据集(如ImageNet)上预训练网络,然后针对数据较少或任务不同的目标数据集,仅微调网络的最后几层,就能获得比从头训练好得多的结果。

最大化激活图像块

另一种技术是寻找“最大化激活图像块”。其思路是:针对网络中某个特定神经元(可以在任何中间层),我们让大量图像通过网络,并记录哪些图像或图像块能最大程度地激活该神经元。

以下是这种方法的结果示例。每一行代表网络中不同神经元的最大激活图像块。可以看到,不同神经元对不同类型的输入有响应,并且这种响应具有语义性。例如,某个神经元专门检测圆形物体(如眼睛、鼻子),另一个神经元对文字敏感,还有一个神经元专门识别人脸。这些功能是通过在分类任务上训练网络而自动涌现出来的。

通过遮挡理解输入重要性

我们还可以探究输入图像的哪些部分对网络的决策最重要。一种方法是遮挡分析

具体操作如下:取一张图像,用一个小窗口依次遮挡图像的不同区域。对于每个被遮挡的图像,我们都将其输入网络并记录分类得分的变化(例如,图像被分类为“大象”的置信度下降了多少)。通过遍历所有位置,我们可以生成一张显著图,它显示了遮挡每个区域对分类结果的影响程度。

例如,对于一张大象图片,遮挡面部区域会导致分类性能下降最明显;对于一张汽车图片,遮挡最前面的汽车或背景中的轨道区域影响最大。这直观地展示了网络决策所依赖的图像区域。

通过反向传播生成显著图

另一种更高效的方法是通过反向传播生成显著图。与运行成千上万次遮挡实验相比,这种方法计算成本低得多。

其原理是:对于一张输入图像,我们计算网络输出(如某个类别的得分)相对于每个输入像素的梯度。梯度的大小表明了微调该像素会对分类得分产生多大影响。然后我们将梯度幅值可视化,得到显著图。图中明亮的区域(高梯度幅值)意味着改变这些像素会强烈影响分类结果。我们还可以对显著图进行阈值处理,得到一个二值化的掩码。

这种方法还能帮助我们发现模型或数据中不希望的偏见。例如,一个网络错误地将一只哈士奇(狗)分类为狼。通过显著图分析发现,网络做出判断的依据不是狗本身,而是背景——因为数据集中狼的照片多在雪地背景下拍摄,网络学会了通过“背景是否有雪”这个捷径来区分,而不是真正识别动物特征。可视化技术帮助我们揭示了这种有问题的学习模式。

反卷积与特征重建

2014年提出的一种流行技术是反卷积。其核心思想是从网络中的某个神经元出发,以相反的顺序重建网络,试图找出是输入图像的哪些区域最负责激活该神经元。对于池化层,该方法会记录前向传播时最大激活值的位置,以便在反向重建时能将信息传递回对应的图像区域。

以下是该技术在不同网络层的结果:

  • 第一层:重建出简单的边缘和基础图案。
  • 第二层:出现物体的局部结构。
  • 第三层:出现更大的物体组合,如头部和躯干。
  • 第四、五层:重建出完整的物体,如整张脸或动物面部。

深度学习与艺术生成

最后,这些可视化技术还可以用于创造艺术效果,即著名的 DeepDream 技术。其思想是:选择一张输入图像和一个网络层,然后通过梯度上升放大该层神经元对当前图像的激活。

具体做法是:将图像输入一个预训练好的网络(如ImageNet上训练的GoogLeNet),计算指定层的激活值,然后将该激活值本身设为梯度,通过反向传播来修改输入图像的像素,使得图像能更强地激活那些已经被激活的特征。通过迭代这个过程,可以在普通图像中“幻化”出网络学到的各种图案和物体,生成如梦似幻的艺术图像,例如在云彩中看到狗、蜗牛、骆驼鸟等奇幻生物。

总结

本节课中,我们一起学习了多种卷积神经网络的可视化技术。我们从简单的权重可视化开始,逐步深入到分析高维特征空间的语义、寻找最大化激活的神经元、通过遮挡和反向传播理解输入的重要性,最后探讨了利用反卷积进行特征重建以及生成艺术图像的DeepDream技术。这些工具不仅帮助我们揭开神经网络“黑箱”的神秘面纱,理解其决策依据,还能用于模型调试、发现数据偏见,甚至进行艺术创作。

039:序列模型与循环神经网络

在本节课中,我们将要学习如何处理可变长度的序列数据。我们将介绍循环神经网络(RNN)这一核心模型,它能够处理输入、输出或两者长度均可变的序列,并广泛应用于机器翻译、图像描述等任务。

从固定输入到序列输入

在之前的课程中,我们讨论的模型,如多层感知机(MLP)或卷积神经网络(CNN),通常处理固定大小的输入,例如单张图像。这些模型首先将输入(如图像)转换为一个固定长度的向量,然后进行处理。

然而,在许多实际应用中,数据天然具有序列结构,例如文本句子、语音信号或视频帧。这些序列的长度是可变的。因此,我们需要能够处理可变长度序列的模型。循环神经网络正是为此类任务设计的强大模型。

课程结构概述

本节课的结构如下:

  • 在第一单元,我们将介绍循环神经网络,这是深度学习中处理序列最著名的模型。
  • 在第二单元,我们将探讨RNN的一些应用,以展示这类模型的灵活性。
  • 在第三单元,我们将了解门控循环网络,它们能够克服普通RNN的一些问题。
  • 在第四单元,我们将讨论自回归模型,这是一种纯前馈网络,但在许多序列任务上表现优异。

现在,让我们开始学习循环神经网络。

计算图回顾与RNN核心思想

首先,我们通过一个单隐藏层的多层感知机(MLP)来回顾前馈神经网络的计算图。该网络接收输入 X,经过隐藏层 H 处理,最终在输出层产生预测 Y_hat。网络中的每一层都有其对应的参数。

为了简化图示并专注于RNN的核心概念,后续我们将隐去参数节点,并引入新的颜色标识:绿色代表输入节点,红色代表隐藏计算层,蓝色代表输出层。

与前馈网络信息单向从输入流向输出不同,循环神经网络引入了反馈连接。其核心思想是存在一个隐藏状态,该状态会根据当前输入 X_t 和前一时刻的隐藏状态 H_{t-1},通过一个参数共享的更新规则进行更新。

我们可以将这种带反馈的RNN在时间维度上“展开”。时间索引 t 可以代表任何序列顺序,在大多数应用中指时间步。展开后,我们得到一个看似前馈的网络结构,其中每个时间步的计算单元(称为RNN Cell)执行相同的操作,并共享同一套参数。

这种参数共享机制使得RNN能够处理任意长度的序列,因为模型参数数量不随序列长度增加而增加。理论上,RNN拥有“无限记忆”,因为隐藏状态 H_t 可以携带并传递从序列开始(t=1)到当前时刻的所有相关信息。不过,我们将在后续课程中看到,由于训练性问题,这种长程记忆能力在实践中可能受限。

RNN单元详解

让我们更仔细地观察一个RNN单元。每个单元在时间步 t 接收两个输入:前一时刻的隐藏状态 H_{t-1} 和当前时刻的输入 X_t。单元的输出是当前时刻的隐藏状态 H_t

其计算过程通常如下:

  1. H_{t-1}X_t 拼接(Concatenate)成一个特征向量/张量。
  2. 对该拼接后的向量应用一个变换函数 F_H,以生成新的隐藏状态 H_t
  3. 可选地,使用另一个变换函数 F_Y 基于 H_t 生成当前时刻的预测输出 Y_t

关键点在于,函数 F_HF_Y 的参数在所有时间步是共享的。

经典RNN:Vanilla Single Layer RNN

最基础的RNN实现之一是Vanilla Single Layer RNN。它使用仿射变换(线性变换加偏置)和Tanh非线性激活函数来定义隐藏状态更新,并使用另一个仿射变换来产生输出。

其数学公式如下:

隐藏状态更新:
H_t = tanh(A_H * H_{t-1} + A_X * X_t + b)

输出预测:
Y_t = A_Y * H_t

其中:

  • A_H, A_X, A_Y 是权重矩阵。
  • b 是偏置向量。
  • tanh 是激活函数。选择Tanh是因为其输出范围在[-1, 1]之间,且是零中心的,这对处理序列中多个时间步堆叠时的梯度问题有好处。

这些参数 A_H, A_X, A_Y, b 在所有时间步保持不变,这正是RNN能处理变长序列的原因。

我们也可以使用爱因斯坦求和约定来简洁地表示这些运算。此外,可以将权重矩阵 A_HA_X 合并为一个更大的矩阵 A,同时将 H_{t-1}X_t 拼接起来作为输入,这在计算上是等价的。

RNN的映射类型

循环神经网络能够实现多种输入输出映射关系,非常灵活:

以下是常见的映射类型:

  • 一对一:经典的前馈网络映射,非RNN典型应用。
  • 一对多:单一输入,序列输出。例如,图像描述:输入一张图片,输出描述该图片的一句话(单词序列)。
  • 多对一:序列输入,单一输出。例如,动作识别:输入一段视频(图像帧序列),输出一个动作类别(如“烹饪”、“跑步”)。
  • 多对多(异步):输入和输出均为序列,但长度可能不同,且元素间无严格对齐。例如,机器翻译:输入一种语言的句子,输出另一种语言的句子。
  • 多对多(同步):输入和输出均为序列,且每个输出元素与对应时刻的输入元素直接相关。例如,视频目标跟踪:输入视频帧序列,输出每一帧中目标物体的位置坐标。

对于需要生成变长序列的任务(如一对多、多对多),一个常见的方法是让模型学习预测一个特殊的停止符号(如<EOS>)。当模型输出该符号时,即表示序列生成结束。

RNN的训练:随时间反向传播

训练RNN需要使用随时间反向传播算法。其思想很简单:对于序列中的每个输出预测,我们计算一个损失(与真实标签比较),然后将这些损失产生的梯度从序列末端开始,沿着时间步反向传播回网络的所有部分。

由于所有时间步的RNN单元共享参数,梯度会在时间维度上累积,类似于卷积网络中参数在空间位置共享时的梯度累积。

然而,BPTT在处理长序列时面临挑战:计算开销大、内存占用高,并且一次参数更新需要处理整个长序列,效率低下。这在处理长文章或视频时变得不切实际。

截断的随时间反向传播

为了解决长序列训练问题,实践中通常采用截断的随时间反向传播。其核心思想是:我们不通过整个长序列进行反向传播,而是将其分割成较短的片段(例如20-50个时间步),仅在每个片段内执行BPTT。

具体操作时,我们在一个片段内进行前向和反向传播,计算梯度并更新参数。然后,我们将该片段最后一个时间步的隐藏状态前向传递到下一个片段,作为其初始隐藏状态,并重复这个过程。这样,模型依然能保持一定的序列连续性,但训练变得可行。总的损失是各个片段损失的和。

构建更深的RNN

基础的Vanilla RNN通常比较浅。我们可以通过两种主要方式构建更深的RNN,以增加模型容量:

以下是构建更深RNN的方法:

  • 堆叠RNN:将多个RNN层堆叠起来。第一层RNN接收输入序列,其输出(隐藏状态序列)作为第二层RNN的输入,依此类推。高层RNN可以学习到更抽象的时间特征。其公式可以扩展为包含层索引 l
  • 深化RNN单元:在每个RNN单元内部使用更深的神经网络(例如,多个全连接层加非线性)来代替简单的单层变换。

此外,像在CNN和MLP中一样,我们也可以将残差连接应用于堆叠的RNN层之间,以缓解深度网络中的梯度消失问题,从而训练更深的RNN模型。

总结

本节课中,我们一起学习了序列建模的核心——循环神经网络。我们了解了RNN通过参数共享和隐藏状态传递来处理可变长度序列的基本原理,探讨了其数学表达和多种输入输出映射类型。我们还介绍了训练RNN的BPTT算法及其在实际中常用的截断版本,并讨论了如何通过堆叠或深化单元来构建更强大的深度RNN模型。RNN为机器翻译、语音识别、文本生成等序列任务提供了强大的基础框架。

040:循环神经网络的应用 🧠

在本节课中,我们将学习循环神经网络(RNN)能够解决的一些具体应用。我们将看到,RNN不仅限于处理文本或时间序列数据,它们还能在计算机视觉、图像生成、机器翻译等多个领域发挥重要作用。


图像中的多对象识别 👁️

上一节我们介绍了RNN的基本原理,本节中我们来看看它在图像识别中的应用。一个典型的例子是识别图像中的多位数字(如门牌号)。

一种解决方法是部署一个RNN,让它像人类一样,迭代地关注图像的不同区域。模型在每个时间步会“瞥见”(glimpse)图像的一个小区域,然后预测下一个应该关注的位置(称为“扫视”,saccade)以及当前区域的类别标签(例如,一个数字)。

以下是该过程的核心步骤:

  1. 感知:模型在时间步 t 关注位置 L_t 并提取该区域的特征。
  2. 预测:模型基于当前信息,预测类别标签 y_t 和下一个关注位置 L_{t+1}
  3. 迭代:将预测的下一个位置 L_{t+1} 作为输入,重复上述过程,直到识别出所有数字。

这个过程可以用一个循环单元来描述:
h_t = RNN(h_{t-1}, glimpse(I, L_t))
其中,h_t 是隐藏状态,I 是输入图像,glimpse 函数从图像 I 中提取位置 L_t 周围的区域特征。


迭代实例分割 🎨

RNN也可以用于迭代地执行实例分割任务。在这种情况下,输入图像首先通过一个全卷积网络处理成特征图。

然后,一个卷积LSTM(ConvLSTM)RNN被用于该特征图。在每个时间步,模型预测并输出一个对象的分割掩码。如下图所示,不同颜色代表不同时间步输出的分割部分。

关键机制在于,模型会将之前所有时间步已生成的分割结果的并集,作为输入反馈到下一个时间步。这样,RNN就能知道哪些部分已经被分割,哪些还没有,从而避免重复分割并逐步完成整个图像的分割。


视频中的目标跟踪 🎬

在视频多目标跟踪任务中,RNN可以用来更新每个目标的内部状态表示。这个内部表示可以包含目标的位置、大小、外观、速度等信息。

通过在每个视频帧上运行RNN,模型能够维持并更新对每个被跟踪目标的记忆,从而在时间维度上实现稳定、连续的跟踪。

有趣的是,RNN也能应用于一些本质上并非严格序列的问题。


序列图像生成 ✍️

图像生成任务通常使用卷积神经网络(CNN)分层生成。然而,研究证明也可以按序列方式生成图像。

例如,在Gregor等人2015年的工作中,模型按顺序生成图像的各个区域。如下图所示,红色矩形框表示模型下一步将要绘制的区域。

模型首先生成物体的粗略轮廓,然后通过更精细的预测逐步细化,直到生成最终的完整图像(例如一个手写数字)。

这种序列生成模型具有多功能性。同一个模型架构既可以用于识别图像(如分类MNIST数据集中的数字),也可以用于生成图像(如生成新的手写数字或街景门牌号)。



图像补全与多模态输出 🌈

在Oord等人2016年的模型中,RNN被用于图像补全任务。给定一张被部分遮挡的图像(左),模型的目标是预测完整的图像(右)。

由于补全任务通常具有高度的模糊性和多模态性(即存在多种合理的补全方式),RNN的优势在于它能轻松输出像素强度的概率分布

以下是生成多样化补全结果的过程:

  1. 在每个时间步,RNN输出下一个像素值的概率分布(通过softmax函数)。
  2. 从该分布中采样一个具体的像素值。
  3. 将采样得到的像素值作为输入,馈送到下一个时间步。

通过这种随机采样机制,模型可以从同一个遮挡输入生成多种多样但都合理的补全结果,如上图所示。这是此类生成模型的一个优良特性。


交互式图像标注 🖱️

创建大型标注数据集(用于训练分割或检测模型)非常耗时。RNN可以帮助加速这一过程。

例如,Polygon RNN是一个交互式对象标注工具。给定一个物体的粗略边界框,模型通过RNN顺序地预测多边形的一个个顶点,从而勾勒出物体的轮廓。

标注者可以在此基础上进行修改,使其更贴合物体,这能显著减少标注一个物体轮廓所需的时间。


道路布局生成 🗺️

道路布局很难用固定长度的向量来表示,它们通常是包含节点和边的空间图。

RNN可以被训练来生成此类空间图。例如,一个在纽约道路数据上训练的模型,会生成纽约风格的道路模式;而在伦敦数据上训练的模型,则生成伦敦风格的道路模式。

这类模型还可以根据输入的鸟瞰图条件,生成对应的街道布局,这对于自动测绘新区域或更新因建设而变化的区域非常有用。


图像描述(看图说话) 🖼️➡️🗣️

图像描述(Image Captioning)的目标是为输入图像生成一句自然语言描述。

在“Show, Attend and Tell”模型中,流程如下:

  1. CNN首先将图像编码成一个低分辨率(如14x14)的特征图。
  2. 一个LSTM网络在该特征图上运行,通过注意力机制(Attention)迭代地关注图像的不同区域。
  3. LSTM根据当前关注的区域和已生成的词语,逐个单词地生成描述句子。

例如,生成“bird”时,模型会关注图像中的鸟;生成“water”时,则关注背景水域。注意力图可以直观展示模型在生成每个词时所关注的位置,这有助于理解模型的决策过程并诊断错误。


视觉问答(VQA) ❓

视觉问答是一个较新的任务,输入是一张图像和一个自然语言问题,输出是一个答案句子或从多个选项中选择正确答案。

例如,对于问题“谁在击球手后面?”,模型需要结合图像信息推理出正确答案是“捕手”。这要求模型同时理解视觉内容和语言语义。


神经机器翻译 🌐

神经机器翻译是RNN的经典应用。谷歌在2016年利用基于RNN的序列到序列(Seq2Seq)模型,显著提升了翻译质量。

模型结构通常包含两部分:

  • 编码器(Encoder RNN):将源语言句子编码成一个固定长度的上下文向量。
  • 解码器(Decoder RNN):以上下文向量为初始状态,逐个单词地生成目标语言句子。

这是一种“多对多”的映射,输入和输出的单词之间没有直接的对应关系。


字符级语言模型 📖

最后,我们通过一个简单的例子来直观感受RNN的序列生成能力。Andrej Karpathy演示了如何使用极简单的字符级RNN来生成自然语言文本。

模型的基本单元如下:

  • 输入:字符的独热编码(one-hot vector),例如 H = [1,0,0,0], E = [0,1,0,0]
  • 处理:RNN单元根据当前输入和前一隐藏状态计算新的隐藏状态 h_t
  • 输出:通过一个全连接层和softmax函数,输出下一个字符的概率分布 P(char | h_t)

生成文本时,我们从初始字符开始,将模型预测的分布中采样得到的字符作为下一个输入,如此循环,即可生成任意长度的文本。

以下是一些有趣的训练成果:

  • 莎士比亚风格文本:模型学习了剧本的结构(如角色名、冒号、换行、逗号、句号),能生成看似合理的对话。
  • LaTeX代码:模型学习了LaTeX语法,能生成可以编译的、包含引理、证明和图的文档(尽管数学内容无意义)。
  • Linux内核代码:模型学习了代码结构(许可证声明、#include、宏定义、函数体、括号匹配),甚至能生成注释。
  • 婴儿名字:模型学习了8000个名字的构词模式,能生成不在训练集中的、听起来合理的新名字。

通过分析RNN隐藏层神经元的激活情况,我们可以窥见模型学到的一些规律,例如:有神经元专门跟踪是否在引号内、是否在if语句中、或者当前处于行中的哪个位置,这些都对生成正确的语法结构至关重要。


总结 📝

本节课我们一起探索了循环神经网络(RNN)丰富多彩的应用场景。我们看到,RNN的能力远不止于处理文本,它还能:

  • 通过迭代注意力机制识别图像中的多个对象。
  • 以序列方式进行图像分割、生成和补全。
  • 为图像生成描述性语言,或回答关于图像的提问。
  • 实现语言间的机器翻译。
  • 从字符层面学习并生成具有复杂结构的文本、代码等数据。

这些应用展示了RNN在处理序列信息依赖关系方面的强大灵活性,是深度学习工具箱中不可或缺的组件。

041:门控循环网络 🧠

在本节课中,我们将要学习循环神经网络(RNN)的改进版本——门控循环网络。我们将首先回顾基础RNN在训练时遇到的问题,然后深入探讨如何通过引入“门”机制来解决这些问题,特别是梯度消失和梯度爆炸。最后,我们将介绍几种主流的门控循环网络架构。


在上一节中,我们介绍了基础的循环神经网络架构。然而,这种基础架构在训练上存在困难,原因我们将在接下来的内容中看到。为了提高循环神经网络的可训练性,几乎所有现代循环神经网络架构都使用了门控机制。这正是我们本节要讨论的内容——门控循环网络。事实上,我们在第二单元中看到的所有应用案例,都使用了这些带有门控机制的改进型循环网络变体。

基础RNN的问题

首先,让我们回顾一下在第一讲中介绍的基础循环神经网络单元。为了清晰起见,我们省略了输出层,也没有明确写出仿射变换。其核心状态更新方程如下:

公式:
H_t = tanh(A_H * H_{t-1} + A_X * X_t + b)

其中,tanh 激活函数将数据映射到 [-1, 1] 的范围内。

那么,这种基础RNN结构的问题是什么呢?为了看清这一点,我们先考虑一个简化情况:假设隐藏状态 H_t 和输入 X_t 都是一维标量。此时,更新方程简化为:

公式:
h_t = tanh(a_h * h_{t-1} + a_x * x_t + b)

在通过时间反向传播(BPTT)时,我们需要计算 h_th_{t-1} 的导数。应用链式法则,我们得到:

公式:
∂h_t / ∂h_{t-1} = tanh'(z_t) * a_h

这里,z_ttanh 函数的输入参数,tanh' 是其导数。

如果我们考虑一个更长的序列,需要计算 h_t 对更早时刻 h_{t-k} 的导数,根据链式法则,这将是 k 个类似导数的乘积:

公式:
∂h_t / ∂h_{t-k} = ∏_{i=t-k+1}^{t} [tanh'(z_i) * a_h] = a_h^k * ∏_{i=t-k+1}^{t} tanh'(z_i)

这就引出了两个核心问题:

  1. 梯度消失:如果 tanh 函数饱和(即输入值很大或很小),其导数 tanh' 接近0。当多个接近0的导数连乘时,梯度会迅速衰减至几乎为0,导致网络无法更新较早时间步的参数。
  2. 梯度爆炸:即使 tanh 未饱和,假设其输入在 [-1, 1] 的线性区间内,此时 tanh'(z) ≈ 1。那么导数近似为 ∂h_t / ∂h_{t-k} ≈ a_h^k。如果权重 a_h > 1,梯度会随着 k 增大而指数级增长(爆炸);如果 a_h < 1,梯度则会指数级衰减(消失)。

在向量情况下,问题类似,但分析涉及权重矩阵 A_H 的特征值。如果特征值大于1,对应方向的梯度可能爆炸;如果小于1,则可能消失。

解决梯度问题的方法

上一节我们分析了基础RNN面临的梯度消失和爆炸问题。本节中,我们来看看解决这些问题的具体方法。

应对梯度爆炸:梯度裁剪

对于梯度爆炸,一个简单有效的启发式方法是梯度裁剪。其核心思想是:在应用随机梯度下降更新参数之前,检查梯度的范数,如果超过某个阈值,就将其缩放。

以下是具体操作:

代码/公式:

设 g 为参数的梯度向量。
计算梯度的L2范数:norm = ||g||_2
设定阈值 τ(超参数,通常为1到10之间)。
如果 norm > τ:
    g = g * (τ / norm)  # 将梯度缩放,使其范数等于 τ

这样,无论原始梯度有多大,更新步长都不会超过由 τ 控制的范围,从而避免了因梯度爆炸导致的训练不稳定。

应对梯度消失:引入门控机制

梯度裁剪无法解决梯度消失问题,因为这需要改变网络架构本身。解决方案是引入门控机制

门控循环单元通过引入由Sigmoid函数(输出范围0到1)控制的“门”,来有选择地让信息通过。这允许网络学习在长序列中保留或丢弃信息,从而让梯度能够更有效地在时间步之间传播。

以下是几种主要的门控循环网络架构,我们按从简到繁的顺序介绍。

门控循环网络架构

上一节我们提到了门控机制是解决梯度消失的关键。本节中,我们将详细介绍三种具体的门控循环网络架构。

更新门循环网络

更新门循环网络(UG-RNN)是最简单的门控架构,只使用一个更新门。

UG-RNN单元的计算步骤如下:

  1. 计算更新门 U_t:这个门决定有多少旧状态信息被保留。
    公式:
    U_t = σ(A_{UH} * H_{t-1} + A_{UX} * X_t + b_U)
    σ 是Sigmoid函数)

  2. 计算候选状态 S_t:这是基于当前输入和前一状态计算出的新状态提议。
    公式:
    S_t = tanh(A_{SH} * H_{t-1} + A_{SX} * X_t + b_S)

  3. 更新隐藏状态 H_t:最终状态是旧状态和候选状态的加权组合,权重由更新门控制。
    公式:
    H_t = U_t ⊙ H_{t-1} + (1 - U_t) ⊙ S_t
    表示逐元素相乘,即Hadamard积)

直觉:当 U_t 接近1时,网络几乎完全复制前一时刻的状态 H_{t-1},忽略新输入的计算结果。当 U_t 接近0时,则完全采用新的候选状态 S_t。更重要的是,在反向传播时,梯度可以通过 U_t 这条路径直接流过(因为 ∂H_t / ∂H_{t-1} 包含 U_t 项),即使其他路径的梯度很小,网络也能学会将 U_t 设置为接近1来维持长期依赖关系中的梯度流。

门控循环单元

门控循环单元(GRU)比UG-RNN稍复杂,使用了两个门:重置门更新门

GRU单元的计算步骤如下:

  1. 计算重置门 R_t:控制前一状态有多少信息被用于计算候选状态。
    公式:
    R_t = σ(A_{RH} * H_{t-1} + A_{RX} * X_t + b_R)

  2. 计算更新门 Z_t:作用与UG-RNN中的更新门类似。
    公式:
    Z_t = σ(A_{ZH} * H_{t-1} + A_{ZX} * X_t + b_Z)

  3. 计算候选状态 S_t:重置门影响了对前一状态的利用。
    公式:
    S_t = tanh(A_{SH} * (R_t ⊙ H_{t-1}) + A_{SX} * X_t + b_S)

  4. 更新隐藏状态 H_t
    公式:
    H_t = Z_t ⊙ H_{t-1} + (1 - Z_t) ⊙ S_t

重置门允许网络选择性地“忘记”过去的状态,这对于学习忽略无关信息非常有用。

长短期记忆网络

长短期记忆网络(LSTM)是最早(1997年)也是最复杂的门控架构,使用了三个门和一个额外的细胞状态 C_t

LSTM单元的计算步骤如下:

  1. 遗忘门 F_t:决定从细胞状态中丢弃哪些信息。
    F_t = σ(A_{FH} * H_{t-1} + A_{FX} * X_t + b_F)
  2. 输入门 I_t:决定哪些新信息将被存入细胞状态。
    I_t = σ(A_{IH} * H_{t-1} + A_{IX} * X_t + b_I)
  3. 候选细胞状态 \tilde{C}_t:新的信息候选值。
    \tilde{C}_t = tanh(A_{CH} * H_{t-1} + A_{CX} * X_t + b_C)
  4. 更新细胞状态 C_t:结合遗忘和输入,更新长期记忆。
    C_t = F_t ⊙ C_{t-1} + I_t ⊙ \tilde{C}_t
  5. 输出门 O_t:基于细胞状态,决定输出什么到隐藏状态。
    O_t = σ(A_{OH} * H_{t-1} + A_{OX} * X_t + b_O)
  6. 更新隐藏状态 H_t:隐藏状态是细胞状态的过滤版本。
    H_t = O_t ⊙ tanh(C_t)

LSTM通过独立的细胞状态 C_t 来专门保存长期记忆,并通过三个门精细控制信息的流入、保留和流出。

架构对比与总结

我们已经介绍了三种主要的门控循环网络。以下是它们的简单对比:

  • UG-RNN:1个门(更新门),参数最少,结构最简单。
  • GRU:2个门(重置门、更新门),参数中等,在许多任务中表现优异且易于训练。
  • LSTM:3个门(输入门、遗忘门、输出门)加细胞状态,参数最多,结构最复杂,是开创性的工作。

研究表明,对于较浅的网络,GRU和UG-RNN在可训练性上往往优于LSTM,而性能却相近甚至更好。因此,GRU和UG-RNN因其简洁和高效而成为更常用的选择。


本节课中我们一起学习了:

  1. 基础循环神经网络(RNN)在训练时面临梯度消失梯度爆炸的根本原因。
  2. 应对梯度爆炸的实用技巧——梯度裁剪
  3. 解决梯度消失问题的核心思想——引入门控机制
  4. 三种主流的门控循环网络架构:
    • 更新门循环网络(UG-RNN):使用单个更新门,结构最简单。
    • 门控循环单元(GRU):使用重置门和更新门,在性能和复杂性间取得了良好平衡。
    • 长短期记忆网络(LSTM):使用三个门和独立的细胞状态,结构最复杂,能够学习非常长期的依赖关系。

门控循环网络通过有选择地让信息通过,显著提升了RNN处理长序列数据的能力,是现代序列建模的基石。

042:序列模型之自回归模型 🧠

在本节课中,我们将要学习一种强大的序列建模方法——自回归模型。我们将看到,这种前馈神经网络模型如何挑战了循环神经网络在序列处理领域的传统地位,并理解其工作原理、优势以及实际应用。

概述

我们之前已经了解到,循环神经网络是一个强大的模型家族,能够处理任意长度的序列。长期以来,人们认为循环模型是唯一能做到这一点的模型类型。但最近的研究表明,实际上存在一类前馈神经网络,其性能能够与循环神经网络相媲美,甚至在某些方面超越它们。这就是我们将在本单元讨论的所谓自回归模型。

什么是自回归模型?

自回归模型是一种前馈模型,它基于时间序列中前K个变量来预测下一个变量X_T。其中,T表示时间。

在数学上,一个K阶自回归模型可以表示为:
X_t = f(X_{t-1}, X_{t-2}, ..., X_{t-K})

下图是一个直观的示例。在这个图示中,x5直接依赖于x4和x3,因此这里的K值为2。

请注意,图中使用了虚线只是为了更清晰地指示不同的映射关系,虚线箭头和实线箭头的含义实际上是相同的。与循环神经网络类似,自回归模型的参数在时间上是共享的,这意味着我们在每个时间步T应用相同的函数,该函数不依赖于时间步索引T。

从图模型的角度来看,我们在此做了一个很强的条件独立性假设。例如,在给定x3和x4的条件下,x5与x2是条件独立的。这与循环神经网络不同,我们将在后续幻灯片中再次审视这一点。

扩展到序列建模

现在,我们可以将预测自身下一个值的自回归模型概念,扩展到我们一直在研究的序列建模场景中,即输入和输出不同的情况。

在这种情况下,输入(绿色)和预测输出(y)是不同的。我们有一个函数,使得在时间T的预测依赖于从时间T直到T-K的所有输入。

换句话说,Y_t依赖于所有满足 i >= t-Ki <= t 的X_i。而在本示例中,y5独立于x2或x1。这是自回归模型最基本的形式,它是一个纯粹的前馈模型,没有递归更新的隐藏状态。

与循环神经网络的对比

为了更清晰地理解,我们将自回归模型与展开的循环神经网络版本进行比较。

以下是两者的核心区别:

  • 信息处理方式:循环模型通过其隐藏状态来总结先前的信息。而在自回归模型中,我们有从先前时间步到当前预测的直接连接,但仅限于前K步。
  • 记忆能力:自回归模型有一个硬性约束,即完全忽略K步之前的信息。而理论上,循环神经网络可以记住很早以前的信息用于很晚的预测。换句话说,与没有无限记忆、做出强条件独立性假设的自回归模型不同,循环神经网络理论上具有无限记忆。当然,问题在于这种无限记忆的实际价值有多大,这正是我们将在本单元探讨的内容。
  • 训练难度:与需要随时间反向传播(这带来了我们在上一单元看到的所有问题)的循环神经网络相比,自回归模型训练起来要容易得多,因为它不需要随时间反向传播。它是一个标准的前馈模型,我们面对的是标准的监督学习问题。例如,对于y5、y4和y3,我们甚至可以并行地应用学习过程,并行更新这些共享权重矩阵的权重。自回归网络比需要随时间反向传播的循环神经网络更容易训练,也更稳定。

深度自回归模型与因果卷积

像循环神经网络一样,自回归模型可以通过简单地堆叠具有自回归性质的层来扩展到具有多个层的深度版本。现在,我们有两个不同的函数(同样,参数在时间上共享,但在层方向上是不同的)。我们有一个函数从x映射到H,然后另一个函数从这个隐藏层映射到预测层Y。

我们还可以看到,这些自回归模型有效地执行了多个因果时间卷积。因为我们在时间上共享参数,所以每一层都可以被视为一个一维卷积层,我们在该层上滑动一个核滤波器矩阵以产生输出。在本例中,它们是因果的,因为我们在时间T进行预测时,只考虑时间T及之前时间步的信息,而不考虑未来时间步的信息。

根据具体情况,您可能希望考虑因果或非因果结构。如果您要处理整个批次或整个句子,而没有在线处理的要求,您可能也希望考虑来自未来的信息。类似地,在循环神经网络中,有所谓的双向循环神经网络,它同时从左到右和从右到左处理,因此也考虑了来自未来的信息。但在本讲座的范围内,我们将考虑标准设置下的循环神经网络,即因果设置,它只利用过去的信息进行预测。

当然,这类模型现在可以与我们(社区)在标准深度神经网络中开发的所有技巧相结合,例如跨层的残差连接以及扩张卷积,以便在保持参数不变的情况下更快地增加感受野大小。

突破性应用:WaveNet

第一个真正成功并展示突破的此类模型是Oord等人在2016年提出的WaveNet。

这是一个用于原始音频波形的生成模型,它能生成逼真的语音,人类观察者很难将其与真实语音区分开来。它结合了残差连接、跳跃连接和扩张卷积,如下图所示,这对于考虑来自任何先前时间步的上下文和输入信息至关重要。

音频处理在某种意义上很困难,因为每秒有大量样本,以至于序列处理社区通常避免使用原始音频波形。但这里已经证明,即使不使用循环神经网络,仅用前馈网络也有可能生成原始音频波形。通过使用扩张卷积的思想,我们可以用很少的参数非常快速地扩展感受野大小,再结合具有残差和跳跃连接的深度多层架构思想,这确实是在原始音频波形生成方面的一个突破。由于音频在多个尺度上存在结构(毫秒级的微观结构和秒级的重复模式),捕获这些多时间尺度的结构非常困难,而这类模型很好地做到了这一点。

在定量性能方面也得到证明,这些是衡量语音自然度的指标,WaveNet展示的性能远优于LSTM,可与人类自然语音相媲美。

进一步的发展与理论探讨

随后,这个想法在Atal 2018年的这篇论文中得到了进一步发展。他们证明了一个更简单的架构(使用相同的想法:具有残差层、扩张卷积和零填充的深度多层网络)在各种任务上都表现出非常好的性能。

以下是他们在多个不同任务上的测试结果,涵盖了图像处理、加法、复制、音乐处理、自然语言处理等。在一系列被认为是LSTM和循环神经网络传统优势领域的任务中,他们证明这些非常简单且易于训练的前馈网络实际上可以超越这些循环模型。

当然,问题是为什么会这样?这些循环网络是必需的吗?它们是否真正利用了其无限记忆的理论优势?这仍然是一个开放讨论的话题。

我想引导您关注这个网站,这是一篇围绕此主题的优秀博客文章。这里有一些来自最近研究该问题的论文的引述:

  • Daniluk等人在2016年发现,循环模型提供的无限上下文对于语言建模等任务并非绝对必要。例如,明确将输入序列长度截断为13或25的模型,通常与具有无限记忆的模型具有竞争力。这表明,也许在理论上,具有无限记忆的模型更好,但在实践中,它们并没有利用无限记忆,或者对于这些特定任务,无限记忆并非必要。
  • Bai等人在2018年也观察到了这一点。他们说,即使在考虑各种不同模型和任务时,循环神经网络的无限记忆优势在实践中也基本不存在。
  • Miller在2018年提供了一个理论结果。该结果表明,如果循环模型是稳定的(意味着梯度不会爆炸,这当然是我们训练循环模型时始终希望的特性),那么该模型可以很好地被一个前馈网络近似,无论是用于推理还是训练。

因此,似乎也有理论证据表明,循环神经网络无限记忆的理论优势实际上在实践中并不存在。我想以这个讨论作为结束,这是一个很好的起点,可以让我们更多地思考这个问题并进一步阅读相关资料。

总结

本节课中,我们一起学习了自回归模型。我们了解到,自回归模型作为一种前馈神经网络,通过直接利用过去有限时间步的信息进行预测,在序列建模任务中提供了循环神经网络之外的一种强大选择。它训练更简单、更稳定,并且通过结合深度架构、残差连接和扩张卷积等技术,能够在音频生成等多种任务上达到甚至超越传统循环模型的性能。理论分析和实践结果表明,循环神经网络理论上的“无限记忆”优势在实际应用中可能并非关键,这使得自回归模型及其相关的因果卷积网络成为许多序列处理场景中一个极具竞争力的替代方案。

043:自然语言处理与语言模型 🧠

在本节课中,我们将要学习自然语言处理(NLP)中的一个核心概念——语言模型。我们将了解什么是语言模型,它如何工作,以及如何评估其性能。

在之前的课程中,我们讨论了卷积神经网络,它是图像处理的理想架构。上一讲我们探讨了用于序列处理的循环神经网络和前馈自回归模型。自然语言是这类模型的理想应用场景,因此,我们将更深入地研究这些模型,并专门用一讲来探讨这个主题。

本讲内容分为四个部分:

  1. 介绍什么是语言模型并展示一些例子。
  2. 讨论在20世纪70、80和90年代使用的传统语言模型。
  3. 探讨这些模型的现代版本,即使用神经网络的神经语言模型。
  4. 讨论语言模型的一个特别重要的应用:机器翻译。

现在,让我们从语言模型开始。

什么是语言模型? 📖

一个语言模型对一系列离散标记(token)的概率分布进行建模。这些标记可以是单词或字符等。每个标记可以从一个词汇表 V 中取值,即 x_t ∈ V

我们有一个长度为 T 的标记序列 x。假设这些是单词,我们有第一个词、第二个词,直到第 T 个词。我们对此离散标记序列的概率分布进行建模。

该序列的联合分布可以分解为 T 个条件分布的乘积:
P(x_1, x_2, ..., x_T) = ∏_{t=1}^{T} P(x_t | x_1, ..., x_{t-1})

这仅仅是概率乘积法则(也称为链式法则)的结果,此处没有做任何近似。我们只是以这种方式重写或分解任何联合分布。这是一种自回归模型,它根据序列中所有先前的标记来预测下一个标记。

根据语言模型的不同,标记可以是单词、字符或其他单位。在单词级别操作的难点在于词汇量非常大(通常为10,000到30,000),而在字符级别操作则更容易建模长期依赖关系,因为词汇量小得多(通常只有几十个字符)。

语言模型中通常会引入一个特殊的标记:句子结束标记(EOS),以指示句子的结束。

让我们看一个单词语言模型的例子。模型为句子 “The dark ran away.” 分配概率质量。根据乘积法则,这可以分解为条件分布:P(The) * P(dark | The) * P(ran | The, dark) * ...。一个好的模型能够以高概率预测可能出现的下一个单词。

语言模型的应用 💡

以下是语言模型的一些主要应用:

1. 语言识别
假设我们训练了两个语言模型 PP‘,它们为句子分配概率,但它们是不同的模型。例如,P 在大量英语句子上训练,P‘ 在大量法语句子上训练。我们可以根据以下规则对句子 x 的语言进行分类:
语言(x) = 英语,如果 P(x) > P‘(x);否则为法语。

2. 生成新序列
从一个已训练好的单词语言模型 P(x) 中,我们可以高效地采样新句子。我们从采样第一个词 x_1 开始,然后根据第一个词采样第二个词 x_2,依此类推。这是一个线性时间的采样过程。

3. 贝叶斯推理(如传统机器翻译)
假设我们有一个在目标语言(如法语)上训练的语言模型 P(x),可以将其视为可能句子的先验分布。再假设有一个基于规则的系统,为给定源语言句子(如英语)y 生成翻译 x 并分配似然 P(y | x)。那么,我们可以使用贝叶斯规则推断后验分布:
P(x | y) ∝ P(y | x) * P(x)
现代机器翻译系统通常直接建模 P(x | y),我们将在后面单元看到这类模型的例子。

语言模型的训练 🏋️

我们使用 P_model(x | θ) 来表示具有参数 θ 的模型分布,以区别于真实的数据分布。

设训练集 XN 个句子 {x^(i)} 组成,句子长度可能不同。我们通过最大似然估计来训练一个无条件语言模型:
θ = argmax_θ ∏_{i=1}^{N} P_model(x^(i) | θ)*
这等价于最小化模型分布与数据分布之间的交叉熵:
θ = argmin_θ H(P_data, P_model)*
其中交叉熵 H(P_data, P_model) = E_{x~P_data}[-log P_model(x)]。最小化交叉熵意味着使模型分布尽可能接近数据分布。

语言模型的评估 📊

字符级语言模型通常用每字符比特数(bits per character)来衡量。首先需要引入信息论中的一些基本量。

香农信息(或惊奇度)
对于一个长度为 T、概率为 P(x) 的字符序列 x,其归一化香农信息定义为:
I(x) = - (1/T) * log₂ P(x)
由于 P(x) 可以分解为条件概率的乘积,因此:
I(x) = - (1/T) * Σ_{t=1}^{T} log₂ P(x_t | x_1, ..., x_{t-1})
单位是比特(bits)。直观上,如果观察到的事件在模型下概率很低,则惊奇度(信息量)很高;反之则很低。

期望惊奇度与交叉熵
模型在数据分布下的期望惊奇度由归一化交叉熵给出:
H(P_data, P_model) ≈ - (1/T) * E_{x~P_data}[log₂ P_model(x)]
更好的模型具有更低的交叉熵,即更少的比特数。根据香农-麦克米伦-布里曼定理,当序列长度 T 趋于无穷时,此近似成立。在实践中,我们是在一个独立于训练集的测试集或验证集上计算此值。

示例理解
考虑一个只有两个符号 {A, B} 的词汇表,序列长度 T=10

  • 情况1:数据与模型分布一致,P(A)=P(B)=0.5。这是一个完全分解的单字(unigram)模型。计算得交叉熵为 1 bit。这是最优编码所需比特数,也是该词汇表大小下的上界。
  • 情况2P(A)=1, P(B)=0。计算得交叉熵为 0 bits。因为下一个字符总是确定的,无需传递信息。这是下界。
  • 情况3P(A)=0.1, P(B)=0.9。计算得交叉熵约为 0.47 bits。介于0和1之间。
  • 情况4:模型分布为 P_model(A)=0.1, P_model(B)=0.9,但真实数据分布是 P_data(A)=1, P_data(B)=0(测试序列全是A)。计算得交叉熵约为 3.32 bits。这可以分解为:数据分布本身的熵(0.47 bits)加上模型与数据分布之间的KL散度(2.85 bits)。交叉熵 = 数据熵 + KL散度 ≥ 数据熵。因此,数据分布的熵是所能达到的交叉熵的下界。

词级语言模型的评估:困惑度(Perplexity)
词级语言模型传统上使用困惑度来衡量,它与交叉熵密切相关:
Perplexity = 2^{H(P_data, P_model)}
T 很大时,可以近似为:
Perplexity ≈ P_model(x)^{-1/T}
困惑度可以解释为测试集概率的几何平均的倒数。它直观地表示了模型在预测下一个词时的“平均分支因子”,即模型有多“困惑”。更低的困惑度代表更好的性能

困惑度示例
考虑词汇表 {A, B, C},序列长度 T=10

  • 情况1:均匀分布,P(A)=P(B)=P(C)=1/3。困惑度 = 3。模型最大程度困惑,平均有3个等可能的选择。
  • 情况2:确定分布,P(A)=1。困惑度 = 1。模型完全确定下一个词,毫不困惑。
  • 情况3P(A)=0.1, P(B)=0.9, P(C)=0。困惑度 ≈ 1.38。模型比较确定,困惑度较低。
  • 情况4:模型分布为 P_model(A)=0.1, P_model(B)=0.9, P_model(C)=0,但测试序列全是A。困惑度 = 10。由于模型与数据严重不匹配,困惑度甚至超过了词汇表大小。

总结与补充说明 📝

本节课中我们一起学习了语言模型的基础知识。我们了解了语言模型是对标记序列概率分布的自回归建模,探讨了它的应用、训练方法(最大似然/最小化交叉熵)以及评估指标(字符级的比特数、词级的困惑度)。

香农在1948年估计英文文本的熵大约在每字符0.6到1.3比特之间。现代字符级语言模型性能大约在每字符1比特。对于词级模型,直到2017年,困惑度在60左右是典型的。根据统计,英文平均每个单词约有4.79个字母(不含空格)。若按每字符1比特计算,对应的困惑度约为2^(4.79) ≈ 27.9,若考虑空格则更高,这与历史数据大致吻合。

但近年来最先进的模型(如GPT-2, GPT-3, Megatron-LM)报告的困惑度可达10-20。需要注意的是,比较不同语言模型的困惑度时必须谨慎,因为它们可能使用不同的词汇表(例如,只包含最常见的前1万个词)、不同的数据集或不同的预处理方式。必须在相同的词汇表和相同的数据集上进行对比才有意义。

最后,如果你对语言模型的更多细节感兴趣,可以查阅相关的在线资源进行深入学习。


本节课中,我们介绍了语言模型的核心概念,它是自然语言处理的基石。从定义、应用、训练到评估,我们建立了一个完整的理解框架。在接下来的章节中,我们将深入探讨传统语言模型和现代神经语言模型的具体实现。

044:传统语言模型(N-gram)📚

在本节课中,我们将要学习传统语言模型,特别是 N-gram 模型。这种模型在过去几十年里一直是语言建模的默认选择。我们将了解它的基本原理、如何构建、其优势与局限性。

概述

语言模型旨在为一系列离散的符号(词元)序列建模一个概率分布。每个词元都从一个固定的词汇表 V 中取值。我们将这个联合概率分布分解为一系列条件概率分布的乘积。其中,用于预测当前词元的历史信息被称为上下文

从概率表到 N-gram 模型

上一节我们介绍了语言模型的基本概念,本节中我们来看看最直观的实现方式及其面临的问题。

最简单的建模方式是为每个条件概率分布使用概率表。例如,给定 x1 预测 x2 的概率,我们可以存储一个二维矩阵(概率表)。随着序列长度增加,我们需要为 P(xt | x1, ..., xt-1) 存储一个维度为 t 的张量。

对于一个词汇量为 |V| 的模型,这个概率表的大小是 |V|^t。假设 |V| = 30,000,序列长度 t = 10,那么表的大小是 30,000^10,这是一个天文数字,无法存储,也需要海量的训练数据来估计每个条目的概率。

这就是 N-gram 模型 出现的原因。N-gram 模型通过做一个近似来简化问题:它缩短了上下文。

N-gram 模型的核心思想

N-gram 模型做出了一个强马尔可夫假设:当前词 xt 的概率只依赖于它前面的 n-1 个词,而不是整个历史。

公式表示如下:
对于一个 N-gram 模型(n 表示考虑的连续词元数量),我们近似:
P(xt | x1, ..., xt-1) ≈ P(xt | xt-n+1, ..., xt-1)

这意味着模型是“记忆有限”的,xt 不依赖于 xt-n+1 之前的任何词。

为了写出整个序列的联合概率分布,我们使用链式法则并结合这个近似:

公式:
P(x1, ..., xT) ≈ P(x1, ..., xn-1) * Π_{t=n}^{T} P(xt | xt-n+1, ..., xt-1)

其中,P(x1, ..., xn-1) 是起始部分的边缘概率,通常也用小规模的 N-gram 表来建模。

通过限制上下文长度 n(例如 2, 3, 4),概率表的维度被限制为 n,这使得存储和计算变得可行。这与我们之前介绍的自回归模型思想类似,但关键区别在于:N-gram 模型中的每个条件分布是用一个概率表表示的,而不是用神经网络。

N-gram 模型实例

以下是两种常见 N-gram 模型分解的具体例子:

Bigram 模型 (n=2):
上下文长度为 1。
P(“The”, “dog”, “ran”, “away”) = P(“The”) * P(“dog” | “The”) * P(“ran” | “dog”) * P(“away” | “ran”)

Trigram 模型 (n=3):
上下文长度为 2。
P(x1, x2, x3, x4) = P(x1, x2) * P(x3 | x1, x2) * P(x4 | x2, x3)

如何训练 N-gram 模型

训练 N-gram 模型的核心是估算条件概率 P(xt | xt-n+1, ..., xt-1)。根据条件概率的定义和训练语料库中的计数,我们可以直接计算。

公式推导(以 Bigram 为例):
P(xt | xt-1) = P(xt-1, xt) / P(xt-1) = count(xt-1, xt) / count(xt-1)

以下是训练步骤:

  1. 计数:在整个训练语料库中,统计所有 N-gram 和 (N-1)-gram 出现的次数。
  2. 计算概率:对于每个条件概率,用相应的 N-gram 计数除以它的上下文((N-1)-gram)的计数。

因此,N-gram 模型的训练过程非常简单,本质上就是计数,不需要梯度下降等复杂优化算法。

平滑技术

对于较大的 n(如三元组、四元组),许多可能的词序列组合在训练集中从未出现,导致其条件概率为 0。这在测试时是个问题,因为模型会给未见过的序列分配零概率,这被称为数据稀疏性

为了解决这个问题,需要使用平滑技术。一个简单的启发式方法是加一平滑,即在所有 N-gram 计数上加 1,确保没有概率严格为 0。当然,还有更复杂的平滑方法(如 Good-Turing、Kneser-Ney 平滑)效果更好。

生成文本示例

由于 N-gram 模型是自回归的,我们可以通过迭代地从条件分布中采样来生成文本:给定前 n-1 个词,采样下一个词,重复此过程直到生成句子结束符。

从简·奥斯汀或莎士比亚文本训练出的不同阶数 N-gram 模型生成示例如下:

  • Unigram (n=1):生成的单词序列几乎是随机的,如 “a a a a a”。
  • Bigram (n=2):开始出现常见的双词搭配,但句子整体可能不连贯。
  • Trigram (n=3):能生成更通顺的短语和短句,如 “King Henry what? I will go seek the traitor.”
  • Four-gram (n=4):生成的文本在局部看起来更加合理和自然。

随着上下文长度 n 的增加,模型捕获的依赖关系更丰富,生成的文本质量也更高。

总结与局限性

本节课我们一起学习了传统的 N-gram 语言模型。我们来总结一下它的特点和主要缺点:

总结:
N-gram 模型是一种简单的、基于马尔可夫假设的序列概率模型,使用概率表进行参数化。它通过计数进行训练,易于理解和实现。

局限性:

  1. 有限的上下文:由于马尔可夫假设,它无法建模长距离依赖关系。
  2. 参数增长:参数数量仍随 n 呈指数级增长,限制了 n 的大小。
  3. 数据稀疏性:需要平滑技术来处理未见过的 N-gram。
  4. 无法条件建模:它是生成模型,无法直接建模基于输入的条件分布(如翻译、摘要)。
  5. 离散表示问题:这是最关键的缺陷之一。在 N-gram 模型中,每个词被表示为离散的符号(如 One-hot 向量)。这导致两个问题:
    • 词汇表限制:必须将词汇表限制在一个较小的大小(如 30,000),忽略低频词。
    • 无法共享语义:任何两个不同的词在 One-hot 表示中的距离都是相同的(例如,欧氏距离为 √2)。这意味着模型无法利用词语之间的语义相似性。例如,“狗”和“犬”在语义上很接近,但对 N-gram 模型来说,它们是两个完全无关的符号,从“可爱的狗”学到的知识无法迁移到“可爱的犬”。

正是这最后一个缺点——缺乏对词语语义关系的建模能力——成为了推动语言模型向前发展的核心动力,并引出了我们下一单元将要学习的内容:基于神经网络的分布式词表示

045:神经语言模型 🧠

在本节课中,我们将学习神经语言模型。这是一种利用神经网络来预测下一个词的语言模型。

概述

上一节我们介绍了传统的N元语法模型。本节中,我们将探讨如何利用神经网络,特别是分布式词表示(词嵌入),来构建更强大、更高效的语言模型。这种方法解决了传统模型面临的核心难题。

从离散到连续的表示

N元语法模型本质上是条件概率表,它们深受维度灾难的困扰。例如,对于一个大小为V的词汇表,对连续N个词的联合分布进行建模,需要处理 V^N 个参数。即使将上下文限制为10个词,参数量也可能达到 10^40 级别,这是难以处理的。

然而,对连续变量的建模通常比对离散变量的建模更容易实现泛化,因为需要学习的函数在局部是平滑的。这个想法催生了神经语言模型。下面引用的论文是这一领域的开创性工作:

Bengio, Y., et al. (2003). A neural probabilistic language model.

核心问题:离散表示的局限性

问题的核心在于,在离散的词表示(如独热编码)中,我们假设任意两个词之间的距离是相同的。

假设 W 表示一个词的独热编码向量(一个维度为词汇表大小V的向量,其中只有一个元素为1,其余为0)。考虑两个不同的词,它们的独热向量相减后计算L2范数(距离),结果总是 √2(除非是同一个词,距离为0)。

这意味着在这种简单表示中,所有词彼此间的距离都相等。但在现实中,有些词比其他词更相似。请看以下句子:

  • The cat is walking in the bedroom.
  • The dog was running in the room.

在词级别(独热编码级别),这两个句子非常不同。但在语义上,它们高度相关。“猫”和“狗”、“走”和“跑”具有相似的语义角色。然而,纯粹的离散模型(如N元语法)无法捕捉这种相似性。

解决方案:分布式词表示(词嵌入)

因此,前述论文提出的思想是使用一种不同的表示方法——分布式表示。它将高维的独热向量(例如维度为10,000)映射到低维的词嵌入向量(例如维度为30或100),即 R^M

在独热编码中,所有信息都集中在一个维度上。而在分布式表示中,词义被分布在整个嵌入向量的所有维度上。这样做的优势在于,我们现在能够建模词与词之间的相似性

另一种理解方式是,这些表示将概率质量分布在重要的方向上,而不是像在独热编码中那样,所有训练点周围的所有方向都是均匀的(因为所有距离都相同)。这使得模型能够捕捉更有信息量的关系,从而实现跨句子的泛化。

词嵌入的实现

这种从独热向量到 R^M 的映射通常通过一个简单的线性变换(矩阵乘法)实现。本质上,这是一个矩阵查找操作,嵌入就存储在这个矩阵中。然后,这些嵌入成为词的表示,并可以被进一步处理,例如通过前馈神经网络、循环神经网络或将在最后一单元学习的Transformer模型。

以下是其工作原理的图示说明:

需要强调的是,我们并不会给词手动附加语义标签。词嵌入是纯粹通过数据学习得到的实值向量。

一个具体例子

假设训练集包含以下三个句子:

  1. The cat is walking.
  2. The dog is walking.
  3. The cat is sitting.

一个分布式表示可以学习将“cat”和“dog”的嵌入放置在嵌入空间中彼此靠近的位置,因为它们在语义上相似。现在,由于“The cat is sitting”在训练集中出现概率高,而“cat”和“dog”相关,因此未在训练集中出现的“The dog is sitting”的概率也会变高。这是关键:通过这种相似性建模,我们可以使未出现在训练集中的句子也获得较高的概率,而这是N元语法模型由于其表示方式所无法实现的。

表示空间的可视化

下图展示了局部表示(独热编码)与分布式表示空间可能的样子:

在左侧的局部表示中,三个词“walking”、“cat”、“dog”位于坐标轴上,彼此间的L2距离都是√2。右侧的分布式表示目标是将它们转换到低维空间(这里是从3维到2维)。现在,“cat”和“dog”彼此之间的距离比它们到“walking”的距离更近。

学习到词嵌入后,我们可以将其可视化。例如,在二维t-SNE可视化中,我们可能看到“国家”聚集在一个区域,“年份”聚集在另一个区域,这表明模型从文本中学习到了语义关联。

但我们必须谨慎对待高维空间的可视化。正如Geoffrey Hinton的一个著名比喻所言:在一个30维的杂货店里,泡菜可能挨着鱼,也可能挨着披萨配料。在高维空间中,任何事物都可能彼此相邻,而我们的可视化仅限于2D,想象力是有限的。

神经概率语言模型架构

前面引用的论文是第一个在语言模型中成功使用分布式表示的工作,这给NLP领域带来了突破性影响。该模型的关键思想是:

  1. 为词汇表中的每个词关联一个分布式词特征向量(即词嵌入)。
  2. 用序列中这些词的特征向量来表达词序列的联合概率函数。
  3. 同时学习词特征向量和概率函数的参数。

这是一个端到端的训练过程,首次同时学习了词嵌入和语言模型参数。

以下是该模型的示意图(摘自原论文):

模型描述如下:

  • 输入:前 n-1 个词的索引(可视为独热向量)。
  • 嵌入层:通过一个共享的矩阵 C(维度为 M x V)将每个独热向量转换为M维词嵌入。
  • 拼接:将所有词嵌入拼接成一个大向量。
  • 隐藏层:输入到一个带tanh激活函数的全连接隐藏层。
  • 输出层:可选地,可以有从嵌入层到输出层的直接连接。最终通过一个softmax层输出下一个词 w_t 的概率分布。

模型的输出是给定之前所有词的情况下,下一个词的概率:
P(w_t | w_{t-1}, w_{t-2}, ..., w_{t-n+1})

该模型的参数数量与词汇表大小 V 和上下文长度 n线性关系,即 O(V * M + n * M * H),其中H是隐藏层大小。这与N元语法模型的指数级复杂度 O(V^n) 形成了鲜明对比。这种效率提升源于将条件信息(历史词)作为神经网络的输入,而非显式地为所有可能的组合建模概率。

模型效果与改进

论文中的主要结果表明:

  • 与最好的N元语法模型相比,神经网络模型能获得显著更好的结果(在Brown语料库上困惑度降低约24%,在AP新闻语料库上降低约8%)。
  • 神经网络能够利用更长的上下文(增加n带来提升),而N元语法模型则不能。
  • 将神经网络的输出概率与插值三元语法模型结合,可以进一步降低困惑度(集成模型)。

高效词嵌入训练:Word2Vec

一个遗留的问题是,在大词汇表上计算softmax(覆盖数万个可能的下一个词)在内存和计算上都非常昂贵。Word2Vec论文提出了一种高效的替代方案。

它提出了Skip-gram模型,其目标不是预测下一个词的完整分布,而是预测一个词是否出现在另一个词的上下文环境中。这将其转化为一个二元分类问题(逻辑回归):给定一对词,模型判断它们是否在训练语料中相邻出现。这种方法可以利用大量数据进行高效训练,并产生高质量的词嵌入。

利用这些词嵌入,我们可以进行一些有趣的“词向量算术”:

  • vec(“Paris”) - vec(“France”) + vec(“Italy”) 的结果最接近 vec(“Rome”)
  • vec(“sushi”) - vec(“Japan”) + vec(“Germany”) 的结果最接近 vec(“bratwurst”)

这不仅有趣,也表明词嵌入学习到的语义关系是非常有意义的。

总结

本节课我们一起学习了神经语言模型的核心思想。我们从传统N元语法模型的局限性出发,引入了分布式词表示(词嵌入)的概念,它通过将词映射到连续的低维空间来捕捉语义相似性。我们详细分析了首个神经概率语言模型的端到端架构,它同时学习词嵌入和语言模型参数,并将复杂度从指数级降低到线性级。最后,我们了解了Word2Vec这类高效训练词嵌入的方法及其有趣的应用。这些技术为现代自然语言处理奠定了坚实的基础。

046:神经机器翻译 🧠➡️🗣️

在本节课中,我们将学习神经机器翻译,这是深度学习领域的一个里程碑式应用。我们将从经典的序列到序列模型开始,逐步深入到完全基于注意力机制的Transformer架构,并了解其如何彻底改变了自然语言处理领域。

概述

神经机器翻译旨在构建一个端到端的可训练模型,将一种语言的句子自动翻译成另一种语言。本节将介绍该领域的关键发展,从最初的突破性模型到当前最先进的Transformer架构。

序列到序列学习

上一节我们讨论了循环神经网络。本节中,我们来看看如何将其应用于翻译任务。2014年,一篇名为《使用神经网络进行序列到序列学习》的论文提出了一个简单但强大的端到端机器翻译模型。

该模型的核心是一个编码器-解码器架构。编码器将源语言句子(如英语)编码成一个固定维度的向量,称为“思想向量”。解码器则根据这个思想向量生成目标语言句子(如法语)。

以下是该模型的关键特点:

  • 编码器和解码器均使用四层LSTM。
  • 编码器处理输入句子时采用逆序。这有助于建立短期依赖关系,因为句子的第一个词在编码结束时被处理,而解码器正是从目标语言的第一个词开始生成。
  • 思想向量是输入句子的全局语义表示,用于连接长度和词序可能完全不同的两种语言。

这个简单的模型,结合海量数据和计算资源,首次在效果上超越了所有基于手工规则的传统机器翻译系统。

解码策略:贪婪搜索与束搜索

上一节我们介绍了如何通过编码得到思想向量。本节中我们来看看解码器如何生成翻译结果。

解码的目标是找到最可能的输出句子序列 W_1, W_2, ..., W_T,使其在给定思想向量 V 下的概率 P(W_1, W_2, ..., W_T | V) 最大。

然而,穷举所有可能的序列计算量巨大。一种简单的近似方法是贪婪解码:在生成每个词时,只选择当前概率最高的那个词。

公式:在生成第 t 个词时,选择 argmax P(W_t | V, W_1, ..., W_{t-1})

但贪婪解码可能陷入局部最优。例如,生成句子开头时,“那些”可能比“苹果”有更高的初始概率,但完整句子“苹果很好吃”的整体概率可能高于“那些苹果很好吃”。

因此,实践中常使用束搜索。束搜索是贪婪搜索和穷举搜索之间的一个折中方案。

以下是束搜索(Beam Search)的基本步骤:

  1. 设定一个束宽 K(例如 K=2)。
  2. 在每一步,保留当前概率最高的 K 个候选序列。
  3. 基于这 K 个候选序列,继续扩展下一个词,并再次从所有扩展结果中选出新的 top-K 个序列。
  4. 重复此过程,直到所有候选序列都生成结束符。

通过始终维护一个“束”的候选解,束搜索通常能找到比贪婪解码质量更高的翻译结果。

Transformer:基于注意力的革命

序列到序列模型取得了成功,但其核心的RNN结构存在顺序计算的限制。2017年,Transformer模型的提出彻底改变了这一局面。

Transformer是一个完全基于自注意力机制的模型,它摒弃了循环和卷积结构。其最大优势在于并行化:句子中的所有词可以同时被处理,极大地提升了训练速度,并能更直接地捕获长距离依赖关系。

Transformer已成为当前所有顶尖自然语言处理模型的标准架构。

Transformer的核心:自注意力机制

现在,让我们深入理解Transformer的核心——多头自注意力层。

在Transformer中,每一层的输入和输出都是一个形如 L[T][J] 的张量,其中 T 是序列位置(词),J 是该位置的神经元(特征维度)。这可以看作是一个序列的向量表示。

自注意力层的目标是让序列中的每个词都能关注到序列中的其他所有词。为此,它为每个词计算三个向量:

  • 查询向量
  • 键向量
  • 值向量

这些向量通过将输入词嵌入与不同的可学习权重矩阵相乘得到。

公式:对于每个注意力头 k 和位置 t

  • 查询:Q_t^k = L_t * W_Q^k
  • 键:K_t^k = L_t * W_K^k
  • 值:V_t^k = L_t * W_V^k

注意力权重 A_{t1, t2}^k 通过计算查询向量和键向量的相似度(点积)并应用Softmax得到,表示词 t1 对词 t2 的关注程度。

公式A_{t1, t2}^k = softmax( (Q_{t1}^k · K_{t2}^k) / sqrt(d_k) ),其中 d_k 是键向量的维度。

最终,该注意力头的输出是注意力权重与值向量的加权和。

公式H_{t1}^k = Σ_{t2} ( A_{t1, t2}^k * V_{t2}^k )

Transformer使用多个这样的注意力头(原文为8个),每个头学习不同的关注模式。所有头的输出被拼接起来,再经过一个线性变换层,形成该自注意力层的最终输出。

Transformer在机器翻译中的应用

在机器翻译任务中,Transformer同样采用编码器-解码器架构。

  • 编码器:由多层Transformer块堆叠而成,处理源语言句子,输出其上下文表示。
  • 解码器:也是多层Transformer块。它接收编码器输出的,以及已生成目标句子的查询,以自回归的方式逐个预测下一个词。解码器使用掩码自注意力确保在预测当前位置时,不会“看到”未来的词。

这种架构在翻译质量上取得了飞跃,并催生了如GPT系列等强大的预训练语言模型。

大语言模型的成就与挑战

基于Transformer架构,通过在海量文本上进行无监督预训练,然后针对特定任务进行微调,产生了如GPT-2、GPT-3等大语言模型。它们展示了令人震惊的文本生成能力,例如根据提示续写文章、故事甚至代码。

然而,这些模型也面临挑战:

  1. 规模巨大:GPT-3拥有1750亿参数,训练需要巨大的工业级计算资源,难以复现。
  2. 理解局限:模型可能生成流畅但毫无意义或事实错误的内容,表明其缺乏真正的“理解”。
  3. 资源消耗:训练此类模型会产生巨大的碳足迹,引发对环境影响的担忧。

总结

本节课中我们一起学习了神经机器翻译的发展历程。我们从基于LSTM的序列到序列模型开始,了解了其编码器-解码器结构和束搜索解码策略。接着,我们深入探讨了革命性的Transformer模型,其核心的多头自注意力机制实现了完全并行化计算,并成为当前NLP的基石。最后,我们看到了基于Transformer的大语言模型所展现的强大能力及其带来的规模、理解和环境方面的挑战。神经机器翻译是深度学习改变现实世界应用的典范。

posted @ 2026-03-26 13:19  布客飞龙V  阅读(0)  评论(0)    收藏  举报