斯坦福-CS231n-计算机视觉-2025-笔记-全-

斯坦福 CS231n 计算机视觉 2025 笔记(全)

001: 计算机视觉与深度学习导论 🧠👁️

在本节课中,我们将要学习计算机视觉与深度学习的基本概念、历史脉络以及本课程的核心内容。我们将从视觉的起源讲起,探讨计算机视觉如何成为人工智能的关键组成部分,并了解深度学习革命如何重塑了这一领域。

概述

计算机视觉旨在使机器能够“看见”和理解视觉世界。深度学习,特别是神经网络,已成为解决计算机视觉问题的强大工具。本节课将概述这两个领域的交汇点,并为本课程后续内容奠定基础。

视觉的起源与重要性

视觉的历史并非始于人类文明,而是可以追溯到5.4亿年前的寒武纪生命大爆发。化石研究表明,在这个相对短暂的进化时期内,动物物种出现了爆炸性增长。

一个最引人注目的理论认为,这一爆发的关键驱动力是“眼睛”的出现。最早的动物(如三叶虫)获得了光敏细胞,形成了一个简单的“针孔”,能够收集光线。

一旦生物能够感知光线,生命便从被动的新陈代谢转变为与环境积极互动的一部分。视觉(连同触觉)作为动物最古老的感官之一,驱动了神经系统和智能的进化。几乎所有现代动物都将视觉作为主要感官之一。

人类是尤其依赖视觉的动物。我们超过一半的大脑皮层细胞都参与视觉处理,并拥有复杂精密的视觉系统。理解视觉智能的奥秘,正是理解智能本身的关键。

从生物视觉到计算机视觉

人类不仅能够看见,还渴望建造能够“看见”的机器。从达芬奇研究“暗箱”,到古希腊和古代中国的思想家探索通过小孔成像,人类一直在尝试复制视觉。

然而,相机(或眼睛)本身并不等同于“看见”。它们只是 apparatus(装置)。我们需要理解视觉智能是如何发生的,而这正是本课程的核心。

计算机视觉与深度学习的简史

上一节我们介绍了视觉的生物学起源,本节中我们来看看推动现代计算机视觉与深度学习交汇的关键历史节点。

神经科学的奠基 (1959)

20世纪50年代,Hubel和Wiesel在神经科学领域进行了一系列至关重要的实验。他们通过电极研究活体(麻醉)猫的初级视觉皮层神经元。

他们的发现有两个关键点:

  1. 神经元的感受野:负责“看见”的每个神经元只对视觉空间中一个特定小区域的特定模式(如特定朝向的边缘)有反应。
  2. 视觉通路的层次性:神经元相互连接,形成层次网络。初级层的神经元(如边缘检测器)将其输出传递给更高层的神经元,后者则对更复杂的模式(如角点、物体)产生反应。

这些发现对后来的神经网络建模产生了深远影响。约30年后,Hubel和Wiesel因揭示视觉处理原理而获得了诺贝尔医学奖。

计算机视觉的诞生 (1960s)

1963年,Larry Roberts撰写了公认的第一篇计算机视觉博士论文,专注于研究形状。其核心思想是:能否通过分析形状的曲面、角点和特征来理解物体?这直觉上正是人类所做的。

1966年,MIT教授发起了一个夏季项目,目标是让本科生“解决视觉问题”。尽管视觉并未在那个夏天被“解决”,但这个项目与Roberts的论文一同被视为计算机视觉领域的开端。

大卫·马尔的理论框架 (1970s)

David Marr在其著作中系统性地思考了视觉处理过程。他的理论框架包含三个层次:

  1. 初始简图:从图像中提取基本特征,如边缘(类似于Hubel & Wiesel的发现)。
  2. 2.5维简图:将图像中的物体按深度分离,区分前景和背景。
  3. 三维模型表示:获得对世界的完整三维理解。这是视觉中最困难的问题,因为从二维图像反推三维世界是一个病态问题。自然界通过进化出多只眼睛(如双眼视觉)并理解对应关系等方式部分解决了这个问题。

视觉与语言的根本差异

这里需要理解一个哲学上微妙但重要的差异:语言在自然界中并不存在,它是人类大脑生成的一维序列信息。而视觉则是对一个真实存在的、遵守物理定律的三维世界的感知。这种根本差异意味着处理视觉和语言的任务与模型会有深刻的不同。

早期探索与AI寒冬

在1970-80年代,尽管缺乏数据、算力和现代数学工具,先驱们已开始尝试解决一些困难的计算机视觉问题,例如物体识别(如“广义圆柱体”模型)、人体组合模型和边缘检测。

然而,到80年代末,进展似乎停滞不前,许多预期未能实现,导致了“AI寒冬”的到来——对AI研究的热情和资金大幅减少。但正是在这个“寒冬”的表象之下,许多研究在计算机视觉、自然语言处理、机器人学等领域持续生长。

认知神经科学的指引

与此同时,认知神经科学持续发展,为计算机视觉指明了应重点研究的“北极星”问题。例如:

  • 研究表明,识别物体(如自行车)的速度和准确性受其周围场景(是否倒置)的影响,这说明视觉处理是整体性的、快速的。
  • 实验表明,人类能在约150毫秒内完成对复杂自然图片的分类(如判断有无动物),这对于生物神经元而言是极快的速度。
  • 研究发现,人类大脑有专门识别面孔、地点或身体部位的特化脑区。

所有这些都指向一个核心问题:在自然场景下的物体识别。这成为解锁视觉智能的关键问题之一,也是本课程后续将深入探讨的重点。

深度学习的平行演进

当计算机视觉领域在神经科学和认知科学的启发下前行时,另一条研究主线——最终发展为深度学习——也在并行发展。

神经网络的早期探索

早期研究集中在感知机等小型人工神经网络上。尽管Marvin Minsky等人曾指出感知机的局限性(如无法学习异或逻辑函数),导致神经网络研究一度受挫,但相关工作仍在继续。

福岛邦彦的新认知机 (1980)

在第一个转折点之前,福岛邦彦的新认知机是一项重要工作。他手工设计了一个5-6层的神经网络,其结构明显受到Hubel & Wiesel描述的视觉通路启发:浅层执行简单功能(卷积),深层整合信息形成更复杂的表示。

然而,这是一个工程壮举,因为成百上千的参数都是手工精心设置的,仅能用于识别数字或字母。

反向传播的突破 (1986)

真正的突破是反向传播学习规则的出现。Rumelhart, Hinton等人为神经网络架构引入了基于误差修正的目标函数。其核心思想是:当输入已知正确答案时,计算网络输出与正确答案的差异(误差),然后利用链式法则将误差信息从输出层反向传播回网络各层,从而更新所有参数以减小误差。

公式表示(简化)参数更新 = 参数 - 学习率 * (损失函数对参数的梯度)
反向传播就是高效计算这个梯度的算法。

杨立昆的卷积神经网络 (1990s)

反向传播最早的重要应用之一是Yann LeCun在贝尔实验室开发的卷积神经网络(LeNet)。他构建了一个约7层的更大网络,并通过出色的工程实现,使其能够有效识别手写数字和字母,并被应用于银行和邮政系统。

尽管如此,神经网络的发展随后再次陷入停滞。原因在于,当面对神经科学家使用的那些包含猫、狗、微波炉、椅子、花朵的复杂自然图片时,这些系统完全失效。一个巨大的瓶颈出现了:缺乏数据

数据:深度学习的引爆点

缺乏数据不仅仅是不方便,更是一个数学问题。深度学习算法是高容量模型,需要海量数据驱动才能学习并泛化到新样本。数据的重要性在早期被低估了。

李飞飞教授及其团队在21世纪初认识到了数据的重要性,并着手创建了ImageNet数据集。该数据集包含约1500万张图像,涵盖约22000个物体类别,其规模大致相当于人类幼年时期学习识别的概念数量。

基于ImageNet,他们发起了ImageNet大规模视觉识别挑战赛,使用一个包含100多万张图像、1000个类别的子集,邀请全球研究者竞赛,看谁的算法识别最准确。

以下是竞赛初期Top-5错误率的变化:

  • 2010/2011年:最佳算法的错误率接近30%,而人类错误率约3%。
  • 2012年:Jeffrey Hinton及其学生使用卷积神经网络(AlexNet)参赛,将错误率几乎降低了一半,震惊了整个领域。

AlexNet在结构上与32年前福岛的新认知机并无本质不同,但两大关键因素使其成功:

  1. 反向传播:提供了数学上严谨、自动化的参数学习规则。
  2. 大数据:ImageNet提供了驱动高容量模型所需的海量数据。

2012年AlexNet在ImageNet挑战赛上的成功,被广泛视为现代AI复兴或深度学习革命诞生的历史性时刻

深度学习时代的爆发

自此,我们进入了深度学习爆炸的时代。计算机视觉领域取得了飞速进展:

  • 任务多样化:从图像分类,发展到图像检索、多物体检测、图像分割、视频分类、人体动作识别等。
  • 领域交叉:深刻影响医学影像、科学发现(如首张黑洞照片)、环境保护等。
  • 生成能力:出现了图像描述、关系理解、风格迁移,并最终进入生成式AI时代,能够生成高度逼真的图像和视频。
  • 硬件驱动:GPU等硬件算力的指数级增长,与算法、数据共同构成了推动领域发展的三大合力。

我们已彻底走出AI寒冬,进入了一个AI技术加速发展的时期。

挑战、责任与未来

尽管成就斐然,计算机视觉仍面临巨大挑战,并伴随着重大责任:

  • 技术局限:人类视觉中的细微差别、丰富情感和复杂理解,仍是当前技术难以企及的。
  • 偏见与伦理:AI模型由数据驱动,而数据反映了人类历史和社会中的偏见。这可能导致算法在面部识别、招聘、信贷等领域产生歧视性结果。
  • 社会影响:AI在医疗(积极)和自动化决策(需审慎)等方面将深刻影响人类社会。

这正是为什么需要来自计算机科学、人文社科、法律、商业等不同背景的学生共同学习和探讨AI,因为AI问题远不止是工程问题。

课程内容预览

接下来,Professor Delli将概述本课程的具体内容结构,主要分为四大主题:

  1. 深度学习基础:从图像分类、线性分类器入手,讲解模型容量、过拟合/欠拟合、正则化、优化以及神经网络的基本原理。
  2. 感知与理解视觉世界:深入探讨卷积神经网络(CNN),并学习超越分类的任务,如语义分割、物体检测、实例分割,以及视频理解、多模态学习等。
  3. 大规模训练与前沿架构:介绍训练大模型所需的分布式训练策略(数据并行、模型并行),以及Transformer等新兴架构。
  4. 生成式与交互式视觉智能:涵盖自监督学习、生成模型(如风格迁移、扩散模型)、视觉-语言模型、3D视觉以及具身智能等前沿方向。
  5. 以人为本的应用与思考:探讨计算机视觉的社会影响、伦理及在医疗等领域的应用。

本课程的目标是让大家能够将计算机视觉问题形式化为任务,开发并训练视觉模型,并理解该领域的现状与未来方向。

总结

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

  • 视觉在智能进化中的核心地位。
  • 计算机视觉与深度学习各自的发展简史及关键里程碑。
  • 数据算法(反向传播)和算力如何共同引爆了2012年的深度学习革命。
  • 现代计算机视觉的广泛应用、当前面临的挑战以及社会责任。
  • 本课程(CS231n)将涵盖的核心知识体系。

下节课,我们将正式进入技术核心,从图像分类和线性分类器开始我们的深度学习之旅。

002:使用线性分类器进行图像分类

在本节课中,我们将继续讨论图像分类这一核心任务。我们将从数据驱动的方法入手,介绍两种基础的图像分类方法:K最近邻算法和线性分类器。通过理解这些基础概念,我们将为后续学习神经网络和卷积神经网络打下坚实的基础。

图像分类任务

图像分类是计算机视觉中的一项核心任务。给定一张图像和一组预定义的类别标签(例如“猫”、“卡车”、“飞机”),系统的任务是为该图像分配一个最合适的标签。

对人类而言,这是一项非常简单的任务,因为我们的大脑能够整体性地理解图像。然而,对于计算机来说,图像通常被表示为数据矩阵(或更一般地,张量)。例如,一张800x600像素的彩色(RGB)图像,可以表示为一个形状为 800 x 600 x 3 的张量,其中每个像素的RGB值通常在0到255之间。

这种表示方式与我们人类的感知之间存在巨大的“语义鸿沟”。为了理解计算机识别图像的挑战,我们需要考虑图像数据中的多种变化因素。

图像分类的挑战

以下是图像分类任务中常见的一些挑战,它们会导致像素值发生变化,从而给机器识别带来困难:

  • 视角变化:即使物体本身静止,移动相机也会改变所有像素值。
  • 光照变化:同一物体在不同光照条件下,其RGB像素值会发生变化。
  • 背景干扰:杂乱的背景或前景物体会干扰对目标物体的识别。
  • 尺度变化:物体在图像中的大小(缩放)不同。
  • 遮挡:物体可能被部分遮挡,只露出一小部分。
  • 形变:物体(如猫)本身具有多种可变形态。
  • 类内差异:同一类别的物体(如不同品种的猫)在大小、颜色、纹理上存在差异。
  • 上下文依赖:有时需要依赖图像的整体上下文才能做出正确判断。

尽管存在这些挑战,得益于大规模数据集(如ImageNet)和深度学习模型的发展,今天的分类器已经能够非常出色地完成图像分类任务。本课程的目标,就是一步步构建实现这些强大算法所需的基石。

数据驱动方法

与传统的、试图通过硬编码规则(如边缘检测、角点统计)来识别物体的方法不同,现代方法主要采用数据驱动的范式。这种方法不依赖于手动设计的逻辑,而是遵循一个三步流程:

  1. 收集数据集:收集包含图像及其对应标签的大量数据。
  2. 训练分类器:使用机器学习算法,在训练数据上学习一个模型(函数),该模型能够建立图像与标签之间的关联。
  3. 评估分类器:在新的、未见过的测试图像上评估训练好的模型,预测其标签。

我们将重点介绍两种基础的数据驱动分类方法。

K最近邻分类器

K最近邻是一种最简单直观的分类算法。它帮助我们理解分类器的基本概念,并引入超参数调优等重要思想。

算法原理

K最近邻分类器包含两个核心函数:

  • 训练函数 train:该函数非常简单,只需“记住”所有的训练图像和标签。用代码表示,即 def train(X, y): self.X_train = X; self.y_train = y
  • 预测函数 predict:对于一个新的查询图像,在训练集中找到与之“最相似”的K个图像,然后通过投票(多数表决)决定其标签。

这里的关键在于如何定义“相似”,即需要一个距离函数

距离度量

以下是两种常用的距离函数:

  • L1距离(曼哈顿距离):计算两个图像所有像素值之差的绝对值之和。
    • 公式d(I1, I2) = sum(|I1[i] - I2[i]|)
  • L2距离(欧几里得距离):计算两个图像所有像素值之差的平方和的平方根。
    • 公式d(I1, I2) = sqrt(sum((I1[i] - I2[i])^2))

L1距离对特征轴(像素值)的旋转敏感,其等距点构成菱形;而L2距离是旋转不变的,其等距点构成圆形。选择哪种距离取决于具体问题和特征的性质。

算法复杂度与可视化

  • 训练复杂度:O(1),因为只是存储数据。
  • 预测复杂度:O(N),对于每个测试样本,都需要与所有N个训练样本计算距离。这使得预测阶段非常慢,不适用于大规模应用。

在特征空间的可视化中,K=1的最近邻算法会根据每个训练样本划出决策区域。K值增大(K近邻)可以使决策边界更平滑,减少对噪声的敏感度,但也可能在某些区域产生平局而无法决策。

超参数调优

K最近邻算法有两个关键的超参数K值距离函数类型。超参数的选择不能基于训练集(会导致过拟合),也不能基于测试集(属于“作弊”)。正确的做法是:

  1. 将训练数据划分为训练集验证集
  2. 在训练集上训练模型,在验证集上评估不同超参数组合的性能。
  3. 选择在验证集上表现最好的超参数组合。
  4. 最终,使用选定的超参数在完整的训练集上重新训练,并在独立的测试集上进行一次性评估。

对于更可靠的估计,可以使用K折交叉验证:将训练数据分成K份,轮流将其中一份作为验证集,其余作为训练集,重复K次后取平均性能。

在CIFAR-10上的结果

在CIFAR-10数据集(10个类别,共6万张32x32小图像)上,K最近邻的最佳准确率约为28-29%,远高于随机猜测的10%,但仍有很大提升空间。观察错误案例可以发现,基于原始像素的距离度量效果有限,因为外观相似的物体(如绿色的青蛙和卡车)可能在像素空间很接近。

总结:K最近邻算法让我们理解了数据驱动方法的基本流程、距离度量的概念以及超参数调优的重要性。然而,其预测速度慢且基于像素的相似性度量效果有限,这促使我们转向更强大的方法——线性分类器。

线性分类器

线性分类器是神经网络和深度学习中最重要的构建模块之一。与K最近邻这种非参数方法不同,线性分类器是一种参数化方法,它通过学习参数(权重)来将输入映射到输出。

算法原理

线性分类器将输入图像 x(例如,CIFAR-10中为3072维向量)通过一个线性函数映射到每个类别的“得分”上。

  • 公式f(x, W, b) = W * x + b
    • x:输入图像展平后的向量(例如 3072 x 1)。
    • W:权重矩阵(例如 10 x 3072),每一行对应一个类别的“模板”。
    • b:偏置向量(例如 10 x 1),允许决策边界不经过原点。
    • 输出:每个类别的得分(例如 10个分数)。

理解线性分类器

可以从三个视角理解线性分类器:

  1. 代数视角:如上公式所示,就是矩阵乘法与向量加法。
  2. 视觉视角:权重矩阵 W 的每一行可以可视化为一个图像“模板”。例如,在CIFAR-10上训练后,“汽车”类对应的模板可能隐约显示出汽车的轮廓。
  3. 几何视角:在高维特征空间中,每个线性分类器实际上是在用一个超平面来划分空间。每个类别的得分反映了输入点距离该类别决策边界的“远近”。

线性分类器的局限性

线性分类器能力有限,它只能学习线性决策边界。对于某些复杂的数据分布(例如异或问题、多模态分布、环形分布),单个线性分类器无法进行有效分类。这为后续引入具有非线性激活函数的神经网络提供了动机。

损失函数与优化

为了找到最优的权重 W,我们需要定义一个损失函数(或目标函数),用以量化当前分类器在训练数据上的“糟糕程度”(即预测得分与真实标签的不匹配程度)。

一种常见的方法是使用Softmax分类器(或称多项逻辑回归):

  1. 将线性函数输出的得分通过 Softmax函数 转换为概率分布。
    • 公式P(Y=k|X=x_i) = exp(s_k) / sum_j(exp(s_j)),其中 s = f(x_i, W, b)
  2. 定义损失函数为交叉熵损失(或负对数似然),即正确类别概率的负对数。
    • 公式L_i = -log(P(Y=y_i|X=x_i))
    • 整个数据集的损失是所有样本损失的平均:L = (1/N) * sum(L_i)

这个损失函数可以从最大似然估计最小化KL散度的角度推导出来。在深度学习中,它常被称为交叉熵损失

初始化分析:在训练开始时,如果权重 W 随机初始化,所有类别的得分可能相近,Softmax输出的概率也近似均匀分布(对于C个类别,每个概率约为1/C)。此时的初始损失值约为 -log(1/C) = log(C)。对于CIFAR-10的10个类别,初始损失约为 log(10) ≈ 2.3。训练的目标就是通过优化算法(如下一讲将介绍的梯度下降)不断调整 W,以最小化这个损失函数。

总结

本节课我们一起学习了图像分类的基础知识。我们首先明确了图像分类任务的定义及其面临的主要挑战。然后,我们探讨了数据驱动方法的三步流程,并深入介绍了两种基础分类器:

  1. K最近邻分类器:一种简单直观的非参数方法,我们通过它理解了距离度量、超参数(K值和距离函数)以及通过验证集或交叉验证进行超参数调优的重要性。
  2. 线性分类器:一种重要的参数化方法,是神经网络的基石。我们学习了其代数、视觉和几何解释,了解了它的能力与局限性,并介绍了如何通过Softmax函数和交叉熵损失函数来形式化分类问题。

在下一讲中,我们将探讨如何通过优化算法(如梯度下降)来找到最小化损失函数的最佳权重 W,从而完成线性分类器的训练。

003:正则化与优化

概述

在本节课中,我们将要学习深度学习和机器学习中两个非常重要的概念:正则化优化。我们将从回顾上周的内容开始,然后深入探讨如何通过正则化防止模型过拟合,以及如何使用优化算法找到模型的最佳参数。


回顾:图像分类与线性模型

上一节我们介绍了图像分类作为计算机视觉的核心任务。其目标是将输入的图像映射到一组预定义标签中的一个。

图像分类面临诸多挑战,例如语义鸿沟(像素值与人类理解之间的差异)、光照变化物体形变遮挡以及类内差异等。由于无法用简单的逻辑规则解决这些问题,我们转向了数据驱动的方法。

我们讨论了最简单的机器学习模型之一:K最近邻模型。其核心思想是为一个新的数据点,在训练集中找到距离最近的K个邻居,并根据这些邻居的标签来预测新数据点的类别。我们通常将数据集划分为训练集、验证集和测试集,并使用验证集来选择超参数(如K值)。

我们还深入探讨了线性分类器。其基本思想是将图像(例如32x32x3的像素值)展平为一个向量(如3072维),然后乘以一个权重矩阵 W,并加上一个偏置向量 b,从而得到每个类别的得分。

我们可以从三个角度理解线性模型:

  1. 代数视角:每个类别的得分由权重矩阵的对应行与输入向量的点积加上偏置得到。
  2. 模板匹配视角:将权重矩阵的每一行重新排列成图像形状,可以将其视为每个类别的“模板”。
  3. 几何视角:每个类别的权重向量在输入空间中定义了一条决策边界(线或超平面),用于区分不同类别。

正则化:防止过拟合

上一节我们介绍了如何用线性模型进行预测,本节中我们来看看如何评估和提升模型的泛化能力。我们通过损失函数来衡量模型预测的好坏。对于分类任务,最常用的是交叉熵损失(或称Softmax损失)。

总损失通常由两部分组成:
总损失 = 数据损失 + 正则化项

数据损失衡量模型预测与训练数据的匹配程度。正则化项则用于惩罚模型的复杂度,防止其在训练数据上表现“太好”(即过拟合),从而提升模型在未见数据(测试集)上的性能。正则化强度由一个超参数 λ 控制。

以下是两种最常见的正则化方法:

  • L2正则化:惩罚权重向量中所有值的平方和。公式为:λ * Σ (W_i)^2。它倾向于让所有权重值都较小且分布均匀。
  • L1正则化:惩罚权重向量中所有值的绝对值之和。公式为:λ * Σ |W_i|。它倾向于产生稀疏的权重向量,即许多权重值为零。

为什么正则化有效?

  1. 它允许我们表达对权重形式的偏好(如稀疏性或均匀性)。
  2. 通过惩罚复杂度,它鼓励模型更简单,从而可能在新数据上表现更好。
  3. L2正则化还能改善优化过程,因为它使损失函数更“平滑”,更容易找到最小值。

优化:寻找最佳参数

现在我们已经知道如何用损失函数评估一个权重矩阵 W 的好坏,接下来的问题是如何找到使损失最小的那个 W。这就是优化要解决的问题。

一个直观的比喻是损失景观:将损失值想象成地形的高度,模型参数(如W的两个分量)是平面坐标。我们的目标是找到这个景观中的最低点(最小损失)。优化算法就像是一个蒙着眼睛的徒步者,试图通过感受脚下地面的坡度(梯度)来找到下山的路。

梯度计算

我们通过计算梯度来获得“坡度”信息。梯度是一个向量,其每个分量是损失函数对相应模型参数的偏导数,它指向损失上升最快的方向。因此,负梯度方向就是损失下降最快的方向。

计算梯度有两种方法:

  1. 数值梯度:使用极限定义进行近似计算(f(x+h) - f(x) / h)。优点是易于实现,缺点是计算慢且不精确。
  2. 解析梯度:使用微积分(如链式法则)直接推导出梯度公式。优点是计算快速精确,缺点是实现时容易出错。实践中,我们常用数值梯度来验证解析梯度实现的正确性,这称为梯度检查

梯度下降

最基本的优化算法是梯度下降。其核心步骤是:
W_new = W_old - learning_rate * gradient
其中,learning_rate(学习率)是一个超参数,控制我们沿着梯度方向迈出的步长。

在实际应用中,我们很少在整个训练集上计算梯度(计算量太大),而是采用随机梯度下降。每次迭代时,我们随机抽取一小批数据(mini-batch)来计算梯度并更新参数。遍历一遍所有训练数据称为一个epoch

梯度下降的挑战与改进

基本的SGD存在一些问题:

  1. 病态条件:在损失函数不同方向曲率差异很大的“峡谷”地形中,SGD会剧烈震荡,收敛缓慢。
  2. 局部极小值与鞍点:梯度为零的点会使优化停滞。在高维空间中,鞍点比局部极小点更常见。
  3. 梯度噪声:由于使用mini-batch,梯度估计存在噪声。

为了解决这些问题,研究者提出了更高级的优化器:

  • 动量法:引入“速度”变量,使其成为过去梯度的指数加权平均。这有助于在相关方向加速,并抑制震荡。更新公式类似于物理中的动量:velocity = momentum * velocity - learning_rate * gradientW += velocity
  • RMSProp:对梯度平方进行指数加权平均,然后根据这个平均值来调整每个参数的学习步长。在梯度大的方向减小步长,在梯度小的方向增大步长,有助于处理病态曲率。
  • Adam:目前最流行的优化器,它结合了动量法RMSProp的思想。它同时计算梯度的一阶矩(动量)和二阶矩(梯度平方),并进行偏差校正,使其在训练初期更稳定。

学习率调度

学习率是优化中最重要的超参数之一。学习率太大会导致震荡甚至发散,太小则收敛缓慢。一个有效的策略是在训练过程中动态调整学习率,这称为学习率调度

常见的调度策略包括:

  • 步进衰减:每经过一定轮次,将学习率乘以一个衰减因子(如0.1)。
  • 余弦衰减:学习率随训练过程按余弦函数从初始值衰减到0。
  • 线性预热:在训练开始时,将学习率从0线性增加到初始值,然后再应用衰减策略。

一个经验法则是线性缩放法则:当批量大小(batch size)增加N倍时,学习率也应大致增加N倍。

二阶优化(简要提及)

除了使用一阶梯度信息,还可以使用海森矩阵(二阶导数)来进行优化(如牛顿法)。它能更准确地估计损失函数的局部形状,从而可能更快收敛。然而,对于现代大型神经网络,计算和存储海森矩阵的开销巨大,因此实践中很少使用。


总结与展望

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

  1. 正则化:通过向损失函数添加惩罚项(如L1、L2)来防止模型过拟合,提升泛化能力。
  2. 优化:通过梯度下降及其变体(SGD、动量、RMSProp、Adam)来寻找最小化损失函数的模型参数。我们讨论了梯度计算、学习率设置以及调度策略。

这些是训练所有深度学习模型的基础。目前,AdamAdamW是许多情况下的优秀默认选择。对于能够使用全批次数据的小型模型,可以考虑探索二阶优化方法。

