nptel-dlcv-merge-3
NPTEL 深度学习计算机视觉笔记(四)
分类、定位和检测之间的区别如下:
-
分类:给定一张包含猫的图像,目标是判断图像中有一只猫。
-
定位:除了判断有一只猫,还需要用一个边界框框出猫在图像中的位置。
-
目标检测:更进一步,检测给定图像中属于你类别集合的所有可能出现的物体,并对它们进行定位。
在定位任务中,你只定位一个物体;而在目标检测中,可能有多个物体,甚至是同一物体的多个实例。例如,图像中可能有一只猫和一只狗,或者两只狗和一只猫。目标检测的目标是识别出每一个物体,并使用边界框对它们进行定位。
最后是分割任务,其工作是将每个像素标记为属于C个类别中的某一个(C取决于我们拥有的类别数量)。因此,我们将在本次讲座中从检测开始,然后在本周晚些时候再讨论分割以及其他任务。
深度学习时代前的目标检测方法
在深度学习出现之前,还有其他方法被用于在各种设备上部署计算机视觉进行目标检测。因此,即使在15年前深度学习方法尚未成熟时,目标检测和人脸检测就已经出现在例如傻瓜相机中。这些方法大多使用了Viola-Jones算法的变体。
Viola-Jones算法
该算法由Viola和Jones开发,因此得名,其工作于2001年的CVPR上发表。这是一个用于实时执行目标检测的框架,最初主要用于检测正面人脸,尽管后来也被调整用于检测其他类型的物体,甚至检测物体的部分(例如人脸中的眼睛)。
这项工作的主要贡献包括:
-
使用所谓的Haar-like特征进行弱分类。
-
引入了积分图像这一非常强大的概念,可以显著加快Haar-like特征的计算速度。
-
使用AdaBoost作为分类器,但其使用方式与传统机器学习中的AdaBoost略有不同。
-
使用了级联分类器。
Haar-like特征
Haar特征是基于Haar小波的矩形特征。它们看起来像这样:通常有一个黑色区域和一个白色区域,但可以有不同组合。你可以有水平或垂直排列的黑白两区域,也可以有白色夹着黑色(水平或垂直),或者棋盘格图案等等。
Haar滤波器的核心思想是:特征值等于黑色矩形内像素和减去白色矩形内像素和。记住这是卷积操作,所以滤波器会翻转。因此,与Haar滤波器卷积的输出将是黑色矩形内的像素和减去白色矩形内的像素和。为了适应不同尺度的图像,这些特征可以通过高度、宽度甚至不同的模式进行缩放。
使用Haar特征进行目标检测(特别是人脸检测)的直觉是,人脸的许多特征都涉及特定区域与相邻区域之间的对比度。例如,观察眼部区域,其下方可能是一个黑色区域,上方则是一个相对明亮的区域。像这样的Haar特征就能捕捉到这种差异。
积分图像
这项工作还引入了积分图像的概念。这是一个基本概念,不一定只与人脸检测相关,它是一种加快计算速度的方法,尤其是在使用Haar-like特征时。
积分图像的定义是:对于积分图像中的任意像素(x, y),其值是从左上角原点开始,到该点为止的所有像素值的总和。
数学上可以写为:
integral_image(x, y) = Σ_{x'≤x, y'≤y} I(x', y')
给定一个图像,其积分图像看起来像这样。例如,一个2x2窗口的值290是由38+66+89+97得到的。
积分图像的递推定义如下:
s(x, y) = s(x, y-1) + I(x, y)
integral_image(x, y) = s(x, y) + integral_image(x-1, y)
其中s(x, y)是累积行和。为了使这个递推定义成立,需要定义s(x, -1) = 0 和 integral_image(-1, y) = 0。
现在让我们看看积分图像如何用于Haar滤波器。假设我们想计算由点1、2、3、4定义的矩形内的像素强度和。设A为左上角小矩形,B为包含A的较大矩形,C为包含A右侧部分的矩形,D为整个大矩形。那么矩形1-2-3-4内的像素和可以表示为:D + A - B - C。
更正式地说,一个左上角为(x1, y1),右下角为(x2, y2)的矩形内像素和可以表示为:
integral_image(x2, y2) + integral_image(x1-1, y1-1) - integral_image(x1-1, y2) - integral_image(x2, y1-1)
这非常有用。当你想使用像Haar滤波器这样的卷积时,如果你有一个两矩形特征(即一个黑色区域接一个白色区域),计算这样一个滤波器的卷积输出只需要在积分图像中进行6次查找。你只需要查找所有角点的值(包括中间的点),就可以计算出Haar滤波器的输出,而无需进行任何实际的卷积操作。
练习:思考一下,对于一个三矩形特征(例如,中间是黑色,两边是白色)需要多少次查找?对于一个四矩形特征(例如棋盘格)又需要多少次查找?
使用AdaBoost构建强分类器
一旦你有了这些Haar滤波器的输出,Viola-Jones算法建议每个特征都可以被视为一个弱分类器。
给定一个特征f_j(可以是两矩形、三矩形、四矩形特征等),一个阈值θ_j和一个表示符号的奇偶性p_j,弱分类器h_j定义为:
h_j(x) = 1 if p_j * f_j(x) < p_j * θ_j
现在,使用这个定义,每个Haar特征都会得到一个分类器。我们知道弱分类器的组合可以产生强分类器。但到目前为止,你看出这个方法有什么问题吗?
仔细想想,即使你有一个简单的24x24滑动窗口,在这个窗口内计算各种Haar特征并使用阈值判断是否有人脸,问题在于,仅在一个24x24的窗口内,就有超过160,000种可能的特征。这意味着在每个滑动窗口内计算160,000个弱分类器是非常繁琐的。我们该怎么办?这时AdaBoost就派上用场了。
Viola-Jones算法中使用AdaBoost的方式如下(这是论文中的算法,我们只关注其关键部分):
-
给定示例图像
(x1, y1) ... (xn, yn),初始时对所有训练样本赋予均匀权重并进行归一化。 -
对于每个特征
j(即所有可能组合中的一个Haar特征),训练一个分类器h_j(一个弱分类器,仅限于使用单个特征)。 -
该弱分类器的误差
ε_j由权重乘以分类器输出与期望输出的差异给出。 -
Viola-Jones方法的关键在于:在所有可能的特征(它们会给出许多不同的分类器)中,选择误差
ε_t最小的那个分类器h_t。 -
基于这个分类器的误差,重新加权所有样本(这是AdaBoost中标准的重新加权步骤)。
-
重复此过程多次。经过一定次数的迭代后,最终的强分类器是
T个特征的加权组合(T是一个可设定的参数)。在每次迭代中,你挑选一个最强的特征,最终只使用T个不同特征的加权组合。
级联分类器
Viola-Jones算法的第三个组成部分是级联分类器。这个想法源于一个动机:图像中实际上包含物体或人脸的子窗口非常少。一个给定的图像可能有数十万个子窗口,但其中不会有很多人脸。因此,如果你评估每一个可能的窗口,误报率可能会非常高。
级联分类器的思想是:尽早排除负样本窗口,从而减少在它们身上花费的计算时间。如果一个子窗口是负样本(不包含目标),就忽略它,并且不再对该子窗口内的任何进一步子窗口进行处理。
通俗地说,所有可能的子窗口首先进入第一阶段,排除其中一部分,假设它们绝对不包含人脸。只有那些可能包含人脸的窗口进入分类器的下一阶段,再次进行检查。如果在该阶段有子窗口被拒绝,它们也会被排除。只有剩余的子窗口继续进入后续阶段,依此类推。在每个阶段,你都可以使用一组弱分类器(可以通过参数T来约束数量)。通过这种方式,你可以使用不同阶段的分类器来逐步优化性能。
为什么级联分类器很重要?我们刚刚提到,对于像人脸检测或目标检测这样的问题,误报率非常重要。如果你有一张100万像素的图像,你至少有大约100万个候选的人脸位置。理想的误报率需要低于10^-6。
如果我们只使用之前提到的基本Haar滤波器组合,可能会得到100%的检测率,但误报率高达50%。这会产生大量可能的人脸,但并非所有都是真正的人脸。
如果我们进一步包含200种特征(即200种不同排列的Haar滤波器),可能会得到95%的检测率,误报率约为1/14000。这虽然更好,但距离我们想要的10^-6误报率还不够。
这时级联分类器就来拯救我们了。记住,当你有一个级联时,最终的检测率和误报率是通过每个阶段的检测率和误报率相乘得到的。因此,如果每个阶段都是一个200特征的分类器(即T=200),每个阶段的检测率约为0.99,误报率为0.3,那么经过10个阶段的级联,总误报率就是0.310,大约在10-6的数量级。级联分类器的思想允许我们在级联的每个阶段拥有合理的检测率(0.99)和误报率(0.3),正如我们所见,这只需几个特征就可以实现。我们只需在级联的各个阶段不断优化,就能获得理想的误报率。
非极大值抑制
该方法的另一个方面(实际上也是任何检测方法)是非极大值抑制的概念。很可能一个物体周围的许多窗口都被分类为包含该物体。那么如何选择哪一个才是正确的物体呢?
我们必须使用某种边界框相似性度量,最流行的是交并比。交并比的定义是:如果你有两个边界框B1和B2,取它们的交集和并集,交集与并集的比值可以衡量这两个边界框的接近程度。如果比值为1,意味着B1和B2完全相同;比值越接近1,说明边界框重叠得越多。
我们如何使用IOU进行非极大值抑制呢?如果你有一组可能包含物体的边界框,你可以从中随机选择一个框,然后将这个框与其余框进行比较。如果IOU大于0.5,就从列表中移除那个框。这样,你确保了在多个重叠度超过50%的框中只保留一个。稍后我们会看到,我们不会仅仅这样做,还会尝试找出这些框中哪个框对包含物体的置信度最高。这个过程被称为非极大值抑制,如今在检测中被广泛使用。
以上就是用于人脸检测的Viola-Jones算法,它非常流行,被用于多种技术中。
方向梯度直方图
当时另一个流行的方法是方向梯度直方图。我们在讨论从图像中提取手工特征时已经见过这个方法,但现在我们将讨论它如何用于行人检测任务。
如果你有一张包含行人的图像,你有一个检测窗口在图像上滑动,并获取所有梯度(可以是Sobel滤波器等)。然后,你将整个图像划分为单元格,在每个单元格中绘制梯度方向的直方图(我们之前在SIFT中做过类似的事情)。一旦你在每个单元格中得到了方向梯度直方图,接着考虑重叠的2x2单元格块。你取这4个块的直方图,对它们进行归一化,并将其定义为该中心像素的最终直方图。当你对图像中的所有单元格都这样做时,你会得到一个最终的描述符,它看起来像是在图像不同位置的一组方向梯度直方图。
如何使用这个描述符呢?得到这个最终描述符后,你训练一个支持向量机来判断这是行人还是非行人。这项工作由Dalal和Triggs于2005年提出。
你还可以可视化支持向量的权重。如果你在所有训练样本上取平均梯度图像,你可以可视化正负SVM权重的样子。给定一张测试图像及其对应的HOG描述符,你可以将这些描述符乘以正负权重,得到类似的结果。你会注意到,观察正权重,SVM很容易将其分类为图像中包含行人。
这种方法还被扩展到多分辨率级别,即使用图像金字塔,在金字塔的每一层执行相同的方法。例如,在最低分辨率下,你有一个特定的块,并使用SVM为该窗口(在最低分辨率图像中)计算一个分数,判断行人是否在那里。你同样对最高分辨率图像进行此操作,然后可以使用各种技术来整合这些分数,例如像金字塔匹配那样给最高分辨率最高分、最低分辨率最低分,或者进行投票,或使用其他启发式方法组合这些分数,以判断图像中是否有人。
以上就是在深度学习出现之前用于检测的不同方法。
使用CNN进行目标检测的初始步骤
现在,让我们尝试看看如何以最简单的方式使用CNN进行目标检测。
滑动窗口CNN方法
假设在训练时你有汽车的图像,你会训练一个CNN来学习汽车分类器,使用标准的交叉熵损失和一个简单的CNN。
在测试时,当你得到一张汽车可能位于图像中任何位置的图像时,你取不同尺度的多个滑动窗口。你可能需要从这张图像中提取多达10万个潜在的滑动窗口,并将每个窗口作为输入提供给CNN(假设它们大小相同,或者调整到相同大小)。然后,CNN将原始图像中的每个窗口分类为包含汽车或不包含汽车。基于此,你会在这里和那里检测到汽车。
同样,在这种方法中,你可以加入非极大值抑制来提高性能。但正如之前所说,一旦你得到所有可能包含汽车的边界框列表,你考虑类别置信度最高的边界框。例如,红色框可能是置信度最高的框(0.93)。然后,你计算所有其他边界框与这个红色边界框的IOU。如果其中任何一个与红色框有很高的重叠或IOU,你就消除它们,只保留红色框,因为它对包含汽车的置信度最高。
这种方法存在的问题
这是使CNN适应目标检测的最简单方法。你看出有什么问题吗?实际上有几个问题:
-
边界框可能没有紧密地包围物体。你在这里可以看到,有些边界框相当松散。我们理想情况下需要一个紧密的边界框,以便精确地聚焦于包含物体的那部分图像。
-
另一个明显的问题是,你必须通过CNN评估成千上万个滑动窗口,才能找出哪些窗口包含你选择的物体。
我们将讨论克服这些问题的方法。但在那之前,我们先谈谈目标定位,正如我们所说,它是检测的前身。
目标定位
目标定位可以通过以下方式实现:如果图像中只有一个物体,你可以将其输入CNN,得到特征图,然后通过全连接网络。现在,你可以将CNN分支为两个部分:
-
一个部分给出分类分数,可以使用交叉熵损失学习。
-
另一个部分进行边界框回归。你可以尝试找出边界框的确切坐标(例如相对于整个图像或图像中任何其他框的偏移量)。你也将其作为学习过程的一部分来学习。
因此,你网络末端的全连接层将有两个“头”:一个分类头和一个回归头。分类头需要交叉熵损失。回归头将学习边界框的左上角坐标(x, y)、高度(h)和宽度(w),共四个值。这些值可以使用L2损失或均方误差损失来学习。因此,整个网络将通过这两个损失之和来训练,不仅对图像进行分类,还要对图像进行定位。
OverFeat:早期成功的定位器
CNN中一个早期成功的优秀定位器是OverFeat。OverFeat是ILSVRC(ImageNet大规模视觉识别挑战赛)定位挑战赛的年度冠军。想象一下,除了分类挑战赛,ILSVRC每年还有一个定位挑战赛。OverFeat是2013年该挑战赛的冠军。
OverFeat实现定位的方式是:确保你的整个网络都是卷积的,而不是包含全连接层。这通过直接将滤波器应用于图像来避免计算子窗口的时间。让我们看看如何实现。
给定一个输入(例如14x14,或者填充为16x16),你执行5x5卷积(这只是一个可视化示例)。如果不进行填充,你会得到一个10x10的输出;如果进行2像素填充,则变成12x12。然后进行2x2池化,得到5x5。再进行5x5卷积,得到1x1。现在,使用多个1x1卷积,你可以直接得到输出,而无需执行任何全连接层操作。
因此,OverFeat的这个想法显著加快了计算速度。正如你现在看到的,它用全卷积层取代了全连接层,最后通过1x1卷积得到最终输出(类似于我们在Inception Net/GoogleNet中看到的那样)。这为什么有用?因为我们记得全连接层使用了大量参数,而使用全卷积方法克服了这一点,从而可以加快性能。

总结
在本节课中,我们一起学习了目标检测的基础知识。我们从区分分类、定位和检测任务开始,回顾了深度学习时代之前的重要方法,特别是Viola-Jones算法(利用Haar-like特征、积分图像、AdaBoost和级联分类器)和方向梯度直方图。然后,我们探讨了将CNN应用于目标检测的最初尝试,即滑动窗口方法,并指出了其效率低下的问题。最后,我们介绍了目标定位的概念以及OverFeat如何通过全卷积网络高效地实现定位,为后续更高效、更精确的现代目标检测方法(如R-CNN系列、YOLO、SSD等)奠定了基础。
100:用于目标检测的CNN - 单阶段模型 🎯

概述
在本节课中,我们将继续学习目标检测的密集采样方法。我们将重点介绍几种经典的单阶段检测模型,包括YOLO、SSD、FPN和RetinaNet,并解释它们的核心思想、架构和损失函数。
从平滑L1损失谈起
上一节我们介绍了区域提议方法,本节中我们来看看密集采样方法。在开始之前,我们先回顾一下上节课留下的思考题:为什么平滑L1损失比L2损失对异常值更不敏感?
当预测输出与真实值偏差很大时(即存在异常值),平方损失或L2损失会放大这种偏差。这也会导致梯度被放大,可能引发梯度爆炸问题。L1损失可以缓解这个问题。但由于L1损失在零点不可导,我们使用其平滑版本,即平滑L1损失,它能减轻异常值带来的问题。
单阶段检测框架简介
当代方法大致可分为区域提议方法和密集采样方法。我们现在将进入密集采样方法,即单阶段检测框架。
其中最流行且至今仍在使用的模型是YOLO(You Only Look Once)。YOLO的第一个版本于2016年在CVPR上发表。它是一个单阶段检测器。可以说,所有这些单阶段检测器都大致基于OverFeat的思想,它们都只使用卷积层,没有全连接层,其主要目标是速度与良好性能。
其高级流程是:调整图像大小,运行卷积网络,得到一组输出,然后进行非极大值抑制以确定最终的边界框。
YOLO v1 详解
以下是YOLO v1的整体流程。给定一张输入图像,首先将其划分为一个S×S的网格。每个网格单元负责预测与其中心重合的物体的边界框。
例如,如果取这个特定的网格单元,它将负责预测这只狗的边界框。
每个网格单元预测B个边界框(B是另一个超参数)以及每个框的置信度分数。因此,每个网格单元可以预测多个边界框及其置信度。
每个网格单元仅为每个类别预测一个概率。对于总共C个类别,这意味着C个总概率。可以将其视为计算条件概率:P(Class_i | Object)。
最终的预测可以编码为:你有一个S×S的网格。在每个网格位置,你预测B个边界框。对于每个边界框,你有五个值:表示物体中心的坐标(x, y)、宽度(w)、高度(h)以及该边界框的置信度,总共5个值。然后,每个网格单元有C个类别概率。因此,在应用非极大值抑制之前,输出层的总输出数为:S × S × (B×5 + C)。
每个边界框给出四个坐标:x, y, w, h。x, y是表示物体中心相对于网格单元的坐标,w, h是物体相对于整个图像的宽度和高度。
为每个边界框给出的置信度分数反映了模型对该边界框包含物体的置信程度,以及框的准确性。可以将置信度视为:P(Object) × IOU(ground truth, predicted)。因此,置信度同时考虑了这两个量。
每个网格单元有每个类别的条件类别概率,无论每个网格单元预测的边界框数量B是多少。YOLO每个网格单元只预测C个类别概率。因此,可以假设这些C个类别概率就是这里的条件概率P(Class_i | Object)。
如果将置信度分数与类别条件概率相乘,你会得到:P(Class_i | Object) × P(Object) × IOU = P(Class_i) × IOU。因此,对于每个单元格,我们计算的是该类别出现在此网格单元的概率,以及预测框与真实框的IOU。
YOLO v1 的损失函数
用于训练YOLO的损失函数有效地结合了多个需要计算的组成部分,以确保预测的每个量都接近真实值。以下是损失函数的各个项:
-
1_{ij}^{obj}:指示单元格i中的第j个边界框预测器是否对该预测负责。
-
1_{i}^{obj}:指示物体是否出现在单元格i中。
以下是各项的作用:
-
首先确保网络预测的x_i, y_i接近期望的x̂_i, ŷ_i。
-
确保网络预测的宽度w_i和高度h_i接近原始的宽度和高度。这里取了平方根,因为宽度和高度是二维的。
-
对于置信度C_i和Ĉ_i,如果存在物体(此时期望置信度为1),则尝试最小化预测置信度与期望置信度之间的差异。
-
所有这些求和都是跨所有网格单元格以及每个网格单元格预测的所有边界框进行的。
-
即使物体不存在,也希望置信度与期望置信度(此时为0)匹配。因此,这两项分别对应正类和负类。
-
最后一项表示条件类别概率与期望条件类别概率的匹配。
这些是用于训练网络的不同损失项。
YOLO v1 的局限性
YOLO v1有一些局限性。首先,它只能检测少量物体。由于方法本身的空间限制以及可以呈现的重叠物体数量,它会漏检小物体或彼此靠近的物体。这导致了较高的定位误差和相对较低的召回率。
为了克服这些问题,YOLO v2作为YOLO v1的扩展,将锚框(anchor boxes)的概念引入了YOLO框架。这些锚框类似于你在Faster R-CNN中看到的。
YOLO v2 与锚框
每个锚框预测五个坐标:t_x, t_y, t_w, t_h, t_o。让我们看看每一个。
如果单元格距离图像左上角的偏移为(c_x, c_y),锚框具有先验宽度p_w和高度p_h,那么对应于锚框的预测如下:
-
b_x = σ(t_x) + c_x
-
b_y = σ(t_y) + c_y
-
b_w = p_w × e^{t_w}
-
b_h = p_h × e^{t_h}
-
物体概率与IOU的乘积(即YOLO v1中的量)是σ(t_o),即条件类别概率。
这些是网络预测的不同量,它们与网格位置试图指向的实际边界框相关。记住,每个锚框只预测这五个值。
YOLO 家族的演进
自v2以来,YOLO出现了多个变体。我们可能无法详细介绍每一个,但让我们快速了解几个,看看这个架构家族是如何演进的。
YOLO v3 有几个有趣的改进,例如逻辑分类器和多尺度预测。
-
逻辑分类器:使用逻辑回归来预测目标性分数以及进行最终分类。YOLO v2或v1在锚框或网格位置上使用softmax进行最终类别预测,这意味着一个特定的锚框只能有一个类别标签作为最终分配。这被修改为在每个类别标签上使用二元分类器(逻辑分类器),从而允许一个特定的锚框拥有多个类别标签(例如,“人”和“女人”可以同时为真)。
-
新主干网络:YOLO v3引入了一个新的主干网络,后来被称为Darknet-53,它包含53个带有残差连接的卷积层。
-
多尺度预测:YOLO v3模型在三个不同尺度上预测边界框,以改进对小物体的检测。这是通过使用空间金字塔池化(SPP)实现的,在主干网络中添加了一个修改后的SPP块,并连接了具有不同内核大小的多个最大池化输出以获得多尺度效果。YOLO v2每个网格单元使用5个锚框,而YOLO v3在每个尺度上使用3个锚框,但总共有三个尺度,所以总共是9个锚框。
随着YOLO v3的出现,YOLO的架构可以明确分为三个部分:
-
主干网络:从给定输入图像中提取特征的网络,通常是在大规模图像分类任务上预训练的CNN。
-
颈部网络:通常用于在不同尺度之间交换信息(包括空间和语义信息),在此阶段通常用于聚合和细化特征。
-
头部网络:执行分类以及后处理步骤(如NMS)。
随后出现了YOLO v4,它引入了一系列变化。特别是:
-
使用了称为CSPDarknet53的架构,是Darknet53的改进版,在其架构内具有跨阶段部分连接,据其创建者称,这提高了模型的计算效率。
-
认为Mish激活函数在YOLO v4模型上表现更好。
-
还使用了空间金字塔池化和称为路径聚合网络(PAN)的结构来跨尺度组合特征,并引入了空间注意力模块以更好地细化特征。
-
在训练过程中,YOLO v4引入了一种称为“免费礼包”的技术,例如马赛克数据增强,将给定图像的不同增强版本拼接起来,以提高模型在非常规上下文中检测物体的能力。
-
还使用了DropBlock代替Dropout来进行正则化。
-
其他改进包括自对抗训练(在输入图像中引入扰动以提高模型鲁棒性)和使用遗传算法进行超参数优化。
请不要担心没有理解YOLO架构的每个部分。事实上,最初的YOLO版本后来进一步扩展到2020年的YOLO v5(包括自动锚框和缩放YOLO版本),然后是YOLO v6、v7、v8、YOLO-NAS,以及最近的2024年YOLO v9和最新的YOLO v10。
为什么我们要如此详细地讨论YOLO?虽然许多目标检测模型在准确性上已经超过了YOLO,但YOLO仍然是实时目标检测的首选,尤其是在边缘设备和小型计算或内存设备上。
单次多框检测器
另一个单阶段检测方法称为单次多框检测器(SSD)。SSD再次使用了类似OverFeat的方法。给定输入图像,网络的初始部分是一个类似VGG的网络,然后是具有逐渐减小尺寸的卷积层,并且有一些跳跃连接将你从卷积层直接带到分类器。
让我们更详细地看看它们。以下是SSD的关键点:
-
用于检测的多尺度特征图:因为直接将卷积层的特征图发送到输出层,所以输出层接收来自不同尺度卷积层的特征图,这就是多尺度特征图。
-
每个卷积层连接到输出层的卷积滤波器:每个卷积层通过一组不同的卷积滤波器连接到输出层,这些滤波器决定了输出层中对应输出的数量。
如果你取任何一个特征图层,例如一个大小为m×n、具有c个通道的特征图,那么它将给出m×n个位置。这些特征图中的每个像素位置都可能是物体的中心(类似于YOLO中的网格单元),边界框偏移值相对于该网格单元位置。
每个卷积图预测(类别数 + 4)个值。这就是它们将预测的值的数量:每个类别的类别概率加上对应于这些卷积特征图中每个像素位置的边界框偏移的四个值。
因此,随着网络向后推进,特征图变得越来越小。例如,如果你有一个8×8的特征图,然后是4×4的特征图。在8×8的特征图中,如果你查看每个网格单元位置的锚框,每个锚框将预测一组值。如果你有k个具有不同宽高比的锚框,SSD将预测c个类别特定分数加上4个锚框偏移。对于一个m×n的特征图,你总共有 (c+4) × k × m × n 个输出。这是因为你假设每个像素都可以是物体的中心,每个像素周围有k个锚框,每个锚框预测c个类别概率和4个边界框偏移。
SSD 的损失函数
SSD使用的损失函数与我们目前看到的非常相似。SSD实现了一个定位损失函数和一个置信度损失函数。
定位损失再次由平滑L1损失给出。它还有一个因子x_{ijk}来表示是否涉及某个类别,因为你只想在涉及类别时评估这个量。L是预测的偏移量,Ĝ是真实偏移量。
置信度损失是类别置信度概率的softmax损失。它由两项组成:第一项针对所有正边界框(标准交叉熵损失),第二项针对负边界框,其中有一个对应于背景类别的标签,你希望最大化其概率。C_0这里对应一个称为“背景”的类别标签,被视为你想要预测的类别之一。Ĉ是你的标准softmax激活函数。
在实际实现中,YOLO和SSD都有这个问题:与Faster R-CNN相比,大多数锚框很可能是负样本。为了应对这一点,你选择具有最高置信度损失的负样本,使得负样本与正样本的比例保持在约3:1。否则,即使在一张图像中有0个物体,锚框和网格单元的数量(在SSD中是每个像素)也可能非常巨大,导致负样本数量过多,影响学习。这就是进行“难负样本挖掘”的原因:在训练时,只选择那些具有非常高置信度损失的部分框,并在我们讨论的损失函数中使用它们。
SSD还使用了一种数据增强策略:除了使用原始图像,还从图像中随机采样图像块,确保与真实物体的最小IOU在预定义的范围内,以确保网络暴露于图像中不同种类的块以及物体及其重叠的不同情况。
通过这种方法,SSD在性能上可以超越YOLO和Faster R-CNN。
评估指标:平均精度均值
所有检测方法都使用一个称为mAP(平均精度均值)的评估指标进行测量。
-
精度指的是机器学习方法中使用的标准精度指标。
-
平均精度谈论的是在所有类别上获得的平均精度。
-
均值是在所有边界框上取平均。
通常,在特定的IOU阈值下测量mAP。例如,预先指定一个IOU(如0.5),只要预测框与真实框的IOU至少为0.5,就认为预测正确。这就是定义正确性的方式,用以计算精度,然后在预测的类别和框上取平均。
可以看到,SSD在mAP上匹配了Faster R-CNN,但以显著更高的FPS(每秒帧数)实现,这也是使这些单阶段方法在实际中更快的主要目标。
特征金字塔网络
第三种单阶段检测器方法称为特征金字塔网络(FPN)。FPN基于这样的思想:来自初始层的特征图可能并不真正适合检测。当你经过卷积网络时,初始层是高分辨率的,然后随着网络加深,分辨率变得越来越低。但是,初始层虽然分辨率高,但从CNN的可视化中我们知道,它们可能并没有真正捕获那些初始特征图中的物体语义。然而,它们是分辨率较高的。因此,我们陷入了两难境地:较低分辨率的特征图具有更丰富的特征用于检测,而较高分辨率则在初始特征图中。
FPN试图做的就是弥合这一差距。以下是FPN如何尝试弥合这一差距的可视化:
-
你可以使用图像金字塔进行检测:对图像进行下采样,为每个分辨率构建特征图并预测。
-
或者,你可以获取输入图像,构建越来越低分辨率的特征图,最后在最低分辨率进行预测。
-
或者,给定输入图像,在构建许多卷积层时在不同分辨率构建特征图,并在每个分辨率进行预测。
FPN建议的是:在通过卷积网络时,在不同分辨率构建特征图,但现在进行上采样以获得更高分辨率的特征图,然后进行预测。这样,你试图在最低分辨率获得语义信息,但通过上采样将语义信息传递到更高分辨率。
让我们看看这在架构中是如何完成的。整体架构如下:你有一张图像,经过一系列卷积和池化层(类似于ResNet),得到特征图C1到C5。在FPN中,所有卷积特征图C1到C5都经过一个1×1卷积(输出256个通道)进行处理。然后,M5被上采样2倍以获得下一个M4,但在获得M4之前,将经过1×1卷积的C4的输出与上采样后的M5结合,得到M4。类似地继续得到M3和M2。完成此操作后,在M4、M3和M2上应用3×3卷积,以减少上采样可能带来的混叠效应。最后,你得到所有这些P(P2, P3, P4, P5),它们被提供给单独的目标检测器以获得最终预测。
RetinaNet 与焦点损失
一种更近期的目标检测方法侧重于用于训练这些检测器的损失函数,这就是RetinaNet。提出的损失函数称为焦点损失(Focal Loss),于2017年ICCV提出。它基于这样的直觉:两阶段检测器已知比一阶段检测器更准确,而一阶段检测器显然更快。据推测,两阶段检测器更准确是因为背景(负类)和包含物体(正类)的提议之间的类别不平衡较少。
记住,在进行选择性搜索时,我们将自己限制在仅2000个区域提议。而在一阶段检测器中,你可能要处理100,000个区域,因为在SSD中,特征图的每个像素上都有多个锚框;即使在YOLO中,你也要为每个S×S网格单元预测B个边界框,这可能是一个非常大的数量。那么,我们如何解决负类或背景类与实际正类之间的这种不平衡呢?
每当存在这样的问题,即有许多对模型没有真正帮助的负样本,而只有少数正样本时,可能会发生以下情况:
-
训练可能变得低效,因为简单的负样本并没有给出任何有用的信号。
-
损失可能被负样本淹没,而不是正样本,这可能导致训练过程退化,产生退化模型。
在一定程度上,我们在SSD中讨论过的“难负样本挖掘”(试图确保最终损失只使用那些具有显著置信度损失的样本,并确保负样本与正样本的比例为3:1)缓解了这些问题,但问题仍然存在。
因此,这篇论文提出,用于检测分类分支的交叉熵损失可能本质上是不好的。让我们看看为什么。记住,交叉熵损失在二分类中定义为 -log(p_t),在多类设置中就是负对数损失。通过他们的实证研究,他们观察到一个图表:当γ=0时,他们提出的称为焦点损失的损失将变成标准交叉熵损失。你可以看到,即使网络对真实类别预测了高概率,损失值也相当不小,即使模型预测正确类别的概率很高,也会获得相当高的损失。这可能会妨碍学习的目的。
因此,RetinaNet与焦点损失提出的是:你可以进行平衡交叉熵,即通过对数损失加权某个量α(α可以给定为某个逆类别频率)。但RetinaNet方法还提出了焦点损失,它考虑预测概率本身来微调损失。
因此,你用 (1 - p_t)^γ 来加权你的对数损失,其中γ是一个可调的聚焦参数。最终的焦点损失给出这个量。假设γ是某个值,比如5。当p_t高时,这个量会变低,从而降低整体损失(因为当p_t高时,你希望损失低)。当p_t低时,比如0.1,由于γ=5,这个量仍然是一个合理的值,并且当预测概率p_t低时,对数损失将保持在高水平。这就是焦点损失的主要思想。
RetinaNet架构在其他方面使用FPN(我们讨论过的特征金字塔网络)以及焦点损失,其中分类子网络使用焦点损失进行学习。
实现与工具
所有这些当代检测方法(包括密集采样方法和基于区域提议的方法)都在一个流行的库中有所实现,这个库称为Detectron。Detectron由Facebook AI Research提供,特别是为了促进目标检测算法的使用。因此,如果你有兴趣为任何项目实现任何这些目标检测算法,可以查看Detectron或Detectron 2以获取更多细节。
总结
本节课中,我们一起学习了目标检测中的几种重要单阶段模型。我们从YOLO系列开始,了解了其从v1到v2引入锚框的演进,并简要了解了后续版本如v3、v4的改进。接着,我们探讨了SSD如何利用多尺度特征图进行密集预测,并使用了难负样本挖掘。然后,我们介绍了FPN如何通过自上而下的路径和横向连接来融合不同层级的特征,以同时利用高分辨率的细节和深层的语义信息。最后,我们学习了RetinaNet如何通过创新的焦点损失函数来解决单阶段检测器中极端的正负样本不平衡问题,从而在保持速度的同时提升了精度。这些模型共同构成了现代实时目标检测的基础。
课后练习
-
YOLO演进:我们只在本讲座中涵盖了YOLO v1和v2。YOLO还有YOLO 9000(谈论将YOLO扩展到9000个类别)和YOLO v3(其思想非常接近YOLO 9000)。然而,这些与YOLO v2的不同之处将是你的作业,请阅读阅读部分中给出的链接以理解YOLO。
-
计算IOU:给定图像中的两个边界框:一个左上角框为2×2,一个右下角框为2×3,重叠区域为1×1。两个框之间的IOU是多少?
-
YOLO输出维度:为了更好地理解YOLO,考虑在19×19的网格上使用YOLO,处理一个具有20个类别和5个锚框的检测问题。在训练期间,你必须构建一个输出体积y作为神经网络的目标值。这个输出体积的维度是多少?记住,在YOLO中,我们设定公式为 S × S × (5B + C)。尝试在这里使用该公式找出这个特定YOLO目标检测器的输出体积应该是多少。

