MIT-6-7960-深度学习笔记-全-

MIT 6.7960 深度学习笔记(全)

001:课程概述与神经网络基础 🧠

在本节课中,我们将要学习麻省理工学院6.7960高级深度学习课程的核心介绍。我们将探讨深度学习为何在今天变得如此重要,回顾其发展简史,并建立对神经网络基本构建块的理解。课程还将介绍本学期的学习目标、评估方式以及重要的课程政策。

为什么是深度学习?🚀

深度学习在社会中正经历着爆炸式增长。大约13年前,机器学习还不太奏效,但现在它已经变得非常有效。从AI辅助文本生成、3D重建(如NeRF)、AlphaGo、图像生成、代码编写辅助到游戏,深度学习几乎触及了社会的每一个维度。看到这些进展令人非常兴奋,希望我们都能在本课程中更深入地理解其含义。

什么是深度学习?🤔

深度学习的一个必要组成部分是神经网络。这是一类机器学习架构,基本上使用堆叠的线性变换,中间穿插着逐点的非线性激活函数。它们是推动上一张幻灯片中许多进展的基础构建块。

深度学习的另一个重要维度是可微分编程的理念。这本质上是一种编程范式,我们将程序的部分参数化,然后让基于梯度的优化来调整这些参数,以优化该程序或至少找到该程序的某种局部最优解。

我们认为,这两者共同构成了深度学习。在本课程中,我们将深入探讨这两方面。

课程理念与结构 📚

深度学习的突破是由理论和实践的混合驱动的,这两个维度对于该领域的未来进展都至关重要。因此,本课程旨在提供重要深度学习构建块的理论基础,同时也让大家实践实现、理解和使用这些模块。

课程作业与评分

课程作业将占总成绩的65%,包括五个问题集。每个问题集大约持续一到两周,需要提交笔头作业(或更现实地说,使用Overleaf)以及代码。

另外35%的成绩将来自一个期末项目。这是一个研究项目,侧重于深入理解课程中涵盖的某些主题。你需要为该项目提交一个初步提案。最终项目将以博客文章的形式提交,旨在展示新颖的实验或可视化。如今,在撰写机器学习论文时,通常也需要撰写相关的博客文章,这是一种非常有价值的技能。项目允许小组合作,但最多两人一组。

一个重要的注意事项是,我们无法提供大量的计算资源。因此,请不要计划一个需要在大规模数据集上训练庞大架构以达到最先进水平的项目。这并不意味着你不能做好研究,相反,这需要创造性地思考如何在资源有限的情况下进行有影响力的机器学习研究。

课程大纲概览

接下来,我将简要概述课程安排,让大家有一个大致的了解:

  • 今日:课程概述,神经网络简介及基本构建块。
  • 后续:如何训练神经网络、近似理论、不同架构(如网格、图)、优化缩放规则、泛化理论、更多架构(包括Transformer)、表示学习、生成模型、泛化(特别是分布外泛化)、迁移学习、大语言模型、缩放定律、自动梯度下降等。
  • 期末:将安排全班范围的项目答疑时间。

PyTorch 教程

我们将举办PyTorch教程,如果你不熟悉PyTorch或需要复习,强烈建议你参加下周提供的两次教程之一。虽然期末项目可能允许使用其他框架,但问题集的部分代码将基于PyTorch构建,因此熟悉PyTorch是必要的。

课程政策 📜

以下是重要的课程政策,详细信息也可在课程网页上找到。

合作政策

每个人都需要独立完成作业。在完成问题集时,请不要合作完成然后提交完全相同的内容。你可以与同学、助教和教师讨论内容,但提交的问题集必须是你自己独立完成的工作,这同样适用于你编写的代码。请勿复制或分享完整的解决方案,也不应询问他人你的解决方案是否正确。

如果你与除助教和教师以外的任何人合作完成了问题集,请在作业顶部注明他们的名字。

AI辅助政策

我们对ChatGPT等AI助手采取与人类合作者相同的政策。你应该尝试使用最新技术,但请将它们视为课堂上的同伴。不要要求它们为你解答问题或编写代码,就像你不会请朋友替你写代码一样。但欢迎将它们作为讨论伙伴,进行上下文相关的问答。

如果你在完成问题集时使用了任何AI工具,请像注明人类合作者一样,注明使用了哪个AI以及如何使用它。如果你不确定某项操作是否允许,可以想象AI是一个人类,并应用相同的规范。

深度学习的核心:建模复杂现象 🌍

我们关注深度学习的一个原因是,我们对建模现实世界中的复杂现象感兴趣。什么是复杂现象?例如自然语言、图像或一般的视觉数据、视频、DNA、生态系统,甚至气候变化都是复杂现象。为什么建模现实世界的复杂现象很困难?因为它们本身就非常复杂。

人类大脑可能是深度学习作为复杂现象建模解决方案的一个存在性证明。在座的各位都在MIT,这意味着你们的大脑非常擅长建模复杂现象。

神经网络简史 📜

让我们通过一张描绘神经网络热情随时间变化的图表,来简要回顾神经网络的历史。

  • 1958年:Rosenblatt在《心理学评论》上介绍了感知机,这是第一个神经网络,旨在识别或分类图像。其核心思想是获取表示的组合(如像素),求和,通过非线性处理,得到分类结果。这个想法至今仍是大多数深度学习的构建块。当时,人们对此非常热情。
  • 1972年:Minsky和Papert出版了《感知机》扩展版。这本书以批判的眼光看待感知机作为大脑模型的可行性,并从数学上仔细描述了感知机的局限性。这导致对机器学习的热情大幅下降,进入了所谓的“AI寒冬”。
  • 1986年:出版了《并行分布式处理》一书,引入了反向传播的概念。这使得多层感知机(不仅仅是单层)能够被有效地训练。这解决了像XOR这样的问题,人们再次对机器学习,特别是深度学习感到兴奋。
  • 1998年:Yann LeCun提出了卷积神经网络。然而,在2000年的顶级机器学习会议NeurIPS上,“神经”和“网络”仍然是论文被拒的最预测性词汇,AI寒冬仍在持续。尽管有了理论和构建块,但我们缺乏高效训练的能力、正确的编程视角和合适的硬件。
  • 2012年:Alex Krizhevsky等人发表了AlexNet。Alex Krizhevsky是一位出色的程序员,他发现了如何对GPU(图形处理单元)进行编程,将这些原本为图形处理设计的硬件重新用于训练神经网络。这是该领域的一个里程碑事件。AlexNet首次在ImageNet大规模数据集上超越了所有其他方法。大规模、精心标注的数据集也是机器学习开始奏效的关键组成部分。从此,人们的热情再次回归。

这些热情周期大约都是28年。现在是2024年,问题是:到2028年我们会处于什么位置?是另一个炒作周期,还是热情持续高涨,或者我们会达到一个新的高度然后再次意识到局限性而振荡?AI会在2028年接管社会吗?答案是我们拭目以待。

深度学习的现状 🏗️

当今深度学习的组成部分包括:

  • 自动微分:如PyTorch和TensorFlow等编码语言,它们提供了巧妙的方法来实现链式法则,充分利用可用硬件,使我们能够近似任何函数的梯度。
  • 十亿级数据点数据集:如LAION,大规模数据至关重要。
  • 数千GPU上的并行训练
  • 十亿级以上参数的架构,且数量持续增长。
  • 百万美元级的训练成本
  • 惊人的好结果,有些结果甚至超出了许多研究人员的预期。
  • 并非所有模型都需要巨大:例如Stable Diffusion相对轻量但极具影响力。
  • 开源社区与模块化重用:开源视角推动了大量进展,但近年来,像GPT-4这样的模型越来越不倾向于开源。

预备知识回顾与课程展望 🔍

接下来的内容将使用两种标识:“回顾”表示我们期望你在课前已经了解的内容;“展望”表示我们将在本课程中涵盖的内容。

回顾:梯度下降

我们期望你在本课程之前已经见过梯度下降。梯度下降本质上是试图优化某个成本函数的思想,我们通过在所有训练数据上找到一组参数,使得损失最小化。在实践中,你试图找到最优模型参数θ*以最小化成本函数。你可能从随机点开始,计算梯度,然后沿梯度方向更新模型权重,逐步迭代。我们将在后续课程中更详细地介绍随机梯度下降等变体。

展望:反向传播与可微分编程

在本课程中,我们将更深入地探讨反向传播,特别是可微分编程的思想。即构建围绕优化思想结构的编程语言,并通过梯度下降优化该架构的不同组件。思考你实际在优化什么,以及将梯度推送到哪些部分,是很有趣的。

回顾:多层感知机与非线性激活函数

我们期望你之前见过多层感知机和非线性激活函数(如ReLU)。神经网络中的计算通常是向量输入、向量输出的形式。神经网络的一个优点是,它们经常重复使用相同的简单计算单元。这些构建块通常是线性层和非线性激活函数的组合。

线性层可以表示为:
z_j = sum_i (w_ji * x_i) + b_j
或者向量化表示为:
z = x^T W_j + b_j
其中θ(模型参数)是所有权重项和偏置项的集合。

使神经网络成为神经网络的关键在于,它不仅仅是输入的线性组合,还有某种非线性函数映射输出,例如逐点非线性激活函数g(z)

一个可能的非线性函数是阶跃函数:如果z > 0则输出1,否则输出0。但这不是一个好的选择,因为它不可微,这会阻碍反向传播,因为梯度为零,模型无法更新。

一个简单的感知机(线性输入加逐点非线性)实际上可以进行线性分类。例如,对于二维输入x1, x2,学习权重w1, w2,得到隐藏单元z,然后通过函数映射到y。定义z = x^T w + by = g(z)。这本质上是一个平面,通过阈值处理可以变成一个线性分类器。但前提是数据是线性可分的。

在实践中,我们通常使用如ReLU(修正线性单元)的激活函数:g(z) = max(0, z)。它在正侧无界,可能导致梯度爆炸,但实现高效,导数简单,且似乎有助于加速收敛。其缺点是,在负区域单元会“死亡”,梯度为零。但ReLU是我们的默认选择,在当前模型中广泛使用。

回顾:堆叠层

我们可以堆叠层。第一层的输出h是点非线性g应用于W1^T x + b1的结果。然后y是相同的点非线性g应用于W2^T h + b2的结果。现在模型表示θ是所有层所有权重和偏置的集合。

有了感知机可以进行线性分类,但有趣的是,即使是两层感知机也可以进行非线性分类。通过两层线性变换中间加入非线性,可以组合出非线性的决策边界。

展望:近似能力与架构

在本课程中,我们将探讨深度学习的近似能力。一个维度是表示能力:单层网络可以实现任何线性决策面;两层及以上网络理论上可以表示任何函数(前提是层间有非平凡的非线性)。但理论上可以用足够宽的两层网络近似任何复杂函数,但这在实践中效率很低。而一个窄而深的模型可能用少得多的参数近似相同的复杂函数。在实践中,我们发现更多层数确实有帮助。我们将在关于近似理论的第3讲中深入探讨。

我们还将涵盖不同的架构。深度网络通常是简单计算的级联,每增加一层,就得到更抽象的表示。深层网络可以高效计算更复杂和抽象的事物。我们将讨论CNN、GNN、Transformer和RNN。

展望:泛化

我们还将讨论深度学习何时以及为何能够泛化。深度网络参数众多,本可以像查找表一样工作,但它们似乎学会了泛化的规则,这违背了经典理论。经典理论认为,如果模型过参数化,就会过拟合。但在现代插值区域,我们发现即使模型 massively overparameterized,我们实际上可能获得更好的性能。我们将在关于泛化(特别是分布外泛化)的讲座中详细讨论。

回顾:Softmax与交叉熵损失

我们期望你之前见过Softmax和交叉熵损失。通常,我们将神经网络的最后一层输出通过Softmax函数归一化为一个概率分布(更准确说是分数分布)。交叉熵损失衡量模型预测分布与真实分布(通常是one-hot编码)之间的距离,其目标是最大化训练数据的(对数)概率。

回顾:并行处理与张量

我们期望你对并行处理和张量的概念有所了解。由于这些计算是重复的,可以批量进行。张量就是多维数组。我们可以将批量数据堆叠起来,通过矩阵乘法高效计算。这也是GPU能进行大量并行乘法运算的重要原因。

展望:深度网络如何表示数据

在本课程中,我们将探讨深度网络如何表示数据。深度网络是表示数据中知识的一种更紧凑的方式。其基本思想是,存在一些对许多不同下游任务有用的低级构建块。例如,要分类字母“T”,可以重用“线条”分类器,然后理解两条线的连接点即可。实际上,在卷积神经网络中,早期层学习低级特征(如边缘、方向),后期层将这些特征组合成更复杂的概念。我们将在关于表示学习的讲座中详细讨论。

展望:生成模型

我们将涵盖生成模型,如基于文本和图像的生成,包括基础、表示和条件模型。

展望:权重重用与迁移学习

我们将讨论如何思考权重重用以及训练效率。如果能够重用预训练模型的组件,这在数据或计算资源有限时非常有价值。我们将在关于迁移学习的讲座中探讨这些表示是否可泛化或可迁移。

展望:缩放

最后,我们将讨论缩放。从大脑规模来看,小蠕虫只有302个神经元,果蝇有1.5万个,人类约有1000亿,大象约有2500亿。我们将讨论深度学习中的缩放意味着什么,缩放规律如何,以及如何自动学习最佳优化模型等。

总结 📝

本节课我们一起学习了深度学习的发展简史、当前核心组成部分以及本课程的基本框架。我们回顾了梯度下降、神经网络基础等预备知识,并展望了本课程将深入探讨的近似理论、各种网络架构、泛化、表示学习、生成模型、迁移学习和缩放定律等重要主题。希望这为你接下来的深入学习奠定了坚实的基础。

002:如何训练神经网络 🧠

在本节课中,我们将学习如何实际训练一个神经网络。我们将从梯度下降和随机梯度下降的回顾开始,然后引入计算图的概念,并详细讲解通过链式结构、多层感知机以及有向无环图进行反向传播的原理。最后,我们将探讨微分编程这一更宏观的概念。

梯度下降与随机梯度下降回顾 📉

首先,我们回顾一下训练神经网络的核心思想。通常,我们有一个输入数据 X 和一个对应的真实标签 y(例如,一张小丑鱼图片的类别)。模型(一系列神经网络层)将 X 转换为预测值。每个层都有其参数 θ。我们通过一个损失函数来衡量预测值与真实值之间的差异。训练的目标是找到一组最优参数 θ*,使得在整个数据集上的总成本 J(θ) 最小化。

梯度下降 是寻找最优参数的基本方法。其核心思想是:从参数空间的某个点开始,沿着损失函数梯度的反方向(即下降最快的方向)迭代更新参数,以逼近最小值点。单次迭代的更新公式为:

θ_new = θ_old - η * ∇J(θ_old)

其中 η 是学习率,控制每次更新的步长。

然而,对于现代大规模数据集,计算整个数据集的梯度(批量梯度下降)在计算上通常是不可行的。因此,我们使用 随机梯度下降。SGD 每次只使用数据集的一个子集(称为一个批次)来计算梯度,并以此作为对整个数据集梯度的近似估计。这大大加快了计算速度,并且批次带来的梯度噪声有时能起到正则化的作用,帮助模型跳出局部极小值。

动量与损失函数特性 🏃

为了改善优化过程,我们可以在梯度下降中引入 动量 的概念。其思想类似于物理中的动量:更新方向不仅取决于当前梯度,还累积了之前更新方向的一部分。这有助于加速收敛并减少震荡。更新公式变为:

v_t = α * v_{t-1} - η * ∇J(θ_t)
θ_{t+1} = θ_t + v_t

其中 α 是动量系数。

并非所有损失函数都易于优化。一个理想的损失函数(或激活函数)通常具备以下特性:

  1. 处处连续
  2. 处处可微
  3. 处处平滑

例如,ReLU 激活函数是连续且几乎处处可微的,但在零点不可微(不平滑)。而像 GELU 这样的激活函数则同时满足以上三点,代表了当前的一种趋势。

计算图与反向传播 🔄

理解神经网络训练的关键是将其视为一个 计算图。计算图是一个有向无环图,其中节点代表计算操作(函数),边代表数据(张量)的流动。

一个多层感知机可以很容易地表示为一个计算图:输入 X -> 线性层1 -> 激活函数 -> 线性层2 -> 输出 y -> 损失计算。

训练过程分为两个阶段:

  1. 前向传播:数据从输入层流向输出层,计算预测值和损失。
  2. 反向传播:损失值从输出层向输入层反向传播,利用链式法则计算损失相对于每一层参数的梯度。

反向传播本质上是一种高效计算梯度的算法。它利用了链式法则中中间结果可共享的特性,避免了重复计算,使得训练深层网络成为可能。

对于一个通用的层,在反向传播中我们需要跟踪两种梯度:

  • L:层输出相对于层输入的梯度(雅可比矩阵)。
  • G:成本相对于层输入的梯度(行向量)。

参数的更新方向可以通过 GL 计算得到。

线性层的具体示例 📐

以最简单的线性层 y = Wx 为例:

  • 前向传播y = W * x
  • 反向传播(计算输入梯度)∇_x J = ∇_y J * W^T
  • 参数更新∇_W J = x^T * ∇_y J,然后 W_new = W_old - η * ∇_W J

可以看到,线性层的反向传播操作同样是矩阵乘法,只是顺序和转置关系发生了变化。

扩展到有向无环图 🌉

实际的神经网络结构通常不是简单的链式结构,而是更复杂的 有向无环图,例如包含跳跃连接、多分支、参数共享等。

要将反向传播应用到任意 DAG,只需要处理两种基本操作:

  1. 合并:当多个分支汇合到一个节点时(例如相加或拼接),反向传播时,梯度会从该节点分发到所有输入分支。
  2. 分支:当一个节点的输出流向多个后续节点时,反向传播时,来自所有后续节点的梯度会 相加 后传回该节点。

通过这两种操作,我们可以将任何复杂的网络结构分解为可反向传播的基本单元。

微分编程:一种新的编程范式 🧩

我们将深度学习的训练过程抽象为 微分编程。在这种范式中,我们构建的程序(计算图)几乎全部由可微模块组成。训练过程就是通过梯度下降来“编写”或优化这些模块的参数,使整个程序能完成特定任务。

微分编程的威力在于其 组合性灵活性

  • 组合性:我们可以像搭积木一样,将预训练或手写的可微模块组合成新系统。
  • 灵活性:我们可以优化任何东西相对于任何标量成本的梯度。这不仅限于模型参数,还可以是输入数据、中间表示等。

例如,我们可以:

  • 固定一个训练好的图像分类模型,通过优化输入像素来生成最能激活某个神经元(如“猫”神经元)的图像(特征可视化)。
  • 将预训练的文本编码器(CLIP)和图像生成器(GAN)组合,通过优化给生成器的输入编码,使其生成与给定文本描述匹配的图像。

这开启了诸如文本生成图像、模型可解释性分析等众多应用。

总结 📚

本节课我们一起学习了神经网络训练的核心机制:

  1. 我们回顾了梯度下降随机梯度下降的基本原理及其权衡。
  2. 我们引入了计算图的概念,将神经网络视为数据流图。
  3. 我们深入探讨了反向传播算法,理解了它如何通过链式法则高效计算梯度,并具体分析了线性层的反向传播过程。
  4. 我们将反向传播推广到任意的有向无环图,理解了通过合并与分支操作处理复杂网络结构的方法。
  5. 最后,我们展望了微分编程这一更宏大的范式,看到了通过组合可微模块并利用梯度优化来构建复杂智能系统的潜力。

掌握这些原理是理解现代深度学习模型如何被训练和优化的基础。

003:近似理论

概述

在本节课中,我们将要学习神经网络的理论基础之一:近似理论。我们将探讨神经网络能够表达哪些函数,以及如何通过调整网络的宽度和深度来影响其表达能力。课程将从一个基本问题开始:在构建神经网络时,是应该增加网络的宽度还是深度?我们将通过分析神经网络的“通用近似”性质来尝试回答这个问题,并理解其在实际应用中的意义和局限性。


动机问题

上一节我们介绍了课程的整体框架,本节中我们来看看几个具体的动机问题,帮助我们理解近似理论要解决的核心问题。

问题一:线性可分性

想象一个二维数据集,包含两个坐标 X1X2,以及两个类别:红色叉号和绿色圆圈。

如果我们使用一个单层网络(即线性模型 W^T X + B,然后应用 ReLU 激活函数),我们能否拟合这个数据?

答案是不能。因为该模型本质上是一个线性分类器,而这个数据集不是线性可分的。这是对第一节课内容的回顾。

那么,如果我们使用一个两层网络呢?理论上,两层网络有可能拟合这个数据。这引出了我们的核心问题:给定一个模型架构(如特定层数的神经网络),是否存在一个该架构下的函数能够完美拟合给定的训练数据?这就是近似问题。

问题二:病态函数

考虑魏尔斯特拉斯函数,这是一个处处连续但处处不可导的经典病态函数。

我们能否用神经网络来拟合这个函数?这是一个有趣的思想实验,它促使我们思考神经网络能够近似的函数类的边界在哪里。

另一个例子是分形函数,它具有自相似性。这些例子都促使我们形式化地思考近似问题。


形式化近似问题

上一节我们通过例子直观感受了近似问题,本节中我们来正式定义它。

给定一个我们希望近似(理想情况下)的函数族 G。我们可以自由选择 G,例如要求函数可微,或者排除像魏尔斯特拉斯函数那样的病态函数。

同时,我们考虑一个神经网络族 F,这由我们的网络架构(例如,一个5层多层感知机)定义。

近似问题的核心是:对于函数族 G 中的任意一个函数,是否能在神经网络族 F 中找到某个网络,使得该网络与目标函数之间的误差小于某个很小的值 ε

我们需要定义误差的度量方式。例如:

  • L∞ 误差max_x |F(x) - G(x)|,即两个函数在所有输入点上的最大差异。
  • L1 误差∫ |F(x) - G(x)| dx,即两个函数差异在整个输入空间上的积分。

在本讲座中,为了便于展示,我们将选择一个特定的函数族 G 来研究神经网络能否近似它。这个函数族是利普希茨连续函数


利普希茨连续函数

上一节我们定义了近似问题,并选择了利普希茨连续函数作为目标函数族。本节中我们来详细了解什么是利普希茨连续性。

一个函数 G: R → R 被称为是 L-利普希茨连续 的,如果存在一个常数 L,使得对于任意输入点 x 和变化量 Δx,都有:
|G(x + Δx) - G(x)| ≤ L * |Δx|

这个定义有一个直观的理解:如果我们取 Δx 非常小的极限,这个不等式类似于说函数 G 的导数绝对值有界,即 |G'(x)| ≤ L。因此,利普希茨连续性是有界导数概念的一种推广。

从几何上看,如果函数在原点,那么它必须位于由直线 y = Lxy = -Lx 形成的“领结”形区域内。这个“领结”可以沿着函数平移,函数在任何点都必须位于该点的“领结”之内。

接下来,我们将这个概念推广到多维输入的函数 G: R^D → R。推广后的定义是:
|G(x + Δx) - G(x)| ≤ L * ||Δx||
这里,我们需要用向量的范数 ||Δx|| 来度量变化量的大小。我们可以选择不同的范数,例如均方根范数
||x||_RMS = sqrt( (1/D) * Σ_i x_i^2 )
这可以看作是欧几里得范数的一种无量纲化形式。


主要定理陈述

现在,我们可以陈述本节课要证明的主要定理了。

我们考虑的函数族 G 是:所有从 D 维超立方体 [0, 1]^D 映射到实数 RL-利普希茨连续函数

定理声称:对于任意误差容忍度 ε > 0,存在一个三层 ReLU 网络(包含一定数量的神经元 N),使得该网络与目标函数 G 之间的 L1 误差(积分误差)满足:
∫_{[0,1]^D} |F(x) - G(x)| dx ≤ 2L * (L/ε)^D

这里,N 是网络中神经元的总数。这个定理是“通用近似定理”的一种具体形式,它表明即使是有限深度的网络,只要足够宽,也能以任意精度近似一大类函数。

然而,这个结果也有明显的局限性:所需的神经元数量 N(L/ε)^D 相关,这意味着对于高维问题(D 很大)或要求高精度(ε 很小),网络可能需要指数级增长的宽度。这在实际中通常是不可行的。我们证明这个定理的目的,主要是展示这类证明的思路和结构,而非宣称这是一个最优或最实用的结果。


证明策略

为了证明上述定理,我们将采用一个三步走的策略:

  1. 一维矩形近似:首先,我们暂时忘记神经网络,考虑用一维的矩形条来近似一个一维的利普希茨连续函数。我们会分析这种近似的误差。
  2. 高维超矩形近似:接着,我们将一维的矩形构造推广到多维输入的情况,即用高维的“超矩形”来近似函数。
  3. 用 ReLU 网络实现超矩形:最后,也是最关键的一步,我们将证明一个两层 ReLU 网络可以很好地近似一个超矩形函数。然后,利用网络的第三层来线性组合这些超矩形,从而最终近似目标函数。

接下来,我们将详细展开每一步。


步骤一:一维矩形近似

我们首先考虑一维情况:G: [0,1] → R 是 L-利普希茨连续的。

我们通过在区间 [0,1] 上放置 N 个等宽的矩形条来近似 G。每个矩形条以其中心点处的函数值作为高度。近似函数 F(x) 可以写成:
F(x) = Σ_i α_i * Indicator_i(x)
其中,α_i 是第 i 个矩形的高度,Indicator_i(x) 是一个指示函数,当 x 落在第 i 个区间内时值为 1,否则为 0。

这种近似的误差来源于矩形顶部与函数曲线之间的三角形区域。由于函数是 L-利普希茨连续的,这个三角形的高度最多为 L * (1/N)(宽度乘以利普希茨常数)。因此,每个三角形的最大面积是 (1/2) * (1/N) * (L/N) = L/(2N^2)

由于有 N 个这样的区间,总的 L1 误差(即所有三角形面积之和)的上界为:
总误差 ≤ N * [L/(2N^2)] = L/(2N)

我们的目标是让误差小于 ε。因此,我们令 L/(2N) ≤ ε,解出 N。忽略常数因子,我们得到:只要 N ≥ L/ε,就能保证误差小于 ε


步骤二:高维超矩形近似

现在我们将思路推广到多维输入 G: [0,1]^D → R

我们将 D 维超立方体划分为 N 个小的超立方体(超矩形)。在每个小超立方体上,我们用常数(例如,在该超立方体某个角点处的函数值)来近似函数 G

误差来源于每个小超立方体顶部与函数曲面之间的“帽子”形区域。这个“帽子”的高度,同样由利普希茨条件限制,最多为 L * (边长)。如果我们有 N 个超立方体,那么每个的边长约为 N^{-1/D},因此高度上界为 L * N^{-1/D}

这个“帽子”的“底面积”是每个小超立方体的顶面积,约为 N^{-1}(因为所有小超立方体的顶面积之和为 1)。因此,每个“帽子”的体积(即误差贡献)上界约为:(底面积) * (高度) ≈ N^{-1} * (L * N^{-1/D}) = L * N^{-(1 + 1/D)}

总共有 N 个这样的“帽子”,所以总误差上界为:
总误差 ≤ N * [L * N^{-(1 + 1/D)}] = L * N^{-1/D}

为了达到误差 ε,我们需要 L * N^{-1/D} ≤ ε,这意味着:
N ≥ (L/ε)^D

这个结果与定理陈述中的形式一致。它表明,在高维空间中,为了达到相同的近似精度,所需的“基础元件”(此处是超矩形)数量随维度 D 指数增长。这被称为维度灾难


步骤三:用 ReLU 网络实现超矩形

前两步我们证明了可以用超矩形来近似函数。现在我们需要证明,一个 ReLU 网络可以有效地表示这些超矩形。

首先,我们展示如何用一个两层 ReLU 网络来近似一个一维矩形脉冲。考虑以下函数:
R(x) = ReLU(Cx) - ReLU(Cx - 1) - ReLU(C(x-1)) + ReLU(C(x-1) - 1)
其中 C 是一个很大的常数。当 C → ∞ 时,这个函数收敛于一个在区间 [0, 1] 上高度为 1,其他地方为 0 的矩形脉冲。通过调整权重和偏置,我们可以将这个矩形脉冲平移到任何位置,并缩放其高度。

对于 D 维超矩形,我们需要一个在 D 维空间的某个小区域内为 1,其他地方为 0 的函数。这可以通过一个巧妙的构造来实现:

  1. 对于每个输入维度 i,我们构造一个一维矩形脉冲 R_i(x_i),它在该维度对应的区间内为 1。
  2. 将这些一维脉冲相加:S(x) = Σ_{i=1}^D R_i(x_i)
  3. 观察 S(x):在所有 D 个维度对应的区间都重叠的中心区域,其值为 D;在只部分维度重叠的边缘区域,其值介于 1D-1 之间;在其他区域为 0。
  4. 为了得到我们想要的超矩形(仅在中心区域为 1),我们可以计算:H(x) = ReLU( S(x) - (D-1) )
    S(x) < D 时,S(x) - (D-1) < 1,但通过让第一步中构造矩形脉冲的常数 C 非常大,我们可以使矩形边缘非常陡峭,使得 S(x) 在非中心区域要么是 D,要么是 0。这样,ReLU( S(x) - (D-1) ) 就近似成了一个 D 维超矩形脉冲。

最后,为了近似目标函数 G,我们只需要构造 M = (L/ε)^D 个这样的超矩形脉冲 H_j(x),每个对应一个位置,并将其高度设置为该位置函数值的近似 α_j。然后用网络的第三层将它们线性组合起来:
F(x) = Σ_{j=1}^{M} α_j * H_j(x)
这就构成了我们的最终三层 ReLU 网络。通过取足够大的 C 和足够多的超矩形 M,我们可以使近似误差任意小。


对理论结果的反思

我们已经完成了一个通用近似定理的证明。然而,重要的是要思考这个理论结果的实用性和局限性。

以下是该构造的一些问题:

  1. 权重趋于无穷:为了完美表示矩形,我们需要让参数 C → ∞,这在实践中是不可能的,且大权重会导致数值不稳定。
  2. 不自然的表示:这种用局部矩形脉冲拟合数据的方式非常低效且不自然。它类似于“查表”,没有利用数据中可能存在的平滑性或结构。
  3. 泛化能力差:这种网络会完美拟合训练数据点所在的局部区域,但对其他区域则没有约束,因此几乎肯定会导致过拟合和极差的泛化性能。
  4. 维度灾难:所需的神经元数量随输入维度 D 指数增长 (L/ε)^D,这对于高维问题完全不现实。

这个练习告诉我们,仅仅证明“存在性”是远远不够的。机器学习成功的拼图包含三块:

  • 近似:模型族中是否存在一个函数能拟合数据?(我们刚讨论的)
  • 优化:我们能否通过算法(如梯度下降)找到这个函数?
  • 泛化:找到的函数在未见过的数据上表现如何?

通用近似性质只解决了第一块拼图,而且是一种非常低效的解决方式。在实践中,我们更关心的是能否高效地(用可接受的模型大小和训练时间)找到一个泛化能力好的函数。


深度分离理论