在下一讲中,我们将超越简单的线性模型,开始学习神经网络。通过引入非线性激活函数和多个层,神经网络能够学习更复杂、更强大的数据表示,从而解决线性模型无法处理的分类问题。

004: 神经网络与反向传播 🧠

在本节课中,我们将要学习神经网络的基本概念以及它们如何通过反向传播算法进行学习。我们将从简单的线性分类器开始,逐步构建多层神经网络,并深入探讨反向传播这一核心优化技术的工作原理。


课程回顾 📚

上一节我们介绍了线性分类器、损失函数(如Softmax)和优化方法(如梯度下降)。本节中,我们来看看如何将这些概念扩展,以构建更强大的神经网络模型。

我们之前定义了线性评分函数 f(x, W) = Wx,并基于此构建了损失函数。需要强调的是,Softmax并不是唯一的损失函数。例如,合页损失(Hinge Loss) 也常用于分类任务。其公式鼓励正确类别的分数 S_yi 比其他所有类别的分数 S_j 至少高出一个边界值 margin

L_i = Σ max(0, S_j - S_yi + 1)

在优化方面,我们讨论了如何通过计算损失函数 L 相对于参数 W 的梯度,并沿负梯度方向更新参数,即梯度下降算法

W = W - learning_rate * dL/dW

由于在整个数据集上计算梯度的成本很高,我们通常使用小批量(Mini-batch) 数据(如32、64、128个样本)进行近似计算,这被称为随机梯度下降(SGD)。此外,我们还介绍了SGD的优化变体,如带动量的SGD、RMSProp和Adam优化器。


从线性到非线性:神经网络 🧱

我们之前讨论的线性函数 f(x, W) = Wx 是最基础的神经网络,仅有一层。为了构建更强大的模型,我们可以添加更多层。

一个两层神经网络可以定义为:

h = max(0, W1 * x)  # 第一层,包含非线性激活函数
scores = W2 * h      # 第二层,输出评分

这里,W1 的维度是 [H, D]W2 的维度是 [C, H]。其中 D 是输入特征维度,H 是隐藏层神经元数量,C 是输出类别数量。

关键点max(0, z) 这个操作引入了非线性。这是至关重要的,因为许多复杂问题无法仅用线性变换解决。非线性激活函数允许网络学习输入数据到新特征空间的复杂映射,从而解决更困难的问题。

这种由全连接层堆叠而成的网络,通常被称为全连接网络多层感知机(MLP)


激活函数 ⚡

max 函数在神经网络术语中被称为激活函数。它扮演着核心角色。如果移除激活函数,多层线性变换的复合仍然是一个线性变换(W2 * (W1 * x) = (W2*W1) * x),这将极大地限制模型的表达能力。

以下是几种常见的激活函数:

  • ReLU(修正线性单元):最常用的默认选择之一。公式为 f(x) = max(0, x)
  • Leaky ReLU:解决了ReLU中神经元可能“死亡”(输出恒为0)的问题。公式为 f(x) = max(0.01x, x)
  • ELU(指数线性单元):具有更好的零中心化特性。
  • Sigmoid:将输入压缩到(0,1)区间。公式为 f(x) = 1 / (1 + exp(-x))
  • Tanh:将输入压缩到(-1,1)区间。

注意:Sigmoid和Tanh在深层网络中间层中使用时,可能导致梯度消失问题,因此它们更常用于输出层(例如,用于二分类)。对于隐藏层,ReLU及其变体通常是更安全、更高效的选择。

选择哪种激活函数通常是经验性的,并取决于具体的网络架构(如CNN常用ReLU,Transformer可能使用GELU)。


实现与容量 🛠️

实现一个简单的两层神经网络非常直观。以下是核心步骤的伪代码:

# 初始化参数 W1, b1, W2, b2
# 前向传播
h = np.maximum(0, np.dot(X, W1) + b1) # 第一层 + ReLU
scores = np.dot(h, W2) + b2           # 第二层
# 计算损失(例如Softmax损失)
# 反向传播计算梯度 dW1, db1, dW2, db2
# 使用梯度下降更新参数

隐藏层神经元数量 H 决定了网络的容量。更多的神经元意味着网络可以学习更复杂的函数和决策边界。然而,容量过高可能导致过拟合,即模型记住了训练数据但无法泛化到新数据。

经验法则:我们通常倾向于使用一个稍大于问题需求的网络,然后通过正则化(如L2正则化)来控制过拟合,而不是将网络大小本身作为主要调优的超参数。正则化强度 lambda 需要仔细调整以在拟合数据和保持泛化能力之间取得平衡。


计算图与反向传播 🔄

手动为复杂网络推导和编写梯度公式是繁琐且容易出错的。计算图反向传播提供了系统化的解决方案。

计算图将整个计算过程分解为一系列节点(操作)。每个节点执行一个简单运算(如加法、乘法、激活函数),并记录其输入和输出。

反向传播是一种利用链式法则,从最终损失开始,逆向计算损失相对于所有中间变量和参数梯度的高效算法。其核心是局部计算:

  1. 前向传播:计算图中每个节点的输出。
  2. 反向传播
    • 从损失节点开始,其梯度为1。
    • 对于每个节点,它接收从后续节点传来的上游梯度
    • 节点根据其运算规则计算局部梯度(输出相对于每个输入的导数)。
    • 节点将下游梯度(上游梯度 * 局部梯度)传递给它的每个输入节点。

这种模块化方法使得添加新的层或损失函数变得非常容易。


反向传播模式示例

以下是一些常见运算节点的梯度传播模式:

  • 加法门:梯度分配器。将上游梯度原封不动地传递给所有输入。
  • 乘法门:梯度交换器。对于 f=x*y,输入x的梯度是上游梯度乘以y的值,输入y的梯度是上游梯度乘以x的值。
  • Max门:梯度路由器。只将上游梯度传递给最大值所在的输入,其他输入的梯度为0。
  • Sigmoid门:局部梯度为 sigmoid(x) * (1 - sigmoid(x))

向量与矩阵的反向传播 📊

在实际神经网络中,数据通常是向量或矩阵。此时,梯度不再是标量。

  • 如果损失 L 是标量,变量 x 是向量,那么梯度 dL/dx 也是一个相同维度的向量。
  • 如果变量 xy 都是向量,那么局部梯度 dy/dx 是一个雅可比矩阵
  • 对于矩阵乘法 Y = X * W,其反向传播公式可以高效地表示为矩阵运算,而无需构造巨大的雅可比矩阵:
    • dL/dX = dL/dY * W.T
    • dL/dW = X.T * dL/dY

现代深度学习框架(如PyTorch、TensorFlow)正是基于这种计算图自动微分原理构建的。我们为每个运算实现前向反向函数,框架会自动组合它们完成整个网络的梯度计算。


总结 🎯

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

  1. 神经网络的基础:从单层线性模型扩展到包含非线性激活函数的多层全连接网络(MLP)。
  2. 激活函数的重要性:引入了非线性,使网络能够解决复杂问题。ReLU是常用的默认选择。
  3. 网络容量与正则化:更大的网络容量可以拟合更复杂的函数,但需要通过正则化来防止过拟合。
  4. 反向传播的核心原理:通过计算图和链式法则,高效地计算损失函数相对于所有参数的梯度。我们了解了加法、乘法、Max等基本操作的梯度传播规则。
  5. 向量化实现:将反向传播扩展到向量和矩阵运算,这是实现高效深度学习库的基础。

反向传播是训练几乎所有现代神经网络模型的基石。下一节课,我们将把这些概念应用到卷积神经网络(CNN) 中,这是处理图像等网格数据的有力工具。

005:使用卷积神经网络进行图像分类

在本节课中,我们将要学习卷积神经网络(CNNs)的基本概念,特别是卷积层和池化层,以及它们如何帮助我们构建更强大的图像分类模型。

概述

在之前的课程中,我们学习了深度学习的基础知识,包括线性分类器、损失函数、优化算法以及神经网络。然而,我们之前看到的全连接网络在处理图像时存在一个主要缺陷:它们忽略了图像固有的二维空间结构。本节课,我们将介绍卷积神经网络,这是一种专门为处理图像等具有空间结构的数据而设计的网络架构。我们将重点学习卷积层和池化层这两个核心组件,理解它们的工作原理和优势。

从线性分类器到卷积神经网络

上一节我们介绍了如何使用线性分类器和全连接网络进行图像分类。本节中我们来看看这些方法的局限性,以及卷积神经网络如何解决这些问题。

线性分类器和全连接网络将图像像素拉伸成一个长向量进行处理。这种方法存在两个主要问题:

  1. 破坏空间结构:图像是二维的,像素之间的空间关系(如边缘、纹理)对于识别物体至关重要。拉伸向量会丢失这些信息。
  2. 参数效率低:对于一张32x32的RGB图像,输入向量长度为 32 * 32 * 3 = 3072。如果下一层有1000个神经元,仅这一层就需要 3072 * 1000 ≈ 3百万 个参数。这会导致模型庞大且容易过拟合。

卷积神经网络通过使用局部连接权值共享来优雅地解决这些问题。

卷积层:核心构建模块

卷积层是CNN的核心。其核心思想不再是让每个神经元查看整个输入图像,而是让每个神经元只查看输入的一小片局部区域(称为感受野),并通过学习一组小的滤波器(或卷积核)来检测局部特征(如边缘、颜色斑点)。

以下是卷积层的关键概念:

  • 滤波器:一个小的三维张量(例如 5x5x3),其深度与输入图像的通道数相同。每个滤波器负责检测一种特定的局部特征。
  • 卷积操作:将滤波器在输入图像上从左到右、从上到下滑动。在每个位置,计算滤波器与当前图像局部区域的点积(即模板匹配),结果是一个标量,表示该局部区域与该滤波器的匹配程度。
  • 激活图:将滤波器滑过整个图像后,得到的所有标量输出组成一个二维矩阵,称为激活图特征图。该图上的每个点对应原图上一个局部区域对特定滤波器的响应强度。
  • 多滤波器:一个卷积层通常包含多个滤波器(例如64个)。每个滤波器独立产生一张激活图。将所有激活图堆叠起来,就得到了卷积层的输出,一个三维张量(宽度 x 高度 x 通道数),其中通道数等于滤波器的数量。

卷积操作的输出空间尺寸计算公式为:

输出尺寸 = (输入尺寸 - 滤波器尺寸 + 2 * 填充) / 步长 + 1

其中:

  • 步长:滤波器每次滑动的像素数。步长为1是常见选择,步长为2则进行下采样。
  • 填充:在输入图像边缘添加零值像素。常用设置是 填充 = (滤波器尺寸 - 1) / 2,这样可以保持输入输出的空间尺寸相同。

卷积层的参数数量远少于全连接层。例如,一个具有10个 5x5x3 滤波器的卷积层,其参数数量仅为 (5*5*3 + 1) * 10 = 760+1 为每个滤波器的偏置项)。

池化层:下采样与特征聚合

在卷积层之后,我们常常会插入池化层。池化层的主要作用是进行下采样,它有以下好处:

  1. 逐步降低特征图的空间尺寸,减少计算量和参数数量。
  2. 扩大后续层的有效感受野,让高层神经元能够整合更广区域的低级特征。
  3. 提供一定程度的平移不变性。

最常见的池化操作是最大池化。以下是其工作方式:

  • 将每个输入特征图(通道)划分为不重叠的区域(例如 2x2 的方块)。
  • 对每个区域,取其中所有值的最大值作为输出。
  • 使用 2x2 的池化窗口和步长2,可以将特征图的宽度和高度减半。

池化是一种非常廉价的下采样方式,它不引入需要学习的参数。

构建一个卷积神经网络

现在,我们可以将学到的组件组合起来,构建一个简单的卷积神经网络。一个典型的模式是交替堆叠卷积层、激活函数和池化层,最后连接一个或多个全连接层进行分类。

以下是构建CNN时常见的层序列模式:

输入图像 -> [卷积层 -> 激活函数(如ReLU) -> 池化层] x N -> 展平 -> 全连接层 -> 输出类别分数

其中 N 可以重复多次。随着网络加深,特征图的空间尺寸逐渐减小,而通道数(即检测的特征复杂度)逐渐增加。

平移等变性:CNN的内在优势

卷积和池化操作具有一个重要的数学性质:平移等变性。这意味着,如果输入图像发生平移,那么经过这些操作后得到的特征图也会发生相应的平移(边界效应除外)。这个性质非常重要,因为它编码了我们的一个直觉:图像中某个特征(如一只猫的耳朵)无论出现在图像的哪个位置,都应该被相同的滤波器检测到。这是CNN能够有效处理图像的关键原因之一。

总结

本节课中我们一起学习了卷积神经网络的基础知识。我们了解到:

  1. 卷积层通过使用局部连接的滤波器来检测图像中的局部特征,并利用权值共享极大地提高了参数效率。
  2. 池化层通过对特征图进行下采样,来聚合信息、扩大感受野并引入一定的平移不变性。
  3. 通过堆叠卷积、激活和池化层,我们可以构建出强大的卷积神经网络,它能够有效地处理图像的二维空间结构。
  4. CNN的平移等变性是其成功处理视觉任务的关键特性之一。

在下一讲中,我们将深入探讨经典的CNN架构(如AlexNet, VGG, ResNet),看看这些基础组件是如何被组合成改变计算机视觉历史的强大模型的。

006:CNN架构与训练

概述

在本节课中,我们将学习如何构建和训练卷积神经网络。课程分为两部分:首先,我们将学习如何组合不同的网络层来构建CNN架构;其次,我们将探讨训练CNN所需的具体步骤和实用技巧。


第一部分:构建卷积神经网络

上一节我们介绍了卷积层和池化层等基础组件。本节中,我们将看看如何将这些组件组合起来,并介绍其他关键的网络层。

卷积神经网络中的层

卷积神经网络主要由几种类型的层构成。我们已经学习了卷积层、池化层和全连接层。接下来,我们将介绍归一化层、Dropout层以及激活函数。

归一化层

归一化层的基本思想是计算输入数据的统计量(如均值和标准差),然后使用这些统计量对数据进行归一化。模型随后会学习一个最优的数据分布。

具体来说,归一化层学习参数,用于对输入数据进行缩放和偏移。所有归一化层都遵循以下两个步骤:

  1. 将输入数据归一化为均值为0、标准差为1的单位高斯分布。
  2. 使用可学习的参数对归一化后的数据进行缩放和偏移。

不同归一化层的主要区别在于它们计算统计量的方式。

层归一化是目前深度学习中最常用的归一化层之一,尤其在Transformer模型中。其工作原理如下:

  • 输入数据 X 的形状为 (N, D),其中 N 是批次大小,D 是特征维度。
  • 对于每个样本,独立计算其沿 D 维度的均值和标准差。
  • 使用可学习的缩放参数 γ 和偏移参数 β 对归一化后的数据进行变换。

公式LayerNorm(x) = γ * ( (x - μ) / σ ) + β,其中 μσ 是每个样本的均值和标准差。

对于卷积神经网络,输入数据通常具有通道、高度和宽度维度。层归一化会计算每个样本在所有通道、高度和宽度维度上的单一均值和标准差。

Dropout层

Dropout是一种在CNN中使用的正则化技术。其基本思想是在训练过程中引入随机性,以提升模型的泛化能力。

具体操作是:在前向传播过程中,随机将某一层中一定比例的激活值置零。这个比例 p 是一个超参数,常用值为0.5或0.25。

在测试阶段,不再进行Dropout操作。但为了保持输入到后续层的数值量级一致,需要将激活值乘以 p

Dropout迫使网络学习冗余的特征表示,防止模型过度依赖某些特定的特征组合,从而有助于泛化。

激活函数

激活函数为模型引入非线性,是神经网络的关键组成部分。

历史上,Sigmoid函数曾被广泛使用,但其存在梯度消失问题。当输入值非常大或非常小时,Sigmoid的梯度接近于零,导致深层网络难以训练。

ReLU函数解决了Sigmoid在正区间的梯度问题,其公式为 f(x) = max(0, x)。ReLU计算高效,但在负区间梯度为零。

近年来,更平滑的激活函数如 GELUSiLU 变得流行。GELU是高斯误差线性单元,其公式为 f(x) = x * Φ(x),其中 Φ(x) 是标准高斯分布的累积分布函数。它在接近零的区域提供了非零梯度,是目前Transformer模型中的主流激活函数。

激活函数通常放置在卷积层或全连接层之后。


第二部分:训练卷积神经网络

现在我们已经了解了CNN的各个组成部分,接下来看看如何将它们组合成有效的架构,并探讨训练过程。

CNN架构实例

历史上,AlexNet 是首个在ImageNet上取得巨大成功的CNN。随后,VGG 网络成为一种标准架构。

VGG网络的特点是大量使用堆叠的 3x3卷积层。使用多个小卷积核堆叠,而非单个大卷积核,有两个主要优势:

  1. 参数更少:三个3x3卷积层的参数数量少于一个7x7卷积层。
  2. 表达能力更强:更多的非线性激活函数允许模型学习更复杂的特征。

三个步长为1的3x3卷积层,其有效感受野相当于一个7x7的卷积层。

残差网络

随着网络层数加深,一个反直觉的现象出现了:更深的网络有时在训练集和测试集上的表现都更差。这并非过拟合,而是优化困难

理论上,更深的网络应能表示更浅网络的所有函数(例如,通过将某些层设置为恒等映射)。但在实践中,让深层网络学习恒等映射非常困难。

残差网络 通过引入残差连接解决了这个问题。在残差块中,输入 x 被直接添加到经过若干卷积层变换后的输出 F(x) 上。

公式H(x) = F(x) + x

这样,如果网络不需要这些卷积层进行变换,它只需将 F(x) 学习为接近零,即可轻松实现恒等映射 H(x) ≈ x。这使得深层网络的训练变得可行,并催生了如ResNet-152等超过百层的模型。

权重初始化

权重初始化的好坏直接影响训练过程。初始化值过小会导致信号在传播中消失;过大则会导致信号爆炸。

Kaiming初始化(又称He初始化)是针对使用ReLU激活函数的网络的一种有效初始化方法。对于全连接层,权重从均值为0、标准差为 sqrt(2 / fan_in) 的高斯分布中采样,其中 fan_in 是层的输入单元数。对于卷积层,fan_inkernel_size * kernel_size * in_channels

这种初始化方法能确保各层激活值的方差在传播过程中保持大致稳定。


第三部分:训练流程与实用技巧

构建好网络架构后,我们来看看如何准备数据并有效地训练模型。

数据预处理与增强

对于图像数据,标准的预处理是进行逐通道归一化:计算数据集中所有图像在每个颜色通道(R, G, B)上的均值和标准差,然后在训练时对每张输入图像进行减去均值、除以标准差的操作。

数据增强是在训练时对图像应用随机变换,以增加数据多样性、防止过拟合的常用技术。以下是一些常见的数据增强方法:

  • 水平翻转:适用于大多数自然场景图像。
  • 随机缩放裁剪:先随机缩放图像,再从缩放后的图像中随机裁剪出目标大小的区域。这是最常用的增强策略之一。
  • 色彩抖动:随机调整图像的亮度、对比度、饱和度和色调。
  • 随机遮挡:随机将图像中的部分区域置为固定值(如灰色),模拟遮挡情况。

在测试阶段,为了进一步提升性能,可以使用测试时增强:对同一张测试图像进行多种不同的增强(如不同裁剪、翻转),将模型对所有增强版本的结果进行平均,作为最终预测。

迁移学习

在实际项目中,我们往往没有海量的标注数据。迁移学习是利用在大规模数据集(如ImageNet)上预训练好的模型,来帮助我们在较小数据集上取得好性能的强大技术。

根据目标数据集的大小和与预训练数据集的相似度,可以采用不同策略:

  1. 数据量少,相似度高:冻结预训练模型的所有层,仅训练新替换的最后一层(分类头)。这相当于将预训练模型作为一个固定的特征提取器。
  2. 数据量中等,相似度高:使用预训练权重初始化模型,然后微调所有层。
  3. 数据量少,相似度低:尝试寻找在更相关领域预训练的模型。这是一个具有挑战性的场景。
  4. 数据量大,相似度低:可以从头开始训练,但使用预训练初始化可能仍有帮助。

超参数选择与调试

训练模型时,遵循一个系统的调试流程很重要:

  1. 在小样本上过拟合:使用极少量数据(如1-5张图片)训练模型。如果模型无法将训练损失降到接近零,说明代码可能存在bug或模型结构有问题。这也有助于确定学习率的大致范围。
  2. 观察训练/验证曲线
    • 如果训练损失和验证损失都在下降,且准确率在上升,可以继续训练。
    • 如果训练损失下降但验证损失上升(或准确率差距拉大),这是过拟合的迹象,需要加强正则化(如增加Dropout率、数据增强)或收集更多数据。
  3. 超参数搜索:相比于网格搜索,在定义的超参数范围内进行随机搜索通常更高效,因为它能更充分地探索对性能影响大的关键参数。

总结

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

  1. CNN的核心层组件,包括归一化层、Dropout层和现代激活函数(如GELU)。
  2. 经典的CNN架构设计思想,如VGG中使用小卷积核堆叠,以及ResNet中革命性的残差连接如何解决了深层网络的优化难题。
  3. 训练CNN的完整流程:从数据预处理、增强,到利用迁移学习应对数据不足的挑战,再到系统性的超参数调试策略。

掌握这些构建和训练CNN的知识,是解决实际计算机视觉问题的基础。

007:循环神经网络

概述

在本节课中,我们将要学习序列建模,特别是循环神经网络。我们将了解RNN的基本原理、如何构建和训练它们,以及它们与之前学习的固定输入/输出网络的区别。我们还将探讨RNN的一些变体,如LSTM,并了解RNN概念如何影响现代序列模型。


上节课内容回顾与澄清

上一节我们介绍了神经网络训练中的Dropout和归一化技术。本节中我们来看看一些需要澄清的要点。

关于Dropout在测试时的缩放问题:在训练时,如果以概率P丢弃了神经元,那么在测试时,为了保持期望输出一致,需要将激活值乘以(1 - P)。请注意,有些实现中P代表保留神经元的概率,此时测试时无需缩放。

关于归一化:层归一化可以帮助缓解权重初始化不当引起的问题,但并不能完全替代良好的初始化。在某些需要精确空间位置信息的问题中,归一化可能会损害性能,因为它会减去均值并除以标准差,从而丢失部分信息。


从固定输入到序列建模

到目前为止,我们主要讨论的是具有固定大小输入和输出的标准神经网络。现在,我们将转向处理可变长度序列的模型。

序列建模任务主要分为三类:

  • 一对一:固定输入到固定输出(标准网络)。
  • 一对多:固定输入到可变长度输出(如图像描述生成)。
  • 多对一:可变长度输入到固定输出(如视频分类)。
  • 多对多:可变长度输入到可变长度输出(如逐帧视频分类或机器翻译)。本节课我们将主要关注多对多场景。

什么是循环神经网络?

循环神经网络的核心思想是处理输入序列X并产生输出序列y。RNN的关键特性在于其循环性,它维护一个内部状态(或隐藏状态),该状态随着序列的处理而更新。

RNN的计算可以通过“展开”图来理解。在每个时间步t

  1. 新的隐藏状态 h_t 由前一个隐藏状态 h_{t-1} 和当前输入 x_t 共同决定。
  2. 输出 y_t 由当前隐藏状态 h_t 决定。

用公式描述如下:

  • 隐藏状态更新h_t = f(W_{hh} * h_{t-1} + W_{xh} * x_t),其中 f 是激活函数(如 tanhReLU)。
  • 输出计算y_t = W_{hy} * h_t

注意W_{hh}, W_{xh}, W_{hy} 这些权重矩阵在所有时间步是共享的。


一个具体的RNN前向传播例子

为了更直观地理解,我们来看一个手动构造的简单RNN例子。

任务:给定一个由0和1组成的序列,当出现连续两个1时输出1,否则输出0。

隐藏状态设计:我们将隐藏状态 h_t 设计为一个三维向量 [当前输入值, 前一个输入值, 常数1]。初始化 h_0 = [0, 0, 1]

权重矩阵设置(使用ReLU激活函数以使计算简单):

  • W_{xh}:将输入 x_t (0或1) 映射到隐藏状态维度。设为 [[100], [0], [0]],这样当 x_t=1 时得到 [100, 0, 0],当 x_t=0 时得到 [0, 0, 0]
  • W_{hh}:将前一个隐藏状态 h_{t-1} 映射到当前隐藏状态计算的一部分。设为 [[0,0,0], [1,0,0], [0,0,1]]。其作用是:
    • 第一行全0:确保“当前值”仅由当前输入 x_t 决定。
    • 第二行 [1,0,0]:将 h_{t-1} 中的“当前值”复制到 h_t 的“前一个值”位置。
    • 第三行 [0,0,1]:保持常数1不变。
  • W_{hy}:将隐藏状态映射到输出。设为 [1, 1, -1]。这样,输出 y_t = max(0, 1*当前值 + 1*前一个值 - 1*1)。当连续两个1时(当前值=1,前一个值=1),结果为1;否则结果小于等于0,ReLU后输出为0。

通过这个构造,RNN就能完成检测连续两个1的任务。


RNN的训练:反向传播与梯度问题

训练RNN同样使用反向传播算法,但由于参数在时间步上共享,该方法被称为通过时间的反向传播

梯度计算:总损失L是各时间步损失 L_t 之和。计算参数 W 的梯度时,需要对所有时间步的梯度贡献求和:dL/dW = Σ_t (dL_t/dW)

梯度消失与爆炸:这是训练RNN(尤其是长序列)的主要挑战。

  • 梯度爆炸:可以通过梯度裁剪解决。
  • 梯度消失:更为棘手。原因在于,计算早期时间步的梯度时,需要连续乘以权重矩阵 W_{hh} 和激活函数(如 tanh)的导数。如果这些乘积累积的模长小于1,梯度会指数级衰减到接近零,导致早期时间步的参数几乎无法更新。

截断的通过时间的反向传播:为了解决长序列训练时的内存和计算问题,实践中常将长序列分成较短的片段(窗口)进行训练。在每个窗口内进行完整的BPTT,窗口之间的隐藏状态会传递,但梯度计算被截断。这是一种近似,但大大提高了训练效率。


RNN的应用:字符级语言模型

RNN一个经典应用是字符级语言模型。它接收一个字符序列,并预测下一个字符的概率分布。

工作原理

  1. 输入字符通常通过嵌入层转换为向量表示,而非直接使用one-hot编码。
  2. RRNN根据当前输入字符和之前的隐藏状态更新隐藏状态。
  3. 输出层(通常是Softmax)基于当前隐藏状态预测词汇表中每个字符作为下一个字符的概率。
  4. 在训练时,目标就是真实的下一个字符。
  5. 在生成(测试)时,模型将预测出的字符作为下一个时间步的输入,循环进行,从而生成文本。

这种模型曾非常有效,能够生成看起来合理的文本甚至代码。它也是现代大语言模型(预测下一个token)的思想先驱。

关于采样:生成时如果总是选择概率最高的字符(贪婪解码),会导致重复、枯燥的输出。因此,通常基于Softmax输出的概率分布进行随机采样,或使用束搜索等策略。


RNN的优缺点

优点

  • 可以处理任意长度序列:理论上没有上下文长度限制。
  • 模型大小固定:不随输入序列变长而增加。
  • 概念对称:在每个时间步执行相同操作。