请完成这些练习,我们将在下一讲中继续讨论。
101:用于分割的CNN 🧠

在本节课中,我们将学习如何使用卷积神经网络(CNN)进行图像分割。我们将从语义分割开始,介绍几种经典的网络架构,然后探讨更复杂的实例分割和全景分割任务,最后了解用于评估和训练分割模型的关键指标与损失函数。
分割任务回顾与CNN引入
上一节我们讨论了目标检测。现在,我们将转向使用CNN架构进行图像分割。
首先快速回顾上一讲的练习。给定图像中的两个边界框:一个左上角框,大小为2x2;一个右下角框,大小为2x3;重叠区域为1x1。那么交并比(IOU)是多少?这应该很简单。
左上角框面积为4,右下角框面积为6。重叠区域面积为1。总区域面积为 4 + 6 - 1 = 9。因此,IOU为 1 / 9。
第二个问题:考虑在19x19的网格上使用YOLO,有20个类别和5个锚框,输出体积是多少?回忆一下,公式是 S x S x (5B + C),其中B是锚框数量,C是类别数。假设C包含背景类,则最终答案为 19 x 19 x (5*5 + 21)。
让我们首先回顾图像分割本身。在课程早期,我们讨论过不同的图像分割方法,如分水岭、图割、归一化割、均值漂移等。在某种程度上,这些方法启发了用于检测和分割任务的早期CNN架构。事实上,使用选择性搜索获取区域提议的R-CNN,就使用了称为CPMC(约束参数化最小割)的最小割分割方法来生成区域提议。
但现在,我们将转向用于分割的深度神经网络。
语义分割:像素级分类问题
我们首先要处理的任务是语义分割,即为图像中的每个像素分配一个类别标签。
如何用神经网络,特别是卷积神经网络解决这个问题?我们将此问题视为一个像素级分类问题。
到目前为止,我们讨论的是图像级分类,即整个图像只有一个标签。然后我们学习了检测,即在边界框级别有标签,我们不仅给出类别标签,还给出边界框的偏移量以进行最终预测。但在语义分割中,我们将在每个像素的级别进行分类。
因此,对于每个图像,每个像素都必须用一个语义类别进行标记。可以想象,为语义分割创建数据集需要非常密集的标注工作。
近年来出现了各种架构,如FCN、SegNet、U-Net、PSPNet、DeepLab等。我们将在本讲座中逐一介绍。
全卷积网络(FCN)
我们从用于语义分割的全卷积网络(FCN)开始,它于2015-2016年提出。😊
FCN将各种分类网络(如VGGNet、GoogleNet等)适配为全卷积网络。通过将全连接层转换为1x1卷积层,我们实现了这一点。这与单次检测方法中的想法相同,因为对于语义分割,你仍然需要在像素级别进行分类,所以不希望脱离卷积机制。
为了获得每个像素的分类,我们可以在任何CNN的末端添加一个1x1卷积层,并在最终的输出体积中设置 C+1 个通道,其中C是类别数,加1通常用于背景类。
但这种方法有问题吗?使用卷积层时存在一个问题:所有标准的CNN随着网络加深都会进行下采样,无论是通过卷积还是池化。这意味着你的输出特征图尺寸不会与输入相同。但我们最终希望以输入图像的分辨率给出像素级标签。
我们该怎么做?答案我们已经知道:可以在某个阶段后进行上采样。因此,你拥有卷积层,在某个阶段后上采样,将特征图恢复到与输入图像相同的维度。
我们如何上采样?之前讨论过的方法之一是转置卷积。
让我们快速回顾一下转置卷积。这是我们讨论卷积神经网络时提到的一维例子,其输出可以大于输入。这是我们为转置卷积给出的图示,当你在输入图像上应用滤波器时,卷积中存在膨胀。由于存在膨胀,转置卷积也被称为膨胀卷积,有时也被称为反卷积。
现在在FCN中,这个想法被用于在某个层级后进行上采样。
让我们看一个带有VGG骨干网络的FCN架构。如果你有一个VGG16骨干网络,FCN做的第一件事是移除所有全连接层,因此你现在只有VGG16的卷积层。三个全连接层被替换为1x1卷积层,最后一个1x1卷积层有 C+1 个滤波器。然而,你会注意到,虽然你有21个通道,但输出尺寸仍然比输入分辨率小。因此,最后使用一个转置卷积的上采样层来恢复到原始尺寸。
这就是全卷积网络的做法。然而,转置卷积是获得上采样的唯一方法吗?不一定,我们可以做很多事情,包括稍后会看到的跳跃连接。
SegNet:编码器-解码器架构
在我们深入之前,我们将讨论另一个与FCN大约同时期出现的方法,称为SegNet。
SegNet就是你在这里图像中看到的:你有一个输入图像,一个输出,其架构是一个全卷积的编码器-解码器架构。
这与FCN有何不同?与FCN非常相似,编码器是去掉全连接层的VGG16。解码器将低分辨率的编码器特征映射到输入分辨率。这里与FCN有一个区别:FCN只在最后有一个上采样层,而SegNet有一系列解码器层,它们镜像了编码器的架构。因此,编码器和解码器架构是彼此镜像的。
SegNet的重要贡献是避免在上采样中使用转置卷积。那么,SegNet如何进行上采样呢?它实际上使用在编码器层中获得的池化索引来进行上采样。
让我们看看这是如何完成的。如果你有一个特定解码器阶段的输出图,我们使用原始编码器的最大池化索引。假设最大池化索引是 [0,0], [1,0], [1,0], [0,1],那么 a 会去到 [0,0] 位置(左上角),b 会去到 [1,0] 位置(左下角),c 会去到 [1,0] 位置(左下角),d 会去到 [0,1] 位置(右上角)。现在,这成为解码器中前一层输出图的上采样版本。可以继续这样做以获得最终的上采样图像。
编码器中的最大池化索引是什么意思?每当有一个池化层时,你都会跟踪在一个2x2或3x3的补丁中(取决于你进行的池化操作)哪些条目的索引是获胜的,并将这些获胜索引存储在一个单独的内存缓冲区中,我们将使用这些最大池化索引来上采样回更高的分辨率。
为什么这很好?存储最大池化索引比存储编码器的完整特征图更高效。在解码器的每个阶段,使用相应编码器的最大池化索引来生成上采样的特征图。
损失函数
现在我们已经看到了几种架构:FCN和SegNet。训练这样的网络会使用什么损失函数?很简单,它是像素级交叉熵损失的总和。记住,在FCN和SegNet中,你的输出将是一个体积,其尺寸与输入分辨率相同,但通道数将是类别数加一(用于背景)。因此,实际上你可以为每个像素在输出体积中拥有一个概率向量,现在你可以计算每个像素的像素级交叉熵,将它们全部加起来,这就成为你的损失函数。由于它是交叉熵的总和,它仍然是可微的。
U-Net:带有跳跃连接的编码器-解码器
第三种架构,也出现在2015/16年左右,被称为U-Net。
U-Net也是一种全卷积的编码器-解码器架构,但它以一种不同于SegNet的方式引入了跳跃连接的概念。
在收缩路径(编码器路径)中,它是一个移除了全连接层的现有分类网络,类似于SegNet和FCN。但在扩展路径中,除了特征图的上采样外,网络还接收来自收缩路径的相应特征图,以及进一步的卷积层。最后,在末端,你有一个具有 C+1 个通道的1x1卷积,其中C是类别数。
U-Net中的上采样是使用步长为2、填充为0的2x2转置卷积来执行的。在每个上采样步骤中,特征通道的数量减半,这意味着总的输入尺寸将加倍。通过2x2转置卷积和通道减半,此时输入图的尺寸将加倍。
原始的U-Net架构执行的是无填充卷积,这意味着输出分割比输入小一个恒定的边框宽度。
让我们看看它的可视化,这就是U-Net。它的形状告诉你为什么它被称为U-Net架构。你可以再次看到收缩路径类似于任何其他没有全连接层的架构,而在扩展路径中,除了在每个步骤进行转置卷积外,你还接收来自收缩路径的相应特征图,这些特征图被添加到你的扩展路径中。因此,这为解码器的每个步骤提供了来自编码器的定位信息,而不仅仅是像SegNet或FCN那样依赖上采样。
U-Net最初是为生物医学图像分割提出的。事实上,它发表在一个医学会议MICCAI上。你认为这会如何影响U-Net模型的学习?生物医学图像分割有一些独特的挑战。首先,通常你没有像一百万甚至几万那样数量级的训练数据集。可能只有很少的训练图像可用。因此,这项特定工作通过对可用的训练图像应用弹性变形来进行过度的数据增强,这是其贡献之一。医学领域的另一个挑战是,同一类别的许多物体可能相互接触。因为可能有彼此相似的物体相邻放置。因此,U-Net还提出了一种损失函数,该函数对靠近边缘的像素的惩罚比对远离边缘的像素的惩罚更重。边缘对于医学图像分割至关重要。
多年来,出现了U-Net的几种变体,主要是通过用密集块替换卷积块和增加U-Net的深度等方式来改变架构。
PSPNet:金字塔池化模块
另一个用于分割的网络被称为PSPNet。PSPNet首先提出了一个问题:如果在复杂场景上应用FCN,你可能会面临哪些挑战?
如果你深入思考,你会注意到FCN没有学习到上下文模式。例如,我们知道汽车在路上,而船在河流附近。FCN也可能将物体的部分预测为不同的类别,例如,它可能看着一座摩天大楼,认为像素属于不同的建筑物,因为该物体的规模巨大。第三,根据架构和所使用的感受野,FCN可能无法检测到大物体或小物体。
那么,我们如何解决这些限制呢?PSPNet假设应该使用全局上下文信息来提高分割性能。
如何获取全局上下文信息?在多个尺度上进行分割。当你在较低分辨率下分割时,你实际上是在尝试将更多的全局信息带入分割结果中。
因此,PSPNet引入了一个金字塔池化模块。给定一张图像,你通过CNN进行前向传播。然后有一个池化层,它在多个尺度上进行池化。有哪些不同的尺度?有一个粗糙的尺度,即全局平均池化,因此这将是 1x1xC,其中C是通道数。每个连续的池化级别提供越来越多的局部化信息。因此,你的池化输出将是 1x1xC(全局平均池化)、2x2xC、3x3xC 和 6x6xC,这些是PSPNet采用的四个尺度。记住,当你进行池化时,通常通道数不会减少,你是在通道维度上操作。
一旦完成这四个尺度的池化,PSPNet现在对来自池化操作的每个映射应用一个1x1卷积,以减少输出中的通道数。每个1x1卷积将通道大小减少了多少?减少了四分之一。这样做是有原因的。原因是,一旦执行了这些1x1卷积,所有这些映射都会被上采样并连接起来。因此,每个池化后的映射都使用双线性插值进行上采样,然后它们被连接起来,这个连接后的体积将具有与原始特征图相同数量的通道。完成后,将其通过一个卷积层以获得最终的分割预测。
DeepLab:空洞空间金字塔池化
另一种方法是DeepLab,这是一种流行的语义分割方法,与PSPNet类似。在这种情况下,图像经过几个卷积块,然后,它执行空洞空间金字塔池化,而不是将图像分成四个不同尺度的池化层。这个模块被称为ASPP模块。记住,空洞卷积与膨胀卷积或转置卷积相同,有时也称为分数步长卷积。因此,当你的步长小于1时,你会得到与在多个尺度上执行池化相同的效果。所以,可以说空间金字塔池化是实现在PSPNet中金字塔池化模块的不同方式。
因此,一旦你进行了空洞空间金字塔池化,你再次上采样、连接,并最终在连接后的体积上得到你的结果。
DeepLab多年来经历了一些不同的版本,其中一个较新的版本称为DeepLab v3。在基本的DeepLab架构中添加了一些模块。在这个DeepLab v3模块中,图像级特征也被传递到ASPP模块。使用了批归一化以方便训练,并且还有一个完整的解码器架构用于细化分割结果。你可以看到这里有一个ASPP模块,使用它执行1x1卷积、3x3卷积等,你得到一组特征图,这些图被连接起来。进行1x1卷积以减少这些通道的深度,然后将其上采样4倍,并与ASPP模块的输出一起传递给解码器。然后将这些连接起来,最后再进行一次卷积,然后上采样并在最后进行预测。记住,当我们说预测时,它将再次是一个输出体积,其分辨率与输入图像相同,通道数将是 C+1,其中C是类别数,加1用于背景。
实例分割:超越语义分割
从语义分割继续前进,实例分割是比语义分割更具挑战性的任务。在语义实例分割,或通常所说的实例分割中,我们的目标不仅是为每个像素提供类别标签,还要为每个对象分配一个对象ID。
因此,如果有多个人员、多把椅子或多只狗,你还想区分这些人员、狗的实例,这是基本语义分割任务没有考虑的。在语义分割中,狗就是狗像素,无论有两只狗、三只狗还是四只狗,它们都只会被分类为狗。但在实例分割中,我们想要狗1、狗2、狗3和狗4。这里的挑战是你可能不知道狗的数量,这与检测问题类似。这就是为什么最流行的实例分割方法之一,称为Mask R-CNN,实际上试图改进Faster R-CNN以实现实例分割。
因此,Mask R-CNN于2017年ICCV提出,它使用类似Faster R-CNN的架构,但增加了一个分支来掩码化并获得实例级分割。所以,你可以将Mask R-CNN视为在感兴趣区域上带有FCN的Faster R-CNN。它添加了一个并行的头部,除了你的SVM对象分类器(或简单的分类模块)和边界框偏移回归器之外,你现在还添加了一个掩码预测器。这是第三个头部。
Mask R-CNN中能够准确实现掩码的一个重要贡献是一个称为RoI Align的模块。由于Mask R-CNN在Faster R-CNN的框架内运行,该模块解决的主要限制是:当你将对象提议映射到特征空间时,回忆一下,在Fast R-CNN和Faster R-CNN中,你都有某些对象提议,并将它们映射到某个特征空间。特征空间的分辨率可能与你获得对象提议的分辨率不同。在Faster CNN和Faster R-CNN中,我们只是将其变形到所需的大小,然后将你的对象提议带到那个维度。然而,当你这样做时,你可能会遇到由于量化而导致的误差。
为什么这很重要?这很重要,因为特征空间中的一个像素可能相当于图像上的许多像素,因为与初始输入相比,后面卷积层中的特征空间更小。当你试图获得一个掩码时,这几个像素可能会在最终性能中带来显著误差。
那么我们如何克服这个问题呢?RoI Align执行双线性插值,以获得对象提议与给定特征图匹配的确切坐标。这保留了掩码的平移等变性。我们是什么意思?我们的意思是,给定一张图像,其中一个对象可能位于这个黄色框或红色框中,两者都可能是有效的检测,但每个边界框中的掩码位置是不同的。因此,通过平移等变性,我们的意思是,如果对象在边界框内移动了一定距离,掩码也必须移动相同的距离。这可以通过RoI Align层实现。
因此,RoI Align的做法是:当你转换你的对象提议到特征图上的位置时,如果该位置恰好落在特征图上的几个像素之间,那么你对特征图周围的像素执行双线性插值。然后你得到对象提议指向的特定点的特定像素值,这就是随后被池化以获得固定维度表示的内容,该表示将传递到Faster R-CNN架构的后续层。
因此,除了具有分类损失和边界框损失的标准Faster R-CNN架构外,我们还有带有掩码头部的Mask R-CNN。Mask R-CNN所做的是使用一个类似FCN的分支,这意味着它是全卷积的。对于每个RoI,掩码大小为28x28。最后,在推理过程中将其缩放到边界框大小,然后叠加到图像上。因此,这个掩码是为每个RoI获得的,然后在推理时最终叠加回输入图像上。
全景分割:语义与实例的结合
最后一种更近期的流行分割形式被称为全景分割。全景分割结合了语义分割和实例分割。因此,正如你所见,语义分割是你分割所有对象的像素,但所有实例保持不变。实例分割是你只关心你类别中对象的实例,但分别标记每个实例。全景分割是两者的结合,你标记每个像素属于不同的对象,并且你还确保每个对象的实例获得完全不同的标签。
那么,如何进行全景分割呢?这项工作首次在2019年CVPR上提出,大约一年前。你可以使用几种架构来实现全景分割。你可以执行Mask R-CNN来获得实例分割结果,你可以使用空洞FCN来获得语义分割结果,然后合并两者以获得全景分割,这是一种方法。因此,这项特定工作实际上实现了几种方法,然后研究了如何以不同的方式对其进行评估。
另一种方法是,你可以使用特征金字塔网络,而不是我们在这里看到的空洞FCN或Mask R-CNN,你可以使用FPN,然后将这些检测结果通过Mask R-CNN头部以获得实例级分割,你也可以将这些不同的特征图通过像素级识别头部进行语义分割。因此,在这两种方法中,都有一个实例分割路径和一个语义分割路径,它们结合起来给出你的全景分割。
这里的损失函数是什么?推荐的损失函数是语义分割和全景分割以及实例分割损失函数的组合。记住,实例分割在Mask R-CNN中有三种损失:分类、边界框和掩码损失。而对于语义分割,你有标准的像素级交叉熵损失。
全景分割的挑战之一是,它不能使用用于语义分割或实例分割的指标进行评估。为什么会这样?在语义分割中,我们使用IOU和每像素精度。因此,你可以尝试查看,如果你绘制了一组属于特定对象的像素,你可以尝试获得该对象与真实标注的交并比。你也可以获得每像素精度,因为你将在每个像素级别为你拥有的所有类别预测概率,你也可以将该精度作为一个指标。😊
另一方面,对于实例分割(类似于检测),你会有在不同IOU阈值上的平均精度。
为什么我们不能将这些用于全景分割?这是因为这可能导致对有或没有实例级标注的类别产生不对称性。记住,在全景分割中,你可能有一些类别有多个实例,例如人员或狗。也可能有一些类别,如天空、道路,可能没有很多实例。因此,不考虑这些不对称性而合并这些指标可能不明智。
因此,这项特定工作推荐了一种称为PQ指标的新指标。PQ指标如何工作?你可以看这里的例子,假设这是你正确的真实标注:有天空、草地、三个人(这是其实例分割部分),然后有一只狗。现在假设我们提出的模型预测结果是这样:天空,它预测对了;草地,它预测对了;狗被预测成了人;并且不是三个人,它只预测了两个人,其中它合并了其中两个人。
那么,你现在如何衡量这种系统的误差呢?因此,这种方法推荐的方式是:首先写出你所有的真正例,这将是你的真实标注和预测之间的两个真正例的人。有一个假阴性,即你错过的那个人。还有一个假阳性,即你添加的而不是狗的那个人。😊
因此,对于一个真实标注段 G 和一个预测段 P,PQ指标计算为:
PQ = Σ(对于所有属于真正例的 PG) IOU(P, G) / (TP + 0.5*FP + 0.5*FN)
如果你分析这个,它可以写成:
PQ = (Σ IOU / TP) * (TP / (TP + 0.5*FP + 0.5*FN))
那么,第一项将是这种方式,第二项将是 TP / (TP + 0.5*FP + 0.5*FN)。因此,这里的第一项给出了在所有真正例中,你的分割质量(IOU是多少)。第二部分给出了识别质量,即在所有可能的段中,我们有多少个是真正例。因此,你有一部分检查分割质量,有一部分检查识别质量,这就是为什么这个指标对于全景分割变得有用。
分割指标与损失函数
虽然我们看到可以使用不同的指标来研究分割的不同变体,但你可能想问:我们能否使用这些指标中的任何一个来训练模型本身?让我们从回答这个问题开始,回到我们的IOU(交并比),其定义为 A ∩ B / A ∪ B,其中A是预测区域或像素集合,B是真实标注的相应像素集合。这衡量了单个类别的预测区域和真实标注区域之间的重叠。然后我们取平均IOU,即数据集中所有类别的IOU平均值。这通常用作语义分割的标准评估指标。
这引出了一个问题:与交并比相比,是否有更好的集合相似性度量?我们能否使用它们直接训练模型?这引出了今天用于分割任务的一个流行损失函数,即Dice损失。
Dice损失基于Dice系数。这个系数可以追溯到20世纪40年代,当时一位名叫Ice,随后是Sørensen的人发明(或者说发现了)这个系数,其公式为 2 * |A ∩ B| / (|A| + |B|)。这也是一个类似于IOU的集合相似性度量,但它在分母中考虑了集合的大小,因此使其成为一个更好的集合相似性度量,特别是对于不平衡的集合。分割中的许多应用具有可能只有很少像素的区域,而其他区域可能具有大量像素。这使得Dice系数更适合于研究分割,而导出损失函数的系数变体是看损失为 1 - Dice系数。
我们在这里写Dice系数的方式是 (2 * y * p) / (y + p),其中 y 是真实分割掩码(一组像素,我们将其写为二进制掩码,这意味着对于该特定真实分割掩码,我们只有0和1),p 对应于预测的分割掩码(同样是二进制或概率掩码)。显然,在 y 为0的地方,相应的概率不会有值;在 y 为1的地方,它会有值。这里的 +1 主要是为了数值稳定性而添加的,以避免除以零。现在,这成为一个我们可以用来训练分割模型的损失,除了使用像素级交叉熵之外。
这是我们使用的唯一损失吗?不完全是,有一整套损失函数分类法可以考虑。你可以看这篇名为《语义分割时代的损失函数》的论文,这是一份非常好的文档,它从像素级别、区域级别和边界级别,以及所有这些损失的组合来审视语义分割的损失函数。我们不会在这里深入探讨每一个,但如果你感兴趣,可以查看这篇特定论文以了解更多细节。
总结
本节课中,我们一起学习了用于图像分割的卷积神经网络。我们从语义分割的基本概念和全卷积网络(FCN)开始,了解了如何将分类网络转换为分割网络。接着,我们探讨了SegNet和U-Net这两种编码器-解码器架构,它们通过不同的方式(如使用池化索引或跳跃连接)来处理上采样和特征融合。然后,我们介绍了PSPNet和DeepLab,它们通过金字塔池化模块和空洞卷积来捕获多尺度上下文信息,以提升复杂场景下的分割性能。
之后,我们进入了更高级的任务:实例分割和全景分割。我们详细分析了Mask R-CNN,它扩展了Faster R-CNN,通过添加掩码预测分支和RoI Align模块来实现精确的实例级分割。全景分割则结合了语义分割和实例分割,我们讨论了其独特的评估指标PQ。
最后,我们探讨了分割任务的评估指标(如IOU)和训练损失函数(如交叉熵损失和基于Dice系数的损失),理解了直接优化评估指标(如使用Dice损失)的重要性。

