泊寓平台第四次打卡 task09

目标检测基础

锚框

目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边缘从而更准确地预测目标的真实边界框(ground-truth bounding box)。不同的模型使用的区域采样方法可能不同。这里我们介绍其中的一种方法:它以每个像素为中心生成多个大小和宽高比(aspect ratio)不同的边界框。这些边界框被称为锚框(anchor box)。我们将在后面基于锚框实践目标检测。

生成多个锚框

假设输入图像高为 hh,宽为ww。我们分别以图像的每个像素为中心生成不同形状的锚框。设大小为s(0,1]s\in (0,1]且宽高比为r>0r > 0,那么锚框的宽和高将分别为wsrws\sqrt{r}hs/rhs/\sqrt{r}。当中心位置给定时,已知宽和高的锚框是确定的。

下面我们分别设定好一组大小s1,,sns_1,\ldots,s_n和一组宽高比r1,,rmr_1,\ldots,r_m。如果以每个像素为中心时使用所有的大小与宽高比的组合,输入图像将一共得到whnmwhnm个锚框。虽然这些锚框可能覆盖了所有的真实边界框,但计算复杂度容易过高。因此,我们通常只对包含s1s_1r1r_1的大小与宽高比的组合感兴趣,即

(s1,r1),(s1,r2),,(s1,rm),(s2,r1),(s3,r1),,(sn,r1). (s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s_n, r_1).

也就是说,以相同像素为中心的锚框的数量为n+m1n+m-1。对于整个输入图像,我们将一共生成wh(n+m1)wh(n+m-1)个锚框。

以上生成锚框的方法已实现在MultiBoxPrior函数中。指定输入、一组大小和一组宽高比,该函数将返回输入的所有锚框。

交并化

我们刚刚提到某个锚框较好地覆盖了图像中的狗。如果该目标的真实边界框已知,这里的“较好”该如何量化呢?一种直观的方法是衡量锚框和真实边界框之间的相似度。我们知道,Jaccard系数(Jaccard index)可以衡量两个集合的相似度。给定集合A\mathcal{A}B\mathcal{B},它们的Jaccard系数即二者交集大小除以二者并集大小:

J(A,B)=ABAB. J(\mathcal{A},\mathcal{B}) = \frac{\left|\mathcal{A} \cap \mathcal{B}\right|}{\left| \mathcal{A} \cup \mathcal{B}\right|}.

实际上,我们可以把边界框内的像素区域看成是像素的集合。如此一来,我们可以用两个边界框的像素集合的Jaccard系数衡量这两个边界框的相似度。当衡量两个边界框的相似度时,我们通常将Jaccard系数称为交并比(Intersection over Union,IoU),即两个边界框相交面积与相并面积之比,如图9.2所示。交并比的取值范围在0和1之间:0表示两个边界框无重合像素,1表示两个边界框相等。

泊寓平台第四次打卡 task09

标注训练集的锚框

在训练集中,我们将每个锚框视为一个训练样本。为了训练目标检测模型,我们需要为每个锚框标注两类标签:一是锚框所含目标的类别,简称类别;二是真实边界框相对锚框的偏移量,简称偏移量(offset)。在目标检测时,我们首先生成多个锚框,然后为每个锚框预测类别以及偏移量,接着根据预测的偏移量调整锚框位置从而得到预测边界框,最后筛选需要输出的预测边界框。

我们知道,在目标检测的训练集中,每个图像已标注了真实边界框的位置以及所含目标的类别。在生成锚框之后,我们主要依据与锚框相似的真实边界框的位置和类别信息为锚框标注。那么,该如何为锚框分配与其相似的真实边界框呢?

