- 文档
- 教程
- 人工智能
- 深度学习
- NLP
- 计算机视觉
- 强化学习
【免费下载链接】d2l-en
Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.
导读
目标检测任务需要"图像 + 物体类别 + 真实边界框(ground-truth bounding box)"三类信息的配套数据集,其数据组织方式与图像分类有着本质差异。本文以《动手学深度学习》(D2L)仓库中自带的香蕉检测数据集为核心,完整讲解该数据集的构建思路、下载方式、CSV 标注格式、read_data_bananas/BananasDataset/load_data_bananas三层数据加载管线,以及 mini-batch 中标签的填充(padding)机制,并结合 d2l/torch.py 与 d2l/mxnet.py 中的源码实现进行底层验证。读完本文,你将掌握目标检测数据集从标注文件到可训练DataLoader的完整组织范式,为后续阅读 SSD、R-CNN 等检测模型(如 chapter_computer-vision/ssd.md)打下数据基础。
为什么目标检测需要一个专门的小数据集
在图像分类领域,研究者习惯使用 MNIST、Fashion-MNIST 这类"小而标准"的数据集来快速验证模型。但在目标检测领域,并没有同等规模的标准小数据集——公开的检测数据集(如 VOC、COCO)体积大、标注复杂,不适合在教材和快速原型中反复加载。
为此,原文档作者采取了一个非常工程化的做法:人工构造一个 1000 张图片的香蕉检测数据集。具体流程是:
- 拍摄办公室里的免费香蕉照片;
- 通过对照片进行不同旋转、缩放,生成 1000 张香蕉图像;
- 将每张香蕉图像随机放置到某些背景图上;
- 为每张图像标注香蕉的边界框。
这种"合成 + 人工标注"的方式,让研究者可以在几乎零成本的前提下,获得一个包含完整目标(类别)与位置(边界框)监督信号的数据集,用于快速演示目标检测模型。需要强调的是,它只是一个简单的合成数据集,真实世界的数据集通常复杂得多。
数据集的下载:DATA_HUB 注册与 SHA-1 校验
在 D2L 中,所有可下载数据集都统一注册在d2l.DATA_HUB字典中,香蕉检测数据集也不例外:
#@save d2l.DATA_HUB['banana-detection'] = ( d2l.DATA_URL + 'banana-detection.zip', '5de26c8fce5ccdea9f91267273464dc968d20d72')注册条目是一个二元组:第一项是完整下载地址(由d2l.DATA_URL拼接文件名得到),第二项是该压缩包的SHA-1 校验值。在 d2l/torch.py 中可以看到DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/',即文件最终指向banana-detection.zip的公开镜像。
SHA-1 校验值的作用体现在底层下载函数download中(见 d2l/torch.py):
- 先检查本地缓存:若目标文件已存在于默认目录
../data,则逐块(每次 1 MB)计算其 SHA-1 并与注册值比对,一致则直接复用,避免重复下载; - 不一致或文件不存在时,才通过
requests.get重新下载。
随后的download_extract(见 d2l/torch.py)负责解压:根据扩展名区分.zip与.tar/.gz,解压到数据目录并返回解压后的文件夹路径。整个下载、校验、解压流程对用户完全透明,调用方只需一句d2l.download_extract('banana-detection')。
数据集目录结构与 CSV 标注格式
解压后的香蕉检测数据集采用训练集 / 验证集分离的目录结构:
banana-detection/ ├── bananas_train/ │ ├── label.csv # 训练集标注 │ └── images/ # 训练图像 └── bananas_val/ ├── label.csv # 验证集标注 └── images/ # 验证图像每个子集的标注统一存放在label.csv中,以图像文件名为索引。每行记录包含 5 个数值,含义为:
| 字段 | 含义 |
|---|---|
| 第 1 项 | 目标类别索引(香蕉数据集全部图像只有香蕉一个类别,索引为 0) |
| 第 2、3 项 | 边界框左上角顶点的 $(x, y)$ 坐标 |
| 第 4、5 项 | 边界框右下角顶点的 $(x, y)$ 坐标 |
需要特别说明的是,CSV 中的坐标以像素为单位,取值范围大致在 0 到 256 之间(对应 256×256 的图像边长)。而模型训练通常偏好归一化坐标,因此读取代码会在末尾统一除以 256(详见下文),将坐标缩放到 $[0, 1]$ 区间。
读取数据:read_data_bananas 的实现剖析
read_data_bananas(is_train)是数据管线的第一层,负责把 CSV 标注与图像文件读入内存。以 PyTorch 版本为例(见 d2l/torch.py):
#@save def read_data_bananas(is_train=True): """Read the banana detection dataset images and labels.""" data_dir = d2l.download_extract('banana-detection') csv_fname = os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'label.csv') csv_data = pd.read_csv(csv_fname) csv_data = csv_data.set_index('img_name') images, targets = [], [] for img_name, target in csv_data.iterrows(): images.append(torchvision.io.read_image( os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}'))) # Here `target` contains (class, upper-left x, upper-left y, # lower-right x, lower-right y), where all the images have the same # banana class (index 0) targets.append(list(target)) return images, torch.tensor(targets).unsqueeze(1) / 256代码的关键处理步骤:
- 延迟下载解压:通过
d2l.download_extract('banana-detection')首次调用时自动下载并解压,之后命中本地缓存; - 按
is_train选择子集:True读取bananas_train/label.csv,False读取bananas_val/label.csv; - 以
img_name为索引:set_index('img_name')后逐行迭代,用图像文件名拼接出实际图片路径并读入; - 坐标归一化:
torch.tensor(targets).unsqueeze(1) / 256是关键一步——unsqueeze(1)在类别维后插入一个长度为 1 的维度(详见下一节"标签形状"),除以 256 则把像素坐标归一化到 $[0, 1]$。
在 MXNet 版本(见 d2l/mxnet.py)中,差异仅在于读图 API 换成image.imread,以及通过np.expand_dims(np.array(targets), 1) / 256完成同样的升维与归一化,整体逻辑完全一致。
标签形状 (batch, m, 5) 与非法框填充机制
为什么读取时要unsqueeze(1)升维?这与目标检测 mini-batch 的标签组织方式密切相关。在演示代码中:
#@tab all batch_size, edge_size = 32, 256 train_iter, _ = load_data_bananas(batch_size) batch = next(iter(train_iter)) batch[0].shape, batch[1].shape运行后会得到两个形状:
- 图像 mini-batch:
(32, 3, 256, 256),即(batch size, 通道数, 高, 宽),与图像分类任务完全一致; - 标签 mini-batch:
(32, m, 5),其中m是数据集中任意单张图像所能包含的最大边界框数量。
这里存在一个普遍性问题:mini-batch 计算要求 batch 内所有样本可通过拼接(concatenation)形成张量,但真实场景中不同图像的边界框数量往往不同。解决方案是填充(padding):图像边界框数量少于 $m$ 时,用"非法边界框"补齐到 $m$ 个。
每个边界框的标签是一个长度为 5 的数组:
- 第 1 个元素是框内目标的类别,
-1表示填充产生的非法边界框(区别于真实类别的非负索引); - 后 4 个元素是左上角、右下角两顶点的 $(x, y)$ 坐标,取值范围 $[0, 1]$(已归一化)。
对香蕉数据集而言,每张图像恰好只有一个边界框,因此 $m = 1$,unsqueeze(1)后标签形状即为(batch, 1, 5)。以非法框-1作为类别占位符的设计,在后续基于锚框的目标检测实现(如 chapter_computer-vision/anchor.md 中assign_anchor_to_bbox的-1哨兵值)中一脉相承,是贯穿 D2L 检测章节的统一约定。
自定义 Dataset 与 DataLoader 封装
BananasDataset:把读取逻辑封装为标准 Dataset
第二层封装是继承框架标准 Dataset 基类的BananasDataset,其职责是让香蕉数据接入框架原生的数据加载体系:
#@tab pytorch #@save class BananasDataset(torch.utils.data.Dataset): """A customized dataset to load the banana detection dataset.""" def __init__(self, is_train): self.features, self.labels = read_data_bananas(is_train) print('read ' + str(len(self.features)) + (f' training examples' if is_train else f' validation examples')) def __getitem__(self, idx): return (self.features[idx].float(), self.labels[idx]) def __len__(self): return len(self.features)要点:
__init__一次性调用read_data_bananas把全部图像与标签载入内存,并打印样本数量,方便确认训练集 / 验证集规模;__getitem__返回(图像, 标签)对,PyTorch 版本把图像转为float32(MXNet 版本则先astype('float32')再transpose(2, 0, 1)把 HWC 转为 CHW),保证与模型输入约定一致;__len__返回样本总数,供DataLoader计算迭代轮数。
load_data_bananas:一键返回训练 / 验证迭代器
第三层封装load_data_bananas(batch_size)组装出两个可直接用于训练循环的迭代器:
#@tab pytorch #@save def load_data_bananas(batch_size): """Load the banana detection dataset.""" train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True), batch_size, shuffle=True) val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False), batch_size) return train_iter, val_iter细节值得注意:训练集迭代器开启shuffle=True以打乱样本顺序(随机梯度下降需要);验证集则保持顺序读取,shuffle取默认的False,便于结果复现与逐样本评估。该函数在 d2l/torch.py 与 d2l/mxnet.py 中均有完整实现,可直接from d2l import torch as d2l后调用。
可视化演示:展示图像与边界框
数据加载完成后,原文档用一个演示片段可视化 10 张图像及其真实边界框:
#@tab pytorch imgs = (batch[0][:10].permute(0, 2, 3, 1)) / 255 axes = d2l.show_images(imgs, 2, 5, scale=2) for ax, label in zip(axes, batch[1][:10]): d2l.show_bboxes(ax, [label[0][1:5] * edge_size], colors=['w'])这里的处理链条是:
permute(0, 2, 3, 1)(MXNet 用transpose(0, 2, 3, 1))把 CHW 换回 HWC 以便绘制,并除以 255 把像素值归一化到 $[0, 1]$;d2l.show_images(imgs, 2, 5, scale=2)将 10 张图排成 2 行 5 列展示;- 对每张图的标签
label[0][1:5]取出归一化的边界框坐标,乘以edge_size = 256还原为像素坐标,再调用d2l.show_bboxes绘制白色边界框(底层实现见 d2l/torch.py,核心是bbox_to_rect生成 matplotlib Rectangle 并axes.add_patch)。
从可视化结果可以看到,10 张图片中香蕉的旋转角度、尺寸和摆放位置各不相同——这正体现了数据集构造时引入的多样性,也说明它是一个便于调试检测流程的合成数据集。仓库中的 img/banana.jpg 即属于该类香蕉素材,在 chapter_computer-vision/ssd.md 中还被用作训练完成后推理演示的输入图片。
总结
回顾本文核心结论:
- 香蕉检测数据集由 D2L 团队人工构造(1000 张、单一类别、每图一个边界框),用于快速演示目标检测模型,可从互联网直接下载,并通过注册在
d2l.DATA_HUB中的 SHA-1 校验值保证数据完整性; - 数据加载流程与图像分类相似:同样经过"读图 → 封装 Dataset → 构造 DataLoader"的管线;但目标检测的标签额外携带真实边界框坐标,这是图像分类标签所不具备的;
- 标签统一组织为
(batch, m, 5)张量:坐标归一化到 $[0, 1]$,第 1 维类别中-1用作填充非法边界框的哨兵值,保证变长标注可以拼成定长 mini-batch; - 坐标系约定:CSV 原始坐标以像素为单位,读取时除以
edge_size(256)完成归一化,可视化时再反向乘以边长还原。
动手练习
- 展示更多样本:修改演示代码,随机选取香蕉数据集中其他图像并绘制真实边界框,观察不同图像的边界框与物体外观差异(如旋转角度、遮挡、背景干扰)。
- 思考数据增强的差异:若要对目标检测应用随机裁剪等数据增强,它与图像分类中的数据增强有何不同?提示:裁剪后的图像如果只包含物体的一小部分,边界框标注应当如何处理(例如保留、调整还是丢弃)?这一问题的工程答案会在 SSD 等检测模型的数据加载实现(见 chapter_computer-vision/ssd.md)中体现。
- 文档
- 教程
- 人工智能
- 深度学习
- NLP
- 计算机视觉
- 强化学习
【免费下载链接】d2l-en
Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.
相关推荐
《动手学深度学习》目标检测数据集指南:香蕉检测数据集的下载、加载与边界框标注格式
《动手学深度学习》目标检测数据集指南:香蕉检测数据集的下载、加载与边界框标注格式 导读 在目标检测领域,并不存在像 MNIST、Fashion MNIST 那样
人工智能深度学习机器学习教程目标检测数据集构建与处理:基于Gluon-Tutorials的香蕉检测案例
目标检测数据集构建与处理:基于Gluon Tutorials的香蕉检测案例 目标检测数据集概述 目标检测是计算机视觉中的重要任务,它不仅要识别图像中的物体类别,
人工智能深度学习机器学习教程动手学深度学习目标检测数据集:香蕉检测数据集的下载、读取与边界框标签详解
动手学深度学习目标检测数据集:香蕉检测数据集的下载、读取与边界框标签详解 目标检测任务缺少像 MNIST、Fashion MNIST 那样小巧的标准数据集,为了
人工智能深度学习机器学习教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考