上一节我们看到了宽而浅的网络的理论能力及其局限性。本节中我们来看看支持增加网络深度的理论依据:深度分离

深度分离理论旨在证明,对于某些函数,用深网络表示比用浅网络表示要高效得多(即需要少得多的神经元)。其典型论证结构如下:

  1. 选择一个函数性质 P(例如,分段线性区域的数量)。
  2. 构造一个深度网络,它具有很多性质 P,但神经元数量并不多
  3. 证明任何具有可比拟性质 P 的浅层网络,都必须拥有指数级更多的神经元。

我们以 ReLU 网络产生的分段线性函数的“拐点”数量 为例。

首先,ReLU 网络是分段线性的。一个具有 D 个输入和 1 个输出的 ReLU 网络,其输出函数是由多个超平面划分区域形成的复杂分段线性函数。

我们关注“拐点”的数量,即函数导数发生突变的位置。对于一个浅层网络(例如3层),其拐点数量的增长最多与网络宽度成多项式关系。然而,对于一个深层网络,拐点数量可以随网络深度呈指数增长。

直观论证

  • 加法:将两个分段线性函数相加,新函数的拐点数量最多是两者之和。
  • ReLU 作用:对一个分段线性函数应用 ReLU,最多可能使其拐点数量翻倍。
  • 深层组合:在一个深度为 L、宽度为 N 的网络中,每一层都可能将上一层的拐点数量最多乘以 2N。因此,经过 L 层,拐点数量的上界约为 (2N)^L,这是随深度 L 指数增长的。

这只是一个上界。但我们可以构造一个具体的深层 ReLU 网络,使其拐点数量确实随深度指数增长。例如,考虑一个简单的“三角波”函数 G(x)。可以证明,将这个函数与自身复合 L 次(即 G∘G∘...∘G,共 L 次),得到的函数拐点数量约为 2^L。这个复合函数可以用一个大约有 2L 层的网络来实现。

现在,假设我们想用一个3层网络来精确表示这个具有 2^L 个拐点的函数。根据理论分析,这个3层网络所需的宽度将需要是 2^L 的量级,这是一个天文数字。这就展示了深度带来的指数级表达效率优势

深度分离理论表明,深度可以通过函数的复合,创造出极其复杂的分段线性结构,而浅层网络要模拟这种结构则需要巨大的宽度。这为在实践中使用深层网络提供了理论动机。


实践中的考量

理论结果(通用近似和深度分离)为我们提供了见解,但实际构建机器学习系统时,我们需要考虑更多复杂的因素。

在实践中,近似、优化和泛化问题交织在一起,难以分离。如果你训练一个模型失败了,很难立刻断定是模型表达能力不足、优化算法问题还是过拟合。

以大型语言模型为例,一个非常实际的问题是:在给定的计算预算下,是应该让模型更宽还是更深

一些实证研究(如 Kaplan 等人 2020 年的论文)发现,在相当宽的范围内,只要总参数量或计算量(FLOPs)相同,模型的性能对宽度和深度的具体比例并不敏感。他们得出的“缩放定律”主要关注数据量、计算量和参数量。

然而,后续研究(如“Chinchilla 缩放定律”)指出,这些结论可能受到其他因素(如学习率调度)的混淆。改变训练细节可能会得到不同的最优宽度-深度比例。这表明,要精确回答这些问题非常困难,因为训练中有太多未完全理解的变量。


总结与前瞻

本节课中我们一起学习了神经网络的近似理论。

我们首先证明了,一个足够宽的三层 ReLU 网络可以近似一大类函数(如利普希茨连续函数),这被称为通用近似性质。然而,这种构造在实践中效率很低,且无法保证优化和泛化。

接着,我们探讨了深度分离理论,它表明深层网络可以更高效地表示某些复杂函数(如具有大量线性区域的函数),而浅层网络则需要指数级更多的神经元。这为使用深层架构提供了理论依据。

最后,我们讨论了这些理论结果在实际中的意义。虽然它们丰富了我们对神经网络表达能力的理解,但机器学习的成功取决于近似、优化和泛化三者的共同作用,而这三者在实践中紧密耦合。关于宽度与深度的最优权衡,目前仍是一个开放的研究和实践问题,受到计算效率、优化难度和问题特定结构等多方面因素的影响。

在接下来的课程中,我们将看到,除了通用的全连接网络,针对不同类型的数据(如图像、音频、序列)设计特定的网络架构(如卷积神经网络、循环神经网络、Transformer),可以更高效地利用数据中的结构,从而在优化和泛化上获得更好的性能。这提示我们,将模型架构与所要解决的问题相匹配,是深度学习成功的关键之一。

004:网格架构 🏗️

在本节课中,我们将开始一系列关于机器学习架构的讲座。我们将超越之前讨论的非常基础的架构——多层感知机,开始探讨如何将结构或对模型空间的假设直接编码到我们要优化的模型架构中。

为什么需要更好的架构?🤔

上一节我们介绍了多层感知机,本节我们来看看它的优缺点。

多层感知机是一个通用逼近器,理论上可以构建任何模型。它的结构简单,便于进行优雅的理论分析,并且具有“令人尴尬的并行性”,可以高效利用GPU等并行计算设备。

然而,它也存在一些缺点:

  • 归纳偏置弱:模型本身没有内置太多关于目标函数结构的直觉或假设。
  • 样本效率低:学习复杂函数可能需要海量数据,甚至是不切实际的量。
  • 计算成本高:对于像高分辨率图像这样的高维输入,全连接层的计算非常昂贵。

架构与数据:缩小假设空间 🎯

我们可以从更宏观的视角来看待这个问题。假设所有可能的从输入X到输出Y的映射集合是一个灰色方框,其中包含了我们试图学习的真实模型。

以下是缩小搜索空间、逼近真实模型的两种主要方式:

  • 增加数据:更多的训练数据会减少能完美拟合这些数据的模型数量,从而缩小搜索空间。
  • 定义假设空间:为模型本身添加结构(即更强的归纳偏置或先验),将搜索范围聚焦在更有可能的模型子集上。

理想情况下,结合更多数据和更好的架构(更强的归纳偏置)能让我们最接近真实模型。一个好的架构应该能够表示数据的真实函数,同时易于通过基于梯度的学习进行搜索,并且易于在GPU上并行化。

卷积神经网络入门 🖼️

上一节我们讨论了架构的重要性,本节中我们将深入探讨一种特定的架构机制:卷积层,它特别适用于处理网格状数据(如图像)。

从局部处理到卷积

对于包含多个语义概念的图像,全局分类可能不够。一个思路是将图像分割成小块(补丁),并对每个补丁单独分类。但小补丁缺乏上下文信息,大补丁又可能包含物体边界。

卷积神经网络的核心理念是使用大但重叠的补丁。我们预测每个像素的类别时,会考虑以其为中心的一个局部上下文窗口。这样,模型在处理每个补丁时使用的是相同的函数,这带来了平移等变性:物体在图像中的位置变化不会影响其被识别的结果。

卷积运算详解

卷积是一种对网格结构输入数据进行线性、平移不变的变换。在深度学习中,我们通常所说的“卷积”运算在信号处理中更接近互相关。

公式:对于给定位置 (i, j) 的输出 y[i, j],其计算为:
y[i, j] = b + Σ_{m, n} w[m, n] * x[i+m, j+n]
其中,b 是偏置,w 是卷积核(滤波器)的权重,x 是输入。

卷积核在图像上滑动,计算局部加权和。例如,一个检测从暗到亮边缘的滤波器,会在相应区域产生高激活值。

卷积层 vs. 全连接层

  • 全连接层:每个输出神经元与所有输入相连。计算成本高,参数多。
  • 卷积层:每个输出神经元只与输入的一个局部区域相连,并且相同的权重(卷积核)在整个输入上共享。这可以看作是对全连接层权重矩阵施加了约束,使其变成一个稀疏的(带状)Toeplitz矩阵。

这种设计带来了多重好处:

  1. 平移等变性:内置了适用于图像识别的假设。
  2. 局部处理与显式并行:计算高效。
  3. 参数共享:极大减少了参数量,降低了过拟合风险。
  4. 处理可变尺寸张量:可以应用于任意大小的输入,具有良好的泛化性。

构建卷积网络:通道、堆叠与池化 🧱

多通道输入与输出

现实数据通常有多通道(如RGB图像)。对于多通道输入,卷积核会为每个输入通道学习一组权重,然后将所有通道的卷积结果求和,得到单个输出值。

为了得到多通道输出,我们需要学习一个滤波器组。每个滤波器独立产生一个输出通道。因此,一个卷积层的参数数量为:(输入通道数 × 核高 × 核宽) × 输出通道数

堆叠卷积层与感受野

通过堆叠卷积层(中间加入非线性激活函数,如ReLU),我们可以构建非线性的卷积滤波器。随着网络加深,每一层神经元所能“看到”的原始输入区域越来越大,这个区域称为感受野。深层神经元能够整合更大范围的上下文信息,从而识别更复杂的模式。

池化与下采样

为了降低计算量、增加平移鲁棒性,我们常在卷积层后使用池化操作,如最大池化(取局部窗口内的最大值)或平均池化。

下采样(如步幅卷积)可以同时减少空间尺寸。通过设置步幅大于1,卷积核在滑动时会跳过一些位置,从而降低输出特征图的分辨率。空洞卷积则通过间隔采样输入,在不增加参数量的情况下扩大感受野。

高级架构模式概览 🚀

上一节我们介绍了CNN的基础组件,本节我们来看看一些由这些组件构建的、在实践中非常成功的高级架构模式。

编码器-解码器结构

这种结构先通过卷积和池化(编码器)将输入压缩为一个低维表示(潜在空间),再通过反卷积或上采样(解码器)将其重建回原始尺寸或目标尺寸。常用于图像生成、自编码器和一些分割任务。

U-Net 与跳跃连接

U-Net是编码器-解码器结构的一个经典变体,其核心创新在于跳跃连接:将编码器中高分辨率特征图直接连接到解码器中对应的层。这使得解码器在重建时既能利用潜在空间的语义信息,又能保留编码器的细节信息,在医学图像分割等领域效果卓越。

ResNet 与残差连接

ResNet(残差网络)的核心思想是残差学习。它不直接学习目标映射 H(x),而是学习残差映射 F(x) = H(x) - x,并通过快捷连接实现 H(x) = F(x) + x。这种结构缓解了深度网络中的梯度消失问题,使训练极深的网络成为可能,并且让网络可以动态地调整其有效深度。

扩展到高维数据:3D卷积与神经场

卷积思想可以扩展到更高维度的网格数据:

  • 3D卷积:用于处理视频(空间+时间)或体数据(如医学CT扫描)。在时间维度上进行卷积可以捕捉动态信息。
  • 神经场:一种用神经网络参数化的场(如将空间坐标映射到颜色、密度的函数)。著名的NeRF(神经辐射场)就是一个例子,它从多视角图像中学习场景的连续体积表示,能够生成新颖视角的图像。

位置编码在神经场和Transformer中至关重要。它将离散的坐标信息(如像素位置)通过正弦函数等方式编码为连续向量,作为网络的附加输入,使模型能够感知位置信息。

总结 📚

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

  1. 构建更好架构的动机:通过引入更强的归纳偏置,我们可以用更少的数据学习更好的模型,并提高其泛化能力。
  2. 卷积神经网络的核心:卷积层通过局部连接、权重共享和平移等变性,为处理图像等网格数据提供了强大而高效的假设空间。
  3. 网络构建块:包括多通道卷积、堆叠以增加感受野、以及池化/下采样操作。
  4. 高级架构模式:编码器-解码器、U-Net的跳跃连接、ResNet的残差连接,这些设计极大地提升了深度网络的性能和可训练性。
  5. 概念的扩展:卷积思想可扩展到视频(3D卷积)和连续场景表示(神经场与位置编码)。

卷积是处理具有结构数据(如图像、时间序列)的基本操作。虽然现代架构如Transformer使用了注意力机制,但卷积所体现的“分块处理相同函数”的思想仍然深远地影响着深度学习的发展。

005:图神经网络 🧠

在本节课中,我们将要学习图神经网络。这是一种专门用于处理图结构数据的神经网络架构。图结构数据在现实世界中无处不在,例如社交网络、分子结构、推荐系统和交通网络。我们将探讨图神经网络的基本原理、其与卷积神经网络的关系,以及它们在理论上的表达能力。


概述

图神经网络是一种强大的工具,用于处理具有节点和边关系的数据。与卷积神经网络处理网格数据类似,图神经网络处理任意拓扑结构的图。本节课将首先介绍图神经网络适用的场景,然后深入其核心算法——消息传递,最后探讨其理论上的近似能力。


图神经网络的应用场景

图神经网络适用于那些数据天然以图的形式存在或可以表示为图的问题。以下是几个典型的例子:

  • 社交网络:用户作为节点,好友关系作为边,可用于用户分类或内容推荐。
  • 分子生物学:原子作为节点,化学键作为边,可用于预测分子的毒性或药物结合位点。
  • 推荐系统:用户和商品/内容构成二分图,可用于预测用户兴趣。
  • 路径规划:道路交叉口作为节点,道路作为边,可用于学习启发式算法以加速最短路径搜索。
  • 物理模拟:粒子作为节点,相互作用力作为边,可用于模拟流体或粒子系统。
  • 组合优化:决策变量作为节点,约束条件作为边,可用于近似求解线性规划等问题。

在这些问题中,图神经网络的优势在于能够利用数据中固有的关系结构,而传统的多层感知机或卷积神经网络难以直接处理这种非欧几里得数据。


图神经网络的核心:消息传递算法

上一节我们介绍了图神经网络的应用场景,本节中我们来看看其核心工作原理。图神经网络通过一种称为“消息传递”的算法来更新图中节点的表示。

一个图可以形式化地表示为:

  • 一组节点,每个节点有一个属性向量 h
  • 一个邻接矩阵 A,描述节点之间的连接关系。

图神经网络的目标是学习一个函数,该函数以节点属性集合和邻接矩阵为输入,输出每个节点或整个图的嵌入向量(用于预测)。

消息传递步骤

消息传递算法在每一层(或每一轮迭代 k)包含两个核心操作:聚合更新

  1. 聚合:对于每个节点,它从其所有邻居节点收集信息。聚合函数 AGGREGATE 接收邻居节点的嵌入向量集合,并输出一个汇总的消息向量 m。关键要求是,聚合函数必须是置换不变的,即无论邻居节点的顺序如何,输出的消息向量都相同。
    m_v^k = AGGREGATE({ h_u^{k-1} : u ∈ N(v) })
    
    其中 N(v) 是节点 v 的邻居集合。

  1. 更新:每个节点结合来自邻居的消息和自身当前的状态,更新自己的嵌入向量。更新函数 UPDATE 通常是一个简单的神经网络层。
    h_v^k = UPDATE(h_v^{k-1}, m_v^k)
    

通过堆叠多个这样的层,每个节点可以接收到来自越来越远邻居的信息,从而获得更全局的图视图。

与卷积神经网络的联系

图神经网络可以看作是卷积神经网络在非网格图上的推广。在CNN中,每个像素从其局部邻域(如3x3网格)聚合信息。在图神经网络中,每个节点从其任意数量的邻居聚合信息。两者都基于局部操作、参数共享,并且可以处理可变大小的输入。


聚合与更新函数的具体形式

上一节我们了解了消息传递的框架,本节中我们来看看聚合和更新函数的具体实现。

聚合函数

聚合函数是一个多重集函数,它接收一个集合(允许重复元素)并输出一个向量,且对输入顺序不变。以下是一些常见的聚合函数:

  • 求和m = Σ_{u∈N(v)} h_u
  • 均值m = (1/|N(v)|) * Σ_{u∈N(v)} h_u
  • 最大值m = MAX({ h_u : u ∈ N(v) })

值得注意的是,求和操作是一个通用的多重集函数近似器。具体来说,以下形式的聚合函数可以近似任何置换不变的多重集函数:

m_v = MLP_2( Σ_{u∈N(v)} MLP_1(h_u) )

其中 MLP 表示多层感知机。这意味着只要使用这种“MLP-求和-MLP”的形式,图神经网络在聚合能力上就是足够强大的。

更新函数

更新函数将节点自身嵌入和聚合后的消息结合起来。一个典型且简单的更新函数是:

h_v^k = σ( W_self * h_v^{k-1} + W_neighbors * m_v^k + b )

其中 σ 是激活函数(如ReLU),W_selfW_neighborsb 是可学习参数。

图级预测:读出函数

如果我们想对整个图做出预测(如图分类),我们需要一个读出函数。这本质上是另一个聚合操作,但作用于所有节点:

h_G = READOUT({ h_v^K : v ∈ V })

其中 V 是图中所有节点的集合,K 是消息传递的总层数。读出函数同样需要是置换不变的。


图神经网络的理论表达能力

前面我们介绍了图神经网络如何工作,本节中我们探讨其理论上的能力边界:图神经网络能区分哪些图,不能区分哪些图?

区分能力的限制:WL测试

图神经网络的消息传递机制决定了,每个节点“看到”的只是以自己为根的计算树的结构。如果两个不同的图,其所有节点对应的计算树结构都相同,那么对于任何图神经网络来说,这两个图就是不可区分的。

这与图论中经典的 1-Weisfeiler-Lehman (1-WL) 测试 的区分能力相同。1-WL测试是一种判断两个图是否同构的启发式算法。理论表明:

  • 图神经网络最多只能达到 1-WL 测试的区分能力。
  • 通过精心设计(如使用前述的通用聚合函数),图神经网络可以达到 1-WL 测试的区分能力。

这意味着存在一些结构不同但1-WL测试无法区分的图,图神经网络也无法区分它们,因此无法学习需要区分这类图的函数。

实践中的影响

这种理论限制在实践中具有重要意义。例如,选择不同的聚合函数会直接影响模型的能力:

  • 使用通用形式(MLP-求和-MLP)的聚合函数可以完美拟合训练数据。
  • 若错误地使用均值代替求和作为聚合函数,模型可能会丢失关于邻居数量的信息,导致在某些任务上完全失败,即使两者看似差别很小。

突破限制:位置编码

如何让图神经网络区分更多类型的图?一个关键方法是打破置换不变性的约束,为每个节点添加位置编码。这类似于在卷积神经网络中添加位置编码以获取绝对位置信息。

在图神经网络中,可以为每个节点添加一个唯一的标识符(如独热编码),或使用更高级的方法,如基于图拉普拉斯矩阵特征向量的编码。这样,节点就能感知自己在图中的“位置”,从而区分那些计算树结构相同但实际布局不同的图。当然,这会牺牲模型对节点索引顺序的泛化能力,是一种权衡。


总结

本节课中我们一起学习了图神经网络。我们首先了解了图神经网络适用于社交网络、分子预测等多种图结构数据问题。然后,我们深入探讨了其核心算法——消息传递,包括聚合和更新两个步骤,并看到了其与卷积神经网络的紧密联系。最后,我们分析了图神经网络的理论表达能力,认识到其区分能力与1-WL测试等价,并了解了通过位置编码可以突破部分限制。图神经网络是连接传统神经网络与更复杂数据结构的重要桥梁,也是理解后续Transformer架构的基础。

006:泛化理论 🧠

在本节课中,我们将要学习深度学习的泛化理论。我们将探讨神经网络为何以及如何能够对未见过的数据进行有效预测,并审视经典泛化理论在解释深度学习现象时的局限性。

什么是泛化? 📖

到目前为止,本课程主要讨论了近似问题,即模型在训练数据上的拟合能力。我们通过经验风险来衡量这种拟合:

R_empirical(θ) = (1/N) * Σ L(f(x_i; θ), y_i)

其中,L 是损失函数,f 是我们的模型,θ 是模型参数。

然而,机器学习的真正目标是泛化,即模型在来自同一数据生成过程 P 的新样本(测试集)上的表现。我们关心的是总体风险(或测试误差):

R_population(θ) = E_{(x,y)~P} [L(f(x; θ), y)]

泛化问题研究的是经验风险与总体风险之间的差距。一个成功的机器学习流程需要兼顾三点:良好的近似能力(低训练误差)、有效的优化算法(找到最优参数)以及出色的泛化性能(低测试误差)。

上一节我们介绍了泛化的基本概念,本节中我们来看看一些关于泛化的直观理解。

泛化的直观理解 🧩

理解泛化需要同时考虑数据和模型。

数据的重要性:如果训练数据缺乏多样性(例如,只包含猫的图片来训练猫狗分类器),模型将无法学习到区分不同类别的关键特征,从而导致泛化失败。高质量、有代表性的数据是良好泛化的基础。

模型的作用:模型本身的结构和偏好也至关重要。考虑以下两个极端模型:

  1. 文件柜模型(纯记忆):此模型只是简单地记忆所有训练数据。虽然其训练误差为0,但对于任何未见过的输入,它都无法做出合理预测,因此泛化误差极高。
  2. 随机预测模型(幸运拟合):像“章鱼保罗”这样的模型可能恰好拟合了训练数据,但其预测机制与真实世界无关,因此也无法可靠地泛化。

深度网络的表现与上述模型不同。它们不仅能完美拟合训练数据,还能以一种平滑、合理的方式对训练点之间的数据进行插值,这表明它们具有更强的泛化能力。

深度网络能泛化吗? ❓

答案是肯定的,现代深度网络展现出了惊人的泛化能力。我们可以通过思想实验来论证这一点。

考虑一个大型语言模型(如ChatGPT)。假设其词汇表大小为 M=30(例如30种水果),生成长度为 N=10 的随机序列。可能的独特序列数量为 M^N ≈ 30^10。如果我们让模型回答关于这些随机序列的问题(例如,“列表中有多少柑橘类水果?”),并发现其正确率 P 很高(例如接近1)。那么,一个纯粹记忆的“文件柜”模型需要存储 P * M^N 个样本才能达到同等性能,这个数字(例如100万亿)远远超过模型实际训练的数据量。这表明模型并非简单记忆,而是在进行某种形式的泛化和推理。

此外,在图像生成等任务中,模型能够处理训练中从未出现过的输入组合(例如,为一张画有三只眼睛的猫的草图生成合理图像),这得益于其架构(如卷积网络)的组合性归纳偏置

接下来,我们将探讨经典泛化理论及其在深度学习时代面临的挑战。

经典泛化理论及其局限 ⚠️

经典机器学习理论深受奥卡姆剃刀原则影响:在同样能完美拟合数据的模型中,应选择最简单的,因为这样的模型最有可能泛化。

过拟合与偏差-方差权衡

经典观点认为,随着模型容量(例如参数数量)增加,训练误差会下降,但测试误差会先下降后上升,出现过拟合。这体现了偏差(训练误差)与方差(泛化误差)之间的权衡。

然而,在深度学习中,我们观察到了双重下降现象。当模型容量远超“插值阈值”(即刚好能完美拟合训练数据的容量)后,测试误差会再次下降并变得更好。这表明,在超高容量下,优化算法倾向于在众多能完美拟合数据的解中,找到那些“更简单”或“更平滑”的解。

VC维理论

VC维是衡量假设空间复杂度的经典方法。其核心思想是:如果训练数据量 N 远大于假设空间所能产生的二分法数量(VC维 D),那么泛化误差就有理论保证。

泛化误差的一个经典上界与 sqrt(D/N) 相关。

然而,对于神经网络,一个关键问题是:它们可以完美拟合任何随机标签的数据集。这意味着对于 N 个数据点,神经网络可以表达所有 2^N 种可能的二分法,即其VC维至少为 2^N。这将导致泛化上界变得极其宽松(甚至超过100%),从而失去实际指导意义。因此,VC维理论无法解释深度网络的泛化能力。

既然经典理论失效,我们必须探究深度网络内在的、促使它们选择可泛化解的机制。

深度网络为何能泛化? 🔍

深度网络之所以能泛化,是因为在完美拟合训练数据的版本空间内,优化过程并非随机选择,而是受到多种归纳偏置的影响,使其倾向于选择“更简单”的解。

以下是几种重要的归纳偏置:

1. 参数-函数映射中的简洁性偏置

从参数空间到函数空间的映射是多对一的。研究表明,在典型的神经网络参数空间中,随机采样得到的参数所对应的函数,具有较高的概率是“简单”的(例如,具有较低的Kolmogorov复杂度或较低的矩阵秩)。因此,从随机初始化开始的优化过程,更有可能收敛到这些简单函数所在的区域。

2. 优化动态的隐式偏置

优化算法本身也引入了偏置:

  • 权重衰减:显式地惩罚大的权重,促使模型使用更小的参数范数,这通常与更简单的函数相关。
  • 梯度下降的隐式偏置:使用固定步长的梯度下降倾向于收敛到平坦的极小值,而非尖锐的极小值。理论分析和实验表明,平坦极小值通常具有更好的泛化性能。

3. 架构中的对称性与组合性(最关键的因素)

这是现代深度网络强大泛化能力的核心来源。模型架构中内置的不变性等变性直接编码了关于世界的先验知识:

  • 卷积网络:具有平移等变性,使其能自动处理图像中物体的位置变化。
  • 图神经网络:具有置换不变性/等变性,使其能处理任意顺序的节点输入。
  • 组合性:如卷积网络将图像分解为局部斑块独立处理,然后组合。这使得模型能够泛化到训练中未出现过的斑块组合方式(例如,更多数量的眼睛)。

4. 领域知识的融入

许多成功的模型将特定领域的知识(如物理定律、化学约束)直接编码到架构或损失函数中。这种“数据+结构”的结合极大地增强了模型的泛化能力和可解释性。

总结 📝

本节课中我们一起学习了深度学习的泛化理论。我们回顾了泛化的基本概念,并通过实验论证了深度网络确实具有强大的泛化能力。我们发现,经典的泛化理论(如VC维)在解释深度学习现象时存在局限。深度网络的泛化能力源于多种归纳偏置的共同作用,包括参数-函数映射中的简洁性偏好、优化算法的隐式偏置,以及最为关键的——模型架构中内置的对称性、组合性和领域知识。这些偏置引导优化过程在众多能拟合训练数据的解中,选择那些更可能反映真实世界规律、从而能够泛化的解。理解这些偏置对于设计和解释高效的深度学习模型至关重要。

007:优化的缩放规则 🚀

在本节课中,我们将学习深度学习优化的核心概念,探讨一些经典的优化方法,并深入理解如何使训练过程在模型规模(宽度和深度)扩大时保持高效和稳定。我们将从基础优化问题出发,分析不同方法的建模假设与局限性,最终聚焦于解决模型缩放时的关键挑战。

概述 📋

深度学习优化的目标是找到神经网络的权重参数,以最小化在训练数据上的损失函数。我们通常使用梯度下降及其变种进行迭代优化。然而,当模型规模(如宽度或深度)扩大时,优化过程会遇到新的挑战,例如最优学习率发生变化、训练性能下降等。本节课将系统性地介绍这些挑战及其解决方案。

优化问题定义

一个典型的深度学习优化问题可以形式化如下:

我们有一个神经网络,它是一个函数 F,接收输入 x 和权重 W,产生输出 ŷ
我们有一个误差度量 L(例如交叉熵或均方误差),用于衡量预测值 ŷ 与目标值 y 之间的差异。
我们收集了包含 N 个数据点的训练数据集 {(x_i, y_i)}
损失函数是数据点上误差的平均值:

L(W) = (1/N) Σ_i L_i(W)
其中 L_i(W) = L(F(x_i; W), y_i)

我们的目标是找到权重 W,以最小化此损失函数 L(W)

经典优化方法

上一节我们定义了优化问题,本节中我们来看看几种经典的优化方法。这些方法都始于对损失函数进行泰勒展开。

损失函数在权重 W 附近的泰勒展开(到二阶)为:
L(W + ΔW) ≈ L(W) + G^T ΔW + (1/2) ΔW^T H ΔW + ...
其中:

  • G 是梯度向量:G = ∇_W L(W)
  • H 是海森矩阵:H = ∇²_W L(W)

不同方法通过对高阶项做出不同假设来推导更新规则 ΔW

牛顿法

牛顿法直接使用二阶泰勒展开,并忽略更高阶项。它通过最小化这个二次模型来求解更新步长。

ΔW = -H⁻¹ G

以下是牛顿法面临的主要问题:

  • 计算与存储成本高:海森矩阵 H 的维度是 D×D(D为参数数量),对于大型神经网络(D可达数十亿),存储和求逆该矩阵是不现实的。
  • 可能收敛到极大值点:求解二次模型临界点得到的不一定是最小值点,也可能是鞍点或最大值点。
  • 需要计算二阶导数:这增加了额外的计算开销。

尽管有研究试图使其更实用(如立方正则化),但在深度学习的实际训练中,我们通常不使用牛顿法。

高斯-牛顿法

高斯-牛顿法针对复合损失函数(如神经网络)设计。它利用了高斯-牛顿分解。

对于复合损失 L(W) = L(F(W)),其海森矩阵可以分解为:
H = J^T (∇²_F L) J + (∇_F L)^T (∇²_W F)
其中 J = ∂F/∂W 是模型输出对权重的雅可比矩阵。

高斯-牛顿法做出了两个关键假设:

  1. 假设误差函数 L 是平方误差,此时 ∇²_F L 是常数(例如单位矩阵)。
  2. 忽略模型 F 本身的曲率项 ∇²_W F

在这些假设下,海森矩阵近似为 H ≈ J^T J。相应的更新规则为:

ΔW = -(J^T J)⁻¹ J^T (∇_F L)

以下是高斯-牛顿法面临的主要问题:

  • 需要计算雅可比矩阵 J:这比计算标准梯度 G 更昂贵。
  • 涉及矩阵求逆:即使 J^T J 比完整海森矩阵小,求逆操作在大规模问题上仍然成本高昂。
  • 建模假设可能不成立:忽略模型曲率项可能不适用于高度非线性的深度网络。

因此,尽管高斯-牛顿法在理论上很有吸引力,但在大规模深度学习实践中并不常用。

最速下降法

最速下降法采用了一种不同的思路:它用一个简单的惩罚项来替代泰勒展开中的高阶非线性部分。

其模型为:L(W + ΔW) ≈ L(W) + G^T ΔW + (λ/2) ||ΔW||²
其中 ||·|| 是我们选择的一种范数,λ 是一个标量参数。

该方法通过最小化上述模型来求解 ΔW。选择不同的范数会导致完全不同的优化算法。

以下是几种常见范数选择及其对应的算法:

  • L2范数(欧几里得范数)||ΔW||₂。此时解为 ΔW = -(1/λ) G,这等价于标准的梯度下降,学习率为 η = 1/λ
  • L∞范数(无穷范数)||ΔW||∞ = max_i |ΔW_i|。此时解为符号梯度下降:ΔW = -(||G||₁ / λ) · sign(G)
  • 弗罗贝尼乌斯范数:用于矩阵权重,是矩阵元素的L2范数。

一个关键问题是:如何选择合适的范数? 理想情况下,我们希望选择的范数和参数 λ 能够真实地反映损失函数中高阶项的行为,从而保证更新步长既不太大(避免破坏线性近似)也不太小(保证训练效率)。这通常需要针对特定模型进行分析,是一个活跃的研究领域。

模型缩放的挑战与启发式解决方案

上一节我们介绍了几种经典的优化方法,本节中我们来看看当扩大神经网络规模(缩放宽度或深度)时,优化会遇到哪些实际问题,以及如何直观地解决它们。