假设图像中锚框分别为A1,A2,,AnaA_1, A_2, \ldots, A_{n_a},真实边界框分别为B1,B2,,BnbB_1, B_2, \ldots, B_{n_b},且nanbn_a \geq n_b。定义矩阵XRna×nb\boldsymbol{X} \in \mathbb{R}^{n_a \times n_b},其中第ii行第jj列的元素xijx_{ij}为锚框AiA_i与真实边界框BjB_j的交并比。
首先,我们找出矩阵X\boldsymbol{X}中最大元素,并将该元素的行索引与列索引分别记为i1,j1i_1,j_1。我们为锚框Ai1A_{i_1}分配真实边界框Bj1B_{j_1}。显然,锚框Ai1A_{i_1}和真实边界框Bj1B_{j_1}在所有的“锚框—真实边界框”的配对中相似度最高。接下来,将矩阵X\boldsymbol{X}中第i1i_1行和第j1j_1列上的所有元素丢弃。找出矩阵X\boldsymbol{X}中剩余的最大元素,并将该元素的行索引与列索引分别记为i2,j2i_2,j_2。我们为锚框Ai2A_{i_2}分配真实边界框Bj2B_{j_2},再将矩阵X\boldsymbol{X}中第i2i_2行和第j2j_2列上的所有元素丢弃。此时矩阵X\boldsymbol{X}中已有两行两列的元素被丢弃。
依此类推,直到矩阵X\boldsymbol{X}中所有nbn_b列元素全部被丢弃。这个时候,我们已为nbn_b个锚框各分配了一个真实边界框。
接下来,我们只遍历剩余的nanbn_a - n_b个锚框:给定其中的锚框AiA_i,根据矩阵X\boldsymbol{X}的第ii行找到与AiA_i交并比最大的真实边界框BjB_j,且只有当该交并比大于预先设定的阈值时,才为锚框AiA_i分配真实边界框BjB_j

如图9.3(左)所示,假设矩阵X\boldsymbol{X}中最大值为x23x_{23},我们将为锚框A2A_2分配真实边界框B3B_3。然后,丢弃矩阵中第2行和第3列的所有元素,找出剩余阴影部分的最大元素x71x_{71},为锚框A7A_7分配真实边界框B1B_1。接着如图9.3(中)所示,丢弃矩阵中第7行和第1列的所有元素,找出剩余阴影部分的最大元素x54x_{54},为锚框A5A_5分配真实边界框B4B_4。最后如图9.3(右)所示,丢弃矩阵中第5行和第4列的所有元素,找出剩余阴影部分的最大元素x92x_{92},为锚框A9A_9分配真实边界框B2B_2。之后,我们只需遍历除去A2,A5,A7,A9A_2, A_5, A_7, A_9的剩余锚框,并根据阈值判断是否为剩余锚框分配真实边界框。

泊寓平台第四次打卡 task09

现在我们可以标注锚框的类别和偏移量了。如果一个锚框AA被分配了真实边界框BB,将锚框AA的类别设为BB的类别,并根据BBAA的中心坐标的相对位置以及两个框的相对大小为锚框AA标注偏移量。由于数据集中各个框的位置和大小各异,因此这些相对位置和相对大小通常需要一些特殊变换,才能使偏移量的分布更均匀从而更容易拟合。设锚框AA及其被分配的真实边界框BB的中心坐标分别为(xa,ya)(x_a, y_a)(xb,yb)(x_b, y_b)AABB的宽分别为waw_awbw_b,高分别为hah_ahbh_b,一个常用的技巧是将AA的偏移量标注为

(xbxawaμxσx,ybyahaμyσy,logwbwaμwσw,loghbhaμhσh), \left( \frac{ \frac{x_b - x_a}{w_a} - \mu_x }{\sigma_x}, \frac{ \frac{y_b - y_a}{h_a} - \mu_y }{\sigma_y}, \frac{ \log \frac{w_b}{w_a} - \mu_w }{\sigma_w}, \frac{ \log \frac{h_b}{h_a} - \mu_h }{\sigma_h}\right),

其中常数的默认值为μx=μy=μw=μh=0,σx=σy=0.1,σw=σh=0.2\mu_x = \mu_y = \mu_w = \mu_h = 0, \sigma_x=\sigma_y=0.1, \sigma_w=\sigma_h=0.2。如果一个锚框没有被分配真实边界框,我们只需将该锚框的类别设为背景。类别为背景的锚框通常被称为负类锚框,其余则被称为正类锚框。