缺点

  • 顺序计算,难以并行:必须按顺序计算每个时间步,训练慢。
  • 长程依赖问题:由于梯度消失,难以学习序列中相隔很远的元素之间的关系。
  • 信息瓶颈:所有历史信息必须压缩到固定大小的隐藏状态中,容易丢失早期信息。


RNN的变体:长短期记忆网络

为了缓解梯度消失和长程依赖问题,研究者提出了LSTM。

LSTM核心思想:引入一个细胞状态作为信息传输的“高速公路”,并利用三个“门”结构(输入门、遗忘门、输出门)来精细控制信息的流入、保留和流出。

  • 细胞状态:贯穿整个序列,主要进行线性操作,便于信息长期保存。
  • 门机制:决定哪些信息需要被记住或遗忘。

LSTM通过这种设计,显著改善了RNN处理长序列的能力,并在Transformer出现之前成为序列建模的主流选择。其思想(如门控和捷径连接)也与ResNet等现代架构有相通之处。


现代视角:RNN的复兴

近年来,RNN的理念在状态空间模型(如Mamba、RWKV)中有所复兴,以解决Transformer的某些局限:

与Transformer相比的潜在优势

  • 无限上下文长度:不受预设上下文窗口限制。
  • 线性推理复杂度:处理长度为N的序列仅需O(N)计算,而Transformer的自注意力机制是O(N^2)。
  • 高效推理:隐藏状态概括了历史,无需像Transformer那样存储所有过去的键值对。

这些新模型试图结合RNN的推理效率与Transformer的表达能力,是当前序列建模的研究热点之一。


总结

本节课中我们一起学习了循环神经网络。我们从序列建模的基本概念出发,详细探讨了RNN的前向传播机制,并通过一个具体例子加深理解。我们分析了RNN训练中的挑战,特别是梯度消失/爆炸问题以及BPTT算法。我们还了解了RNN在字符级语言模型等任务中的应用,认识了其优缺点。为了克服基础RNN的缺陷,我们介绍了LSTM这一重要变体及其核心思想。最后,我们看到了RNN的基本概念如何在现代状态空间模型中重新获得关注,以追求更高效的序列建模。下一节课,我们将学习注意力机制和Transformer架构。

008:注意力机制与Transformer

在本节课中,我们将要学习注意力机制和Transformer架构。注意力是一种全新的神经网络基本单元,它从根本上对向量集合进行操作。Transformer则是一种以自注意力为核心的神经网络架构,如今已成为深度学习几乎所有领域的主流模型。

从循环神经网络到注意力机制

上一节我们介绍了循环神经网络及其处理序列数据的能力。本节中,我们来看看RNN在处理序列到序列任务(如机器翻译)时遇到的一个核心瓶颈问题。

在传统的编码器-解码器RNN架构中,编码器将整个输入序列的信息压缩到一个固定长度的上下文向量 C 中。解码器则基于这个向量生成输出序列。然而,当输入序列很长时,将所有信息压缩到一个固定大小的向量中会形成通信瓶颈,模型性能会因此受限。

为了解决这个问题,我们引入注意力机制。其核心思想是:在解码器生成输出的每一个时间步,都允许它“回顾”整个输入序列,并动态地决定需要关注输入序列的哪些部分,而不是依赖于一个固定的总结向量。

以下是注意力机制在RNN中的工作步骤:

  1. 计算对齐分数:对于解码器当前的隐藏状态(查询向量),我们计算它与编码器所有隐藏状态(数据向量)的相似度。这可以通过一个简单的可学习函数实现,例如:
    alignment_score = F_att(concat(decoder_hidden_state, encoder_hidden_state_i))
    其中 F_att 是一个线性层,将拼接后的向量映射为一个标量分数。

  2. 生成注意力权重:将对齐分数输入Softmax函数,将其转换为一个概率分布,即注意力权重。这些权重之和为1,表示解码器在当前时间步对输入序列各个部分的关注程度。

  3. 计算上下文向量:将编码器的所有隐藏状态按照上一步得到的注意力权重进行加权求和,生成一个新的、针对当前时间步的上下文向量 C_t
    C_t = sum(attention_weight_i * encoder_hidden_state_i)

  4. 生成输出:将这个动态生成的上下文向量 C_t(而非固定的 C)与解码器上一时刻的隐藏状态和输出一起,输入解码器RNN单元,以生成当前时刻的输出。

这个机制的妙处在于它是完全可微分的。我们不需要告诉模型输入和输出单词之间应该如何对齐,模型会通过端到端的梯度下降训练,自动学会在生成每个输出单词时应该关注输入序列的哪些部分。

此外,注意力权重还为我们提供了模型可解释性。通过可视化注意力矩阵,我们可以看到模型在生成输出时“看”向了输入的哪些部分。例如,在英法翻译中,我们常能看到对角线结构,表明模型学会了单词间的大致对应关系。

注意力作为一种通用算子

上一节我们看到了注意力如何增强RNN。本节中,我们来将注意力机制抽象出来,使其成为一个独立、强大的神经网络基本单元。

注意力算子的核心是处理查询向量数据向量之间的关系。在机器翻译的例子中,查询向量是解码器的隐藏状态,数据向量是编码器的隐藏状态。

为了使其更通用和高效,我们进行以下改进:

  1. 使用缩放点积计算相似度:我们使用点积作为相似度函数,但为了稳定训练(防止梯度消失),会将其除以查询/键向量维度的平方根。
    similarity = (Q · K^T) / sqrt(d_k)

  2. 处理多个查询向量:我们可以一次性处理一组查询向量 Q(矩阵),通过矩阵乘法高效地计算所有查询与所有数据向量之间的相似度矩阵。

  3. 引入键和值:我们将每个数据向量投影到两个不同的空间:键向量用于与查询向量计算相似度,值向量用于在计算输出时进行加权求和。这增加了模型的灵活性,允许模型学习以不同方式“理解”和“使用”数据。
    K = X * W_k, V = X * W_v
    其中 W_kW_v 是可学习的权重矩阵。

  4. 自注意力:当查询向量和数据向量来自同一组输入时,就得到了自注意力。此时,我们从每个输入向量生成三个投影:查询、键和值。
    Q = X * W_q, K = X * W_k, V = X * W_v
    自注意力允许序列中的每个元素直接与序列中的所有其他元素进行交互,从而高效地捕获长距离依赖关系。

自注意力层具有一个有趣的性质:它对输入向量的顺序是不变的(置换等变性)。打乱输入向量的顺序,输出向量也会以相同的方式被打乱,但内容不变。这意味着自注意力本质上是对无序集合进行操作。为了注入位置信息,我们通常需要添加位置编码

Transformer架构

上一节我们定义了自注意力这个强大的基本单元。本节中,我们来看看如何以它为核心构建完整的神经网络架构——Transformer。

Transformer由多个相同的Transformer块堆叠而成。每个块主要包含两个子层:

  1. 多头自注意力层:我们并行运行多个(h个)独立的注意力头,每个头都有自己的 W_q, W_k, W_v 投影矩阵。这允许模型在不同的表示子空间中共同关注来自不同位置的信息。所有头的输出被拼接起来,并通过一个线性投影层进行融合。

  2. 前馈网络:这是一个简单的两层全连接网络(例如,一个ReLU激活函数夹在两个线性层之间),独立地应用于每个位置(每个向量)。它用于对自注意力层提取的信息进行进一步的处理和转换。

为了稳定深度网络的训练,每个子层都包裹着残差连接层归一化。因此,一个Transformer块的计算可以概括为:
Z = LayerNorm(X + MultiHeadAttention(X))
Y = LayerNorm(Z + FFN(Z))

Transformer架构是高度可并行化的,因为自注意力层中的核心操作(矩阵乘法)和FFN层都可以在序列的所有位置上并行计算。这与RNN的序列依赖性形成了鲜明对比。虽然自注意力的计算复杂度与序列长度的平方成正比(O(n²)),但通过利用现代硬件(如GPU)的大规模并行计算能力,我们可以有效地进行扩展。

如今,Transformer已成为自然语言处理、计算机视觉、音频处理等领域的基石模型架构。从最初的数亿参数,发展到如今拥有万亿参数的巨型模型,其核心架构保持了惊人的一致性和可扩展性。

总结

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

  1. 注意力机制的起源:为了解决RNN在序列到序列任务中的信息瓶颈问题,我们引入了允许解码器动态关注输入序列不同部分的注意力机制。
  2. 注意力作为通用算子:我们将注意力抽象为处理查询、键、值向量的可微计算单元,并推广出自注意力,使其能处理单一序列内部的关系。
  3. Transformer架构:我们构建了以多头自注意力和前馈网络为核心、辅以残差连接和层归一化的Transformer块。这种架构高度并行、可扩展,已成为当前深度学习应用中最主流的模型骨架。

Transformer及其核心的注意力机制,为我们提供了一种强大而灵活的方式来建模数据中的复杂关系,是理解现代深度学习发展的关键。

009:目标检测、图像分割与可视化

在本节课中,我们将学习计算机视觉中的几个核心任务:目标检测与图像分割。我们还将探讨如何可视化和理解神经网络所学到的内容。课程内容将涵盖这些领域的基础算法和重要概念,旨在为初学者提供一个清晰、全面的入门指南。

从序列模型到视觉Transformer

上一节课我们讨论了从循环神经网络(RNN)到Transformer模型的演变。我们看到,Transformer模型由编码器和解码器组成,编码器包含多层多头自注意力机制、层归一化以及多层感知机。这种架构能够有效地编码输入序列,并在需要时解码生成输出序列,如文本或图像。

Transformer模型,特别是自注意力机制,在许多应用中表现优于传统的RNN和卷积方法,尽管其计算和内存需求更高。这种优势源于其强大的序列建模能力。

视觉Transformer(ViT)简介

为了将Transformer应用于图像处理,我们需要将图像转换为序列。具体做法是将图像分割成多个小块(patches),每个小块被视为一个“令牌”(token)。这些令牌通过线性投影转换为向量,并输入到Transformer中。

然而,将图像分割成小块会丢失其二维空间位置信息。为了解决这个问题,我们引入了位置编码(positional embedding),可以是简单的序列编号,也可以是二维坐标编码,以告知模型每个令牌在原始图像中的位置。

在图像分类任务中,一种常见做法是添加一个特殊的类别令牌(class token)。这个可学习的参数在输出时被转换为类别概率向量。另一种方法是直接对所有输出令牌进行池化操作,然后投影到类别概率空间。

Transformer的优化与变体

原始的Transformer架构经过多年发展,出现了许多优化变体,旨在提升性能或训练稳定性:

  • 层归一化位置:将层归一化置于残差连接之前,有助于模型学习恒等映射。
  • RMSNorm:使用均方根归一化,有时能使训练更稳定。
  • 门控MLP:在MLP层引入门控非线性激活,增加模型表达能力而不显著增加参数量。
  • 混合专家(MoE):使用多个并行的MLP层作为“专家”,通过路由机制将不同令牌分配给不同专家处理,能在不显著增加计算量的情况下提升模型容量和鲁棒性。专家数量是一个需要预设的超参数。

计算机视觉核心任务概述

计算机视觉涵盖多种任务,其中两个基础且重要的任务是:

  • 图像分类:为整张图像分配一个类别标签。
  • 语义分割:为图像中的每一个像素分配一个类别标签。

语义分割与全卷积网络

语义分割的目标是生成与输入图像同尺寸的像素级标签图。一种朴素的方法是独立处理每个像素及其周围区域,但这计算效率极低。

更高效的方法是使用全卷积网络。FCN接收整张图像作为输入,并直接输出分割图。为了处理高分辨率图像并减少计算量,FCN通常采用编码器-解码器结构:

  • 编码器:通过卷积和下采样(如池化或步长卷积)逐步降低空间分辨率,扩大感受野,提取高级语义特征。
  • 解码器:通过上采样操作逐步恢复空间分辨率,最终输出与输入同尺寸的分割图。

以下是常见的上采样方法:

  • 最近邻上采样:简单复制像素值。
  • 反池化:如果在编码器使用了最大池化并记录了最大值位置,解码器可使用反最大池化将值放回原位置。
  • 转置卷积:一种可学习的上采样方法,通过卷积操作生成更大尺寸的输出。

训练FCN时,损失函数通常是对每个像素计算的分类损失(如交叉熵损失)的总和。这需要像素级的标注数据作为监督信号。

一个著名的FCN变体是U-Net,它在解码器阶段将编码器对应层的特征图通过跳跃连接(skip connections)与解码器特征图融合,这有助于保留细节信息并生成更清晰的分割边界,在医学图像分割等领域仍被广泛使用。

从语义分割到实例分割与目标检测

语义分割只区分像素类别,不区分同一类别的不同个体。实例分割则要求区分出每个独立的物体实例。这自然引出了目标检测任务,即不仅要识别物体类别,还要用边界框定位每个实例。

对于单目标检测,网络可以同时输出类别分数和边界框坐标(如中心点x, y,高度h,宽度w),并使用多任务损失(如分类的交叉熵损失和边界框的L2回归损失)进行训练。

对于多目标检测,直接回归所有目标的坐标不具可扩展性。早期解决方案包括:

  • 滑动窗口:在图像上滑动不同大小和位置的窗口,对每个窗口进行分类。但窗口组合数量巨大,效率低下。
  • 区域提议网络(RPN):先由一个网络(RPN)快速生成可能包含物体的候选区域(Region Proposals),再对每个候选区域进行分类和边界框微调。R-CNN系列算法(如Fast R-CNN)即基于此思想,但需要两阶段处理,计算量较大。

单阶段目标检测器:YOLO

为了提升速度,出现了单阶段检测器,如YOLO。YOLO将图像划分为S×S的网格,每个网格单元负责预测以该单元为中心的B个边界框及其置信度(包含物体的概率)和类别概率。网络一次性完成所有预测,速度极快。通过置信度阈值和非极大值抑制(NMS)等后处理,筛选出最终的检测结果。YOLO及其后续变体因其速度和精度的平衡,在工业界应用广泛。

基于Transformer的目标检测:DETR

DETR是一个使用Transformer架构进行端到端目标检测的模型。其流程如下:

  1. 使用CNN骨干网络从图像中提取特征图,并将其转换为一系列令牌,加入位置编码。
  2. 令牌输入Transformer编码器进行特征交互。
  3. 在解码器端,引入一组固定数量的对象查询(object queries,可学习参数)。每个查询通过解码器中的自注意力和与编码器输出的交叉注意力,学习“询问”图像中某个特定物体的信息。
  4. 解码器的每个输出通过一个前馈网络,预测一个边界框(坐标)和类别标签(包括“无物体”类别)。

DETR简化了检测流程,无需手工设计锚框或NMS后处理。对象查询的数量设定了图像中可检测物体的最大数量。模型通过二分图匹配损失进行训练,将预测与真实标注进行最优匹配。

实例分割:Mask R-CNN

Mask R-CNN是在目标检测框架(如Faster R-CNN)基础上的扩展,用于实例分割。它在原有分支(分类和边界框回归)之外,增加了一个并行的全卷积网络分支,用于为每个检测到的物体预测一个二值掩码(mask),精确勾勒出物体的像素级轮廓。

神经网络的可视化与理解

理解神经网络内部的工作机制对于调试模型、建立信任(尤其在医疗等关键领域)至关重要。以下是一些核心方法:

1. 可视化滤波器权重
对于网络第一层的卷积滤波器,可以直接将其权重可视化为小图像,通常能看到其学习到的边缘、颜色或纹理等基础模式。

2. 显著性图
通过计算网络输出(如某个类别的得分)相对于输入图像每个像素的梯度,可以得到显著性图。图中高亮区域表示改变这些像素值对类别得分影响最大,即这些区域对网络的决策最重要。

3. 类激活映射

  • CAM:针对卷积神经网络,通过将最后一个卷积层的特征图按其对应分类权重重加权求和,并上采样回原图尺寸,生成显示哪些区域激活了特定类别的热力图。但其局限性是只能应用于最后一个卷积层。
  • Grad-CAM:CAM的泛化版本。它使用流向目标卷积层的梯度信息作为权重,因此可以应用于网络中间的任何卷积层,提供更灵活的可视化。

4. Transformer的注意力可视化
Transformer模型天然具备可解释性。其自注意力机制生成的注意力权重矩阵,可以直接显示输入序列中不同部分(如图像块)之间的关联强度,这本身就是一种强大的可视化工具,便于理解模型关注了图像的哪些区域以做出决策。

总结

本节课我们一起学习了计算机视觉中的几个核心任务与关键技术。我们从视觉Transformer的回顾开始,深入探讨了语义分割(使用全卷积网络如U-Net)、目标检测(从两阶段的R-CNN到单阶段的YOLO,以及基于Transformer的DETR)以及实例分割(Mask R-CNN)的基本原理。最后,我们了解了如何通过可视化技术(如显著性图、Grad-CAM和注意力图)来窥探和理解神经网络内部的决策过程。这些知识构成了现代计算机视觉应用的重要基础。

010:视频理解 🎥

在本节课中,我们将要学习视频理解的基础知识。视频可以看作是二维图像加上时间维度,这带来了新的挑战和机遇。我们将探讨如何将图像分类的技术扩展到视频领域,并介绍专门用于处理视频数据的模型架构。


视频理解简介

视频是二维图像加上时间维度。我们处理的不仅是三维图像(高度、宽度、通道),而是四维数据:时间(T)、高度(H)、宽度(W)和通道(C)。视频可以看作是一系列连续的图像帧。

一个典型的任务是视频分类。给定一个视频,例如一个人在跑步,我们希望训练一个深度学习模型来分类这个人是在游泳、跑步还是跳跃。这与图像分类类似,但输入是视频帧的时间序列。

视频理解与图像分类的一个关键区别在于,我们通常更关注视频中的动作,而不仅仅是场景或物体。例如,我们关心的是人的活动。

视频数据通常非常庞大。标准清晰度视频每分钟可能占用约1.5GB的存储空间,而高清视频(1920x1080)每分钟可能占用约10GB。我们无法直接将如此庞大的数据全部加载到GPU内存中。

一个简单的解决方案是缩小视频的尺寸。我们可以在时间和空间维度上进行压缩。例如,对于一个3.2秒的视频,我们可以每秒只采样5帧,并将空间分辨率降低到112x112,从而将视频大小减小到约588KB。

对于长视频的训练,一种常见的方法是使用剪辑。我们训练模型对短片段(例如几秒钟的视频)进行分类。在训练时,我们从长视频中滑动采样多个片段作为训练数据。在测试或推理时,我们同样采样多个片段,运行模型,然后对预测结果进行平均,作为整个长视频的预测。


简单的视频分类模型

视频本质上是一系列图像帧。因此,最简单的方法是将其视为独立的图像进行处理。

我们可以直接运行单帧卷积神经网络。这意味着我们使用已经学到的图像分类器,对视频的每一帧单独处理。例如,对于一个跑步的视频,每一帧可能都预测为“跑步”。然后我们对所有帧的预测结果进行平均,得到视频的最终预测。这种方法通常是一个强大且简单的基线模型,尤其是当视频帧之间变化不大时。

这种方法的关键问题是如何采样帧。一种简单的方法是随机采样。例如,对于一个一小时的视频,我们可能每分钟采样一帧。虽然这能给出一些结果,但可能不是最智能的采样策略。其他方法尝试提出更智能的采样策略,例如根据已采样帧的信息决定下一步在哪里采样。


融合策略:晚期融合与早期融合

除了单帧处理,我们还可以在特征层面进行跨帧融合。

晚期融合 的思路是:我们仍然对每一帧使用2D CNN提取特征。假设有T帧,我们会得到T个特征图。然后,我们可以将所有特征图展平并连接成一个巨大的特征向量,这个向量包含了所有帧的信息。接着,我们使用全连接网络将这个向量映射到类别分数。这种方法称为“晚期融合”,因为我们在非常靠后的阶段才合并特征。

晚期融合的一个缺点是会引入大量参数。连接后的特征向量非常庞大,映射它的全连接层参数很多,效率不高。

另一种晚期融合的方法是使用池化操作。我们对T个帧的特征进行池化(例如平均池化或最大池化),得到一个固定长度的特征向量,然后再用线性层映射到类别分数。这不会增加特征向量的长度,但池化操作可能会丢失一些重要信息。

晚期融合的一个潜在问题是,当我们在网络后期才融合特征时,一些低级的运动信息(例如人物脚部的上下移动)可能已经在深层特征中丢失了,因为这些特征更侧重于高级语义信息。

为了解决这个问题,我们可以尝试 早期融合

在早期融合中,我们从一开始就聚合时间信息。具体做法是:将输入重塑为 (3*T) x H x W,即将所有帧的通道堆叠起来。然后,我们使用一个2D卷积层,将通道维度从 3*T 映射到 D。这样,我们在网络的第一层就试图处理所有帧的信息。网络的其余部分则是标准的2D CNN。这种方法试图在早期捕捉运动信息。

早期融合的缺点是,我们试图在单个卷积层中捕获所有时间信息,这可能过于雄心勃勃,难以实现。


3D卷积神经网络

介于早期融合和晚期融合之间,我们可以使用 3D卷积神经网络,这被称为 慢速融合

其核心思想是使用3D版本的卷积和池化操作,在网络中逐渐融合时空信息,而不是在早期或晚期一次性完成。

3D卷积 与2D卷积类似,但多了一个时间维度。在2D卷积中,滤波器在空间维度(高度和宽度)上滑动。在3D卷积中,滤波器在时间、高度和宽度三个维度上滑动,形成一个立方体。例如,一个 3x3x3 的3D卷积核会在3个连续帧、3个像素高、3个像素宽的立方体区域上进行计算。

通过堆叠多个3D卷积和池化层,网络可以逐渐增大在时间和空间上的感受野,从而学习到视频中的时空模式。

与早期融合(使用2D卷积但通道维度包含所有时间信息)相比,3D卷积的主要优势在于具有 时间平移不变性。在早期融合中,滤波器在时间维度上是“全连接”的,这意味着要识别在不同时间点发生的相同运动模式(例如从蓝色到橙色的变化),需要学习不同的滤波器。而3D卷积的滤波器只在局部时间窗口内操作,并可以滑动,因此同一个滤波器可以识别在不同时间点出现的相同运动模式,提高了表示效率。

我们可以可视化3D卷积网络学习到的滤波器。有些滤波器学习的是静态的颜色或边缘模式(类似于图像滤波器),而有些则学习到了明显的时空运动模式。


视频数据集与模型性能

一个著名的视频分类数据集是 Sports-1M,包含487种细粒度的体育类别。

在这个数据集上,一些有趣的实验结果包括:

  • 单帧模型(将视频视为图像)取得了77.7%的Top-5准确率,是一个非常强的基线。
  • 早期融合模型 的性能略差。
  • 晚期融合模型 性能稍好。
  • 3D卷积神经网络(以2014年的模型为例)带来了约2-3%的性能提升。

这个结果告诉我们,在设计视频分类器时,应该首先尝试简单的单帧模型。当然,在过去十年中,3D卷积网络已经有了巨大的进步。


双流网络:显式建模运动

空间(外观)信息和时间(运动)信息本质不同。人类仅凭运动信息(如关节点轨迹)就能很好地区分动作。因此,我们可以显式地分别建模外观和运动。

一种显式测量运动的方法是计算 光流。光流估计了相邻帧之间每个像素的运动矢量(dx, dy),描述了像素从一帧到下一帧的移动。水平光流和垂直光流可以分开可视化。

双流网络 包含两个并行的分支:

  1. 空间流:处理单帧RGB图像,负责识别外观。
  2. 时间流:处理多帧堆叠的光流图像(水平和垂直分量),负责识别运动。

两个分支分别进行预测,最后将它们的预测分数融合,得到最终的动作类别。实验表明,在某些数据集(如UCF-101)上,仅使用运动流(时间流)的性能甚至优于仅使用外观流(空间流),这可能是因为运动信息更不容易过拟合背景等无关信息。


建模长时依赖:循环网络与注意力机制

之前讨论的模型主要处理短片段。为了建模视频中的长时依赖关系,我们可以使用序列模型。

一种方法是使用 循环神经网络(RNN, LSTM)。我们可以用CNN(无论是单帧CNN还是3D CNN)从每个片段或帧中提取特征,然后将这些特征序列输入RNN/LSTM。这可以实现“多对一”的映射(整个视频输出一个标签)或“一对一对”的映射(每帧输出一个标签)。

我们可以进一步结合CNN和RNN,构建 循环卷积神经网络。其思想是将RNN中的矩阵乘法替换为2D卷积操作。这样,网络中的每个“状态”都是一个特征图(C x H x W),它依赖于上一时间步的同一层状态和同一时间步的上一层状态。这结合了卷积的空间处理能力和循环的时间建模能力。

然而,RNN难以并行处理长序列。因此,自注意力机制(如Transformer)成为了更受欢迎的选择。自注意力高度可并行化,能有效捕捉长距离依赖。

我们可以将自注意力机制扩展到3D。给定一个 C x T x H x W 的特征图,我们可以使用 1x1x1 的3D卷积来生成查询(Query)、键(Key)和值(Value)特征图。然后计算注意力权重,并聚合值特征。这种模块(常被称为“非局部块”)可以插入到现有的3D CNN架构中, powerful地融合时空信息。


利用图像网络知识:膨胀3D网络(I3D)

我们拥有大量在图像数据上预训练成功的2D CNN架构和权重。能否将它们用于视频?

膨胀3D网络(I3D) 的核心思想是“膨胀”现有的2D架构。具体做法是:将2D卷积核(如 KxK)复制并扩展到时域,变成3D卷积核(如 TxKxK)。例如,将ImageNet上预训练的2D Inception网络的权重进行膨胀,初始化对应的3D网络。

为了保持输入为静态图像时输出不变,可以将膨胀后的3D卷积核权重除以时间维度T。这样,我们就有了一个利用图像先验知识初始化的3D视频网络,然后可以在视频数据上进行微调。这种方法被证明非常有效,性能优于早期的双流网络。


超越分类:其他视频任务与多模态

视频理解不仅限于分类。

  • 时序动作定位:不仅要识别动作,还要定位动作在视频中发生的时间段。
  • 时空动作检测:在视频中同时定位动作发生的空间位置(边界框)和时间区间。

此外,视频通常包含 音频 信息。结合视觉和听觉的多模态理解能开启更多有趣的任务,例如:

  • 视觉引导的音频源分离:利用视频信息(如说话人的嘴型或乐器的演奏动作)来分离混合音频中的不同声源。
  • 音视频联合分类:使用Transformer等架构同时处理视觉片段和音频频谱图,进行更鲁棒的分类。

当前的研究热点还包括:

  • 高效视频理解:通过智能采样、模型选择等技术,减少需要处理的视频剪辑数量,提高效率。
  • 第一人称(Egocentric)视频理解:处理来自智能眼镜等设备的第一人称视角视频,理解复杂的社交互动。
  • 视频大语言模型:构建能够理解和生成视频描述的大型基础模型。

总结

本节课中,我们一起学习了视频理解的基础知识。我们从最简单的单帧图像分类方法开始,探讨了晚期融合和早期融合策略,并深入介绍了核心的3D卷积神经网络。我们还了解了显式建模运动的双流网络,以及用于处理长序列的循环网络和注意力机制。最后,我们看到了如何利用图像网络的预训练知识(I3D),并简要介绍了视频理解的其他任务和多模态扩展方向。视频理解是一个丰富且快速发展的领域,结合了计算机视觉、序列建模和多模态学习的多种技术。