通过学习这些内容,你现在应该对使用深度学习进行图像分割的主流方法、架构演变以及关键技术细节有了一个全面的认识。
102:循环神经网络 - 介绍 🧠

在本节课中,我们将要学习循环神经网络的基本概念。我们将了解RNN为何被设计用于处理序列数据,其核心架构,以及如何通过计算图来理解其工作原理。
到目前为止,我们已经了解了卷积神经网络的工作原理、如何使用反向传播训练它们、它们如何用于图像分类、使用的损失函数,以及如何可视化和理解CNN。我们也看到了CNN如何扩展到其他任务,如检测、分割、人脸识别以及其他杂项任务。
本周,我们将转向深度学习领域的另一个支柱——循环神经网络。循环神经网络旨在处理时间序列,这意味着在计算机视觉中应用时,当输入是视频时,它们就变得相关。
在开始本主题之前,我们有一个问题:如何发现模型中的偏见?如果你有一个人脸识别模型,你如何知道该模型是否对特定人群或数据中心存在偏见?希望你有机会思考过这个问题。在当今这类技术被大规模部署的时代,这是一个重要问题。
一个简单的检查方法是,如果你认为可能存在某个特定的特征或属性,例如某个人的背景。那么你可以在输入中改变该属性,并观察预测是否发生变化。如果发生变化,则表明模型依赖该特征进行预测。如果你认为这种依赖不应该存在,你应该尝试基于该属性进行正则化,使用数据增强方法来改变该属性的值,并确保模型的预测不发生变化。
现在,我们将转向循环神经网络。本讲座幻灯片基于Fili的CS231N课程和Mihikara的IIT Madras课程。
序列学习问题
循环神经网络的更广泛背景是序列学习问题。
到目前为止,无论是前馈网络还是卷积神经网络,输入的大小始终是固定的。给定一个CNN,你总是输入一个固定尺寸的图像,例如用于图像分类的32x32图像。每个输入也独立于之前或未来的输入。并且,两个连续图像的计算、输出和决策彼此完全独立。
这是什么意思?这也与我们在机器学习数据集中常说的IID属性有关。IID代表独立同分布。如果我们有一个图像数据集,我们假设数据集中的每个图像都是独立同分布的。这意味着无论你以何种顺序提供这些图像,你的模型最终都可能学习到相同的预测函数。无论输入是这张图像、那张图像还是另一张图像,显然类别标签会改变,但模型参数或架构中的其他内容不会改变。
然而,还有另一类问题,称为序列学习问题,它们需要比我们目前所见更多的东西。
以文本自动补全为例,这是你可能每天都会遇到的情况。如果你输入“Google Autocomplete A”,你会得到一组推荐;如果你输入“Google autocomplete B”,你会得到另一组推荐;对于“C”也是如此。在这类问题中,连续的输入不再独立。为了在特定状态下进行预测,你需要查看之前时间步的输入。这是我们在讨论前馈网络或CNN时不需要的。
在这类问题中,输入的长度和需要进行的预测数量也可能不是固定的。例如,如果你输入“Google autocomplete C”,最后一个词可能是“code”或“country”,它们的长度不同。你希望你的模型不受此类约束的影响。
这类问题的另一个重要启示是:无论你的输入是什么,无论“C”之前有两个词、三个词还是一个词,模型必须在所有这些上下文中执行相同的任务。给定一个先前的短语,它必须预测下一组字符。无论你在哪个时间步尝试进行预测,该任务都不会改变。如果用户输入了一个词,你仍然需要预测下一组字符;如果用户输入了两个词,你的上下文增加了,时间上下文的长度增加了,但任务需要实现的目标保持不变。
这类问题被称为序列学习问题。为处理此类序列学习问题而设计的神经网络架构被称为循环神经网络,我们很快将看到它们为何如此命名。
RNN的架构类型
以下是RNN的不同架构变体。
如果你将一个标准的前馈普通神经网络可视化为这种形式,其中有一个输入、一组隐藏层和一个输出,那么循环神经网络的变体可以可视化为以下几种:
-
一对多:你的输入在一个时间步,但你的输出实际上是跨时间步的一系列输出。例如:图像描述生成,其中输入是单个图像,输出是一系列单词,这些单词需要前后连贯以组成完整的句子。
-
多对一:你在多个时间步有输入,但只在一个特定时间步进行预测。例如:动作预测,其中输入是一系列视频帧,模型在看完完整的输入序列后给出一个特定的类别。例如,在看完一段完整的足球比赛视频片段后,模型应预测该片段是否为进球事件。
-
多对多:给定一组跨时间步的多个输入,输出也需要有跨时间步的多个输出。这被称为多对多,一个例子是视频描述生成,其中输入是视频(一系列随时间变化的帧),输出也是一系列随时间变化的单词。
-
同步多对多:你有一组随时间变化的输入和一组随时间变化的输出,但输出和输入在每个时间步是同步的。一个例子可以是逐帧视频分类,因此视频作为输入进入你的模型(记住,视频具有时间成分),但同时你希望在每一帧级别进行预测。在这种特定上下文中,这将是一个多对多设置。
如何为序列建模
那么,我们如何为涉及序列的任务建模呢?我们看到了高级架构,但实际上如何用这些架构学习模型?如何考虑时间序列数据中常见的输入之间的依赖性?如何考虑可变数量的输入?如何确保,例如,在图像描述生成中,输入或输出的数量是可变的?对于另一个应用,如何允许可变数量的输入或输出?
第三个考虑因素是,如何确保在每个时间步执行的函数是相同的?为什么需要这个?无论一帧在视频中的哪个位置,你都希望使用相同的逻辑来执行预测。这类似于我们在卷积中所说的,无论猫在图像的左上角还是右下角,我们都想说图像中存在一只猫。我们通过卷积的思想克服了那个问题。但现在,我们如何在时间序列和序列学习问题上做到这一点,这就是我们接下来几张幻灯片要讨论的内容。
RNN的核心抽象
因此,循环神经网络的高级抽象是这里的这个图。你有一个输入x,一个输出y。在两者之间,有一个被称为循环神经网络的东西,正如其名,这里有一个循环,现在由这个单元循环表示。
RNN的关键思想是它们有一个内部状态,该状态在序列被处理时更新。人们可以将这个内部状态视为某种记忆,它保留了同一时间序列过去数据的一些信息,这个想法我们本周稍后也会讨论。
如果将循环展开,你会得到一个像这样的网络:你有一组随时间变化的输入x1到xt,这就是你拥有的输入序列,这就是为什么我们将其视为序列学习问题。对于每个输入,你将该输入提供给一个RNN块。这个RNN块可以是单个隐藏层,可以是多个隐藏层,可以有跳跃连接,可以有任何其他架构,但我们将把它视为一个单一的抽象。该RNN的输出就是模型在该时间步的输出。这将是整个展开的RNN。
当你使用RNN时,你必须事先决定你想要展开RNN多少。这是什么意思?对于特定问题,你必须决定这个小T必须是多少。你是想查看过去的20帧来做决定,还是想查看过去的100帧,或者过去的200帧?这是在你开始学习RNN之前必须决定的事情。这里的P将对应于你在进行预测时想要查看的序列长度。
一旦T被决定,你可以这样展开RNN:RNN块内的相同操作用于每个时间步的输入。正如我们之前所说,根据你使用RNN的应用程序,你可以在每个时间步都有输出,或者只在最后一个时间步有输出,或者也可以在最后一个时间步之后有一系列输出。所有这些可能性都是我们在屏幕上看到的这个抽象的变体。
循环公式
现在让我们深入更多细节。我们说过它是一个循环神经网络,并且我们也希望这里的RNN块对每个时间步的输入重复相同的操作。那么,我们实际想要使用的循环操作是什么?
对我们来说,每个时间步的循环公式是:给定特定时间步的输入Xt,以及Ht-1(RNN在前一个时间步的状态),然后你将应用某个函数,这个函数是RNN内部的参数,带有一些权重U和W,你会得到该时间步的输出Ht,它被传递到RNN的下一个状态,以处理下一个时间步的输入。
这是一个可视化:在时间步T1,给定输入x1和初始状态h0,RNN使用权重U和W(权重矩阵)处理这两个输入h0和x1,并在该时间步输出y1(如果需要输出)和一个状态H1,该状态被传递到RNN的下一个时间步。但在下一个时间步,RNN接收x2和h1作为输入,使用与前一个时间步相同的权重U和W(记住我们说过,我们希望RNN将每一帧视为类似输入并执行类似操作,尽管它们在时间上位置不同)。因此,第二个时间步的RNN输出y2(根据问题在该阶段是否需要输出)和h2(其隐藏状态),该状态被传递到第三个时间步的RNN,依此类推,直到最后一个时间步。
因此,重复一下循环公式:Ht = F(U, W),其中U和W是那些权重矩阵,给定输入Xt和Ht-1。这里的关键观察是,相同的函数和相同的参数集在每个时间步都被使用。这不会改变,而这正是循环神经网络中“循环”所在之处。
现在,你可以考虑根据任务使用激活函数。因此,我们刚才在这里写的循环关系现在可以是:H = tanh(U * Xt + W * Ht-1),其中U是乘以Xt的权重矩阵,W是乘以输入Ht-1的权重矩阵。Yt可以是V * Ht的softmax。所以,如果你有一个从RNN输出的Ht,那么你有一组权重V乘以VHt,然后如果你在处理分类问题,你对该VHt应用softmax以给出RNN的最终输出。从x到RNN的权重是U,查看Ht-1并给出Ht的权重是W。所以你可以看到有三组权重矩阵(当然,W可能根据RNN块内有多少层而变化),否则这里有三个权重集作用于输入、隐藏状态和隐藏状态到输出。这有时也被称为Elman RNN,以Jeffrey Elman的名字命名,他负责提出这些想法。
计算图简介
为了继续前进并理解RNN实际上是如何训练的,我们将退一步,看看所谓的计算图的概念。
计算图是实现神经网络(包括推理和反向传播)的一种非常常见的方法,如今在PyTorch或TensorFlow等框架中也是如此。因此,我们将首先回顾一般的计算图,然后讨论如何为RNN定义计算图。
计算图是一种有向图,其节点对应于操作或变量。输入节点和从节点输出的值是张量。张量是多维数组,通常是大于二维或二维的。向量的推广是矩阵,矩阵的推广通常称为张量。张量通常包含标量、向量、矩阵等,张量是表示这些量的更通用术语。
正如我刚才提到的,计算图可以实例化以执行神经网络的前向传播和后向传播。
在我们讨论神经网络之前,让我们举一个简单的例子,谈谈如何使用计算图来创建表达式并计算它们。
如果你有表达式:E = (a + b) * (b + 1),这是一个算术表达式。这里有三个操作:两个加法和一个乘法。可以将整个表达式或操作写为一系列中间操作。这些中间操作是什么?你可以首先说C = a + b,D = b + 1,然后E = C * D,这是你的最终输出。所以,你说这里的第一个项是C,第二个项是D,然后你最终将两者相乘得到E。这看起来很简单,我们为什么要讨论这个?
现在可以将其写为计算图。你有作为输入给出的值A和B。你计算由a + b给出的C。然后你计算D,它只依赖于B,即b + 1。然后你组合C和D得到最终输出E,即C * D。
如何评估这些表达式?假设给你A和B的具体值,你如何使用计算图评估这个表达式?你将输入变量设置为给定的特定值,例如a=2,b=1,然后你将从这些变量实例值开始,沿着图向上计算节点,直到根节点,得到最终答案。所以它可能看起来像这样:设置a=2,b=1。首先计算c = a + b,得到C=3;类似地,d=2;然后组合它们得到E的最终答案,即E=6。这很好很简单。
但是,你如何使用这样的计算图进行反向传播?换句话说,如何使用计算图计算梯度或导数?这里的关键是,要理解梯度,你需要理解计算图的边。因为边表示一个特定变量如何影响另一个变量。
例如,如果你有相同的计算图,你在这个叶节点有a,并且在那个叶节点的父节点有C = a + b。现在,可以写出∂C/∂a。因为C = a + b,所以∂C/∂a = 1。类似地,在连接B和C的这条边上,∂C/∂b也将是1,因为c = a + b,依此类推,对于图中的每一组边。
我们如何做到这一点?我们正在将标准的求和规则和乘积规则适当地应用于梯度,以计算每条边的梯度。为了得到你的最终梯度,你可以对从一个节点到另一个节点的所有可能路径求和,将路径上每条边的导数相乘。例如,如果我们想要∂E/∂a,我们查看所有从a到E的可能路径,所以在这种特定情况下,就是这条路径。你将那条路径上的所有梯度相乘,所以在这种情况下是1 * 2,这就给出了∂E/∂a的梯度。类似地,如果你必须计算∂E/∂b,你必须考虑所有从B到E的可能路径。你将每条路径中的梯度相乘,并将这两条路径的贡献相加,你将得到最终梯度。所以再次,在这种情况下,∂E/∂b将是1 * 2 + 1 * 3。
这就是你如何使用计算图来获取特定表达式中的梯度。因此,为了给你一个更具体的例子,在你可能使用的框架PyTorch中,PyTorch在前向传播期间动态跟踪变化,允许创建这样的计算图,称为动态图。当你说loss.backward()时(当你在PyTorch中训练神经网络时,如果你说loss.backward(),例如,或者如果你只是评估一个表达式,在我们看到的这个特定例子中,你可以说c.backward()),PyTorch框架会触发梯度的计算,然后你可以打印出表达式中每条边或每个梯度的梯度。
神经网络的计算图
那么,前馈神经网络的计算图呢?所以,如果你在神经网络中有几层,给定输入X,假设你的隐藏层H是tanh(Wx + b),Wx + b是你的输入x的线性组合,让我们假设你在每个节点上有一个非线性激活函数tanh,这给你一个向量H。然后你在H上应用另一组权重V,给你最终输出y,其中B和a是你的偏置(如果这只是前馈神经网络的数学表示)。你对应的计算图将如下所示:你的第一个输入是W和x,基于此你在这里得到某个输出。然后你将其与+b组合,然后你可以得到某个输出。然后最后你取那个并应用tanh,得到你的tanh(U)。所以这里的第一步可以表示为一个计算图,它是一个序列,以W、X和b作为输入,输出H。类似地,现在对于下一个操作,你现在可以将h视为输入,v视为另一个输入,a视为另一个输入,此时的计算图将是这组操作。显然,此计算图中某个节点的输入是作为前一个计算图的输出而来的边。
RNN的计算图
现在,回到RNN,由于RNN稍微复杂一些,让我们尝试从计算图的角度来看待RNN。因此,给定输入x1和H0,输入被提供给F(U, W),这是我们之前讨论过的,F(U, W)输出h1。然后h1和x2再次进入F(U, W),这是我们在每个时间步评估的相同函数。然后第二个时间步的F(U, W)给你H2。这再次循环地给出到下一个时间步,依此类推,直到你得到序列最后一个时间步的最终隐藏状态。
因此,从计算图的角度来看,这里的输入U、W(权重矩阵)在每个时间步都是相同的,这就是这些箭头所显示的,它们进入每个时间步的F(U, W)。
如果你有一个多对多RNN,你只会稍微改变这一点,因为现在你将在每个时间步都有输出,而我们在前面看到的RNN中没有。你现在可以有一个从多对多RNN出来的输出L1,这最终是作为输出给用户的。你也可以考虑组合所有这些L1、L2、L3...直到Lt,以给出你的最终L作为输出。为什么这很重要?如果你看,比如说,视频描述生成,这将是这种多对多架构的一个实例。你可以将L1、L2、L3、LT等视为保存描述每个单词的变量,而L是作为RNN输出给出的最终描述。
如果你考虑多对一架构,计算图将看起来像这样,初始部分大致保持不变,但输出y现在只在最后一个时间步。这完整吗?在计算图方面并不完全,你还需要连接,其中y可以是一个输出,它也接收h1、h2、h3等作为输入来计算最终输出值。
在一对多设置的情况下,你可以有这种计算图,其中输入仅在第一个时间步是x,但你现在在每个时间步都有输出y1、y2、y3直到yt。这里的一个问题是,在中间时间步你会给出什么作为输入?为什么我们需要讨论这个?记住,F(U, W)在每个时间步是相同的函数。我们已经知道在第一个时间步F(U, W)需要两个输入x和H0,这意味着在每个时间步它确实需要两个输入。我们该怎么办?一个选择是,我们可以在每个时间步输入零,这可能是我们可以提供的一个选项。有时遵循的另一个选择是,通过调整维度,前一个时间步的输出也可以作为下一个时间步的输入。你可以使用前一个时间步的输出也作为下一个时间步的输入。为什么这相关?为了生成描述中的下一个单词,你可以提供描述中的前一个单词来帮助模型生成适当的下一个单词。
一个具体例子
为了给出一个更具体的例子,让我们考虑一个RNN模型。让我们假设输入和输出来自我们的词汇表“H E L O”。你的输入字符,假设是“h”,表示为[1,0,0,0,0]。这样的表示也称为独热向量。独热向量是一个向量,在给定输入的位置(在词汇表中的位置)为1,其他地方为0。由于“h”是这里的第一个字符,如果你的输入字符是“h”,对应的独热向量将是[1,0,0,0,0]。给定该输入,让我们假设你有一个隐藏层,一个输出层,并假设输出层有d组输出,在进行softmax后,你最终得到一组概率,其中第二个概率最高,这意味着你将预测下一个字符为“E”。现在,这个字符作为输入被提供给下一个时间步(记住我们刚刚说过y1作为输入被提供给下一个时间步,作为x2)。所以“E”,由于“E”在词汇表中的第二个位置,将由独热向量[0,1,0,0]表示,然后通过相同的网络传递,现在你得到一个softmax输出,如[0.25, 0.2, 0.5, 0.05],最高的是第三个,即“L”,这是RNN在该时间步的输出。那个“L”然后再次作为输入被提供给下一个时间步,“L”表示为[0,0,1,0](因为“L”在词汇表中的位置),再次执行此过程,它再次给出“L”作为输出。那个“L”被提供给下一个时间步,然后输出是“O”,在某个时刻,整个RNN模型可以给出一个结束标记作为输出,以知道这组输出在哪里结束。所以结束标记将是词汇表中的一个默认字符,当RNN模型决定结束该特定单词的生成时,就会预测它。
总结与作业
在本节课中,我们一起学习了循环神经网络的基本概念。我们了解了RNN为何适用于序列数据,探讨了其一对多、多对一和多对多等不同架构。我们深入研究了RNN的核心循环公式,并通过计算图理解了其前向和反向传播机制。最后,我们通过一个简单的字符预测例子,直观地感受了RNN的工作流程。
作为课后作业,你的阅读材料包括《深度学习》书籍的第10章,以及Andrej Karpathy关于RNN的优秀博客文章。此外,如果你愿意,可以浏览斯坦福大学的CS231N课程或印度理工学院马德拉斯的CS 7015课程。

在下一讲中,我们将讨论如何使用反向传播来训练这些RNN。在此之前,有几个问题供你思考:RNN可以有多个隐藏层吗?我们已经回答了这个问题,但请思考以确保它可以。下一个问题是:RNN的状态H记录了来自所有先前时间步的信息。在每个新的时间步,从某种意义上说,旧的信息被当前输入稍微修改。你有一个Ht-1到来,你有一个Xt到来。先前步骤的状态现在由于这个时间步到来的输入而被修改。如果我们过多地修改状态会发生什么?更确切地说,如果你只非常关注当前输入,而较少关注先前时间步的状态,会发生什么?思考一下,我们很快就会讨论这个问题。
103:RNN中的反向传播


在本节课中,我们将学习循环神经网络中的反向传播。我们将首先回顾RNN的前向传播过程,然后详细推导如何计算误差相对于网络权重(U、V、W)的梯度。最后,我们将讨论RNN训练中一个关键挑战——梯度消失与爆炸问题,并简要介绍其应对思路。
回顾RNN架构与问题
上一节我们介绍了RNN的基本结构。本节中,我们来看看RNN架构的两个扩展以及一个核心问题。
首先,RNN可以拥有多个隐藏层。实际上,你可以在每个RNN模块中设置任意数量的隐藏层。此外,你还可以将多个RNN模块堆叠起来,这种架构被称为堆叠RNN。
以下是堆叠RNN的示意图:
输入 -> RNN块1 -> RNN块2 -> 输出
在这种架构中,每一层(级别)的权重在所有时间步之间是共享的。例如,第一层RNN块在所有时间步共享同一组权重,第二层RNN块也是如此。
其次,我们讨论了RNN状态更新的一个潜在问题。RNN的隐藏状态记录了之前所有时间步的信息。如果在某个时间步,当前输入对状态的改变过大,那么之前时间步的影响就会被削弱,这对于序列学习任务可能是不利的。
RNN的前向传播与损失
现在,我们正式进入RNN的反向传播。首先,让我们回顾一下RNN的前向传播过程。
假设我们有一个用于分类任务的RNN,其前向传播公式如下:
隐藏状态更新公式:
h_t = σ(U * x_t + W * h_{t-1})
其中,σ 是激活函数(如tanh或sigmoid)。
输出计算公式:
ŷ_t = softmax(V * h_t)
对于分类问题,我们通常使用交叉熵损失函数。总误差 E 是每个时间步误差 E_t 的总和。
计算梯度:以权重V为例
我们的目标是计算总误差 E 相对于三组权重 U、V、W 的梯度,以便使用随机梯度下降法更新权重。总梯度是各时间步梯度的和:
∂E/∂W = Σ_t ∂E_t/∂W
让我们从一个相对简单的情况开始:计算误差 E_3(在时间步t=3)相对于输出层权重 V 的梯度。
我们定义 z_3 = V * h_3。根据链式法则,梯度计算如下:
∂E_3/∂V = (∂E_3/∂ŷ_3) * (∂ŷ_3/∂z_3) * (∂z_3/∂V)
-
∂E_3/∂ŷ_3:对于交叉熵损失,这部分通常简化为(ŷ_3 - y_3),其中y_3是真实标签。 -
∂ŷ_3/∂z_3:这是softmax函数对其输入的导数。 -
∂z_3/∂V:根据z_3的定义,这部分等于h_3。
因此,∂E_3/∂V 可以顺利计算。总梯度 ∂E/∂V 就是所有时间步 ∂E_t/∂V 的和。
计算梯度:权重W与时间依赖的挑战
接下来,我们计算误差 E_3 相对于循环权重 W 的梯度。这比计算 V 的梯度要复杂。
一个初步的想法是:
∂E_3/∂W = (∂E_3/∂ŷ_3) * (∂ŷ_3/∂h_3) * (∂h_3/∂W)
然而,这并不完整。因为 h_3 不仅直接依赖于 W,还通过 h_2 间接依赖于 W,而 h_2 又通过 h_1 依赖于 W,依此类推。这意味着 W 通过多条路径影响了 E_3。
为了正确计算梯度,我们必须考虑从时间步0到3的所有路径。应用链式法则,完整的表达式为:
∂E_3/∂W = Σ_{k=0}^{3} [ (∂E_3/∂ŷ_3) * (∂ŷ_3/∂h_3) * (∂h_3/∂h_k) * (∂h_k/∂W) ]
其中,∂h_3/∂h_k 项本身可以进一步展开为一系列雅可比矩阵的乘积,例如:
∂h_3/∂h_1 = (∂h_3/∂h_2) * (∂h_2/∂h_1)
因此,∂E_3/∂W 的核心部分包含了一个跨越多个时间步的连乘积:
Π_{j=k+1}^{3} (∂h_j/∂h_{j-1})
梯度消失与爆炸问题
上述连乘积项正是RNN训练中著名问题的根源:梯度消失和梯度爆炸。
梯度消失问题:
如果每个 ∂h_j/∂h_{j-1} 的值(通常是激活函数的导数)小于1(例如使用sigmoid或tanh函数时),那么当时间步很长时,这个连乘积会指数级地趋近于零。这意味着,较早时间步的梯度信号在反向传播到更早的层时会变得极其微弱,导致网络无法学习到长距离的依赖关系。
梯度爆炸问题:
相反,如果这些导数值大于1,连乘积会指数级增长,导致梯度变得异常巨大。在数值计算中,这通常表现为“NaN”(非数字)错误。
在实践中,梯度爆炸问题相对容易处理,常用的方法是梯度裁剪。即设定一个梯度阈值,当梯度的范数超过这个阈值时,就将其缩放回该阈值范围内。
然而,梯度消失问题更为棘手,因为它意味着模型从根本上难以学习长期依赖。关于权重 U 的梯度计算与 W 类似,也会面临同样的问题,我们将其留作练习。
总结与下节预告
本节课我们一起学习了RNN中的反向传播。
-
我们回顾了RNN的前向传播公式和损失函数。
-
我们推导了误差相对于输出权重
V的梯度计算,这个过程相对直接。 -
我们重点分析了误差相对于循环权重
W的梯度计算,揭示了其中包含跨越时间步的连乘积。 -
正是这个连乘积导致了梯度消失(当因子小于1时)和梯度爆炸(当因子大于1时)问题。梯度爆炸可以通过梯度裁剪缓解,但梯度消失问题需要更根本的架构改进。
在下一讲中,我们将探讨如何通过改进RNN的架构(例如引入门控机制,如LSTM和GRU)来有效地解决梯度消失问题,从而使RNN能够学习长序列中的长期依赖关系。
课后作业:
-
请推导误差
E_3相对于输入权重U的梯度∂E_3/∂U。 -
思考:在不改变RNN基本架构的前提下,能否通过某些选择(如激活函数、权重初始化等)来缓解梯度消失问题?
推荐阅读:
-
《深度学习》第10章。
-
Denny Britz的“Wild ML”博客中的RNN教程,它对RNN的反向传播有非常清晰的解释。


105:使用CNN和RNN进行视频理解 🎥