下面演示一个具体的例子。我们为读取的图像中的猫和狗定义真实边界框,其中第一个元素为类别(0为狗,1为猫),剩余4个元素分别为左上角的xxyy轴坐标以及右下角的xxyy轴坐标(值域在0到1之间)。这里通过左上角和右下角的坐标构造了5个需要标注的锚框,分别记为A0,,A4A_0, \ldots, A_4(程序中索引从0开始)。先画出这些锚框与真实边界框在图像中的位置。

输出预测锚框

在模型预测阶段,我们先为图像生成多个锚框,并为这些锚框一一预测类别和偏移量。随后,我们根据锚框及其预测偏移量得到预测边界框。当锚框数量较多时,同一个目标上可能会输出较多相似的预测边界框。为了使结果更加简洁,我们可以移除相似的预测边界框。常用的方法叫作非极大值抑制(non-maximum suppression,NMS)。

我们来描述一下非极大值抑制的工作原理。对于一个预测边界框BB,模型会计算各个类别的预测概率。设其中最大的预测概率为pp,该概率所对应的类别即BB的预测类别。我们也将pp称为预测边界框BB的置信度。在同一图像上,我们将预测类别非背景的预测边界框按置信度从高到低排序,得到列表LL。从LL中选取置信度最高的预测边界框B1B_1作为基准,将所有与B1B_1的交并比大于某阈值的非基准预测边界框从LL中移除。这里的阈值是预先设定的超参数。此时,LL保留了置信度最高的预测边界框并移除了与其相似的其他预测边界框。
接下来,从LL中选取置信度第二高的预测边界框B2B_2作为基准,将所有与B2B_2的交并比大于某阈值的非基准预测边界框从LL中移除。重复这一过程,直到LL中所有的预测边界框都曾作为基准。此时LL中任意一对预测边界框的交并比都小于阈值。最终,输出列表LL中的所有预测边界框。

下面来看一个具体的例子。先构造4个锚框。简单起见,我们假设预测偏移量全是0:预测边界框即锚框。最后,我们构造每个类别的预测概率。

样式迁移

如果你是一位摄影爱好者,也许接触过滤镜。它能改变照片的颜色样式,从而使风景照更加锐利或者令人像更加美白。但一个滤镜通常只能改变照片的某个方面。如果要照片达到理想中的样式,经常需要尝试大量不同的组合,其复杂程度不亚于模型调参。

在本节中,我们将介绍如何使用卷积神经网络自动将某图像中的样式应用在另一图像之上,即样式迁移(style transfer)[1]。这里我们需要两张输入图像,一张是内容图像,另一张是样式图像,我们将使用神经网络修改内容图像使其在样式上接近样式图像。图9.12中的内容图像为本书作者在西雅图郊区的雷尼尔山国家公园(Mount Rainier National Park)拍摄的风景照,而样式图像则是一副主题为秋天橡树的油画。最终输出的合成图像在保留了内容图像中物体主体形状的情况下应用了样式图像的油画笔触,同时也让整体颜色更加鲜艳。

泊寓平台第四次打卡 task09

方法

图9.13用一个例子来阐述基于卷积神经网络的样式迁移方法。首先,我们初始化合成图像,例如将其初始化成内容图像。该合成图像是样式迁移过程中唯一需要更新的变量,即样式迁移所需迭代的模型参数。然后,我们选择一个预训练的卷积神经网络来抽取图像的特征,其中的模型参数在训练中无须更新。深度卷积神经网络凭借多个层逐级抽取图像的特征。我们可以选择其中某些层的输出作为内容特征或样式特征。以图9.13为例,这里选取的预训练的神经网络含有3个卷积层,其中第二层输出图像的内容特征,而第一层和第三层的输出被作为图像的样式特征。接下来,我们通过正向传播(实线箭头方向)计算样式迁移的损失函数,并通过反向传播(虚线箭头方向)迭代模型参数,即不断更新合成图像。样式迁移常用的损失函数由3部分组成:内容损失(content loss)使合成图像与内容图像在内容特征上接近,样式损失(style loss)令合成图像与样式图像在样式特征上接近,而总变差损失(total variation loss)则有助于减少合成图像中的噪点。最后,当模型训练结束时,我们输出样式迁移的模型参数,即得到最终的合成图像。