问题描述:最优学习率的漂移

当改变模型宽度(增加层中神经元数量)时,观察到以下现象:

  • 更宽的模型通常能达到更低的损失(性能更好)。
  • 但是,达到最佳性能所需的最优学习率会随着宽度变化而改变。

这意味着,如果我们先在小型模型上调好学习率,然后直接将其用于放大后的模型,训练可能失败,必须重新调整学习率。这给大规模训练带来了不便。

类似地,当增加网络深度时,如果方法不当,训练性能可能会随着深度增加而变差(例如在残差网络普及之前)。

解决方案思路:选择正确的范数

问题的核心在于:当我们说权重更新 ΔW “太大”或“太小”时,必须明确是在哪种范数下衡量的。对于神经网络,由于其权重以矩阵形式组织,选择矩阵范数比向量范数更自然。

一个特别有用的视角是谱视角。任何矩阵 M 都有奇异值分解(SVD)。矩阵的谱范数(最大奇异值)具有清晰的几何意义:它衡量了在输入具有单位L2范数时,输出向量的最大可能L2范数。

||M||2 = max ||Mv||₂

对于神经网络层,我们通常更关心其激活值的尺度是否稳定。实践中,常用RMS(均方根)范数对激活进行归一化(如LayerNorm)。RMS范数定义为:

||v||_RMS = (1/√d) ||v||₂,其中 d 是向量 v 的维度。

基于此,我们可以为矩阵定义一个RMS-to-RMS诱导算子范数

||M||RMS→RMS = max ||Mv||_RMS

可以证明,这个范数与谱范数成正比,但包含了维度缩放因子。

宽度缩放问题的解决方案

为了消除最优学习率随宽度变化的漂移,我们可以采取以下两步:

  1. 初始化:将每一层的权重矩阵 W_L 初始化为满足 ||W_L||_RMS→RMS ≈ 1
  2. 更新:控制每一层的权重更新 ΔW_L,使其满足 ||ΔW_L||_RMS→RMS ≈ η(其中 η 是学习率)。

直观解释

  • 初始化:确保前向传播时,如果输入特征的RMS范数约为1,则输出特征的RMS范数也大约为1,防止激活值爆炸或消失。
  • 更新:确保单次更新对网络内部激活(特征)的改变幅度是受控的,且与学习率 η 成正比。这样,学习率 η 本身就成为一个与模型宽度无关的、衡量“特征学习速度”的标量。

通过这种方式,所有与宽度相关的缩放因子都被吸收到了范数的定义和初始化/更新规则中,使得最优学习率 η 在不同宽度的模型间可以保持恒定。

深度缩放问题的思路

对于深度缩放,核心思想类似于残差网络的设计。考虑一个残差块:x_{l+1} = x_l + Block_l(x_l)

为了使极深网络(层数 L 很大)的训练稳定,我们需要仔细设计残差块的缩放。一个类比是极限:

lim_{L→∞} (1 + x/L)^L = e^x

我们需要决定在残差块前乘以一个什么样的因子(例如 1/L, 1/√L),才能保证当 L 很大时,整个网络的复合行为既不会爆炸也不会消失。这是一个仍在研究中的问题,现代Transformer架构中常使用 1/√L 的缩放因子,其动机类似于随机游走的方差缩放。

总结与展望

本节课我们一起学习了深度学习优化的核心内容。

我们首先回顾了优化问题的形式化定义。然后,系统性地探讨了几种经典优化方法(牛顿法、高斯-牛顿法、最速下降法),分析了它们背后的建模假设以及在实际深度学习中不常使用的原因,核心在于计算成本和高阶项建模的困难。

接着,我们聚焦于模型缩放带来的实际挑战:宽度增加导致最优学习率漂移,深度增加可能导致性能下降。我们指出,关键在于选择正确的范数来衡量权重更新的“大小”。通过为神经网络层引入RMS-to-RMS诱导算子范数,并以此规范初始化和更新过程,可以有效地解决宽度缩放问题,实现学习率在不同宽度模型间的可迁移性。对于深度缩放,我们讨论了通过精心设计残差块缩放因子来维持稳定性的思路。

最后,我们展望了一个模块化、可组合的优化理论方向,旨在为任何神经网络架构自动提供合适的优化规范,这是当前研究的前沿领域。

通过理解这些缩放规则,我们可以更高效、更稳定地训练大规模深度学习模型。

008:Transformer架构 🧠

在本节课中,我们将学习Transformer架构。这是当前最流行、最强大的深度学习架构之一,广泛应用于自然语言处理、计算机视觉等多个领域。我们将从卷积网络的局限性出发,逐步介绍Transformer的三个核心思想:令牌化注意力机制位置编码。最后,我们会了解如何将这些组件组合起来,构建出强大的视觉Transformer和语言模型。


卷积网络的局限性

上一节我们介绍了卷积网络(CNN)及其在图像处理中的优势。本节中我们来看看CNN的一个主要限制:局部性偏置

CNN的滤波器是局部的,这意味着每个输出神经元只依赖于输入的一个小邻域。这种设计对于许多任务(如图像分类)非常有效,因为它利用了信号的局部平滑性。然而,当任务需要理解图像中相距较远部分之间的关系时,CNN的效率就不高了。

例如,要回答“右上角的鸟和左下角的鸟是同一物种吗?”这个问题,CNN需要非常深的网络才能让信息从图像一端传播到另一端。这是因为信息在CNN中是通过一层层的局部感受野缓慢“扩散”的。

另一种方法是使用全连接网络(MLP),它允许每个输出神经元直接连接到所有输入神经元,从而实现全局信息处理。但全连接网络参数过多(N²个),计算量大,且容易过拟合。

Transformer的核心思想——注意力机制——提供了一种折中方案:它允许网络在处理每个部分时,有选择地、稀疏地关注输入中任何相关的部分,从而实现高效的全局信息处理。


核心思想一:令牌化

Transformer处理的基本单位不是单个神经元(标量),而是令牌。令牌是一个神经元向量,可以看作是一个封装好的信息包。

以下是关于令牌的关键点:

  • 令牌是向量:每个令牌是一个D维向量。
  • 令牌代表数据块:原始数据(如图像、文本)被分割成有意义的块(如图像块、单词),每个块被编码成一个令牌。
  • 操作于令牌集合:Transformer的输入和输出通常是一个令牌集合。我们可以将其排列成一个矩阵 T ∈ ℝ^(N×D),其中N是令牌数量,D是每个令牌的维度。
  • 领域无关性:将数据转换为令牌是唯一需要领域知识(如图像分块、文本分词)的步骤。之后,通用的Transformer架构就可以处理这些令牌。

将数据转换为令牌后,我们就可以定义基于令牌的基本操作,这与MLP中的操作类似。


基于令牌的网络操作

在标准MLP中,我们有标量神经元的线性组合逐点非线性激活函数。在基于令牌的网络中,我们将其推广为:

  1. 令牌的线性组合:输出令牌是输入令牌的加权和。如果输入是令牌矩阵 T,权重向量为 w ∈ ℝ^N,则输出令牌 t_out = w^T T。这相当于对令牌行进行低秩线性变换。
  2. 令牌级非线性:将一个非线性函数 F(通常是一个小型MLP)独立且相同地应用于每一个输入令牌,产生输出令牌。这类似于在令牌序列上应用“1x1卷积”。

通过堆叠“令牌线性组合”层和“令牌级非线性”层,我们可以构建一个“令牌网络”。这实际上与图神经网络(GNN) 中“聚合”与“更新”的步骤非常相似。关键区别在于,在标准的Transformer中,每个令牌都可以关注所有其他令牌(全连接图),而GNN中的连接由输入图结构决定。


核心思想二:注意力机制 🎯

注意力机制决定了在“令牌线性组合”步骤中,权重如何计算。在Transformer中,这些权重不是固定的可学习参数,而是由输入数据本身动态计算出来的。

其直观思想是:根据当前要处理的问题(或“查询”),网络应学会“注意”输入中与之最相关的部分。

最常用的注意力形式是查询-键-值注意力。以下是其工作原理:

每个输入令牌通过三个不同的可学习线性投影,生成三个向量:

  • 查询向量(Query):表示该令牌“想要寻找什么”。
  • 键向量(Key):表示该令牌“包含什么信息”,用于被查询匹配。
  • 值向量(Value):表示如果该令牌被选中,它将“提供什么信息”。

对于给定的一个输出位置(其查询向量为 q),我们计算它与所有输入令牌键向量 k_i 的相似度(通常用点积):s_i = q · k_i
然后,对这些相似度分数应用Softmax函数进行归一化,得到注意力权重 α_i
最后,输出是所有输入值向量 v_i 的加权和输出 = Σ α_i * v_i

在数学上,对于整个令牌序列,这可以简洁地表示为著名的注意力公式:
输出 = softmax( (Q K^T) / √d_k ) V
其中 Q, K, V 分别是所有令牌的查询、键、值矩阵,d_k 是键向量的维度。


自注意力

自注意力中,查询、键、值都来自同一组输入令牌。这意味着每个令牌都可以通过查询其他令牌来更新自己的表示。

例如,图像中的一个斑马令牌可以通过查询其他看起来像斑马的图像块(具有相似键向量的令牌),并聚合它们的值信息,从而更好地确认自己确实是斑马。这本质上是在学习一种分割聚类

自注意力使Transformer成为强大的集合到集合的映射模型。


注意力与其他层的比较

现在,我们可以比较几种不同的“线性组合”层:

  • MLP全连接层:权重矩阵是稠密的、独立可学习的参数(N²个)。没有内置的结构偏置。
  • 卷积层:权重矩阵具有拓扑结构(如带状、权重共享),编码了平移不变性的强偏置。
  • 注意力层:权重矩阵由数据动态计算(A = softmax(QK^T/√d))。它是低秩的,并且内置了根据内容相关性进行全局交互的能力。同时,它对输入令牌的排列是等变的


完整的视觉Transformer架构 🖼️

一个标准的视觉Transformer(ViT)块按以下顺序组合了这些操作:

  1. 层归一化
  2. 多头自注意力:并行运行多个自注意力机制(“头”),每个头可以学习关注不同类型的信息,然后将结果拼接起来。
  3. 残差连接:将注意力块的输入加到其输出上。
  4. 层归一化
  5. 令牌级MLP:一个应用于每个令牌的前馈网络(通常包含非线性激活)。
  6. 残差连接:再次将输入加到MLP的输出上。

整个Transformer由多个这样的块堆叠而成。开头是将图像分割为补丁并线性投影为令牌,以及添加位置编码。结尾可能有一个用于分类的全局池化或特殊令牌。

伪代码如下所示,突出了其简洁性:

def transformer_block(tokens):
    # 1. 层归一化 + 自注意力 + 残差
    attn_out = attention(layer_norm(tokens)) + tokens
    # 2. 层归一化 + MLP + 残差
    mlp_out = mlp(layer_norm(attn_out)) + attn_out
    return mlp_out


核心思想三:位置编码 📍

由于自注意力对输入顺序是排列等变的,它本身无法感知令牌的位置信息。然而,对于许多任务(如语言理解、图像中物体的绝对位置),顺序或位置至关重要。

解决方案是位置编码:将表示位置信息的向量与每个令牌向量相加。

常用的方法是正弦/余弦编码。对于序列中的位置 pos 和维度 i,编码值为:
PE(pos, 2i) = sin(pos / 10000^(2i/D))
PE(pos, 2i+1) = cos(pos / 10000^(2i/D))
这种编码能使得模型轻松学习到相对位置关系。

对于图像等二维数据,可以将x和y坐标分别进行编码后合并。位置编码是注入领域特定结构知识(如空间邻接性)的主要位置之一。


Transformer用于语言建模与因果注意力 🤖

Transformer是大型语言模型(如GPT)的核心。用于语言建模时,通常采用自回归方式:给定前面的词,预测下一个词。

为了在训练时防止模型“偷看”未来的答案,我们使用因果注意力掩码。这强制规定,在生成序列中位置 i 的令牌时,它只能关注位置 0i-1 的令牌。在注意力矩阵中,这体现为一个上三角矩阵掩码(未来位置权重被设为负无穷,经Softmax后变为0)。

带有因果掩码的Transformer能够以高效并行的方式训练自回归模型,并在推理时逐个生成令牌。


多模态应用:图像描述生成

Transformer的灵活性使其易于用于多模态任务。例如,在图像描述生成中:

  1. 图像编码器:一个视觉Transformer处理图像令牌,通过自注意力提取图像特征。
  2. 文本解码器:一个带有因果掩码的Transformer处理文本令牌。
  3. 交叉注意力:在文本解码器的每一层,其查询向量可以关注图像编码器输出的所有令牌(值向量)。这使得生成文本时能够动态地从图像中获取相关信息。

这种“编码器-解码器”加“交叉注意力”的结构是处理序列到序列任务(如翻译、描述生成)的强大范式。


总结

本节课中我们一起学习了Transformer架构,这是当前深度学习的基石。

  • 我们首先理解了CNN的局部性限制,从而引出了对全局但稀疏交互的需求。
  • 我们学习了Transformer的三个支柱:
    1. 令牌化:将数据转化为向量集合,作为通用处理单元。
    2. 注意力机制:特别是自注意力,它允许每个令牌根据内容相关性动态聚合全局信息。
    3. 位置编码:为排列等变的模型注入必要的顺序或位置信息。
  • 我们看到了如何将这些组件组合成视觉Transformer和用于语言的因果Transformer
  • 最后,我们了解了Transformer如何通过交叉注意力桥接不同模态,实现如图像描述生成等复杂任务。

Transformer的成功在于其简洁性通用性以及对硬件友好的矩阵运算。虽然未来可能会有新的架构出现,但掌握Transformer的原理对于理解和构建当前最先进的AI系统至关重要。

009:实用技巧与启发式方法

在本节课中,我们将学习一系列关于构建有效深度学习系统的实用建议、启发式方法和技巧。与侧重于理论和算法的课程不同,本节课将分享来自实践经验的见解,涵盖数据处理、模型构建、优化、评估框架以及高效利用计算资源等多个方面。

数据:与你的数据成为朋友

上一节我们介绍了课程概述,本节中我们来看看数据处理的第一原则:深入理解你的数据。

一个重要的故事来自我的博士后导师Alyosha Efros。当他还是一名研究生时,他向他的导师Jitendra Malik展示训练结果(如准确率或损失曲线)。Jitendra Malik告诉他,仅仅看这些汇总统计数据是不够的,必须观察数据本身。他提出了一个生动的建议:“与每一个像素成为朋友”。无论你在哪个领域,都应努力理解你的每一个数据点。

观察数据的重要性

以下是一些观察数据的理由:

  • 发现虚假相关性:在一项使用深度学习诊断乳腺癌的研究中,模型达到了99%的准确率。然而,分析发现模型并非根据组织特征进行判断,而是根据图像角落的字母“R”(可能代表特定医院)进行预测。由于数据存在偏差,模型学到了错误的捷径,这导致其无法推广到其他医院。
  • 理解模型行为:仅观察损失曲线可能无法揭示全部问题。例如,在训练生成猫图像的模型时,观察模型输出的样本图像能更直观地发现训练过程中的振荡行为。在训练蚂蚁进行追逐的强化学习任务中,奖励函数停滞不前,通过观察输出才发现是因为蚂蚁“摔倒”了,而非优化算法本身的问题。

数据检查清单

以下是检查数据时需要注意的要点:

  • 检查输入/输出分布:了解训练集中输入和目标的分布。例如,数据类别可能严重不平衡(如99%良性肿瘤,1%恶性肿瘤),这会导致模型简单地偏向多数类。
  • 可视化数据:随机选择小批量数据进行可视化,绘制直方图和边际统计图。
  • 了解数据范围:数据的数值范围是多少(例如0-1,0-255)?是否包含负值?这些信息对预处理至关重要。

数据加载与预处理中的常见陷阱

最常见的错误之一是数据预处理与模型期望不匹配。

  • 格式不匹配:例如,假设图像数据应在0到1之间,但实际加载的是0到255范围的uint8类型。如果代码将输入限制在0到1,所有数据将被压缩为0或1,导致信息丢失。
  • 通道顺序不一致:一些旧库(如Caffe)使用BGR而非RGB作为默认颜色通道顺序。如果使用在RGB数据上预训练的模型处理BGR格式的输入,会导致颜色理解错误。

核心建议:在代码中调用model.forward()之前插入数据检查点。这是确保数据格式、形状和维度符合模型期望的关键位置。

一个简单的数据检查函数示例如下:

def inspect_data(tensor, name="tensor"):
    print(f"{name}:")
    print(f"  dtype: {tensor.dtype}")
    print(f"  shape: {tensor.shape}")
    print(f"  requires_grad: {tensor.requires_grad}")
    print(f"  min: {tensor.min().item():.4f}")
    print(f"  max: {tensor.max().item():.4f}")
    print(f"  mean: {tensor.mean().item():.4f}")
    print(f"  std: {tensor.std().item():.4f}")

数据标准化

标准化是消除数据测量单位影响、使不同特征处于相似尺度的常用方法。

  • 标准化:对每个特征减去其均值,然后除以其标准差。公式为:x' = (x - μ) / σ。这使得数据具有零均值和单位方差。
  • 归一化:将数据缩放到固定范围,如[0, 1]。公式为:x' = (x - min) / (max - min)

标准化有助于防止某些特征因尺度巨大而主导梯度更新,使得网络能够平等地学习所有特征。然而,在某些领域知识表明不同特征具有不同物理意义时,需要谨慎处理。

维度:警惕低维度的复杂性

上一节我们讨论了数据本身,本节中我们来看看数据的维度特性。人类的直觉基于三维空间,但深度学习通常在高维空间中运作。

有趣的是,在高维空间中,许多事情反而变得更简单、更规律。例如,RMSNormLayerNorm在低维下的行为差异显著,但在高维下几乎相同。

  • RMSNorm:将向量除以其均方根,将数据点映射到单位超球面上。
  • LayerNorm:先减去向量均值,再除以其标准差。

在二维空间中,LayerNorm会损失自由度,导致输出坍缩到有限的点上,而非一个连续的流形。因此,当批量大小或层维度过小时,BatchNorm或LayerNorm可能无法正常工作或产生奇怪效果。

核心建议:尽量在高维度下工作。确保你的张量维度(如批量大小、特征维度)足够大(例如大于10),这样可以利用大数定律,使行为更稳定、更可预测。虽然增加维度会带来计算成本,但对于统计推断和预测任务而言,通常是值得的。

张量操作与调试技巧

深度学习代码中充斥着大量的张量重塑操作(reshape, permute, flatten, transpose等),这很容易引入错误。

  • 使用唯一维度进行调试:在测试网络架构时,为张量的每个维度使用不同的数值(例如,batch=4, height=32, width=32, channels=3)。这样,如果重塑操作顺序错误,会立即引发形状不匹配错误;如果所有维度相同(如都是64),则错误可能被掩盖。
  • 注意数据类型转换:将数据转换为uint8以节省内存时,需确保数据不包含负值,因为uint8无法表示负数。
  • 使用einops库简化重塑操作einops库提供了一种清晰、可读的方式来表述复杂的张量重塑操作,并通常能保证操作的可逆性。例如:
    from einops import rearrange
    # 将 BHWC 格式转换为 (B*W) H C 格式
    output = rearrange(input_tensor, 'b h w c -> h (b w) c')
    

数据增强与问题难度

上一节我们探讨了数据的维度和操作,本节中我们来看看如何通过数据增强来改进模型。

数据增强通过对输入应用不影响目标标签的变换(如图像的翻转、裁剪、亮度调整),从小数据生成更多数据。这是一种强大且架构无关的技术。

尽管“几何深度学习”旨在通过设计等变/不变的网络架构来内置这些不变性,但数据增强通常更简单、更直观,且不依赖于特定架构设计。

主动增加问题难度

一个关键思想是:不要让你的问题太容易。观察以下三条损失曲线:

  1. 损失迅速下降并很快平坦化。
  2. 损失在训练过程中上升。
  3. 损失平稳、持续地下降。

最理想的通常是第三条曲线。第一条曲线表明问题可能太简单,模型很快过拟合或学不到更多信息;第二条曲线显然有问题。你应该通过增加数据多样性或难度,使损失曲线更像第三条

领域随机化是数据增强的一种高级形式,尤其在机器人等领域常用。例如,在模拟中训练机器人抓取物体时,随机化光照、物体颜色、纹理甚至物理参数(如重力)。这虽然会使训练变得更慢、更困难(损失下降更缓),但能极大地提升模型在真实多变环境中的泛化能力

OpenAI曾用此方法训练机械手,他们发现,加入大量随机化后,模型需要模拟“十年”的经验才能达到与无随机化时“一年”经验相当的性能,但最终模型在现实世界中的鲁棒性远胜前者。

改变数据而非模型

在学术界,我们常专注于在固定数据集上设计新模型或算法。然而,在工业界和实际应用中,更大的杠杆往往在于改变数据

你可以收集更多数据、更高质量的数据、以新的方式标注数据或构建不同的数据集。改变数据通常比改变学习算法能带来更大的性能提升

输入上下文:更多信息,更易预测

上一节我们讨论了通过增强数据来提升泛化,本节中我们来看看如何设计输入信息。

一个基本规律是:条件信息越多,预测问题越简单。例如,在语言模型中,给定更长的上文,预测下一个词就越准确。

在构建预测模型(如根据药物化学式预测疗效)时,除了核心输入(化学式),应尽可能加入更多相关上下文信息(如患者年龄、病史、蛋白质结构、其他类似药物信息等)。输入的信息越丰富,输出Y的条件分布P(Y|X)就越接近确定性分布(即方差越小)

这使得即使使用简单的回归模型(如L2损失),也能很好地解决问题,因为任务从预测一个复杂分布简化为预测一个近乎确定的点。这正是当前文本到图像生成模型(如DALL-E、Stable Diffusion)成功的关键之一:它们不是直接建模整个图像空间的复杂分布,而是在给定详细文本描述的条件下,建模一个相对简单的图像分布

核心建议

  1. 数据规模大:使用大量数据。
  2. 输入信息量大:使用高维、信息丰富的输入,以确定输出。
  3. 输出信息量大:有时让输出也变得复杂(如生成整个图像)反而能构建更有效的学习信号(后续生成模型课程会详述)。

当然,也需注意平衡:如果关注的事件极其罕见(如特定类型的车祸),单纯增加数据规模可能效率低下,此时应更注重数据的多样性和对关键类别的覆盖

模型:保持简单

上一节我们强调了数据的重要性,本节中我们来看看模型构建的原则。

第一原则是:保持简单。简单的模型更容易构建、调试、理解和分享,也更容易进行理论分析。更重要的是,简单的模型往往更强大,符合奥卡姆剃刀原则,通常具有更好的泛化能力。

在研究中,如果方法A比方法B在基准测试上性能高1%,但复杂度显著增加,那么选择并发表更简单的方法B通常会产生更长远的影响力。不要为了微小的性能提升而引入不必要的复杂性。

实用模型选择指南

对于实际应用(非前沿研究),以下建议很有用:

  • 流行度优于性能:在GitHub上,项目的star数量通常是其实用性和可靠性的更好指标,而不是它在某个排行榜上的最高精度。选择那些流行、经过充分测试的模型。
  • 使用预训练模型:站在巨人的肩膀上。从Hugging Face等平台获取流行的预训练模型作为起点,这几乎总是比从头训练更好。
  • 将问题转化为已解决的问题:尽可能将你的新问题转化为一个已知的、有成熟解决方案的问题。例如,早期的图像着色任务被转化为分类问题
    1. 将连续的颜色空间离散化为有限的颜色类别。
    2. 对每个像素,预测其所属的颜色类别。
    3. 使用卷积神经网络(CNN)在全图进行滑动窗口式的像素级分类。
      这样做利用了成熟的分类框架(交叉熵损失),并且类别分布能够自然地表达预测的不确定性。

当前(2024年)的通用配方建议:将数据转化为one-hot向量,将目标转化为交叉熵分类任务,然后使用AdamW优化器Transformer架构来解决。

关于BatchNorm的警告

避免使用BatchNorm。BatchNorm引入了对批量大小的强烈依赖,导致训练和测试阶段行为不一致,并使分布式计算变得复杂。目前,LayerNorm是更推荐的标准选择。

扩展:简单而有效的策略

提升模型性能最直接的方法通常不是设计复杂的新结构,而是:

  1. 扩展数据
  2. 扩展模型(更宽、更深、更多通道/头数)。
  3. 扩展计算,训练更长时间。

这就是“规模即一切”的部分真理。虽然并非所有问题的唯一解,但规模是达到高性能的必要条件

追求简洁

当你通过不断增加组件使系统工作并达到高性能后,工作只完成了一半。接下来,应该逐步移除所有非必需的组件,直到系统刚好停止工作为止。真正的完美不是无法再增添,而是无法再删减。

优化与调试

上一节我们讨论了模型构建的原则,本节中我们来看看优化和调试过程中的实用技巧。

使用编码助手

在最终项目和实际工作中,应积极使用AI编码助手(如GitHub Copilot)。它们能提高效率,但关键在于:

  • 先思考,后询问:不要用AI替代思考。先理解问题,再让AI帮助实现。
  • 审查与测试:不要盲目信任生成的代码,务必理解、审查并进行测试。
  • 编写清晰的文档字符串:清晰的注释能极大提升AI生成代码的质量。

优化流程

  1. 从小开始:首先在单个数据点上过拟合你的模型,确保模型有能力学习。然后扩展到几个数据点,最后再进行大规模训练。
  2. 理解损失值:熟悉常见损失值的含义。例如,对于二分类问题,交叉熵损失为-0.69对应于50%的准确率(即随机猜测)。对于10分类问题,损失-2.3对应于10%的准确率(随机水平)。回归损失(如L2)的最小值是0,出现负值肯定是错误。
  3. 调整学习率与批量大小:始终仔细调整学习率,并尝试不同的批量大小,它们对训练动态有显著影响。
  4. 使用指数移动平均:对梯度、权重、激活值等使用EMA可以平滑训练过程,在时间维度上平均噪声,有时能替代在批量维度上的平均,带来稳定效果。

总结:深度学习的“烹饪术”

本节课中我们一起学习了构建深度学习系统的众多实用技巧和启发式方法。

我们首先强调了深入理解数据的重要性,要“与每一个像素成为朋友”,避免虚假相关性。接着,我们探讨了数据预处理、标准化以及在高维空间工作的优势。我们学习了通过数据增强领域随机化主动增加问题难度以提升泛化,并指出在实践中改变数据往往比改变模型更有效。我们还讨论了如何通过提供丰富的输入上下文来简化预测任务。

在模型方面,我们主张保持简单,优先选择流行和预训练的模型,并将新问题转化为已解决的问题。我们警告了BatchNorm的陷阱,并肯定了扩展数据、模型和计算的基础价值。最后,我们介绍了一些优化调试技巧,并鼓励使用AI编码助手。

深度学习就像烹饪,没有单一的“终极香料”。注意力机制、ReLU、归一化层、正则化器、跳跃连接……每一种“配料”都有其特定作用。成功的关键在于找到适合你具体问题的正确组合。不要过度依赖某一种技巧,而应理解每种组件的作用,并不断实验和简化你的系统。

希望这些来自实践的经验能帮助你在深度学习之旅中更有效地构建、调试和提升你的模型。

010:记忆与序列建模 🧠

在本节课中,我们将学习深度学习架构中处理记忆和序列建模的模型。这是关于机器学习架构的最后一个专题。之前我们讨论了CNN和Transformer,今天我们将引入一个以不同方式处理记忆和序列的新模型类别。这将是第一个“图灵完备”的架构,也是我们将介绍的最通用的框架。

我们将首先探讨为何需要建模序列(如时间或顺序),然后讨论如何使用CNN处理序列。接着,我们将介绍循环神经网络(RNN)的概念,并深入探讨RNN的一个特定扩展子集——长短期记忆网络(LSTM),它旨在解决RNN常见的“遗忘”问题。最后,我们将引入序列建模和长时记忆的概念,并讨论自回归等思想。


为何需要序列建模?🎬

计算机视觉可以从单帧图像中理解许多信息。例如,仅看一帧,我们可以识别出“孩子们在玩耍”,可能是在幼儿园教室里。现有的模型可以理解单帧中的物体,如电视、人、椅子,并进行问答(例如,“椅子是什么颜色?”答案是红色)。

然而,我们之前讨论的模型都没有真正尝试去理解事物之间的关系或预测未来。例如,“女孩接下来会做什么?”仅从当前帧我们无法确定。但如果我们观看整个视频,到达某个时间点后,我们就能大致猜测接下来会发生什么(例如,因为男孩移开了椅子,女孩可能会摔倒)。这涉及到对场景、物体、意图等复杂而细致的理解。虽然我们可以争论这些建模序列的机器学习模型是否真的理解了人类意图,但它们确实开始尝试捕捉“接下来会发生什么”的感觉。

序列可以是多种形式:

  • 我们之前介绍过,可以为图像添加时间维度,从而得到视频。
  • 语言序列,例如“在城市广场的晚间漫步”是一系列单词。
  • 音频信号序列,例如说出这句话的声音也是一系列数据。

使用CNN处理序列 🧩

回顾我们熟悉的卷积神经网络(CNN)架构,我们可以将其扩展到时空维度,形成一个类似“时空立方体”的结构。实际上,这是一个四维张量(宽度、高度、通道、时间),时间成为了张量中的另一个空间维度。

思考如何在这个立方体中捕捉时间结构,有几种有趣的方式:

  • 水平切片:在时间维度上取一个单一的水平像素切片。这看起来像是一些条纹,可能代表了以不同轨迹或速度行走的人。
  • 垂直切片:在时间维度上取一个垂直切片。这显示了在画面中某个水平或垂直位置上,谁先经过。例如,在奥运会中,通过分析终点线的像素变化,可以确定谁最先冲过终点线。

如果我们从更简单的1D标量视角来看待时间卷积:

  • 我们学习一个在固定时间窗口上的权重矩阵(例如,覆盖序列中的三个元素)。
  • 这可以看作是一个序列到序列模型。我们将这个卷积核在时间上滑动,可以处理任意长度的输入序列并生成任意长度的输出序列。只要输入序列持续,理论上这个过程可以无限进行。

然而,随着时间序列变得任意长,无论是经验上还是直觉上,序列开头发生的事情都很难被记住。原因在于卷积方法的局限性:它只能看到固定时间窗口内的输入。例如,在视频开头看到一只猫(Frank),但在未来的某个时间窗口,模型只能看到当前窗口的信息(可能是户外的橙色物体),而无法回忆起开头的信息,因此可能错误地预测为“老虎”。

为了捕捉过去的上下文信息,记住之前看到的内容,我们引入了循环神经网络(RNN) 的概念。与Transformer或CNN的固定时间窗口不同,RNN通过引入隐藏单元的概念,使得记忆理论上可以无限延续。


循环神经网络(RNN) 🔄

RNN的核心思想是引入一个隐藏状态,该状态不仅用于预测当前输出,还会被传递到下一个时间步,作为“记忆”的一部分。