在本节课中,我们将学习如何利用卷积神经网络(CNN)和循环神经网络(RNN)来处理和理解视频数据。视频是视觉领域中的时序序列,我们将探讨几种核心方法。
上一节我们介绍了处理时序序列的RNN。本节中,我们将转向视觉领域的时序序列——视频。
为何需要理解视频?🤔
以下是几个例子。第一个例子是自动棒球棒击打棒球。要理解棒球棒将在何处击中球、球将飞向何方或这是什么活动等问题,需要观察所有帧才能做出决策。序列中的单个帧可能无法回答这些问题,例如球将飞向哪里。
另一个例子是理解螺丝的旋转方向。仅凭其中一帧图像无法获知螺丝的旋转方向。需要观察一系列帧才能做出判断。
因此,我们的问题是:如何利用迄今为止学到的知识(前馈神经网络、CNN和RNN)来理解视频?仅靠其中一种网络是否足够,还是需要组合使用?让我们尝试从基础开始,自己找出答案。
理解视频的可能方法 🛠️
如果要理解视频,可以关注特定位置。以螺丝视频为例,可以跟踪螺丝上的一个点,观察其随时间的变化,从而判断旋转方向。那么,如何获取这些信息并跟踪特定点呢?
考虑到本课程的重点是深度学习,我们将关注基于深度学习的方法来理解视频。广义上说,可以仅使用CNN,也可以结合使用CNN和RNN来理解视频。本节课我们将看到这两种例子。
即使在CNN内部,也有两种执行视频理解的方法:一种是进行所谓的3D卷积,另一种是尝试重新利用2D卷积本身来理解视频。
3D卷积方法
在3D卷积方法中,时间成为分析的另一个维度。就像图像是二维信号一样,视频是三维信号,时间成为另一个维度。因此,我们迄今为止看到的每个操作都必须增加一个维度,例如3D卷积。
2D卷积方法
另一种看待问题的方式是使用2D卷积,但将时间视为一个独立的实体,而不是一个单独的维度。我们将通过一个例子来更清楚地说明这一点。
你可能会问:我们之前不一直在做3D卷积吗?我们CNN的输入是体积(例如R、G、B通道)。那么,我们实际上不是已经在进行体积卷积了吗?那不是3D卷积吗?
如果你之前没有仔细思考过这个问题,答案是否定的。我们不称之为3D卷积的原因是,我们只沿着图像的两个维度移动卷积核。深度(通道数)是固定的,执行卷积时并不跨越该维度移动,移动只发生在两个维度上。因此,我们迄今为止看到的是2D卷积,尽管滤波器延伸到第三维度并被处理,但我们实际上并不在第三维度上移动窗口。
现在,当我们讨论视频和3D卷积时,我们指的是在时间维度上移动。
深入理解3D CNN 📊
首先,让我们尝试将3D CNN作为理解视频的一种方式。传统上,如果从视频中取一帧或多帧,并执行标准的2D卷积,你会查看每一帧的特定区域进行卷积,并在输出特征图中得到某个输出。然而,以这种方式应用卷积时,两帧之间没有连接,也没有共享任何信息。
然而,在进行视频理解时,核心思想是能够传递或组合来自多帧的信息以进行预测(例如分类问题)。那么,我们如何做到这一点呢?正如刚才提到的,想法是将卷积扩展到另一个维度。
如果你有三帧图像,我们提出的不是对每一帧单独进行2D卷积,而是在帧之间进行卷积。让我们更仔细地研究这一点。你可以看到,第一个区域有一组权重,在输出特征图中产生一个像素。但该特征图也受到应用于第二帧的另一组权重和应用于第三帧的第三组权重的影响。类似地,在第二个特征图中,你会看到视频的每一帧都应用了不同的权重集。因此,你现在有了一个新的维度(时间维度)需要进行卷积。
我们将看一篇特定的论文,它可能是引发后续大量工作的先驱,即发表于2010年ICML和2012年PAMI的《3D卷积神经网络》。让我们看看这个3D CNN的架构。架构多年来有所发展,但至少这个例子可以给你一个概念。
在这个特定例子中,3D卷积神经网络用于人类动作识别,即观看视频并预测动作。输入是七帧分辨率为60x40的灰度图像。这项工作的初始层使用了硬编码的滤波器,这些滤波器不是学习得到的,而是五种不同类型:灰度滤波器、沿x方向的梯度、沿x方向的光流、沿y方向的光流等。这些滤波器产生了总共33个特征图,每个特征图的分辨率仍为60x40。
在初始处理之后,下一层是使用学习权重进行的3D卷积。他们执行了7x7x3的3D卷积,并确保在这五种不同类型的特征图各自内部进行3D卷积,正如你在这里看到的颜色编码所示。为了增加多样性,他们还引入了另一组完全不同的特征图和权重集,但执行相同的操作。你可以认为这类似于AlexNet第一层中48个特征图分别进入两个不同GPU的情况,尽管在这里意图不是发送到不同GPU,而是为了在特征图中获得更多多样性。
其余层遵循传统的CNN方法:在每个特征图内进行2x2下采样,然后进行另一个3D卷积(7x6x3),同样有两组特征图,接着是3x3下采样,然后是7x4卷积将所有内容整合在一起,最后是一个全连接层进行最终决策。这些是如何学习的呢?从这个角度来看,没有任何变化,你仍然在最后有交叉熵损失,并且仍然可以反向传播通过所有这些层,非常类似于我们为CNN讨论的方式。
使用这种方法,他们可以展示出能够执行动作识别。他们数据集中的某些动作包括:检查是否有人将手机放在耳边,或者发现有人在公共环境中遗留物品,或者有人指向另一个人。如果你仔细观察,每个框旁边还有一条轨迹,显示了导致当前帧结果的先前帧及其位置,以及一个得分(而非概率)。这就是3D CNN理解视频的方法,多年来已经出现了多种变体。
使用2D CNN进行视频理解 🔄
现在让我们换个角度思考问题:我们能否仅使用2D CNN来解决视频理解问题?事实证明,有一种非常流行的方法可以做到这一点,称为双流卷积网络,至今仍广泛用于视频理解任务。
让我们尝试理解它的工作原理。假设有一段视频,在某一时刻你看到一个人试图从衬衫后面取出某物,手移动,然后有东西从衬衫后面出来。现在,我们可以使用传统的光流方法来找出像素在x和y方向上移动了多少。利用这个光流,你可以有水平分量和垂直分量。
在双流CNN中,你有输入视频(一个体积)。你有一个CNN的空间流,它接收单个帧(可以是彩色图像),然后通过标准的CNN架构传递,最终输出这可能是什么动作。这包括conv1、conv2、conv3、conv4、conv5、全连接层、全连接层,非常类似于AlexNet架构,最终得到分类结果。但这仅针对单帧。
另一个流被称为时间流,它执行多帧光流分析,以了解内容在帧之间的变化情况。因此,你得到了输入视频中每对连续帧之间的光流图像。这些光流图像在时间维度上形成一组通道,现在可以对这个光流体积执行标准的2D卷积。每个光流图像都是一个2D图像,跨时间堆叠在一起形成一个体积。这个体积可以通过另一个标准的2D CNN架构(我们称之为时间流卷积网络)传递,两者都提供它们自己对动作的分类。
最后一步是执行类别得分的融合,你可以通过多种方式聚合它们(例如多数投票或平均),然后做出最终决策,从而得出这是什么动作的最终判断。请注意,这里所有的卷积都是2D卷积,而不是3D卷积。
结合CNN与RNN进行视频理解 🤖
那么,如何使用RNN与CNN结合来进行视频理解呢?给定一个视觉输入(如图像),首先从CNN中提取视觉特征。因此,你会将其前向传播通过一个CNN,并取倒数第二层(例如AlexNet的某层)作为图像的表示。
现在,这些图像表示可以提供给一组LSTM或RNN进行序列学习,以给出最终的预测。假设你有一段视频,有多帧图像。那么,你将每一帧提供给相应的CNN以获得不同的特征表示,然后这些表示可以提供给一个LSTM,LSTM会给出输出。你可以假设这里的每一列是你的LSTM在时间上展开,这是一个堆叠的LSTM在时间上展开。最后,这些LSTM的输出就是你做出的预测。因此,你可以每帧都有一个预测,或者忽略所有这些,只在最后一个时间步做出预测。
这就是结合CNN与RNN的方式。如何学习这样的网络呢?请记住,我们在这里看到的每个组件都是可反向传播的。我们知道如何通过RNN、LSTM和CNN进行反向传播。因此,给定最后的交叉熵损失或每个时间步的交叉熵损失之和,我们可以计算反向传播的误差,即该误差相对于CNN或LSTM中每个权重的梯度。CNN是用于每个图像的相同模型。
视频理解的应用领域 🌐
有了这些方法(双流CNN、CNN+RNN或3D CNN),我们可以解决哪些视频理解问题呢?如果你查看像UCF 101这样的数据集(一个非常流行的视频理解数据集),你可以执行动作识别,例如判断人是否在做俯卧撑、攀岩、蹦床跳跃等。或者,如果你有另一种数据集,例如另一个流行的活动理解数据集Hollywood in Homes,你也可以执行动作识别,例如对着书微笑等。或者,你还可以执行句子预测,例如为整个视频生成字幕或类似问题。
视频理解还有更多问题可以解决,例如动作预测:如果你有一段体育视频,并且看到了迄今为止的一系列事件,接下来会发生什么?例如,在水球比赛中,根据迄今为止看到的动作序列,球员接下来会把球传给谁?或者在篮球比赛中?目标跟踪是另一个重要的视频理解任务。动态场景理解:迄今为止,我们已经看到了给定场景可以将场景分类为室内/室外、早晨/夜晚、城市/乡村等的方法,使用的是图像级分类方法。我们知道CNN,给定数据和交叉熵损失,我们可以学习一个场景分类模型。但是,如果你想理解一个动态场景、一个动态视频,那么我们必须转向视频理解模型,如3D CNN、双流CNN或CNN+RNN来解决这样的问题?人们还可以考虑其他问题,例如时序动作分割等,这将是一种无监督的方法,也是视频理解中的重要问题。
你的作业是阅读这篇关于大规模整体视频理解的教程,它应该能让你更清楚地了解视频理解中的更多任务,以及如何调整这些架构来解决这类问题。这个链接还很好地列出了该领域的论文。
总结与思考题 💡
本节课中,我们一起学习了利用CNN和RNN进行视频理解的几种核心方法:3D CNN、双流CNN以及CNN与RNN的结合。这些方法使我们能够处理视频中的时空信息,解决动作识别、目标跟踪、动态场景理解等问题。

我们将以一个非常有趣的问题结束本周的课程:如果你在正常视频上训练一个模型,然后在测试时输入一段反向播放的视频进行推理,会发生什么?它会工作吗?还是不会?思考一下,我们下次再讨论。
107:软注意力和硬注意力 - 图像字幕

在本节课中,我们将要学习图像字幕任务,这是一种让计算机为图像生成自然语言描述的技术。我们将从回顾自动编码器与PCA的联系开始,然后深入探讨如何结合卷积神经网络和循环神经网络来实现图像字幕,并重点介绍使用注意力机制(包括软注意力和硬注意力)来提升模型性能的先进方法。
自动编码器与PCA的联系
上一节我们介绍了自动编码器作为一种降维技术。本节中我们来看看它与主成分分析的联系。
答案是肯定的。让我们尝试理解这种联系。如果你以特定方式对数据进行归一化,其中归一化数据 x_hat 由公式 1/√m * (x - 该维度均值) 给出。这个归一化步骤使每个维度的均值为零,这是一个标准的归一化步骤。当你进行这种归一化时,协方差矩阵由 x^T * x 给出,即协方差为 1/m * x_hat^T * x_hat。这是因为在计算 x_hat 时,你引入了 1/√m 这个因子。
给定这个预处理,让我们尝试观察自动编码器的损失。自动编码器的损失,假设其参数总结为 θ,是重建输出与原始输入之间的均方误差。我们也可以将这个损失用 X 表示为 ||X - h * W*||_F^2。这里,Frobenius范数 ||A||_F 由矩阵 A 所有元素的平方和给出。h 在这里表示自动编码器中间层(瓶颈层或上下文向量)的表示。W* 是解码器的权重,因此 W* * h 是从该中间表示重建的输出。X 是原始输入。这两种表示对于损失函数是等价的。
我们为什么要这样写?让我们进一步看看。我们知道,以这种方式书写的损失函数,h * W* 可以使用奇异值分解写为 U * Σ * V^T。在这里,我们只考虑 U 和 V^T 中具有非零奇异值的 K 列。所以,我们将 h * W* 写成这样,这来自于 X 的SVD分解。如果这确实是解,那么一种可能的因式分解可能是:h 是前两项,即 U * Σ,而 W 是 V。这是一种可能的因式分解,让我们将其视为神经网络的一种可能结果,并在此基础上继续。
如果我们考虑 h 为 U * Σ(仅考虑前 K 列),那么我们可以将 h 写为 h = X * X^T * (X * X^T)^(-1)。我们知道这是恒等变换,即取 h 的展开并乘以恒等矩阵,恒等矩阵由 X * X^T * (X * X^T)^(-1) 给出。现在,根据 X 的SVD,我们可以将 X^T 写为 V * Σ^T * U^T。记住,如果 X = U * Σ * V^T,那么 X^T = V * Σ^T * U^T,这就是我们在这里写的。X 写为 U * Σ * V^T,X^T 写为 V * Σ^T * U^T,然后求逆。
观察这个表达式,你可以看出,再次根据SVD的定义,V^T * V 应该是单位矩阵,因为在SVD中 V 是一个正交矩阵,V^T 等于 V^(-1),因此 V^T * V = V^(-1) * V = I。所以当这个项 V^T * V 出现时,它坍缩为单位矩阵 I,你剩下 U * Σ * Σ^T * U^T。
你也可以将其写为:第一个 U 提出来,然后有 (Σ * Σ^T)^(-1),再乘以 U^T。这是怎么发生的?这来自于这个矩阵的逆。记住,如果你有矩阵 A * B * C 的逆,那就是 C^(-1) * B^(-1) * A^(-1)。现在,U^T 的逆就是 U 本身。(Σ * Σ^T)^(-1) 保持不变,而 U 的逆再次是 U^T。因为 U 是正交矩阵,你可以交换逆和转置。现在,U^T * U 可以坍缩,因为正交性意味着 U^T * U = U^(-1) * U = I,即单位矩阵,所以这部分坍缩,剩下其余项。
再次,这里 (Σ * Σ^T)^(-1) = (Σ^T)^(-1) * Σ^(-1),这就是我们在这里写的。现在你有 Σ * (Σ^T)^(-1),这将是单位矩阵并坍缩。所以你剩下 X * V * Σ^(-1) * U^T * U。再次,U^T * U,其中 U 受限于前 K 列,将给出受限于前 K 列的单位矩阵 I,这就是我们在这里写的。
因此,进行到最后一步,Σ^(-1) * I(受限于前 K 列)将是受限于前 K 列的 Σ^(-1),而 Σ^(-1) * Σ_kk 将是受限于前 K 列的单位矩阵 I。这可以写为 X * V(受限于前 K 列)。
为什么这个推导很重要?这现在告诉我们 h 是 X 的一个线性变换,而 W 在这里是受限于前 K 列的 V。这意味着什么?从SVD中我们知道,给定一个矩阵 X 及其SVD U * Σ * V^T,我们知道 V 是 X^T * X 的特征向量矩阵。回顾课程中的作业0,如果不了解这一点,请复习基础知识。所以,这意味着我们的编码器权重(由 V 给出)是协方差矩阵的特征向量。
这告诉你什么?它应该让你想起PCA,其中投影矩阵就是协方差矩阵的特征向量。那么,自动编码器总是PCA的变体吗?或者它们在学习模型后就变成了PCA?并非完全如此。这种情况发生在你的编码器和解码器都是线性的时候。记住,在整个例子中,我们没有讨论在编码器或解码器中加入任何激活函数。并且你必须按照我们看到的方式对输入进行归一化,并且你必须使用均方误差作为损失函数。在这些条件下,自动编码器的编码器所引导的映射确实是主成分分析的投影矩阵。
图像字幕任务介绍
在回答了这个问题之后,让我们回到本讲座的主题:图像字幕。假设我们想描述一张图像,例如你在这里看到的,或者你在这里看到的,或者你在这里看到的。作为人类,我们能够用文字描述这些图像。我们如何确保计算机或机器学习模型也能做到同样的事情?我们该如何着手?
理想情况下,我们显然需要一些能够处理图像并给出输出的方法。因此,给定一些图像和描述这些图像的字幕,我们将它们作为输入提供给某个方法。该方法现在接收一张新图像并给出输出,例如“狗正在看向窗外”。现在,我们该如何着手设计这种方法?
我们查看图像并使用基于视觉的深度学习模型。我们查看文本并使用基于自然语言处理的深度学习模型,在这种情况下通常是循环神经网络。因此,在这种情况下,计算机视觉部分将由卷积神经网络替代,自然语言处理部分将由循环神经网络替代,你将它们结合起来以获得最终的字幕输出。
基础模型:CNN与RNN的结合
让我们详细看看这些组件。假设我们有这样一个模型的训练阶段。你有一张图像,并且你知道对应的字幕是“草帽”。你如何训练一个相关模型?你取图像并将其作为输入给一个CNN。你取文本并用它来训练一个RNN。然而,这里重要的是能够学习你的CNN和RNN之间、图像和文本之间的交互。我们如何做到这一点?我们在上一讲中简要提到了这一点。
你丢弃CNN最后的分类层。取CNN输出中的一个表示,并将其也作为输入给RNN。那么RNN现在发生了什么?早些时候,你的RNN是这样的,其中隐藏状态 h_n 由某个公式给出,比如如果你使用LSTM,那么它将是 max(0, W_x * x_n) 的形式,这来自于ReLU激活函数,W_x 是权重矩阵,x_n 是你的输入。但现在,你不仅要 max(0, W_x * x_n),还要加上一个新的权重矩阵 W_Ih * V,其中这个 V 向量来自CNN的输出。
在测试时间会发生什么?给定一张不同的图像,这张图像作为输入给CNN。CNN的FC7表示与一个开始标记一起作为输入给RNN。记住,对于RNN开始输出过程,你必须给出某个起始标记。在那里没有单词,你只需在你的词汇表中考虑,除了词汇表中的所有单词外,你还有两个词,称为开始标记和结束标记。开始标记是你将给RNN第一个时间步的输入。因此,RNN接收开始标记以及输入图像的FC7表示。这些共同产生某个输出 y_0。然后那个 y_0 被用作 x_1 来生成一个新单词 y_1,而 y_1 被作为输入 x_2,这又生成一个新单词。如果最终单词是结束标记,你就知道你已经完成了单词的生成,在这种情况下就是“草帽”。
生成单词意味着什么?RNN在实践中、在实现中是如何生成单词的?它实际上并不生成单词。你有一个词汇表,可能是一千个甚至更多的单词集合。RNN预测一个在你的词汇表上的softmax分布,你选择概率最高的单词。根据softmax从词汇表中选择该单词时,你可以改变一些方式,但最标准的方法是选择在你的softmax向量中概率最高的单词。从这个意义上说,所有这些都被视为分类问题。在这种情况下,不是图像类别,而是你有一个词汇表,RNN预测词汇表中每个单词的softmax分布。
那么开始标记是什么?如果你的词汇表中有1000个单词,开始标记和结束标记将是两个额外的单词,所以你现在会有1002个单词。当你给出开始标记时,你会在那个特定位置设置为1,而在102维向量中的其他所有位置设置为0。这也被称为独热向量,其中当前活跃的单词被赋予1,其他每个单词被设置为0。这就是这种RNN在实践中的实现方式。
回到字幕生成,给定图像,你得到一个FC7表示。显然,你也可以取更早层的表示。这作为输入给RNN,RNN生成短语“草帽”。
以下是这种方法的一些结果。你可以在这里看到,对于这张图像,该方法预测“一群人站在房间里拿着遥控器”,这是一个相当好的字幕。“一个小男孩拿着棒球棒”,再次,一个相当好的字幕。“一头牛站在街道中间”,再次,一个相当好的字幕。对数概率在某种意义上显示了你在该特定测试实例上获得的损失。
另一方面,这里有一些这种模型的失败案例。在这个特定案例中,对于这张图像,模型预测“一个男人站在墙上的钟旁边”。对于这张图像,模型预测“一个小男孩拿着棒球棒”。对于这张图像,模型预测“一只猫坐在沙发上拿着遥控器”。所有这些字幕都是错误的,但人们可能能理解方法失败的原因。它相当接近输出,但可能被图像中的一些元素以及元素之间的交互误导了。
还有一些更明显的失败案例。所以你在这里看到一张图像,字幕是“一个女人在镜子前拿着泰迪熊”,或者对于这张图像,你有“一匹马站在路中间”。对于这些情况,很难证明方法失败的原因。那么,我们怎样才能做得更好?我们如何超越仅仅使用CNN和RNN来提高图像字幕的性能?
引入注意力机制
我们已经知道主题,我们已经知道答案:我们想使用注意力来帮助提高性能。现在让我们更详细地看看如何使用注意力来改进这种方法的性能。
这种方法基于一篇名为“Show, Attend and Tell”的论文,该论文发表在2015年的ICML上,是一种非常流行的图像字幕方法。在这种方法中,你有一张输入图像。你提取某个特征图。记住,这里我们在上一讲中也提到过,你不考虑FC7层,因为如果你需要注意力,你必须保留表示的空间属性,所以你提取某个卷积层的输出,在这种情况下,比如一个14x14的特征图。
然后你有一个带有注意力机制的RNN在这个特征图上。最后你得到逐词生成,例如“一只鸟飞过一片水域”。现在让我们尝试看看在这个特定方法中,带有注意力的RNN是如何执行的。
给定一张输入图像,其尺寸为 H x W x 3。你将其作为输入给一个CNN,特定卷积层后的特征图,让我们假设是 L x D,其中 L = W x H。现在,正如我们所说,我们将其作为输入给RNN的第一个隐藏状态 h_0。所以,h_0 除了让我们假设一个开始标记外,还接收这个特征图作为输入,并且 h_0 这次给出一个在位置上的分布。回想一下,我们刚刚说过RNN输出一个在词汇表上的分布。这次我们将要求RNN生成一个在 L 个可能位置上的分布。这些位置是什么?这些是特征图中每个网格的位置。所以我们要问RNN:你能给我一个softmax向量吗?该向量在这些网格位置中的每一个上分配注意力。
我们用这个 a_1 做什么?a_1 是一个softmax向量,其长度将是你拥有的网格位置数量,即 H x W,我们在这里将其表示为一个单一的值 L。现在我们将使用那个 a_1 来加权特征图中每个位置,这成为特征的加权组合,我们将其表示为 z_1。z_1 由 ∑ a_i * v_i 给出,记住,v_i 是来自特征图输出的每个网格位置内的特征。那个 z_1 现在与第一个单词或开始标记一起成为RNN的输入。现在,h_1,即RNN中下一个时间步的隐藏状态,给我们两个输出:它给出 a_2,这再次是一个在 L 个位置上的分布;以及 y_1,这是一个在词汇表上的分布,它告诉我们预测的单词是什么。
我们用这两个做什么?a_2 再次与特征图相乘,给我们一个新的加权图像特征图表示 z_2。而 y_1 是生成的单词,可以作为输入 y_2。或者,在训练时,你可以直接将字幕的第二个单词作为输入 y_2 给RNN的下一步。所以,z_2 和 y_2 再次作为输入给RNN中下一步的隐藏状态,这再次生成 a_3 和 y_2。a_3 是在 L 个位置上的分布。y_2 是在词汇表上的分布,告诉你单词是什么。a_3 告诉你关注图像的哪一部分。这个过程一次又一次地重复,字幕就生成了。
正如我们在上一讲中所述,有两种方法来实现这个注意力模型:软注意力和硬注意力。
在软注意力中,权重向量 a_1 是一个softmax向量。你用它作为权重乘以特征图中的每个网格位置,所以你得到 z_1 为 a_1 * v,v 取决于你查看的是哪个条目,例如 a_1_i * v_i。这给了你一个软注意力或整个图像的特征图。
那么硬注意力是什么?硬注意力是当你查看那个特定的 a_1 softmax向量时,选择一个获胜的条目,并且只使用那个网格位置作为下一个时间步的输入。这被称为硬注意力,因为你没有使用跨图像所有部分的权重软分布。你将选择一个获胜的条目,并且只选取图像的一部分,也许这里的一个特定网格位置,也许只有这个位置作为输入 z_1 给下一个图像。
有优点和缺点吗?是的,确实。硬注意力在这一步需要一个 argmax,因为你想考虑softmax向量中获胜的位置。而 argmax 是不可微的,所以你可能无法使用反向传播来解决硬注意力方法。那么它是如何实现的呢?它是使用强化学习方法实现的,我们此时不会关注这些方法。
但同时,软注意力方法是网格所有位置的加权组合,这是确定性的,可以通过反向传播,因此可以通过反向传播学习。那么你如何使用反向传播学习这样的网络呢?记住,你有一个在位置上的分布,你有一个在词汇表上的分布。对于词汇表上的分布,你知道要预测的单词,所以你会在那里有一个交叉熵误差。类似地,对于RNN的每个时间步,你都会有一个交叉熵误差,使用这个误差,你可以学习RNN中的所有权重,如果需要,你还可以将该误差反向传播回CNN,以微调CNN的权重。但这里的损失将是每个时间步交叉熵损失的总和。
注意力机制的可视化与结果
这里是软注意力与硬注意力的可视化。你可以在这里看到,在软注意力中,你将注意力集中在图像的不同部分。当方法生成“一只鸟飞过一片水域”时,你可以看到,最初当它看“鸟”时,它的注意力就在那里;当它生成单词“鸟”时,它的注意力就在那里;逐渐地,当它开始生成“一片水域”时,它看的是鸟以外的所有东西。如果你看这部分,鸟是黑色的,图像的其余部分是白色的,这意味着注意力建议模型查看鸟以外的所有东西来生成单词“一片水域”。
在硬注意力的情况下,记住,模型只关注一个图像块。当涉及“鸟”时,它确实关注图像的某个部分,该部分对应于鸟。当涉及“一片水域”时,它确实关注图像的某个部分,该部分对应于水域。但定位可能不那么明显,因为我们只选择了一个单一的部分,即 a_1 向量softmax的获胜者。
以下是基于这篇名为“Show, Attend and Tell”的论文,使用注意力进行图像字幕的一些结果。在这种情况下,你可以看到,对于这张图像,模型生成的字幕是“一个女人正在公园里扔飞盘”,你可以看到,当生成单词“飞盘”时,注意力完全在飞盘上。
这里还有几个例子:“一只狗站在硬木地板上”。当模型生成“狗”时,注意力完全在狗身上。“一个停车标志在一条有山背景的路上”,当它生成单词“停车标志”时,注意力在停车标志上,依此类推。
这里的最后一张图像是一个具有挑战性的图像,生成的字幕说“一只长颈鹿站在有树木背景的森林中”,当它生成单词“树木”时,它查看了长颈鹿以外的所有东西,这证实了在这个特定上下文中的选择。
图像字幕的近期进展
就近期工作而言,“Show, Attend and Tell”是2015年发表的一篇论文。更近期的努力从几个不同的方式探索了图像字幕,让我们看几个不同的例子。
在2017年的一项工作中,想法是看看图像字幕的性能是否可以通过在LSTM中使用属性来提升。所以,如果你有一张图像 I 和一组构成字幕 W 的单词。那么 x_{-1} 是图像通过某种变换 T_V 得到的,这可能只是一组权重。而每个时间步的输入 x_t 是字幕中每个单词的变换。
所以,这是我们迄今为止见过的图像字幕的朴素方法,其中图像被处理,然后给LSTM的第一步。字幕的每个单词在展开的LSTM的每个时间步作为输入给出。而隐藏状态 h_t 使用某个函数 F 处理每个时间步的输入。这是使用图像表示和字幕单词训练模型的朴素版本。
因此,这项特定工作尝试了几种不同的变体,让我们看看每种变体。
在其中一个变体中,我们将其表示为 A1,他们考虑将图像转换为属性。所以,给定一张图像,你可以使用一个图像分类器,或者你可以使用可能作为元数据与图像一起提供的属性。与这张图像相关的属性是“船”、“水”、“男人”、“骑”、“狗”、“小人”和“河”,具有不同的概率。现在,这些成为一组属性,一个属性向量,来自一组属性。在这种方法中,你给第一个隐藏状态的输入 x_{-1} 是属性,而不是图像。所以图像不作为输入给你的字幕模型。只有你从这张图像中得到的这些属性。为什么我们要这样做?因为无论如何输出都是字幕,所以你不如给一个图像的文本版本作为输入。其余部分保持不变。这是变体 A1。
在第二个变体 A2 中,图像和属性都作为输入给图像字幕模型。所以图像首先给RNN的LSTM的一个步骤,在随后的时间步,属性给LSTM。所以你有一个基于图像的 x_{-2} 和一个基于属性的 x_{-1}。再次,T_V 和 T_A 是由学习到的权重给出的变换,所以你可以想象 T_V 在这里是一组权重,T_A 是一组权重,这些权重在你从输出反向传播时学习。还有其他权重需要学习。所以这是一个称为 A2 的变体。
A3 是一个变体,其中属性先给出,图像后给出。它是 A2 的一个变体,其中输入被交换:先属性,后图像。
然后来了一个变体 A4,在 x_{-1} 只给出属性。但在每个时间步,你在每个时间步同时给出字幕中的单词和图像作为输入。所以,最初是属性,每个时间步是图像。这是变体 A4。
A5 是一个变体,其中图像在 x_{-1} 作为输入给出,而属性与字幕一起在每个时间步给出。这是变体 A5。
因此,这项由Yao等人发表在ICCV 2017上的特定工作得出结论:变体 A1 优于朴素LSTM。这表明使用高级属性而不是图像表示的优势。变体 A2,其中图像表示也与属性集成,有助于进一步提高性能。类似地,通过先馈送属性后馈送图像,A3 的表现优于 A2。
然后出现了一个意外:A4 的表现不如 A3。回想一下,如果你回去看看 A4 是什么,A4 是你在 x_{-1} 提供属性,并在每个时间步将图像作为输入。不幸的是,这表现得不太好。一个可能的假设是,为什么它表现不佳,是因为图像中的任何噪声现在都可能累积在RNN的每个时间步中,因此模型可能遭受一些过拟合,并且没有很好地学习图像和文本之间的关系。
然而,当这在 A5 中被交换时,即你在 x_{-1} 给出图像,并在每个时间步给出属性,现在这个模型的表现优于 A3。这给出了最终的结论:图像表示和属性可以是互补的,并且两者都可以帮助提高图像字幕的性能,具体取决于你如何在模型中使用它们。
风格化字幕与检测辅助字幕
另一个有趣的图像字幕方法被称为StyleNet,它试图改变字幕的语气。所以,如果你有一张图像,原始字幕是“一个男人在岩石山坡上,旁边是一堵石墙”。现在可以给出一个浪漫版本,说“一个男人用攀岩来征服山峰”。或者可以给出一个幽默版本,即“一个男人像蜥蜴一样爬岩石”。你如何改变字幕的语气?为此,回想我们标准的LSTM,我们有输入门、遗忘门和输出门,那就是我们的LSTM。
因此,这种特定方法建议改变LSTM的权重矩阵,其中它建议权重矩阵 W_x 特别可以写为 U_x * S_x * V_x,这类似于SVD分解,所以他们称之为因式分解的SVD。这有什么帮助?给定一张输入图像和一个字幕,你通过CNN发送图像,然后你现在使用一个因式分解的SVD,其中权重 W_x 现在对于事实性字幕由 U * S_F * V 给出。对于浪漫字幕,那个权重 W_x 将是 U * S_R * V。对于幽默字幕,那将由 U * S_H * V 给出。这些矩阵 U、S_F、S_R、S_H 和 V 在训练模型时学习,但现在在测试时,我们可以相应地交换 S_x 以获得字幕上所需的效果。所以在测试时,给定一张这样的图像,你可以有一个事实性字幕,陈述“一个滑雪板在空中”。或者一个浪漫字幕,说“一个男人在滑板上做一个技巧来展示他的勇气”。或者一个幽默字幕,即“一个男人在滑雪板上跳跃以到达外太空”。语气可能不完全令人信服,但它给出了一个想法,即如何通过分解LSTM,然后尝试改变中间的 S 矩阵来获得字幕中不同的语气。这就是StyleNet。
另一项近期工作被称为Neural Baby Talk,来自2018年的CVPR,它再次为图像字幕的执行方式提供了不同的视角。因此,在更传统的计算机视觉意义上,在深度神经网络出现之前,为了能够生成字幕,需要两种特定类型的方法,称为可变形部件模型和条件随机场。我们现在不会深入这些细节。但可以假设,使用这些基于图形模型的方法,你可以说“这是一张有一只狗和一个蛋糕的照片”,也许字幕在那之后继续。
在更自然的意义上,此时基于深度神经网络,给定一张图像,你将其提供给一个CNN,然后是一个RNN,然后你得到“一只狗坐在沙发上拿着玩具”。现在,我们能否介于两者之间?这就是这种方法试图做的。Neural Baby Talk 说,让我们尝试使用检测模型来获得更好的字幕生成性能。
所以给一张图像。你有一个检测器。你得到你的区域特征。你将这些区域特征给一个RNN以获得你的最终生成,它陈述“一个[框]与一个特定的[框]坐在一个[框]与一个[框]”,其中每个这些框是不同的边界框或区域提议。第一个黄色框是一只小狗,第二个是一条领带,绿色的是一个蛋糕,蓝色的是一个桌子。所以“一只系着领带的小狗坐在一张有蛋糕的桌子旁”。如果你在这里使用检测,就会这样做。
让我们更详细地看看这个模型。在训练阶段,你会看到,给定一张图像,使用一个物体检测器,如Faster R-CNN模型或Mask R-CNN模型,你会得到一组区域提议。如果你使用来自Mask R-CNN的ROI Align,你会得到一组对齐的区域提议。类似地,你在训练数据中也有一个给定的字幕。你转换它,通过嵌入获得该字幕的某种表示。我们所说的嵌入是指通过学习到的权重获得的该字幕的表示,这些权重通过反向传播获得。这些嵌入与每个这些区域提议一起给一个带有注意力的RNN。
然后你有两部分:一部分是这里的底部,它包含一个softmax并给你文本输出,即字幕部分,你将用于推理或测试时间。第二部分是使用带有注意力的RNN,你决定当前关注字幕中的哪个单词,你取所有这些区域提议,一起构建一个向量。然后将其给一个模型,该模型预测图像中该特定区域的标签。因此,通过结合区域和文本预测,可以预测单词,并且还可以说出图像的哪一部分在预测该单词。
总结与展望
本节课的作业是阅读相应幻灯片上的论文。如果你没有理解每一篇论文,这没关系,本讲座旨在让你了解不同的图像字幕方法。如果你除了我们这里的作业之外还想获得一些实践经验,佐治亚理工学院发布了一个关于图像字幕的不错的笔记本。
本节课的问题是:我们现在能做相反的事情吗?我们能从字幕生成图像吗?与我们在这节课中学到的相比,我们如何做到这一点?思考一下,我们下次讨论。

