{ "cells": [ { "cell_type": "markdown", "id": "66010731", "metadata": { "origin_pos": 0 }, "source": [ "# 多尺度目标检测\n", ":label:`sec_multiscale-object-detection`\n", "\n", "在 :numref:`sec_anchor`中,我们以输入图像的每个像素为中心,生成了多个锚框。\n", "基本而言,这些锚框代表了图像不同区域的样本。\n", "然而,如果为每个像素都生成的锚框,我们最终可能会得到太多需要计算的锚框。\n", "想象一个$561 \\times 728$的输入图像,如果以每个像素为中心生成五个形状不同的锚框,就需要在图像上标记和预测超过200万个锚框($561 \\times 728 \\times 5$)。\n", "\n", "## 多尺度锚框\n", ":label:`subsec_multiscale-anchor-boxes`\n", "\n", "减少图像上的锚框数量并不困难。\n", "比如,我们可以在输入图像中均匀采样一小部分像素,并以它们为中心生成锚框。\n", "此外,在不同尺度下,我们可以生成不同数量和不同大小的锚框。\n", "直观地说,比起较大的目标,较小的目标在图像上出现的可能性更多样。\n", "例如,$1 \\times 1$、$1 \\times 2$和$2 \\times 2$的目标可以分别以4、2和1种可能的方式出现在$2 \\times 2$图像上。\n", "因此,当使用较小的锚框检测较小的物体时,我们可以采样更多的区域,而对于较大的物体,我们可以采样较少的区域。\n", "\n", "为了演示如何在多个尺度下生成锚框,让我们先读取一张图像。\n", "它的高度和宽度分别为561和728像素。\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "5e98dd98", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T07:01:14.742245Z", "iopub.status.busy": "2023-08-18T07:01:14.741240Z", "iopub.status.idle": "2023-08-18T07:01:17.317727Z", "shell.execute_reply": "2023-08-18T07:01:17.316623Z" }, "origin_pos": 2, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "(561, 728)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%matplotlib inline\n", "import torch\n", "from d2l import torch as d2l\n", "\n", "img = d2l.plt.imread('../img/catdog.jpg')\n", "h, w = img.shape[:2]\n", "h, w" ] }, { "cell_type": "markdown", "id": "e73d91ad", "metadata": { "origin_pos": 4 }, "source": [ "回想一下,在 :numref:`sec_conv_layer`中,我们将卷积图层的二维数组输出称为特征图。\n", "通过定义特征图的形状,我们可以确定任何图像上均匀采样锚框的中心。\n", "\n", "`display_anchors`函数定义如下。\n", "我们[**在特征图(`fmap`)上生成锚框(`anchors`),每个单位(像素)作为锚框的中心**]。\n", "由于锚框中的$(x, y)$轴坐标值(`anchors`)已经被除以特征图(`fmap`)的宽度和高度,因此这些值介于0和1之间,表示特征图中锚框的相对位置。\n", "\n", "由于锚框(`anchors`)的中心分布于特征图(`fmap`)上的所有单位,因此这些中心必须根据其相对空间位置在任何输入图像上*均匀*分布。\n", "更具体地说,给定特征图的宽度和高度`fmap_w`和`fmap_h`,以下函数将*均匀地*对任何输入图像中`fmap_h`行和`fmap_w`列中的像素进行采样。\n", "以这些均匀采样的像素为中心,将会生成大小为`s`(假设列表`s`的长度为1)且宽高比(`ratios`)不同的锚框。\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "b7ab8598", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T07:01:17.322639Z", "iopub.status.busy": "2023-08-18T07:01:17.321969Z", "iopub.status.idle": "2023-08-18T07:01:17.328192Z", "shell.execute_reply": "2023-08-18T07:01:17.327078Z" }, "origin_pos": 6, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def display_anchors(fmap_w, fmap_h, s):\n", " d2l.set_figsize()\n", " # 前两个维度上的值不影响输出\n", " fmap = torch.zeros((1, 10, fmap_h, fmap_w))\n", " anchors = d2l.multibox_prior(fmap, sizes=s, ratios=[1, 2, 0.5])\n", " bbox_scale = torch.tensor((w, h, w, h))\n", " d2l.show_bboxes(d2l.plt.imshow(img).axes,\n", " anchors[0] * bbox_scale)" ] }, { "cell_type": "markdown", "id": "4f1d121e", "metadata": { "origin_pos": 8 }, "source": [ "首先,让我们考虑[**探测小目标**]。\n", "为了在显示时更容易分辨,在这里具有不同中心的锚框不会重叠:\n", "锚框的尺度设置为0.15,特征图的高度和宽度设置为4。\n", "我们可以看到,图像上4行和4列的锚框的中心是均匀分布的。\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "b8088160", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T07:01:17.333487Z", "iopub.status.busy": "2023-08-18T07:01:17.332535Z", "iopub.status.idle": "2023-08-18T07:01:17.807920Z", "shell.execute_reply": "2023-08-18T07:01:17.806753Z" }, "origin_pos": 9, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-08-18T07:01:17.694754\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.5.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "display_anchors(fmap_w=4, fmap_h=4, s=[0.15])" ] }, { "cell_type": "markdown", "id": "05c390b8", "metadata": { "origin_pos": 10 }, "source": [ "然后,我们[**将特征图的高度和宽度减小一半,然后使用较大的锚框来检测较大的目标**]。\n", "当尺度设置为0.4时,一些锚框将彼此重叠。\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "28b6ce1d", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T07:01:17.812435Z", "iopub.status.busy": "2023-08-18T07:01:17.811538Z", "iopub.status.idle": "2023-08-18T07:01:18.128829Z", "shell.execute_reply": "2023-08-18T07:01:18.127647Z" }, "origin_pos": 11, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-08-18T07:01:18.034151\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.5.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "display_anchors(fmap_w=2, fmap_h=2, s=[0.4])" ] }, { "cell_type": "markdown", "id": "ef16223f", "metadata": { "origin_pos": 12 }, "source": [ "最后,我们进一步[**将特征图的高度和宽度减小一半,然后将锚框的尺度增加到0.8**]。\n", "此时,锚框的中心即是图像的中心。\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "bcc1b8c9", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T07:01:18.134209Z", "iopub.status.busy": "2023-08-18T07:01:18.133433Z", "iopub.status.idle": "2023-08-18T07:01:18.417530Z", "shell.execute_reply": "2023-08-18T07:01:18.416424Z" }, "origin_pos": 13, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-08-18T07:01:18.352215\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.5.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "display_anchors(fmap_w=1, fmap_h=1, s=[0.8])" ] }, { "cell_type": "markdown", "id": "b6a349bc", "metadata": { "origin_pos": 14 }, "source": [ "## 多尺度检测\n", "\n", "既然我们已经生成了多尺度的锚框,我们就将使用它们来检测不同尺度下各种大小的目标。\n", "下面,我们介绍一种基于CNN的多尺度目标检测方法,将在 :numref:`sec_ssd`中实现。\n", "\n", "在某种规模上,假设我们有$c$张形状为$h \\times w$的特征图。\n", "使用 :numref:`subsec_multiscale-anchor-boxes`中的方法,我们生成了$hw$组锚框,其中每组都有$a$个中心相同的锚框。\n", "例如,在 :numref:`subsec_multiscale-anchor-boxes`实验的第一个尺度上,给定10个(通道数量)$4 \\times 4$的特征图,我们生成了16组锚框,每组包含3个中心相同的锚框。\n", "接下来,每个锚框都根据真实值边界框来标记了类和偏移量。\n", "在当前尺度下,目标检测模型需要预测输入图像上$hw$组锚框类别和偏移量,其中不同组锚框具有不同的中心。\n", "\n", "\n", "假设此处的$c$张特征图是CNN基于输入图像的正向传播算法获得的中间输出。\n", "既然每张特征图上都有$hw$个不同的空间位置,那么相同空间位置可以看作含有$c$个单元。\n", "根据 :numref:`sec_conv_layer`中对感受野的定义,特征图在相同空间位置的$c$个单元在输入图像上的感受野相同:\n", "它们表征了同一感受野内的输入图像信息。\n", "因此,我们可以将特征图在同一空间位置的$c$个单元变换为使用此空间位置生成的$a$个锚框类别和偏移量。\n", "本质上,我们用输入图像在某个感受野区域内的信息,来预测输入图像上与该区域位置相近的锚框类别和偏移量。\n", "\n", "当不同层的特征图在输入图像上分别拥有不同大小的感受野时,它们可以用于检测不同大小的目标。\n", "例如,我们可以设计一个神经网络,其中靠近输出层的特征图单元具有更宽的感受野,这样它们就可以从输入图像中检测到较大的目标。\n", "\n", "简言之,我们可以利用深层神经网络在多个层次上对图像进行分层表示,从而实现多尺度目标检测。\n", "在 :numref:`sec_ssd`,我们将通过一个具体的例子来说明它是如何工作的。\n", "\n", "## 小结\n", "\n", "* 在多个尺度下,我们可以生成不同尺寸的锚框来检测不同尺寸的目标。\n", "* 通过定义特征图的形状,我们可以决定任何图像上均匀采样的锚框的中心。\n", "* 我们使用输入图像在某个感受野区域内的信息,来预测输入图像上与该区域位置相近的锚框类别和偏移量。\n", "* 我们可以通过深入学习,在多个层次上的图像分层表示进行多尺度目标检测。\n", "\n", "## 练习\n", "\n", "1. 根据我们在 :numref:`sec_alexnet`中的讨论,深度神经网络学习图像特征级别抽象层次,随网络深度的增加而升级。在多尺度目标检测中,不同尺度的特征映射是否对应于不同的抽象层次?为什么?\n", "1. 在 :numref:`subsec_multiscale-anchor-boxes`中的实验里的第一个尺度(`fmap_w=4, fmap_h=4`)下,生成可能重叠的均匀分布的锚框。\n", "1. 给定形状为$1 \\times c \\times h \\times w$的特征图变量,其中$c$、$h$和$w$分别是特征图的通道数、高度和宽度。怎样才能将这个变量转换为锚框类别和偏移量?输出的形状是什么?\n" ] }, { "cell_type": "markdown", "id": "0876c3a0", "metadata": { "origin_pos": 16, "tab": [ "pytorch" ] }, "source": [ "[Discussions](https://discuss.d2l.ai/t/2948)\n" ] } ], "metadata": { "language_info": { "name": "python" }, "required_libs": [] }, "nbformat": 4, "nbformat_minor": 5 }