011:大规模分布式训练

欢迎回到CS231n。今天第11讲,我们将讨论大规模分布式训练。这是一个令人兴奋的话题,因为如今所有神经网络的实践训练都基于此。无论是初创公司、工业界还是学术界的大型模型,大规模训练已成为深度学习的常态。这与10年前我们开设这门课程时的情况相比,发生了巨大变化。10年前,基本上所有模型都在单个GPU设备上训练,多设备训练相当少见。但如今,新的常态是在数十、数百、数千甚至数万台设备上并发训练模型。因此,我们需要开发新的算法和新的思维方式来实现这一点。

😊

作为贯穿今天讲座的一个运行示例,我们将大量讨论Llama 3 405B模型。这并非因为它是最好的或最有趣的模型,而是因为它是一个相当接近前沿的模型,并且实际分享了大量关于其训练方式、模型架构等实现细节。过去几年,谷歌、OpenAI、Anthropic等公司训练了许多真正强大惊人的模型,但它们基本上不再分享任何模型细节。对我而言,一个非常著名的引述标志着行业的一个重大转变,这出现在2023年的GPT-4论文中。当他们发布GPT-4模型时,他们写道:“考虑到像GPT-4这样大规模模型的竞争格局和安全影响,本报告(即他们关于GPT-4的论文)不包含关于架构的进一步细节,包括模型大小、硬件、训练计算、数据构建、训练方法或类似内容。”这基本上就是过去三年来大规模模型的最新技术状态。自GPT-4以来,他们不会告诉你任何信息。你能知道它是一个Transformer模型就算幸运了。

因此,Llama 3之所以引人注目,并非因为它是最好的模型,而是因为它是最开放的模型之一。这是一个由Meta训练并于2024年4月开源发布的大型语言模型。与OpenAI不同,其论文确实分享了大量关于模型训练的细节,虽然关于数据的信息不多,但分享了大量用于训练的系统基础设施信息。这让我们得以一窥当今大规模LLM的实际训练方式。顺便提一下,上个月(2025年4月)刚刚发布了新的Llama 4模型,开源领域已经有了稍好的模型,但Llama 4还没有论文。我希望几个月后论文发布时能读到它,看看我们能从新一代Llama训练中学到什么。但作为今天讲座的运行示例,我们将出于这个原因引用许多Llama 3 405B模型的例子。

今天我想讨论的基本上是两件事。一是关于GPU硬件,二是如何在大量GPU上进行训练。我想让大家了解这些模型实际执行的硬件是什么,以及我们需要使用哪些算法来在大量GPU上进行训练。

首先,我们将稍微讨论一下GPU硬件。GPU,对于不了解的人来说,是图形处理单元。这些是专门设计的协处理器,最初是为计算机图形学开发的,后来被发现是非常有用的通用并行处理器。在这个房间进行这个讲座非常合适,因为这是黄仁勋礼堂。黄仁勋是英伟达的CEO和创始人,英伟达是过去几十年来最大的公司,为游戏和AI生产GPU。这些东西最初是为图形学设计的,因为如果你想想计算机图形学,你需要在屏幕上生成大量像素,需要处理许多小的原始几何图形来生成这些像素。因此,在进行计算机图形学时,很自然地会进行大量并行计算。人们很快发现,这些为计算机图形学构建的硬件实际上也可以用于更通用的并行计算。在21世纪初,研究人员就发现了如何利用这些显卡进行通用并行编程。进入21世纪10年代,英伟达真正抓住了这一点,开发并推广了这些设备,旨在成为通用并行处理器。当时他们并不完全清楚它们将被用于什么,但他们有一个普遍的想法,即并行处理将变得重要。当深度学习在21世纪10年代初开始兴起时,英伟达充分利用了这一点。值得称赞的是,我认为他们很早就认识到了这个研究领域的潜力,甚至在21世纪10年代初就投入了大量资源,确保他们的硬件对深度学习训练真正有用。基于此,十多年来,这已成为人们训练大规模深度学习模型的主要方式,尽管正如我们将看到的,这种情况正在开始改变。但他们的芯片仍然是人们使用的主要芯片。

我总是喜欢观察这些设备的内部结构,看看里面有什么。这是一张英伟达H100的图片,它是当前深度学习训练的主力。下一代已经发布,但还不太容易获得,我还没有在上面训练过任何东西,所以这基本上是当前的技术水平。在这个英伟达H100 GPU的中间是这些计算核心,周围是80GB的HBM(高带宽内存)。你可以看到内存与计算核心是分离的,它们需要通过总线相互通信,在GPU内存和核心之间来回移动数据,速度约为每秒3TB,这是大量的比特移动。

现在,如果我们深入GPU核心内部,可以看到在中间的计算核心部分,有一块较小的内存,大约50MB的L2缓存,这比80GB的HBM内存小得多,但它非常接近实际的计算元件,因此计算核心可以更快地访问它。然后,真正的核心是这132个流式多处理器(SM)。这些就像是独立的并行核心,在某些方面比典型的CPU核心更强大,因为它们可以进行更多的并行计算,但在许多方面也比典型的CPU核心弱得多,因为它们往往时钟速度较低,无法进行那么多的指令预测和分支预测。因此,很难在GPU核心和CPU核心之间进行精确的苹果对苹果的比较,但我通常认为这些流式多处理器大致相当于一个CPU核心。我知道有人会回家仔细数屏幕上的小方块,你会发现实际上有144个,而我刚才说只有132个。为什么会这样?这是因为所有GPU硬件都使用一个称为“分档”的过程。在制造这些东西时,它们有如此多的晶体管和计算元件,无论投入多少资金,都无法完美制造出来。总有一些会出点问题。因此,他们在产品开发中为此做了规划。他们说,我们尝试制造一个芯片。理论上完整的芯片有144个核心,但没有一个芯片是完美的。但我们知道,其中相当一部分至少有132个核心能正常工作。因此,他们倾向于使用这种分档过程。这样,他们实际上可以通过只保证132个核心能开启,来销售更大比例的他们尝试生产的芯片。

然后,我们可以更深入地观察其中一个流式多处理器,看看GPU内部更多的情况。这只是H100内部132个活动流式多处理器中的一个。这里面有几个有趣的元素需要关注。首先,我们看到有256KB的L1缓存和寄存器文件。这延续了GPU内存层次结构的趋势。我们发现在学习深度学习时,实际上是在学习计算机体系结构,这有点意外。事实证明,内存层次结构对于深度学习和所有高性能计算都非常重要。总的趋势是,离计算核心越远,内存容量越大;离计算核心越近,内存容量越小,但速度要快得多。如果你编写在这些设备上运行的低级算法,了解这个内存层次结构并非常仔细地在不同层次之间传递数据是非常重要的。如果你编写高性能的GPU内核,你会花很多时间尝试优化这一点。

为了让你有个概念,H100中有三个级别的内存层次结构:256KB的L1缓存、50MB的L2缓存和80GB的HBM内存。这是H100中内存层次结构的三个主要级别。

😊

然后,我们还有128个FP32核心。这些是可以进行通用浮点运算的小型算术单元。具体来说,这128个FP32核心中的每一个都可以在一个时钟周期内计算 AX + B,其中A、X和B都是标量。然后,如果你把它们加起来,AX + B 基本上是一次乘法和一次加法,你有128个这样的核心,所以整个SM每个设备时钟周期可以执行256次浮点运算。

然后,我们还会看到用红色标出的部分,这是真正神奇的地方。除了这些FP32核心,还有这些张量核心。我认为这个名字有点用词不当,它们实际上是矩阵核心。每个张量核心都是一个专用电路,只做一件事:矩阵乘法。具体来说,我相信H100的张量核心可以处理一个16x4的输入矩阵A和一个4x8的输入矩阵B,然后加上一个16x8的偏置矩阵。它基本上执行 AX + B,其中A、X和B是这种固定大小的矩阵块。每个张量核心每个时钟周期可以执行一次这样的小块矩阵乘法。然后,如果你把这些数字乘起来,你会发现那个特定大小的 AX + B 矩阵乘法涉及1024次浮点运算(每次乘法和每次加法算作一次浮点运算)。乘以SM中的四个张量核心,我们看到整个SM如果通过张量核心运行,每个时钟周期可以执行超过4096次浮点运算。我们需要将这个数字与从FP32核心获得的256次进行比较。在这里我们看到,就像张量核心是所有魔力的来源一样,设备的主要吞吐量也来自这里。如果你编写的代码想要在这些GPU上以最大利用率运行,你需要最大限度地利用这些张量核心。

关于这些张量核心的另一个有趣之处是,它们实际上以混合精度运行,而不是传统的32位浮点数。张量核心倾向于使用混合精度过程,输入通常是16位,有几种不同的有趣16位格式,我们今天无法深入讨论。它们会以这种较低精度的16位进行乘法运算,然后以较高精度的32位进行加法(累积)。因此,这些张量核心接受低精度16位输入,进行一些中间计算,并以较高精度32位产生输出。这很重要,因为如果你忘记将PyTorch模型转换为16位,它将在浮点核心上运行,速度将比你预期的慢20倍。这看起来像是细节问题,但当你在PyTorch代码中搞错数据类型时,它会变得非常明显。

GPU确实非常快,而且过去十年或十五年它们的速度提升之快令人难以置信。当我刚开始攻读博士学位并从事深度学习研究时,我们都在使用的最先进的GPU是2013年发布的K40 GPU。整个设备只能进行约5 teraflops的FP32计算。我应该解释一下这个图表:X轴是时间,大约从2013年到现在;Y轴是每个设备的峰值吞吐量,以每秒teraflops为单位。你可以看到图表上升了很多,但这里需要注意一点:从K40到P100,再到V100(大约在2016-2017年我博士毕业前后发布),发生了一些真正惊人的事情。V100是第一款引入这些张量核心的设备。自那以后,更新的设备拥有了更多的张量核心、更大的张量核心,设备面积更多地分配给了张量核心,这导致了这些设备在过去10到15年里吞吐量的巨大增长。最新的设备是正式宣布的B200,目前正在缓慢推出。理论上,它的FP32计算能力约为83.3 teraflops/秒,张量核心的NI精度计算能力约为5000 teraflops/秒。退一步看,这确实意味着过去12年里,计算能力增长了1000倍,而这还只是在单个设备层面。为什么过去10年AI变得如此强大?这就是答案之一:我们现在利用的计算资源在十年内增长了1000倍。世界上任何事物发生1000倍的变化时,你都应该站起来关注,因为这将在我们的技术能力上引起重大变化。我认为,这1000倍的改进是过去十年深度学习改进的主要驱动力。

所以,B200没有500个张量核心,而是有5000 teraflops的张量核心计算能力。是的,我们总是试图区分张量核心上的计算和FP32核心上的计算。

这已经很疯狂了,对吧?一个你可以拿在手里的设备,在十年内性能提升了1000倍,这已经够疯狂了。我手里拿过K40,虽然还没有机会拿B200,但它们感觉像是相同的物理对象,大小、重量、外观都差不多,但今天的设备比12年前的快1000倍。这太疯狂了。

但更疯狂的是,我们并不只在一台GPU上训练。我说过,当K40在2013年首次推出时,实际上很多模型都是在单个GPU上训练的。但今天,我们不仅在单个GPU上训练,而是在数千、数万甚至数十万台GPU上共同训练一个模型。将这个叠加在每台设备吞吐量1000倍的提升之上,过去十年确实发生了一些真正疯狂的事情。

😊

那么,我们已经观察了GPU内部。现在,我想放大视角,将GPU置于上下文中,不再看单个设备,而是思考我们构建的、将许多这样的设备连接在一起的现代GPU集群。

我们已经看到了单个H100 GPU。在这里,我们可以将其视为内存层次结构的另一个级别。我们已经看到H100内部,随着我们接近计算元件,有三个层次的内存层次结构。随着你离计算元件越远,内存带宽(设备在不同系统部分之间移动比特的能力)就越慢。这种趋势实际上在超出单个设备的范围,想象这些设备在完整数据中心中的背景下时,仍在继续。

这里我们看到,单个H100 GPU的内存带宽约为每秒3TB,这是GPU内存与其自身计算元件之间的通信速度。但这些设备通常位于GPU服务器内部。几乎所有的GPU服务器都有8个设备,装在一个大箱子里。这些GPU可以相互通信,通常服务器内任何一台GPU与另一台GPU之间的通信速率约为每秒900GB。你可以看到,这与GPU内部设备间的通信带宽相比,大约减少了3倍。

在这里,我们再次转向Llama 3。许多主要参与者不公布其训练集群的细节,但Llama 3技术报告确实提供了大量关于其训练集群的细节。因此,这里的一些具体细节可能因集群而异,但这些数字来自用于训练其模型的Llama 3集群。给定一个GPU机箱,他们将两个这样的机箱堆叠到一个服务器机架中。服务器机架大约6英尺高,和一个人差不多高,可以想象一下这些东西的大小。一个服务器机架内部有两个服务器,总共16个GPU。然后,我们将许多服务器机架连接成一个GPU pod。Llama 3集群的GPU pod由192个机架组成,总共3072个GPU。这些机架之间有非常高带宽的连接器,因此,pod内任何一对GPU可以以大约每秒50GB的速率相互通信。现在你可以看到,这比单个服务器内部GPU之间的通信带宽又降低了大约20倍。

3072个GPU似乎计算能力很强,但现在还远远不够。因此,我们将把这些GPU pod堆叠在一起,形成一个完整的GPU集群。这实际上是Meta为训练Llama 3模型构建的完整GPU集群。这个东西将8个GPU pod组合在一起,总共24576个GPU。我找不到这些pod之间内存流量的确切数字,但肯定低于每秒50GB。顺便说一下,这远非世界上最大的GPU集群,只是我能快速找到精确数字的最大集群。但世界上肯定存在拥有5万、10万个GPU的GPU集群,它们确实存在,并且人们在其上训练模型。

这种方式是自然扩展的。你只需将更多pod集群在一起,创建一个更大的集群,或者你可能会有另一个层次结构,比如一个超级pod连接到其他超级pod,以获得更高的级别。

他们用那个GPU集群训练了多长时间?我不记得Llama 3模型的具体时间了,但过去十年有一个经验法则:人们训练的最长模型通常在几个月左右。我认为这更多地与技术无关,而与人员有关。在制定计划、让人们从事工作时,很难进行非常、非常长的训练运行。因此,最大的最先进模型的训练运行时间通常以月计。如果像GPT-4.5、GPT-5这样的最大模型现在接近一年,我也不会感到惊讶,但在这些非常长、非常大的训练集群上,看到持续几个月的训练运行是相当常见的。

问题在于,为什么将服务器组织成机架而不是pod?你必须把它们放在某个地方。这些东西有物理限制。因此,服务器机架几十年来一直是数据中心的标准单元。当这些新的GPU设备出现时,它们提供了不同种类的服务器,物理尺寸更大,功耗更高,但你无法一夜之间从头开始重新设计整个数据中心。因此,服务器机架一直是一种标准单元,具有标准的硬件尺寸,数据中心通常围绕它构建。

一个集群需要多大的物理空间?哦,这是个好问题。一个服务器机架大约6-8英尺高,大概这么大,也许像这个讲台大小,和我差不多高。然后一个pod里有192个机架,所以想象一下大约200个这样的讲台有多大,然后乘以8。但这实际上有点低估了,因为你通常将这些设备组织成行,以便人们可以在其间行走,并且集群中还需要打包更多硬件。除了包含物理GPU服务器的计算机架外,还会有其他机架包含网络硬件,因为所有设备之间需要传输大量比特;还会有专门的机架仅用于存储硬件,因为你需要将训练数据存储在某个地方并输入到设备中。这些东西会占用相当大的空间。

哦,是的,问题是当你使用这些大型集群时,较小的计算单元是否保持较高的吞吐量?是的,它们确实如此。这是设计这些系统的秘诀和挑战的一部分,因为你理想情况下希望在可能的时候利用快速通信,同时在扩展时优雅地回退到较慢的通信。

它有多热?相当热。如果你们中有人是游戏玩家,家里台式机里有4090或5090 GPU,单个4090 GPU在玩游戏时会使你的房间变热,让你想开窗,会使房间物理上更温暖。想象一下,单个游戏GPU就能对一个普通大小的房间产生这种影响。是的,一旦你将数万台这样的设备堆叠在一个大型数据中心里,就需要一些严重的冷却要求。

😊

尽管另一个有趣的事情是,冷却变得疯狂,对吧?游戏台式机通常是风冷,有时是水冷。然后你可以设计不同的冷却系统,可以在硬件上大做文章,尝试优化所有这些。

😊

好了,我认为这些东西超级酷,想象一下这些GPU不仅仅是漂浮在云中的神秘生物,它们是有人建造并堆放在某个房间里的实际物理原子,想象它们的样子真的很有趣。

😊

因此,当我们转向这些大型GPU集群时,一种思维方式的转变实际上是不再过多考虑单个设备或单个服务器,我基本上尝试将整个数据中心视为一台大型计算机。在这种情况下,这台大型计算机有24000个GPU、1.8 PB的GPU HBM内存、4.15亿个FP32核心、1300万个张量核心,整个系统每秒可以进行24 exaflops的计算(24乘以10的18次方)。这是大量的flops。但我保证,五年后的今天,这不会让人觉得是很多flops,这才是更疯狂的部分。我们的目标实际上是将这24000个GPU的整个块视为一台巨型超级计算机。然后问题是,我们如何在这台巨型超级计算机上连续数月训练一个神经网络,并训练一个真正庞大、强大、能够吸收海量数据的神经网络?这基本上就是我们在深度学习中转向的问题和范式。

顺便说一下,我一直说GPU,一直说英伟达,因为它们是目前最主要的训练架构和硬件。但也出现了一些其他竞争者。我认为目前英伟达训练硬件的最大竞争对手是谷歌。谷歌有自己的硬件,称为张量处理单元(TPU),这些硬件非常出色,已经经历了六代。这是V5 TPU的统计数据,你现在可以在谷歌云上租用,其规格与我们刚刚讨论的H100大致处于同一数量级,有些相似。TPU中有一些有趣的设计决策与GPU有很大不同,我觉得很迷人,但我们今天没有时间深入探讨。有人问这些东西有多大?这是一张实际图片,就像GPU一样,这些TPU被排列成pod,V5 TPU可以排列成最多8960个芯片的pod。这实际上是一张V2 TPU pod的图片,只有256个芯片。这让你对这些东西的大小有个概念。每个机架,你可以看到这里有四个机架,这些机架大概比我高一点,四个并排放置,容纳256个TPU芯片。现在想象一下,在拥有近9000个芯片的更新pod中,这个东西会变得大得多。

是的,谷歌的Gemini模型几乎肯定是在TPU上训练的。当然,他们不会告诉你,但如果它们不是,我会感到非常震惊。

正如我所说,TPU实际上非常好。我假设大多数大规模的谷歌模型都是在这些设备上训练的,而且这些模型非常有竞争力。因此,这是非常好的训练硬件。与英伟达的不同之处在于,你无法购买它,访问TPU的唯一方式要么是在谷歌工作,要么是在谷歌云上租用。但它确实是非常好的硬件,很多人都在使用它,但我认为目前它仍然比H100、比英伟达GPU稍微不那么流行。当然,其他公司显然知道这是非常重要的事情。因此,有许多其他公司正在尝试构建有竞争力的训练硬件。但我诚实的评估是,目前英伟达和TPU可能是两大巨头,在可用性、性能和市场份额方面遥遥领先于其他所有人。但也有很多其他公司正在努力追赶这里。两个值得注意的公司是AMD,AMD几十年来一直是第二大GPU制造商,他们也有一个训练加速器,称为MI325X。在纸面上,它实际上有非常好的统计数据,与H100相当,但目前还没有产生与H100相同的影响。AWS也开发了自己的训练芯片,称为Trainium。我对这个了解不多,我自己从未尝试使用过它,但我知道Anthropic在他们的部分训练中使用它。我不知道他们的训练在多大程度上完全使用Trainium而不是GPU。

因此,我们应该期待看到更多,但就目前而言,我认为英伟达GPU可能是最主要的,谷歌TPU紧随其后,它们也非常好,但可能不如英伟达的GPU使用广泛。

好了,这基本上是第一部分:什么是GPU?我们如何将它们组织成集群?只是让你了解一下我们构建和训练的机器的物理特性。

第二个问题是,我们如何实际编写算法,能够利用这个拥有数万台GPU的巨型GPU集群?这将需要我们开发新的算法、新的计算思维方式以及新的并行化和拆分神经网络的方法。这里的基本策略是拆分你的计算。这些是巨大的并行设备,我们看到了它们有很多GPU、很多CPU核心、很多可以独立运行的GPU核心,而且它们之间不能过多通信。如果你从高层次思考计算机真正做什么,计算机基本上做两件事:计算(接收输入比特并从中计算新的输出比特)和通信(将比特从一个地方的内存移动到另一个地方的内存)。整个诀窍是如何利用整个集群中多个级别的内存层次结构,将通信与计算重叠,并拆分和并行化计算。

在训练一个巨型神经网络的过程中,我们有有用的工作让那数万台独立GPU、数百万个独立计算元件全部并行执行,然后让它们以某种方式相互通信它们的工作,从而实现在这个巨型集群上训练一个巨型神经网络。

😡

为此,我喜欢的一种思考方式是,如今在训练大规模神经网络时,人们基本上利用了五种并行度。其中很多是针对Transformer的,因为它们是人们用于大规模训练的主要架构。如果你考虑一个Transformer,它基本上是一个L层的堆栈,每一层都在一个三维张量上操作。一个维度是小批量维度,我们有一批序列在小批量中操作;一个序列维度,我们在序列或标记集上操作;一个维度维度,每个标记本身是一个具有某个维度的向量。因此,我们的Transformer在这些三维张量上操作,并通过一系列层进行操作。这给了我们四个可以并行化的轴:我们可以在层轴上并行化(称为流水线并行),可以在批量维度上并行化(称为数据并行),可以在序列维度上拆分(称为上下文并行),可以在那个维度维度上拆分(称为张量并行)。所有这些都有有趣的名字,但如果你这样思考,它们基本上都是在Transformer内部这四个计算轴上进行计算拆分的不同方式。然后我们将更详细地逐步介绍每一种,因为所有这些不同的分布式训练机制都有很多有趣的细微差别。

第一种是数据并行(DP)。基本思想很简单。记住,当我们训练神经网络时,我们总是在小批量样本上操作,对吧?我们总是取一小批元素,根据我们的训练任务计算每个条目的损失,然后计算梯度,其中梯度通常是每个小批量元素损失梯度的平均值。在大多数神经网络架构中,计算损失然后计算梯度对于小批量中的每个元素是独立的。因此,这似乎是可轻松并行化的。

😊

基本思想是,如果你可以在单个GPU上容纳n个示例的小批量,并且你可以访问M个GPU,那么我们将使用一个M乘以n个示例的巨型小批量来训练我们的模型,我们将那个巨型小批量拆分成更小的n个样本的小批量,每个GPU上放一个。如果你从数学上思考为什么这有意义,那是因为梯度是线性的。实际上,如果你计算一个标量损失L,它将是每个条目上计算的某些个体损失的平均值(这些Xj是你整个宏批次中的所有条目),而w是整个网络的权重矩阵,那么通常在正向传播结束时计算的损失是每个小批量元素损失的平均值。然后,如果你计算损失相对于网络权重的梯度(这是我们需要计算以进行权重更新的东西),那么这实际上会拆分,因为梯度是线性的。

你可以选择我们想以什么顺序进行求和、进行梯度计算、进行平均。具体来说,将梯度安排在这种特定的公式中变得很方便,其中我们突出显示了蓝色的内部项,这基本上是在n个元素上的正常反向传播,这些可以在不同的GPU上并行计算。然后有一个外部求和,我们需要在所有参与训练的M个不同设备上对梯度取平均值。

这就是从数学角度发生的事情,我们看到这在数学上是完全合理的。这基本上与在单个设备上训练完全相同,我们只是巧妙地运用了代数,改变了平均和求和的顺序。但这不是近似,这与我们在单个更大GPU上进行的计算完全相同。

从GPU的角度来看,这看起来像是我们有M个GPU(这里我展示M=3,因为幻灯片上只能合理容纳这么多,但请理解在实践中这比3大得多)。然后,每个GPU实际上维护其自己独立的神经网络权重副本、优化器状态副本和梯度副本。然后,每个GPU将并行加载不同的小批量数据(这里我们展示每个GPU加载三个元素的小批量)。至关重要的是,不同的GPU需要加载不同的小批量数据。我的代码和学生的代码中曾出现过错误,他们不小心加载了相同的小批量到所有GPU上,这不会有帮助,不好,不要犯那个错误。因此,不同的GPU实际加载不同的小批量数据至关重要。然后,每个GPU将独立地在其自己的小批量数据上执行自己的正向传播,计算其自己的局部损失。这些都可以完全独立操作,不需要GPU之间的任何通信。然后,每个网络将执行自己的反向传播,计算其自己的局部损失相对于模型所有权重的梯度。同样,这可以完全独立发生,因为每个模型都有自己的模型权重独立副本,可以完全独立地执行自己的正向和反向传播。

但在反向传播完成后,事情就变得棘手了。记住,我们需要计算所有参与训练的设备上这些梯度的平均值。因此,我们需要通信。这时我们执行一个“全归约”操作。每个GPU需要将其梯度发送给所有其他GPU。因此,有两件事同时发生:一、每个GPU需要将其梯度广播给所有GPU;二、每个GPU需要收集所有参与训练的GPU的梯度。这是一个全归约操作,通常在对数时间内发生,取决于GPU数量。在这个全归约操作结束时,每个GPU现在拥有所有设备上所有梯度的平均值。此时,通信已经发生,每个GPU现在拥有一个相同的梯度副本,这些梯度已经在所有设备上进行了全归约。现在,在训练迭代开始时,我们假设每个GPU都有其自己独立的模型权重副本。此时,每个GPU都有其自己独立但相同的、来自整个宏批次数据的梯度副本。因此,此时每个GPU可以对其自己的局部权重副本进行权重更新。因为它们从相同的权重开始,并应用了相同的梯度,假设算术是确定性的,它们在局部权重更新后将具有相同的权重。

顺便说一下,这非常重要:步骤4和5实际上可以并行发生。我们说过,这里实际上有两件事可以并行发生:一是反向传播,每个GPU计算自己的反向传播以计算梯度;二是跨GPU的梯度通信。在实践中,这两件事通常会同时发生。这意味着每个模型将开始计算网络最后一层的反向传播,然后计算其自己的局部梯度。然后,模型将开始计算倒数第二层的反向传播,当计算元件忙于计算倒数第二层的反向传播时,GPU将同时进行最后一层梯度的全归约。这意味着这些操作可以并行进行:第L+1层的通信和第L层的反向传播。它们可以并行地进行,希望到我们到达网络末端、反向传播完成时,梯度已经在所有设备上完成了全归约。然后我们可以立即进行权重更新,无需等待。这非常重要,因为正如我们所说,通信相对较慢。因此,在这些系统中的整个诀窍是找出隐藏通信成本的方法,并在计算的同时进行通信。

问题是,步骤4或5会成为瓶颈吗?答案是:是的。这完全取决于你的设备速度、模型大小、小批量大小、设备间互连速度。当你进行这种较低规模的分布式训练时,答案总是:这取决于你的具体情况,你需要为你的情况进行基准测试。