更详细地看,我们可以将时间视为离散序列(尽管也可以应用于连续序列)。下一个隐藏状态 h_t 由某个函数 F 决定,该函数依赖于前一个隐藏状态 h_{t-1} 和当前输入 x_t。这个函数 F 在所有时间步共享。同样,输出 y_t 由另一个函数 G 从当前隐藏状态 h_t 映射得到,该函数也共享于所有时间步。

公式表示如下:

  • 隐藏状态更新:h_t = F(h_{t-1}, x_t; θ)
  • 输出计算:y_t = G(h_t; φ)

这是第一次我们看到的计算图不是有向无环图(DAG),而是包含循环的有向循环图。为了便于理解,我们通常将计算在时间上“展开”,这样在每个时间点,它仍然是一个前馈网络,只是参数在时间上共享。

假设这些函数都是我们熟悉的线性层加非线性激活函数,那么最简单的RNN形式可以写作:

  • h_t = σ(W * h_{t-1} + U * x_t + b_h)
  • y_t = σ(V * h_t + b_y)

其中,W, U, V 是可学习的权重矩阵,b_h, b_y 是偏置项,σ 是非线性激活函数(如tanh或ReLU)。如果去掉 W * h_{t-1} 项,这个结构就类似于一个多层感知机(MLP)。RNN的关键区别在于通过 W 矩阵将历史信息循环传递下去。隐藏状态旨在捕捉最相关的历史信息,而 W 则学习如何决定需要保留什么。

RNN可以扩展为深度结构,包含多个隐藏层,并且隐藏状态可以是任意维度的张量。


RNN的反向传播与长程依赖问题 ⚙️

反向传播:由于RNN包含循环,标准的反向传播算法(为前馈网络设计)无法直接应用。实用的方法是通过时间截断反向传播。在训练时,我们选择一个固定的时间窗口长度 T,将RNN在这个窗口内展开,使其变成一个深度为 T 的前馈网络。然后在这个展开的图上进行标准的反向传播计算梯度。由于参数 W, U, V 在时间步之间共享,计算梯度时需要将所有时间步上该参数对应的梯度求和。现代深度学习框架(如PyTorch)的自动微分功能可以自动处理这种共享参数的梯度求和。

长程依赖问题:理论上,RNN可以通过隐藏状态捕捉任意远的历史信息。但实际上,随着时间窗口变长,信息很难有效传递。从数学上看,如果我们忽略非线性激活和偏置,连续应用隐藏状态更新公式,会发现 h_t 中包含了 W^t 项(Wt 次幂)。这导致了两个问题:

  1. 梯度消失:如果 W 的特征值小于1,W^t 会随着 t 增大而指数级趋近于0,导致早期时间步的梯度几乎为零,模型无法学习到长程依赖。
  2. 梯度爆炸:如果 W 的特征值大于1,W^t 会指数级增长,导致梯度变得极大,训练不稳定。

梯度爆炸可以通过梯度裁剪等技术缓解,但梯度消失问题更棘手,它直接导致模型“遗忘”久远的信息。这就是促使长短期记忆网络(LSTM) 发展的原因。


长短期记忆网络(LSTM) 🚪

LSTM被设计成默认行为是不遗忘,然后学习需要忘记什么。它引入了一个新的状态——细胞状态 C_t,可以看作是一个“传送带”或“磁带”,信息在上面相对稳定地流动。LSTM通过三个“门”结构来精细控制细胞状态:

  1. 遗忘门 f_t:决定从细胞状态中丢弃哪些信息。它查看 h_{t-1}x_t,并通过sigmoid函数输出一个0到1之间的向量,作用于 C_{t-1}。1表示“完全保留”,0表示“完全遗忘”。
    • f_t = σ(W_f · [h_{t-1}, x_t] + b_f)
  2. 输入门 i_t候选细胞状态 \tilde{C}_t:决定将哪些新信息存入细胞状态。输入门 i_t 决定更新哪些值,候选状态 \tilde{C}_t 表示可能的新值。
    • i_t = σ(W_i · [h_{t-1}, x_t] + b_i)
    • \tilde{C}_t = tanh(W_C · [h_{t-1}, x_t] + b_C)
  3. 更新细胞状态:结合遗忘门和输入门的信息来更新细胞状态。
    • C_t = f_t ⊙ C_{t-1} + i_t ⊙ \tilde{C}_t
      表示逐元素相乘)
  4. 输出门 o_t:基于更新后的细胞状态,决定输出什么到隐藏状态 h_t
    • o_t = σ(W_o · [h_{t-1}, x_t] + b_o)
    • h_t = o_t ⊙ tanh(C_t)

LSTM的关键在于其加性更新门控机制。细胞状态的更新是加性的(C_t = ... + ...),而非RNN中的乘性递归(h_t = W * h_{t-1} + ...),这极大地缓解了梯度消失问题。门控结构(使用sigmoid)允许网络学习何时读写和遗忘信息,提供了对记忆的显式控制。其默认的“恒等映射”倾向(当遗忘门接近1,输入门接近0时)使得信息更容易长期保存。


序列建模与自回归模型 📝

我们讨论了多种序列建模方法。现在引入一个重要的概念:自回归模型。其思想很简单:给定序列的开头,预测下一个元素;然后将预测出的元素加入输入,再预测下一个,如此循环。

训练:在训练时,我们使用大量序列数据(如句子)。对于每个序列,我们取前 t 个词作为输入,让模型预测第 t+1 个词,并使用交叉熵损失进行优化。为了避免错误累积导致训练不稳定,常用教师强制技术:即在每个时间步,无论模型上一时间步预测是什么,都将真实的前一个词作为当前输入。

推理/采样:在生成新序列时,我们从起始标记开始,让模型预测下一个词的概率分布。然后我们可以:

  • 贪婪采样:选择概率最高的词作为下一个词。
  • 随机采样:根据概率分布随机采样下一个词。
  • 束搜索:保留概率最高的 K 个候选序列,在每一步都扩展这 K 个序列,最终选择整体概率最高的序列。这能在生成质量和多样性间取得更好平衡。

自回归建模可以形式化为学习序列的联合概率分布,并将其分解为一系列条件概率的乘积:
P(单词1, 单词2, ..., 单词n) = P(单词1) * P(单词2|单词1) * ... * P(单词n|单词1,...,单词n-1)

每个条件概率 P(下一个词|历史词) 可以看作是一个多类分类问题(例如,在词汇表上使用softmax)。由于词汇表可能很大(数万),直接分类可能困难。实践中常使用子词单元(如Byte Pair Encoding, BPE)来降低分类难度,同时保持语义粒度。


长时记忆的替代方案与模型对比 🏗️

除了RNN/LSTM的递归传递方式,还有其他方法可以建立长时依赖:

  1. 时间卷积:使用固定大小的卷积核在时间上滑动。它能直接链接过去某个窗口内的信息,但无法看到窗口之外更早的历史。
  2. 注意力机制(如Transformer):通过计算序列中所有元素对之间的相关性(注意力权重),动态地决定每个位置应该关注历史上哪些部分。权重是输入数据的函数,非常灵活。
  3. 记忆网络:显式地维护一个外部记忆库,模型可以从中读取或写入信息。

下表对比了不同方法在序列长度 n 和表示维度 d 下的计算复杂度:

方法 每层复杂度 最大路径长度
自注意力 O(n^2 * d) O(1)
循环网络 O(n * d^2) O(n)
卷积网络 O(k * n * d^2) O(log_k(n))
受限自注意力 O(r * n * d) O(1)
  • 自注意力:计算所有元素对之间的关系,复杂度为 O(n^2),是序列长度的平方,但任意两个位置间的信息流动路径很短。
  • 循环网络:每步计算复杂度与 d^2 相关,但信息从序列开头流到末尾需要 n 步。
  • 卷积网络:使用大小为 k 的卷积核,信息需要经过 O(log_k(n)) 层才能传递整个序列。

自注意力的 O(n^2) 复杂度对于长序列是巨大的计算和内存负担。因此,研究者提出了许多高效注意力方法,如:

  • 稀疏注意力/局部注意力:只计算每个位置与附近位置或某些全局位置的注意力(如Longformer)。
  • 低秩近似:使用核函数等方法近似注意力矩阵(如Performer)。
  • 哈希注意力:使用局部敏感哈希将键分组,减少计算量(如Reformer)。

另一种思路是检索增强生成:将庞大的知识存储在外部数据库(记忆库)中,让一个较小的语言模型在需要时去检索相关信息。这可以提高事实准确性并减少“幻觉”。


我们真的需要多长的上下文?🤔

随着技术进步,Transformer的上下文窗口不断增大,从BERT的512个标记,到GPT-3的2048,再到一些模型能处理数万甚至近十万个标记(相当于几本书)。如果计算资源允许,似乎我们可以拥有任意长的上下文。

但一个关键问题是:对于给定的任务,我们真的需要这么长的上下文吗? 一项关于视频动作识别的研究提出了“最小认证集”的概念,即准确识别一个动作所需的最少视频片段长度。研究发现,许多现有基准测试中的动作只需要几秒的上下文。如果基准测试不包含需要长时上下文的任务,那么改进长时建模能力就无法在现有指标上体现进步。因此,社区需要开发需要长程依赖的新基准,以推动该方向的研究。

最后,从更宏观的视角看,我们可以将神经网络中的参数视为从训练数据中学到的“慢记忆”,而将前向传播时产生的激活值视为对当前输入数据的“快记忆”。还有一些有趣的工作,如超网络(输出另一个网络的权重)或学习码本,模糊了参数和激活、快记忆和慢记忆的界限,为我们思考模型的记忆方式提供了新视角。


总结 📚

本节课我们一起学习了深度学习中处理记忆和序列建模的核心架构。

  • 我们首先探讨了序列建模的动机,以及如何使用CNN处理时空数据。
  • 然后,我们深入介绍了循环神经网络(RNN) 的基本原理、计算方式及其在反向传播和长程依赖上面临的挑战。
  • 为了克服RNN的梯度消失问题,我们学习了长短期记忆网络(LSTM),它通过细胞状态和门控机制,实现了对记忆更精细的控制。
  • 接着,我们讨论了自回归模型,这是许多现代生成模型的基础,并了解了其训练和采样策略。
  • 最后,我们对比了递归、卷积和注意力等不同的序列建模方法,探讨了高效注意力技术和长上下文窗口的实用性,并思考了模型记忆中“快”与“慢”的哲学。

通过本课,你应该对深度学习如何建模时间和序列有了系统的理解,并能够认识到不同方法之间的权衡与适用场景。

011:基于重建

概述

在本节课中,我们将要学习表示学习的基本概念,特别是基于重建的方法。我们将探讨深度网络如何学习数据的有效表示,并介绍自编码器、聚类以及自监督学习等核心方法。

表示学习简介

深度网络通过逐层处理数据,将原始输入X转换为更抽象、更有效的表示,我们称之为嵌入或表示。前向传播过程称为编码,它将数据映射到更简单的表示空间。反向传播则与生成建模相关,从抽象表示生成数据。这两种方向可以大致视为互逆过程。

为什么学习表示?

学习表示的一个主要原因是加速未来的学习。一个好的表示能使后续的学习任务变得更容易。在实践中,我们通常不会从零开始训练网络,而是使用在大规模数据上预训练好的表示作为起点,然后针对特定任务进行微调。这种预训练-微调的范式是当前解决实际问题的主流方法。

什么是好的表示?

一个好的表示应具备多种属性。以下是几个关键特性:

  • 紧凑性:表示应具有较低的维度或信息量,便于存储,并可能带来更好的泛化能力。
  • 充分性:表示应包含足够的信息来解决我们关心的任务。
  • 可解释性:表示应易于人类理解和解释。
  • 解耦性:数据中不同的变化因素应在表示的不同维度上体现。
  • 鲁棒性:表示应对输入数据的小扰动保持稳定。

通过压缩学习表示:自编码器

一种学习表示的核心思想是压缩。我们希望找到一个数据的紧凑表示,同时又能从该表示中重建出原始数据。这种方法的核心模型是自编码器

自编码器由两部分组成:一个编码器 F 将高维输入 X 映射到低维嵌入 Z,一个解码器 G 试图从 Z 重建出 X。其目标是最小化重建误差:

L = E[||X - G(F(X))||^2]

自编码器的关键在于对假设空间施加约束(例如,嵌入维度 M 小于输入维度 N),迫使网络学习数据的关键特征。

自编码器与经典方法

自编码器可以看作是经典降维方法的非线性扩展。

  • 线性自编码器:当编码器和解码器都是线性变换时,其最优解与主成分分析(PCA) 密切相关,学习到的嵌入张成了与PCA相同的主子空间。
  • 聚类作为表示:聚类可以看作是一种特殊的表示学习,它将每个数据点映射到一个整数(簇标签),这等价于一个独热编码向量K均值算法可以重新表述为一个使用整数瓶颈和L2重建误差的自编码器。其深度学习版本称为矢量量化自编码器

通过预测学习表示:自监督学习

另一种学习表示的原则是预测。我们不是直接压缩数据,而是尝试预测被遮挡或缺失的部分数据。这种方法称为自监督学习

在自监督学习中,我们构造一个“前置任务”,例如:

  • 给定一张黑白图像,预测其颜色(着色任务)。
  • 给定一段文本的部分词汇,预测被遮挡的词汇(掩码语言建模)。
  • 给定视频的前几帧,预测下一帧。

模型在解决这些前置任务的过程中,被迫学习对数据语义内容有意义的表示。例如,一个为着色任务训练的深度网络,其深层神经元会对物体类别(如“草莓”、“天空”)敏感,因为物体类别是预测颜色的强线索。

掩码自编码器

掩码自编码器是自监督学习的一种流行架构。它随机掩码输入数据(如图像块或文本词元)的一部分,然后训练模型基于剩余部分预测被掩码的内容。这种方法在图像(MAE)和文本(BERT)领域都取得了巨大成功。

压缩与预测的对比

一个有趣的现象是,基于预测的方法(如掩码预测)通常比基于压缩的方法(如标准自编码器)学习到更具语义意义、更利于下游任务的表示。具体原因仍在探索中,可能的假设包括:

  1. 通过维度控制压缩比较困难且不稳定。
  2. 自编码器容易学习“捷径解”,直接复制局部信息而非捕获全局语义。
  3. 预测任务更接近我们实际关心的智能任务(如推理、规划)。

总结

本节课我们一起学习了表示学习的基本概念。我们了解到,深度网络通过逐层变换学习数据的有效表示。我们探讨了两种学习表示的核心范式:基于压缩(如自编码器、PCA、K均值)和基于预测(如自监督学习、掩码自编码器)。表示学习是智能系统的基石,它使我们能够通过在大规模无标签数据上进行预训练,获得强大的、可迁移的表示,从而高效地解决各种下游任务。

012:基于相似性

概述

在本节课中,我们将继续探讨表示学习,并重点介绍基于相似性的表示学习。我们将了解学习良好表示的原因,探讨良好表示应具备的特性,并深入讲解度量学习对比表示学习的核心概念与方法。


为什么学习表示?

学习数据的表示有多个重要原因。

以下是几个关键动机:

  • 任务通用性:一个好的数据表示可以广泛应用于多种下游任务。
  • 高效性:数据的表示形式通常比原始数据本身更紧凑、更高效,便于处理。
  • 提升泛化能力:捕获数据语义或重要成分的表示有助于模型更好地泛化到新数据。
  • 促进迁移学习:预训练的表示可以更容易地进行微调,从而在新的任务上使用更少的训练数据。
  • 利用几何相似性:可以用于识别(例如,判断是否见过某人)或检索(例如,查找与给定查询相似的物品)。
  • 改进聚类:利用关于数据对相似或不相似的辅助信息,可以改善聚类效果。
  • 降维:以更紧凑的方式存储信息,通常可以通过无监督的方式实现。

什么是好的表示?

上一节我们讨论了学习表示的原因,本节中我们来看看一个好的表示应该具备哪些特性。

一个直观的想法是:语义相似的对象在表示空间中应该彼此接近。这有助于模型对噪声和扰动保持鲁棒性,并且使得表示空间更易于线性分离,从而简化后续的分类任务。

2016年,Goodfellow等人提出:一个好的表示应该使后续的学习任务变得更容易

具体而言,一个好的表示应具备以下特性:

  • 紧凑性:表示不应过于庞大,应保持信息的高效性。
  • 解释性:表示应能充分捕获原始数据中重要的维度。这通常与下游任务相关。
  • 集中性:来自同一类别的数据点在表示空间中应彼此接近。
  • 分离性:来自不同类别的数据点在表示空间中应彼此远离。
  • 鲁棒性:对不相关的扰动(如光照、姿态变化)应保持稳定,不会轻易跨越类别边界。

研究表明,在CIFAR-10数据集上,使用正确标签训练的模型学到的表示空间具有清晰的聚类和分离性。而使用随机标签训练的模型,虽然也能形成聚类(通过记忆),但其聚类更松散、更不易分离,泛化能力更差。


如何学习好的表示:度量学习

既然我们知道了好表示的特性,那么如何引导模型在学习过程中实现这些特性呢?

一个核心思想是:通过数据之间的相似性反馈来显式地鼓励模型学习好的表示。这意味着我们不是孤立地看待每个样本,而是同时考虑两个或多个样本,并告诉模型哪些应该相似,哪些应该不同。这种对比学习的方式,类似于人类通过比较来学习概念(例如,通过对比犀牛来描述大象)。

实现这一思想的最初方法是度量学习。其核心在于,原始输入空间(如图像像素空间)中的欧氏距离可能并不理想。我们的目标是学习一个度量(或表示),使得属于一类的数据点彼此接近,而不同类的数据点彼此远离。监督信号就是关于数据对相似与否的信息。

线性度量学习

假设我们有一组数据点 X1...Xn,以及关于哪些数据对属于同一类别(相似对)、哪些属于不同类别(不相似对)的监督信息。我们的目标是学习一个线性变换 Z = WX,使得在表示空间 Z 中的欧氏距离能满足我们的相似性要求。

表示空间中两点 ZiZj 的距离可以表示为:
d(Zi, Zj) = sqrt((Xi - Xj)^T * W^T * W * (Xi - Xj))
这定义了一个由正半定矩阵 A = W^T * W 决定的马氏距离

学习最优的 W(或 A)可以转化为一个优化问题:在约束不相似对距离至少为某个边界值(例如1)的条件下,最小化相似对之间的距离。也可以反过来,在约束相似对距离不超过某个值的条件下,最大化不相似对之间的距离。

深度度量学习

线性度量学习局限于线性变换。深度度量学习则使用神经网络学习一个非线性变换 Z = f(X),其中 f 是一个神经网络。此时,我们通过随机梯度下降优化神经网络的权重,而不是直接优化正半定矩阵。

另一个重要改进是表示归一化。通过将表示约束在一个超球面上,距离度量就等价于角度度量,我们可以使用内积来衡量相似性,从而避免了尺度变化带来的复杂性。


对比表示学习

上一节介绍了度量学习的基本框架,本节我们将深入探讨一种特别重要的实现方式:对比表示学习

对比学习的核心直觉是:判断两个样本是否属于同一类别可能很难,但判断哪两个样本更相似则相对容易。例如,给定三张飞蛾图片,我们可能难以准确说出哪些属于同一物种,但很容易判断哪两张看起来更相似。

三元组损失

一种经典的对比损失是三元组损失。它需要一个锚点样本、一个正样本(与锚点同类)和一个负样本(与锚点不同类)。损失函数要求:锚点与负样本之间的距离,必须大于锚点与正样本之间的距离加上一个边界值 m

公式表示为:
L = max(0, d(anchor, positive) - d(anchor, negative) + m)

在实际训练中,我们通常使用三元组网络:三个样本共享权重的编码器网络,分别计算嵌入,然后根据三元组损失进行反向传播。为了提高训练效率,可以在一个批次内构建所有可能的正负样本三元组,并重点关注那些模型判断错误的“困难负样本”,以获取更强的训练信号。

通过这种方式学习到的嵌入空间会形成清晰的聚类,相似的项目彼此靠近。然而,模型学到的相似性可能包含我们并不关心的维度(例如背景、姿态),这取决于训练数据和任务目标。


自监督对比学习

前面的方法假设我们拥有关于数据对相似性的监督信息。但在许多情况下,我们并没有这样的标签。自监督对比学习旨在利用度量学习的思想,但不依赖显式标签来构建表示空间。

一个常见的设置是:我们有一个编码器网络,将数据映射到超球面上。然后,我们使用基于相似性的交叉熵损失(例如InfoNCE损失)来区分样本。其思想是最大化正样本对之间的内积(即最小化夹角),同时最小化负样本对之间的内积。

关键问题在于:在没有标签的情况下,如何定义“正样本对”和“负样本对”?

SimCLR方法

SimCLR是一个经典的自监督对比学习方法。对于批次中的每个数据点,它生成两个不同的随机增强版本(例如裁剪、颜色抖动、旋转等),这两个增强视图被视为正样本对。而批次中所有其他数据点(及其增强视图)则被视为该正样本对的负样本。

这种方法的核心假设是:同一个数据点经过合理增强后的不同视图,应该在表示空间中非常接近。通过这种方式,模型被迫学习对定义好的增强变换保持不变的表示,从而捕获更本质的特征。

方法的关键要素与局限

自监督对比学习的成功依赖于两个关键要素:对比损失函数数据(正负样本对)的选择

对比损失函数(如InfoNCE)被证明可以同时鼓励表示的集中性(正样本靠近)和均匀性(负样本在超球面上均匀分散)。均匀性有助于最大化不同类别间的分离度。

数据方面,以下因素至关重要:

  1. 强数据增强:使用多样且合理的增强组合对性能提升巨大。
  2. 投影头:在编码器后添加一个简单的投影网络(如MLP)来计算对比损失,而下游任务使用编码器本身的输出。这有助于编码器保留更多信息。
  3. 大批次大小:提供更多的负样本,能显著提高性能。
  4. 困难的负样本挖掘:找到那些被模型错误放置的负样本,能加速学习。

然而,这种方法也有局限:

  • 增强策略的依赖性:选择的增强方式隐式定义了模型应该对哪些变化保持不变。这对于图像可能直观,但对于其他模态数据(如分子、文本)则需要领域知识。
  • “假阴性”问题:在自监督设置中,如果批次中包含同一类别的不同实例,它们会被视为负样本,这可能会损害学习到语义相似性的能力。在有部分标签的情况下,可以使用监督对比学习来缓解此问题。

案例研究:iNaturalist数据集

为了具体理解这些概念,我们来看一个在iNaturalist 2021数据集上的案例。该数据集包含约1万个物种的270万张图像,具有清晰的分类学树状结构(从粗粒度的“界”到细粒度的“种”)。

实验比较了监督学习SimCLRMoCo(另一种自监督方法)学到的表示。使用线性分类器在物种级别(最细粒度)进行训练,然后在不同分类学级别上评估准确率。

结果发现:

  • 在ImageNet这类粗粒度数据集上,自监督与监督学习的性能差距较小(约7%)。
  • 但在iNaturalist的细粒度物种识别任务上,差距很大(约30%)。
  • 通过最近邻检索发现,监督模型学到的表示更能根据物种聚集样本,而SimCLR学到的表示则更倾向于根据背景、上下文(如“被人手握住”)等特征进行聚集。

这个案例表明,自监督对比学习的效果高度依赖于其定义“相似性”的方式(即数据增强策略)是否与下游任务的目标对齐。如果增强策略引入的“不变性”与任务无关,那么学到的表示可能对目标任务不是最优的。


总结

本节课中,我们一起学习了基于相似性的表示学习。

我们首先探讨了学习良好表示的原因及其理想特性:集中性分离性紧凑性解释性鲁棒性

接着,我们介绍了度量学习的基本框架,它旨在学习一个使相似样本靠近、不相似样本远离的度量空间。

然后,我们深入讲解了对比表示学习,特别是三元组损失自监督对比学习(如SimCLR)。这些方法通过构造正负样本对,并利用对比损失显式地优化表示的几何结构。

最后,通过iNaturalist数据集的案例,我们认识到自监督对比学习的成功关键在于:对比损失函数促进了表示的集中与均匀分布,而数据增强策略则决定了模型学习到何种不变性,这必须与下游任务的目标相匹配

无论是监督还是自监督,基于相似性的学习为我们提供了将领域知识(关于什么应该相似、什么应该不同)注入模型的有效途径,从而学习到强大且可迁移的表示。

013:表示学习理论

在本节课中,我们将要学习表示学习的理论基础,特别是探讨神经网络架构如何隐式地定义了数据点之间的相似性。我们将介绍高斯过程这一经典机器学习方法,并揭示它与无限宽神经网络之间的深刻联系。


课程回顾与引入

上一节我们介绍了对比学习技术,它旨在学习数据的嵌入表示,使相似数据点映射到相近的嵌入,不同数据点映射到远离的嵌入。

在更早的课程中,我们从张量和谱性质的角度思考了神经网络内部发生的事情。本节我们将更抽象地看待神经网络,将其视为一系列向量空间之间的映射。深度学习的一个可能目标是学习数据点的良好表示,我们希望网络末端的表示是线性可分的。

本节要讨论的核心观点是:当你选择神经网络架构时,它就已经对哪些数据点相似、哪些不相似持有某种“观点”,即使在没有进行对比学习训练之前也是如此。我们将介绍一些工具来捕捉这个想法。


经典函数空间方法

在深入神经网络之前,我们先回顾几种经典的构建函数空间并进行机器学习的方法。

核方法 🧮

核方法是一种经典方法。其核心思想是选择一个核函数(可以想象为一个“凸起”函数,如高斯函数),并将其放置在每个输入数据点的位置上。

对于 N 个数据点,我们放置 N 个核函数,并通过标量系数重新调整它们的高度,以拟合数据。我们构造的函数形式为:
F(x) = Σ_i α_i * K(x, x_i)
其中 K 是核函数,α_i 是标量系数。

如果我们考虑将核函数放置在所有可能的位置上(即使用无限多个核),那么由此生成的函数空间被称为再生核希尔伯特空间。在深度学习兴起之前,核方法曾风靡一时。

高斯过程 🌀

第二种经典方法是考虑从某个随机过程中采样函数。我们可以从一个随机过程中不断采样函数,保留那些与数据拟合的,丢弃那些不拟合的。实际上,我们不会真的采样并丢弃,而是通过“条件化”函数分布在训练数据上,得到所谓的后验函数分布

为了使计算可行,通常假设生成函数的过程是高斯型的。这种方法被称为高斯过程。有趣的是,高斯过程的后验均值函数,恰好等价于在某个RKHS范数定义下的最小核范数插值器。

神经网络 🧠

第三种方法就是我们课程的核心——神经网络。

这三种方法之间存在深刻的对应关系:可以从高斯过程得到核方法,也可以从核方法得到高斯过程。而最有趣的是,存在一种方式可以从神经网络得到高斯过程,这基本上发生在网络无限宽的情况下。当我们随机初始化权重时,无限宽神经网络所生成的函数分布,等价于一个高斯过程。


深入理解高斯过程

为了理解上述对应关系,我们需要更正式地理解高斯过程。

直观理解

想象我们有一些数据点。高斯过程允许我们采样许多与数据一致(即穿过数据点)的随机函数。所有这些函数的均值通常会是一条更平滑的曲线。同时,我们可以计算在每个点处的标准差,它通常在数据点处为0,并随着远离数据点而增长。

更简单地说,我们可以将高斯过程理解为无限维高斯随机向量的推广。考虑一个有限维的高斯随机向量,将其每个坐标绘制在轴上并连接起来,就得到一条线。如果我们选择协方差矩阵,使得向量中相邻的坐标高度相关,那么连接起来的线就会比较平滑。让这个向量的维度趋于无穷,并将采样点无限密集地排列,我们就得到了一个高斯过程——一个连续的随机函数。

正式定义

X 为输入空间(例如实数轴)。对于空间中的每个点 xF(x) 是一个随机变量。如果对于任何有限个输入点 {x_1, ..., x_N},随机向量 [F(x_1), ..., F(x_N)] 的联合分布都是高斯的,那么我们就称 F 是一个高斯过程

高斯过程由两个函数描述:

  • 均值函数 m(x) = E[F(x)],通常设为0。
  • 协方差函数(核函数) k(x, x') = Cov[F(x), F(x')]

协方差函数编码了我们对“相似性”的理解。例如,一个常见的选择是平方指数核:k(x, x') = exp(-||x - x'||^2)。当 xx' 很接近时,F(x)F(x') 高度相关;当它们远离时,相关性降低。

如何进行预测

假设我们有训练数据 (x_i, y_i),其中 y_i = F(x_i)。我们想预测新点 x* 处的值 F(x*)

根据定义,所有已知点和待预测点的联合分布 [F(x_1), ..., F(x_N), F(x*)] 是一个多元高斯分布。利用多元高斯分布的条件分布公式,我们可以直接计算出 F(x*) 在给定训练数据下的条件分布——它本身也是一个高斯分布。这个条件分布的均值和方差就给出了我们的预测(均值)以及预测的不确定性(方差)。通过滑动 x*,我们就能得到之前图中那条平滑的均值曲线和置信区间。


神经网络与高斯过程的对应关系 🧬

现在,我们回到神经网络,并阐述那个关键的联系。

现象观察

考虑一个神经网络。如果我们随机初始化其权重,就会得到一个随机函数。重复初始化多次,我们就得到了一个随机函数的分布。

固定两个输入 xx'(例如两张图片),对于成千上万个随机初始化的网络,收集它们在 xx' 处的输出,并绘制散点图。实验发现:

  • xx' 相似时(例如同一张图片加轻微噪声),输出高度相关。
  • xx' 不相似时(例如同一张图片加大量噪声),输出的相关性较低。
    这个散点图看起来像是从一个二维高斯分布中采样得到的。

对应定理

神经网络高斯过程对应关系 指出:对于一个无限宽的神经网络,如果我们随机初始化其权重,那么对于任何有限集合的输入点,网络输出的联合分布都是一个多元高斯分布。这正是高斯过程的定义。

因此,一个无限宽的随机初始化神经网络,等价于一个高斯过程。这个对应的高斯过程的协方差函数 k(x, x') 具体形式取决于神经网络的架构(深度、非线性激活函数等)和权重初始化方案。

这意味着,神经网络架构的选择,本身就隐式地定义了一个衡量数据相似性的核函数

一个具体例子

对于一个 L 层的 MLP,使用 ReLU 激活函数,并以“Xavier初始化”(方差为 1/fan_in)方式随机初始化权重。可以证明,在无限宽极限下,网络输出所对应的高斯过程的协方差函数为:
k(x, x') = Σ_L( ... Σ_2( Σ_1( x^T x' ) ) ... )
其中 Σ 是一个与 ReLU 相关的函数(称为“组合反余弦核”),它被递归地应用了 L-1 次。这清楚地显示了深度如何影响最终的相似性度量。


讨论与思考

为何需要无限宽?

这个对应关系依赖于中心极限定理。在网络每一层,预激活值是许多随机权重的加权和。当隐藏层宽度趋于无穷时,根据中心极限定理,这些预激活值的分布趋于高斯分布。通过逐层归纳,最终整个网络的输出分布趋于高斯过程。

这对实践有何意义?

