From a62cb2fab11d0c2a969839db053d2e09953b0b5f Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Mon, 5 Nov 2018 03:55:04 +0000 Subject: [PATCH] ssd multi dim det --- TERMINOLOGY.md | 4 ++++ chapter_computer-vision/anchor.md | 20 ++++++++++++++++ chapter_computer-vision/ssd.md | 38 ++++++++++++++++++++----------- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/TERMINOLOGY.md b/TERMINOLOGY.md index 097d7b5..d6e52ef 100644 --- a/TERMINOLOGY.md +++ b/TERMINOLOGY.md @@ -78,6 +78,8 @@ 负采样,negative sampling +感受野,receptive field + 共现词频,co-occurrence frequency 归一化,normalization @@ -172,6 +174,8 @@ softmax回归,softmax regression 特征,feature +特征图,feature map + 特征值,eigenvalue 梯度,gradient diff --git a/chapter_computer-vision/anchor.md b/chapter_computer-vision/anchor.md index 2a6a4b1..035faa0 100644 --- a/chapter_computer-vision/anchor.md +++ b/chapter_computer-vision/anchor.md @@ -30,11 +30,31 @@ $$(s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s ```{.python .input n=2} img = image.imread('../img/catdog.jpg').asnumpy() h, w = img.shape[0:2] + +print(h,w) X = nd.random.uniform(shape=(1, 3, h, w)) # 构造输入数据。 Y = contrib.nd.MultiBoxPrior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]) Y.shape ``` +```{.json .output n=2} +[ + { + "name": "stdout", + "output_type": "stream", + "text": "561 728\n" + }, + { + "data": { + "text/plain": "(1, 2042040, 4)" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } +] +``` + 我们看到,返回锚框变量`y`的形状为(批量大小,锚框个数,4)。将锚框变量`y`的形状变为(图像高,图像宽,以相同像素为中心的锚框个数,4)后,我们就可以通过指定像素位置来获取所有以该像素为中心的锚框了。下面例子里我们访问以(250,250)为中心的第一个锚框。它有四个元素,分别是锚框左上角和右下角的$x$和$y$轴坐标。其中$x$和$y$轴的坐标值分别已除以图像的宽和高,因此值域均为0和1之间。 ```{.python .input n=3} diff --git a/chapter_computer-vision/ssd.md b/chapter_computer-vision/ssd.md index b22ae8b..13bc4ff 100644 --- a/chapter_computer-vision/ssd.md +++ b/chapter_computer-vision/ssd.md @@ -1,23 +1,15 @@ # 单发多框检测(SSD) -我们在前几节分别介绍了边界框、锚框、数据集等背景知识,下面我们来构造一个目标检测模型:单发多框检测(single shot multibox detection,简称SSD)[1]。它简单、快速,并得到了广泛使用。本节也将通过单发多框检测来详解目标检测的实现细节。 +我们在前几节分别介绍了边界框、锚框、数据集等背景知识,下面我们来构造一个目标检测模型:单发多框检测(single shot multibox detection,简称SSD)[1]。它简单、快速,并得到了广泛使用。该模型的部分设计思想和实现细节同样适用于其他目标检测模型。 -## 模型 - -图9.4描述了SSD模型。给定输入图像,其首先使用主要由卷积层组成的模块来进行特征抽取。在其特征输出上,我们以每个像素为中心构建多个锚框(往左的箭头),然后使用softmax来对每个锚框判断其包含的目标类别,以及用卷积直接预测它到真实目标边界框的距离。卷积层的输出同时被输入到一个高宽减半模块(往上的箭头)来缩小图像尺寸。这个模块的输出将进入到另一个卷积模块抽取特征,并构建锚框来预测目标类别和边界框。这样设计的目的是在不同的尺度上进行目标检测,例如前一层的锚框主要检测图像中尺寸较小的目标,而这一层我们则检测尺寸稍大的目标。自然的,我们会重复这一过程多次以保证在多种不同的尺度下检测目标。 - -![SSD模型。](../img/ssd.svg) - -接下来我们介绍并实现图中各个模块。由于这里锚框是由上小节介绍的方法生成而来,我们不再赘述。接下来直接介绍类别预测和边界框预测。 - -### 类别预测 +## 多尺度检测目标 -假设数据集中有$n$种不同的目标类别,那么我们将对每个锚框做$n+1$类分类,其中类0表示锚框只包含背景。因为我们对输入像素为中心生成多个锚框,记为$a$,我们将会预测$hwa$个锚框的分类结果,这里$h$和$w$分别是输入高和宽。如果使用全连接层作为输出,可能会导致有过多的模型参数。回忆[“网络中的网络:NiN”](../chapter_convolutional-neural-networks/nin.md)这一节里我们介绍了使用卷积层的通道来输出类别预测,SSD采用同样的方法来降低模型复杂度。 +在[“锚框”](anchor.md)一节中,我们在实验中以输入图像的每个像素为中心生成多个锚框。这些锚框是对输入图像不同区域的采样。然而,如果以图像每个像素为中心都生成锚框,很容易生成过多锚框而造成计算量过大。举个例子,假设输入图像高和宽都是512像素,如果以每个像素为中心生成4个不同形状的锚框,那么一张图像上则需要标注并预测一百多万个锚框($512 \times 512 \times 4$)。 -具体来说,类别预测层使用一个保持输入高宽的卷积层,其输出的$(x,y)$像素通道里包含了以输入$(x,y)$像素为中心的所有锚框的类别预测。其输出通道数为$a(n+1)$,其中通道$i(n+1)$是第$i$个锚框预测的背景置信度,而通道$i(n+1)+j+1$则是第$i$锚框预测的第$j$类目标的置信度。 +一个简单的方案是在输入图像中均匀采样一小部分像素,并以采样的像素为中心生成锚框。但仅仅这样还不够。直觉上,较小目标比较大目标在图像上出现位置的可能性更多。举个简单的例子,形状为$1 \times 1$、$1 \times 2$和$2 \times 2$的目标在形状为$2 \times 2$的图像上可能出现的位置分别有4、2和1种。因此,当使用较小锚框来检测较小目标时,我们可以采样较多的区域;而当使用较大锚框来检测较大目标时,我们可以采样较少的区域。这种方法叫做多尺度检测目标。 -下面我们定义一个这样的类别分类器,指定$a$和$n$后,它使用一个填充为1的$3\times3$卷积层。注意到我们使用了较小的卷积窗口,它可能不能覆盖锚框定义的区域。所以我们需要保证前面的卷积层能有效的将较大的锚框区域的特征浓缩到一个$3\times3$的窗口里。 +为了演示多尺度检测目标,我们先读取一张图像。它的高和宽分别为512像素。 ```{.python .input} import sys @@ -30,6 +22,10 @@ from mxnet.gluon import loss as gloss, nn import time img = image.imread('../img/pikachu.jpg') +img.shape[0:2] +``` + +```{.python .input} bbox_scale = nd.array(img.shape[0:2] * 2) def display_anchors(fmap_w, fmap_h, s): @@ -48,6 +44,22 @@ display_anchors(fmap_w=2, fmap_h=2, s=[0.3]) display_anchors(fmap_w=4, fmap_h=4, s=[0.15]) ``` +## 模型 + +图9.4描述了SSD模型。给定输入图像,其首先使用主要由卷积层组成的模块来进行特征抽取。在其特征输出上,我们以每个像素为中心构建多个锚框(往左的箭头),然后使用softmax来对每个锚框判断其包含的目标类别,以及用卷积直接预测它到真实目标边界框的距离。卷积层的输出同时被输入到一个高宽减半模块(往上的箭头)来缩小图像尺寸。这个模块的输出将进入到另一个卷积模块抽取特征,并构建锚框来预测目标类别和边界框。这样设计的目的是在不同的尺度上进行目标检测,例如前一层的锚框主要检测图像中尺寸较小的目标,而这一层我们则检测尺寸稍大的目标。自然的,我们会重复这一过程多次以保证在多种不同的尺度下检测目标。 + +![SSD模型。](../img/ssd.svg) + +接下来我们介绍并实现图中各个模块。由于这里锚框是由上小节介绍的方法生成而来,我们不再赘述。接下来直接介绍类别预测和边界框预测。 + +### 类别预测 + +假设数据集中有$n$种不同的目标类别,那么我们将对每个锚框做$n+1$类分类,其中类0表示锚框只包含背景。因为我们对输入像素为中心生成多个锚框,记为$a$,我们将会预测$hwa$个锚框的分类结果,这里$h$和$w$分别是输入高和宽。如果使用全连接层作为输出,可能会导致有过多的模型参数。回忆[“网络中的网络:NiN”](../chapter_convolutional-neural-networks/nin.md)这一节里我们介绍了使用卷积层的通道来输出类别预测,SSD采用同样的方法来降低模型复杂度。 + +具体来说,类别预测层使用一个保持输入高宽的卷积层,其输出的$(x,y)$像素通道里包含了以输入$(x,y)$像素为中心的所有锚框的类别预测。其输出通道数为$a(n+1)$,其中通道$i(n+1)$是第$i$个锚框预测的背景置信度,而通道$i(n+1)+j+1$则是第$i$锚框预测的第$j$类目标的置信度。 + +下面我们定义一个这样的类别分类器,指定$a$和$n$后,它使用一个填充为1的$3\times3$卷积层。注意到我们使用了较小的卷积窗口,它可能不能覆盖锚框定义的区域。所以我们需要保证前面的卷积层能有效的将较大的锚框区域的特征浓缩到一个$3\times3$的窗口里。 + ```{.python .input n=2} def cls_predictor(num_anchors, num_classes): return nn.Conv2D(num_anchors * (num_classes + 1), kernel_size=3, -- GitLab