为什么不在每个GPU上执行M个不同的梯度步骤?这实际上是一个非常酷的想法。实际上,过去有一组流行的算法,称为异步SGD,它们基本上会这样做:让一堆不同的模型副本独立地执行一些模型步骤,然后每隔一段时间尝试对它们进行平均。这些算法曾经很流行,谷歌在开发TPU pod之前,他们在21世纪10年代初的一些早期网络就是以这种方式训练的。但首先,它往往更不稳定;其次,它很难调试和复现,往往效果稍差。因此,它感觉上是一种更具扩展性的方法,但在实践中,如果你能同步进行所有操作,那么你的算法更容易调试、理解和推理。基本上,如果你能实现同步梯度更新,它可能会工作得更好。但实际上,我个人不会对在未来几年内看到异步SGD方法的复兴感到太惊讶,因为我认为它们对分布式更友好。

没有一台计算机可以协调所有这些事情。所有这些设备都是具有自己独立资源的独立设备,没有驱动程序可以以上帝视角来执行这些步骤。

所有这些计算都必须在某个地方进行。好问题。我说过,当你重叠通信和计算时,你需要为此编写代码,还是硬件会自动完成?你肯定需要为此编写代码。硬件不够智能,无法理解你想做什么。正如我们所说,硬件理解这些小矩阵乘法块,理解相当低级的东西。任何你想要调度通信的事情,都需要在软件中处理。但幸运的是,对于许多这些常见用例,PyTorch已经为你提供了支持。例如,在这种情况下,有一个PyTorch类叫做DistributedDataParallel,它会为你完成这些,并在你编写的其他直接了当的PyTorch代码之上相对透明地实现这一点。

😊

尽管实际上,这与单个设备的情况形成了有趣的对比。因为如果你在CUDA(英伟达用于编程GPU的语言)中编程单个GPU,那么硬件实际上会自动为你处理很多这种异步传输。但在集群级别,通常不会,你通常需要在软件中完成。因此,在单个设备级别的并行性(其中很多确实在硬件中自动发生)与集群级别(需要在软件中编排)之间,实际上存在一些有趣的不对称性。

是的,所以通常这些是异构系统,系统的不同部分用不同的编程语言编写。会有低级设备内核,这些是实际在GPU内部执行的代码,通常用CUDA编写,CUDA是英伟达用于编程其GPU的类C语言。然后,这些单独的GPU内核会被包装起来,你可以从Python调用这些GPU内核。这基本上是PyTorch的工作方式。PyTorch就像是一堆可以在GPU上做很多有趣事情的GPU内核的集合,以及大量围绕这些GPU内核的C++和Python代码,使其更易于编程。

在这张图中,每个GPU自己计算其自己的黑色梯度,然后红色梯度通过所有GPU的并行全归约计算。

哦,较低层的反向传播依赖于上一层的梯度,但至关重要的是,每个GPU只在其自己的小批量上本地进行反向传播。因此,现在基本上有两种不同变体的梯度需要考虑:一是局部梯度,即我的小批量的局部损失相对于我的网络权重的梯度;二是全局梯度,即宏批次总损失相对于网络权重的导数。为了计算反向传播,每个GPU只需要其上游梯度的局部版本。但计算上游梯度的全局版本需要通信。

😊

这就是数据并行。这里实际上有一个问题:这是一种很好的并行化GPU计算的方法,也是人们开始并行化神经网络训练的第一种方法,但我们很快在模型大小上遇到了瓶颈。记住,这里每个GPU都维护其自己独立的模型参数副本,当你想拥有非常大的模型时,这就成了瓶颈。具体来说,现在你的神经网络中的每个权重,你基本上需要跟踪四个数字:权重本身、该权重的梯度、梯度的梯度以及优化器状态。如果我们使用Adam,通常每个网络参数有一个beta1和一个beta2,有时你还会有一个模型参数的指数移动平均值。因此,通常你需要为网络中的每个权重跟踪四到五个标量。如果你使用16位精度进行训练(这在当今相当常见),其中一些你有时会以更高精度保存,但让我们以16位作为下限来讨论。那么每个数字需要2字节,这意味着我们需要为网络中的每个标量跟踪8字节。因此,10亿个模型参数将需要大约8GB的GPU内存来存储所有这些东西。而我们说过,整个H100 GPU只有80GB内存。这意味着,在这种情况下,你希望训练的最大模型大约是100亿参数。这还不够大。我们想要真正的大模型。我们不希望受限于GPU内存大小,从而限制我们允许训练的模型大小。因此,我们需要以某种方式解决这个问题。

解决这个问题的方法实际上相对容易:我们需要将模型权重拆分到不同的GPU上。因此,除了将数据批次拆分到GPU上,我们还将把模型权重拆分到GPU上。这导致了数据并行的一个变体,称为完全分片数据并行(FSDP)。从概念上讲,我们将要做的是:网络中的每个模型权重,每个权重W_i,我们将把它分配给一个所有者GPU。因此,每个权重将由我们正在训练的M个GPU中的一个唯一GPU拥有,并且拥有每个权重的GPU也将负责管理该权重的全局梯度和优化器状态。

通常你会按层拆分,你不是管理单个标量,这个W你应该认为是神经网络整个层的权重矩阵。

现在,右边的图片发生了一点变化。这里我们只展示两个GPU,因为剧透一下,稍后会有更多箭头飞来飞去。这里我们展示了一个四层网络,分布在两个不同的GPU上。我们将前两个网络层(W1和W2)的权重分配给GPU1拥有,权重W3和W4由GPU2拥有。这意味着在每个批次的开始,网络权重以这种方式拆分到GPU上。但它仍然是数据并行,仍然是相同的基本思想:每个GPU将加载其自己独立的元素批次,在该批次上执行完整的正向和反向传播以计算其自己的局部梯度,然后我们将归约梯度并执行梯度步骤。相同的基本算法,但现在变得棘手了,因为模型权重被拆分了。这里我们需要引入额外的通信。在进行完全分片数据并行时,现在在正向传播开始之前,在开始第一层的正向传播之前,拥有第一层权重的GPU需要将该权重矩阵广播给所有正在训练的其他GPU。在这种情况下,GPU1拥有W1,因此它将其广播给GPU2。现在GPU2有了W1的副本。现在所有GPU都有了W1的副本,它们可以运行网络第一层的正向传播,并计算网络第一层的激活值。

现在,在你运行正向传播之后,每个不拥有W1的GPU将删除其W1权重矩阵的本地副本以节省内存。然后,在我们运行第一层的正向传播之后,我们回到了模型权重拆分到GPU上的状态,但现在所有GPU在GPU内存中也有了运行网络第一层的结果激活值。现在,是时候进行第二层了,我们做完全相同的事情。然后,拥有第二层权重矩阵的GPU将把它广播给所有正在训练的GPU。现在它们都有了W2的本地副本,它们可以继续正向传播。顺便说一下,我们也有机会在这里交错计算和通信。因此,在实践中,在FSDP运行的正向传播过程中,当我们计算第i层时,我们可以预取下一层的权重。因此,在实践中,这将并行发生。然后,当我们到达第3层时,注意现在GPU1拥有第3层,因此GPU1将权重广播给所有正在训练的GPU。这将重复,直到我们到达网络末端。现在,在网络末端,所有模型现在每个模型都完成了一次完整的正向传播,计算了其自己小批量的局部损失,并且内存中已经有了所有层的激活值用于反向传播。现在我们需要反向执行相同的操作来计算反向传播。在最后一层的反向传播开始时,拥有该最后一层权重的GPU会将其广播给所有设备。一旦设备有了该权重,它们就可以执行反向传播。整个反向传播过程将执行类似的程序。现在,在网络的最后一层,我们可以做一点优化:不要删除最后一层的权重,让所有GPU在内存中保留最后一层的权重。这是你在实践中通常会做的事情,因为在最后,所有GPU已经从正向传播中有了最后一层权重的副本,它们会将其保留在内存中,并且知道无论如何它们即将在反向传播中重用。

因此,我们只是不会删除最后一层的权重。

现在,在反向传播过程中基本上需要发生三件事:一是一旦GPU计算了网络最后一层的反向传播,现在它们有了权重的副本,此时每个GPU已经计算了其自己的局部反向传播,即其自己的局部损失相对于该最后一层权重的局部梯度。然后我们需要通信这些梯度,并且我们说过,拥有权重矩阵的GPU也将负责管理该权重矩阵的梯度。因此,现在与数据并行情况下的全归约梯度不同,我们将只让拥有最后一层权重的那个GPU收集并求和所有设备上的所有局部梯度。在这种情况下,GPU1将发送其最后一层的局部梯度给GPU2,GPU2然后将拥有整个宏批次相对于最后一层权重的完整梯度dL/dW4。

在停机期间会发生什么?你必须让所有这些事情并行发生。因此,在反向传播期间,基本上需要发生三件事:一、我们需要通信权重,因此拥有该层权重的GPU必须广播它们;二、所有GPU一旦获得该权重,需要计算该层的反向传播;三、在每个GPU计算其反向传播之后,它需要将该反向传播相对于权重的结果(梯度)发送回拥有它的GPU。然后,一旦权重的所有者有了完整的梯度,那么只有权重矩阵的所有者现在可以对该权重矩阵进行梯度更新。但我想在这一点上,我们实际上不需要通信更新后的权重矩阵,因为它将在下一次正向传播时重新通信给所有GPU。这与DP情况有点不同。然后,基本上所有这些事情实际上也可以并行发生。

它们将对网络的每一层重复此过程,然后基本上在一个非常深的网络的稳定状态下,所有这三件事将同时发生。因此,当我们计算第L层的反向传播时,我们将在第L+1层上聚合梯度并执行权重更新,并且我们将预取第L-1层的权重。我说过需要发生三件事:获取权重、运行反向传播、然后更新权重(聚合梯度并更新权重)。这些事情都可以并行发生,因此我们基本上在反向传播过程中,通常会在三个连续的层上操作,并同时进行所有这三件事。

是的,然后当我们反向通过网络时,如果你能够适当地重叠所有通信和计算,那么到你完成反向传播时,所有梯度应该已经通信完毕,所有GPU应该已经完成了对所有权重的更新。此外,希望你的数据加载器(在服务器的CPU核心上异步加载数据)也同时在进行。因此,CPU已经准备好一批新的数据可以再次进行正向传播。因此,这些基本上是并行化机器,我们需要

012:自监督学习

概述

在本节课中,我们将要学习自监督学习。这是一种无需大量人工标注数据即可训练神经网络的方法。我们将探讨如何通过设计“前置任务”来让模型从无标签数据中学习到有用的特征表示,并了解如何将这些特征应用于下游任务。


前置任务与自监督学习概念

上一节我们介绍了计算机视觉中的各种任务,如分类、检测和分割。这些任务通常需要大量带标签的数据。本节中,我们来看看如何在没有标签的情况下训练模型。

自监督学习的核心思想是:我们拥有一个没有标签的大型数据集(例如图像)。我们的假设是,我们可以通过定义一个前置任务来训练一个神经网络,使其学习到图像的良好特征。这个前置任务的目标函数不需要人工标注,其“标签”可以从数据本身自动生成。

训练完成后,我们可以使用这个训练好的编码器来提取特征,并将其用于一个下游任务(例如分类)。对于下游任务,我们只需要一个较小的带标签数据集,并在编码器提取的特征之上训练一个简单的分类器(如线性层)。

核心概念公式化
E 为编码器,D 为用于前置任务的解码器/分类器,x 为输入数据,y_pretext 为自动生成的前置任务标签。
前置任务训练过程可表示为:L_pretext = loss(D(E(x)), y_pretext)
下游任务微调过程可表示为:L_downstream = loss(Classifier(E(x)), y_true)


前置任务示例

以下是几种常见的图像前置任务设计思路:

  • 图像旋转预测:将图像旋转一个固定角度(如0°, 90°, 180°, 270°),让模型预测旋转角度。这迫使模型理解物体的正常朝向。
  • 拼图游戏:将图像分割成网格并打乱 patches 的顺序,让模型预测正确的排列顺序。
  • 图像补全:随机遮盖图像的一部分,让模型根据未遮盖的部分预测被遮盖的内容。
  • 图像着色:将彩色图像转换为灰度图(仅亮度通道),让模型预测缺失的颜色通道。

具体前置任务详解

1. 旋转预测

这个任务假设模型只有具备对物体的视觉常识,才能正确判断其旋转角度。实现时,通常将旋转角度离散化为几个类别(如4类),从而将问题转化为分类任务。实验表明,用此方法预训练的模型,在下游分类任务上,其起点准确率远高于随机初始化,性能接近在有标签ImageNet上预训练的模型。

2. 拼图游戏

一种进阶形式是预测 patches 的正确排列,而非单个 patch 的位置。由于全排列数量巨大(9!),实践中会定义一个较小的、有代表性的排列子集(如64种)作为分类目标。

3. 图像补全(掩码自编码器)

这是一个基于重建的前置任务。其核心是随机遮盖图像中大部分区域(例如75%),然后让模型重建被遮盖的部分。掩码自编码器 是这类方法的代表。它使用不对称的编码器-解码器架构(通常基于Vision Transformer):

  • 编码器:仅处理未被遮盖的图像 patches,将其编码为特征。
  • 解码器:接收编码器输出的特征以及代表被遮盖区域的可学习掩码令牌,重建出完整的图像。
    损失函数仅针对被遮盖的区域计算均方误差。这种方法能学习到非常强大的特征表示,在下游任务中表现出色。

4. 图像着色

此任务利用LAB颜色空间中亮度与颜色分离的特性。给定亮度通道,预测颜色通道。一个扩展是“分脑”自编码,将输入分为两组通道,用两个网络相互预测对方。这种方法学到的特征可用于分类,其本身也能用于黑白图像上色、视频着色等应用。在视频着色中,通过参考帧对后续帧进行着色,模型还能隐式地学习到物体跟踪能力。


对比学习简介

上一节我们介绍了基于图像变换的前置任务。本节中,我们来看看另一种强大的自监督学习范式——对比学习。

对比学习的核心思想不是预测某种变换,而是学习一种特征表示空间,使得同一张图像的不同变换(正样本)在空间中距离很近,而不同图像的变换(负样本)在空间中距离很远。

核心概念公式化(InfoNCE损失)
f(x) 为样本 x 的特征表示,x+ 为正样本,{x_i-} 为负样本集合,sim 为相似度函数(如余弦相似度)。
InfoNCE损失函数定义为:
L = -log[ exp(sim(f(x), f(x+)) / τ) / (exp(sim(f(x), f(x+)) / τ) + Σ_i exp(sim(f(x), f(x_i-)) / τ) ) ]
其中 τ 是温度参数。最小化该损失函数等价于最大化正样本对的互信息下界。


对比学习框架

以下是两个重要的对比学习框架:

  • SimCLR:这是一个简洁的框架。它对批次中的每张图像生成两个随机增强视图,通过编码器和投影头得到特征表示,然后使用InfoNCE损失进行训练。正样本对是同一图像的两个视图,批次内所有其他图像的视图均作为负样本。SimCLR表明,大的批次大小和强的数据增强至关重要。
  • MoCo:为了克服大批次大小的内存限制,MoCo引入了动量编码器动态字典队列。它维护一个包含大量负样本特征的队列,用于计算对比损失。查询编码器通过梯度更新,而键编码器通过查询编码器的动量平均来更新。这种方式解耦了批次大小与负样本数量,允许使用大量负样本。

总结

本节课中我们一起学习了自监督学习。我们首先了解了其核心概念:通过设计无需人工标签的前置任务,从无标签数据中预训练一个特征提取器(编码器)。然后,我们详细探讨了几种具体的前置任务,如图像旋转预测、拼图、图像补全和着色,并重点介绍了强大的掩码自编码器方法。最后,我们引入了对比学习范式,它通过拉近正样本、推开负样本的方式来学习特征表示,并简要介绍了SimCLR和MoCo这两个经典框架。这些方法使得我们能够利用海量无标签数据来训练模型,为下游任务提供强大的特征基础。

013:生成模型(第一部分)

概述

在本节课中,我们将学习生成模型的基本概念、分类以及两种重要的生成模型:自回归模型和变分自编码器。我们将从概率建模的角度出发,理解生成模型与判别模型的区别,并探讨如何利用最大似然估计等方法来训练这些模型。


从自监督学习到生成模型

上一节我们介绍了自监督学习,这是一种无需人工标注标签,直接从数据中学习结构的方法。其典型流程是:将大量无标签数据(如图像)输入编码器以提取特征表示,再通过解码器基于这些特征预测某些内容。自监督学习的核心在于设计一个“前置任务”,使得整个系统可以在没有人工标注的情况下进行训练。常见的任务包括图像旋转、图像块重排或图像修复等。

自监督学习通常分为两个阶段:首先,在所有可用的无标签数据上训练编码器和解码器以完成前置任务;然后,丢弃解码器,接入一个新的(可能很小的)全连接网络,并在少量有标签的下游任务上进行微调。这样做的目的是通过前置任务从海量数据中学习到关于数据(如图像)的通用结构知识,然后将这些知识迁移到我们真正关心但只有少量标注数据的任务上。

除了上述基于重构或几何扰动的前置任务,另一种非常成功的自监督学习范式是对比学习。其核心思想是:获取相似的数据对和不相似的数据对,并希望模型将相似对的特征拉近,将不相似对的特征推远。具体实现时,对每个输入图像应用两种随机变换(如裁剪、灰度化),得到两个增强样本。将所有增强样本通过特征提取器(如CNN或ViT)得到特征向量,然后计算一个巨大的相似度矩阵。我们希望来自同一原始图像的两个增强样本的特征相似度高,而来自不同原始图像的增强样本的特征相似度低。

SimCLR是成功应用此思想的代表性工作。但它的一个问题是需要相当大的批次大小才能良好收敛,因为如果负样本不够多,网络区分正负样本的任务就太简单了,无法提供足够强的学习信号。这促使了后续改进方法的出现。

例如,MoCo方法通过维护一个“负样本队列”来解决大批次问题。它使用两个编码器:一个通过梯度下降正常更新的编码器,和一个作为“动量编码器”的编码器。动量编码器的权重不是通过梯度更新,而是作为正常编码器权重的指数移动平均。这样,模型可以利用历史批次中的样本来构建负样本队列,而无需在每次迭代中都处理巨大的批次,从而降低了内存需求。

另一个重要工作是DINO及其升级版DINOv2。DINO也采用了类似MoCo的双编码器结构,但使用了不同的损失函数(如KL散度)。DINOv2的关键突破在于成功地将这种自监督学习方法扩展到了更大的数据集(约1.42亿张图像)上,从而学习到了非常强大的特征表示,这些特征在实践中被广泛用于下游任务的微调。

以上是关于自监督学习的补充。现在,让我们进入本节课的核心主题:生成模型。


生成模型简介 🎨

生成模型是深度学习领域中一个非常激动人心的方向。大约在十年前,生成模型的效果还很不理想,生成的样本往往是模糊、低分辨率的。但在过去几年里,随着计算力的提升、训练方法的稳定以及数据规模的扩大,生成模型取得了突破性进展,催生了像语言模型、图像生成模型、视频生成模型等广泛应用。

尽管应用效果发生了翻天覆地的变化,但生成模型的许多基本数学思想和建模方法在过去十年中并没有根本性的改变。进步主要来自于更强大的计算资源、更稳定的训练方案、更大的数据集以及分布式训练能力的提升。当然,也有一些重要的算法改进,我们将在下一讲讨论扩散模型时看到。

首先,为了明确术语,让我们回顾一下监督学习与无监督学习的区别。

监督学习 vs. 无监督学习

  • 监督学习:这是我们本学期大部分时间都在学习的内容。在监督学习中,我们有一个由输入X和标签Y组成的数据集。目标是学习一个从X映射到Y的函数。例如:

    • 图像分类:X是图像,Y是类别标签。
    • 图像描述:X是图像,Y是描述文本。
    • 目标检测:X是图像,Y是边界框和类别标签的集合。
    • 图像分割:X是图像,Y是每个像素的标签。
  • 无监督学习:在无监督学习中,我们只有数据X,没有标签Y。目标是直接从数据中发现某种结构或模式,而不是完成某个特定的预测任务。例如:

    • K均值聚类:识别数据中的簇。
    • 主成分分析:发现数据的低维子空间或流形。
    • 密度估计:拟合数据的概率分布。

监督和无监督可以看作是对学习方法进行分类的一个维度。

判别模型 vs. 生成模型

另一个独立的分类维度是判别模型生成模型,这两者都涉及概率建模。

  • 判别模型:学习的是条件概率分布 P(Y | X)。即,给定输入数据X(如图像),模型输出所有可能标签Y的概率分布。

    • 关键点:对于每一个输入X,模型都独立地分配一个概率分布给所有可能的Y。不同X之间的概率质量没有竞争关系,竞争只发生在同一个X对应的不同Y之间。
    • 局限性:模型无法拒绝不合理的输入。例如,对于一个猫/狗分类器,输入一张抽象画,它仍然必须在“猫”和“狗”之间分配概率。
  • 生成模型:学习的是数据本身的概率分布 P(X)。即,模型需要为所有可能存在的图像X分配一个概率值。

    • 关键点:现在,所有可能的图像都在竞争有限的概率质量。模型必须深入理解现实世界的结构,才能判断哪些图像更可能(概率高),哪些图像不可能(概率低)。
    • 优势:模型有能力拒绝不合理的输入,只需为其分配极低或零概率即可。
  • 条件生成模型:学习的是条件概率分布 P(X | Y)。即,给定某个条件信号Y(如文本描述),模型输出所有可能图像X的概率分布。

    • 关键点:对于每一个条件Y,都引发了一场在所有可能图像X之间的竞争。这使得模型能够根据丰富的条件(如一段文字)生成多样化的、符合描述的图像。
    • 实用性:条件生成模型是最有用、最令人兴奋的,它使我们能够通过控制输入Y来生成想要的图像。

这三种模型通过贝叶斯规则相互关联。理论上,如果拥有其中两种模型(例如判别模型P(Y|X)和生成模型P(X)),可以推导出第三种(如条件生成模型P(X|Y))。但在实践中,条件生成模型通常是从头开始训练的。

为什么需要生成模型?

当任务输出存在模糊性不确定性时,生成模型就派上了用场。例如:

  • 语言建模:输入“写一首关于生成模型的押韵短诗”,存在无数种可能的诗。
  • 文生图:输入“一个人在白板前讲授生成模型课程”,存在无数种符合描述的图像。
  • 图生视频:给定一张图像,预测接下来会发生什么,存在多种合理的未来场景。

生成模型的目标就是建模这种在给定输入条件下,所有可能输出的整个概率分布


生成模型的分类 🌳

生成模型领域有很多不同的方法,我们可以用一个分类树来概括:

  1. 显式密度模型:模型能够直接计算(或近似计算)概率密度值 P(X)

    • 精确密度模型:可以精确计算密度值。例如:自回归模型
    • 近似密度模型:可以计算一个近似的密度值或密度下界。例如:变分自编码器
  2. 隐式密度模型:模型无法直接计算密度值 P(X),但能够以某种方式从该分布中采样。

    • 直接采样方法:通过单次网络前向传播即可生成样本。例如:生成对抗网络
    • 间接采样方法:需要通过迭代过程才能生成样本。例如:扩散模型

注意:在文献和本讲后续内容中,为了简化公式,我们常常省略条件生成模型中的条件Y,写作P(X)。但请始终记住,在大多数有意义的应用中,我们实际指的是P(X | Y)

本节课我们将重点介绍分类树的左半部分:自回归模型变分自编码器。下一讲我们将介绍右半部分:生成对抗网络扩散模型


自回归模型 📝

最大似然估计

在深入自回归模型之前,我们先了解一个训练概率模型的通用框架:最大似然估计

假设我们有一个由神经网络参数化的概率密度函数 P(X; W),其中W是网络权重。给定一个包含N个独立同分布样本的数据集 {X¹, X², ..., Xᴺ},最大似然估计的目标是找到一组权重W,使得该数据集在这些权重下的似然最大化。

由于概率的连乘可能导致数值下溢,我们通常转而最大化对数似然,因为对数函数是单调的,且能将连乘转化为连加:

最大化 log P(X¹, X², ..., Xᴺ; W) = Σᵢ log P(Xⁱ; W)

这为我们提供了一个可以用于训练神经网络的具体损失函数。

自回归建模

直接对整个高维数据X建模非常困难。自回归模型的核心思想是:将数据X顺序地分解为一系列子部分 (x₁, x₂, ..., xₜ)。然后利用概率的链式法则:

P(X) = P(x₁) * P(x₂ | x₁) * P(x₃ | x₁, x₂) * ... * P(xₜ | x₁, x₂, ..., xₜ₋₁)

这个分解本身没有做任何假设,它对任何联合分布都成立。现在,问题转化为:顺序地预测序列中下一个部分的概率分布,条件是其之前的所有部分。

这听起来很熟悉吗?是的,循环神经网络Transformer(特别是掩码Transformer)天然适合这种任务。

  • RNN:通过隐藏状态传递序列的历史信息,在每个时间步预测下一个元素。
  • Transformer:通过恰当的注意力掩码,使每个输出位置只能关注到它之前的序列部分,从而实现自回归预测。

应用于图像

文本数据天然是离散的一维序列,非常适合自回归模型。但图像是二维的,且像素值通常是连续的(或量化为离散的8位整数)。如何应用自回归模型呢?

一种朴素的方法是:将图像光栅化成一个长序列。例如,对于一个1024x1024的RGB图像,我们可以将其展开为一个包含约314万个子像素值(1024 * 1024 * 3)的序列,每个子像素值是0-255的整数。然后,我们可以像处理文本一样,用RNN或Transformer对这个离散值序列进行自回归建模。

问题:序列过长,导致计算成本极高,难以扩展到高分辨率图像。

提示:近年来的一些新方法通过将图像编码成更紧凑的离散“token”序列,而非原始像素,使自回归模型在图像生成领域重新焕发生机。我们将在下一讲中提及。


变分自编码器 🔄

上一节我们介绍了自回归模型,它是一种可以精确计算数据密度的生成模型。本节我们来看看变分自编码器,它属于“显式但近似”的密度模型。它放弃了计算精确密度的能力,但换来了一个关键优势:学习到一个结构化的、易于采样的隐变量空间

从普通自编码器说起

普通自编码器是一种无监督学习方法,用于从数据X中学习特征表示Z,而无需标签。它包含一个编码器(将X映射到Z)和一个解码器(将Z映射回X)。训练目标是让重建输出尽可能接近原始输入,即最小化重建误差。

关键在于,我们通常对中间表示Z施加一个瓶颈,使其维度远小于输入X。这迫使网络在压缩和重建的过程中,学习到数据中重要的、非平凡的结构信息。

训练完成后,我们可以丢弃解码器,将编码器提取的特征Z用于下游任务(如分类器的初始化)。但是,如果我们想生成新数据呢?理想情况是:我们能从隐空间Z的分布中采样,然后将采样的Z通过解码器得到新样本X。问题在于,我们不知道Z应该服从什么样的分布,也不知道如何从中采样。

变分自编码器的核心思想

变分自编码器为普通自编码器引入了概率框架。它假设:

  1. 每个观测数据X都是由某个不可见的隐变量Z生成的。
  2. 隐变量Z服从一个我们预先设定的简单先验分布,通常是标准正态分布 N(0, I)