一个自然的问题是:既然存在这种对应,我们为什么不直接使用高斯过程或核方法,而要训练复杂的神经网络呢?有几个可能的原因:

  1. 计算 scalability:大规模核方法通常需要 O(N^3) 的计算复杂度(N 是数据点数量),而神经网络训练可以做到近似 O(N)
  2. 表示能力:虽然无限宽网络对应某个核,但有限宽网络在训练过程中的动态特性可能使其能够学习到更丰富、更适合任务的表示。
  3. 实用性:直接训练一个有限宽网络通常比先推导其极限核再进行分析更简单直接。

这个理论对应关系更多地是提供了一个理解神经网络的新视角,以及一个连接经典机器学习与现代深度学习的桥梁。它帮助我们理解,架构设计本质上是在先验地注入我们对问题结构的假设。


总结

本节课我们一起学习了表示学习的一个理论基础。

  • 我们回顾了核方法和高斯过程这两种经典的函数空间建模方法。
  • 我们重点介绍了高斯过程,它可被视为无限维高斯分布,并通过协方差函数定义数据点间的相似性。
  • 我们探讨了神经网络高斯过程对应关系:无限宽的随机初始化神经网络,其生成的函数分布是一个高斯过程。这意味着神经网络架构隐式地定义了一个先验的相似性核。
  • 我们通过一个MLP的例子看到了该对应的具体形式,并讨论了该理论的意义与局限性。

这个理论揭示了模型架构本身如何承载着我们对世界的假设,为我们理解和设计神经网络提供了另一个有价值的维度。

014:生成模型基础

在本节课中,我们将要学习生成模型的基础知识。生成模型,也被称为生成式人工智能,是近年来人工智能领域的重大进展。我们将了解什么是生成模型,它们与表示学习的关系,并初步探索几种流行的生成模型。

上一节我们介绍了表示学习,它关注如何从数据中提取低维嵌入。本节中我们来看看生成模型,它本质上是表示学习的逆过程:从简单的低维嵌入生成复杂的数据。

数学背景回顾

为了理解生成模型,我们需要一些概率论的基础知识。本节将回顾关键概念,为后续学习打下基础。

我们将改变对神经网络的看法。之前,我们主要将神经网络视为从输入数据到输出数据的确定性函数。对于生成模型,我们需要模型能够输出一个可能性分布,而不仅仅是一个确定的点。

例如,在图像分类中,我们通常将神经网络 F 视为一个函数,输入是图像张量,输出是类别标签(一个整数)。现在,我们将把神经网络视为一个映射,从输入空间 X 映射到输出 Y 上的一个概率分布 P(Y)

这种视角的转变是必要的,因为对于许多任务(如生成图像),存在多个合理的输出,我们需要模型能够捕捉这种不确定性。

我们将使用以下符号:

  • 大写字母(如 X)表示随机变量,由其分布 P(X) 表征。
  • 小写字母(如 x)表示随机变量的一个具体取值(实现),其概率(或概率密度)为 P(x)

对于离散随机变量,我们使用概率质量函数,它满足:

  • P(x) >= 0
  • ∑ P(x) = 1 (对所有可能的 x 求和)

对于连续随机变量,我们使用概率密度函数,它满足:

  • p(x) >= 0
  • ∫ p(x) dx = 1 (在整个定义域上积分)

什么是生成模型?

生成模型的核心目标是创建能够生成类似于训练数据的新数据的算法。这与我们之前学习的分类、决策等分析型AI任务方向相反。

生成模型的应用非常广泛:

  • 娱乐:生成图像、编写故事、创建聊天机器人。
  • 科学:设计蛋白质结构、生成药物分子。
  • 工程:进行图像到图像的转换(如将MRI图像转换为CT图像)。

生成模型通常通过显式或隐式地对数据分布进行建模来生成数据。

生成模型的直觉与构建

我们可以将生成模型视为分类器的逆过程。分类器接收数据(如图像)并输出抽象标签(如“鸟”)。生成模型则接收抽象描述(如“鸟”)并输出数据(如鸟的图像)。

关键挑战在于,对于同一个抽象描述,存在无数种合理的具体输出(不同颜色、角度、种类的鸟)。因此,生成模型需要输出一个分布,而不仅仅是一个点。

一种直观的方法是使用一个生成器(通常是一个神经网络)。它是一个确定性函数,但我们将其输入与一个随机变量 Z(可以看作是“骰子”或“控制旋钮”)相结合。通过改变 Z 的取值,我们可以得到不同的输出。Z 被称为潜变量,它决定了数据中所有未被输入命令明确指定的属性。

例如,一个简单的生成模型可以是模拟河流的程序:通过多次抛硬币(随机变量)来决定河流的每一次转弯是左还是右,从而生成一条随机的河流路径。

生成模型的两种主要方法

生成模型主要有两种构建思路:直接法和间接法。

直接法(隐式生成模型)

直接法学习一个函数,该函数直接从随机噪声(潜变量 Z)映射到数据样本 X。训练目标是使生成的数据看起来像训练数据。

  • 训练:学习一个参数化函数 G,使得 G(Z) 的分布与真实数据分布 P_data 相似。
  • 采样:从简单分布(如高斯分布)中采样 Z,然后计算 X = G(Z)

间接法

间接法不直接学习采样函数,而是学习一个对数据评分的函数。

  • 密度模型:学习一个概率密度函数 P_θ(X),该函数为每个可能的数据点 X 分配一个概率值(满足归一化条件 ∫ P_θ(X) dX = 1)。
  • 能量模型:学习一个能量函数 E_θ(X),它是一个未归一化的评分函数。通过公式 P_θ(X) = exp(-E_θ(X)) / Z 可以将其转化为概率密度,其中 Z 是归一化常数。

拥有评分函数后,我们可以通过优化(寻找高分数据点)或采样算法(如马尔可夫链蒙特卡洛)来生成数据。

最大似然学习

在密度模型中,一个核心的训练目标是最大似然估计。我们希望找到模型参数 θ,使得模型分布 P_θ 与真实数据分布 P_data 尽可能接近。通常使用KL散度来衡量两者的差异。

最小化 P_θP_data 之间的KL散度,等价于最大化模型对训练数据(来自 P_data 的样本)的似然。其目标函数为:

max_θ E_{x~P_data} [log P_θ(x)]

这被称为最大似然学习。在训练中,我们用训练数据的经验分布来近似期望。

需要注意过拟合:如果模型容量无限大,最大似然的最优解是“记忆”所有训练数据(即在每个训练数据点上放置一个概率质量)。这与分类任务中的过拟合类似。我们需要通过模型架构的归纳偏置、正则化或早停等方法来确保模型能够泛化到新的、未见过的数据。

基于能量的模型

基于能量的模型提供了另一种间接方法。能量函数 E_θ(x) 将数据映射到一个标量,能量越低表示数据越可能(概率越高)。概率定义为:

P_θ(x) = exp(-E_θ(x)) / Z, 其中 Z = ∫ exp(-E_θ(x)) dx

Z(配分函数)通常难以计算,但许多任务(如比较数据点的相对概率、使用MCMC采样)只需要能量差,而不需要 Z 本身。

训练基于能量的模型也采用最大似然原则。其参数梯度推导后包含两项:

  1. 正相:降低训练数据点的能量。
  2. 负相:提高当前模型分布中高概率样本(可通过采样获得)的能量。

这种训练算法称为对比散度。直观上,它不断降低真实数据处的能量,同时提高模型生成样本处的能量,直到两者达到平衡。

流行的深度生成模型简介

以上是生成模型的基础理论。现在,我们快速浏览三种当前流行的深度生成模型。

自回归模型

自回归模型通过将生成高维数据的问题分解为一系列简单的下一个元素预测问题来工作。它利用概率的链式法则:

P(x1, x2, ..., xn) = P(x1) * P(x2|x1) * ... * P(xn|x1, x2, ..., x_{n-1})

  • 在文本中:预测给定上文后的下一个词(如语言模型)。
  • 在图像中:按顺序(如光栅扫描顺序)预测给定已生成像素后的下一个像素的颜色。

每个条件概率 P(x_t | x_<t) 都被建模为一个分类分布(通过Softmax输出)。训练通过标准监督学习(交叉熵损失)进行,采样通过依次从每个条件分布中采样并反馈回模型进行。

自回归模型是一种密度模型,因为我们可以明确计算整个序列的联合概率。

扩散模型

扩散模型采用了一种巧妙的策略:将数据生成这个困难问题,转化为学习逆转一个简单的加噪过程。

  1. 前向过程(加噪):从真实数据 x0 开始,逐步添加高斯噪声,经过很多步后,数据 xT 变成了纯噪声(近似标准高斯分布)。这个过程是固定的、已知的。
  2. 反向过程(去噪):训练一个神经网络 f_θ 来学习从 x_t(第t步的噪声数据)预测 x_{t-1}(噪声更少的数据)。这被构造成一系列监督学习问题。
  3. 采样:从标准高斯分布中采样一个噪声 xT,然后反复应用学习到的去噪网络 f_θ,逐步去除噪声,最终得到一个新数据样本 x0

扩散模型将复杂的生成任务分解为许多简单的去噪步骤,是一种非常强大的直接生成模型

生成对抗网络

GAN采用了一种博弈论的训练方式,包含两个网络:

  • 生成器 G:接收随机噪声 z,生成数据 G(z)。目标是生成以假乱真的数据。
  • 判别器 D:接收一个数据样本,判断它是来自真实数据分布还是生成器。目标是准确区分真假。

两者进行对抗性训练:

  • D 试图最大化它区分真假数据的能力。
  • G 试图最小化 D 对其生成数据的判别准确率(即欺骗 D)。

理想情况下,当训练达到平衡时,生成器能够生成与真实数据分布无法区分的数据。GAN是一种直接生成模型

总结

本节课中我们一起学习了生成模型的基础知识。我们首先回顾了必要的概率论概念,并理解了生成模型的目标是学习数据的分布以生成新样本。我们探讨了生成模型的两种主要范式:直接学习采样函数的直接法,以及学习密度或能量函数再通过其采样的间接法。我们还介绍了最大似然学习的原则以及如何避免过拟合。最后,我们简要了解了三种重要的深度生成模型:自回归模型、扩散模型和生成对抗网络的基本思想。在接下来的课程中,我们将深入探讨变分自编码器、条件生成模型以及生成模型的具体应用。

015:表示学习与生成建模的结合 🧠

在本节课中,我们将深入探讨变分自编码器。我们将看到,生成建模和表示学习是紧密耦合的:如果你能解决生成建模问题,它应该能帮助你解决表示学习问题,反之亦然。变分自编码器正是将这两个想法结合在一起并同时解决它们的模型。与之前几节课不同,本节课不会浅显地覆盖多个主题,而是会深入讲解VAE。我们还会在最后讨论生成模型学习到的表示类型,以及“解耦”这一概念。

概述:两个方向的问题

在表示学习和生成建模的课程中,我多次展示过这张图,它代表了我对这两个问题的思考方式。表示学习是从原始数据到某种更优、结构化、抽象化表示的映射。生成建模则是从一个简单、抽象的表示到数据的映射。可以说,这两个方向在某种程度上是互逆的。

  • 表示学习:映射数据到抽象表示。
  • 生成建模:映射抽象表示(例如文本指令)到数据。

在本节课中,我们将看到一个明确同时建模这两个方向的模型。

生成模型作为概率分布的变换

我们今天要探讨的视角是:将生成模型视为从一个概率分布到另一个概率分布的变换。我们将有一个先验分布,这通常是我们上节课讨论的噪声,结构非常简单。然后,我们将这个先验分布映射到一个复杂的数据分布。

我们总是有我们的隐变量或噪声基 Z,它通常是高斯分布。数据分布 X 则更为复杂。我们需要找到一个映射,将分布在这个简单空间(如高斯分布)中的数据,通过神经网络的层进行几何上的推拉变换,直到它变成数据分布的形状。

  • 数据:变量 X
  • 隐变量:变量 Z
  • 编码器 F:从数据 X 到表示 Z 的映射。
  • 解码器/生成器 G:从表示 Z 到数据 X 的映射。

自编码器与生成建模

我们已经见过自编码器。自编码器将数据映射到某个低维表示 Z,然后将该向量解码回数据本身,目标是重建数据。解码器部分在功能上基本就是我们描述的生成模型所做的事情:从某个 Z 向量映射到数据 X

现在的问题是:我们能否使用这个解码器作为生成模型来采样新的随机照片?为了做到这一点,我们首先需要能够采样一个 Z 向量,然后将其解码成图像。

那么,这里的困难是什么?

问题在于,自编码器会学习到一个映射,将数据编码到一组嵌入中,但并不能保证这组嵌入会形成一个易于采样的简单形状分布。我们不知道如何采样新的、有效的 Z。它可能是一个充满“空洞”的奇怪分布。如果我们去到一个从未被编码器 F 观察过的点,解码器 G 将不知道如何解码它。

变分自编码器的核心思想

变分自编码器的全部目的就是解决这个问题:让自编码器的编码空间形成一个你可以从中采样的简单分布。本节课中我们将要发展的所有数学和算法,都只是为了实现这个目标。

以下是理解VAE的第一个层面:VAE只是一个自编码器,但有一个关键改变:编码器 F 不是映射到一个复杂形状,而是映射到一个高斯分布。这意味着我可以通过从高斯分布中采样,然后将高斯随机向量通过解码器来生成新图像。

为什么我们希望隐空间是高斯分布?目前的理解层面是:如果隐空间是高斯分布,你就可以从中采样,然后它就变成了一个生成模型。从更深层的表示学习角度看,映射到一个高斯分布也可能导致更好的表示,这一点我们稍后会讨论。

VAE既是优秀的表示学习器,也是优秀的生成模型。在涵盖了基础的表示学习和生成建模之后,我们现在学习VAE,正是因为它将两者结合了起来。它几乎涵盖了我们已经见过的所有模型,例如扩散模型可以被视为某种VAE,而自编码器则像是VAE的一种退化形式。因此,VAE在数学上是生成建模和表示学习的顶峰框架。

VAE的概率解释:无限高斯混合模型

现在,我们退一步,讨论高斯混合模型。变分自编码器实际上是在用无限高斯混合模型来拟合数据分布。

  • 单一高斯:用最大似然目标拟合数据,得到一个密度模型。
  • 高斯混合模型:为了建模非高斯形状的复杂分布,我可以使用多个高斯分布并将它们求和。

一个高斯混合模型就是多个高斯分量的加权和。每个分量都是一个高斯“斑点”,具有均值 μ 和协方差 Σ。在本次讲座中,为简单起见,我们通常假设各向同性的高斯分布(即协方差矩阵是单位矩阵的倍数),在图上画成圆形。

VAE是一个高斯混合模型,但它不是普通的混合模型,而是一个无限的高斯混合模型。那么,如何用有限数量的参数来参数化一个无限混合模型呢?

这里有一个非常巧妙的技巧:通过一个从某个基础的连续分布到混合分量的映射 G 来参数化这个无限混合,而 G 本身具有有限的参数集。在本课程中,G 将是一个神经网络。

具体工作原理如下:

  1. 我们有一个先验分布 P(Z),通常是一个单位高斯分布。
  2. 我们从该空间中采样一个随机向量 z
  3. 将其通过我们的生成器/解码器 G
  4. G 输出一组数,我们将其解释为无限混合模型中某一个分量的均值 μ 和方差 σ²

如果我们采样 Z 空间中的不同点,就会参数化不同的高斯分量。因为 Z 是连续的,所以我们可以得到无限多个可能的高斯混合分量。而 G 只是一个具有权重和偏置的神经网络,参数数量是有限的。

这个映射所隐含的密度 P(X) 不再是求和,而是对连续空间的积分。我们需要对所有混合分量(即所有可能的 Z)进行积分。这个积分就是我们的边际似然

从概率图模型的角度看:我们采样一个 Z,然后以 Z 为条件采样一个 X。我们只观测到 X,隐变量 Z 未被观测。我们的数据建模问题是拟合一个以 X 为变量的分布,该分布表示为从某些底层变量 Z 的映射。为了计算数据点 X 的似然,我们需要对隐变量 Z 进行积分。

VAE的目标函数和假设空间如下:

  • 目标:最大化我的模型赋予数据的似然(最大似然学习)。
  • 假设空间:无限高斯混合模型,通过一个从先验空间 Z 到数据空间 X 的映射 G(神经网络)来参数化。

这个模型给了我两样东西:

  1. 一个密度函数 P_θ(X)
  2. 一种从该密度中采样的简单方法:首先从单位高斯分布中采样一个随机 Z,然后通过解码器 G 解码它。

采样的具体过程是:给定 ZX 的分布是一个均值为 G_μ(z)、方差为 G_σ(z)² 的高斯分布。我们可以通过从一个单位高斯分布采样噪声 ε,然后进行变换 x = G_μ(z) + G_σ(z) * ε 来采样 X。这被称为重参数化技巧

现在的困难在于:为了最大化模型赋予数据的似然,我需要计算那个积分。这个积分是难以处理的,因为每次我想通过反向传播调整参数以增加数据概率时,都必须计算这个积分。而为了很好地近似这个积分,我可能需要对 Z 进行大量采样,每次采样都需要运行神经网络 G 前向传播一次,这在计算上非常昂贵。

技巧一:用蒙特卡洛采样近似积分

第一个技巧是将积分写成期望的形式。边际似然 P_θ(X) 等于 P_θ(X|Z) 在从先验 P(Z) 中采样的 Z 上的期望。

我们可以通过简单地取有限个样本并求平均来近似这个期望,这称为蒙特卡洛估计。随着样本数 M 趋于无穷,这个估计会变得无偏。

具体操作是:从我的先验(单位高斯)中采样 Mz,然后计算模型在每个 z 下赋予 x 的密度 P_θ(X|Z) 的平均值。P_θ(X|Z) 只是一个由神经网络输出参数化的高斯分布,可以直接计算其密度值。

这样,我们就把积分转化成了可计算的离散求和形式,可以用PyTorch等框架实现了。

然而,问题在于:对于高维数据,我们需要高维的隐空间。为了对这个高维空间上的积分有一个好的近似,可能需要指数级数量的样本。在实践中,简单的随机采样在高维情况下效果不佳。

技巧二:重要性采样

第二个技巧是进行更智能的采样,而不是简单的随机蒙特卡洛近似,这称为重要性采样

核心思想是:对于给定的数据点 X,如果我随机采样 Z,那么对于大多数 Z,条件概率 P(X|Z) 几乎为零,因为大多数混合分量都不在 X 附近。只有少数 Z 对应的混合分量会对积分有显著贡献。

重要性采样通过从一个不同的分布 Q(Z) 中采样来更有效地估计期望。我们在积分中乘以再除以 Q(Z),从而将期望转化为关于从 Q(Z) 中采样的形式。如果我们选择的 Q(Z) 是一个只在那些对 P(X|Z) 有高贡献的 Z 上放置高密度的分布,那么用更少的样本就能得到积分的好估计。

理论上,最优的重要性采样分布是后验分布 P(Z|X),即在给定观测数据 X 的条件下,隐变量 Z 的分布。如果我们能从 P(Z|X) 采样,就能高效地估计积分。

但问题又来了:我们不知道 P(Z|X)。它本身可能又是一个复杂的分布。

技巧三:预测最优采样分布(编码器)

第三个技巧是:我们将尝试建模或预测那个最优的采样分布 P(Z|X)。我们将训练一个模型,以 X 作为输入,输出一个在 Z 空间上的密度。这个模型就是对 P(Z|X) 的近似。

现在,模型开始看起来像自编码器了!这就是编码器:一个从 XZ 空间分布的映射。

我们称这个近似分布为 Q_φ(Z|X),其中 φ 是编码器神经网络的参数。为简单起见,我们通常也将其建模为高斯分布。编码器神经网络 F 输出均值 μ_z 和方差 σ_z²

现在,我们有了完整的图景:

  • 解码器/生成器 P_θ(X|Z):定义生成模型。
  • 编码器 Q_φ(Z|X):近似后验分布,用于高效的重要性采样。

我们需要为编码器设定目标函数。我们希望编码器建模的分布 Q_φ(Z|X) 尽可能接近真实的后验 P_θ(Z|X)。我们可以使用KL散度来衡量两个分布之间的差异,并最小化这个差异。

通过一些代数推导(详见课程资料),我们可以得到编码器的学习目标是最大化一个特定的目标函数 J_Q。同时,对于解码器的学习目标 J_P(最大化数据似然),我们可以利用重要性采样,从我们学到的分布 Q_φ(Z|X) 中采样来近似积分。

证据下界与VAE最终目标

将两者结合起来时,还有一个细节:如果我们对 log E[.] 进行蒙特卡洛估计,然后取对数,会得到一个有偏估计。我们利用琴生不等式,将 log 移到期望内部,从而得到一个证据下界

证据下界是数据对数似然的一个下界。最大化这个下界,就能提高模型赋予数据的似然。ELBO可以写成两项具有直观形式之和:

  1. 重构项:期望 log P_θ(X|Z),衡量解码器重建数据的能力。当 P_θ(X|Z) 是高斯分布且方差固定时,该项简化为负的均方误差。
  2. 正则化项-D_KL(Q_φ(Z|X) || P(Z)),衡量编码器分布 Q 与先验分布 P(Z)(单位高斯)的差异。它迫使编码器产生的隐变量分布接近简单的先验分布。

VAE的最终目标就是最大化这个证据下界 L(θ, φ; X)。我们可以通过采样来无偏地估计这个期望。

VAE的直观流程与实现

经过大量数学推导后,VAE最终看起来就像一个简单的自编码器:

  1. 从数据集中取一个数据点 X
  2. 编码:通过编码器 F_φ,得到均值 μ_z 和方差 σ_z²
  3. 采样:从分布 Q(Z|X)(即 N(μ_z, σ_z²))中采样一个 z。使用重参数化技巧:z = μ_z + σ_z * ε,其中 ε ~ N(0, I)。这一步相当于在隐空间添加了噪声,是重要性采样的体现。
  4. 解码:将 z 通过解码器 G_θ,得到重建的数据。
  5. 计算损失:损失函数包含两部分:
    • 重构损失:例如,负的 log P_θ(X|z),在高斯假设下近似为均方误差。
    • KL散度损失D_KL(Q_φ(Z|X) || N(0, I)),有解析形式。
  6. 反向传播更新编码器和解码器的参数。

在实践中,为了简化,人们有时会固定解码器输出的方差,或者固定编码器输出的方差。例如,如果固定所有高斯分量的方差为1,并且也固定编码器输出的方差为1,那么重构项就变成最小化均方误差,KL散度项则简化为最小化编码均值的平方和(即迫使编码向原点收缩)。这看起来就像一个带有额外正则项(迫使隐编码接近原点)的自编码器。

为什么这种“收缩”不会导致所有编码都塌缩到同一个点(例如原点)?
这是因为存在张力:重构项要求编码保留足够的信息以重建输入,这迫使编码分散开;而KL散度项则要求所有编码向原点收缩。最终的平衡点是编码分布被“压”成一个单位高斯球体,但同时各个编码点又尽可能分开以保留信息,类似于一个球体填充问题。

VAE学习到的表示与解耦

VAE不仅是一个生成模型,其编码器也学习到了数据的有用表示。通过观察在隐空间 Z 中沿不同坐标轴移动时,解码器生成的图像如何变化,我们可以发现,VAE学习到的隐变量维度常常对应着数据中独立的、有意义的变化因子。

例如,在一个生成“河流与草地”场景的VAE中,一个隐变量维度可能控制草地的颜色,另一个维度可能控制河流的弯曲程度。这些维度的影响是独立的:改变颜色维度不影响弯曲度,反之亦然。这种特性被称为解耦

解耦的表示是理想的,因为它意味着隐空间的每个维度都对应一个独立的、语义上有意义的生成因子。这并非VAE独有,其他生成模型如GAN和扩散模型,在其隐空间或中间激活空间中也表现出类似的性质。生成建模的过程迫使模型去寻找能够简洁解释世界的表示,而解耦的因子正是实现这一目标的有效方式。

总结

本节课中,我们一起深入学习了变分自编码器。我们从生成模型与表示学习相结合的角度出发,将VAE理解为一个旨在使自编码器隐空间规范化为简单分布(如高斯分布)的模型。

我们从概率角度将其解释为一个无限高斯混合模型,并通过三个核心技巧解决了其训练中的计算难题:

  1. 蒙特卡洛采样近似难处理的积分。
  2. 重要性采样提高采样效率。
  3. 引入编码器网络预测最优的重要性采样分布(即近似后验分布)。

最终,VAE的目标函数归结为最大化证据下界,它平衡了数据重构精度隐分布与先验分布的接近程度。实现上,VAE类似于一个在隐层添加噪声并带有KL正则项的自编码器。

此外,我们还看到VAE学习到的隐表示往往具有解耦的特性,不同维度控制数据中独立的变化因子,这使得它同时成为一个强大的表示学习工具。

VAE提供了一个统一框架来理解生成建模和表示学习,虽然当前其流行度可能不及扩散模型,但其数学思想深刻影响了整个领域。

016:条件生成模型 🎃

在本节课中,我们将学习条件生成模型。我们将探讨如何利用生成模型来解决结构化预测问题,即输入一个高维结构化数据(如图像或文本),并预测另一个高维结构化输出。我们将看到,与传统的回归或分类方法相比,生成模型能够更好地处理输出的多模态分布和维度间的联合依赖关系。


结构化预测的动机 🎯

上一节我们介绍了生成模型的基本概念,本节中我们来看看如何将它们应用于条件预测任务。

结构化预测是指预测一个高维、结构化的对象,而不是一个单一的数字或类别。这个输出对象内部的各个维度之间不是统计独立的,而是存在复杂的依赖关系。例如:

  • 图像到图像:如图像着色、语义分割(为每个像素分配类别)。
  • 音频到文本:语音识别。
  • 文本到图像:根据描述生成图片。
  • 蛋白质序列到3D结构:预测蛋白质的几何形状。

传统的“非结构化”预测方法(如最小二乘回归)假设输出维度是独立的,并使用如均方误差(MSE)的损失函数。这会导致两个主要问题:

  1. 无法处理多模态分布:当真实的后验分布有多个峰值(如一件灰度衬衫可能是粉色或青色)时,MSE会促使模型预测所有可能性的平均值,而这个平均值在真实分布中可能概率为零。
  2. 忽略联合依赖:当独立预测每个像素的颜色时,即使每个像素的预测概率都接近正确,也可能因为概率的微小波动导致相邻像素被预测成不同颜色,破坏了整体一致性(如衬衫应统一为一种颜色)。

生成模型通过建模完整的联合概率分布 P(Y|X),能够同时解决这两个问题,从而成为处理结构化预测任务的强大工具。


图像到图像的映射 🖼️→🖼️

现在,让我们具体看看如何用生成模型实现图像到图像的转换。我们将重点介绍条件生成对抗网络和条件变分自编码器。

条件生成对抗网络

生成对抗网络的核心思想是让一个生成器 G 和一个判别器 D 进行对抗游戏。在条件设置中,生成器不仅接收随机噪声 z,还接收条件信息 x(如输入图像),并生成输出 y

以下是条件GAN的目标函数:

判别器 D 的目标:区分“真实配对” (x, y) 和“生成配对” (x, G(x))

max_D [ log D(x, y) + log(1 - D(x, G(x))) ]

D(x, y) 试图给真实数据对高分,D(x, G(x)) 试图给生成数据对低分。

生成器 G 的目标:生成以假乱真的输出,欺骗判别器。

min_G [ log(1 - D(x, G(x))) ]

G 试图使 D(x, G(x)) 给出高分(即判别器认为生成数据是真实的)。

从生成器的角度看,判别器充当了一个可学习的损失函数。它不再仅仅比较像素值,而是评估输出的整体结构和真实性。判别器的架构决定了它关注何种层次的结构:

  • 小感受野:近似于逐像素损失。
  • 中等感受野(如16x16):可以惩罚模糊(需要多个像素才能判断)。
  • 大感受野:可以评估更高阶的统计特征和全局一致性。

一种常见的架构是PatchGAN,它对图像的每个局部块进行真假判别,从而在有限数据下更有效地提供监督信号。

条件变分自编码器

变分自编码器通过引入潜在变量 z 来建模数据分布。在条件VAE中,解码器在重建数据时,不仅依赖潜在变量 z,还依赖条件信息 x

其目标是最大化条件对数似然的下界:

log P(y|x) >= E_{z~q(z|y,x)}[log P(y|z,x)] - KL( q(z|y,x) || p(z) )

其中:

  • q(z|y,x) 是编码器,将目标 y 和条件 x 编码为潜在变量 z
  • P(y|z,x) 是解码器,根据 zx 重建 y
  • p(z) 是先验分布,通常为标准高斯分布 N(0, I)

潜在变量 z 学习编码那些在条件 x 中未指定的、但为确定输出 y 所必需的所有不确定性信息(例如,在给定建筑平面图 x 时,z 编码建筑材料和颜色)。在推理时,我们从先验分布 p(z) 中采样 z,与条件 x 一起输入解码器,即可得到多样化的输出样本。


文本到文本的映射 📝→📝

对于文本数据,自回归语言模型本质上是条件生成模型。它们通过链式法则将序列的联合概率分解为一系列条件概率的乘积:

P(序列) = P(x1) * P(x2|x1) * P(x3|x1,x2) * ...

因此,要将一个语言模型用于条件文本生成任务(如情感分析、翻译、问答),只需将条件信息 x(如产品评论、问题、指令)作为前缀输入模型,然后让模型自回归地生成目标序列 y 即可。这种技术通常被称为提示

例如,进行情感分析:

输入(条件 x): “这款相机画质出色,但电池续航太差。____”
模型续写(预测 y): “总体评价是负面的。”

模型学习了在互联网文本中“评论”和“后续情感总结”之间的条件分布。


跨模态映射:图像与文本 🔄

现代生成模型的核心优势之一是能够连接不同模态的数据,如图像和文本。这通常通过Transformer架构实现,它将所有数据(图像块、文本词元)转换为统一的“词元”表示。

图像到文本(图像描述)

模型通常包含以下部分:

  1. 图像编码器:如Vision Transformer,将图像分割成块并编码为一系列词元。
  2. 文本解码器:一个自回归语言模型(如GPT),用于生成文本。
  3. 连接机制:通过交叉注意力将两者连接。文本解码器中的词元可以查询图像编码器输出的词元,从而将视觉信息整合到文本生成过程中。

文本到图像

这是当前文本生成图像模型(如Stable Diffusion)的基础。以扩散模型为例,将其变为条件模型非常简单:

在去噪过程的每一步,去噪网络 U-Net 不仅接收带噪声的图像 x_t 和时间步 t,还接收文本条件 c(由文本编码器如CLIP产生)的嵌入。文本信息引导去噪过程朝向与描述相符的图像区域发展。损失函数为:

L = E[ || ε - ε_θ(x_t, t, c) ||^2 ]

其中 ε_θ 是去噪网络预测的噪声,c 是文本条件。

许多先进模型是混合体,例如先使用VQ-VAE将图像压缩到离散潜在空间,然后在潜在空间中使用扩散模型或自回归模型进行生成,并以文本条件进行引导。


无配对数据的条件生成 🧩