泊寓平台第四次打卡 task09

下面,我们通过实验来进一步了解样式迁移的技术细节。实验需要用到一些导入的包或模块。

预处理和后处理图像

下面定义图像的预处理函数和后处理函数。预处理函数preprocess对输入图像在RGB三个通道分别做标准化,并将结果变换成卷积神经网络接受的输入格式。后处理函数postprocess则将输出图像中的像素值还原回标准化之前的值。由于图像打印函数要求每个像素的浮点数值在0到1之间,我们使用clamp函数对小于0和大于1的值分别取0和1。

抽取特征

我们使用基于ImageNet数据集预训练的VGG-19模型来抽取图像特征 [1]。
为了抽取图像的内容特征和样式特征,我们可以选择VGG网络中某些层的输出。一般来说,越靠近输入层的输出越容易抽取图像的细节信息,反之则越容易抽取图像的全局信息。为了避免合成图像过多保留内容图像的细节,我们选择VGG较靠近输出的层,也称内容层,来输出图像的内容特征。我们还从VGG中选择不同层的输出来匹配局部和全局的样式,这些层也叫样式层。在“使用重复元素的网络(VGG)”一节中我们曾介绍过,VGG网络使用了5个卷积块。实验中,我们选择第四卷积块的最后一个卷积层作为内容层,以及每个卷积块的第一个卷积层作为样式层。这些层的索引可以通过打印pretrained_net实例来获取。
在抽取特征时,我们只需要用到VGG从输入层到最靠近输出层的内容层或样式层之间的所有层。下面构建一个新的网络net,它只保留需要用到的VGG的所有层。我们将使用net来抽取特征。

定义损失函数

下面我们来描述样式迁移的损失函数。它由内容损失、样式损失和总变差损失3部分组成。

内容损失

与线性回归中的损失函数类似,内容损失通过平方误差函数衡量合成图像与内容图像在内容特征上的差异。平方误差函数的两个输入均为extract_features函数计算所得到的内容层的输出。

样式损失

样式损失也一样通过平方误差函数衡量合成图像与样式图像在样式上的差异。为了表达样式层输出的样式,我们先通过extract_features函数计算样式层的输出。假设该输出的样本数为1,通道数为cc,高和宽分别为hhww,我们可以把输出变换成cchwhw列的矩阵X\boldsymbol{X}。矩阵X\boldsymbol{X}可以看作是由cc个长度为hwhw的向量x1,,xc\boldsymbol{x}_1, \ldots, \boldsymbol{x}_c组成的。其中向量xi\boldsymbol{x}_i代表了通道ii上的样式特征。这些向量的格拉姆矩阵(Gram matrix)XXRc×c\boldsymbol{X}\boldsymbol{X}^\top \in \mathbb{R}^{c \times c}iijj列的元素xijx_{ij}即向量xi\boldsymbol{x}_ixj\boldsymbol{x}_j的内积,它表达了通道ii和通道jj上样式特征的相关性。我们用这样的格拉姆矩阵表达样式层输出的样式。需要注意的是,当hwhw的值较大时,格拉姆矩阵中的元素容易出现较大的值。此外,格拉姆矩阵的高和宽皆为通道数cc。为了让样式损失不受这些值的大小影响,下面定义的gram函数将格拉姆矩阵除以了矩阵中元素的个数,即chwchw

总变差损失

有时候,我们学到的合成图像里面有大量高频噪点,即有特别亮或者特别暗的颗粒像素。一种常用的降噪方法是总变差降噪(total variation denoising)。假设xi,jx_{i,j}表示坐标为(i,j)(i,j)的像素值,降低总变差损失

i,jxi,jxi+1,j+xi,jxi,j+1 \sum_{i,j} \left|x_{i,j} - x_{i+1,j}\right| + \left|x_{i,j} - x_{i,j+1}\right|

能够尽可能使邻近的像素值相似。

损失函数

样式迁移的损失函数即内容损失、样式损失和总变差损失的加权和。通过调节这些权值超参数,我们可以权衡合成图像在保留内容、迁移样式以及降噪三方面的相对重要性。

Kaggle上的图像分类(CIFAR-10)