VAE的目标是同时学习:

  • 一个编码器(推断网络):近似后验分布 Q(Z | X)。给定数据X,它输出隐变量Z的概率分布(通常是高斯分布,输出均值和方差)。
  • 一个解码器(生成网络):似然分布 P(X | Z)。给定隐变量Z,它输出数据X的概率分布。

这样,训练完成后,我们可以轻松地从先验分布N(0, I)中采样一个Z,然后通过解码器生成一个新的X。

训练目标:证据下界

我们最终的目标仍然是最大化数据的对数似然 log P(X)。经过一系列数学推导(涉及贝叶斯规则、引入编码器Q、利用KL散度的性质),我们可以得到证据下界

log P(X) ≥ E[log P(X | Z)] - KL( Q(Z | X) || P(Z) )

其中:

  • E[log P(X | Z)]重建项。它鼓励解码器能够从编码器产生的Z中很好地重建出原始X。
  • KL( Q(Z | X) || P(Z) )正则项。它鼓励编码器输出的分布Q(Z|X)接近我们预设的先验分布P(Z)(标准正态分布)。

ELBO是真实对数似然的一个下界。通过最大化ELBO,我们间接地最大化了数据的似然。

训练过程与特性

  1. 编码器:输入X,输出高斯分布的参数(均值μ和方差σ²)。
  2. 采样:使用重参数化技巧从N(μ, σ²)中采样一个Z。这使得采样操作可导,允许梯度反向传播。
  3. 解码器:输入Z,输出重建的X(通常输出高斯分布的均值,方差固定)。
  4. 损失计算:计算重建损失(如均方误差)和KL散度损失,两者加权求和。

VAE的训练存在一种有趣的对抗:重建项希望每个X都有独特、确定的Z以便完美重建;而KL正则项希望所有Z的分布都接近标准正态。训练过程就是在这两者之间寻找平衡。

训练完成后,VAE的隐空间具有很好的性质:

  • 连续性:隐空间是连续的,轻微改变Z,生成的X也会平滑变化。
  • 解耦性:由于先验是各向同性的高斯分布,隐变量的不同维度往往对应数据中某些独立、可解释的变化因素(如笔画的粗细、数字的倾斜度)。

总结

本节课我们一起深入探讨了生成模型的世界。

  • 我们首先区分了监督学习无监督学习,以及判别模型生成模型的核心区别,理解了条件生成模型 P(X | Y) 的强大之处在于它能建模输出的不确定性。
  • 我们建立了一个生成模型的分类框架,将其分为显式密度模型和隐式密度模型两大类。
  • 我们重点学习了两种显式密度模型:
    1. 自回归模型:通过链式法则将联合分布分解为条件分布的乘积,天然适合RNN和Transformer,是语言模型的基石,也可通过特定方式应用于图像。
    2. 变分自编码器:通过引入隐变量和变分推断,学习一个结构化的、接近高斯先验的隐空间。其训练目标是最大化证据下界,在数据重建和隐空间正则化之间取得平衡,从而能够从隐空间中采样以生成新数据。

下一节课,我们将继续探索生成模型家族的另一半:生成对抗网络扩散模型,看看它们如何以不同的方式实现强大的生成能力。

014:生成模型(第二部分)

概述 📖

在本节课中,我们将继续探讨生成模型。我们将深入介绍两种重要的生成模型:生成对抗网络扩散模型。我们将了解它们的工作原理、训练方式、优缺点,以及它们如何共同构成了现代生成式AI的核心技术栈。


生成对抗网络(GANs)🎭

上一节我们介绍了基于显式密度的生成模型,如自回归模型和变分自编码器。本节中,我们来看看另一大类模型:隐式密度模型。这类模型不直接输出数据的概率密度值 P(x),但能够从学习到的分布中采样生成新数据。

GANs的基本思想

生成对抗网络的核心思想是让两个神经网络——生成器判别器——相互对抗、共同进化。

  • 生成器 的目标是生成足以“以假乱真”的数据。
  • 判别器 的目标是准确区分真实数据和生成器产生的“假”数据。

通过这种对抗训练,生成器被迫不断改进其生成能力,最终生成与真实数据分布非常接近的样本。

GANs的数学框架

GAN的训练过程被形式化为一个极小极大博弈。其目标函数 V(G, D) 如下:

V(G, D) = E_{x~P_data}[log D(x)] + E_{z~P_z}[log(1 - D(G(z)))]

其中:

  • G 是生成器,输入噪声 z,输出生成数据 G(z)
  • D 是判别器,输入数据 x,输出该数据为“真实”的概率 D(x)
  • P_data 是真实数据分布。
  • P_z 是噪声的先验分布(通常是标准高斯分布)。

以下是训练过程中两个网络的视角:

  • 判别器的目标最大化 V(G, D)。它希望对于真实数据 xD(x) 接近1;对于生成数据 G(z)D(G(z)) 接近0。
  • 生成器的目标最小化 V(G, D)。它希望生成的数据 G(z) 能骗过判别器,即让 D(G(z)) 接近1。

GANs的训练过程

训练GANs采用交替优化的策略:

  1. 固定生成器G,更新判别器D:通过梯度上升来最大化 V(G, D),使D更好地区分真假。
  2. 固定判别器D,更新生成器G:通过梯度下降来最小化 V(G, D)(或等价地最大化 log D(G(z))),使G生成更逼真的数据。

一个重要的实践技巧是,在训练初期,使用 -log D(G(z)) 作为生成器的损失函数,而不是原始的 log(1 - D(G(z))),这能提供更有用的梯度信号。

GANs的优缺点

优点

  • 概念直观,公式相对简单。
  • 在精心调优后,能生成质量极高、细节清晰的图像(如StyleGAN系列)。
  • 其隐空间通常具有平滑的插值特性,意味着在隐空间中移动能产生语义上连续变化的图像。

缺点

  • 训练极其不稳定。目标函数 V(G, D) 的值本身并不能反映模型好坏,难以监控训练进程。
  • 容易出现模式崩溃,即生成器只学会生成有限的几种样本,缺乏多样性。
  • 生成器和判别器的能力需要精细平衡,否则训练容易失败。

尽管挑战重重,GANs在2016至2021年间曾是生成模型领域的主导方法,催生了海量的研究和应用。


扩散模型 🌪️

近年来,扩散模型 在图像和视频生成质量上取得了突破性进展,逐渐成为主流的生成模型范式。

扩散模型的直观理解

扩散模型的核心思想是通过一个逐步去噪的过程来生成数据。

  1. 前向过程(加噪):从一张干净图片开始,逐步添加高斯噪声,直到它变成纯随机噪声。这个过程将数据分布 P_data 逐渐转化为一个简单的噪声分布 P_z(如标准高斯分布)。
  2. 反向过程(去噪):训练一个神经网络,学习如何从带噪数据中预测并移除一小部分噪声。
  3. 生成过程(采样):从纯噪声开始,多次调用这个训练好的神经网络进行迭代去噪,最终得到一张干净的、来自数据分布的图片。

整流流模型:一种简洁的扩散模型

在众多扩散模型变体中,整流流模型 提供了非常直观和简洁的实现方式。其训练目标可以几何化地理解:

  1. 从数据分布中采样一个真实样本 x,从噪声分布中采样一个噪声样本 z
  2. xz 之间连一条直线,定义速度向量 v = z - x
  3. 在这条线上随机选取一个点 x_t(由噪声水平 t 控制,t=0 时为 xt=1 时为 z)。
  4. 训练一个神经网络 f_θ,输入 x_tt,目标是预测出真实的速度向量 v

训练代码 可以非常简洁:

# 训练循环(伪代码)
for x in data_loader:
    z = sample_noise_like(x)          # 采样噪声
    t = uniform(0, 1)                 # 采样噪声水平
    x_t = (1-t)*x + t*z               # 线性插值得到带噪样本
    v_true = z - x                    # 真实速度向量
    v_pred = model(x_t, t)            # 模型预测的速度向量
    loss = MSE(v_pred, v_true)        # 均方误差损失
    loss.backward()
    optimizer.step()

采样(生成)过程 则是一个迭代的去噪过程:

  1. 从噪声分布采样一个初始样本 x_1(纯噪声)。
  2. t=1t=0 循环:
    • 将当前 x_tt 输入模型,得到预测速度 v_pred
    • 更新 x_{t-Δt} = x_t + v_pred * Δt(沿预测方向走一小步)。
  3. 循环结束后,x_0 即为生成的干净样本。

条件生成与无分类器引导

我们通常希望生成模型是可控的,即能根据文本描述等条件生成特定内容。这可以通过条件扩散模型实现:在训练和采样时,将条件信息(如文本编码)作为额外的输入提供给模型。

为了增强模型对条件信号的遵循程度,一个关键技巧是无分类器引导

  • 训练时:随机以一定概率(如50%)将条件信息置为空(null),迫使模型同时学习有条件无条件的生成。
  • 采样时:分别计算有条件预测 v_y 和无条件预测 v_null,然后按以下公式进行引导:
    v_cfg = v_null + w * (v_y - v_null)
    
    其中 w 是引导尺度。w 越大,生成结果对条件 y 的遵循程度越高,但多样性可能降低。

潜在扩散模型:现代生成管道

直接在原始高分辨率像素空间训练扩散模型计算成本极高。因此,现代主流方法是潜在扩散模型

  1. 第一阶段:训练一个变分自编码器,将图像压缩到一个低维的潜在空间。这个VAE的编解码器需要训练得非常出色,以保证重建质量。
  2. 第二阶段:在VAE编码器得到的潜在空间上,而非像素空间,训练扩散模型。
  3. 生成时:用扩散模型在潜在空间中生成样本,再用VAE的解码器映射回像素空间。

这种架构结合了VAE、GAN(常用于提升VAE解码器质量)和扩散模型的优势,是当前文生图、文生视频等SOTA系统的基础。

扩散模型的数学视角(补充)

扩散模型有多个等价的数学解释,这增加了其深度但也带来了复杂性:

  • 潜变量模型视角:类似于VAE,将加噪过程视为潜变量,通过优化变分下界进行训练。
  • 得分匹配视角:模型在学习数据分布(在不同噪声水平下)的得分函数,即概率密度对数的梯度。
  • 随机微分方程视角:将扩散和生成过程视为一个SDE的正向和反向过程,神经网络在学习其漂移项。

总结 🎯

本节课中我们一起学习了两种强大的生成模型:

  1. 生成对抗网络:通过生成器和判别器的对抗博弈进行训练,能产生高质量样本,但训练不稳定。
  2. 扩散模型:通过学习和迭代逆转一个逐步加噪的过程来生成数据。我们重点介绍了直观的整流流模型,并了解了其训练和采样过程。现代潜在扩散模型通过结合VAE、GAN和扩散模型,构成了当前文生图、文生视频等应用的基石。

生成模型是一个快速发展的领域,这些基础模型以各种方式组合,不断推动着人工智能生成内容能力的边界。

015: 3D视觉

概述

在本节课中,我们将要学习3D视觉的基础知识。我们将从3D物体的不同表示方法开始,探讨它们各自的优缺点,然后了解深度学习如何与这些表示方法结合,以解决3D生成、重建和理解等任务。课程内容将涵盖从显式表示(如点云、网格)到隐式表示(如符号距离函数、神经辐射场)的演变,并介绍相关的数据集和应用。


3D物体表示方法

在2D图像中,我们使用像素矩阵来表示物体。然而,在3D世界中,物体的表示方法更加多样和复杂。3D物体不仅包含几何形状,还可能包含纹理、材质等信息。我们首先关注几何形状的表示。

3D物体的几何表示方法大致可以分为两类:显式表示隐式表示

  • 显式表示:直接描述物体表面的点或面。例如点云、多边形网格。
  • 隐式表示:通过一个函数来描述物体,该函数定义了空间中哪些点属于物体表面、内部或外部。例如水平集、符号距离函数。

每种表示方法都有其适用的任务和优缺点,特别是在与深度学习方法结合时。


显式表示

上一节我们介绍了3D表示的两大类别,本节中我们来看看具体的显式表示方法。

显式表示直接给出了物体表面的位置信息。以下是几种常见的显式表示:

点云

点云是最简单的3D表示形式,它仅由一组3D空间中的点构成,不包含点与点之间的连接信息。

  • 数据结构:一个 3 x N 的矩阵,其中每一列代表一个点的 (x, y, z) 坐标。
  • 附加信息:有时会包含每个点的表面法向量,用于指示该点所在表面的朝向,这对渲染光照效果非常重要。
  • 优点
    • 获取简单:是许多3D传感器(如深度相机、激光雷达)的直接输出格式。
    • 灵活通用:可以表示任意拓扑结构的物体。
  • 缺点
    • 无连接信息:无法直接判断物体的拓扑结构(例如,一个点云无法区分一个环面和一个球体)。
    • 采样不均:如果点分布不均匀,某些区域细节会丢失。
    • 操作困难:难以直接进行平滑、简化或细分等操作。

多边形网格

为了克服点云缺乏连接信息的缺点,多边形网格在点的基础上,增加了点与点如何连接以构成面的信息。

  • 数据结构:包含顶点(3D点)和(由顶点索引构成的多边形,通常是三角形)。
  • 应用广泛:是计算机图形学、游戏引擎中最主流的3D表示方法。
  • 优点
    • 信息完整:明确表达了物体的表面和拓扑结构。
    • 支持丰富操作:存在成熟的算法支持网格的细分(增加细节)、简化(减少面数)和正则化(使网格更规整)。
  • 挑战:网格结构不规则(面的大小、顶点连接数可变),早期难以与需要固定尺寸输入的卷积神经网络结合。

参数化表示

对于一些具有规则结构的物体(如椅子、桌子上的直线),可以使用数学函数来精确描述。

  • 核心思想:利用函数将低维参数空间映射到3D空间中的物体表面。
  • 示例
    • 2D圆:可以用参数方程表示:x = cos(t), y = sin(t),其中 t 是角度参数。
    • 3D球体:可以用两个参数 (u, v) 表示:x = sin(u)cos(v), y = sin(u)sin(v), z = cos(u)
  • 高级形式:贝塞尔曲线/曲面,通过少数控制点来定义平滑的曲线和曲面。
  • 优点:表示紧凑,易于进行数学操作和插值。
  • 缺点:难以描述任意复杂、不规则的形状。

显式表示的优缺点

综合来看,显式表示有其明显的优势和不足。

  • 优点易于采样。给定一个参数化表示或一个密集网格,我们可以轻松地生成物体表面上任意多的点。
  • 缺点难以进行内外测试。对于一个给定的3D空间查询点,判断它位于物体内部还是外部通常比较困难。这对于一些需要空间查询的任务(如某些新型渲染方法)是个障碍。

隐式表示

由于显式表示在空间查询上的局限性,人们发展了隐式表示方法。

隐式表示的核心思想是,用一个函数 F(x, y, z) 来定义物体。所有位于物体表面的点满足 F(x, y, z) = 0;函数值为负的点在物体内部;函数值为正的点在物体外部。

  • 经典示例:单位球体可以用函数 F(x, y, z) = x² + y² + z² - 1 表示。表面点满足 F=0,内部点 F<0,外部点 F>0
  • 优点
    • 易于空间查询:判断点是否在物体内部变得非常简单,只需计算函数值。
    • 易于组合:通过逻辑运算(并、交、差)或算术运算,可以轻松地将多个简单隐式函数组合成复杂形状。这在计算机辅助设计中广泛应用。
    • 支持平滑混合:使用有符号距离函数,可以通过加法等操作实现形状间的平滑过渡。
  • 缺点难以采样。从 F(x, y, z)=0 这个方程中直接解出表面上的点(即采样)通常很困难。

从隐式函数到体素

虽然隐式函数表达能力强,但对于复杂形状,其函数可能没有解析式,且每次查询都需计算。一个实用的折中方案是预计算

  • 方法:在3D空间中定义一个密集的网格,预先计算每个网格点处的隐式函数值(例如,有符号距离)。
  • 存储:将这些值存储在一个3D矩阵中。
  • 二值化:如果只关心物体内部/外部,可以将距离值二值化(例如,内部为1,外部为0)。这样得到的3D二值矩阵就是体素表示。
  • 特点:体素可以看作是3D像素,它本质上是隐式表示的一种离散化、非参数化的形式。


3D视觉的数据集

要应用深度学习,高质量、大规模的数据集至关重要。与ImageNet推动2D视觉类似,3D领域也有几个关键数据集:

  • Princeton Shape Benchmark:早期数据集,约1800个模型,180个类别。
  • ShapeNet:一个大规模数据集,核心子集包含约5万个模型,55个类别(如椅子、汽车),推动了3D深度学习的早期发展。
  • Objaverse:更近期的超大规模数据集,包含数百万个带纹理的3D资产。
  • 真实物体扫描数据集:例如CO3D,通过众包方式收集真实物体的多视角视频,用于3D重建。
  • 场景级数据集:如ScanNet,包含真实室内场景的3D扫描和标注。
  • 部件级数据集:如PartNet,标注了物体的部件层次结构和运动属性。

尽管3D数据集在不断增长,但其规模与互联网上的2D图像/视频数据相比仍有巨大差距,这是3D视觉面临的一个挑战。


深度学习与3D表示的结合

有了对3D表示和数据集的了解,我们现在来看看深度学习如何与它们结合,解决各种任务,如生成、识别、重建等。

早期方法:基于2D渲染

在3D深度学习初期,一个直接的想法是利用强大的2D图像模型。

  • 思路:将3D物体从多个视角渲染成2D图像,然后使用标准的2D卷积神经网络处理这些图像,最后融合多视角信息进行分类或识别。
  • 优点:直接利用了在ImageNet等大型2D数据集上预训练的、性能优异的模型。
  • 缺点:不是原生的3D方法,且渲染质量受原始3D数据影响。

体素方法:3D卷积神经网络

最直接的3D深度学习方法是将2D卷积扩展到3D,处理体素数据。

  • 思路:将二值体素网格作为输入,使用3D卷积核进行特征提取。
  • 优点:概念简单,是2D CNN的自然延伸。可用于分类和生成(如3D-GAN)。
  • 缺点:计算和内存消耗大(O(n³)),且分辨率受限,导致模型表面粗糙。

改进:八叉树

为了提升体素方法的效率,八叉树被引入。

  • 思路:自适应地细分3D空间。在物体表面附近使用高分辨率体素,在空旷区域或物体内部使用低分辨率体素。
  • 优点:在相同内存下,能表示比均匀体素更高分辨率的细节。

点云方法:PointNet

为了处理更常见的点云数据,斯坦福团队提出了PointNet。

  • 核心挑战:点云是无序且点数可变的集合。
  • 解决方案
    1. 使用共享的多层感知机独立处理每个点,提取点特征。
    2. 使用一个对称聚合函数(如 maxsum)将所有点的特征聚合为一个全局特征。这个操作保证了排列不变性。
    3. 基于全局特征进行分类或其他任务。
  • 损失函数:对于生成任务,需要比较两个点云。常用倒角距离(Chamfer Distance)或推土机距离(Earth Mover‘s Distance)。

参数化表面方法:AtlasNet

为了生成光滑的曲面,AtlasNet等方法用神经网络学习参数化映射。

  • 思路:训练多个小型神经网络,每个网络将一个2D正方形参数域(如 (u, v))变形并映射到3D物体表面的一块“碎片”上。所有碎片拼接成完整的物体。
  • 优点:可以生成连续、光滑的网格表面。

隐式函数方法:DeepSDF等

人们意识到,深度神经网络本身就是强大的函数逼近器,非常适合用来表示隐式函数。

  • 思路:训练一个神经网络 f(p),输入是一个3D坐标点 p=(x, y, z),输出是该点的有符号距离值(SDF)或占用概率(0/1)。f(p)=0 的等值面就是物体表面。
  • 优点
    • 表示紧凑:一个网络可以表示一个复杂形状。
    • 无限分辨率:可以查询任意位置,不受离散网格限制。
    • 易于组合:网络可以编码形状先验,用于从图像或代码生成3D形状。

神经辐射场:NeRF

NeRF将隐式表示推向了新的高度,它不仅建模几何,还建模外观。

  • 核心:用一个神经网络表示一个3D场景。输入是一个3D点 p 和观察方向 d,输出是该点的颜色 c=(r, g, b)体密度 σ
  • 可微分渲染:通过经典的体渲染方程,将沿着一条射线的所有点的颜色和密度积分,合成该射线对应的2D像素颜色。这个过程是可微分的。
  • 训练:只需输入多视角的2D图像及其相机参数,通过最小化渲染图像与真实图像的差异,即可优化神经辐射场。
  • 优点:能够重建出具有复杂光照和细节的高质量3D场景。
  • 缺点:渲染速度慢,需要沿着每条射线密集采样并查询网络。

高效渲染方法:3D高斯泼溅

为了加速NeRF的渲染,3D高斯泼溅提出了一种混合表示。

  • 思路:用一组3D高斯椭球(具有位置、尺度、旋转、颜色、不透明度属性)来显式地表示场景。渲染时,只需将这些高斯投影到2D并进行混合。
  • 优点:渲染速度极快(实时帧率),质量与NeRF相当。
  • 本质:可以看作是一种具有空间范围的、可高效渲染的“智能点云”。

超越几何:结构与关系

物体的3D表示不仅包含几何细节,还包含高层次的结构信息,如对称性、部件层次、物体间关系等。这也是一个重要的研究方向。

  • 基于图网络的方法:将物体部件表示为图的节点,部件间关系表示为边,使用图神经网络进行学习和生成。
  • 程序化生成:用程序(如循环、条件语句)来描述具有重复和规则结构的物体。最近,研究者开始探索利用大语言模型来生成此类程序,再结合神经隐式函数刻画细节,实现可控的3D内容生成。

总结

本节课我们一起学习了3D视觉的基础。我们从3D物体的不同表示方法(显式 vs. 隐式)出发,探讨了点云、网格、体素、参数化表面、隐式函数等各自的特点。随后,我们回顾了3D领域的重要数据集,并深入讲解了深度学习如何与这些表示方法结合:从早期的多视角渲染和3D卷积,到直接处理点云的PointNet,再到用神经网络表示光滑曲面的AtlasNet,以及革命性的、联合建模几何与外观的隐式表示方法——神经辐射场及其高效变体3D高斯泼溅。最后,我们简要提到了对物体结构和关系进行建模的高级表示方法。理解这些表示方法的演进和权衡,是掌握现代3D视觉与图形学算法的关键。

016:视觉与语言

概述

在本节课中,我们将要学习多模态基础模型。我们将探讨如何构建能够处理图像和文本等多种模态信息的强大模型,并了解它们如何通过预训练和微调来适应各种下游任务。课程将从图像分类基础模型开始,逐步深入到结合视觉与语言的多模态模型,并讨论如何通过模型链式组合来解锁新的能力。

图像分类基础模型:CLIP 🖼️

在之前的课程中,我们学习了为单个任务(如图像分类、图像描述)构建模型的流程。这通常包括收集数据集、训练专用模型并在测试集上评估。然而,近年来领域内出现了一个转变,即从构建单个模型转向构建更通用的基础模型。

基础模型旨在通过预训练让模型掌握多种技能和任务,随后再根据具体需求将其适配到个别任务上。例如,GPT就是一个常见的语言基础模型,它在大量互联网数据上预训练,然后可以针对数学问题、符号推理等不同任务进行微调。基础模型的优势在于,它通常只需要极少甚至无需额外训练数据,就能快速适应新任务。

当我们讨论基础模型时,通常会看到一些共同特征:它们对多种任务具有鲁棒性和通用性,参数量巨大,训练数据量庞大,并且通常使用自监督目标进行训练。本节课我们将重点讨论图中绿色部分,即视觉和多模态基础模型。

从自监督学习到多模态

上一节我们介绍了自监督学习的概念,本节中我们来看看如何将其扩展到多模态领域。回忆一下自监督学习方法SimCLR,它使用对比目标,将同一图像的不同增强版本在表示空间中拉近,同时将不同图像的表示推远。这种方法的理念是让相似概念(如猫的不同形态)的表示彼此靠近,而不同概念(如猫和狗)的表示彼此远离。

我们希望这些通过自监督学习目标训练出的表示具有足够的通用性,这样当模型看到新的内容(如猫或狗的素描)时,仍然能将其嵌入到合适的表示空间中以便分类。

我们可以将同样的思想和目标应用到多模态场景中,开始思考如果在这个表示空间中加入文本会怎样。例如,如果我们能将“一只毛茸茸的猫”这段文本的表示也嵌入进来,并让它靠近猫的图像表示,那就非常理想了。这样,我们就可以通过图像和文本来查询概念。

CLIP模型的工作原理

CLIP模型采用了与SimCLR类似的思想,但将其扩展到了图像和文本两种模态。在CLIP中,我们仍然有一个图像编码器(左侧),但现在右侧多了一个文本编码器。这个文本编码器用于嵌入各个图像的描述文本。

训练目标是:一张狗的图像应该学习到其表示更接近“我最喜欢的狗是金毛寻回犬”这段文本的表示,而远离所有其他文本的表示。由于这与SimCLR的公式相同,训练此类模型的目标就是收集大量图像-文本对,将它们输入模型进行小批量训练,并确保使用SimCLR中的对比目标,但现在将其应用于图像和文本之间。

我们希望每张图像最接近其对应的文本,并远离所有其他文本。同时,我们也希望反之亦然:每段文本最接近其对应的图像,并远离所有其他图像。因此,这是一个在两种输入模态之间互补的对称损失函数。

CLIP类模型的优点在于,它只需要图像和文本的关联数据即可训练,而互联网上存在大量此类数据。OpenAI在2021年发布CLIP模型时,正是从互联网上收集了大量数据,并使用这种对比目标进行训练。

训练完成后,遵循自监督学习课程中看到的两步流程:第一步是预训练,第二步是取用训练好的图像编码器,将其适配到新任务。你可以在这个预训练的图像编码器上添加一个额外的线性层,以适应图像分类、检测任务,甚至可以接入解码器来输出语义分割图。通过从这种预训练目标初始化模型,许多不同的任务都成为可能。

当CLIP论文发布时,令人兴奋的是,仅在这个CLIP编码器之上线性添加一个分类器,就带来了性能的巨大提升。图表显示,在多个图像分类数据集上的平均性能,CLIP模型(红色)遥遥领先。随着训练图像数量的增加,性能持续提升。这表明我们找到了一个非常有效的预训练目标,并且互联网上丰富的图像-文本数据意味着我们可以训练出规模巨大、性能优异的模型。

零样本分类与提示工程

当然,故事并未结束。理想情况下,我们希望能够直接使用CLIP模型,而无需为每个新任务微调特征。对于语言模型,你训练一个模型进行自动补全(例如,给定“I love”,模型补全“cake”)。在第二阶段,你可以直接使用同一个模型,通过提示将其适配到新任务,而无需重新训练。

但对于CLIP,问题在于没有这种自动补全过程。我们使用对比目标训练了模型,但要将其适配到新任务,仍然需要训练数据和需要在顶部训练的线性层。

因此,人们开始思考如何让模型能够直接使用。他们想出了一个巧妙的技巧:利用文本编码器来引导模型泛化到任何下游分类任务。其工作原理如下:假设你想用CLIP模型对图像进行分类,但不想为任何下游任务重新训练或适配模型。你可以取文本编码器,将类别名称输入其中以创建文本向量,然后使用最近邻方法找出正确的分类。