到目前为止,我们假设拥有配对数据 (x, y)。但如果只有两个独立的数据集(一堆照片和一堆梵高画作),而没有一一对应的配对,该如何学习映射呢?这称为无配对跨域翻译

一种经典方法是CycleGAN,它利用循环一致性约束。其核心思想是学习两个映射:

  • 生成器 G: X -> Y(照片变画作)
  • 生成器 F: Y -> X(画作变照片)

并引入两个判别器 D_YD_X 分别判断图像是否属于域 Y 或域 X。关键约束是循环一致性损失

L_cycle = E_x[ || F(G(x)) - x || ] + E_y[ || G(F(y)) - y || ]

这强制要求将一个域的数据转换到另一个域再转换回来,应该能够近似还原。结合GAN的对抗损失,模型就能在缺乏明确配对的情况下,学习到有意义的跨域映射(如照片的风格化)。其有效性依赖于神经网络和优化器倾向于找到“简单”或“平滑”的映射这一归纳偏置。


总结 🎓

本节课中我们一起学习了条件生成模型。我们了解到:

  1. 结构化预测是AI中的核心问题,生成模型因其能建模多模态分布和维度间联合依赖而成为理想工具。
  2. 条件生成对抗网络通过对抗训练,使用一个可学习的判别器作为损失函数,鼓励生成器输出在结构上逼真的结果。
  3. 条件变分自编码器引入潜在变量来捕捉条件信息之外的剩余不确定性,便于进行多样化采样。
  4. 自回归模型天然适用于序列数据的条件生成,通过提示工程可完成多种任务。
  5. 跨模态模型利用Transformer和交叉注意力等机制,实现了图像与文本等不同模态间的相互理解和生成。
  6. 无配对学习方法如CycleGAN,通过利用循环一致性等约束,使得在没有显式配对数据的情况下进行跨域翻译成为可能。

条件生成模型极大地扩展了AI的能力边界,使其能够处理现实世界中复杂的、结构化的预测任务,是当前生成式AI浪潮背后的关键技术之一。

017:分布外(OOD) 🎯

在本节课中,我们将要学习机器学习中的一个核心挑战:分布外泛化。我们将探讨为什么模型在训练数据分布之外的数据上表现不佳,并介绍两种主要的问题类型:对抗性示例和分布偏移。我们将了解这些问题的成因、影响以及一些当前的研究方向。


概述 📋

我们之前讨论了生成模型。本节中,我们将转向一个现代机器学习面临的重大开放挑战:泛化到训练数据分布之外的能力。尽管机器学习在许多领域取得了巨大成功,但在现实世界部署时,模型常常因为数据分布的变化而失败。本节课将深入探讨这一问题的本质。

现实世界中的挑战与风险 ⚠️

上一节我们介绍了分布外泛化的概念。本节中我们来看看它在现实世界中的具体表现和风险。

许多成功部署的AI系统都属于“高精度人工验证系统”。模型需要足够好用,但总有一个人类在回路中进行验证和纠正。例如,ChatGPT生成内容后,用户会进行编辑和交互;医疗领域的决策支持工具由放射科医生最终核实。当模型在没有人工验证的情况下被完全信任时,问题就开始出现。

然而,对于高风险场景(如自动驾驶汽车、直接医疗诊断),我们缺乏可靠的机制来保证模型的鲁棒性,无法保证它们不会犯下潜在的有害错误。这引发了关于机器学习系统是否真正为现实世界做好准备的问题。

理想与现实:IID假设的破灭 📊

在标准机器学习设置中,我们通常假设训练数据和测试数据是独立同分布的。这意味着我们从感兴趣的数据分布中随机采样进行训练,然后在同一分布的其他数据上进行推理。

但这在现实世界中从不成立。不存在一个能代表所有现实情况的数据集。即使我们收集了海量数据(例如数十亿张图像),世界也在不断变化。今天收集的数据,一个月或一周后可能就不再具有代表性。

因此,在部署环境中,训练分布和测试分布之间通常存在根本性差异。例如,一个在剑桥数据上训练的模型,在雪季可能就会失效。分布变化的方式多种多样:

  • 数据扰动:例如对抗性攻击或传感器更新导致的统计差异。
  • 标签分布变化:某些类别变得非常普遍或完全消失。
  • 其他偏移:序列长度、地理位置、测量源等变化。

无法预见的分布偏移:以Roomba为例 🐕

当出现分布差异时,具体会出什么问题?一个有趣的例子是Roomba扫地机器人。它们曾有一个故障模式:会碾过狗粪,然后把粪便涂满整个屋子。为了解决这个问题,开发人员收集了一个庞大而多样的狗粪数据集。

但随后有用户指出,Roomba还会把黑色条纹地毯误认为是悬崖,因此无法清洁部分地面。这个例子说明,你可以为你能预见的分布偏移收集数据,但总会有你无法预见的情况。我们不可能描述和预测世界将要发生的每一种变化。

因此,认为我们可以收集到足够有代表性的数据,以确保部署环境与训练数据IID的想法是非常困难的。这在AI安全、特别是高风险场景(如医疗、自动驾驶)中是一个具体而严峻的问题。

评估中的陷阱:数据污染 🔍

在机器学习应用于科学领域时,存在一个“可重复性危机”。一项研究发现,许多研究未能实现可重复性或泛化性的一个主要原因是数据污染

评估机器学习系统的方式,在某种程度上人为地制造了IID的假象,而这并不能代表现实。例如,在生态学中,如果在一个国家公园开发了一个识别所有个体猕猴的系统,那么只有当你的评估方式能够代表猕猴随时间的变化时,你才能合理地声称未来在该公园监测猕猴的能力。

设计机器学习模型的评估系统需要非常仔细的构建和思考。如果使用现成的工具(如Roboflow),它们通常自动假设随机划分数据集是可行的。这通常会导致对模型性能产生错误的高期望,因为训练和测试数据中存在强烈的空间、时间或上下文相关性,让模型误以为自己表现良好。

本节课路线图 🗺️

接下来,我们将具体探讨两类分布外问题及其应对方法。

首先,我们将讨论对抗性示例,思考如何通过训练来应对这类针对单个数据点的小扰动。

然后,我们将讨论分布偏移,即整个数据分布的大规模变化,并介绍一些人们用来尝试解决它们的简单机制。需要指出的是,这两个都是活跃的研究领域,我们会讨论进展,但目前尚无完美的解决方案。


对抗性示例 🎭

首先,我们来谈谈对抗性示例。

什么是对抗性示例?🤔

假设我们有一个简单的图像分类器。一张猪的图片,模型以91%的置信度将其分类为猪。对抗性示例会向这张图片添加非常少量的、看起来像随机噪声的扰动。虽然人眼几乎无法察觉差异,但这个经过优化的扰动可以完全改变模型的预测,甚至可能以更高的置信度将其分类为其他物体(如飞机)。

这指出了机器学习模型通常存在的脆弱性:它们可能大体准确,但对人类视觉系统完全不敏感的变化却异常脆弱。

物理世界中的对抗性攻击 🌍

对抗性攻击可以发生在物理世界。例如,在一个香蕉旁边放上一个经过特殊构造的对抗性贴纸,可以成功让模型将其分类为烤面包机。另一个例子是一个3D打印的海龟模型,大多数图像分类模型都会非常自信地将其归类为步枪,即使从不同角度观察也是如此。在语音识别中,添加微小的针对性噪声也能让模型产生完全不同的翻译结果。

生成对抗性示例的方法 ⚙️

那么,如何创建对抗性示例呢?我们首先假设有一个固定的机器学习模型。我们的目标是找到对给定图像的一个小扰动,这个扰动不改变对人类的意义,但会改变机器学习模型的判断。

模型会输出给定输入 x 和模型参数 θ 时,类别 y 的概率 P(y|x; θ)。对抗性攻击试图解决以下优化问题:找到一个小的扰动 δ,使得模型对扰动后的图像 x + δ 预测为某个目标类别 y_target(而非原始类别)的概率最大化。

这个过程通常受到一个约束,即扰动必须很小。一个常见的方法是使用 L∞ 范数 进行约束,它限制的是每个像素的最大变化量,这往往能产生人眼难以察觉的扰动。

在实践中,我们可以使用投影梯度上升 方法来近似求解。首先,沿着梯度的方向更新扰动,然后将结果投影回允许的扰动集合内,反复迭代。这种方法简单且有效。

防御对抗性示例:对抗训练 🛡️

如何防御对抗性示例?一种思路是进行对抗训练

我们可以换一种方式思考对抗性示例:不是最大化模型将图像预测为特定错误类别的概率,而是最大化模型在扰动图像上的损失。在标准训练中,我们使用SGD最小化所有数据点上的损失。而在对抗训练中,我们试图让模型对预期的扰动类型具有鲁棒性。

这形成了一个嵌套优化问题:对于训练数据中的每个图像,我们寻找最坏情况的扰动,然后寻找一个模型参数,使得在所有数据点上,即使面对这些最坏情况扰动,损失也尽可能小。这也可以看作是一种自适应数据增强,专注于寻找最困难的样本。

在训练循环中,具体步骤是:

  1. 采样一个数据点 (x, y)
  2. 计算该数据点的近似最优对抗性扰动 δ(例如使用投影梯度上升法)。
  3. 计算模型在扰动后数据 (x+δ, y) 上的梯度。
  4. 根据该梯度更新模型参数。

这种方法计算成本较高,因为每个数据点都涉及一个内部优化循环。

对抗性示例揭示了什么?🔬

对抗性示例的存在告诉我们模型学到了什么。它揭示了输入特征中哪些统计特性对模型的决策至关重要。

例如,在一个区分数字4和9的任务中,如果在每个4的左上角添加一个白像素,在每个9的旁边添加一个白像素,模型就会学会只关注那个像素位置,而忽略图像的其他部分。这成为一个完美的分类器,但也极其脆弱。

模型学到的特征可以分为:

  • 鲁棒特征:与标签相关,即使经过扰动也保持稳定(如猫耳、狗鼻的形状)。
  • 非鲁棒特征:在训练数据中与标签相关,但可以通过扰动被翻转,从而改变预测。

许多特征在训练数据中与标签相关,因此具有预测性,也能帮助提高准确率。但有些特征可能基于数据集中存在的偏见或虚假相关性,例如数据集中医生多为白人男性,家庭主妇多为女性。模型使用这些特征不仅会导致非鲁棒性,还可能加剧社会偏见。

数据集的局限性:捷径与虚假相关 🏞️

这些相关性根植于数据本身。理想情况下,我们拥有代表现实世界分布的数据和专家标注。但实际上,我们通常使用从互联网抓取的图像和众包标注,这导致了噪声和复杂的偏见。

模型倾向于学习捷径——那些在训练数据中与标签相关,但在现实分布变化下可能不再相关的特征。例如,在物种识别中,如果训练数据中浣熊多在夜间出现,模型可能将“夜间”与“浣熊”关联,导致在白天浣熊图像上表现不佳。

对抗性示例可以在使用相同数据训练的不同模型之间迁移,因为相同的捷径被不同的架构学到了。

这些捷径可能表现为:将没有羊的绿色山坡识别为“羊群”,将树上的山羊识别为“鸟群”,或将人抱着的山羊识别为“狗”。在医疗领域,模型可能学会根据X光片的采集医院(而非病理特征)来预测疾病。

对抗训练的效果与局限 📈

对抗训练旨在使模型对非鲁棒特征具有不变性。虽然这种噪声添加不一定代表所有可能的扰动方式,但有研究表明,对抗训练得到的模型特征往往更符合人类直觉(例如,注意力更集中在语义对象上)。此外,为鲁棒模型生成的对抗性示例,看起来会更像目标语义类别(例如,想让鸽子看起来像狗,生成的图像就真的更像狗了)。

一些论文还显示,对抗训练模型的表征可能更好地迁移到其他数据集。对抗训练可以被视为一种有针对性的正则化或领域适应方法,旨在使模型对特定构造的扰动具有鲁棒性。


分布偏移 🌐

现在,我们来讨论不是针对单个数据点,而是针对整个数据分布的扰动。

现实世界的分布偏移:以生态学为例 🌳

在现实世界中,训练和推理的分布可能存在巨大差异。例如,全球物种丰富度分布图与现有物种观测记录分布图并不吻合,甚至近乎反相关。如果你在一个地区训练模型,然后部署到另一个地区,就会面临显著的分布偏移。

这种偏移的维度可能很复杂,包括背景、相机角度、拍摄时间(昼夜)等视觉变化,以及亚群分布偏移——即不同地点或相机有其独特的类别分布。例如,不同城市的树木种类构成不同,不同河流的鱼类群落也不同。

性能通常与亚群分布的相似性有很强的相关性。模型在训练时,会基于训练集中的亚群分布形成一个先验。如果测试分布与此先验更接近,模型就更可能预测得好。

应对分布偏移:分布鲁棒优化(DRO) 🛠️

一种应对分布偏移的方法是分布鲁棒优化。这与对抗性示例的思路类似,但现在是扰动整个训练分布。

DRO假设最坏情况的数据分布偏移可能会发生,然后寻找一个模型,使得在数据分布所有可能的偏移范围内,期望损失最小化。这同样是寻找最坏情况并确保模型对其鲁棒。

但挑战在于如何衡量两个复杂分布(如百万张图像数据集)之间的距离,以及如何采样这些假设的分布。在实践中,这通常很棘手。

DRO在处理类别不平衡或亚群问题时比较有效。传统训练可能会忽略少数亚群,因为即使在该亚群上错误率很高,总体损失仍然可以很低。DRO可以自动重新加权数据,迫使模型更关注少数群体,减少对噪声特征的依赖,更关注有用的预测特征。即使不知道亚群的具体划分,也有方法可以实现这一点,因此DRO常被用于公平性机器学习问题。

其他应对策略 🧩

除了DRO,还有其他应对分布偏移的策略:

  1. 学习先验:显式地学习关于分布偏移的先验。例如,在物种识别中,可以学习一个基于空间和时间的模块化先验,用来根据图像拍摄地点和时间重新映射物种预测的概率。这可以显著提高准确率。
  2. 领域适应:这是一个庞大的模型类别,旨在给定源领域和目标领域(可能目标领域标签很少)的情况下,在训练时对齐它们的表征,以强制模型泛化或适应新领域。
  3. 诊断失败模式:通过创造性方法诊断模型的失败模式,这通常能揭示训练数据中的偏见。例如,可以按类别、时间、性别等维度分解评估结果。也可以使用现代生成式AI模型生成反事实样本来理解失败原因(例如,发现模型能很好识别有食物的盘子,但空盘子常被误认为托盘或水桶)。
  4. 获取代表性数据:从实践角度,获取能代表测试领域的训练数据通常是最好、最有效的方法。许多算法方法的效果通常远不如获取更多正确的训练数据。如果由于各种限制无法获取足够数据(如濒危物种、外星环境),那么可能仍然需要大量人工介入。

总结与问答 🎓

本节课中我们一起学习了分布外泛化这一重大挑战。深入探讨这个问题有助于我们理解神经网络学到了什么,以及训练数据与模型有效使用范围之间紧密而复杂的关系。我们具体讨论了对抗性示例分布偏移的成因、影响及部分应对策略。

问答环节

  • :有人研究过激活函数(如ReLU, GeLU)的选择如何影响泛化或鲁棒性吗?
  • :可能有相关研究,但我不了解具体文献。我的直觉是影响不会特别大,可能在简单数据集上效果更明显。这是一个有趣的问题。
  • :与其针对假设的扰动进行鲁棒训练,使用人类直接反馈(如人在回路、强化学习、DPO)是否更高效?
  • :当然,如果有一个特定的目标数据集,使用这些方法进行高效地专门优化是完全可行的,也是一个活跃的研究领域。今天讨论的工作更侧重于追求普适的鲁棒性,即对任何想象的变化都鲁棒。而如果你明确知道数据将如何变化,那么针对该特定偏移进行高效优化是可能的。这体现了“应对分布偏移”和“适应特定分布偏移”之间的区别。

利用额外时间,去投票吧! 🇺🇸

018:迁移学习

概述

在本节课中,我们将学习迁移学习。迁移学习的核心思想是,如何利用从一个模型中学到的信息,并将其转移到新的任务或挑战上,即使我们只有很少的数据。我们将探讨几种不同的迁移学习子领域,包括微调、领域适应、知识蒸馏以及基础模型。


为什么需要迁移学习?🤔

上一周我们讨论了分布偏移。从高层次来看,迁移学习的理念是,即使数据量很少,我们也希望模型能够学习。

深度学习与许多传统的学习算法不同,其性能不会随着训练数据量的增加而饱和。相反,随着数据规模的扩大,深度学习模型的性能可以持续提升。最近的分析表明,即使面对数十亿数据点规模的数据集,我们最大的深度学习模型的计算能力也尚未被完全利用。

小样本学习

小样本学习是指我们能够从很少的数据中进行学习。人类在这方面表现得非常出色,我们擅长识别模式并将其推广到从未见过的事物上。然而,当数据量很少时,要明确理解我们感兴趣的类别边界可能会很困难,因为存在模糊性。


迁移学习的动机 🧠

到目前为止,我们讨论的深度学习通常是从零开始,在一个数据集上学习特定任务所需的物理规则、概念和相似性维度。

但人类在学习新事物时,会利用大量的先验知识。这些知识来自我们从幼年时期开始在世界中的物理体验,学习物理、结构等概念,并通过整个教育和生活经验学习如何思考相似性和差异性的度量。不同的人可能拥有不同的世界心智模型。

这就引出了一个核心问题:我们如何让深度学习模型或深度网络也能获得类似的先验知识?一个非常简单的方法就是在其他任务或先前的任务上对它们进行预训练

我们可以将神经网络“送去上学”,让它学习如何学习。具体做法可能是,在一个拥有海量数据但类别可能与最终目标不完全相同的任务上进行训练,然后通过某种机制将知识从预训练网络迁移过来,以解决新任务。

因此,迁移学习或小样本学习的一个观点是:深度学习的意义在于使我们能够利用少量数据解决这类问题。我们试图学习那些具有良好基础、知道什么是有用的、知道在什么之上进行构建的表示和模型。


迁移什么知识?🔍

当我们考虑一个模型时,它通常以非常复杂、非线性的方式将一组输入映射到一组输出。

那么,我们到底在迁移什么?我们可以考虑迁移关于输入到输出关系的知识,也可以考虑以不同方式迁移知识,例如迁移模型中的偏差、优化器的知识,或者模型潜在的误差模式、伦理风险或社会影响。

我们将主要关注相对简单的知识迁移机制。但需要指出的是,在机器学习研究领域,关于如何以有价值的方式共享信息和迁移知识,仍有很大的探索空间。


迁移映射知识:微调 🛠️

首先,我们来谈谈如何从模型本身(即从输入到有用输出的映射)迁移知识。

一个简单的例子是:我们拥有大量带有流派标签的音乐数据。能够识别流派可能是有用的,因为它教会了模型关于音乐结构的知识。然后,我们可能希望将这种知识适应或迁移到一个数据量少得多的任务上,例如预测个人对歌曲的偏好。

微调的基本流程

在高层次上,微调的过程如下:

  1. 在一个任务A(预训练任务)上预训练你的网络(权重W和偏置B)。
  2. 使用部分或全部W和B初始化第二个网络。
  3. 在任务B上训练第二个网络,得到调整后的参数W‘和B’。

我们迁移的是从初始模型中学到的表示,它是一个编码器,编码了关于数据的有用信息。然后我们学习如何在下游任务中利用这个有用的表示。

在实践中,只要输入数据的类型相似(如图像和图像),我们常常发现,即使在微调之后,模型权重和偏置中的许多结构(尤其是在早期层)实际上保持不变。这似乎表明,其中一些结构在某种程度上是通用的,对于特定类型的数据(如图像或音频)普遍有用。

自监督预训练

我们之前在讨论基于相似性的表示学习时,看到了一个非常具体的案例:自监督预训练

在这种自监督预训练中,我们不一定直接训练模型去做我们关心的事情,而是训练模型学习数据相似性。事实证明,这对于训练适用于下游任务的表示非常有用。这种预训练的主要好处是,因为它不需要数据标签,所以可以在海量数据上进行预训练。没有特定标签的数据量远大于任何有标签或干净的数据集。

实际考虑:输入/输出维度变化

如果你预训练的模型输入是RGB图像,预测两个类别(小麦或玉米),而现在你想微调的模型输入是灰度图像(单通道),输出需要预测三个类别,那么模型结构实际上就不同了。

一个简单的机制是,你可以在模型中添加一些“粘合层”,将数据转换到正确的格式。例如,定义一些新的层(Z),它们可能只是几个简单的全连接层,用于将每个输入和输出映射到相同的维度。

你可以在需要的地方这样做。例如,如果你仍然使用RGB图像,只是增加了一个输出维度,那么你可以获取初始模型的最终输出层,并学习一个重投影将其映射到更多维度;或者,你可以直接替换掉从编码器表示到预测类别数量的最终映射层。

这种灵活性在实践中很常见。例如,大多数图像模型是在ImageNet等RGB数据上预训练的,但如果你在机器人领域工作,使用的是RGB-D数据(增加了深度维度),你仍然可以从没有深度信息的预训练模型中进行迁移学习。通常的做法是学习一个额外的编码,将深度维度映射到三个通道,或者使用PCA等方法将高维输入降维。

核心思想:你可以将这些组件视为模块化的构建块。你可以使用别人训练好的构建块,然后添加自己需要的模块,使其对你的问题有用。一个训练良好的构建块仍然是有价值的。


避免灾难性遗忘 🚫

如果你只有很少的数据,并且让整个模型训练直到损失完全饱和,你可能会再次遇到灾难性遗忘问题。模型权重改变太多,以至于失去了从原始数据中学到的能力。

为了避免在少量数据上过度拟合,同时又不忘记通用的有用表示,有几种机制可以尝试:

以下是几种常用策略:

  • 冻结大部分网络:只训练最后几层或新增的层。
  • 使用非常小的学习率:限制模型在每次训练步骤中的移动幅度。
  • 早停:使用验证集,根据验证集上的最佳性能决定保留哪个模型权重,在模型忘记太多预训练任务知识之前停止训练。
  • 继续在原始数据上训练:如果在微调时能继续使用一些原始的预训练数据,会很有帮助。但这在输入类型和目标不同时会变得复杂。

这些选择都假设你有一个具有代表性的验证集。如果未来可能出现分布偏移,那么尽可能接近你的实际问题来衡量相对性能就变得非常重要。


领域适应 🌐

领域适应是另一个研究领域。其思想是,你有一个源域和一个目标域,你希望模型在目标域上表现良好,同时确保源域和目标域的表示空间良好对齐。

这在处理分布偏移问题时经常出现。一种动机是,如果你强制这些特征空间在总体上对齐,那么你可以用更少的数据有效地学习映射。

一种常见的方法是对抗性领域适应

  • 在训练时,你同时拥有源数据和目标数据。
  • 对于每个输入,你希望预测你关心的类别,同时也预测它来自哪个域。
  • 关键技巧是:反转来自域预测器的梯度。你试图训练模型在预测类别时尽可能好,而在预测域时尽可能差。这迫使两个域的表示对齐。

这种方法甚至可以扩展到目标域没有类别标签的情况。你仍然可以尝试保持模型在预测类别上的良好性能,同时使其难以预测数据来自哪个域。希望即使目标域的标签数据非常有限甚至没有,你仍然能学到一些有用的东西。

无监督领域适应

在无监督领域适应中,你可能有目标域的数据,但没有标签。一个经典的简单方法是:使用在源域上预训练的模型对目标数据进行预测,然后选择一个阈值,将超过该阈值的预测视为“伪标签”。

一个更复杂的机制是师生蒸馏:我们创建一个新的学生模型,并使用源模型在目标数据上的预测作为弱监督来训练学生模型。学生模型可能能够学到比原始预测更有用的东西。

人们在此基础上添加了许多改进,例如:

  • 使用指数移动平均来正则化师生模型之间的权重变化。
  • 在源数据上进行额外训练。
  • 使用蒸馏损失(匹配输出分布而非硬标签)。
  • 尝试显式地对齐源域和目标域的特征。

重要提示:这些方法在特定场景下可能非常有效,但它们几乎永远不如拥有目标域的真实标签数据有效。此外,领域适应文献中的一个常见问题是,它们通常在有标签的目标域验证数据上展示性能。这引发了一个问题:如果你需要标签数据来进行模型选择和确定最佳机制,为什么不直接使用这些标签数据进行训练呢?

领域适应方法的效果也很大程度上取决于起始点模型的性能。因此,它几乎可以看作是微调的一种补充机制,适用于你拥有大量无标签数据而非少量有标签数据的情况。


迁移输出知识:知识蒸馏 🧪

接下来,我们讨论如何迁移关于模型输出的知识。

回想一下,我们通常使用交叉熵损失来训练分类器。模型输出一个预测分布(如经过softmax的分数),而真实标签是一个独热编码的向量。交叉熵损失试图让模型的预测分布尽可能接近真实分布。

知识蒸馏的思想是:现在我们有一个大的、固定的预训练模型(教师模型)。我们想学习一个学生模型。我们希望学生模型的预测分布与教师模型的预测分布尽可能接近。

为什么知识蒸馏有用?

  • 模型压缩:学生模型可以更小、计算效率更高。
  • 传递相似性信息:教师模型的软目标(输出分布)比硬标签(独热编码)包含更多信息。例如,一张猫的图片可能被预测为“猫”的概率很高,但“老虎”的概率也较高。这告诉学生模型:“这是一只看起来有点像老虎的猫”,这为模型提供了比可分离类别更有用的上下文信息。
  • 处理闭源模型:如果你只能访问一个模型的输出预测分数,而无法获取模型本身,你可以通过学习一个模型来匹配这些预测分数。

知识蒸馏不仅可用于创建小模型,还可用于跨模态学习(例如,从RGB图像教师模型蒸馏到只有深度信息的学生模型),或蒸馏集成模型的结果(将多个昂贵模型的集成知识压缩到一个更高效的模型中)。

表示蒸馏

如果你不仅想蒸馏输出,还想捕获和蒸馏表示本身的知识呢?例如,你可以使用对比学习来匹配模型中某个中间点的嵌入。

在这种情况下,你希望学生模型输出的嵌入或表示与教师模型的表示相似。研究表明,与标准知识蒸馏相比,这种对比表示蒸馏能产生更清晰、更相关的行为。

压缩 vs. 蒸馏

压缩和蒸馏都能产生与大型模型精度相似的小型模型。

  • 蒸馏:使用SGD训练一个小模型以匹配大模型的特性。训练过程计算成本可能较高。
  • 压缩:使用剪枝或量化等技术来降低大型模型的计算成本。从计算角度看,这可能更高效。

有时人们会结合使用这些方法,例如“最优大脑压缩”工作就同时进行了量化和剪枝。如果允许模型在压缩时专门化到特定分布,你甚至可以进一步缩小模型并保持精度。


基础模型 🏗️

最后,我们来探讨基础模型。“基础模型”这个术语由斯坦福大学计算机科学系提出,旨在为这种非常有用的迁移学习模型建立一个语义概念。

基础模型是指那些非常通用、对许多不同下游任务都非常有效的模型。随着基础模型变得越来越通用,你需要对它们进行微调的需求也越来越少。如果模型不需要微调,它的可访问性就更高。像ChatGPT、CLIP或大型图像生成模型这样的模型,对于绝大多数用户感兴趣的事情,都可以直接使用,无需重新训练。

基础模型的规模与成本

模型的规模随着时间的推移呈指数级增长。这些大型模型往往具有更好的泛化能力。然而,训练这些模型需要巨大的计算资源、资金和能源。

气候成本是一个重要考量。训练大型模型会产生大量的碳排放,其影响很大程度上取决于数据中心能源的可持续性。此外,水资源消耗也是一个严峻问题。这些资源的使用与模型的受益者之间存在复杂的伦理关系。

如何使用基础模型?

基础模型通常在海量数据上训练,生成各种类型的模型,其中许多可以直接使用。现在,我们希望用很少甚至没有数据来使用或适应这些基础模型以解决新问题。

这催生了“提示工程”这一新范式。你不再进行传统的微调,而是通过设计巧妙的输入(提示)来引导模型执行任务。

提示工程示例

  • 语言模型:例如,给GPT-3一个提示:“这是一条评论:[评论内容]。这条评论的情感是:____”,模型会填空。你还可以进行上下文学习,即提供几个输入-输出示例,然后让模型对新输入进行预测。
  • CLIP模型:CLIP是一个学习了文本和图像之间语义相似性的对比学习模型。你可以将其用作表示学习模型(在其图像编码上构建线性分类器),也可以直接进行“零样本”分类:计算图像编码与“一张狗的照片”等文本编码之间的余弦相似度。

然而,这些模型对提示非常敏感和脆弱。不同的措辞可能导致完全不同的结果。

改进提示的方法

  • 软提示:优化一个小的、连续的、任务特定的向量(提示嵌入),而冻结语言模型参数。
  • 思维链提示:在提示中展示带有推理过程的答案示例,引导模型在回答时进行逻辑推理。
  • 视觉提示:对于图像模型,可以学习在图像上添加一个最优的“边框”或扰动,以最大化模型正确分类的概率。甚至存在一些奇特的研究,例如在卫星图像的固定位置添加一个特定的粉色像素,就能提升所有类别的分类准确率。这揭示了模型底层统计表示的脆弱性和复杂性。

组合基础模型

你还可以考虑将不同的基础模型组合起来,构建模块化系统。例如,文本到图像生成可以结合VQ-GAN(图像生成器)和CLIP(文本-图像相似性),通过优化一个潜在空间向量来生成与文本提示最匹配的图像。


总结 📚

本节课中,我们一起学习了迁移学习的核心概念。我们探讨了如何利用少量数据进行学习,并迁移不同形式的知识:

  1. 迁移映射知识:通过微调,将预训练模型学到的表示应用于新的下游任务,并讨论了避免灾难性遗忘的策略和领域适应技术。
  2. 迁移输出知识:通过知识蒸馏,训练一个学生模型来模仿教师模型的输出分布或内部表示,以实现模型压缩或知识传递。
  3. 基础模型与提示工程:介绍了强大的、通用的基础模型,以及如何通过提示工程上下文学习等技巧,以少量数据或无数据的方式引导这些模型解决新任务。

迁移学习是深度学习赋能小样本学习的关键,它使我们能够站在巨人的肩膀上,更高效地构建和应用AI系统。

019:数据

在本节课中,我们将完成关于迁移学习的简短系列。我们将重点讨论如何将关于系统输入的知识进行迁移。我们将花时间思考生成模型如何作为捕获数据输入信息的另一种方式。然后,我们还将简要讨论“学会学习”的模型,即元学习的概念,以及如何构建本质上擅长迁移学习的模型。

生成模型作为数据增强

上一节我们介绍了迁移学习的基本概念,本节中我们来看看如何利用生成模型来增强数据。

如果你有一个数据集,你可以使用生成建模技术来学习如何从同一分布中生成数据。这让你获得一个能够从该分布中采样的模型。

乍一看,这似乎并不有趣:你已经有数据,然后训练了一个模仿这些数据的模型。但生成模型的有趣之处在于,如果模型足够好,你可以将其视为一种超越初始训练数据集能力的新数据访问机制。你可以称之为“数据++”。生成模型是一种非常酷的数据压缩形式。例如,Stable Diffusion模型将人类的视觉信息压缩到几GB的文件中,这与互联网上所有图像的存储容量相比,差异显著。