在本节课中,我们一起学习了图像字幕的基本原理,从CNN与RNN的结合,到引入软注意力和硬注意力机制以提升模型性能。我们还探讨了近期的一些进展,如使用属性增强LSTM、风格化字幕生成以及结合物体检测的字幕方法。这些技术展示了如何通过结合计算机视觉和自然语言处理的最新进展,让机器更好地理解和描述视觉世界。
108:超越字幕 - 视觉问答与对话

在本节课中,我们将学习计算机视觉与自然语言处理相结合的两个高级任务:视觉问答和视觉对话。我们将了解这些任务的定义、所需的数据集、解决问题的基本模型架构,以及如何评估这些系统的性能。
概述
循环神经网络和注意力模型在计算机视觉中的重要性,在处理视觉与语言结合的问题时变得尤为突出。这种结合产生了许多序列学习问题。我们在上一讲中看到了图像字幕生成。现在,我们将更进一步,讨论视觉问答和视觉对话等任务。
从字幕到图像生成
一个遗留的问题是:我们能否进行与图像字幕生成相反的任务,即从字幕生成图像?使用我们目前见过的模型,我们无法做到。但很快我们将看到生成模型,并了解如何使用它们来完成从字幕到图像的生成。
视觉问答任务概述
视觉问答任务可以看作是图像字幕生成任务的扩展。任务概述如下:给定一张图像和一个问题,目标是让深度学习模型给出合理的答案。
以下是该任务的一个演示:给定一张图像和问题“孩子坐在哪里?”,一个训练良好的模型会以99%的置信度回答“水槽”,这与图像内容相符。这个演示来自佐治亚理工学院的Cloud CV网站。
在讨论解决VQA问题的模型之前,我们先了解所需的数据集类型。
视觉问答数据集
解决VQA问题需要复杂的数据集。以下是几个重要的数据集:
VQA数据集:于2015年发布,是首个VQA数据集。它包含图像和开放式问题,答案以多项选择形式提供。该任务对模型而言变成了分类问题。数据集包含约25万张图像(来自MS COCO数据集)和5万张抽象图像,总计约75万个问题和1000万个答案。
COCO-QA数据集:同样于2015年发布,利用MS COCO图像字幕数据集自动生成问答对。答案均为单个单词。数据集包含约11.8万个问答对和12.3万张图像,问题类型包括“什么物体?”、“多少?”、“什么颜色?”和“在哪里?”。
Visual 7W数据集:于2016年发布。名称中的“7W”代表七类问题:什么、哪里、何时、谁、为什么、如何以及哪一个。该数据集包含“讲述”和“指向”两种任务。共有约32.8万个问答对和4.7万张图像。
CLEVR数据集:于2017年发布,是一个半合成数据集,旨在回答复杂的推理问题。问题可能非常复杂,需要仔细解析。数据集包含约10万张渲染图像和100万个自动生成的问题,答案均为单个单词。
了解了用于视觉问答的数据集后,我们现在来看看为解决VQA问题而开发的一些模型。
视觉问答模型
一个显而易见的基线模型是结合LSTM和CNN图像特征。
给定一张图像,通过CNN(例如VGGNet)得到全连接层表示(例如1024维)。同时,将问题的每个单词输入LSTM的不同时间步,LSTM的输出也是一个向量(例如1024维)。将这两个向量连接起来,通过几个全连接层,最终预测答案。如果答案在一组选项中,则使用交叉熵损失,并通过反向传播学习网络所有权重。
2015年开发的另一个基线模型更简单:从CNN某一层提取图像特征,对问题使用简单的词袋频率直方图作为文本输入。将两者连接后,通过一个简单的神经网络预测正确答案。
在了解了这些简单的基线模型后,我们来看看如何通过更复杂的模型进行改进。
2016年CVPR上提出的堆叠注意力网络是一个方向。在该模型中,给定图像,从CNN获取表示。类似于之前讨论的注意力模型,我们取某个卷积层的特征图,将其映射到原始图像的空间位置。现在,图像的每个部分都有对应的特征向量。将问题输入LSTM,LSTM的输出给出问题的表示。该问题表示与图像不同部分的特征向量相结合,通过注意力机制生成图像不同部分的注意力图。基于此注意力图和问题表示,在图像特征向量上执行另一层注意力,得到最终表示,并通过输出层的softmax在答案选项中给出最终结果。
可视化显示,第一层注意力关注问题中提到的所有概念,第二层注意力则缩小范围,只关注包含答案的图像部分,从而预测出答案。
这引出了一个后续问题:在我们刚才看到的模型中,我们只对图像执行了注意力。我们原样使用了问题表示,并尝试基于注意力建立问题表示与图像不同部分之间的关系。那么,我们能否在问题中也引入注意力呢?
2016年ECCV上提出的分层协同注意力模型实现了这一点。在该模型中,我们同时关注图像的部分和问题的部分。“分层”体现在我们首先获取问题的词级嵌入,然后是短语级嵌入,最后是整个问题级别的嵌入。
具体来说,给定一个问题,我们提取其词级、短语级和问题级嵌入。在每一级,我们都应用协同注意力,即同时关注问题本身和对应的图像。最终答案基于所有协同注意后的图像和问题特征。
例如,对于问题“红绿灯上亮着的是什么颜色?”,模型在词级关注“stop”,在图像上关注交通灯区域;在短语级关注“the stop light”,图像关注更集中在红绿灯上;在问题级关注“light”,图像则只关注交通信号灯内的灯光,从而得出答案“绿色”。
为了获得短语级嵌入,可以对字幕中每个单词的嵌入执行具有不同宽度滤波器的卷积,得到向量表示。然后对卷积层输出执行最大池化,并将该最大池化输出输入LSTM,从而获得问题级表示。
视觉对话任务概述
现在,我们转向本节课计划讨论的第二个任务,可视作视觉问答的扩展,即视觉对话。
任务概述如下:给定一张图像和一个初始字幕,视觉对话类似于聊天机器人,但这是一个针对图像进行问答的聊天机器人,而非简单的自由文本聊天。
例如,给定图像和字幕“一只猫正在用咖啡杯喝水”,对话可能如下:问“杯子是什么颜色?”,答“白色和红色”。问“杯子上有图案吗?”,答“没有,好像有什么,但看不清”。问“杯子和猫在桌子上吗?”,答“是的”。问“桌子上还有其他物品吗?”,答“有,杂志、书、烤面包机、篮子和一个盘子”。
这是一个演示:给定图像,机器人说“一个男人和一个女人拿着伞”。人类问“他的伞是什么颜色?”,机器人答“黑色”。人类问“她的呢?”,这是一个棘手的问题,因为模型需要理解问题指向伞,并且是“她的”伞,这意味着模型需要知道有两个人,一男一女,然后才能回答。机器人答“她的伞是彩色的”。人类问“图像中还有其他人吗?”,机器人答“我想有三个,他们被遮挡了”。
视觉对话任务的形式化定义与评估
如何形式化并解决这个任务?给定图像 I,对话历史(一系列问答对 Q1 A1, Q2 A2, …, Q_{t-1} A_{t-1}),以及当前的后续问题 Q_t,任务是生成一个自由形式的自然语言答案 A_t 来回答当前问题 Q_t。
评估答案可能很棘手。自然语言处理中常用的评估指标(如BLEU、METEOR、ROUGE)与人类判断的相关性往往不高。因为一个问题可能有多种不同表述的正确回答。
一种评估方法是进行人类图灵测试,即给出生成的句子,让一组人类评估其质量,并判断答案是由机器还是人生成的。但这种评估可能成本高昂且主观。
在视觉对话数据集中,常见的评估方法是:给定图像、对话历史和当前问题,提供大约100个候选答案选项(可能来自50个最相似问题的答案、30个热门答案和20个随机答案)。模型的任务是对这些答案选项进行排序。模型可以输出一个在100个选项上的softmax概率向量,并据此排序。
评估性能的指标可以是:
-
平均排名:在所有测试样本中,正确答案的平均排名。排名越靠前越好。
-
平均倒数排名:对于每个测试问题,计算正确答案排名的倒数,然后对所有问题取平均值。如果正确答案总是排名第一,MRR为1;如果排名靠后,MRR接近0。
视觉对话模型
视觉对话模型通常采用编码器-解码器框架。编码器可以是:
-
晚期融合编码器:分别用CNN处理图像,用LSTM处理当前问题和对话历史,然后将它们的输出表示连接起来。
-
分层循环编码器:图像通过CNN,当前问题通过一个LSTM(该LSTM也接收图像表示)。对话历史的每一轮通过一个LSTM实例,得到不同表示。然后用另一个LSTM结合历史表示、当前问题表示和图像表示,输出给解码器。
-
记忆网络编码器:使用注意力机制。将当前图像-问题表示与对话历史中每一轮的表示进行对齐(例如通过内积计算注意力权重),得到一个加权的历史表示,再与当前特征结合后传递给解码器。
解码器可以是生成式或判别式:
-
生成式解码器:在训练时,最大化生成真实答案序列的对数似然(通常通过多时间步的LSTM和交叉熵损失实现)。在测试时,使用模型在词汇表上的对数似然分数对候选答案进行排序。
-
判别式解码器:计算输入编码与每个候选答案编码(例如通过词嵌入获得)的点积相似度,然后通过softmax计算每个选项的后验概率。在训练时,最大化正确选项的对数似然。在测试时,根据后验概率对选项进行排序。
总结
本节课我们一起学习了计算机视觉与自然语言处理交叉领域的两个高级任务:视觉问答和视觉对话。
我们首先了解了视觉问答的任务定义,并回顾了用于训练和评估的多个重要数据集,如VQA、COCO-QA、Visual 7W和CLEVR。接着,我们探讨了解决VQA问题的模型演进,从简单的CNN-LSTM基线模型,到引入图像注意力的堆叠注意力网络,再到同时关注图像和问题不同层次的分层协同注意力模型。
随后,我们将讨论扩展到视觉对话任务。我们学习了如何形式化定义该任务,并了解了其独特的评估挑战和方法,如基于排序的平均排名和平均倒数排名。最后,我们介绍了用于视觉对话的编码器-解码器框架,涵盖了晚期融合、分层循环和记忆网络等编码器类型,以及生成式和判别式解码器。

这些任务展示了如何将我们已学的CNN、RNN和注意力机制等组件灵活组合,以解决更复杂、更接近人类认知的跨模态问题。
110:从Transformer到视觉Transformer 🧠

在本节课中,我们将学习Transformer架构如何从自然语言处理领域扩展到计算机视觉领域,并演变为视觉Transformer。我们将探讨其核心思想、架构设计、与卷积神经网络的对比,以及后续的重要改进模型。
概述
在之前的课程中,我们讨论了自注意力机制和Transformer架构。该架构彻底革新了处理文本模态的NLP任务。此后,Transformer架构已成为处理文本、视觉、语音甚至图数据等多种模态数据的默认选择。在本模块中,我们将重点讨论计算机视觉中的Transformer。在这第一讲中,我们将从Transformer出发,探讨其如何演变为视觉Transformer。
从NLP Transformer到视觉Transformer
上一节我们介绍了Transformer在NLP中的革命性作用,本节中我们来看看如何将其应用于图像数据。
Transformer通过引入基于自注意力机制的新颖架构,革新了NLP任务。这有助于机器翻译、语言理解、文本生成、问答、命名实体识别等多项任务。在最初的Transformer工作之后,又出现了如BERT、RoBERTa、DeBERTa、T5以及整个流行的GPT系列等架构,它们代表了为NLP任务开发的基于Transformer模型的主要贡献。
当我们谈论计算机视觉中的Transformer时,需要注意一点:Transformer编码器通常需要一个向量序列作为输入。在语言应用中,这个序列就是对应于输入中标记的有序词嵌入集合。然而,当输入是图像时,你该怎么做?每个像素都是一个标记吗?如果这样做,内存使用量和计算量会急剧增加。
以下是具体分析:
-
如果你有一张224x224的彩色图像,最终需要存储
3 * 224 * 224的平方个参数,因为对于每个像素,你都需要评估其与所有其他像素的自注意力。 -
这总计约为220亿个参数用于每次自注意力计算。这显然是不可持续的。
那么,我们如何使Transformer适应计算机视觉?这是我们要讨论的第一个核心问题。
视觉Transformer的核心思想 🖼️
上一节我们提出了将Transformer用于图像的核心挑战,本节将介绍其解决方案。
这个问题首次在ICCV 2021的一篇题为《一张图像值16x16个词:大规模图像识别的Transformer》的论文中被提出。他们提出的方案如下:
假设有一张大小为 224 x 224 x 3 的RGB彩色图像。他们建议将图像分割成 14 x 14 个块(即小图像),每个块的大小为 16 x 16。这意味着现在你的图像总共有 196 个块。
现在,每个输入块可以被视为独立的“块标记”,形状为 3 x 16 x 16。然后,他们将每个块投影到一个 D 维向量(我们很快会讨论这个投影涉及什么)。一旦投影到D维向量,他们就添加位置编码,这类似于我们在Transformer中看到的位置编码,但也有一些不同,我们稍后会讨论。
当我们把这些块标记嵌入到一个D维向量并附加位置编码后,它被送入一个与NLP Transformer非常相似的Transformer编码器。其余过程与NLP Transformer在机器翻译等任务中几乎相同。
然而,当你试图使视觉Transformer适应图像分类任务时,你只需要一个输出,而不需要从Transformer解码器输出196个输出标记。如何处理这个问题?他们提出了使用一个额外的、可学习的“分类标记”,它在反向传播过程中也会被更新。这个分类标记也称为 [CLS] 标记。
在输出端,解码器输出一个类似的分类标记。这个分类标记通过一个线性层投影到一个C维的预测类别分数向量,再经过Softmax操作(类似于标准神经网络的最后一层)。然后,取线性投影输出的argmax,通过视觉Transformer给出最终的类别预测。
综上所述,你的视觉Transformer看起来大致是这样的。
位置编码与参数效率
上一节我们概述了视觉Transformer的流程,现在让我们深入一个细节:位置编码。
在NLP Transformer中,位置嵌入是固定的,编码了句子中单词的相对位置。然而,在视觉Transformer中,他们建议将这些块编号为0, 1, 2, 3……但他们发现,简单地将这些数字作为额外的位置编码并没有真正帮助或显著提高性能。因此,他们提出了一种类似表格的结构。
以下是具体实现:
-
对于图像中每个块的位置,都有一个对应的D维向量。
-
这个向量被附加到196个块集合中对应的块上。
-
这些对应于每个位置的D维嵌入也是可学习的,并在反向传播过程中更新。
现在,让我们尝试与之前的情况进行比较。我们说过,如果我们将一个像素作为一个标记,对于大小为 3 x 224 x 224 的图像,我们有220亿个参数。而现在,我们有 14 x 14 = 196 个块标记。由于对于每个块标记,你需要计算它与其他所有块标记的注意力,这总计大约为 196 * 196 ≈ 38,416 个参数,这大约是每个Transformer层150KB。可以看到从220亿到仅约3.8万的显著减少。
视觉Transformer与卷积的关系 🔄
现在你脑海中可能有一个问题:我们现在是在没有卷积的情况下解决计算机视觉问题吗?计算机视觉的历史在很大程度上一直依赖于卷积操作。那么,我们现在是否已经设法在没有卷积的情况下处理计算机视觉任务了?并非完全如此。
以下是卷积在视觉Transformer中的体现:
-
输入层中的线性投影实际上是一个2D卷积,其步长就是块大小本身。
-
此外,Transformer中的MLP层严格来说实际上是
1x1卷积。
因此,卷积正在被使用,只是在Transformer架构中以不同的方式使用。
视觉Transformer的性能与改进
在这项工作中,他们通过实验将视觉Transformer与残差网络进行了比较。事实上,他们提出了几种不同的视觉Transformer变体:Base 变体有12层,Large 变体有24层,Huge 变体有32层。在此范围内,他们还可以将块大小从 14x14 变化到 16x16 或 32x32。请记住,块尺寸越小,模型越大,因为在给定图像内你将拥有更多的块。
他们通过决定在什么数据集上预训练这些模型来与ResNet进行比较。左边的第一个结果显示了在ImageNet数据集上训练这些模型的比较(如你所知,ImageNet有1000个类别,120万张图像)。他们实际上发现,在这种特定情况下,残差网络的表现优于所有这些ViT模型。
然而,当改进预训练时,他们发现:
-
当模型在ImageNet-21K数据集上训练时,ResNet和ViT几乎不相上下。
-
当预训练改进到JFT数据集(JFT-3亿是谷歌内部的一个包含3亿张标注图像的数据集)时,他们发现ViT在ImageNet上的性能超过了ResNet。
因此,结论是:ViT确实需要大量的预训练才能超过ResNet。然而,当你这样做时,ViT需要的训练时间显著减少,并且比ResNet更高效地利用计算资源。人们可以假设,与在前馈网络、MLP或自注意力计算中使用的简单矩阵乘法相比,卷积的成本相当高。这有助于ViT更高效地利用GPU。
视觉Transformer的后续改进
自视觉Transformer首次提出以来,还有其他工作以不同方式改进了ViT的性能。
以下是两项重要的改进工作:
-
《如何训练你的ViT》:这项早期改进提出了在Transformer的前馈层中使用正则化方法,如权重衰减、随机深度或Dropout;或者使用数据增强策略,如MixUp或RandAugment(在一组预定义增强中提供随机增强)。这些有助于提高视觉Transformer的性能。
-
《DeiT:数据高效的图像Transformer》:这项改进提出了使用知识蒸馏。其中,一个通常较大的教师CNN模型被用作训练学生ViT模型的教师。这是使用学生模型的标准交叉熵损失以及学生模型输出与教师模型输出之间的KL散度损失来完成的。KL散度衡量两个概率分布之间的差异。因此,你可以将教师模型和学生模型的输出视为两个不同的概率分布,并计算它们之间的KL散度。为了实现这一点,这项工作还引入了一个“蒸馏标记”,这同样是一个作为输入提供的可学习标记。输出端对应的蒸馏标记用于通过KL散度匹配教师。这项名为DeiT的工作表明,这个想法将ImageNet top-1准确率从77.9%提高到了83.4%。事实上,当他们使用更多的训练周期或更高的图像分辨率时,效果甚至更好。
层次化视觉Transformer:Swin Transformer
如果我们现在比较ViT和标准卷积神经网络(以残差网络为例),我们在课程中观察到的一个现象是:在CNN中,分辨率通常随着你在神经网络中越来越深而降低,而通道数通常会增加。这导致了一种层次化架构。直觉是,这种方法是有用的,因为对象可以出现在多个尺度上,你希望在这种架构的不同层中发现这些不同尺度的对象。
然而,对于ViT,它遵循所谓的“各向同性架构”,这意味着视觉Transformer的每个块都具有几乎相同的架构、相同的分辨率和相同的通道数。那么你可能会问:我们是否因此失去了层次化信息?我们能使ViT具有层次性吗?
这正是后续工作 《Swin Transformer》(发表于CVPR 2021)所做的事情。Swin Transformer试图在视觉Transformer的表征学习中引入不同尺度或层次的概念。
他们是如何做到这一点的?给定一个输入图像,假设为 3 x H x W(3是颜色的通道数,H x W是实际分辨率)。这经过一个块分割和线性嵌入,与我们目前看到的视觉Transformer非常相似。但在此之前,他们将图像分成 4x4 的块并投影到C维。
具体过程如下:
-
如果你的图像是
3 x H x W,他们建议将图像分成3 x 4 x 4的块,即48维(4x4=16, 16x3=48)。 -
现在你的图像变成了
48 x (H/4) x (W/4),因为你将整个图像分成了4x4的块。 -
现在,他们取这个
48 x (H/4) x (W/4)的张量,并线性投影到C维,所以这个48现在变成了C,类似于我们在普通视觉Transformer中看到的线性投影。
现在,这是如何构建的?一旦他们在第一层完成这个操作,他们现在合并 2x2 的邻域,现在块的有效感受野变成了 8x8,这导致了 (H/8) x (W/8) 的总体分辨率。这是如何工作的?你原来有 C x (H/4) x (W/4)。当你合并 2x2 邻域时,现在变成了 (H/8) x (W/8)。但由于合并,每个 2x2 现在被累积,所以这里的每个位置现在有 4C 个通道。然后,2x2 的合并导致 4C,这现在被线性投影到 2C 维,这就是你得到 2C x (H/8) x (W/8) 的方式。
现在,这被逐步实施以再次合并 2x2 邻域,现在块或其原始感受野有效地变成了 16x16,并且你在这一阶段的维度变成了 4C x (H/16) x (W/16)。这个过程进一步重复以获得 8C x (H/32) x (W/32)。现在,这产生了在不同尺度上学习图像的效果,从而获得输入的层次化表征。
Swin Transformer的窗口注意力与移位窗口
现在出现了几个问题。视觉Transformer的原始目标是通过使用 H x W 的标记网格来使自注意力计算高效,但现在你的注意力矩阵将再次大约是 (H*W)^2,这在图像大小上是二次的。如何处理这个问题?为了处理这个问题,Swin Transformer建议将图像分解成 M x M 个标记,并且仅在每个窗口内计算自注意力。
这有什么帮助?现在因为你的图像被分成了 M x M 个标记,所以你总共有 M x M 个窗口。这意味着参数总数将是 M^2。但是,对于每个块,你需要计算它与其他每个块的自注意力,所以你这里将有一个 M^2。然而,每个标记内部的大小由 (H/M) x (W/M) 给出。因此,这总共变成了 M^2 * H * W,这对于自注意力计算来说是图像大小的线性。Swin Transformer在他们的工作中使用 M=7。
你可能问的另一个问题是:因为我们限制自注意力仅在每个 M x M 窗口内,那么你如何捕获图像内的长距离关系?毕竟,自注意力的目的是捕获特定对象相对于图像任何其他部分的上下文。如何处理这个问题?为此,他们引入了“移位窗口注意力”的概念,其中注意力窗口以层次化的方式逐步改变或移动。这使得不同抽象层次的块之间能够进行交互,从而在保持计算可处理性的同时捕获长距离依赖关系。这在图像分类、目标检测和语义分割等应用中至关重要。
他们是如何做到的?在任何一个特定层中,你有这些 M x M 标记或 M x M 部分,你只在每个部分内计算注意力。他们现在在视觉Transformer的连续层之间轮换这些局部窗口的计算方式。在一层中,如果你的自注意力窗口是这样的,那么在下一层,它变成了那样,然后这个过程不断重复。
更全面地看,你在特定的Transformer层中开始,使用层归一化,然后在这里应用你的窗口自注意力,然后进入层归一化模块,接着是MLP模块,然后在下一个Transformer层中,你使用移位窗口自注意力,这个过程在你的整体架构的连续块之间再次重复。
那么,你这里可能再次问一个问题:你的位置编码或嵌入会发生什么?Swin Transformer不像视觉Transformer那样使用绝对位置编码,而是使用学习的偏置(包含在你的自注意力计算中)来编码块之间的相对位置。这个 B 是一个维度为 (M^2) x (M^2) 的学习偏置矩阵,对应于你的块,它给出了你在图像中研究的不同块的相对位置。
通过这一点,他们实际上表明,他们可以在比DeiT等其他视觉Transformer架构更少的时间内(在V100 GPU上每张图像的毫秒数),获得比使用蒸馏的视觉Transformer更高的准确率。
总结与应用展望
本节课中我们一起学习了视觉Transformer及其重要变体Swin Transformer。
虽然我们在这节课中讨论了视觉Transformer和Swin Transformer,但在过去几年中,这两种架构已成为图像分类、目标检测、语义分割等多种视觉任务的通用骨干网络。
以下是具体应用方向:
-
在目标检测方面,我们有一个名为 DETR 的架构,我们将在下一讲中看到。
-
对于分割,我们有非常流行的 Segment Anything 模型,我们也将在本模块中讨论。
-
除此之外,用于视觉语言任务的 CLIP 也使用视觉Transformer。
-
还有其他架构,如用于图像恢复的 SwinIR。