具体操作是:取新数据集中的所有类别(例如,“飞机”、“狗”、“鸟”),在文本空间中嵌入它们,得到每个类别的向量。当新图像输入时,只需使用图像编码器嵌入该图像,然后找到最接近的邻居。在这个例子中,图像与“狗”向量的相似度得分最高,因此可以将其分类为狗。

你可以将整个过程视为构建一个“1-最近邻”算法。你在文本空间中生成了一堆中心点或嵌入,可以将它们用作类别标签,并对任何新输入的图像执行1-最近邻搜索以找到最佳分类。

当然,单个单词可能不足以获得非常好的词向量。相反,你可能希望使用一个短语。原因在于,互联网数据中的文本通常不是孤立的单词。CLIP是从互联网下载的短语中训练的,因此理想情况下,你希望选择能给出最佳表示的短语。例如,不是简单地嵌入“飞机”、“狗”、“鸟”,而是嵌入代表“一张飞机的照片”、“一张狗的照片”的向量。事实证明,仅仅做这个小改动,就能在ImageNet上获得约1.3%的性能提升。

当然,选择正确的短语本身也很困难。因此,人们通常不会只选一个短语,而是选择许多不同的短语(例如,“一张狗的照片”、“一幅狗的图画”等),为每个类别生成多个向量。最后,取每个类别所有短语向量的平均表示,将其作为该类别的平均向量(如平均狗向量、平均飞机向量)。然后,你就可以回到起点,在这个平均向量上执行相同的1-最近邻算法。

CLIP的泛化能力与局限性

CLIP能够适配各种新的图像分类任务。虽然它在ImageNet上表现良好并不稀奇,但真正有趣的是它在其他数据集上的表现。例如,ObjectNet数据集包含人们在非常规场景下拍摄的物体照片(如把香蕉放在地上拍照,或拍摄腐烂的香蕉)。这些图像中的物体并不常见。如果在ImageNet上训练,模型表现不会很好,因为ImageNet包含的大多是这些类别的典型形态。但CLIP模型表现同样出色,这让许多人感到兴奋,因为这种泛化到前所未见、甚至某种程度上超出分布的数据的能力非常强大。

CLIP泛化能力更好的原因主要有两点:首先,从互联网下载的文本不仅包含类别标签,还包含更多结构信息,如形状、颜色等,这些都有助于学习更好的表示,使模型能更好地适应分布外或外观略有不同的物体。其次,数据规模巨大。ImageNet只有约130万张图像,而互联网上可轻松下载的图像-文本对已达数十亿。这些模型看到了多得多的数据,使得适配变得容易得多。

人们开始在广泛的泛化任务上进行实验,结果表明这些模型不仅对自然图像,对素描、对抗性数据等都非常鲁棒。性能普遍表明这些模型非常优秀,适用于多种应用。

图表显示了零样本分类和线性探测(添加线性分类器并微调)之间的性能差异。当然,线性探测在大多数数据集(绿色部分)上能提升性能,但并非总是如此。在某些情况下,CLIP零样本直接表现就很好。这似乎表明我们终于解锁了将图像编码器适配到各种下游任务的能力,这也是许多人将CLIP视为第一个图像基础模型的原因。

那么,是什么让CLIP工作得如此出色?关键在于其训练数据。CLIP在训练时没有使用真实的标签,只是下载了与图像相关的任何文本。使其表现出色的因素包括:模型参数量巨大(从ResNet架构转向ViT,使用了3.07亿参数的Transformer架构),以及数据量巨大(从ImageNet的120万张图像扩展到从互联网下载的约4亿个图像-文本对)。模型规模和数据的扩展极大地提升了性能。

视觉语言模型:从ViLBERT到Flamingo 🦩

CLIP问世后,人们立即开始尝试这个目标,多年来出现了许多CLIP的变体。其中一个特别突出的变体是2022年提出的CoCa。CoCa采用了与CLIP相同的目标(图像编码器、文本编码器及两者间的对比损失),但额外添加了一个解码器。该解码器接收来自图像编码器的图像特征,通过交叉注意力机制为图像生成描述。事实证明,这个描述过程也有助于模型学习更丰富的信息。

其动机在于,仅仅区分图像是猫还是狗并不足够,而用文本描述图像则需要模型学习更多信息。因此,假设这是一个更强的学习目标,能学习到更好的特征。总体而言,CoCa相比CLIP在所有图像变体和数据集上的性能都有显著提升,平均约有10%的性能提升。

我认为这是基础模型首次在性能上全面超越我们通过监督学习训练的所有模型。此时,人们开始放弃用于图像编码器的监督学习目标,转而完全专注于使用互联网数据的自监督学习方法进行预训练。

CLIP的优势与局限

CLIP有很多有趣的优势:训练简单(只需简单的对比学习目标),推理速度快(可以将整个数据集嵌入表示,分类时只需进行检索),这使得它不仅适用于分类任务,也适用于搜索和检索任务。此外,CLIP是开放词汇的,可以输入任何文本描述来检索相关图像,这使其能够应用于许多不同领域。当然,CLIP也易于与其他模型链式组合,这个想法后来变得非常流行。

然而,CLIP也存在许多局限性。例如,它无法区分“草地上的杯子”和“杯子里的草”这两张图像。原因在于CLIP的学习目标严重依赖于批次大小。如果批次大小不够大,批次中的其他元素不太可能为模型提供有用的监督。如果总是比较猫和卡车,你实际上学不到好的猫的表示。相反,你只能得到某种在高层面上还行的表示。但如果你将批次大小增加到32,000并在多个GPU上训练,你就能开始学习到真正好的表示,甚至可以区分威尔士柯基犬和其他柯基犬。这是因为需要批次中有足够接近的负样本(即困难负样本)来迫使模型学习。这对于让这些模型良好工作非常重要。

不幸的是,无论人们如何尝试增加批次大小,都不能保证模型能学到好的表示。因此,你实际上受制于训练数据的随机性。增加批次大小确实有助于学习一些细粒度概念,但它仍然是有限的,并且32,000的批次大小对大多数实验室来说都太大了。

人们已经在许多基准测试中发现了CLIP的错误,并指出CLIP缺乏组合性概念。例如,“草地上的杯子”和“杯子里的草”涉及组合不同概念(杯子、草地)及其关系,而这些在CLIP的表示中并没有很好地组合。已经出现了许多基准测试(如Winoground、CLEVR、ARO等),它们不断发现CLIP存在大量局限性,有很多事情它根本无法完成。

作为回应,社区立即开始思考如何手动构建批次以包含困难负样本。例如,如果批次中有一种柯基犬,最好能有另一种柯基犬也在批次中,这样模型就不得不学习好的表示。因此,“使用困难负样本训练”的想法在社区中流行了大约一年,直到我们发布了一篇后续论文指出,使用困难负样本训练实际上会导致模型遗忘很多语义信息。原因尚不明确,但我们在不同环境和数据集上的泛化性能确实变得更差。因此,在确定构建数据和训练信号的正确方法方面,仍有大量工作要做。

此外,图像级别的描述仍然不够。理想情况下,我们需要的远不止这些。我们不仅希望识别出“有人过马路”,还希望知道人的位置、车的位置、街道的位置等。所有这些 grounding 信息在CLIP中是完全缺失的。理想情况下,你希望数据集包含这类信息,模型也能对此进行推理。

最后,CLIP的一个重大劣势是,无论你的数据集有多大(即使收集了多达50亿张图像),仍然不足以涵盖你可能关心的所有重要信息。因此,我们在数据过滤方面做了很多努力,研究如何过滤互联网数据以找到训练CLIP模型的最佳训练数据。这已成为当前该领域研究的前沿。

多模态语言模型:Flamingo与MoO 🦜

上一节我们讨论了关于泛化分类的基础模型分支,现在让我们来谈谈视觉和语言模型。在过去的两年半里,出现了一类新的流行基础模型,我们通常称之为多模态语言模型。我将以LlaVA开始讨论,它可以说是最早流行起来的多模态语言模型之一。

其动机在于,语言模型通过下一个词预测(自动补全)过程,非常适用于适配许多新任务。因此,我们能否开始思考让图像模型也做类似的事情?我们能否给出一张图像,并开始进行类似于自回归过程的不同推理?这催生了一类称为视觉语言模型或多模态模型的新模型。

当然,从历史角度看,这个想法在2022年并非全新。早在2019年,ViLBERT就引入了这个想法,它将图像模型和语言模型结合在一起,以实现跨不同任务的泛化。但它们都是在Transformer之前训练的,并且大多使用LSTM。而现在的复兴正是发生在LlaVA等模型上,这些模型转向了更好的架构、更好的目标集,并且不再仅仅在单个任务上训练,而是使用互联网数据的某种预训练目标,在多种任务的基础上进行训练。

Flamingo模型架构

那么,这是如何工作的?我们如何思考LlaVA?要讨论LlaVA,让我们先退一步思考Transformer模型或自注意力机制。具体来说,当我们思考语言模型时,它们所做的是关注过去的历史。例如,输入序列“Cats are so”,模型为了生成下一个词,会关注这个历史上下文,然后生成它认为应该的下一个词(如“cute”)。这是表示相同目标的另一种方式:底部是输入文本,模型将生成下一个词“cute”。

当我们思考视觉语言模型时,人们通常指的是通过将我们关心的图像 grounding 到对话中,来增加额外的上下文。我们可能关心以某种方式对图像进行标记,并将这些标记与历史上下文(如“Cats are so”)一起输入到语言模型中,然后利用它来自动补全图像的其余描述。

这就是LlaVA背后的基本思想:将这些图像标记与正在生成的词语一起输入,以持续生成关于该图像的更多描述。

当然,随之而来的问题是:如何定义这些标记?它们首先应该是什么?在LlaVA中,他们的解决方案是使用CLIP图像编码器。他们取用CLIP模型,提取其图像编码器,然后基本上从该编码器中提取标记。

你首先可能想到的是只使用CLS标记。图像被分割成多个块,每个块变成一个表示并输入到Transformer架构中。在CLIP中,它经过多层处理,最后得到每个块的标记以及CLS标记的表示。到目前为止,我们只考虑了CLS标记,仅用它进行分类任务。但其中还有其他标记。问题在于,这些其他标记从未被监督过。CLS标记通过对比目标与文本一起被监督,但其他标记从未用于任何目的。因此,它们可能不包含任何有用信息,经验表明这些特征并不非常有用。

但人们发现,如果你往回退一层,使用CLIP编码器中倒数第二层的特征,这些特征实际上非常有用。这些特征用于在最后一层生成最终的CLIP嵌入,并且它们包含了关于图像中物体位置的大量空间信息。因此,当将CLIP编码器与基于LLM的Transformer模型结合时,人们通常使用这些特征。

这就是整个LlaVA架构的样子:将图像输入预训练的CLIP编码器,提取一堆特征,将这些特征通过一个需要训练的线性层。这个线性层将训练成将CLIP表示转换为LLM能够理解和处理的东西。一旦有了这些标记,你就可以将它们全部输入到语言模型中,现在它可以生成关于该图像的对话。

LlaVA是最早流行的模型之一。随后,谷歌迅速发布了Flamingo。在Flamingo中,它基本上遵循了LlaVA的整个设置,能够将视觉编码器的特征与大型语言模型结合,但其创新之处在于如何融合这些不同的特征。

在LlaVA中,特征通过线性层输入,并作为输入的一部分馈入。而在Flamingo中,他们所做的不是这样,而是基本上将从视觉编码器输出的所有特征馈入到LLM的每一层。因此,他们必须对LLM架构本身进行一些修改。

以下是Flamingo训练数据的一个示例:图像被编码(例如,一只狗和一只猫),它们都被嵌入,并将被馈入到LLM的每一层。下方是描述每张图像的数据:以图像开始,描述该图像,然后下一张图像,描述下一张图像,依此类推。它们作为输入馈入LLM,而输出将是自动补全最后一张图像的描述。因此,你有一张图像,接着是狗的描述,然后是第二张图像,你开始描述,模型将被训练来自动补全第二张图像的描述。

他们对模型本身做了哪些改变?他们在LLM的每一层都添加了这种门控交叉注意力模块,并做了另一个改变:他们还添加了这个感知采样器,它基本上对图像表示进行采样和下采样,为每一层提供固定数量的、维度更小的标记。

总体而言,大多数组件都是冻结的:所有语言模型权重、所有视觉模型部分都被冻结。唯一被训练的部分是这些感知采样器组件和添加到LLM每一层的交叉注意力层。

这个交叉注意力模块看起来是这样的:在每个LLM层之前,都有这个交叉注意力组件。其目的是查看图像特征,然后决定它想要保留哪些图像特征,以及哪些部分对语言模型有用。他们将其设计为一组你已经见过的组件:使用交叉注意力层关注图像特征,然后在交叉注意力之后添加tanh非线性激活,这基本上决定了要保留这些组件中的哪些部分,以及要忘记图像的哪些部分。接着,它通过一个全连接层,稍微调整这些表示,然后再次通过tanh非线性激活来决定哪些部分应该保留,哪些不应该。经过这两个组件(每个都有残差连接)后,它进入正常的语言模型处理,然后继续生成所需的词。

这些额外的层被添加进来,只是为了让语言能够在每一层处理时融入并关注视觉特征。实际的修改本身,如果你对代码感兴趣,只是大约两三行代码,他们在其中添加了这个交叉注意力层,然后中间是tanh非线性激活。就代码而言,这是一个非常小的改动,尽管对模型来说是一个巨大的变化,因为它现在可以选择在每一层处理时关注图像的哪些部分。

Flamingo的训练与应用

Flamingo非常令人兴奋,但训练它非常困难。他们有一种非常巧妙的训练方法,使得模型能够适应许多不同的任务。他们训练的方式是通过将一堆不同的图像和描述连接在一起。你不仅仅有一张图像和一个描述,而是在开头有一个描述说“这里有一些我宠物的可爱照片”,然后是“图像开始”的句子,接着是第一个组件的描述,然后是第二张图像和第二张图像的描述,依此类推。因此,训练设置看起来像是一个图像-文本-图像-文本交错的长序列数据。

当然,在描述任何单张图像时,你并不希望模型查看整个上下文,而只希望它关注那一张特定的图像。因此,他们创建了一个掩码方案,其中每张图像在生成时只查看该特定图像的特征,而不是其他图像的特征。这意味着,当生成“我的小狗坐在草地上”的描述时,你只查看与小狗对应的特征;同样,当生成猫的描述时,你只查看猫的图像,而不是其他图像。因此,他们创建了这种手工制作的掩码方案,以确保描述始终只关注特定的图像,但在训练时,模型确实能看到它正在生成的所有内容的整个上下文。

为什么这个过程有帮助?为什么能够同时看到所有这些内容是有帮助的?因为它允许你实现这些类型的应用。以下是Flamingo能够展示的三种不同应用,它们都围绕着进行多次对话或处理多张图像。

在第一种情况下,输入一张图像,Flamingo模型通过说“这是一张两只泰迪熊在月球上的照片”来描述图像。然后,它允许人们提出另一个问题,比如“它们在做什么?”。由于它已经通过现有的LLM进行了训练,继承了该LLM的推理能力,现在它可以推理并回答这个问题,可以说“泰迪熊正在交谈”。然后用户可能会问“它们在使用什么物体?”,Flamingo可以回答“看起来是一台电脑”,等等。因此,你可以通过做两件事来实现关于图像的多轮对话:首先预训练语言模型,然后将其融入Flamingo;其次,在训练数据中允许模型看到许多不同的图像和多轮对话,从而适应更长的文本序列。

你也可以给它多张图像,并询问这些图像的共同点是什么。现在,Flamingo模型将查看每个不同的组件并进行推理,说“它们都是火烈鸟”。因此,你可以开始进行许多这类非常酷的应用。

人们还展示了你可以开始进行上下文学习(如果你已经在GPT中使用过上下文学习,你应该熟悉)。你可以对Flamingo做同样的事情:传入一张图像和描述,再传入另一张图像和描述,现在当你传入一张新图像时,它会给你一个描述。或者你可以说“这里有一张图像,一个问题和一个答案;这里有一张图像,一个问题和一个答案”,然后当你传入一张新图像并只问问题时,它会给出答案。因此,你并没有训练它执行这些不同类型的任务,而是提供了它应该具有的行为示例,它应该泛化到你可能关心的新类型的行为。

同样,你可能关心分类,也可以使用Flamingo进行分类。你可以给它一张图像并说“这是地下,这是国会”,然后你可以问“这是什么?”。你甚至可以教它进行OCR和数学运算:你给它一张图像并说“哦,这应该对应2加1等于3”,最终当你给它一张新图像时,它应该能够自动补全并提取出“3乘以6”,然后通过推理整个过程给你输出。是的,这将是少样本学习的一个例子,你给它一些示例,然后问它新事物应该是什么。如果我丢弃所有的上下文示例,那将是零样本学习。

我们并不是在技术上连接它们,而是将图像标记通过这个感知采样器传递到LLM的每一层。因此,只有文本是被连接并作为输入馈入Flamingo模型的,而它选择何时关注图像的哪些部分。

你一次性输入给它,但在幕后,当然,这是网络接口。实际上,他们所做的是缓存模型,假设用户会继续对话,因此模型被缓存并准备好接受更多标记。是的,但如果他们没有缓存,那么是的,他们会将整个对话作为输入传入。

Flamingo非常酷,因为他们的论文中有这些非常大的表格,你可以去查看。但真正酷的是,有许多任务非常困难,你必须调整CLIP才能完成,而Flamingo只需零样本或少样本就能做到。你开始在许多不同的基准测试中看到这些巨大的改进。我认为,正是在这个时候,该领域从报告少数分类基准测试转向报告任何类型的理解任务,只要你能将其构建为问答过程,你就可以为各种技能构建基准测试。在过去的两年里,我们看到这成为计算机视觉领域的常态。

开源多模态模型的崛起:MoO 🦉

大约在去年这个时候,看到了LlaVA的成功,许多公司开始大力投资这些模型。因此,你开始看到许多API模型,如GPT-4V、Gemini 1.5 Pro、Gemini 1.5 Flash,许多这些模型开始发布,甚至Anthropic也加入了竞争,推出了Claude 3 Opus。当然,现在Claude 4 Opus也已经发布。因此,出现了许多这些模型,并且它们在一系列基准测试上表现更好。

图表显示了该领域11个更流行的视觉理解基准测试的平均性能。这里存在巨大的差异。LlaVA(我们讨论过的开源模型)的平均准确率大约在43%,而GPT和其他模型的表现要好得多,大约在80%左右或高70%。这两种模型之间的性能差异巨大。

当然,一看到这种差异,人们立即开始将GPT和Gemini的知识蒸馏到变体模型中并尝试发布这些模型。例如,中国的阿里巴巴公司发布了名为Qwen的模型,还有InternVL、Phi等,所有这些模型都开始出现,并且它们都是从GPT(如果不是GPT,就是从Gemini)蒸馏而来的。

这导致了该领域的一个大问题,也成为了我自己研究议程中试图关注的一个重要部分:作为一个研究社区,我们实际上不知道如何构建真正高性能的视觉语言模型。如何构建这些模型的技巧只有OpenAI和Google Gemini团队中的人知道。但开源社区,研究社区,还停留在较低的水平。

当然,你可以说这些是非常好的开源模型,但它们并不真正开放,因为它们是蒸馏而来的。我们实际上不知道如何复现这些模型。我们只能生产它们,但如果GPT不存在,我们就不知道如何创建这些其他模型。因此,我过去几年的研究议程一直专注于弄清楚如何缩小这个差距,如何构建真正优秀的多模态语言模型,并将这种理解传播给整个社区。

在过去的六个月(大约一年)里,我们所做的是创建了我们自己的一类模型,我们称之为MoO。图表顶部显示了MoO的性能。MoO与所有其他模型的不同之处在于,它是完全开源的:开放权重(你可以下载模型)、开放数据(你可以下载训练集和评估集)、开放代码(你基本上可以在自己的家中训练自己的MoO,假设你有足够的GPU),并且你还可以进行评估,适配这个模型用于各种新事物,当然也可以开始在各种不同的上下文中使用它。

当然,学术基准测试还不够,因为我们最终关心的是人们是否会使用这些模型,人们是否会想用这些模型而不是GPT。为了确保我们正确进行了评估,我们发布了一个MoO的演示平台,并进行了一项大规模的用户研究,实际上将我们模型的输出与所有其他模型的输出进行了头对头比较。我们的模型与GPT-4V具有相同的Elo评分,排名第二,与GPT-4V的Elo评分仅差一分。这是一个巨大的评估,涉及约870名用户,我们向他们展示了这些模型的输出,并进行了约325,000次成对比较,询问人们更喜欢哪个模型的输出。我们的MoO模型排名第二,人们在GPT和我们的模型之间的偏好几乎像是抛硬币。但它已经超过了Gemini 1.5 Pro和Claude 3.5。最大的区别在于,我们是一个小型研究实验室,却击败了谷歌对Gemini的数十亿美元投资以及Anthropic的数十亿美元投资,并且已经与GPT匹敌。因此,我们对整个过程感到非常兴奋。

我们还开发了一个70亿参数的模型,紧随那些大型模型之后。这个70亿参数的模型非常令人兴奋,因为你可以将其放在单个GPU上。因此,你现在可以拥有这个能够执行各种视觉任务的模型,并且它可以在单个GPU上运行,这意味着许多人现在可以使用它并针对各种任务进行微调。我们在2025年9月发布了这个模型,社区对此非常兴奋。这是第一次发布了一个性能非常高的多模态视觉语言模型,许多人开始讨论和撰写文章,谈论他们想要使用它的各种方式。不断出现的一个用例是将MoO微调用于机器人应用。我今天不会讨论机器人,因为你将在下一节课中学习,但我想给你一些人们对此感到兴奋的例子。

许多公司,甚至像NVIDIA这样的人,也开始谈论开源社区最终会赶上,无论你在私下里做了多少模型开发。在那个时候,我们正在赶上。看到我们的模型发布后,Meta迅速发布了他们的Llama 3.2模型作为回应,许多人进行了评估,比较MoO与Meta的Llama模型。同样,我非常高兴我们也在Llama之上胜出。

MoO成功的关键:数据与像素级定位

让我展示一下MoO表现出色的原因。MoO成功的诀窍在于将其决策建立在像素本身之上。通常,当你给模型一个问题,比如“数一数有多少艘船”,它会给你一个数字,并且经常产生幻觉。但我们的模型不同之处在于,它实际上会指向它正在计数的所有东西。它会生成指向所有船的点,然后输出最终的数字。因此,它的决策是建立在像素本身之上的。这使我们能够训练一个模型,与Meta的Llama(在约60亿图像-标记对上训练)不同,我们的模型仅在70万个图像-标记对上训练。

最大的区别在于,我们手工策划了这70万个图像-标记对。这是我们能够做到的与这些公司构建的模型之间最大的区别。

许多人目前正试图从互联网下载这些图像-标记对,这已成为许多人训练这些视觉语言模型的基础:收集大量带有相关文本的互联网图像数据。但互联网数据的问题在于它是附带的。通常与图像关联的文本描述的是主观内容或上传者对图像的感受,很少真正谈论图像本身的内容。

与此同时,我们的数据看起来是这样的:对于单张图像,我们有关于该图像实际内容的密集描述,并且包含了许多人们在互联网上从未谈论过的东西。关于视觉世界,有大量的任务和知识我们从未提及。我永远不会告诉你某物在另一物的左边,仅仅因为对我们来说这样做很不自然。某物在另一物的左边是如此明显,你为什么还要传达这些信息?这就是我们开始从人们那里获取的那种信息。我们开始让人们谈论事物具有特定大小(如大)、形状(如矩形)、材料(如抛光、丰富)以及它在图像中的位置(如横跨图像的水平面)。所有这些信息确实使这些模型表现更好。

这是数据中的另一个例子:一张非常简单的手机屏幕或平板屏幕图像,我们这里的信息再次是互联网上完全缺失的、人们会发现有用的信息,比如“这是一个平板设备”、“时间是……”、“设备剩余电量是……”。这是那种会帮助人们使用这些模型的信息,但这也是我们从未在互联网上谈论过的信息。为了获取这类信息,我们设计了许多不同的问题,花了两年的时间进行不同类型的启发研究,以找出互联网上缺失的正确信息或信息片段,以及如何尽可能有效地获取它们。

非常重要的一点是,我们让所有的标注员不是输入描述,而是谈论描述。交谈自动打破了许多关于格莱斯准则的刻板印象,因此通过让人们交谈,我们让他们说出了他们通常不会输入的内容。

模型本身与LlaVA没有什么不同,我们有相同的设置:CLIP编码器输入,一个只是线性层的连接器,然后是一个大型语言模型,接收所有这些标记并输出任何你关心的东西。因此,模型本身看起来与现有模型非常相似,最大的区别在于数据本身的质量和密度。

由于这种定位能力,模型将其决策建立在图像本身之上,你可以让MoO做一些你无法使用任何其他模型做的事情,比如指向菜单。它实际上会告诉你菜单项在哪里。或者你可以告诉它指向“我可以在哪里设置搜索选项”,它会显示“好的,这是你可能想要设置这些选项的地方”,或者指向“中型数据集在哪里”,它会告诉你需要移动哪个选项。

我已经向你展示了它可以指向计数,但它也可以指向做非常细粒度的事情,比如能够询问“这辆公交车的路线号是多少?”。MoO不仅仅是简单地给你一个答案,它实际上会指向图像中(在这种情况下是包含公交车号的区域),然后返回公交车号给你。你可以让它推理左边有多少辆车 vs. 右边有多少辆车。你可以让它推理深度图像、俯视图像,甚至是非常拥挤的场景和运动区域。

同样令人兴奋的是(我们将在几分钟后讨论)是链式组合的想法,这在当今的多模态模型中不断出现。将MoO与其他模型链式组合的想法是,你可以将MoO的输出作为另一个模型(如SAM2)的输入。因此,你可以告诉MoO指向板球拍,现在你取那个点,将其输入到像SAM2这样的模型中进行分割,现在你可以对那个板球拍进行跨时间的分割。因此,你可以开始启用各种新的应用。

这是我们在办公室里尝试的一个例子,希望你在下一节关于机器人的讲座中会学到。我们让MoO指向水瓶在哪里,然后使用简单的运动规划器将机器人移动到那个水瓶旁。接下来,我们让它将那个水瓶移到脏盘子所在的地方,它指向水槽,然后将机器人移动到那里。然后我们告诉它指向水槽中的空闲空间,并将瓶子放在那个位置。因此,你可以再次将所有这些能力组合在一起,并将它们链式组合,甚至自动化许多机器人应用。现在,我的团队中很多工作都集中在适配许多这些视觉语言模型,并在实际物理领域中实现泛化。

分割基础模型:Segment Anything (SAM) ✂️

在剩下的20分钟左右的时间里,我想讨论如何泛化这些基础模型,使其不仅能处理图像分类和文本,还能泛化到你可能关心的任何输出空间。在这个领域中一个变得非常流行的模型是Segment Anything模型。

Segment Anything模型(简称SAM)试图构建一个分割模型,作为所有类型分割任务的基础模型。他们真正想做的是允许任何人在图像中指向他们关心的东西,然后希望模型能为那个东西输出一个掩码。例如,你希望一个模型能够泛化到固定数量的类别之外,涵盖你可能关心的任何类别,并且理想情况下,你希望这些输出是用户真正感兴趣的类别的掩码。因此,我们有两个目标:泛化到任何类别(大量类别),并且理想情况下,我们希望能够非常具体地输出用户真正关心的东西。这两个都是挑战:如何收集涵盖广泛类别的大量数据,以及如何设计一个能够精确定位用户真正关心的内容的架构。