以下是“数据++”可能带来的一些操作:

  • 插值:你可以在两个不同的数据集或生成器之间进行插值,生成新的数据对象。
  • 操作:你可以向潜在空间添加偏置项,从而以新的方式访问和交互数据。
  • 组合:你可以将不同的数据对象真正组合起来,并以有趣的方式操作它们。
  • 优化:你可以明确地找到一组最优的生成器、数据、潜在空间和逆生成器,以适应特定的最优性维度。

这些操作可以应用于图形学、可视化、数据增强和反事实推理等多个领域。

潜在空间与控制

现在,让我们更具体地看看生成模型如何实现控制。

考虑一个生成模型 G,它从一个潜在变量 Z 映射到图像 x。通常,我们会约束 Z 服从正态分布。通过在这个潜在空间中采样不同的点,你可以合成不同的图像。

给定潜在空间中移动的维度与生成图像之间的关系,你可以将其视为一种控制机制。例如,你可能会在潜在空间中找到一些维度,这些维度对应于生成图像中某些解耦的变化因素,比如鸟的姿态或方向。

在实践中,对于某些类别的生成模型,确实可以找到这样解耦的维度,但这通常很困难且具有启发性。

反事实推理与数据增强

如果你有这个从数据到“数据++”的模型,并且可以将实际图像映射回潜在空间(即逆映射 G⁻¹),那么你现在就有能力提出有趣的反事实问题。

例如,你可以探索潜在空间中的不同维度,看看它们如何对应真实图像流形中的不同变化。有研究表明,通过在潜在空间中某个输入图像的邻域内移动,生成该图像的不同姿态或方向的变体,然后对这些变体进行集成预测,可以提高分类器的准确性和鲁棒性。这就像是在生成器内部进行测试时数据增强。

但这里有一个重要的假设:在图像空间中相近的点应该属于相同的类别,不会显著跨越类别边界。这个假设的有效性取决于你的具体任务。例如,在区分猫的品种时,耳朵长度的变化可能就会跨越类别边界。

发现潜在空间中的变化维度

那么,如何发现潜在空间中有趣的变化维度呢?一种方法是通过实验:例如,你拍摄一张白天的图像和一张夜晚的同一场景图像,计算它们在潜在空间中对应的点之间的方向向量,这个向量就对应了“从白天到夜晚”这种特定的反事实变化。

通过这种方式,你可以发现诸如缩放、平移、亮化、暗化等与输入无关的变化维度。但这也揭示了数据中的偏见,例如,当试图让火山图像变暗时,模型可能会让它喷发,因为训练数据中黑暗的火山照片很可能正在喷发。

这种解耦的潜在空间变化维度在许多模态中都有发现,例如在词嵌入模型中,存在一个方向可以改变单词的时态(如 swimming -> swam)。

在潜在空间中进行插值

如果我们考虑在数据空间中进行简单的线性插值,例如在一只鸟和一只苍蝇的图像之间,中间结果看起来很不自然,不符合真实图像的统计特性。

相反,如果我们有一个行为良好的潜在空间,任何点都能映射到真实图像流形上。那么,在潜在空间中进行线性插值,将对应于数据流形空间中的非线性插值,并能确保整个插值路径都落在真实图像流形内。例如,使用BigGAN模型进行插值,结果看起来更自然,尽管可能产生一些奇怪的混合体(如半鸟半蝇)。

然而,这个初始的 Z 空间可能不是组织数据以进行插值的最佳方式。在训练变分自编码器(VAE)时,数据分布映射到潜在分布,再映射回数据分布。潜在空间的训练过程可能像将东西揉成一团,导致潜在空间中相近的点在数据空间中可能相距甚远。因此,线性插值可能会跨越“接缝”,产生不自然的跳变。

有研究发现,使用生成模型中间层的表示(如StyleGAN中的 W 空间)进行插值,可能比在初始 Z 空间更好,因为它减少了扭曲,能更干净地分离变化维度(如汽车的车盖样式、大灯类型、车身颜色),从而得到更真实、更友好的结果。

为生成数据添加标签

现在,我们来讨论如何为生成的数据添加标签。假设你用于训练生成模型的原始数据是有标签的。那么,在生成模型的流形中,彼此接近的点应该在语义上相关。因此,如果你在流形上没有移动太远,可以认为类别应该保持不变。

这是“数据集GAN”论文所探索的。他们在一个数据集(如汽车)上训练了一个StyleGAN,然后想解决一个相关的新任务(如汽车的语义分割)。他们利用StyleGAN,仅用少量手工标注的示例,定义了一个额外的“标注臂”。该标注臂利用学到的、结构良好的潜在空间特征,可以生成大量带有(弱)标签的图像。然后,他们可以在这些合成数据上训练一个部件分割模型,并在真实数据上测试,证明这是有益的。

具体来说,他们将带有像素级标签的原始数据通过网络映射到那些保持空间关系的中间特征,然后训练一个轻量级的预测模型,将这些特征映射到部件标签。结果表明,一张带标签的生成图像,在达到相同准确率的前提下,其训练效率相当于约100张带标签的普通图像。

如今,对于语义分割,许多人会从“SAM”(Segment Anything Model)这样的基础模型开始,它利用自监督技巧构建了大规模数据集,并学习了可泛化的、鲁棒的表示。

生成模型与可解释性

生成模型和“数据++”方法还可以帮助我们解释模型决策,提高可解释性。

在标准分类中,模型输入图像并预测类别(如“猫”)。现在,我们可以利用在“数据++”空间中探索反事实的能力,来理解为什么图像被分类为猫。

你可以将猫的图像映射到“数据++”空间(通过 G⁻¹G),然后观察在潜在空间中扰动哪些维度会最大程度地改变模型的预测类别。这可以帮助你理解图像的哪些特征最被模型识别为感兴趣类别。

例如,StyleX方法试图找到最能影响模型预测类别的Top-K个风格空间方向。结果显示,张开嘴巴是使预测从“猫”变为“狗”的一个强维度(因为狗更常喘气),改变瞳孔大小或耳朵尖度也会影响模型对“猫”的判断。这揭示了模型从数据中学到的偏见。

你也可以生成针对特定类别的解释。例如,对于一个感知年龄分类器,你可以探测出哪些维度变化会影响预测年龄,比如更浓的眉毛、更浅的肤色对应更年轻的年龄,添加眼镜或变成灰发则对应更年长的年龄。这些维度可能捕捉到与模型公平性相关的偏见。

在科学和医学数据中的应用

我们可以将这种可解释性机制应用于科学或医学数据。例如,在视网膜眼底图像分类中,使用生成模型来理解如何最大程度地改变图像以对应特定疾病的预测。生成的解释性特征与医生用于诊断的细微特征相符,这有助于建立对模型的信任,因为它表明模型没有学习到我们不希望的相关因素。

然而,这些方法的一个主要限制是,你需要手动发现潜在空间的变化维度并分析其含义,这需要大量努力。

最近的工作尝试使用基于文本条件的现代扩散模型来进行类似的反事实推理。文本控制提供了简单可解释的操作机制,使得测试反事实假设变得更加高效。例如,通过文本提示生成可能的反事实图像,并定量测试模型在这些反事实上的性能,这是一种与数据集交互、探索底层偏见的有效方式。

生成数据与表征学习

生成数据是否对表征学习也有用呢?这个问题比较复杂。

有一种观点认为,在数据上训练的生成模型,可能捕获了与数据相同甚至更多的信息,这可能对表征学习有用。例如,与之前在GAN中使用多视图集成类似,你可以从不同视角生成同一真实图像,并将其作为对比学习模型的输入。

这里,你不是使用基于启发式规则的数据增强(如随机裁剪、翻转)来构建正样本对,而是在潜在空间中围绕真实图像采样,生成同一事物的不同“潜在视图”作为正样本对。这引入了多样性,但关键在于在潜在空间中移动多远仍能保持相同类别,这通常作为一个超参数进行调整。

研究表明,与仅使用标准数据增强相比,这种深度生成视图可以改进对比学习。如果生成模型质量很高,其性能甚至可能超过从真实数据中学习。但这要求生成器必须是高质量的,而对于许多不常见的事物,这很难实现。

生成模型的局限性:多样性 vs. 保真度

生成模型在多样性和保真度之间往往存在权衡。对于训练信号而言,知道两个差异较大的事物实际上是同一类,可能比知道两个几乎相同的事物是同一类更有用。然而,生成模型很难在保持高保真度(生成物看起来真实且属于正确类别)的同时,实现高度的多样性(如不同的姿态、场景结构)。

此外,生成模型通常更擅长改变纹理而非形状,这表明它们对形状有很强的先验,难以打破。例如,让模型生成一只“短喙的巨嘴鸟”非常困难,而让它生成一个“毛茸茸的棒球”则相对容易。

“数据++”总结与开放问题

总而言之,“数据++”理念认为,你可以从一个隐式生成模型中采样,并将其用作获取具有额外功能数据的机制。你可以定义其上的操作(插值、外推、操作、组合、优化),并可能高效地用于标注数据。理论上,任何你能对常规数据做的事情,都可以尝试用这种生成增强数据来做,并且可能效果更好。

然而,这个领域仍有许多研究需要深入:

  1. 我们需要能够用更少数据学习的生成器。
  2. 我们需要更好地理解这些模型是如何学习的,以及其中的偏见。
  3. 我们需要在多样性和保真度之间找到更好的平衡。
  4. 需要警惕“模型崩溃”风险,即如果使用过多生成数据训练模型,可能导致真实数据分布中的部分信息丢失。

元学习:学会快速学习

最后,我们来谈谈“学会学习”,即元学习。其核心思想不是仅仅寻找一种迁移学习的机制,而是学习一个本身擅长迁移学习、能够快速适应的模型。

经典的元学习工作如MAML(Model-Agnostic Meta-Learning)。它试图学习一个初始化参数,使得只需几步随机梯度下降(SGD)就能轻松适应新任务。算法上,这涉及一个双循环优化:内循环在特定任务上进行几步SGD,外循环根据内循环后的损失更新元初始化参数。这需要反向传播穿过内循环的梯度更新步骤,计算上可能非常昂贵。

尽管MAML在理论上很优雅,并在简单问题上显示出优势,但其额外的计算成本常常使得在实践中不如直接在大量数据集上预训练然后微调有效。不过,一些研究通过引入任务特定的条件(如地理空间嵌入)在元学习目标中,显示了在特定应用(如跨国作物分类)上的有效增益。

另一种视角是将元学习视为序列建模。例如,在大型语言模型的上下文学习中,模型通过观察一系列输入-输出对(示例),学会在给定新输入时预测输出。这可以看作是一种大规模的元学习,其中模型的隐藏状态学会了根据序列的统计信息来快速适应新任务。与MAML这类需要内外循环的方法相比,基于序列建模的元学习可能动机相似,但在实际输出和扩展性上更具优势。

课程总结

本节课中,我们一起学习了生成模型作为数据的概念及其优缺点。我们探讨了如何利用生成模型进行数据增强、反事实推理、可解释性分析和表征学习。同时,我们也介绍了元学习的基本思想,即让模型学会如何快速适应新任务。这些都是迁移学习领域中深入且有趣的方向。

020:缩放定律 📈

在本节课中,我们将要学习深度学习中的“缩放定律”。缩放定律描述了模型性能(如测试损失)如何随着计算资源、数据量或模型参数数量的增加而可预测地变化。理解这些规律对于高效分配资源、规划未来模型能力以及选择最优模型配置至关重要。


缩放定律的重要性 🤔

上一节我们介绍了缩放定律的基本概念,本节中我们来看看为什么我们需要关心深度学习中的缩放问题。

深度学习在拥有大规模资源时表现尤为出色,这些资源包括大量数据、强大的计算能力(FLOPS)以及庞大的模型参数量。深度学习成功的一个关键部分在于如何有效地扩展这些资源以充分利用它们。

一个值得注意的现象是,模型在扩展过程中的行为有时是可预测的。因此,我们可以定义缩放定律,来预测诸如“如果我将计算量翻倍,模型性能会如何变化”或“如果我将模型规模翻倍,结果会怎样”等问题。如果我们能识别这些规律,将对我们规划资源和预测未来模型能力非常有帮助。

例如,下图展示了一个缩放定律的实例,它显示了随着时间推移,用于训练模型的计算量呈指数级增长。


缩放定律的动机:理查德·萨顿的“苦涩教训” 📖

研究缩放定律的另一个重要动机,源于理查德·萨顿的一篇极具影响力的文章《苦涩的教训》。

萨顿作为一名长期从事人工智能研究的人员,观察到我们大多数巧妙的算法和想法最终并未取得预期效果。而唯一能系统性地取得成功的,是那些设计简单但扩展性良好的算法。

他的核心观点是,为了识别最终会胜出的模型,必须考虑模型性能如何随资源扩展。因为随着时间的推移,扩展性更好的模型终将胜出。这篇文章促使我们严肃对待模型的扩展性问题。

当然,关于算法如何随资源扩展的思考并非新事物,它可以追溯到计算机科学的基础,例如算法分析中的大O符号(Big O notation),就是一种描述算法时间复杂度随输入规模变化的缩放定律。


核心问题:如何最优分配计算预算? 💡

缩放定律研究的一个基本问题是:在给定计算预算(硬件资源)的情况下,如何最优地分配这个预算?具体来说,我们应该训练多大的模型,使用多少数据,才能最有效地利用预算?

下图展示了OpenAI用于决定如何训练GPT-4模型的另一个缩放定律。横轴是计算量(FLOPS),纵轴是模型性能(以比特/词衡量,与下一词预测的交叉熵相关,数值越低表示预测越准确)。

他们采用的策略是:在小规模上(例如最终规模的1/100或1/1000)进行大量的超参数搜索和模型比较,拟合出性能随计算量变化的曲线,然后外推这条曲线到他们最终要训练的大规模上。这样,他们可以预测最终模型的性能,并只在大规模上训练一次选定的最佳模型,从而节省大量成本。


缩放定律的普遍形式:幂律关系 📊

缩放定律研究的一个主要发现是,测试损失与关键资源(如数据量、参数量或计算量)之间通常存在幂律关系

幂律关系的一般形式可以表示为:

L(X) = A / X^α + B

其中:

  • L 是测试损失。
  • X 是资源(如数据量 D、参数量 N 或计算量 C)。
  • A, B, α 是常数,取决于任务、架构和数据分布。

这意味着,如果将资源 X 翻倍,损失大约会降低到原来的 1/(2^α)。例如,如果 α = 1,资源翻倍,损失减半;如果 α = 0.5,资源翻倍,损失降至原来的约 1/√2

以下是针对不同资源拟合出的幂律关系示例:

  • 数据量 (D):测试损失随训练数据量的增加而遵循幂律下降。
  • 参数量 (N):测试损失随模型参数量的增加而遵循幂律下降。
  • 计算量 (C):当模型参数量和数据集大小都针对每个计算水平进行最优调整时,所能达到的最佳性能前沿也遵循关于计算量的幂律。


模型规模与数据规模的协同缩放 🔄

上一节我们看到了单个资源的幂律关系,本节中我们来看看模型规模和数据规模如何相互作用。

一个重要的发现是,为了观察到持续的幂律改进并达到最佳性能,必须协同缩放模型规模和数据规模。如果模型太小,即使增加大量数据,性能也会很快饱和,因为模型没有足够的容量来从额外数据中学习。

因此,测试损失 L 可以建模为同时关于参数量 N 和数据量 D 的函数。经验拟合的公式可能如下所示(具体形式可能因研究而异):

L(N, D) = [ (N_c / N)^α_n + (D_c / D)^α_d ]

这个公式表明,损失由模型容量不足和数据不足两部分组成,需要同时扩大两者以持续降低损失。


最优分配策略 🎯

基于对缩放定律的拟合,我们可以推导出在固定总计算预算 C 下,如何最优分配资源以最小化测试损失。

根据Kaplan等人(2020)的经典研究,他们得出的结论是:

对于给定的计算量 C,最优的模型参数量 N 和数据量 D 应满足:

  • N ∝ C^0.73
  • D ∝ C^0.27

这意味着,应将大部分计算预算分配给增加模型参数,而非单纯增加数据量。换句话说,最优策略是训练一个相对较大的模型,但使用相对较少的数据(在单轮训练的意义上)。


缩放定律的普适性与验证 🌍

缩放定律不仅存在于自回归Transformer语言模型中,在计算机视觉、强化学习等领域也被广泛观察到。

一个关键问题是:这些基于小规模数据拟合的幂律关系,能否有效外推预测更大规模的行为?答案是肯定的。研究通过“留出”验证表明,基于部分规模数据拟合的定律,能够准确预测在未见过的、更大或更小规模上的模型性能。这证明了缩放定律并非过拟合的“数字游戏”,而是具有实际预测能力的经验规律。


总结 📝

本节课中我们一起学习了深度学习中的缩放定律。

我们了解到:

  1. 缩放定律描述了模型性能(如测试损失)与关键资源(计算量、数据量、参数量)之间可预测的幂律关系
  2. 理解这些定律对于高效分配资源规划模型发展选择最优模型配置至关重要。
  3. 理查德·萨顿的“苦涩教训”强调了设计扩展性良好的简单算法的重要性。
  4. 为了达到最佳性能,需要协同缩放模型规模和数据规模
  5. 在固定计算预算下,最优策略往往是优先扩大模型规模,其次才是数据规模。
  6. 这些幂律关系在多种任务和架构中普遍存在,并且能够通过外推进行有效预测。

掌握缩放定律,能帮助我们在资源有限的情况下做出更明智的决策,并更好地预见深度学习发展的未来轨迹。

021:P21-语言模型 👨‍🏫

概述

在本节课中,我们将跟随麻省理工学院的Jacob Monrez教授,学习关于大语言模型的核心知识。我们将从语言模型的基本定义和历史讲起,逐步深入到现代大语言模型的训练范式、核心能力(如上下文学习、思维链推理)以及关键的优化技术(如指令微调与人类反馈强化学习)。


什么是语言模型?🤔

上一节我们介绍了课程背景,本节中我们来看看语言模型的基本定义。

语言模型的核心任务是:给定一个词序列(或标记序列)作为输入,预测下一个可能出现的词的概率分布。用公式可以表示为:

P(下一个词 | 已输入的词序列)

如果你拥有这种预测下一个词的能力,那么通过链式法则,你可以将其转化为对整个字符串序列的分布,从而可以从中采样以生成语言,或完成其他各种任务。

这个“根据前面的词预测下一个词”的基本问题,在语言处理领域有着悠久的历史,最早可以追溯到二战时期用于解码的语境中。


语言模型的发展历程 📜

上一节我们定义了语言模型,本节中我们来看看它的发展历程。

在很长一段时间里(大约从20世纪60年代到21世纪初),人们主要通过在大规模文本语料库中计数来估计这种概率分布。例如,统计“once upon”后面出现“a”和“time”的频率。这种方法生成的文本看起来像自然语言,但能力有限。

大约在21世纪初,人们开始尝试用神经网络来近似这个分布。最初,神经网络模型仅比基于计数的大型模型在预测任务上表现略好一点。随着深度学习工具包的进步,特别是Transformer架构的出现,预测能力得到了显著提升。

以下是推动语言模型发展的几个关键因素:

  • 更好的预测架构:从计数模型到神经网络,再到Transformer。
  • 更大的数据集:用于训练的数据量急剧增长。
  • 更多的参数:模型规模变得越来越大。

需要指出的是,模型架构的选择至关重要。历史表明,即使参数数量相同,不同的架构(如神经网络对比计数模型)也会带来截然不同的性能表现。神经序列模型所具备的归纳偏置,是此前几代语言模型所没有的。


语言模型的训练范式演变 🔄

上一节我们回顾了发展历程,本节中我们来看看训练范式是如何演变的。

在2014年之前,语言模型通常只是大型流水线系统(如自动语音识别或机器翻译)中的一个组件,用于对候选输出进行重排序,判断其合理性。

大约从2014年开始,范式发生了转变。人们意识到,可以直接使用语言模型来完成感兴趣的任务本身。例如,将机器翻译任务重新表述为一个“给定法语序列和已生成的部分英语序列,预测下一个英语词”的问题。语言模型开始被用于序列生成。

2017年左右,预训练与迁移的范式变得流行。其核心思想是:首先在大规模无标签文本数据上训练一个通用的语言模型(预训练),然后使用少量特定任务的数据对这个模型进行微调,使其适应下游任务(如翻译、摘要)。这减少了对大量下游任务数据的需求。

如今,我们进入了预训练与提示的时代。在这个范式下,我们甚至不需要或只需要极少的任务特定数据来调整模型参数。我们依靠一个极其强大的通用下一个词预测系统,并通过精心设计的输入提示(Prompt)来引导它完成各种任务。


掩码语言建模与生成式语言建模 🎭

上一节我们介绍了训练范式的演变,本节中我们具体看看两种重要的预训练任务。

最初,一种流行的预训练任务是掩码语言建模。其基本流程如下:

  1. 取一段文本作为输入。
  2. 随机掩盖其中一些词。
  3. 要求模型根据周围的上下文预测被掩盖的词。

这种方法不需要任何标注数据,只需大量文本。训练出的模型能学到大量关于语言结构和世界知识的信息,其内部表征对下游任务非常有用。然而,这种模型通常无法直接用于文本生成,因为它在训练时已知句子的总长度,且掩盖的词多在句子中间。

为了获得能够生成文本的模型,我们需要转向生成式语言建模,即训练模型根据前面的所有词来预测下一个词。这类模型(如GPT系列)经过大规模预训练后,不仅可以作为下游任务的起点进行微调,其内部表征也同样有用。


涌现的能力:上下文学习与指令遵循 🚀

上一节我们区分了两种建模方式,本节中我们看看生成式语言模型在达到一定规模后展现出的惊人能力。

当模型参数和数据规模达到一定程度后,人们发现,即使不进行任何额外的微调,这些模型也能完成许多令人惊讶的任务,例如:

  • 进行算术运算。
  • 执行需要专业语言学知识的任务(如音标转录)。
  • 进行多语言任务。
  • 回答关于物理世界和常识的问题。

这种仅通过改变输入(即“提示”)就能让模型执行特定任务的方式,主要分为两类:

1. 上下文学习
其核心思想是:通过提供少量任务示例(输入-输出对),让模型理解当前需要执行的任务格式,并据此对新输入做出响应。
例如,对于情感分析任务,我们不是直接问“这部电影很糟糕”的下一个词是什么,而是构造一个包含示例的上下文:

我喜欢它。正面。
最差的电影。负面。
这部电影很糟糕。

模型会基于这个上下文,推断出下一个词应该是“负面”。

2. 零样本指令遵循
我们不给模型提供示例,而是直接给出任务的自然语言描述(指令)。
例如,输入:“将以下电影评论分类为正面或负面:这部电影很糟糕。

起初,人们认为上下文学习可能只是帮助模型“检索”出在预训练中学过的任务,而非真正的学习,因为模型对示例中的标签是否正确并不敏感。但随着模型规模进一步扩大,现代大模型确实展现出了在上下文环境中进行某种形式“学习”的能力,能够关注输入与标签之间的关联。


思维链提示:解锁复杂推理 🧠

上一节我们看到了模型处理分类等任务的能力,本节中我们探讨如何让模型解决更复杂的推理问题。

对于复杂的数学或逻辑推理问题,直接要求模型给出最终答案往往效果不佳。因为模型在读取完问题后,必须在生成第一个输出词之前,在内部完成所有计算,这计算复杂度很高。

人类在解决此类问题时,会进行逐步推理。受此启发,思维链提示应运而生。其做法是:在提供给模型的示例中,不仅包含问题和最终答案,还包含得出答案的中间推理步骤。

例如:

问题:Roger有5个网球。他又买了2罐网球,每罐有3个。他现在总共有多少个网球?
推理:Roger一开始有5个球。2罐网球,每罐3个,总共是2*3=6个新球。5个球加上6个新球等于11个球。
答案:11

当模型看到这种示例后,对于新的问题,它也会首先生成一步步的推理过程,然后给出最终答案。这显著提升了模型在复杂任务上的表现。

令人惊讶的是,有时甚至不需要提供详细的推理示例,只需在问题前加上一句简单的指令,如“让我们一步步地思考。”,也能大幅提升模型的推理性能。


指令微调:对齐模型行为 🎯

上一节我们学习了如何通过提示引导模型推理,本节中我们看看如何在训练阶段就让模型更好地遵循指令。

尽管大规模预训练模型具备多种能力,但要让它们可靠地遵循用户的自然语言指令,通常还需要一个专门的指令微调阶段。原因在于,互联网上的文本并非总是“指令-回答”的格式。模型可能将指令误解为需要续写的普通文本。

指令微调的做法是:收集大量(指令,期望回答)配对的数据集,然后在这个数据集上对预训练好的模型进行有监督的微调。这教会了模型默认将输入解释为需要遵循的指令,并生成相应的回答。

指令微调数据可以通过多种方式构建:

  • 将现有NLP数据集(如翻译、摘要)重新格式化为指令形式。
  • 人工编写多样化的指令和回答。
  • 利用模型自身生成指令,再由人工标注回答。

研究表明,模型规模指令微调之间存在积极的交互作用:只有当模型足够大时,指令微调才能激发出强大的通用指令遵循能力。


人类反馈强化学习:优化模型输出 ✨

上一节我们通过指令微调让模型学会了遵循指令,本节中我们探讨如何进一步优化模型的输出质量和安全性。

仅靠指令微调可能不足以解决所有问题:

  1. 模型应知道何时说“我不知道”:对于超出其知识范围或无法回答的问题,模型应诚实回应,而非胡编乱造。
  2. 对齐人类价值观与安全准则:模型应拒绝回答有害、危险或不道德的请求。
  3. 处理主观性或多样正确答案的任务:例如文本摘要,可能存在多个同样好的摘要。

由于我们无法确切知道模型“知道”什么,因此很难通过有监督学习直接针对上述情况提供精确的“正确回答”。基于人类反馈的强化学习(RLHF)是解决这一问题的关键方法。

RLHF通常包含三个主要步骤:

  1. 收集人类偏好数据:给定一个输入(如一个问题),让模型生成多个不同的回答。然后请人类标注员对这些回答进行排序或打分(例如,哪个更好/更差)。
  2. 训练奖励模型:使用上一步收集的偏好数据,训练一个单独的“奖励模型”来学习人类的评判标准,使其能够对任何(输入,模型回答)对给出一个质量分数。
  3. 优化语言模型:利用训练好的奖励模型作为指导,通过强化学习算法(如PPO)优化原始语言模型,使其生成的回答能获得更高的奖励分数。

近年来,也出现了一些无需训练独立奖励模型的方法(如DPO),它们能直接利用偏好数据来优化语言模型。


总结 🎓

本节课中,我们一起学习了关于大语言模型的核心知识。我们从语言模型的基本定义和历史出发,追溯了其从基于计数的模型到现代神经网络的演变过程。我们深入探讨了关键的训练范式转变:从任务特定模型,到预训练-微调,再到如今的预训练-提示范式。

我们重点分析了现代大语言模型涌现出的核心能力:

  • 上下文学习:通过少量示例让模型理解并执行新任务。
  • 指令遵循:通过自然语言指令与模型交互。
  • 思维链推理:通过引导模型进行逐步推理来解决复杂问题。

最后,我们了解了为了使模型更安全、更可靠、更符合人类期望而采用的关键技术:

  • 指令微调:让模型更好地理解并遵循指令。
  • 基于人类反馈的强化学习:根据人类偏好优化模型的输出行为。

大语言模型代表了建立在深度学习之上的一种新范式,它通过海量数据训练和精妙的后续调整,获得了极其灵活和强大的通用任务处理能力。理解其原理和发展脉络,是深入探索当前人工智能前沿领域的基础。

022:模块化视角与优化 🧱

在本节课中,我们将学习一种理解深度神经网络的新视角:将其视为由“乐高积木”般的模块化组件构建而成。我们将探讨如何为每个模块定义理论属性(如Lipschitz连续性),以及如何通过组合规则将这些属性扩展到整个网络。这种视角不仅有助于构建统一的理论框架,还能指导我们设计更稳定、更高效的优化算法。

模块化理论:构建神经网络的“乐高积木”

上一节我们介绍了模块化视角的核心思想。本节中,我们来看看如何将其形式化。

我们称这种理论为“模块理论”。在编程中,有一种“组合子模式”,其思想是通过组合简单事物来构建复杂结构。对我们而言,抽象类型被称为“模块”,它包含输入、权重和输出。这可以是一个单独的神经网络层,也可以是整个神经网络,两者都是模块。

给定两个模块,我们有组合规则。一种是“组合”,类似于串联。另一种是“拼接”,类似于并联。这就像乐高积木,可以上下堆叠或并排放置。

实际上,通过这些简单的二元组合规则,足以构建你想要的任何神经网络。例如,你可以通过并联三个模块,再与一个加法模块组合来实现加法。标量乘法可以通过与一个特殊的标量乘法模块组合来实现。你可以组合这些操作来构建残差块,进而通过多次组合自身来构建残差网络。

模块的类型与核心属性

基于这种构建方式,我们对不同类型的模块进行了分类。

以下是模块的主要类型:

  • 原子:我们给简单层起的名字,例如线性层、卷积层、嵌入层。
  • :指那些具有平凡权重空间的模块,例如非线性激活函数没有权重。函数式注意力(不带权重的注意力部分)也属于此类,你可以将其与一些线性模块组合来构建实际的注意力模块。
  • 复合物:我们给任何通过组合构建的模块起的名字。

我们真正关心或想知道的是敏感性信息:如果我改变输入和权重,输出会改变多少?我们能否预测输出的变化量?我们能否对任何模块做到这一点?假设我们可以对基本构建块做到这一点,我们能否对任何复合模块或任何由我们的“乐高积木”构建的东西做到这一点?

定义“良范数”模块

为了使理论严谨,我们与纯数学家合作,以相当抽象和形式化的方式解决了这个问题。我们提出了以下定义。

首先,一个模块是具有输入、权重和输出的东西。但我们还赋予它一些元数据:它的前向函数、一个敏感性数值(用于衡量输入-输出敏感性,即Lipschitz常数之一)、一个质量参数(用于设置该模块相对于其他模块的重要性,即它对学习的贡献程度),以及权重空间上的一个范数。任何具有这些属性的东西都是一个有效的模块。

但在我们的库中,我们不希望有非常糟糕的函数,我们希望有人们可以使用的“好”函数。因此我们有了第二个定义:良范数模块。这些是我们实际喜欢使用并希望包含在库中的“好”模块。

对于一个模块是良范数的,它需要在其输入空间和输出空间上有一个范数的概念。这大致意味着你想知道输入如何行为,以及你希望输出如何行为。然后我们基本上说,一个模块是良范数的,如果它在权重上是Lipschitz的,并且在输入上是Lipschitz的,其中分配给模块的敏感性数值就是Lipschitz常数。

我们这样思考:当我们说一个乐高积木有与之相关的理论时,基本上意味着这个乐高积木具有这些属性。第二个定义说明这些属性是基于彼此之间的约束。如果乐高积木满足这些约束,我们会把它放入库中;如果不满足,我们就把它扔掉,因为我们不喜欢它。