如果你对此更感兴趣,我强烈建议你也查看这些主题的相应参考文献。我们很快将深入探讨用于目标检测的DETR或Transformer的细节。
112:用于检测的Transformer 🎯

在本节课中,我们将要学习Transformer架构如何被应用于目标检测任务。我们将从回顾Transformer在自然语言处理和视觉任务中的成功应用开始,然后深入探讨一个名为DETR的端到端目标检测框架。我们将了解其核心组件、工作原理,以及后续的改进版本如何解决其计算效率等问题。最后,我们会简要介绍当前最先进的检测方法DINO。
概述
在上一节中,我们介绍了Transformer如何通过自注意力机制革新了序列数据建模,以及Vision Transformers如何将这一架构成功应用于图像分类任务。本节中,我们来看看如何将Transformer应用于计算机视觉的另一项核心任务——目标检测。
从传统检测到DETR
我们知道,目标检测任务需要处理大量数据,并为图像中的每个物体预测精细的边界框。现有的方法,如Faster R-CNN和FCN系列,依赖于许多手工设计的组件,例如锚框、非极大值抑制和一些阈值设定。这使得整体架构和框架变得复杂,甚至在某些情况下效率低下。
那么,Transformer能为此做些什么呢?DETR于2020年被提出,它是一个使用Transformer架构的端到端目标检测框架,使得整个检测流程比之前的框架(如Faster R-CNN)简单得多。
以下是DETR用于目标检测的整体架构图:

如图所示,输入图像首先通过一个CNN来提取一组特征图。这些特征图被展平,并添加位置编码,然后送入Transformer编码器,这与我们在ViT中看到的过程非常相似。编码器输出一组嵌入向量。
这些编码表示与一组称为“对象查询”的输入一起,被提供给Transformer解码器。解码器为每个对象查询输出一个编码,每个编码再被送入一个前馈神经网络,最终预测该位置是否存在物体(类别)以及边界框的坐标偏移。
请注意,模型也可以预测“无物体”这个类别标签,特别是在对象查询对应背景区域时。这就是DETR的整体思路,我们将在接下来的幻灯片中讨论更多具体细节。
DETR的核心:集合预测与匹配
在深入之前,这种方法的另一个重要表述是:由于Transformer解码器输出一组编码,任务现在变成了一个集合匹配问题——将一组解码器输出与训练数据中的一组真实边界框和标签进行匹配。我们如何使用损失函数来匹配这两个集合,是接下来要讨论的内容之一。
因此,DETR的主要组成部分包括:
-
一个基于集合的全局损失:通过两个集合之间的二分匹配来强制进行唯一预测。
-
一个Transformer编码器-解码器架构。
编码器流程
输入图像通过卷积神经网络,提取出一组维度为 C x H x W 的特征图。这些特征图经过CNN处理,在不同空间尺度上捕获了图像的视觉信息。
与Vision Transformers类似,这些特征图被展平,得到一个 C x HW 的矩阵。根据这些特征图在原图中的位置,为每个位置添加位置编码。这使得模型能够追踪特定边界框或图像块在原图中的位置。
添加位置编码后,这些令牌被送入Transformer编码器。编码器由多个Transformer编码器层组成,每层都包含自注意力机制和前馈网络。自注意力机制允许模型捕获图像的全局上下文和不同部分之间的关系。
解码器与对象查询
现在,这些编码表示与一组可学习的查询一起被送入Transformer解码器。在DETR论文中,这些可学习的查询也被称为位置编码或可学习的位置编码。它们代表了可能的物体位置和类别信息。
解码器同样由带有自注意力和前馈网络的Transformer层组成,并且它还接收编码器的输出作为额外输入。因此,解码器同时关注编码后的图像特征和这些对象查询,以最终预测物体的存在、位置和类别。
解码器的输出嵌入每个都通过一个前馈神经网络,以获得最终的类别标签预测和对应的边界框坐标。这就是DETR框架的整体流程。
你可能会问,这些可学习的查询是什么?我们向里面输入什么?顾名思义,它们是可学习的,初始化为随机向量,并通过训练中的反向传播进行更新。
那么,我们为什么要将它们作为输入?我们想通过它们得到什么?请记住,在Transformer中,输出编码的数量取决于提供给Transformer的输入令牌的数量。因此,这些可学习的查询在某种意义上允许解码器将它们转换为一组输出嵌入,这些嵌入随后映射到图像中的边界框。这就是这些可学习查询的主要目的。
论文还尝试对它们进行可视化,以理解每个可学习查询关注图像的哪个方面或部分。例如,其中一个可能只关注图像的左侧部分,另一个可能关注图像中心的大片区域等。更多关于这些可视化的细节,请参阅DETR论文。
另一个问题是,我们如何知道图像中有多少个物体,从而知道需要多少个输出嵌入?为此,论文简单地建议将 N 设置为任何测试或训练图像中可能出现的物体数量的上限。因此,解码器预测一个上限数量的编码,每个编码可能对应也可能不对应一个带有物体的边界框。
如果其中一个编码不对应任何物体会怎样?模型只需将其匹配到“无物体”类别,且没有特定的边界框坐标。这使得模型可以将这些解码器嵌入映射到真实标注中的一组边界框和类别预测,并在两者之间进行集合匹配,其中对应背景或没有物体的边界框被简单地替换为“无物体”类别。
这种方法的一个优点是,Transformer解码器或整个模型并行地为所有对象查询预测所有这些输出,这允许进行端到端训练,无需任何后处理步骤或两阶段网络,而这在早期目标检测框架中非常流行。
DETR的损失函数:匈牙利匹配
现在,我们来看看输出端如何进行关键的集合匹配。DETR在每次解码过程中预测 N 个物体,其中 N 大于典型的物体数量,它是给定图像中可能看到的物体实例数量的上限。
与现有的目标检测方法(如Faster R-CNN或FPN)不同,DETR不依赖阈值或启发式分配规则来匹配提议或锚框与真实标注。那么,它是如何进行最终分配的呢?如前所述,它使用一个损失函数,在预测集合和用于训练的真实标注图像的边界框集合之间实现最优的二分匹配。
这是如何做到的?如果 y 表示真实标注物体的集合,并额外添加一个“无物体”标签以匹配预测集合 ŷ(大小为 N)的大小,那么通过考虑一个在这两组预测匹配中损失最小的排列,可以找到这两个集合之间的二分匹配。
作为一个例子,如果你的Transformer解码器或DETR预测了,比如说,五个边界框,每个都有特定的类别标签和对应的边界框坐标,你需要用你的真实标注来检查。在特定图像中,你可能只有两个物体。所以,如果你有两个具有特定类别标签(比如 C1 和 C2)的物体,其余的只需用“无物体”或空集填充,以表示除了这两个之外没有其他物体。
现在,在这两个集合之间进行集合匹配,其方式是你考虑一种映射,该映射在所有可能的排列中给出最小的误差。因此,现在每个真实标注元素 yi 可以看作是 (c, b),其中 c 是类别标签,b 是边界框信息。对于索引为 σ(i) 的预测(请记住,σ 是所考虑的预测边界框和类别标签的排列),p̂_σ(i)(c_i) 是类别 c 的概率,b̂_σ(i) 是预测的边界框。
因此,这两个集合之间的匹配损失由以下公式给出:
L_match(y_i, ŷ_σ(i)) = -1_{c_i ≠ ∅} p̂_σ(i)(c_i) + 1_{c_i ≠ ∅} L_box(b_i, b̂_σ(i))
你理想情况下希望最小化损失,所以你希望最小化预测边界框和真实边界框之间的误差,同时希望最大化在该边界框中找到该特定类别的概率,这就是你在这里看到负号的原因,因为你理想情况下希望最大化这个概率。
这种匹配遵循一个非常著名的匹配算法,称为匈牙利匹配算法。因为它使用了匈牙利匹配算法,所以该损失也被称为匈牙利损失,你试图最大化正确类别的概率,并最小化预测边界框和真实边界框之间的测量误差。
请记住,你现在尝试采用一个考虑所有这些排列中最小损失的匹配损失。当你匹配两个集合时,集合一的某种顺序可能与集合二匹配得最好。你考虑所有可能的排列,并选择误差最小的那个。这就是匈牙利损失试图做的事情。
除此之外,DETR还对目标检测框架中使用的另一种典型损失——L1损失(用于测量边界框坐标的误差)进行了创新。因为L1损失对于大物体和非常小的物体,如果它们的相对误差相似,可能会有不同的尺度。因此,他们提出使用L1损失和广义IoU损失的线性组合作为训练DETR的边界框损失(IoU代表交并比,请回忆我们在语义分割中的讨论以理解IoU的含义)。
通过这种方法,他们表明,在遵循非常简单的整体架构(主要在集合预测方面进行创新)的同时,其性能显著优于Faster R-CNN及其各种变体(包括FPN变体)。
DETR的局限性与改进:Deformable DETR
虽然DETR是在目标检测中使用Transformer的重要一步,但它也有局限性。主要原因是自注意力机制的二次方计算复杂度。请记住,DETR几乎在像素级别处理信息,并且必须计算图像中所有其他像素的自注意力,这具有二次方复杂度。这可能使处理速度非常慢。
由于初始化注意力权重为 1/N_k(即对所有初始像素或所有输入块令牌给予均匀注意力,其中 N_k 是键元素的数量),它需要很长的训练时间。在图像领域,N_k 可能非常大(几乎是图像像素或小块),这种通过均匀分布初始化注意力权重的方法可能使训练非常繁琐,收敛需要很长时间。此外,还发现由于自注意力机制的工作方式,DETR在处理小物体方面存在局限性。
那么,如何解决这些问题呢?有几种方法可以处理这些挑战,让我们来看看。
降低计算复杂度
如何降低自注意力的二次方计算复杂度?有一个后续方法称为Deformable DETR,它试图解决这个问题。
他们是如何做到的呢?他们考虑了早期一些尝试以不同方式处理自注意力或注意力概念的方法。一种方法是仅在相邻窗口内进行稀疏的局部注意力,而不是与图像的每个其他部分进行自注意力。也有关于所谓“可变形卷积”的尝试。
可变形卷积是一种与Transformer无关的方法,它试图以略有不同的方式定义或重新定义卷积。通常,在卷积中,你将一个滤波器放置在中心像素位置,并与该像素周围的 3x3 或 5x5 邻域进行卷积。可变形卷积的建议是,与其预先固定这个邻域为 3x3 或 5x5,为什么不将这个邻域设为可学习的偏移量呢?这样,你就可以对,比如说,这个邻居、这个邻居和这个邻居等进行卷积,这些偏移量实际上是作为可变形卷积的一部分学习的,一旦你得到这些偏移量,你就可以使用标准程序执行卷积。
受这种方法的启发,Deformable DETR建议,在计算自注意力时,不是计算与图像中所有其他输入令牌的注意力,而是学习一些偏移量,并且只计算与那些其他输入令牌的注意力。由于这种与可变形卷积的相似性或受其启发,该方法被称为Deformable DETR。
现在,回想一下,Transformer中的多头注意力可以用幻灯片上看到的这个方程来表示:
MultiHeadAttn(z_q, x) = Σ_{m=1}^{M} W_m [ Σ_{k∈Ω_k} A_{mqk} · W‘_m x_k ]
如果 q 是一个查询元素,其特征为 z_q;k 是一个键元素,对应特征为 x_k;m 表示注意力头;c 是特征维度;Ω_q 和 Ω_k 是查询和键元素的集合。那么,多头注意力特征可以按上述公式计算。
这里有几个含义。一是如果你的注意力权重 A_{mqk} 被统一初始化为 1/N_k,会导致训练和收敛缓慢。其次,多头自注意力的计算复杂度可以写为 O(N_q N_k C + N_q C^2 + N_k C^2)。现在,如果 N_q 和 N_k 是你的查询和键的数量,通常与 HW(图像的分辨率或下采样分辨率)同阶。由于 N_q 乘以 N_k,这将与你原始特征图或图像大小的平方成正比。
我们能做些什么呢?受可变形卷积启发的Deformable DETR提出了一个可变形注意力模块,它只关注参考点附近的一组令牌或采样点。它在计算自注意力时,将特征图的大小从方程中移除。
这是如何实现的?可变形注意力特征的计算公式为:
DeformableAttn(z_q, p_q, x) = Σ_{m=1}^{M} W_m [ Σ_{k=1}^{K} A_{mqk} · W‘m x(p_q + Δp) ]
你的注意力(查询 q 的注意力头)乘以你的权重矩阵,但现在你的 x 不是完整的 x,而只是少数几个 x 位置,这些位置相对于当前位置 p_q 的偏移量也是你的可变形DETR学习的一部分。
你有多少个这样的位置?k 从 1 到 K。Deformable DETR在其实现中使用 K=4,这意味着Deformable DETR不是与图像中所有其他输入令牌计算自注意力,而是只与另外四个位置进行计算,这显著减少了计算量。其余注意力计算与我们在上一张幻灯片上看到的类似。
由于 K(可变形注意力采样点的数量)明显小于 HW,这带来了一个很好的影响。你的可变形注意力模块的复杂度由 O(2N_q C^2 + min(HW C^2, N_q K C^2)) 给出。对于编码器,其中元素数量为 HW,这将变为 O(HW C^2);对于解码器,你只给出一定数量的对象查询,复杂度变为 O(N K C^2)。对于编码器,HW 对应于特征图,这意味着它只是特征图的线性复杂度,而对于解码器,它甚至与输入的空间大小无关。与DETR相比,这显著加快了计算速度。
多尺度特征与迭代优化
他们还提出了使用多尺度特征来提高Deformable DETR的性能。在这种情况下,与许多其他CNN方法中使用多尺度的方式非常相似。给定特定图像,他们获取CNN不同层的特征图。请记住,DETR或Deformable DETR首先将图像通过CNN,并仅将该CNN的输出特征图作为其Transformer编码器的输入。现在,你可以考虑不同层的特征图,每个特征图将代表不同尺度的信息。一旦获得这些,Deformable DETR只需连接或展平所有这些图像特征图,然后像在基本模块中那样计算自注意力。因此,在某种意义上,你可以假设尺度的概念在输入Transformer编码器之前,通过简单地展平整个特征图集而被移除。其余部分与我们在 earlier slides 上看到的非常相似。
它还有其他一些改进,一是迭代边界框细化,其中Deformable DETR使用了一种机制,每个解码器层基于前一层的预测来细化边界框。因此,每个解码器块输出一组边界框预测,这些预测作为输入提供给下一个解码器块,下一个块再细化这些边界框预测,依此类推。这也导致了Deformable DETR的一个变体,称为两阶段Deformable DETR。可以说,他们受到了像Faster R-CNN这样的两阶段检测框架的启发,其中编码器的输出也被作为输入提供给Deformable DETR,而不仅仅是在特定阶段被通知。这使他们能够将Transformer的编码输出视为物体可能所在的区域提议,这些成为你的对象查询,然后通过Transformer解码器来获得最终的输出嵌入。
通过这些方法,他们表明,在显著更短的时间内,其性能可以显著优于DETR。
更先进的检测器:DINO
虽然DETR是目标检测中使用Transformer的重要一步,但后续还有更多改进。例如,Dynamic Anchor Boxes DETR 将DETR查询公式化为动态锚框。还有另一种改进称为 Denoising DETR,它试图通过引入去噪技术来改进DETR中的集合匹配。他们让Transformer的解码器在一个任务中进行去噪,在另一个任务中进行集合匹配,并表明这样做有助于提高DETR的集合匹配性能。
还有其他一些方法,如Cascade DETR、DINO以及最近的Grounding DINO。在本模块中,我们将在转向下一个分割任务之前,再讨论一种方法。这种方法叫做DINO,它是一种非常流行的方法,并且是当今检测的默认选择方法,这也是我们选择讨论它的原因。
DINO代表带有改进去噪锚框的DETR。顾名思义,它的许多改进都围绕着那些作为输入提供给Transformer解码器块的可学习查询。它也融入了一些其他新颖之处。它融入了DN-DETR的元素以引入去噪训练,引入了“向前看两次”方案以进行更好的边界框预测,还引入了混合查询选择以进行锚框初始化。
让我们在接下来的幻灯片中详细看看这些。
DINO中的一些贡献借鉴了我们提到的其他方法。他们遵循去噪DETR,在Transformer解码器层中添加噪声以执行去噪任务。他们还遵循Deformable DETR,使用可变形注意力以获得更好的计算效率。遵循Dynamic Anchor Boxes DETR,他们还将查询公式化为动态锚框,这些锚框在解码器层中逐步细化。
除了从先前工作中借鉴的这些改进之外,他们还引入了对比去噪训练以改进DETR的集合匹配步骤。他们还引入了混合查询选择,以更好地初始化提供给解码器的可学习查询。他们还提出了一种称为向前看两次的策略,以改进不同解码器块之间的边界框预测。
对比去噪训练
DINO中提出的改进之一是对比去噪训练,主要在解码器块中进行。他们引入了两组对比去噪组:一组正令牌和一组负令牌(或嵌入),其中正令牌组应该很好地重建去噪输入(因此每个查询都带有一些噪声,重建必须给出一个去噪后的输出),而在负令牌组中,解码器必须预测“无物体”。他们在这里有两个这样的组。
他们对去噪DETR的另一个改进是使用了两个超参数 λ1 和 λ2。如果添加的噪声小于超参数 λ1,则将其视为正查询,解码器应重建相应的真实边界框(即这里的黄金框);当噪声介于 λ1 和 λ2 之间时,解码器应预测对应查询为“无物体”。这就是他们所谓的对比去噪训练。
这有什么帮助呢?当有多个锚框非常接近一个物体时,模型通常可能会混淆使用哪个锚框来预测其输出中的物体。这可能导致两个问题:一是重复预测,二是不正确的锚框选择。他们表明,通过对比去噪训练,DINO可以区分锚框之间的微小差异,从而避免这些问题。
混合查询选择
他们带来的第二个贡献是混合查询选择。通常,DETR中的查询(即提供给解码器的可学习查询)有两个部分:内容部分和位置部分。DETR和DN-DETR的做法是,他们只考虑位置查询(这些是可学习和更新的),而内容查询都被视为零向量。因此,提供给解码器的这些可学习查询只是位置信息(如 x, y, h, w:边界框位于哪里,它的宽度和高度是多少),这显然是解码器块学习的一部分,而内容查询则作为零向量给出。所以,没有为框内内容提供可学习查询,只有位置信息。这就是DETR和DN-DETR向解码器块提供查询的方式。
Deformable DETR的做法略有不同,在其一个变体(如果你还记得两阶段变体)中,他们将编码器的输出作为区域提议提供给解码器。在这种情况下,你的锚框(即你的位置信息)和内容信息都作为可学习查询提供给解码器,两者都是可学习的,这就是为什么我们称之为动态。
那么,DINO做了什么?它提供了一种混合查询选择策略。他们说,在Deformable DETR中,你的编码器特征是初步的内容特征,可能不一定真正帮助解码器,甚至可能误导解码器。因此,他们做的是使这些内容查询成为可学习的和静态的(我稍后会解释静态的含义)。而这里的位置查询是你的Transformer编码器块中前K个编码器输出的位置查询。因此,你有一组来自Transformer的边界框编码器输出,你有这些边界框可能位于的位置信息,这被用来初始化输入解码器的位置查询。而内容查询保持可学习和静态(这里的“静态”是指在推理时对于不同的图像保持相同,在训练时它们是可学习的)。这就是他们在DINO框架中所谓的混合查询选择。
向前看两次
最后,如前所述,他们还引入了一种称为“向前看两次”的策略。他们观察到,在Deformable DETR中,每个解码器块(这里的每个块都是解码器层或解码器块)都会预测一个边界框,并且在该解码器块本身有一个辅助损失,用于告知该边界框是否正确以及它离真实标注有多远,而该损失实际上用于训练每个解码器块中的边界框。事实上,他们阻止了梯度从后面的块反向传播到前面的块以进行边界框预测。
DINO发现这种方法是一种“向前看一次”的方法,相反,它提出了一种“向前看两次”的方法,其中第 i 层的参数受到第 i 层以及第 i+1 层边界框损失的影响。你可以在这里看到,这个和这个的梯度都会传播以更新给定层中的边界框偏移。他们称这种方法为“向前看两次”方法。
通过这些改进,他们表明其性能显著优于大多数其他检测方法,而且模型尺寸比许多其他框架(尤其是那些使用Swin Transformer的框架)更小。
总结

本节课中,我们一起学习了Transformer如何应用于目标检测任务。我们从传统的两阶段检测方法面临的挑战出发,深入探讨了DETR这一端到端的集合预测检测框架,理解了其编码器-解码器架构、可学习的对象查询以及基于匈牙利匹配的集合损失函数。接着,我们分析了DETR在计算效率和训练速度上的局限性,并介绍了Deformable DETR如何通过可变形注意力机制和多尺度特征显著提升了性能。最后,我们了解了当前最先进的检测器DINO,它通过对比去噪训练、混合查询选择和向前看两次等策略,进一步优化了检测精度和效率。这些工作展示了Transformer在目标检测领域的强大潜力和持续演进。
114:生成对抗网络 - 第一部分

概述
在本节课中,我们将要学习一种非常流行的深度生成模型——生成对抗网络。我们将从回顾上一讲的内容开始,逐步理解GANs的核心思想、目标函数、训练策略以及其背后的工作原理。
从KL散度到最大似然估计
上一讲我们遗留了一个问题:为什么在寻找生成模型时使用KL散度会简化为最大似然估计?
让我们回顾一下问题。最优参数θ*被定义为在所有可能的θ中,使模型分布Pθ与真实数据分布Pd之间的某种距离函数最小化的那个参数。如果这个距离函数是KL散度,那么问题就简化为最大似然估计。
KL散度的定义是:对于两个概率分布P和Q,其KL散度为 KL(P||Q) = Σ P(x) log(P(x)/Q(x))。在我们的场景中,就是计算 KL(Pd||Pθ)。
展开后得到:KL(Pd||Pθ) = Σ Pd(x) log Pd(x) - Σ Pd(x) log Pθ(x)。第一项与θ无关,因此在优化时可以忽略。我们只剩下最小化 - Σ Pd(x) log Pθ(x),这等价于最大化 Σ Pd(x) log Pθ(x),也就是最大似然估计。这证明了我们的结论。
生成模型概念回顾
在深入GANs之前,让我们简要回顾上一讲的核心概念。
生成模型的目标是学习一个在给定训练图像数据集X上的概率密度函数P(x)。如果我们知道这个分布(例如参数化为高斯分布),那么P(x)会为每一张可能的图像分配一个正数,表示该图像由该分布生成的可能性。
生成模型的应用包括:
-
采样与生成新数据:从学习到的分布中生成新的图像样本。
-
似然估计与异常检测:计算一个数据点属于该分布的可能性。如果可能性很低,则该点很可能是异常值。
-
特征学习:我们将在本讲中更多地讨论这一点。
我们还讨论了密度估计的两种视角:
-
显式密度估计:尝试为概率分布P(x)写出一个明确的函数形式(例如,假设为高斯函数)。输入是图像x,输出是其似然值P(x)。这类方法擅长异常检测,但有时在生成高质量图像方面可能表现不佳或速度较慢。
-
隐式密度估计:不尝试为P(x)分配函数形式。其目标仅仅是确保给定一个模型,我们可以从中采样图像,而无需为每个样本分配明确的似然值。这类方法通常能实现更好的样本生成质量和更快的采样速度。
生成对抗网络简介
现在,让我们进入本讲的重点:生成对抗网络。GANs由Goodfellow等人在2014年提出。
GANs的目标是构建一个优秀的采样器,能够从模型分布P_model(x)中抽取高质量的样本。正如我们提到的,这里没有对P(x)进行显式的函数形式计算。我们唯一的目标是确保从模型中采样的图像,其样本分布P_model(x)看起来与原始数据分布P_data相似。我们不希望样本与训练数据完全相同,因为那意味着模型只是在记忆,而没有学到泛化能力,也无法生成新的、多样的样本。
那么,如何实现这个目标呢?GANs的关键思想是引入一个潜变量z。这个潜变量通常有一个简单的先验分布,例如高斯分布。我们从该分布中采样一个随机向量z,然后将其输入一个称为生成器的模块(通常是一个神经网络)。生成器输出一张图像X̂。我们将生成器输出的图像分布记为P_g。
我们的目标是:使生成器产生的分布P_g尽可能接近真实数据分布P_data。挑战在于,我们并没有对P_g或P_data施加任何特定的参数化形式(例如,假设它们是高斯分布)。因此,我们需要一种方法,在不知道分布具体形式的情况下,确保P_g接近P_data。
对抗训练的核心思想
为了解决上述挑战,GANs的提出者引入了一个巧妙的构思:一个称为判别器的分类器,记作D。
判别器的任务是区分“真实”样本(来自P_data)和“生成”样本(来自P_g)。如果样本来自P_data,判别器应输出1(“真”);如果来自P_g,则应输出0(“假”)。
这个设计如何帮助我们呢?我们的目标是训练生成器G,使其能够“欺骗”判别器D,让判别器将生成的样本X̂错误地分类为“真”(即输出1)。如果生成器足够好,以至于判别器无法再可靠地区分真实样本和生成样本,那么我们就可以认为P_g已经非常接近P_data了。
这就像一个“警察与小偷”的游戏:判别器是警察,试图揪出假币(生成样本);生成器是小偷,试图制造出以假乱真的假币来骗过警察。
GANs的目标函数
如何形式化地训练这样的生成器和判别器呢?GANs的训练目标是一个极小极大优化问题:
min_G max_D [ E_(x~P_data)[log D(x)] + E_(z~P_z)[log(1 - D(G(z)))] ]
其中:
-
G:生成器,参数为θ_G。
-
D:判别器,参数为θ_D。我们假设其输出层使用Sigmoid激活函数,因为它是一个二分类器。
-
z:从先验分布P_z(如高斯分布)中采样的潜变量。
-
G(z):生成器根据z生成的图像。
-
D(x):判别器判断样本x为“真”的概率。
让我们来解析这个目标函数:
-
对于判别器D(max_D部分):
-
第一项 E[log D(x)]:鼓励判别器为真实样本x赋予高概率(接近1),从而最大化log D(x)。
-
第二项 E[log(1 - D(G(z)))]:鼓励判别器为生成样本G(z)赋予低概率(接近0),从而最大化log(1 - D(G(z)))。
-
总之,判别器希望最大化这两项之和,即更好地区分真假。
-
-
对于生成器G(min_G部分):
-
第一项与G无关,可以忽略。
-
生成器通过最小化整体目标来影响第二项。注意,最小化 E[log(1 - D(G(z)))] 等价于让D(G(z))尽可能大,即让判别器认为生成样本是“真”的。
-
因此,生成器的目标是最大化判别器对其输出误判的概率。
-
这种一方试图最大化、另一方试图最小化同一目标函数的形式,在博弈论中被称为零和博弈。
GANs的训练算法
我们如何训练这两个网络呢?一个朴素的想法是先完全训练好判别器D,再训练生成器G。但这会带来问题:如果初始判别器过于强大,能准确识别所有生成样本为假(即D(G(z)) ≈ 0),那么第二项log(1 - D(G(z))) ≈ log(1) = 0,其梯度也会接近0。这将导致生成器G无法获得有效的梯度进行学习,训练停滞。
因此,GANs采用交替训练的策略。以下是原始论文推荐的算法步骤:
以下是训练GANs的迭代过程:
-
循环多次训练迭代:
-
对于k个步骤(通常k=1):
-
采样:从噪声先验P_z中采样一个小批量的M个噪声样本{z¹, ..., z^M}。同时,从真实数据分布P_data中采样一个小批量的M个真实样本{x¹, ..., x^M}。
-
更新判别器D(梯度上升):计算判别器的损失关于其参数的梯度,并执行梯度上升以最大化目标函数。
- 梯度公式:∇_θ_d (1/M) Σ [log D(xⁱ) + log(1 - D(G(zⁱ)))]
-
-
更新生成器G(梯度下降):
-
采样:从噪声先验P_z中采样一个新的小批量的M个噪声样本{z¹, ..., z^M}。
-
计算生成器的损失关于其参数的梯度,并执行梯度下降以最小化目标函数的第二部分。
-
梯度公式:∇_θ_g (1/M) Σ [log(1 - D(G(zⁱ)))]
-
(注:实践中常使用最大化 E[log D(G(z))] 的替代目标来提供更稳定的梯度,但其效果与最小化原目标在理论上是等价的。)
-
-
在测试阶段,我们丢弃判别器D。要生成新图像,只需从高斯分布中采样一个噪声向量z,然后将其输入训练好的生成器G,得到的输出G(z)就是生成的图像。
关于收敛的思考
在传统的神经网络训练中,我们通过观察损失函数值下降来判断收敛。然而在GANs中,我们面临一个极小极大博弈:判别器试图提高目标函数值,而生成器试图降低它。
那么,GANs的收敛意味着什么?理想情况下,当生成器完美地模拟了真实数据分布(即P_g = P_data)时,判别器将无法区分真假,其对任何样本的预测概率都应是0.5(即随机猜测)。此时,目标函数的值会达到一个纳什均衡点。在实践中,我们通常观察生成样本的质量和多样性,以及判别器的准确率是否在50%左右波动,作为训练是否良好的经验指标。