让我们先从第二个问题开始:当我们想要某个东西的掩码时,这真的很模糊。想象一个场景,图像中有两只猫,用户进来说:“嘿,我想要猫的分割掩码。”但到底想要哪只猫的掩码并不清楚。理想情况下,如果你有指向能力,你实际上可以指向你关心的那只猫,然后根据点来创建相关的掩码。

当然,这些掩码质量可能不是很好,理想情况下,你希望这些掩码质量非常高,能够支持各种下游应用,如图像编辑或任何你可能想到的其他事情。

因此,要构建这种允许任何用户能够精确指定他们关心内容的架构,我们需要它超越仅仅输入文本描述你关心什么。SAM架构有两个或三个组件:图像编码器(同样可以是CLIP编码器),以及一个特殊的提示编码器,它试图编码文本、点、边界框或用户可能想要指定他们关心内容的任何方式。然后,给定这两个输入,它通过一个非常轻量级的解码器输出掩码。这个解码器看起来非常像你在这门课程中已经见过的分割解码器。

总体而言,这就是模型的样子:给定一张图像,我们使用图像编码器对其进行编码,然后有一堆不同的提示通过解码器与这些图像编码进行交互,并输出一个掩码。这是整体的架构设计。

现在,分割有一个大问题。假设用户确实指向了这个特定位置,并说:“嘿,我想要这个位置的分割掩码。”现在,那个分割掩码的问题在于,即使有一个点,它仍然不够明确,因为那个点可能指的是整个剪刀,可能只指你可以握持的部分,或者可能指你可以握持的其中一个部分。这种模糊性真的很难解决,而且你不想因为模型选择了错误的掩码而惩罚它。

因此,SAM架构所做的不是输出一个分割掩码,而是输出三个不同粒度的分割掩码,然后选择最接近真实情况的一个来计算损失,从而不惩罚其他掩码。希望随着时间的推移,这个模型将学会输出所有不同类型的掩码,然后用户基本上可以选择哪个最适合他们的用例。

如果你把所有这些东西放在一起,你现在唯一需要的就是数据:你需要跨许多不同类别的大量数据来真正使这个模型成为可能。数据的问题在于,直到2023年这个模型发布(大约一年半到两年前),大多数分割数据集都非常小。

这篇论文的作者所做的是,他们增长了当时存在的分割数据集的数量,图像数量增加了约6倍,分割掩码数量增加了约400倍。因此,他们显著增长并收集了大量掩码,以使这个模型尽可能高性能。同样,信息与我们在Flamingo和MoO中看到的信息非常相似:你需要真正高质量的数据来使这些模型尽可能高性能,并且对于许多视觉任务,数据在互联网上完全缺失,你需要出去寻找和收集这些数据才能使这些模型良好工作。

为了使这些数据成为可能,他们创建了这种循环过程:最初他们有一些标注数据,从这些标注中创建训练数据,训练一个模型,然后使用该模型标注更多数据,然后迭代地使用用户细化模型生成的分割,并继续这个过程。他们有一个“人在循环中,模型在循环中”的过程,提出分割建议,然后使用人工标注员修复分割。

这是他们数据中一个示例图像的样子:有很多类别,每个单独的蔬菜都有自己的掩码标注。收集这些数据非常昂贵,他们在数百万张图像上进行了这样的标注。这是另一个例子,同样,所有单个雨伞都被标注。这是另一个水下场景的例子,当然还有绘画,他们也有绘画的分割。所有这些加在一起,确实是构建这个分割基础模型的基础。

模型链式组合:解锁新能力 🔗

链式组合背后的想法你已经在本讲座中多次看到我给出的提示。其思想是能够将不同的模型组合在一起,以实现单个模型无法单独完成的事情。这是一个我们可以作为课堂进行的有趣小练习:我给你四张图像和四个类别。这些可能是你们中一些人从未见过的类别,也是CLIP未曾见过的类别。因此,CLIP实际上无法区分这些类别,因为它不知道哪个与哪个相关联。

这里有人知道哪个是哪个吗?马林巴琴?是的,第二个是马林巴琴。没错。有一个比较容易的,高架桥。是的,我想你们很多人都知道是哪个。但是,哪个是狗,哪个是鸟?如果我给你这些描述呢?现在我给了你这些东西的描述,突然之间,将每个与正确的类别关联起来对你来说变得非常容易,对吧?

这就是链式组合的基本思想:即使CLIP从未见过这些图像,这些概念很可能在互联网上以某种程度被讨论过,GPT可能能够描述它们。如果GPT可以创建这些描述,那么这些描述就成为分类哪个类别是哪个的非常好的方法。

链式组合的思想是,你取一个模型的优势,将其与另一个模型的能力结合起来,突然之间,你就获得了以前没有的各种新能力。因此,你可以取一堆CLIP没有训练数据的类别,但如果你能描述它们,因为CLIP见过很多事物的描述,它现在可以开始非常好地对它们进行分类。你可以开始让CLIP为单个花朵、单个汽车、单个空间甚至不同种类的宠物生成分类,

017:机器人学习导论

在本节课中,我们将要学习机器人学习的基础知识。机器人学习是计算机视觉、机器学习和机器人技术的交叉领域,其核心目标是让机器人能够通过感知和物理交互来理解和影响周围的世界。我们将从问题定义开始,探讨机器人感知的特殊性,并介绍几种核心的学习范式,包括强化学习、模型学习和模仿学习。最后,我们将了解当前热门的机器人基础模型及其面临的挑战。

问题定义 🎯

上一节我们介绍了课程概述,本节中我们来看看如何形式化地定义机器人学习问题。机器人学习与传统的计算机视觉任务有本质区别。计算机视觉主要关注从高维输入数据中学习环境的表示,而机器人学习本质上是一个优化问题:在物理世界的约束下,通过一系列动作来最大化或最小化某个目标函数。

一个通用的机器人学习框架可以用以下要素描述:

  • 智能体:执行任务的机器人或程序。
  • 目标:任务目标,可以是人类的语言指令或定义的奖励函数。
  • 状态:环境或机器人自身的当前状态,例如传感器读数、关节角度等。
  • 动作:智能体在环境中执行的操作,例如施加力、移动关节。
  • 奖励:环境对智能体动作的反馈,衡量任务完成的好坏。

这个框架可以形式化为一个序列决策问题,智能体在时间步 t 观察状态 s_t,执行动作 a_t,环境转移到新状态 s_{t+1} 并给出奖励 r_t。智能体的目标是学习一个策略 π,以最大化累积奖励 R = Σ γ^t * r_t,其中 γ 是折扣因子。

以下是该框架在不同领域的具体实例:

  • 倒立摆:目标是平衡杆子。状态是杆的角度、角速度、小车位置和速度。动作是施加给小车的水平力。奖励是每个时间步杆子保持直立时获得+1。
  • 机器人移动:目标是让机器人向前走。状态是所有关节的角度、位置和速度。动作是施加给每个关节的扭矩。奖励是机器人每前进一步并保持直立时获得+1。
  • 雅达利游戏:目标是获得最高分。状态是游戏屏幕的原始像素。动作是游戏控制(上、下、左、右)。奖励是每个时间步得分的增减。
  • 围棋:目标是赢得比赛。状态是当前棋盘上所有棋子的位置。动作是在棋盘上放置下一个棋子的位置。奖励是最终获胜得+1,失败得0。
  • 大语言模型:目标是预测下一个词。状态是当前句子中的词。动作是选择下一个特定的词。如果预测正确则获得奖励。
  • 聊天机器人:目标是成为人类用户的好伙伴。状态是当前的对话。动作是聊天机器人生成的下一个句子。根据人类评价,如果用户满意则获得奖励。
  • 叠衣服:目标是叠好衣服。状态是机器人从环境获得的多视角RGB或RGBD观测。动作是机器人如何移动其末端执行器以及是否开合夹爪。根据人类评估,如果衣服叠好则获得奖励。

机器人感知 👁️

上一节我们定义了机器人学习的问题框架,本节中我们来看看机器人如何感知世界。机器人感知的目标是从高维、非结构化的传感器数据(如RGB图像、深度图、触觉信号)中提取对下游决策有用的结构化和知识。

机器人感知与计算机视觉有几个关键区别:

  1. 具身性:机器人拥有物理身体,其动作会直接影响自身的感知。例如,抓取物体时触觉传感器的反馈。
  2. 主动性:机器人是主动的感知者。它可以决定感知什么、何时感知以及如何感知。例如,移动头部去看桌子后面的东西。
  3. 情境性:机器人处于具体环境中,其感知需要与任务和决策系统紧密耦合。它不需要知道环境的完整状态,只需关注与当前任务相关的区域。例如,扣衬衫时只需关注纽扣附近的局部区域。

此外,机器人面临的环境更加复杂和动态:

  • 观测不完整:存在遮挡、传感器误差。
  • 动作不完美:抓取可能失败,物体会掉落。
  • 环境动态:包含刚体、可变形物体(如衣服、绳索)、颗粒介质以及其他智能体(如人、宠物)。

因此,机器人系统通常融合多种传感器(视觉、触觉、听觉、深度),让它们互补以提供更鲁棒的感知。

强化学习 🤖

上一节我们探讨了机器人如何感知,本节中我们来看看机器人如何通过试错来学习行动,即强化学习。强化学习的核心思想是让智能体通过与环境的广泛交互,收集经验数据,通过试错来学习哪些动作能带来更高的奖励,从而调整其行为策略。

强化学习与监督学习有几个重要区别:

  1. 环境随机性:环境可能是随机的,相同的动作可能导致不同的状态转移和奖励。
  2. 信用分配:奖励可能是延迟的,需要将最终的成功或失败归因到之前的一系列动作上。
  3. 环境不可微:环境动态通常不可微分,无法直接通过反向传播计算梯度,有时需要依赖大量采样进行零阶估计。
  4. 非平稳性:环境的状态演变是智能体自身动作的结果,数据分布会随着策略改变而变化。

一个经典的强化学习算法是Q学习。Q函数 Q(s, a) 衡量在状态 s 下执行动作 a 后,所能获得的期望累积折扣奖励。智能体通过与环境交互来学习Q函数,然后选择能最大化Q值的动作。Q函数通常用神经网络(如卷积神经网络)来近似。

以下是强化学习取得显著成功的领域:

  • 游戏:例如DeepMind的DQN智能体玩雅达利游戏《打砖块》,通过训练发现了人类玩家未知的高效策略(在墙边挖隧道)。AlphaGo及其后续版本在围棋上超越了人类顶尖选手。
  • 机器人移动:通过在仿真中大量随机化物理参数(摩擦、几何等)并训练策略,可以实现非常鲁棒的仿真到现实迁移,让机器人在雪地、冰面等复杂地形中行走。
  • 灵巧操作:例如OpenAI使用强化学习训练机械手解魔方。虽然成功率初期较低,但展示了在仿真中训练并迁移到现实进行灵巧操作的潜力。

然而,纯粹的无模型强化学习也存在瓶颈:

  • 样本效率低:需要海量的环境交互,在现实世界中成本高、不安全。
  • 安全性差:训练过程中会产生许多怪异、危险的行为。
  • 可解释性差:策略难以理解和修正。

模型学习与基于模型的规划 🧠

上一节我们讨论了强化学习及其局限性,本节中我们来看看如何让机器人像人类一样,通过学习环境模型来进行想象和规划。基于模型的方法的核心思想是:从机器人与环境的物理交互中学习一个前向动力学模型,然后用这个模型进行规划,找到能达到目标状态的动作序列。

具体流程如下:

  1. 学习前向模型:模型 f_θ 学习预测给定当前状态 s_t 和动作 a_t 时,下一个状态 s_{t+1} 的变化。即 s_{t+1} ≈ f_θ(s_t, a_t)
  2. 轨迹优化:给定当前状态(蓝点)和目标状态(红点),优化一系列动作,使得通过前向模型预测出的状态轨迹(绿点)尽可能接近目标。这通常通过梯度下降等优化方法实现。
  3. 滚动执行:由于模型不完美,通常只执行优化得到的第一动作,然后从真实环境中获取新状态,并基于新状态重新规划。

模型表示的选择至关重要:

  • 像素动力学:以2D图像为状态,预测动作后的图像变化。可用于推动物体等任务。
  • 关键点动力学:以物体上的3D关键点为状态,预测其运动。可用于更精确的物体操控。
  • 粒子动力学:用一组粒子表示物体(如一堆颗粒、面团),预测粒子的运动。这种方法非常灵活,能处理可变形物体。

一个成功的例子是“包饺子机器人”。该系统使用粒子表示面团和工具的形状,学习了一个前向动力学模型。机器人根据当前面团形状的观测,利用该模型进行规划,决定使用哪种工具以及执行什么动作,最终能将面团做成饺子皮并包好馅料。该系统还能在人类不断干扰的情况下鲁棒地恢复并完成任务。

基于模型的方法的优点是:

  • 样本效率更高:模型可以从相对较少的数据中学习,并用于规划多种任务。
  • 可进行离线规划:在行动前进行“想象”,减少危险尝试。
  • 更易解释:模型本身提供了对环境动态的理解。

模仿学习 👥

上一节我们介绍了基于模型的方法,本节中我们来看看另一种高效的学习范式:模仿学习。模仿学习的核心思想是:通过提供大量展示任务如何完成的示范数据,让机器人直接学习一个映射从观测到动作的策略,而无需明确指定奖励函数。

最简单的模仿学习方法是行为克隆,它本质上是一个监督学习问题:学习一个策略 π_θ,使得在示范状态 s 下,其预测的动作 a 与专家动作 a* 尽可能接近。损失函数可以定义为均方误差:L(θ) = Σ ||π_θ(s) - a*||^2

然而,行为克隆存在级联错误问题:由于策略在执行时会产生微小的误差,导致进入训练数据分布之外的状态,进而产生更大的误差,最终可能严重偏离成功轨迹。

因此,实用的模仿学习通常是一个迭代过程:

  1. 收集专家示范数据。
  2. 用行为克隆训练初始策略。
  3. 在真实环境中运行策略,收集失败案例。
  4. 针对失败案例,收集额外的纠正数据或让专家提供干预。
  5. 将新数据加入训练集,重新训练策略。重复此过程。

另一种思路是逆强化学习:从专家示范中推断出隐含的奖励函数,然后利用这个奖励函数进行标准的强化学习。这结合了示范的效率和强化学习的探索能力。

近年来,模仿学习得益于生成模型的发展而变得更强大:

  • 基于能量的模型:学习一个能量函数 E(s, a),其值在专家数据上较低。策略通过最小化能量进行推断,能处理多模态的示范。
  • 扩散策略:将扩散模型用作策略函数类别。策略不是直接输出动作,而是通过一个去噪过程来生成动作序列。这种方法在多种灵巧操作任务上取得了成功,如涂抹黄油、削土豆皮等。

模仿学习是目前在现实世界中获得可用策略最高效的方法之一,可以在较短时间内(例如半天)收集数据、训练并部署一个能完成特定任务的策略。

机器人基础模型 🚀

上一节我们介绍了模仿学习,本节中我们来看看当前推动机器人学习领域快速发展的前沿:机器人基础模型。机器人基础模型是一种旨在获得广泛泛化能力的策略模型。它类似于大语言模型或视觉-语言模型,但输出是机器人的动作。

其核心目标是:给定当前观测(如多视角图像)和任务描述(如语言指令),模型能生成合理、平滑且符合指令的动作序列,并在大量未见过的场景和任务中都能有效工作。

这类模型也被称为视觉-语言-动作模型或大行为模型。它们通常遵循一个类似的构建流程:

  1. 大规模数据收集:聚合来自不同机器人平台、执行多种任务的海量机器人交互数据。
  2. 预训练:通常从一个预训练的视觉-语言模型开始,以保留其语义知识。然后在机器人数据上对模型进行协同微调,同时优化动作预测损失和视觉问答等任务的损失。
  3. 后训练:对于特定任务,收集任务相关的数据对基础模型进行微调,以提升在该任务上的性能。

一个著名的例子是Pi0模型。它展示了在叠衣服、叠盒子等长视野任务上的可靠性能。评估通常分为三类:

  • 已知任务:基础模型在预训练阶段见过的任务上表现良好。
  • 领域内新任务:与预训练任务相似但更复杂,通过后训练可以提升性能。
  • 领域外新任务:完全新的任务,必须通过任务特定的后训练才能获得满意性能。

然而,该领域也面临挑战:

  • 评估困难:泛化能力难以量化,真实世界评估成本高、噪声大。
  • 效率问题:由于示范数据通常通过遥操作收集,速度较慢,导致学到的策略也较慢。
  • 规模化:单个大策略能否处理家庭中所有复杂、多样的任务仍需验证,可能需要更高层的抽象或符号规划。

挑战与未来方向 🔮

上一节我们了解了前沿的机器人基础模型,在本节课的最后,我们总结一下机器人学习领域仍然面临的主要挑战和未来方向。

  1. 评估基准:当前评估主要依赖昂贵、嘈杂的真实世界测试。仿真评估则受限于仿真到现实的差距、资产创建难度和世界数字化问题。社区亟需一个像ImageNet之于计算机视觉那样的、能可靠衡量进展的基准平台。
  2. 训练目标与最终性能脱钩:策略的训练损失(如单步动作预测误差)往往不能准确反映其在长视野任务中的真实成功率。需要建立更好的代理指标。
  3. 从数据中学习世界模型:目前收集的大规模机器人交互数据主要用于训练策略,其中蕴含的丰富动力学知识未被充分挖掘。未来方向之一是利用这些数据训练基础世界模型,它们可以与策略模型协同,提升规划能力和样本效率。
  4. 构建真正通用的机器人基础模型:最终目标是开发出能在我们周围非结构化环境中广泛、稳健工作的机器人基础模型。这需要算法、数据、仿真和硬件等多方面的共同进步。

总结 📝

本节课中我们一起学习了机器人学习的核心内容。我们从问题定义开始,理解了机器人学习作为一个序列决策优化问题的特殊性。接着,我们探讨了机器人感知与计算机视觉的区别,强调了其具身性、主动性和情境性。然后,我们深入介绍了三种核心学习范式:强化学习通过试错优化策略,但样本效率低;模型学习与规划让机器人学会预测环境变化并进行内部模拟,样本效率更高;模仿学习则直接从专家示范中学习策略,是当前最高效的实践方法之一。最后,我们展望了机器人基础模型这一前沿方向及其面临的评估、效率与泛化等挑战。机器人学习是一个充满活力且快速发展的领域,正致力于让机器人在复杂的物理世界中为我们提供切实的帮助。

018:以人为本的人工智能

在本节课中,我们将探讨人工智能发展历程中一个至关重要的视角——以人为本。我们将回顾计算机视觉如何从模仿人类视觉能力,发展到超越人类局限,并最终致力于实现人类所期望的价值。我们将看到,人工智能不仅是技术演进的产物,更应成为服务于人类、增强人类能力的工具。

第一部分:构建能看见人类所见之物的AI

上一节我们介绍了课程背景,本节中我们来看看人工智能发展的第一个阶段:模仿人类视觉能力。

人类视觉系统非常强大。半个世纪前的实验表明,即使以每秒10帧的速度播放从未看过的视频,人类也能轻松识别出复杂场景中的目标。神经科学研究进一步证实,人类能在刺激出现后150毫秒内完成复杂的物体分类,并且大脑中存在专门处理物体(如面孔、地点、身体部位)的区域。这激发了计算机视觉领域的早期目标:让机器也能识别图像中的物体。

然而,对于机器而言,这是一个极其困难的任务。数学上存在无限的可能性,因为光照、纹理、背景、遮挡和视角等因素都会变化。

在深度学习兴起之前,物体识别经历了两个主要阶段:

  • 第一阶段(70-90年代):受心理学启发,尝试使用预定义的几何部件来组合识别物体。这些模型在数学上优美但未能成功。
  • 第二阶段(21世纪初):统计机器学习兴起。人们认识到,要获得泛化能力,需要从数据中学习参数,而非手动设计模型。这一时期出现了随机场、贝叶斯网络、支持向量机等模型,并在少量物体类别的基准测试上取得了进展。

真正的突破源于对认知科学的再次借鉴。心理学家艾尔·比尔曼推测,人类儿童能识别约3万到10万个视觉类别。这个“比尔曼数字”揭示了当时计算机视觉数据集规模的不足。

因此,ImageNet项目应运而生,它构建了一个包含约2.2万个类别、1500万张图像的数据集。大数据与卷积神经网络等强大算法的结合,最终在2012年迎来了深度学习的“诞生时刻”,基本解决了通用物体识别问题。

但视觉智能不止于给物体贴标签。认知科学家杰里米·沃尔夫指出,理解物体间的关系至关重要。受此启发,计算机视觉开始研究场景图,用节点表示物体,用边表示关系。Visual Genome数据集和场景图表示方法使得模型能够进行“零样本”学习,例如理解“马戴帽子”这种不常见的关系。

更进一步,视觉智能需要讲述故事。2014年左右,结合卷积神经网络和LSTM语言模型的工作开启了图像描述和密集描述生成的先河,为今天的多模态大模型奠定了基础。

动态场景理解则更具挑战性。理解视频中多个参与者的活动及其相互关系,对于未来机器人融入日常生活至关重要,目前这仍是一个未完全解决的问题。

此外,计算机视觉领域在3D视觉、人体姿态估计和生成式AI等方面也发展迅速。

本部分的要点是:数据、算力和神经网络算法在大约十年前汇聚,催生了现代AI革命。而这一历程始终深受认知科学、心理学和神经科学的启发,这种紧密联系未来仍将继续。

第二部分:构建能看见人类所未见之物的AI

上一节我们回顾了AI模仿人类视觉的历程,本节中我们来看看AI如何超越人类视觉的局限,或弥补人类的不足。

首先,AI可以追求“超人类”的精细识别能力。例如,细粒度物体分类(如识别成千上万的鸟类或汽车型号)是人类的弱项。早期工作表明,在细粒度级别上,算法的错误率仍然很高。有趣的是,这种能力可以成为研究社会的透镜。例如,通过街景图像识别汽车型号,可以分析其与教育水平、收入、投票模式等的相关性,这是任何个人或群体都难以手动完成的。

其次,人类视觉本身存在局限。例如,斯特鲁普测试揭示了阅读单词颜色时的认知冲突;变化盲视实验表明我们难以察觉场景中的显著变化。这些注意力局限在某些领域可能造成严重后果,例如医疗错误是美国医疗系统中第三大致死原因。

因此,AI可以辅助人类克服这些局限。例如,在手术室中,传统依靠人工清点器械,效率低且有风险。演示表明,AI有潜力辅助进行物品追踪,以提升安全性和效率。

再者,人类的视觉认知存在由进化或社会经验造成的偏差。例如著名的棋盘阴影错觉,揭示了我们对光照和阴影的固有假设。更严重的是,算法可能放大社会偏见,如几年前的人脸识别算法在不同肤色和性别上的性能差异。值得欣慰的是,到2025年,AI偏见问题已受到学术界和工业界的广泛关注。

最后,有时“看不见”恰恰是出于对隐私的尊重。如何在利用AI提供帮助的同时保护隐私,是一个技术和人文交织的难题。

以下是几种可能的机器学习隐私保护技术方案:

  • 模糊或掩码
  • 降维
  • 联邦学习
  • 加密

一项结合硬件和软件的工作令人印象深刻:通过特殊设计的镜头过滤视觉数据,在保护人脸和身体隐私的同时,仍能解析出人物的活动信息,实现了“看见所需,保护隐私”的平衡。

本部分的要点是:AI是一把双刃剑,既能帮助和增强人类,也可能放大人类的偏见和问题。因此,在构建AI时,必须秉持以人为本的视角,研究、预测并引导其社会影响,尊重人类价值。

第三部分:构建能看见人类所愿所见之物的AI

上一节我们探讨了AI如何看见人类所未见,本节我们将更进一步,连接“看见”与“行动”,探讨AI如何实现人类所期望的价值。

当前社会对AI的一大焦虑是劳动力替代。历史表明,技术变革总会带来劳动力市场变化,有些过程是痛苦的。但我们也应看到,在许多领域,如老年护理和医疗保健,我们正面临劳动力短缺。AI的角色不应仅仅是“替代”,更应是“增强”。

在医疗保健的诸多“黑暗空间”(如手术室、病房、药房、家庭)中,缺乏足够的“眼睛”进行监测。环境智能医疗旨在结合智能传感器和机器学习算法,从中提取关键健康信息并及时预警。

以下是几个应用示例:

  • 手部卫生项目:医院感染是导致患者死亡的主要原因之一。传统人工审计效率低。通过深度传感器和视觉算法监测洗手动作,AI的检测效果比人类更准确、更一致。
  • ICU患者活动监测:帮助患者正确活动对康复至关重要。在ICU部署智能传感器,辅助医生监测患者下床、上床等活动,在劳动力短缺的情况下尤为宝贵。
  • 居家养老:通过智能传感器(如热成像相机)帮助监测老年人的感染早期迹象、活动能力、睡眠和饮食模式,助力老年人健康独立生活。

然而,传感器只能收集信息,无法提供物理帮助。这就引向了具身AI——机器人技术,它能够闭合感知与行动的循环。但当前机器人仍面临速度慢、适应性差、任务范围有限等挑战。

我们的研究尝试让机器人能响应开放指令。通过利用大语言模型和视觉语言模型,将自然语言指令转化为代码和环境理解,再生成运动规划图,使机械臂能在非预设环境中执行如“打开抽屉但避开花瓶”这类任务。

为了推动机器人学习,我们需要像自然语言和计算机视觉领域那样,建立大规模、多样化的基准测试数据集。因此,我们启动了“行为”项目,旨在建立一个生态化的机器人学习环境与基准。

关键问题是:机器人应该学习哪些任务?我们通过以人为本的调查,询问人们希望机器人提供帮助的任务,并据此对数千项日常活动进行排序和筛选,确保机器人学习的任务是人们真正需要的。

我们扫描了50个不同的真实环境,获取了超过1万个具有物理属性的3D物体资产,并与NVIDIA Omniverse合作构建了高保真、支持物理交互的仿真环境。在这个环境中对当前机器人算法进行测试,发现在不给特权信息的情况下,其完成“行为”任务的能力几乎为零,这说明该领域仍有巨大发展空间。

此外,我们也在利用该环境研究视障患者,并与心理学家和医生合作,探索如何利用脑电波非侵入式地控制机器人。演示中,学生通过EEG帽发送指令,控制机械臂完成了一餐日式料理的烹饪,这为帮助严重瘫痪患者带来了希望。

“行为”项目旨在增强人类能力,它是一个大规模、多样化、具有真实生态物理和感知的基准。本部分的最终要点是:我们不仅希望构建能“看见”或“做事”的AI,更希望构建能“帮助人”的AI。将AI作为人类的增强工具,而非替代工具,这一点至关重要。

总结

本节课中我们一起学习了人工智能发展的三个层次:从模仿人类视觉,到超越人类局限并辅助人类,最终致力于实现人类期望的价值。我们看到了技术演进与认知科学的深刻联系,认识到在开发强大AI时必须同时考虑其社会影响和人文关怀。未来,以人为本、增强人类能力的AI将继续是研究和应用的重要方向。

posted @ 2026-03-26 13:17  布客飞龙V  阅读(1)  评论(0)    收藏  举报