实例:线性模块与嵌入模块

我想给你一些实际的例子,看看这到底是什么样子。

首先是线性模块,最经典的模块。它执行矩阵向量乘法。我们说它的敏感性为1,质量为1。我们赋予它这种特殊的范数:一种重新缩放后的谱范数。然后我们需要问,这个模块是良范数的吗?我们是否想把它放入库中?我们的主张是,如果你将输入和输出范数设置为RMS范数(这是神经网络内部特征向量的一种自然选择),那么在线性模块具有有界输入和有界权重的情况下,它是良范数的。

一个有用的对照点是嵌入模块。乍一看,它似乎和线性模块是同一回事,你可以把它写成矩阵向量乘法。区别在于,在神经网络中,你传入一个独热向量,它选出矩阵的一列,这就是该标记对应的嵌入。因此,嵌入层具有与线性层非常不同的语义。我们通过赋予嵌入模块的权重空间一个不同的范数来表达这一点。同样,当我们问模块是否是良范数时,我们会在输入空间上放置一个不同的范数。我们的思考方式是,嵌入层的输入向量是一个独热向量,有点像概率向量,因此我们赋予它L1范数。基本上,在相同的单位输入和单位权重条件下,选择该输入范数和输出范数,它就是良范数的。

组合规则:保持良范数属性

我们建立这些关于什么是模块、什么是良范数模块的抽象定义的原因是,我们希望拥有能自动保持这些属性的组合规则。如果你从库中取出两个良范数模块并将它们组合,你得到的新东西会自动满足良范数的定义。

因此,我们想要展示的是,组合规则是可结合的。例如,如果你组合一个链式结构,无论你以何种顺序进行这些组合,你都会得到相同的基本对象。我们希望模块空间在组合下是封闭的。我们希望良范数模块空间在组合下是封闭的。然后,我们希望质量参数(那些属性之一)能让我们调整不同模块的相对贡献程度。直观上,可以将其视为如果你想这样做,它给了你调整不同层学习率的自由。

我们提出了这些规则。同样,这些只是定义。这种风格的难点在于提出非常巧妙的定义,之后的一切就变得容易了。定义才是困难的部分。

对于组合模块或将模块串联:

  • 组合前向函数,这看起来很自然。
  • 敏感性(即输入到输出的Lipschitz常数)会相乘,这很直观,因为组合时Lipschitz常数会相乘。
  • 质量需要多思考一下。
  • 重要的是,我们使用最大范数。当我们组合模块时,我们希望在联合权重空间上有一个范数,我们选择带有特殊加权的最大范数。存在系数P和Q,用于加权两个不同子范数在最大值中的贡献。P和Q取决于质量比,这就是质量允许你调整组合中两个不同模块重要性的思想。P的尺度还与第二个模块的敏感性耦合。因此,如果你组合两个东西,但第二个对其输入非常敏感,那么第一个应该知道这一点,因为如果你要改变第一个的权重,你希望知道第二个是非常敏感还是非常不敏感。

拼接模块与此类似,但更简单,因为它们彼此不组合,所以没有耦合。

这些定义的意义在于,你可以进行验证。你取良范数的定义,然后可以证明,如果你组合两个良范数的东西,那么组合结果也是良范数的。因此它们保持了该属性。另一个优点是,你可以将这个理论推广到二阶。你可以定义“锐度”的含义,有点像平滑度的倒数。但你说一个模块是 (α, β, γ) 锐的,如果它在权重范数和输入范数下是Lipschitz平滑的。但因为模块有两个参数,我们可以赋予它三个锐度系数,分别对应于仅改变权重、同时改变权重和输入、仅改变输入。你可以将这些视为广义的顶部特征值。通常,当人们谈论深度学习中海森矩阵的锐度时,其含义是最大特征值有多大。但这是一种欧几里得概念。如果你不相信权重空间的几何是欧几里得几何,那么你就不必关心顶部特征值,而需要某种广义的概念。关键是,如果一个模块是良范数的并且具有这些锐度系数,那么存在一种自动的方法来确定组合和拼接的锐度系数。然后你可以用它来证明整体损失函数是Lipschitz平滑的。

应用一:解决缩放问题

构建了这么多理论,还不清楚它是否有用。接下来的两部分将讨论我们尝试应用这一理论的不同方式。从某种意义上说,该理论也正是为了尝试解决这些问题而设计的。

现在我想谈谈缩放问题。你想扩大训练规模,想让Transformer尽可能大。这是一种自然界的观念,更大的大脑应该更智能。我们有一个实现人工通用智能的“配方”:获取最大的超级计算机,抓取所有数据,然后训练一个超大的Transformer。但问题是,随着你扩大规模,系统的训练属性可能会漂移。以宽度为例,即使网络变得更宽,性能变得更好,但最佳步长(在X轴上)却在漂移。有时,如果在深度上缩放不当,随着网络加深,性能可能会变差。我们想尝试找到解决其中一些问题的方法。

我们的论点是:良好缩放其实并不困难。关键在于真正理解系统的Lipschitz性质,而人们对此并不真正理解。因此,我们的论点是,如果你的网络具有紧致的Lipschitz保证(即不是那种无用的宽松界限),并且如果Lipschitz常数是无量纲的(基本上意味着当你改变系统规模时,Lipschitz常数保持固定,不变化),那么,如果你在相应的范数下规范化更新,就会获得良好的缩放。这更像是一个论点,而非严格的定理。

因此,我们希望整个神经网络具有与缩放维度无关的Lipschitz常数。我们真正希望的是相信这些Lipschitz界限是紧致的,并且紧致程度不依赖于规模。那么,如果我们能做到这一点,那么整体模块的范数(这些界限在其中成立)——如果我们控制权重更新的范数,我们将能够预测输出改变了多少。这就是我们建立这个体系的全部原因之一。换句话说,如果神经网络是1-Lipschitz的(即Lipschitz常数为1),那么1就是一个很好的数字,它是无量纲数字的一个例子。那么,如果我们控制权重更新的范数,我们将能够预测输出变化的范数。

我们实际上构建了这个软件包(仍在开发中),其思想是所有那些规则、我向你展示的一切都是可以编程实现的。你可以实际构建这个库。例如,通过组合层和非线性来构建一个MLP,这个MLP将成为一个复合模块。然后,通过组合,你会自动获得它的前向函数。但你也会获得这个规范化函数。当我们组合东西时,我们会自动在权重空间上构建一个范数。然后我们可以用那个范数来规范化。我们的主张是,由于我们进行了这种巧妙的规范化,训练现在将更好地缩放,而无需调整学习率。你可以将其建立在不同的编程语言之上,例如PyTorch。

一些经验证据表明:我们展示了实际的宽度扫描实验,即增加网络的宽度。左图是训练nanoGPT(类似Coppath的Transformer实现)的结果。如果你尝试改变宽度,最佳学习率会漂移。中间图是我们自己的Transformer实现,它对架构进行了一些调整以使其真正Lipschitz。第三图是在优化之上添加了规范化。关键点在于,通过添加这种规范化,我们可以修复学习率的漂移。

在深度缩放方面,基线nanoGPT性能不算太差,只是有点混乱。中间图(未应用我们的规范化程序)显示Adam在深度缩放方面偶然表现得相当好,而在其上添加规范化后,情况基本保持不变。这很好,因为这正是我们构建整个体系想要实现的目标。我们凭直觉知道如何缩放,然后构建理论来实现它。我们知道这会奏效。

应用二:对偶性与优化加速

我们花费了大量精力构建这个理论框架,现在的想法是,它可能对其他事情也有用。我现在真正兴奋的是对偶性以及我们称之为“模块对偶性”的思想。

这是什么意思?这正是导致训练速度大幅提升的原因之一,也是我们关心它的原因之一。

如果你还记得,我们假设现在我们已经提出了一个范数,使得我们可以界定目标函数线性化中的误差。然后你记得,因为我们在作业中做过,你可以通过求解这个东西的极小值来推导最速下降优化算法。然后你记得,因为你在作业中做过,你可以对偶化这个解。所以那个问题分为两部分:一是评估一个对偶范数,另一个是评估一组单位向量上的arg max。

我们将紫色框大致视为步长。我们可以将第二件事称为对偶映射。这意味着什么?基本上,在更经典的优化理论中,梯度是一个对偶向量,权重是一个常规向量。你对对偶向量和常规向量唯一能做的就是组合它们。基本上,对偶向量是一个线性映射,它接受一个常规向量并返回一个实数。这体现在这里:如果你有一个梯度,梯度真正有用的地方是与权重做点积,并描述函数的线性化。你应该永远只相信梯度告诉你的是函数的线性化。认为你可以直接使用梯度进行梯度下降的想法,通常是一个坏主意,是我们都应该忘记的东西。

所以这就是对偶性的思想。对偶向量,即使它们可能具有相同的维度(技术上你可以对它们进行加减,因为它们具有相同的维度),但这仍然可能是一个坏主意。但我们需要一种程序,一种将梯度这样的对偶向量转换回原始空间的过程,以便我们可以对它们进行加减,但我们不应该直接加减。这就是一个例子。仅仅看这个arg max,你可以将其视为:我想迈出单位范数的一步,以最大化目标函数的线性改进。仅仅从权重中减去梯度可能无法解决这个问题。在非线性优化中,你不能保证梯度加上步长会带来改进(对于足够小的步长,它会带来改进,但可能非常次优,因为它可能无法解决……这里存在一种优化问题,有一个arg max。因此,解决这个问题将满足某种最优性准则。如果你选择了一个好的范数,这就是你真正关心的最优性准则。仅仅沿梯度方向移动,相当于说这是欧几里得范数。而在神经网络中,优化空间极不可能具有欧几里得结构。这就是论据。

这只是一张由优化中其他思想启发的图片:你考虑权重空间。梯度所在的空间,你将其视为一个扭曲的版本。因此,即使梯度空间可能与权重空间具有相同的维度,但生活在梯度空间中的东西可能被拉伸和扭曲了。我们需要某种程序来“反扭曲”它们,然后再从权重中减去。这就是说,除非你非常确定你的空间是欧几里得的,否则你永远不应该再这样做。你应该始终思考:我真正需要仔细考虑的对偶化程序是什么?从某种意义上说,这并不奇怪,因为每个人都会告诉你,如果你要做深度学习,你应该使用Adam等优化器,没有人告诉你要使用普通的梯度下降。所以直观上,我们都知道普通的梯度下降有问题,只是不太清楚问题是什么。

那么,问题来了:我们如何为神经网络提出这样的对偶化程序?如果我们构建了这个递归地诱导整个神经网络权重空间范数的程序,那么也许存在一个相应的递归构建对偶映射的程序。这就是我们想要提出的。思想是:给定单独的层,如果你在单个层的权重上有一个范数,你可以为该层求解一个对偶映射。这就是你们在作业中做的,因为我们说我们将赋予线性层谱范数,然后求解对应于谱范数的对偶映射。一旦我们求解了这些逐层的对偶映射,事实证明存在一种有效的递归过程来组合它们,从而为整个架构提出一个对偶映射。

这将意味着梯度G的对偶化总是有一个范数。因为我们把问题分成了两部分:求解单位向量上的arg max,然后乘以对偶范数。我说过也存在计算对偶范数的递归过程。

你可以将其视为获取梯度并将其投影到单位范数球上,同时也扭曲了方向。但更直观的描述是:你有一些Lipschitz保证和Lipschitz平滑保证,它们告诉你神经网络输出对其权重变化的响应程度。这特别告诉你,网络输出的二阶变化相对于一阶变化何时变得重要。因此,一个合理的做法是采取一个优化步骤,在确保二阶网络效应不主导一阶效应的同时,尽可能地从线性化中榨取“汁液”,因为梯度只告诉你一阶效应。这是一个直观的描述。如果你能为单个层解决这个问题,你如何为整个网络解决它?我只是声称存在一个递归过程来实现这一点。

我想告诉你一些背景,至少是我对此产生兴趣的原因。谷歌的研究人员提出了一种名为Shampoo的优化算法,它在某个行业基准测试的速度挑战中获胜。但如果你读提出这个方法的论文,首先,他们实际使用的方法与论文中的方法完全不同。感觉可能对这个方法在做什么没有完整的理解。所以我想审视这个方法,剥离掉一些人们实际上不用的部分。看起来这个算法的核心原语基本上是通过线性层进行这种有趣的梯度变换。然后意识到这种有趣的梯度变换实际上等同于你们在作业中做的问题。但读论文时,他们并没有那样表述。这很有趣,但它似乎确实能加速训练。

基本上,我们的主张是:隐藏在这个谷歌研究人员非常兴奋的优化算法内部的,就是这个非常简单的原语。因为我们正在研究模块化理论,实现这种算法非常容易,因为我们只需重写线性模块的规范化函数来执行这种谱操作。然后我在一个简单的笔记本中实现了它作为概念验证,实际上可以看到,进行这种谱规范化,比中间那种朴素的谱规范化形式训练得快得多,而两者都比普通的梯度下降好得多。

然后,在开源神经网络速度竞赛社区中,一个人(Keller Jordan)为这种谱对偶化过程制作了一个非常快速的实现。目前,这是在速度竞赛中训练nanoGPT最快的方法。因此,对该技术的担忧(即原则上它可能是正确的做法,但执行这些谱规范化操作成本太高,实际上无用)被证明是多余的。实际上有一种使用迭代方法非常快速地实现这种对偶化程序的方法。

这就是说,当我告诉你我们编写这个模块化包时,我们实际上做错了。当我们说应该规范化时,那实际上并不是最优的。我们应该执行这种对偶化程序,并且它应该基于网络架构递归计算对偶化。我们仍然需要更新包来实现这一点。

总结与展望

总之,我真的很喜欢这个想法:我们应该将构建神经网络视为构建乐高积木,你应该能够构建任何你想要的东西,并且你应该能够对你构建的任何东西有理论上的理解,并且这种理解应该基于你如何组合单个积木的思考。

到目前为止,我们一直在思考这些缩放问题。现在,显然也包括这些训练速度记录。我认为这个想法还没有真正普及,但我认为它可以成为一个更有用的通用思想。如果你想节省能源、金钱和时间,一个好方法是让你的模型精度非常低。但据我所知,没有人对神经网络中的精度应该如何行为有理论上的理解。所有研究都只是尝试各种东西。但直观上,精度和连续性是非常密切相关的概念,因为如果你想要尽可能量化,你需要知道空间在什么离散尺度上是连续的。类似地,经典的对抗鲁棒性问题也应该与对空间连续性属性的良好理解有关。因此,你想将这些问题转化为模块敏感性问题。

我想把这变成一个开源项目。我创建了这个网站,但上面还没有太多信息。

本节课中,我们一起学习了如何用模块化(乐高积木)的视角理解深度神经网络。我们探讨了如何定义“模块”和“良范数模块”,如何通过组合规则(串联与并联)将简单模块的理论属性扩展到复杂网络,并看到了这种理论在解决网络宽度/深度缩放问题以及设计高效优化算法(如谱下降法)中的实际应用。这种视角为构建统一、可组合的深度学习理论框架提供了有希望的路径。

023:深度学习的推理方法 🧠

在本节课中,我们将学习深度学习中一个日益重要的主题:推理方法。我们将探讨如何超越简单的模型前向传播,在测试时利用搜索、优化和自适应技术来获得更优的结果。这些方法正是当前最先进模型(如OpenAI的o1)背后的核心思想。


概述 📋

传统深度学习的重点主要放在训练阶段,即通过大量数据学习模型参数。而在测试或部署阶段,通常只是简单地执行一次模型前向传播来获得预测结果。然而,我们可以做得更多。通过应用搜索算法、优化目标函数或在测试时自适应更新模型,我们可以显著提升模型的性能。本节课将介绍这些先进的推理方法,并解释它们如何成为现代人工智能系统的关键组成部分。


训练与推理的二分法 ⚖️

上一节我们概述了课程内容,本节中我们来看看如何划分人工智能系统的生命周期。我们可以将其分为两个主要阶段:训练推理

  • 训练:也称为学习或统计推断。这是在“工厂”中进行的阶段,即利用数据学习模型参数,使其能够捕捉数据分布或完成特定任务。这包括预训练、微调等。
  • 推理:在机器学习领域,这通常指统计学家所说的“预测”。这是在“部署”环境中进行的阶段,即利用已训练好的模型对新的查询点做出预测、回答问题或生成内容。

这两个阶段并非严格割裂,中间存在许多交叉方法。一个关键的趋势是,行业正从只关注训练时计算,转向在测试时也投入大量计算资源进行更智能的推理。


搜索的力量:从国际象棋到语言模型 ♟️

在深入具体方法之前,我们先理解一个核心概念:搜索。搜索是经典人工智能的基石,它通过系统地探索可能的行动或解决方案空间来找到最佳答案。

一个著名的例子是1997年击败国际象棋世界冠军卡斯帕罗夫的“深蓝”计算机。它本质上没有“学习”,而是通过前瞻搜索,枚举未来许多步的所有可能走法,并选择能带来最佳预期结果的走法。

对于现代的自回归语言模型,简单的贪婪采样(每一步都选择概率最高的下一个词元)并不能保证得到整个序列的联合概率最高的输出。这是因为模型在训练时使用的是“教师强制”,而在推理时是基于自身之前的输出来预测下一个词元,这可能导致分布偏移。

那么,如何获得更好的输出序列呢?以下是几种核心方法:

最佳N采样 🎯

最佳N采样是一种简单而有效的方法。

以下是其基本步骤:

  1. 对于同一个输入提示,使用语言模型独立采样 N 个不同的输出序列。
  2. 对于每个生成的序列,使用链式法则计算模型赋予它的总概率(或对数似然)。
  3. 选择这N个序列中概率最高的那个作为最终输出。

这种方法本质上是拒绝采样,通过增加采样次数来寻找更优解。

束搜索 🔦

束搜索是一种更高效的树搜索方法。

以下是其基本步骤:

  1. 在生成的每一步,保留当前概率最高的 K 个部分序列(称为“束宽”)。
  2. 基于这K个候选序列,分别生成下一个词元的所有可能扩展。
  3. 从所有扩展中再次选出概率最高的K个新序列。
  4. 重复此过程直到生成结束,最终选择完整序列中概率最高的路径。

与独立采样相比,束搜索通过维护多个候选路径,能更系统地在序列空间中进行探索。

思维链 🤔

思维链是一种“涌现的”或“习得的”搜索。它不是运行外部的搜索算法,而是通过特定的提示(如“让我们一步步思考”),引导语言模型在生成的文本中显式地写出推理步骤,最终得出答案。

这相当于语言模型在内部模拟了一个搜索或推理过程。其效果取决于模型是否在训练数据中见过类似的推理模式。


超越似然:使用任意评分函数 🏆

上一节我们介绍了如何搜索更高似然的序列,本节中我们来看看一个更通用的范式:我们可以优化任何我们关心的评分函数,而不仅仅是模型本身的似然。

假设我们有一个自回归语言模型,它能生成多个候选输出。我们可以定义一个评分函数 S(sequence) 来评估每个序列的质量。这个函数可以是:

  • 另一个训练好的模型(例如,评估科学准确性的模型)。
  • 人类反馈(例如,人工标注的喜好评分)。
  • 形式化验证器(例如,代码编译器、数学证明检查器)。

核心思想是:验证通常比生成更容易。检查一段代码是否正确、一个数学证明是否成立,比从头编写正确的代码或证明要简单得多。

因此,我们可以:

  1. 用语言模型生成多个候选答案。
  2. 用验证器或评分函数评估每个候选答案。
  3. 选择得分最高的答案作为最终输出。

这种方法将强大的生成能力与可靠的验证能力相结合,从而得到更可靠的输出。


在潜空间中进行可微分搜索 🎨

上述搜索方法主要针对离散的、自回归的序列生成。对于图像生成器等连续输出模型,我们可以利用可微分性进行更直接的优化。

考虑一个生成模型,它将潜变量 z 映射到图像 G(z)。我们还有一个评分函数 S(image)(例如,CLIP模型,用于评估图像与文本描述的匹配度)。

我们的目标是找到能最大化评分函数的图像。与其在像素空间盲目搜索,我们可以在模型的潜空间中进行优化:
z* = argmax_z S(G(z))
同时,我们可以约束 z 使其服从先验分布(如高斯分布),以确保生成的图像是“自然”的。

由于 GS 通常都是可微分的神经网络,我们可以直接使用梯度下降法来优化 z。这种方法在文本到图像生成(如早期的CLIP+GAN模型)和图像属性优化(如让图像更令人难忘、更美观)中取得了成功。


测试时训练:在部署中学习 🚀

到目前为止,我们讨论的推理方法都没有改变模型本身的权重。但模型在测试时也可以根据新数据自适应地更新,这被称为测试时训练

一个相关的概念是上下文学习:给语言模型提供一些输入-输出示例作为上下文,然后提出查询,模型能够根据上下文中的模式给出答案。这本质上是在让模型执行一次性的监督学习。

研究表明,在某些简化情况下,上下文学习的行为等价于在上下文示例上对模型参数执行一步梯度下降。这引出了一个想法:为什么不直接做梯度下降呢?

测试时训练正是这样做的。在收到测试查询(可能包含少量示例)后,模型不是仅仅进行前向传播,而是根据这些新数据计算损失,并执行几步梯度下降来更新自己的权重,然后再对最终查询进行预测。

这样做的好处包括:

  • 理论保证:梯度下降的收敛性比黑盒的上下文学习更好理解。
  • 利用更多计算:可以进行多轮迭代优化,而上下文学习通常只是一次前向传播。
  • 专业化:模型可以针对当前特定的测试分布进行快速适配,而无需在训练时见过所有情况。

测试时训练甚至可以在没有标注的情况下进行,例如使用自监督损失(如掩码自动编码)在测试样本上更新特征提取器,从而提升下游任务的性能。


搜索与学习的协同循环 🔄

最强大的系统往往结合了搜索和学习,并让它们相互促进。一个经典的范式是强化学习,其核心循环是:

  1. 用学习来改进搜索:使用当前策略(学习到的模型)来指导搜索过程,使其更高效。
  2. 用搜索来改进学习:通过搜索(如蒙特卡洛树搜索)找到高质量的动作序列或解决方案,然后将这些解决方案作为训练数据来更新策略(模型),使其在未来能直接输出好的结果。

这个“搜索-蒸馏”的循环可以不断迭代,使系统能力持续提升。AlphaGo正是利用这个原理击败了人类围棋冠军。

对于大型语言模型,类似的思想体现在如STaR或推测中的o1等方法中:

  1. 让语言模型为问题生成多个带有“思维链”的答案。
  2. 通过验证器(可以是另一个模型、编译器或人工)筛选出那些得出正确答案的思维链。
  3. 将这些“成功的推理过程”作为训练数据,微调语言模型,使其更倾向于产生有效的思维链。
  4. 重复此过程。随着模型改进,搜索空间更聚焦于高质量推理,进而产生更好的训练数据,形成良性循环。

在最终部署时,可以直接使用微调后的模型进行快速推理,也可以在此基础上附加额外的搜索步骤,以换取更高的准确性。


总结 📝

本节课我们一起学习了深度学习中先进的推理方法。我们从训练与推理的二分法出发,探讨了如何利用搜索(如最佳N采样、束搜索)来获得比贪婪采样更好的输出。接着,我们看到了如何利用任意评分函数进行优化,并介绍了在图像生成等连续问题中进行的可微分搜索

更重要的是,我们了解了模型可以在测试时通过测试时训练来自适应更新,以及如何构建搜索与学习的协同循环来让系统不断自我改进。这些方法代表了当前深度学习的前沿,通过将大量计算从训练时转移到测试时,极大地提升了模型解决复杂问题的能力。

理解这些推理方法,对于构建下一代更强大、更灵活的人工智能系统至关重要。

024:PyTorch基础教程 🚀

在本节课中,我们将学习PyTorch的基础知识。PyTorch是一个强大的深度学习框架,因其速度快、支持GPU计算和自动求导功能而被广泛使用。我们将从张量操作开始,逐步深入到自动求导和模型训练,为后续的课程作业和项目打下坚实基础。


张量基础 🧱

张量是PyTorch中存储数据的基本结构,类似于NumPy数组,但支持GPU加速。理解如何创建和操作张量是使用PyTorch的第一步。

张量的创建

以下是几种创建张量的常用方法。

从Python列表创建

data = [[1, 2], [3, 4]]
tensor_from_list = torch.tensor(data)

从NumPy数组创建

np_array = np.array(data)
tensor_from_numpy = torch.from_numpy(np_array)

创建特定形状的张量

shape = (2, 3)
tensor_ones = torch.ones(shape)
tensor_zeros = torch.zeros(shape)
tensor_random = torch.rand(shape)

张量的属性

了解张量的属性对于调试和确保计算正确性至关重要。

形状

tensor = torch.rand(3, 4)
print(tensor.shape)  # 输出: torch.Size([3, 4])

数据类型

print(tensor.dtype)  # 输出: torch.float32

设备

print(tensor.device)  # 输出: cpu 或 cuda:0

张量操作 🔧

上一节我们介绍了如何创建张量,本节中我们来看看如何对它们进行各种操作。这些操作包括数学运算、形状变换和索引等。

数学运算

可以对张量执行各种数学运算,这些运算通常是逐元素进行的。

基本运算

x = torch.linspace(0, 5, 101)
y_sin = torch.sin(x)
y_cos = torch.cos(x)
y_complex = x ** 2 * torch.cos(x)

聚合操作

tensor = torch.tensor([1.0, 2.0, 3.0, 4.0])
print(torch.sum(tensor))   # 求和
print(torch.mean(tensor))  # 求均值
print(torch.max(tensor))   # 求最大值

形状操作

在处理多维数据(如图像批次)时,经常需要改变张量的形状。

改变维度顺序

# 假设 tensor 形状为 [batch, channels, height, width]
tensor = torch.randn(2, 3, 4, 5)
# 交换高度和宽度维度
permuted_tensor = tensor.permute(0, 1, 3, 2)

重塑形状

reshaped_tensor = tensor.view(2, -1)  # -1 表示自动计算该维度大小

索引与切片

PyTorch的索引语法与NumPy和Python列表类似。

基本索引

tensor = torch.randn(4, 4)
first_row = tensor[0, :]      # 第一行
first_column = tensor[:, 0]   # 第一列
last_element = tensor[-1, -1] # 最后一个元素

赋值

tensor[:, 1] = 0  # 将第二列所有元素设为0

连接与堆叠

有时需要将多个张量组合在一起。

沿指定维度连接

tensor1 = torch.ones(4, 4)
tensor2 = torch.zeros(4, 4)
concatenated = torch.cat([tensor1, tensor2], dim=1)  # 沿列方向连接

创建新维度堆叠

stacked = torch.stack([tensor1, tensor2], dim=0)  # 在批次维度堆叠

矩阵运算

深度学习模型的核心是线性代数运算。

矩阵乘法

A = torch.randn(3, 4)
B = torch.randn(4, 5)
C = A @ B  # 矩阵乘法

逐元素乘法

D = A * B  # 要求A和B形状相同

自动求导(Autograd) 📈

上一节我们学习了张量的基本操作,本节将探讨PyTorch的核心特性之一:自动求导。它允许我们自动计算梯度,这是训练神经网络的关键。

计算简单梯度

我们可以使用autograd来计算一个标量输出相对于其输入的梯度。

基本示例

x = torch.linspace(0, 5, 101, requires_grad=True)
y = (x ** 2) * torch.cos(x)
# 为了计算梯度,需要得到一个标量输出
y_sum = y.sum()
y_sum.backward()  # 计算梯度
# 梯度存储在 x.grad 中
print(x.grad)

使用.backward()方法

在实际训练模型时,我们使用.backward()方法一次性计算所有需要梯度的参数的梯度。

训练循环中的典型用法

# 假设 loss 是一个标量损失值
loss.backward()  # 计算图中所有 requires_grad=True 的张量的梯度

禁用梯度计算

在不需梯度时(如模型推理),禁用梯度可以节省内存和计算资源。

使用torch.no_grad()上下文管理器

with torch.no_grad():
    # 在这个块内的所有操作都不会计算梯度
    output = model(input_data)

优化器与模型训练 🏋️‍♂️

知道了如何计算梯度后,我们需要使用优化器来更新模型参数,以最小化损失函数。本节将介绍几种常见的优化器。

朴素梯度下降

这是最基本的优化方法。

手动更新参数

learning_rate = 0.01
for param in model.parameters():
    param.data -= learning_rate * param.grad

使用torch.optim模块

PyTorch提供了optim模块,其中包含多种优化算法。

随机梯度下降

import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 在训练循环中
optimizer.zero_grad()  # 清空之前的梯度
loss.backward()        # 计算新梯度
optimizer.step()       # 更新参数

Adam优化器
Adam是实践中非常流行且高效的优化器。

optimizer = optim.Adam(model.parameters(), lr=0.001)
# 使用方式与SGD相同

构建神经网络 🧠

最后,我们将学习如何使用PyTorch的模块化组件来构建神经网络。这是将前面所有知识整合起来的关键一步。

线性层

线性层(全连接层)是神经网络的基本构建块。

定义与使用

import torch.nn as nn
linear_layer = nn.Linear(in_features=10, out_features=5, bias=True)
input_data = torch.randn(32, 10)  # 批次大小为32
output = linear_layer(input_data)

查看与操作参数

我们可以检查和修改层的参数。

访问权重和偏置

print(linear_layer.weight.shape)  # 输出: torch.Size([5, 10])
print(linear_layer.bias.shape)    # 输出: torch.Size([5])

设置参数

nn.init.zeros_(linear_layer.bias)  # 将偏置初始化为0

使用nn.Sequential构建网络

对于简单的顺序网络,nn.Sequential容器非常方便。

构建多层感知机

model = nn.Sequential(
    nn.Linear(2, 64),
    nn.ReLU(),
    nn.Linear(64, 32),
    nn.ReLU(),
    nn.Linear(32, 2)  # 假设是二分类
)

一个完整的训练示例

让我们结合损失函数和优化器,完成一个简单的分类模型训练循环。

训练循环结构

# 定义模型、损失函数和优化器
model = nn.Sequential(...)
criterion = nn.CrossEntropyLoss()  # 分类任务常用损失
optimizer = optim.Adam(model.parameters(), lr=0.001)

num_epochs = 10
for epoch in range(num_epochs):
    # 假设 `data_loader` 是一个数据加载器
    for inputs, labels in data_loader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')

总结 🎯

本节课中我们一起学习了PyTorch的基础知识。我们从张量的创建和操作开始,这是所有计算的基础。接着,我们深入了解了自动求导机制,它使得梯度计算变得自动化。然后,我们介绍了如何使用优化器来利用这些梯度更新模型参数。最后,我们学习了如何用nn.Modulenn.Sequential构建神经网络,并完成了一个简单的训练循环。

掌握这些核心概念后,你已经具备了使用PyTorch进行深度学习实验和完成课程作业的基本能力。记住,实践是学习的关键,多动手编写代码是熟悉这个框架的最佳途径。

posted @ 2026-03-26 08:58  布客飞龙I  阅读(15)  评论(0)    收藏  举报