总结
本节课我们一起学习了生成对抗网络的第一部分。我们首先回顾了KL散度与最大似然估计的关系,以及生成模型的两种密度估计方法。然后,我们重点介绍了GANs的核心思想:通过生成器和判别器之间的对抗博弈来学习数据分布。我们详细分析了GANs的极小极大目标函数,并讲解了其交替训练的策略和算法步骤。最后,我们讨论了GANs这种博弈框架下“收敛”的特殊含义。在接下来的课程中,我们将继续探讨GANs的变体、训练技巧以及实际应用。
115:生成对抗网络 - 第二部分

概述
在本节课中,我们将深入探讨生成对抗网络的数学原理、一个重要的改进架构DCGAN,以及如何评估GAN模型的性能。我们将从理论推导开始,理解GAN的最优解,然后介绍DCGAN的关键训练技巧和有趣特性,最后学习评估生成模型质量的常用指标。
最优判别器与生成器的推导
上一节我们介绍了GAN的基本框架和训练过程。本节中,我们来看看如何从数学上推导出GAN的最优解。
首先,我们重新审视GAN的目标函数:
Min_G Max_D V(D, G) = E_{x~p_data(x)}[log D(x)] + E_{z~p_z(z)}[log(1 - D(G(z)))]
为了评估训练此类网络的最优性,让我们重新考虑这个目标。将期望展开为积分形式:
Min_G Max_D ∫_x [p_data(x) log D(x) + p_g(x) log(1 - D(x))] dx
这里,p_g(x) 是生成器定义的生成数据分布。
现在,我们将最大化操作移到积分内部。为了理解此类函数的最大值,我们使用变量替换进行分析。
令:
-
y = D(x) -
a = p_data(x) -
b = p_g(x)
则积分内部的函数为:f(y) = a log y + b log(1 - y)
为了找到函数的最大值,我们对其求导并令导数为零:
f'(y) = a/y - b/(1 - y) = 0
求解得到最大值点:y* = a / (a + b)
将变量替换回原式,这意味着对于给定的输入 x,最优判别器的输出为:
D*(x) = p_data(x) / (p_data(x) + p_g(x))
生成器的最优解
我们得到了最优判别器,但故事并未结束,因为我们还需要考虑生成器的最优性。
现在,我们来看生成器一侧的最优性。假设我们已经评估了判别器的最大值,并将最优判别器 D* 代入目标函数中关于生成器 G 的最小化部分:
Min_G ∫_x [p_data(x) log D(x) + p_g(x) log(1 - D(x))] dx
将 D*(x) 的表达式代入,并进行代数运算,我们可以将此表达式重写为期望形式:
Min_G { E_{x~p_data} [log( p_data / (p_data+p_g) )] + E_{x~p_g} [log( p_g / (p_data+p_g) )] }
经过一系列变换(包括乘以并除以2,以及引入KL散度),最终可以将目标函数表示为:
Min_G { 2 * JSD(p_data || p_g) - log 4 }
其中,JSD 是 Jensen-Shannon散度,它是衡量两个概率分布之间距离的另一种度量,其定义为:
JSD(P||Q) = 1/2 KL(P || M) + 1/2 KL(Q || M),其中 M = (P+Q)/2
与KL散度一样,Jensen-Shannon散度也是一个非负值。
全局最优解的结论
现在,让我们将所有结论汇总。
由于Jensen-Shannon散度是非负的,当且仅当 p_data = p_g 时,JSD(p_data || p_g) = 0,此时目标函数达到最小值 -log 4。
因此,生成器 G 达到最优时,其数据分布 p_g 等于真实数据分布 p_data。这符合我们的直观理解。
此时,最优判别器变为:
D*(x) = p_data(x) / (p_data(x) + p_data(x)) = 1/2
这意味着在全局最优状态下,对于任何输入样本,判别器的输出都应为 0.5。它无法区分真实样本和生成样本,达到了纳什均衡。
DCGAN:深度卷积生成对抗网络
理解了GAN的理论基础后,我们来看一个重要的实践改进。2016年,Radford等人提出了深度卷积生成对抗网络。
DCGAN是一个里程碑,它显著提升了使用GAN生成图像的质量。其主要思想是将CNN在监督学习中的成功经验与无监督生成模型相结合,将训练CNN的最佳实践引入到深度GAN架构的训练中。
以下是DCGAN引入的关键训练策略:
-
卷积替代池化:使用步幅卷积(strided convolution) 替代确定性的空间池化函数(如最大池化),以学习空间下采样。
-
去除全连接层:在深层架构中移除全连接隐藏层,主要使用卷积层。
-
引入批量归一化:在生成器和判别器中都使用批量归一化(Batch Normalization),这有助于防止梯度问题,但不在生成器的输出层和判别器的输入层使用。
-
激活函数选择:生成器使用ReLU激活函数,判别器使用Leaky ReLU激活函数。
-
输出层激活:生成器的输出层使用Tanh激活函数,判别器的输出层使用Sigmoid激活函数来输出真假概率。
潜在空间的操作与向量算术
DCGAN展示了如何操作潜在空间以生成不同类型的图像,这是一个有趣的实验。
如果你从高斯分布中采样两个潜在向量 z1 和 z2,并通过生成器得到两个不同的输出图像 G(z1) 和 G(z2)。现在,如果你在潜在空间中对它们进行线性插值:
z' = α * z1 + (1 - α) * z2,其中 0 ≤ α ≤ 1
然后将 z' 输入生成器,你会得到从 G(z1) 到 G(z2) 的平滑过渡图像。
这可以看作是在潜在空间中进行向量算术。例如:
-
计算戴眼镜男性的平均潜在向量
z_man_with_glasses。 -
计算不戴眼镜男性的平均潜在向量
z_man_without_glasses。 -
计算不戴眼镜女性的平均潜在向量
z_woman_without_glasses。
然后进行运算:z_result = z_man_with_glasses - z_man_without_glasses + z_woman_without_glasses
将 z_result 输入生成器,可以得到戴眼镜女性的图像。这说明了生成器如何学习将潜在空间的语义操作映射到图像空间。
GAN的特征学习能力
DCGAN的工作还展示了GAN如何学习到可用于分类的良好特征。
实验方法如下:
-
在ImageNet数据集上训练DCGAN。
-
使用另一个数据集(如CIFAR-10)的图像,将其输入到已训练好的GAN的判别器中。
-
提取判别器某一卷积层的特征。
-
使用这些特征训练一个SVM分类器,对CIFAR-10图像进行分类。
实验结果表明,使用GAN判别器学到的特征进行分类,其性能与2016年当时的许多其他方法具有竞争力。这证明了判别器所学特征的鲁棒性。
如何评估生成对抗网络?
对于监督学习,我们可以使用准确率等指标。但对于GAN,如何评估其模型性能呢?
以下是几种评估方式:
1. 人工评估
一种直接的方法是依赖人类判断。一个好的生成器应能生成具有清晰可辨物体的图像,并且生成的样本在语义上具有多样性。
-
可识别性:一个独立的分类器能够以高置信度预测生成图像的类别。
-
语义多样性:生成器能生成训练集中各种不同类别的样本。
2. 初始分数
这是一个旨在与人类判断相关联的流行指标。它使用一个在ImageNet上预训练的Inception-v3网络。
-
p(y|x):给定生成图像x,Inception模型输出的类别标签概率分布。我们希望这个分布是尖锐的(即某一类概率很高)。 -
p(y):所有生成样本的边际类别分布。我们希望生成样本覆盖所有类别,即p(y)接近均匀分布。
初始分数(IS) 定义为 p(y|x) 和 p(y) 之间的KL散度:
IS = exp( E_{x~p_g} [ KL( p(y|x) || p(y) ) ] )
更高的初始分数意味着更好的生成质量。它也可以理解为 H(y) - H(y|x),即希望类别熵 H(y) 高(多样性好),条件熵 H(y|x) 低(每个图像分类明确)。
初始分数的局限性:它完全不考虑真实数据分布,仅基于生成分布本身进行评估。
FID分数:弗雷歇初始距离
为了弥补初始分数的不足,研究者提出了弗雷歇初始距离。
FID旨在直接衡量生成分布与真实分布之间的距离。计算方法如下:
-
将真实图像和生成图像分别输入Inception-v3网络。
-
提取池化层(pool3) 的激活值作为特征。
-
假设这两组特征均服从多元高斯分布,分别计算它们的均值(
m,m_w)和协方差矩阵(C,C_w)。 -
计算这两个高斯分布之间的弗雷歇距离:
FID = ||m - m_w||^2 + Tr(C + C_w - 2(CC_w)^{1/2})
其中 Tr 表示矩阵的迹。FID分数越低,表示生成分布与真实分布越接近,生成质量越好。
FID对噪声和扰动更敏感。例如,向图像添加高斯噪声、高斯模糊或椒盐噪声都会导致FID分数显著上升(变差),而初始分数对这些变化的敏感性则相对较低。
总结
本节课中,我们一起学习了生成对抗网络更深层次的内容。
首先,我们从数学上推导了GAN的全局最优解,证明了在最优状态下,生成器的数据分布会收敛到真实数据分布,而判别器的输出将始终为0.5。
接着,我们介绍了DCGAN这一重要架构,它通过引入卷积网络的最佳实践(如步幅卷积、批量归一化、特定的激活函数)显著提升了训练稳定性和生成质量。我们还探索了在潜在空间中进行向量算术的有趣应用,以及GAN判别器所具备的强大的特征学习能力。
最后,我们讨论了评估GAN模型的挑战和方法,包括依赖人类判断、初始分数和弗雷歇初始距离。我们了解到,虽然这些指标被广泛使用,但生成模型的评估仍然是一个开放的研究问题。

通过本课的学习,你应该对GAN的工作原理、改进方法以及如何衡量其成功有了更全面的认识。
116:GAN技巧与改进

在本节课中,我们将学习生成对抗网络(GAN)在过去几年中的多种改进方法。我们将从几种基础GAN的改进方案开始,简要探讨潜在空间解耦的概念,并了解GAN在图像和视频生成中的应用。
基础GAN的改进方法
上一节我们介绍了GAN的基本原理,本节中我们来看看几种旨在提升生成质量和稳定性的改进型GAN架构。
StackGAN:生成高分辨率文本条件图像
第一个要讨论的方法是StackGAN,发表于2017年ICCV会议。该工作的目标是生成256x256分辨率、具有照片级真实感的图像,并且这些图像以文本描述为条件。
整个生成器模型以一个文本描述(例如一段标题)为条件。使用Word2Vec、GloVe、BERT等标准NLP方法获取该文本描述的嵌入向量,并将其作为生成器的输入。
以下是StackGAN的两阶段流程:
-
第一阶段:生成器接收文本嵌入向量和标准高斯噪声,生成一个64x64的图像。该图像包含一些高级或低频细节。
-
第二阶段:第一阶段的输出图像与相同的文本嵌入向量一起,输入到第二个生成器阶段,最终生成256x256的高细节、照片级真实感图像。
现在让我们看看整个架构。文本描述(例如“这只鸟是灰色的,胸部白色,喙很短”)被编码为嵌入向量。该嵌入向量通过一个全连接层生成均值和标准差,用于对标准正态分布的噪声样本进行变换,然后与另一个标准正态样本拼接,共同作为第一阶段生成器的输入。
第一阶段生成器输出64x64图像。判别器接收生成的图像和真实图像批次,并对它们进行下采样。在最终分类为“真”或“假”之前,判别器还会将文本嵌入向量与下采样后的特征表示相结合。因此,判别器的任务是判断(图像特征,文本嵌入)这个组合是真实的还是伪造的。
第二阶段,第一阶段的输出图像再次与文本嵌入向量一起,输入到第二阶段生成器。图像先被下采样,然后与文本嵌入拼接,经过一系列残差块,最后上采样得到256x256图像。该图像随后输入到第二个判别器,判别器同样需要结合文本嵌入来判断(图像,文本)组合的真伪。
为了完成这个任务,判别器需要提供三种分数:
-
S_R:真实图像 + 正确文本 -> 判别器希望输出高分(接近1)。
-
S_W:真实图像 + 错误文本 -> 判别器希望输出低分。
-
S_F:伪造图像 + 正确文本 -> 判别器希望输出低分,但生成器希望提高此分数。
StackGAN的优化过程与标准GAN类似,交替最大化判别器损失和最小化生成器损失。判别器损失函数试图最大化log(S_R),同时最小化log(S_W)和log(S_F)。生成器损失函数则试图最大化log(1 - S_F),并最小化文本嵌入层输出的均值/方差与标准正态分布之间的KL散度。
以下是StackGAN的一些生成结果。顶行是2016年发表的基线方法GAN-INT-CLS的结果,底行是StackGAN的结果。可以看到,在文本描述(如“这朵花的花瓣是白色的,带有粉色阴影”)条件下,StackGAN生成的图像质量远高于早期方法,更加逼真。
Progressive GAN:渐进式生成高分辨率图像
接下来介绍2018年提出的Progressive GAN(渐进式GAN),它旨在生成高达1024x1024分辨率的高清图像。其核心思想体现在名称中:渐进式地增长生成器和判别器。
Progressive GAN的整体思路如下图所示。生成器首先从一个标准正态分布的潜在向量开始,通过一个非常简单的网络生成一个4x4的图像。该图像与真实样本一起提供给判别器D进行真伪判断。
在接下来的训练迭代中,分辨率逐渐增加到8x8。与4x4设置相比,8x8的图像虽然仍然模糊,但包含了更多细节。可以注意到,当生成更高分辨率时,生成器和判别器中都会添加新的层。这个过程不断重复,直到最终生成1024x1024的图像。这种方法的核心优势在于能够稳定地训练生成高分辨率图像的GAN。
当Progressive GAN从某个分辨率升级到下一个更高分辨率时,会引入新的层。引入方式与残差网络(ResNet)非常相似。例如,从16x16升级到32x32时,16x16层的输出会通过最近邻插值上采样到32x32。这个上采样后的“投影层”通过一个跳跃连接,与新的32x32层的输出以加权和的方式结合。最终输出由公式 alpha * 新层输出 + (1 - alpha) * 投影层输出 给出。这允许Progressive GAN以有机的方式“淡入”新层,从而生成更好的图像。
除了渐进式增长,Progressive GAN还引入了其他几项设计决策:
-
小批量标准差(Mini-batch Standard Deviation):在此步骤中,计算一个小批量内,每个空间位置(即每个像素)上每个特征的标准差,然后进行平均。这个计算出的标准差被拼接到判别器后续层的所有空间位置上。思考:为什么这样做?(本节课后作业之一)
-
均衡学习率(Equalized Learning Rate):生成器网络每一层的权重都用一个每层特定的常数 C 进行归一化。这允许我们在每一层调整C,从而帮助在训练期间保持权重处于相似的尺度。之所以称为“均衡学习率”,是因为权重的值实际上会影响梯度,从而影响学习率。虽然像Adam这样的优化器会自适应调整学习率,但在权重本身非常低或非常高的情况下,学习率可能仍然很低。通过常数归一化,可以使所有层的权重在训练期间保持相似的尺度,从而实现更好的学习。
-
生成器中的逐像素特征向量归一化(Pixel-wise Feature Vector Normalization in Generator):对于生成器中的每个卷积层,归一化定义为:
b_{x,y} = a_{x,y} / sqrt( (1/N) * sum_{j=0}^{N-1} (a_{x,y}^j)^2 + epsilon )其中,
a_{x,y}是特征图中(x, y)位置的像素值,N是特征图的数量,epsilon是为了数值稳定性而添加的小常数,避免除零错误。分母考虑了所有特征图在同一位置上的像素值,并用此分母对第i个特征图的值进行归一化。输出b_{x,y}是归一化后的值。
凭借这些贡献,Progressive GAN报告了令人印象深刻的结果。与Mao等人和Gulrajani等人的早期工作相比,采用渐进式GAN方法生成的结果具有更高的照片真实感和分辨率,细节也更加丰富。
StyleGAN:基于风格控制的高质量图像生成
第三个更近期的GAN改进是2019年CVPR会议上发表的StyleGAN。它着眼于Progressive GAN(也称为ProGAN),后者能生成高质量图像,但难以控制生成过程中的特定特征。例如,很难使用ProGAN来指定为图像添加某种颜色或改变特定属性。
StyleGAN的目标是能够使用预定义的特定风格来控制图像的生成。它能够无监督地自动学习高级属性的分离,例如人脸图像中的姿态和身份、随机变化(如具有很大随机性的头发)以及特定尺度的属性控制。
StyleGAN的直觉是:在图像中,粗糙分辨率(例如4² - 8²)影响姿态、整体发型、脸型等属性;中等分辨率(例如16² - 32²)影响更精细的属性,如面部特征、更具体的发型、眼睛开合等;精细分辨率(例如64² - 1024²)则影响色彩方案(眼睛、头发、皮肤)并引入面部的微观特征。StyleGAN试图在这些分辨率级别上分别引入风格特定的组件,以达到其预期效果。
它是如何做到的呢?给定一个输入随机向量(与原始GAN相同),StyleGAN首先对该向量进行归一化,然后通过8个全连接层(不改变输入维度)进行处理,得到一个512x1的向量 w。这个变换后的向量 w 被输入到生成器(或称为合成网络)。
现在,让我们看看向量 w 如何影响生成过程中的不同分辨率。这是通过引入一个矩阵 A 来实现的,该矩阵在不同分辨率下学习一种仿射变换。合成网络(生成器)包含上采样模块,然后是自适应实例归一化(Adaptive Instance Normalization, AdaIN),接着是卷积层,然后是另一个AdaIN层,这个过程在多个块中重复。
让我们看一下这些卷积层和自适应实例归一化层组合中的一个。对于一个卷积层,其特定通道的特征图通过其均值和方差进行归一化,以实现自适应实例归一化。在此之前,通过 A 映射对权重向量 w 进行变换,得到一组值 ys_i 和 yb_i,用于改变卷积层输出的尺度和偏置。这就是“自适应”归一化的来源。
具体操作如下:首先进行归一化 (x - μ(x_i)) / σ(x_i),然后乘以从 A 变换得到的缩放值 ys_i,再加上偏置值 yb_i。注意,ys_i 和 yb_i 在合成网络的不同块中可能不同。每个这样的变换从而定义了该层级特征的视觉表达,这使得输入潜在向量能够在生成的不同分辨率上产生不同的影响。
SPADE:基于空间自适应归一化的语义图像合成
另一个于2019年CVPR发表的方法是SPADE(Spatially Adaptive Normalization,空间自适应归一化)。其核心思想是:先前的方法直接将语义布局图作为网络的输入。而在SPADE中,输入布局通过这种空间自适应的学习变换来调制归一化层中的激活。
让我们看看这是如何发生的。在标准的批归一化层中,你可以将输出视为经过一个仿射变换:缩放值并加上一个常数(即 γ * x + β)。然而,在SPADE中,一个语义分割图被作为输入。这个语义分割图经过一个卷积层,该卷积层输出一个 γ 图和一个 β 图,用于对生成器的前一层进行归一化。
与盲目归一化特定层的输出特征图不同,在这种情况下,归一化是以空间自适应的方式进行的,其中 γ 和 β 来自语义分割图中的空间关系。语义分割图具有像素级的类别标签关联(例如,树、天空、山、草、路)。这用于定义归一化的 γ 和 β。
一个随机潜在向量也可以用来操纵生成图像的风格,但语义分割图提供了一种利用每个像素的空间内容进行归一化的方法。可以注意到,现在的归一化是在每个像素上定义的,γ 和 β 是在像素级别定义的(由逐元素相乘 * 和相加 + 表示),这允许每个像素根据语义分割掩码以不同的方式进行归一化。
以下是一些有趣的结果。可以看到,语义分割输出作为SPADE的输入(这些是像素级类别标注的掩码),第三列和第四列对应其他方法,而SPADE生成了相当逼真的输出,接近真实情况。图中展示的是SPADE中生成器的架构,它与许多其他GAN架构相似,但区别在于每个层都有语义分割掩码的输入。
BigGAN:大规模GAN用于类别条件图像生成
最后,我们将讨论本讲座中最后一个方法,即目前非常流行的BigGAN,发表于2019年ICLR。BigGAN的重点是扩大GAN的规模以实现更好的高分辨率生成。它专为类别条件图像生成而设计,这意味着输入既包括类似于原始GAN的噪声向量,也包括一些类别信息(例如一个one-hot向量),它们共同提供给生成器以生成对应类别的图像。
BigGAN也引入了一些不同的设计决策。一个重要决策是使用自注意力GAN(SAGAN) 的思想。SAGAN是一项较早的工作(2018年底提出,2019年ICML发表),它引入了自注意力的概念。这与我们一周前在Transformer中看到的自注意力是相同的想法。
在生成器中某一特定层的卷积特征图输出上,通过多个1x1卷积进行处理。其中一些1x1卷积经过变换和转置以获得注意力图。这三个分支类似于Transformer架构中的查询、键和值。其中两个分支生成一个注意力图,用于聚焦卷积图的特定部分,以便在下一层生成图像的该部分。之所以需要这个,是因为通常的GAN在生成清晰图像时,可能会遗漏更精细的细节(例如,在狗的图像中可能生成了狗的毛发但遗漏了腿)。使用自注意力的思想是聚焦图像的特定部分,并更恰当地生成每个局部细节。
在BigGAN中,与自注意力GAN一起使用的损失函数是铰链损失(Hinge Loss)。这个铰链损失类似于支持向量机中使用的铰链损失,由公式 max(0, 1 - t*y) 给出,其中 t 是目标输出,y 是预测输出。这是因为GAN是类别条件的,所以你希望输出图像属于特定类别,这个类别信息在判别器阶段被使用,因为判别器现在不仅判断真伪,还可以给出一个类别标签,其损失在这种情况下由铰链损失给出。
此外,BigGAN还以略有不同的方式引入了类别条件潜在变量。不是将类别条件潜在变量(例如one-hot向量)直接提供给生成器的输入,而是将其作为单独的输入在生成的多个阶段提供,在那里它与某些输入拼接,然后提供给每个残差块。
在每个残差块内部,拼接后的类别标签向量通过线性变换(实际上是两个不同的线性变换),然后提供给该残差块内的两个批归一化层。
BigGAN还有其他有助于其性能的设计决策:
-
谱归一化(Spectral Normalization):对每一层的权重矩阵进行归一化,以确保其谱范数保持不变,并满足Lipschitz约束
σ(W) = 1。其主要思想类似于批归一化,即约束权重具有某些特性,以确保学习可以更好、更快地进行。在这种情况下,我们试图约束权重矩阵的最大奇异值。 -
正交权重初始化(Orthogonal Weight Initialization):一种众所周知的权重初始化方法,其中每一层的权重被初始化为确保
W^T W = I,即权重矩阵是正交的。 -
跳跃-Z连接(Skip-Z Connections):输入潜在向量 Z 连接到网络深处的特定层。
-
正交正则化(Orthogonal Regularization):鼓励权重在每次训练迭代中保持正交。这是通过一个正则器项
β * ||W^T W - I||_F^2来实现的,其中β是一个系数。这个正则器试图确保W^T W接近单位矩阵,从而使矩阵接近正交。思考:为什么我们需要正交正则化?(本节课后作业之二)
BigGAN中采用的其他技巧包括:在每次迭代中,判别器模型更新两次后才更新生成器模型;使用移动平均方法对几次训练迭代的模型权重进行平均,作为最终模型(这在ProGAN中也做过)。
BigGAN还观察到,在使用小批量SGD时,使用非常大的批量大小(如256、512、1024和2048)效果更好,实际上他们在最大的2048批量大小时观察到了最佳性能。他们还使每层的通道数(即模型参数)翻倍,并采用了称为截断技巧(Truncation Trick) 的方法。在训练时,生成器从高斯分布接收潜在向量;在测试时,从高斯分布采样一个潜在向量,如果该值小于某个阈值,则丢弃它并重新采样。这被称为截断高斯分布。其主要思想是,你不会使用这些潜在向量得到异常的生成结果,而是得到属于该分布核心部分的生成结果。
总结与作业
本节课中,我们一起学习了生成对抗网络(GAN)的多种重要改进技巧。我们从StackGAN开始,了解了如何通过两阶段结构和文本条件生成高分辨率图像。接着探讨了Progressive GAN,它通过渐进式增长生成器和判别器来稳定地生成极高分辨率的图像,并引入了小批量标准差、均衡学习率等技巧。然后,我们学习了StyleGAN,它通过分离和控制不同分辨率级别的风格属性,实现了对生成图像特征的高度可控性。之后,我们介绍了SPADE,它利用语义分割图进行空间自适应的归一化,实现了高质量的语义图像合成。最后,我们讨论了BigGAN,它通过扩大模型规模、使用自注意力、谱归一化、正交初始化与正则化等一系列技术,在类别条件图像生成上取得了突破性进展。
你的课后作业是:
-
思考并回答:为什么在Progressive GAN中使用小批量标准差?
-
思考并回答:为什么在BigGAN中使用正交正则化?
-
阅读这篇优秀的GAN综述(深度学习书第20章)以及本讲座中讨论的大多数GAN的代码链接(如果你想查看并尝试运行它们)。

我们留下的问题是:为什么在Progressive GAN中使用小批量标准差?为什么在BigGAN中使用正交正则化?思考一下,我们下次课讨论。
117:变分自编码器 🧠

在本节课中,我们将学习一种重要的深度生成模型——变分自编码器。我们将从回顾自编码器开始,逐步理解变分自编码器的核心思想、数学原理、网络结构以及训练方法。
从自编码器到生成模型 🔄
上一节我们介绍了生成对抗网络,本节中我们来看看另一种深度生成模型,称为变分自编码器,通常简称为 VAE。
在讨论变分自编码器之前,让我们先简要回顾一下上周学过的自编码器。自编码器是一种用于无监督学习的神经网络,其目标是重构输入数据本身。
自编码器的架构通常包含一个将输入压缩到瓶颈层的编码器,该瓶颈层包含输入的某种表示,也称为潜在编码。随后,解码器使用这个潜在编码来重构输出,理想情况下输出应尽可能接近输入。
在本周主题的背景下,我们想问的问题是:如何利用这种方法来自动生成图像?回想一下,当我们讨论自编码器时曾提到,训练完成后,我们通常会丢弃解码器。给定一个输入,我们仅使用编码器来获取数据的低维表示。
现在,让我们尝试反转这个思路。在训练结束时,我们能否丢弃编码器而非解码器?如果我们能使用某个向量作为代码,生成接近训练数据分布的图像,那么这个目标就与 GAN 试图实现的目标相似,但实现视角不同。
变分自编码器的诞生 📜
变分自编码器的思想大约在同一时间由两组研究人员提出:
-
Kingma 和 Welling 在 2014 年 ICLR 上发表的论文《Auto-Encoding Variational Bayes》。
-
Rezende、Mohamed 和 Wierstra 在 2014 年 ICML 上发表的论文《Stochastic Backpropagation and Variational Inference in Deep Latent Gaussian Models》。
现在,让我们尝试理解变分自编码器。
核心:潜在变量模型 🎯
VAE 的核心在于学习所谓的潜在变量模型。在潜在变量模型中,我们的目标是学习从某个潜在随机变量 Z 到一个可能很复杂的分布 X 的映射。我们假设 X 是训练数据的真实世界分布,我们希望生成与之相似的数据。
从数学上讲,可以写作:
p(x) = ∫ p(x, z) dz
其中 p(x, z) = p(x|z) * p(z)。
我们在这里假设,潜在变量 Z 的先验或边缘分布是简单的。那么如何得到 p(x|z) 呢?我们假设 p(x|z) 是输入向量 z 的某个函数。这就是这里简单的数学构造。
我们提出的问题是:在给定训练数据(这里假设是无监督学习,只有 X)的情况下,我们能否学会解耦数据分布背后真正的解释因子或潜在变量?这些潜在变量可能正在生成数据。
例如,在人脸图像中,你可能想假设存在一个名为“身份”的潜在随机变量和一个名为“表情”的潜在随机变量。因此,如果说人物 A 是快乐的,你应该能够生成人物 A 的快乐表情人脸图像。在这种情况下,身份和表情就是潜在变量。这些潜在变量可能取不同的值,身份可能取不同的人如 A、B、C,表情可能取快乐、悲伤、愤怒等不同值,组合起来就生成了特定类型的图像。
所以,我们讨论的是一组潜在变量(在此例中表示为 z1, z2)和一个函数 G,该函数将我们从这个潜在空间带到原始数据空间。我们如何学习这样一个从 Z 到 X 的函数 G?
我们将假设使用神经网络来学习这样一个从 Z 到 X 的函数。因此,这里的条件分布 p(x|z),给定一个向量 z,理想情况下我们应该能够使用神经网络来给出一个向量 x。
现在剩下的问题是:Z 从何而来?计算后验概率 p(z|x) 是难以处理的,因为我们不知道 Z 是什么样子,不幸的是,我们需要它来训练 G(z) 模型。没有 Z 的值,我们也无法训练 G(z) 模型。那么,我们如何解决这个问题?
VAE 的贝叶斯视角与直觉 🤔
到目前为止,你可以看到变分自编码器为自编码器提供了贝叶斯视角。
如果我们假设数据是通过这个生成过程产生的:Z 是一组潜在随机变量,我们假设它们属于某个先验分布,我们从真实的条件分布中采样,从而得到 X,也就是我们看到的训练数据分布。
这里的直觉是:X 可以是一张图像,它是由一些潜在属性生成的,例如类别标签、方向属性等。因此,我们可以说类别标签是人脸,方向是他/她看的方向,属性可以是肤色、眼睛颜色、头发颜色等。现在对我们来说,问题是在不知道这些 z 是什么的情况下,我们需要估计这个条件分布 p(x|z) 的参数 θ,而我们无法访问 Z。
如何做到这一点是 VAE 的核心思想。
VAE 的架构构建 🏗️
为了启动这个过程,我们现在假设随机变量 Z 上有一个先验分布,为了方便起见,我们假设它是一个单位高斯分布。一旦你有了 Z,你现在可以构建一个神经网络,我们称之为解码器网络,它具有某些参数 θ,即解码器网络中那些层的权重。解码器输出一组均值和方差,然后可以用来采样并获得类似于 X 的数据。
因此,这里的条件分布 p_θ(x|z) 被假设为对角高斯分布。这是为了简化,可以假设是具有某个协方差矩阵的多元高斯分布,但过程会变得有点复杂,我们将其保持为简单的对角高斯分布,其每个维度的均值和方差由解码器网络预测。
根据贝叶斯规则,我们知道后验概率 p_θ(z|x) 由 p_θ(x|z) * p_θ(z) / p_θ(x) 给出。
现在,如果我们观察右边的项:
-
p_θ(x|z):我们刚刚讨论了如何获得它。假设你的 z 是单位高斯分布,并学习一个可以给你输出 x 的解码器网络,这就给了你p_θ(x|z)。 -
p_θ(z)呢?我们刚刚说我们假设它是高斯分布,所以这是一个假设。 -
分母
p_θ(x)呢?不幸的是,这是一个难以处理的积分。目前我们还不知道 x 来自什么分布及其参数化。
那么,我们该如何解决这个问题?我们现在将假设存在另一个分布 q_φ(z|x),我们现在称之为近似后验分布。这是使用一个编码器网络获得的。
编码器网络的作用是:它接收你的训练数据分布(即神经网络从训练数据中接收所有输入),它有一组由某些权重参数化的层,我们用 φ 表示这些权重。它再次输出一组多元对角高斯分布的均值和方差,该分布由近似后验 q_φ(z|x) 给出。
编码器-解码器结构 🔗
如果你把所有这些部分放在一起,我们现在就有了下面的编码器和顶部的解码器。
编码器接收 x,并为这个近似后验分布 q_φ(z|x) 输出一组均值和方差。一旦你有了那个分布,你就可以从中采样一个 z。然后将该样本 z 作为输入给你的解码器网络,解码器网络输出一组均值和方差,对应于 p_θ(x|z)。给定这些均值和方差,你可以从 p_θ(x|z) 中采样以获得你的输出图像或重构。
我们在这里假设 q_φ 和 p_θ 都是具有特定均值和对角协方差的多元高斯分布,因此每个维度是独立的。
训练目标与证据下界 📉
我们如何训练这样的网络?让我们试着理解一下。
我们希望训练解码器网络,就像普通的自编码器一样。因为它将构建并给我们一个输出 x。理想情况下,我们希望使用像重构损失这样的东西,比如均方误差损失,以确保这里的重构尽可能接近 x 本身,这肯定是我们损失函数的一项。我们还需要做什么?
我们还想确保这个近似后验 q_φ(z|x) 应该接近我们假设的 Z 的先验。我们为解码器网络假设 Z 来自单位高斯先验。因此,理想情况下,我们希望 q_φ(z|x) 接近分布 p_θ(z),在我们的例子中,我们假设它是一个均值为 0、协方差矩阵为单位矩阵的高斯分布。
让我们试着把这些放在一起,得到训练这种变分自编码器的正式损失函数。
请记住,这里的目标是进行最大似然估计。我们有一个由 {x_i} 给出的训练数据,理想情况下,我们希望学习参数 θ(即解码器的参数),以最大化该分布生成训练数据点的可能性,这就是数据集的最大似然估计。
我们将把它转换为最大对数似然估计,其中乘积转换为和,然后你有 log p_θ,这在数学上使事情变得简单一些。
现在,p_θ(x) 可以定义为 ∫ p_θ(x, z) dz。这可以展开为 ∫ p_θ(x|z) * p_θ(z) dz。不幸的是,在这种情况下,我们再次留下了一个难以处理的积分。
因此,让我们尝试看看如何解决这个最大似然估计问题,我们将用一个技巧来解决它。
让我们重写最大对数似然估计问题。我们有对数似然 log p_θ(x),它可以表示为从近似后验中采样的 Z 向量对 log p_θ(x) 的期望。如果我们从你的近似后验中采样 Z 向量并作为输入给解码器,那么生成类似于训练分布的样本的可能性必须很高。这就是第一句话的含义。
现在,log p_θ(x) 可以使用贝叶斯定理写成:log p_θ(x) = log [ p_θ(x|z) * p_θ(z) / p_θ(z|x) ],这只是贝叶斯定理的不同写法。对于这个特定表达式,我们将乘以并除以近似后验 q_φ(z|x),这只是一个简单的乘以和除以一个常数。
一旦我们有了这个等价于该问题最大似然估计设置的表达式,我们可以以不同的方式分组项来书写它。
第一项 log p_θ(x|z) 保持不变,我们将其写为对 Z 的期望:E_z[log p_θ(x|z)]。
然后你有另一项 q_φ(z|x) 和 p_θ(z)。理想情况下,你可以将其写为加上对 Z 的期望:E_z[log (p_θ(z) / q_φ(z|x))]。现在,我们不加上它,而是减去它并这样写:- E_z[log (q_φ(z|x) / p_θ(z))],这只是取倒数并在开头改变符号。
然后剩下的项是这两项:分母中的 p_θ(z|x) 和分子中的 q_φ(z|x),这就是第三项给我们的。这只是分组项并以不同方式书写。
现在,如果你观察这里的第二项和第三项,你会注意到第二项是近似后验 q_φ(z|x) 和 Z 的先验 p_θ(z) 之间的 KL 散度。第三项是真实后验 p_θ(z|x) 和近似后验 q_φ(z|x) 之间的 KL 散度。
现在,如果你在这里观察,KL 散度是一个非负量,所以这里的第三项将大于或等于 0。因为这是一个对数似然估计问题,我们理想情况下希望最大化这个量。
因此,要最大化由这三项组成的量(其中第三项大于等于 0),可以通过最大化前两项本身来实现。
所以我们现在说的是,我们有一个下界,由前两项给出。记住第三项是非负的并且加在它上面,所以整个对数似然将以下面这两项为下界。
因此,如果我们想最大化对数似然,我们可以最大化这个下界。这个下界也被称为证据下界或 ELBO。这整个优化过程被称为变分推断的方法,其中我们引入了一个变分分布(在本例中,变分分布是近似后验 q_φ),并利用它来学习分布的参数化。
所以,我们现在的目标是找到 θ* 和 φ*,以最大化这个证据下界。
训练挑战与重参数化技巧 ⚙️
让我们把这些放在一起。我们现在说,我们引入了一个推理模型 q_φ(z|x)(近似后验),它试图通过优化变分下界来近似原始后验。我们使用编码器网络对 q_φ(z|x) 进行参数化。
所以,我们现在有了一个编码器和一个解码器网络。在将它们放在这个上下文中之前,让我们单独看看它们。给定训练数据 X,我们将其作为输入提供给编码器。编码器输出近似后验分布的均值和方差,我们希望它接近 Z 的先验。然后我们从 Z 采样,并使用解码器生成 x,我们希望它接近训练数据分布。
那么,我们将如何训练这个网络?
如果你看目标函数本身,这是我们要最大化的总体目标,但在这个训练过程中存在一个问题。
如果我们注意到我们所说的:我们将输入分布给编码器,编码器给出近似后验的参数化,然后我们从先验中采样,再给解码器,然后你得到最终输出。这意味着我们现在有一个编码器-解码器模型,一个自编码器,其中在中间步骤或瓶颈层步骤,我们将采样一个向量,而不是前向传播一个向量。
为什么这是一个问题?那个样本可能不是确定性的,我们不知道如何通过那个瓶颈层将梯度传递到编码器。这是一个挑战。我们该怎么办?
理想情况下,我们必须以某种方式确保我们得到的那个样本变得独立于编码器的输出,因为如果它变得独立于编码器的输出,你可以简单地通过解码器和编码器反向传播梯度,并假设随机性来自某个外部量。
我们如何将随机性与编码器输出分离?这是通过一个称为重参数化技巧的技巧完成的。这是变分自编码器方法的主要贡献之一,一个简单的技巧,但解决了通过网络反向传播的问题。
请记住,我们将从某个近似后验分布中采样 Z,该分布由具有均值 μ_z(x) 和方差/标准差 σ_z(x) 的高斯分布给出。
现在,从这样的分布中抽取的样本可以稍微不同地重写。我们可以将该样本 Z 参数化为:
z = μ_z(x) + σ_z(x) * ε
其中 ε 是从单位标准正态分布中随机采样的向量。
这如何解决了真正的问题?请记住,在之前的步骤中,到目前为止,你现在可以将其视为类似这样的东西:你在神经网络中有一组层。在变分自编码器的早期版本中,其中一层变成了随机的,你必须采样一个向量,而不是像我们迄今为止所见的那样通过神经网络前向传播的向量。
因为有一个样本,我们不知道如何通过该层反向传播到更早的层(在我们的例子中是编码器)。因此,重参数化技巧建议,让我们将该层固定为前一层的输出。相反,从外部引入这个随机性,而不是该层输出本身。
如果你在这里看到,现在的新样本由 μ_z(编码器的输出)、σ_z(也是编码器的输出)乘以 ε 给出。你现在几乎可以将 ε 视为一个偏置向量,它来自噪声。它实际上并不影响反向传播到更早的层,它只是在该特定层中给出的外部输入,现在你可以像以前一样使用梯度下降通过整个网络反向传播。
因此,你现在在编码器中给定 x,生成 μ_z 和 σ_z,然后使用重参数化技巧采样 z,通过解码器并生成输出端的 μ_x 和 σ_x,你可以从中采样并获得任何生成。
通过重参数化技巧,整个变分自编码器变得可微分,你可以使用梯度下降和反向传播来更新 VAE 中的权重并训练网络。
损失函数与训练 🧮
如前所述,这里的目标函数由下式给出:
L(θ, φ; x) = -D_KL(q_φ(z|x) || p_θ(z)) + E_{z~q_φ(z|x)}[log p_θ(x|z)]
现在,log p_θ(x|z) 结果是一个简单的重构损失,我们希望它接近你的训练分布,因此你甚至可以使用均方误差来最大化这个似然项。
那么第一项呢?我们如何对其微分以进行反向传播?关键点在于,我们假设 q_φ 是一个具有编码器提供的特定均值和方差的高斯分布。我们还假设 p_θ(z) 是单位高斯分布。高斯分布之间的 KL 散度是一个定义明确且可微分的公式,这有助于我们确保可以计算梯度并端到端地反向传播和训练这个网络。
总结与应用 🎓
这就完成了我们对 VAE 如何学习的讨论。
总结并与自编码器区分:
-
传统自编码器通过学习重构输入来训练,通常用于学习特征(低维表示),然后可用于训练另一个神经网络、SVM 或任何其他监督模型。
-
变分自编码器在概念上是贝叶斯学习和深度学习的结合。在训练变分自编码器之后,你可以丢弃编码器,只需从你的先验
p_θ(z)(单位高斯分布)中采样,现在你的解码器已经学会了生成图像,它将单位高斯分布作为输入来生成看起来接近输入数据分布的图像。这就是使 VAE 成为生成模型的原因。
VAE 能做什么?以下是几个例子,其中 VAE 在 MNIST 数据和 Frey Face 数据集上进行了训练。你可以看到,当潜在变量(即 VAE 中的 z)被微小值修改时,生成的图像(即解码器的输出)随着 Z 值的变化而逐渐变化。这表明变分自编码器确实学会了能够在其所在的流形上平滑生成数据的潜在变量值。
类似地,就像我们在 Frey Face 数据集上对 GAN 所做的那样,你可以学习诸如表情和姿势之类的潜在变量。当你改变这些对应的潜在随机变量的值时,最终会得到具有不同姿势和不同表情的人脸图像生成。
VAE 还能用于什么?它们有很多应用,我们将在下周详细讨论其中一些,但这里有几个例子:图像和视频生成、超分辨率、从静态图像进行预测、图像修复等等,我们将在下周看到其中一些。
在过去的几年里,VAE 也以多种方式得到了扩展。虽然我们可能没有在本课程中涵盖所有这些的范围,但如果你想知道更多,这里有一些提示:
-
原始作者扩展了 VAE 以获得半监督变体,其中可以使用标记和未标记数据来学习 VAE。
-
另一个流行且强大的扩展是条件 VAE,你可以在给定特定类别标签的情况下生成数据。
-
还有一种变体称为重要性加权 VAE,它对潜在随机变量进行不同的加权以获得更强大的生成。
-
然后是去噪 VAE,你可以将其视为去噪自编码器到 VAE 设置的扩展。
-
VAE 也已用于图形学,一个流行的网络称为逆图形网络,它使用类似 VAE 的策略来学习潜在变量,使你能控制生成各种类型的对象、3D 对象等。
-
最后,另一种网络称为对抗自编码器,它结合了 GAN 和 VAE,这将是我们下一讲立即讨论的主题。
课后思考与作业 📚
你本次的作业是阅读关于变分自编码器的教程,以及 PyTorch 中的 VAE 示例。如果你有兴趣,可能也值得阅读关于变分自编码器的原始论文。
这里有几个问题供你思考:
-
为什么 VAE 的编码器映射到均值向量和标准差向量?为什么编码器不映射到均值向量和协方差矩阵?(记住,编码器为你提供近似后验。)
-
类似的问题,解码器呢?如果我们假设重构损失(VAE 目标中的第二项)使用均方误差,那么
p(x|z)高斯分布的协方差是什么?你认为它看起来像什么?思考一下,我们很快会讨论。

本节课中,我们一起学习了变分自编码器的基本原理、数学推导、网络架构、训练技巧(特别是重参数化技巧)以及其作为生成模型的应用和扩展。VAE 通过结合深度学习和贝叶斯推断,提供了一种强大且可解释的生成数据方式。
118:VAE与解耦 🧩

在本节课中,我们将学习生成模型中的一个重要概念——解耦。这个概念与变分自编码器密切相关,我们将探讨其原因,并介绍实现和评估解耦的方法。
什么是解耦?
解耦旨在分离观测数据中的变化来源。例如,给定一张“大红色苹果”的图片,我们能否将其生成因素分离为:尺寸(大)、颜色(红)和物体(苹果)?我们能否在训练深度生成模型时,强制模型将这些因素隔离开来学习?
为什么需要解耦?
如果能够解耦生成因素,我们就能生成训练数据集中可能不存在的全新图像。例如,如果训练数据包含“小黑葡萄”和“大红苹果”的图片,通过解耦模型,我们可以通过设定颜色、尺寸和物体为特定值,生成“小黑苹果”的图片。这只有在生成模型的潜在变量能够隔离图像的这些组成部分时才能可靠实现。
以人脸图像为例,潜在变量可能对应性别、年龄、发型、种族等因素。如果我们知道哪个潜在变量对应性别,就可以单独操纵该变量,生成从女性到男性等不同变化的图像。
为什么是变分自编码器?
与生成对抗网络不同,GAN的潜在向量是来自高斯分布的噪声,本身不进行学习。而在变分自编码器中,潜在变量是学习得到的。如果能确保这些潜在变量是解耦的,我们就能对VAE生成的图像类型进行大量控制。
VAE的整体架构和公式如下:输入数据 x,编码器提供近似后验分布的均值和方差,该分布在训练过程中试图接近预设的先验分布。然后从先验中采样一个向量,解码器根据该采样向量重建数据。这里的潜在变量是一个多维向量,如果它们是解耦的,就能生成更可控的数据。
显然,像对抗自编码器这样的VAE-GAN框架也能从解耦潜在变量中受益。
Beta-VAE:实现解耦的方法
第一个将解耦概念引入社区并开发出实现方法的工作是Beta-VAE,发表于2017年的ICLR会议。它本质上是VAE的一个变体。
回顾VAE的损失函数,其证据下界包含两项:一项是最小化负对数似然(即最大化生成训练集中数据的对数似然),另一项是最小化近似后验与真实后验之间的KL散度。经过推导,我们最终在训练VAE时使用的是近似后验 Q_φ(z|x) 与先验 P_θ(z) 之间的KL散度。
我们可以将整个目标函数以另一种方式表述:我们希望最大化从 z 生成 x 的对数似然,同时约束近似后验 Q_φ 与先验 P_θ(z) 之间的KL散度尽可能小(小于某个正常数 δ)。这相当于在保持真实分布与近似后验分布之间距离小的同时,最大化生成真实数据的概率。
利用拉格朗日乘子法处理这个约束优化问题(类似于支持向量机的推导),我们可以得到Beta-VAE的损失函数:
L(θ, φ; x, z) = -E_{z∼Q_φ}[log P_θ(x|z)] + β * D_{KL}(Q_φ(z|x) || P_θ(z))
当 β = 1 时,即为标准VAE的损失。当 β > 1 时,它会在生成模型中引入更强的解耦能力。原因在于,损失函数的第一项旨在提高解码器的重建能力,而第二项则负责学习VAE的潜在表示。通过赋予第二项更大的权重,我们试图让潜在变量以更解耦的方式被更好地学习。
然而,这可能导致潜在变量 z 的表征能力受限,从而影响整个VAE的重建性能。这引出了一个权衡:在Beta-VAE中,增加 β 能获得更好的解耦,但重建能力会下降。
Beta-TCVAE:平衡解耦与重建
为了解决这个问题,在2018年的ICLR会议上,Chen等人提出了Beta-TCVAE方法。
Beta-TCVAE的关键在于对近似后验与先验之间的KL散度项进行分解。该KL散度可以分解为两部分:一项是潜在变量 z 与数据索引 n 之间的互信息,另一项是边际KL散度。进一步地,边际KL散度可以分解为总相关项和维度级KL散度项之和。
-
总相关:衡量潜在变量 z 的各维度之间的独立性。它是互信息在多随机变量情况下的推广。
-
维度级KL散度:衡量每个潜在维度与先验的差异。
在解耦表示中,我们希望每个维度都有独立的存在。分解表明,总相关项对解耦表示最为重要。因此,Beta-TCVAE的最终损失函数为:
L = -E[log P(x|z)] + I(z; n) + β * TC(z) + Σ_j D_{KL}(q(z_j) || p(z_j))
其中,TC(z) 是总相关项。注意,β 系数仅作用于总相关项,而不是整个KL散度项。这允许我们专注于使用 β 来促进解耦,同时不影响VAE的重建能力。
如何评估解耦效果?
评估生成模型是否有效学习了解耦表示,除了定性地生成不同图像进行检查,还有一些定量的评估指标。
互信息间隙
MIG使用生成因子 G(数据中已知的变化因素,如尺寸、颜色)与学习到的潜在维度 Z 之间的互信息进行计算。
以下是计算步骤:
-
计算每个生成因子 G_i 与每个潜在维度 Z_j 之间的互信息,得到一个矩阵。
-
对于每个生成因子 G_i,找出与其互信息值最高的两个潜在维度 Z_j 和 Z_l。
-
定义该生成因子的MIG为这两个互信息值之差,并进行归一化(除以该生成因子的熵,再对所有生成因子取平均)。
MIG(G_i) = (I(G_i; Z_j) - I(G_i; Z_l)) / H(G_i)
MIG值在0到1之间。如果MIG为0,意味着两个潜在维度都高度关联同一个生成因子,解耦效果差。如果MIG为1,则表示解耦效果好。
DCI指标
DCI代表解耦性、完备性和信息量。计算步骤如下:
-
使用待评估模型(如Beta-VAE)获取数据集中所有图像的潜在表示 z。
-
训练 K 个线性回归器 f_1 到 f_K,每个回归器使用整个潜在向量 z 来预测一个生成因子 G。
-
从这些回归器中得到一个相对重要性矩阵 R,其中元素 R_ij 表示潜在维度 Z_i 对预测生成因子 G_j 的重要性。
基于矩阵 R,定义三个指标:
-
解耦性:衡量表示解耦底层变化因素的程度。理想情况下,一个潜在维度应只对应一个生成因子。
-
完备性:衡量每个底层生成因子是否由单个潜在变量捕获。理想情况下,一个生成因子应只由一个潜在维度解释。
-
信息量:衡量解耦的潜在表示在捕获底层因素方面的有用性,通常通过预测生成因子的误差来衡量。
总结
本节课我们一起学习了生成模型中的解耦概念。我们了解到,解耦旨在分离数据中的独立变化源,这对于实现可控的图像生成至关重要。我们重点介绍了两种在VAE框架中实现解耦的方法:Beta-VAE通过增加KL散度项的权重来促进解耦,但可能牺牲重建质量;Beta-TCVAE则通过分解KL散度,将权重专注于总相关项,从而更好地平衡解耦与重建。最后,我们探讨了评估解耦效果的定量指标,包括互信息间隙和DCI指标。

作为课后作业,建议阅读关于从自编码器到Beta-VAE的优秀博客文章,以及关于VAE解耦的综述,并思考在MIG指标中为什么使用互信息差值而不是互信息本身。
119:扩散模型与DDPM介绍 - 第一部分

概述
在本节课中,我们将学习一种新的深度生成模型——扩散模型,特别是去噪扩散概率模型。我们将从回顾上周的内容开始,然后深入探讨扩散模型的基本原理、数学基础以及其核心组成部分。
课程内容
回顾与引言
上一节我们讨论了生成对抗网络和变分自编码器等深度生成模型。本节中,我们将转向另一类深度生成模型,这类模型近年来改变了图像和视频生成的格局,它们就是扩散模型。
在深入讨论扩散模型之前,我们先快速回顾一下上节课留下的关于变分自编码器和解耦表示的问题。问题是:为什么我们使用互信息间隙,而不是互信息本身,作为解耦的度量标准?
原因在于,如果某个生成因子G1与两个潜在变量L1和L2的互信息都很高,这表明L1和L2没有充分解耦,它们都在捕获同一个生成因子。互信息间隙度量了L1与G1的互信息和L2与G1的互信息之间的差异,这有助于惩罚未对齐的潜在变量,从而成为一个良好的解耦度量标准。
生成模型概览
生成模型的目标是学习数据的底层分布,以便模型能够生成与原始数据分布相似的新样本。
我们见过生成对抗网络,它包含生成器和判别器之间的竞争过程。我们也见过变分自编码器,它是一种带有编码器和解码器的概率方法,其中间的潜在层具有概率模型,从中采样并由解码器生成新样本。
归一化流是另一类生成模型,本质上是可逆变换,用于建模数据分布。但本周的主题是扩散模型,它通过逆转一个加噪过程来生成数据。
扩散模型高层介绍
深度学习中的扩散模型灵感来源于物理扩散过程本身,即粒子因随机运动随时间扩散开来。
扩散模型包含一个前向过程,该过程逐渐向数据添加噪声,最终导致图像看起来就像高斯噪声。你从一个想要复制和生成更多样本的起始图像开始,在多个时间步长上不断添加噪声,直到达到纯高斯噪声。
扩散模型中的反向过程则完全相反,它从噪声开始,尝试去噪,最终得到原始数据,这就是生成的部分。训练模型以准确逆转扩散过程,可以从噪声分布本身恢复原始数据分布,这是扩散模型的核心思想。
去噪扩散概率模型是扩散模型的一种特定类型,它对反向扩散过程使用概率方法。这个反向扩散过程被建模为一序列学习的条件分布。
整个扩散模型由一个马尔可夫前向过程(其中每一步添加少量噪声)和一个学习的反向过程(我们用神经网络参数化,在每一步对数据进行去噪)来表征。
DDPM在图像、视频甚至分子结构的生成方面都显示出令人印象深刻的结果。流行的图像生成工具,如DALL-E和Stable Diffusion,都是扩散模型实现的例子。
预备知识
在深入探讨扩散模型或DDPM的工作原理之前,我们需要先了解一些预备知识。
高斯分布
高斯分布,也称为正态分布,是一种概率分布,通常以其钟形曲线为特征。其概率密度函数由以下公式给出:
其中,\(\mu\) 是均值,\(\sigma^2\) 是方差。这在DDPM的实现中至关重要。
随机过程
随机过程可以看作是随时间变化的随机变量。它是一个由时间 \(t\) 索引的随机变量集合 \(\{X_t\}\),表示概率系统随时间的演化。随机过程的每个实现都是系统可能遵循的一条路径或轨迹。
随机过程的一些关键属性包括:
-
平稳性:随着时间的推移,该随机过程在期望中保持相同的分布。
-
独立性:任何给定时间发生的事件不依赖于其他任何时间步的结果。
-
马尔可夫性质:当前状态仅依赖于前一个状态,而不依赖于更早的历史。
一个简单的理解方式是:如果你从A地随机走到B地,在每一步,你都可以自由地走向任何方向(独立性),而你在任何一步的行动只取决于你到达上一步时的坐标(马尔可夫性质)。
马尔可夫链
马尔可夫链是具有马尔可夫性质的随机过程,这意味着未来状态仅取决于当前状态。马尔可夫链通常使用转移矩阵来定义,该矩阵给出了从一个状态转移到另一个状态的概率矩阵。
维纳过程
维纳过程是一个连续时间随机过程,具有以下性质:
-
\(W_0 = 0\),过程从0开始。
-
对于任意两个时间步 \(s\) 和 \(t\)(\(0 \le s < t\)),\(W_t - W_s\) 的差值服从均值为0、标准差为 \(\sqrt{t-s}\) 的高斯分布。
-
增量在非重叠时间区间内是独立的。
-
过程的路径以概率1连续。
维纳过程是金融、物理和工程中许多应用的基础构建块,例如金融市场的布莱克-斯科尔斯期权定价模型。
随机微分方程
随机微分方程通常用于对具有随机行为的系统进行建模,这些系统可能同时受到确定性成分和随机成分的影响。SDE的一般形式是:
其中:
-
\(X_t\) 是时间 \(t\) 的状态变量。
-
\(\mu(X_t, t)\) 被称为漂移系数,代表确定性趋势。

浙公网安备 33010602011771号