14.6. Набор данных для обнаружения объектов

В области обнаружения объектов нет такого небольшого набора данных, как MNIST и Fashion-MNIST. Чтобы быстро продемонстрировать модели обнаружения объектов, мы собрали и размечали небольшой набор данных. Сначала мы сделали фотографии бананов, оставленных в офисе, и сгенерировали 1000 изображений бананов с различными поворотами и размерами. Затем мы разместили каждое изображение банана в случайной позиции на некоторых фоновых изображениях. В конце мы размечали ограничивающие рамки для этих бананов на изображениях.

14.6.1. Загрузка набора данных

Набор данных для обнаружения бананов, включающий все файлы изображений и CSV-файлы с метками, можно напрямую загрузить из Интернета.

%matplotlib inline
import os
import pandas as pd
import torch
import torchvision
from d2l import torch as d2l
#@save
d2l.DATA_HUB['banana-detection'] = (
    d2l.DATA_URL + 'banana-detection.zip',
    '5de26c8fce5ccdea9f91267273464dc968d20d72')

14.6.2. Чтение набора данных

Мы будем читать набор данных для обнаружения бананов с помощью функции read_data_bananas, приведённой ниже. Набор данных включает csv-файл с метками классов объектов и координатами ограничивающих рамок, заданными по верхнему левому и нижнему правому углам.

#@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

Используя функцию read_data_bananas для чтения изображений и меток, следующий класс BananasDataset позволит нам создать пользовательский объект Dataset для загрузки набора данных обнаружения бананов.

#@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)

Наконец, мы определяем функцию load_data_bananas, чтобы возвращать два экземпляра итераторов данных для обучающего и тестового наборов. Для тестового набора данных нет необходимости считывать его в случайном порядке.

#@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

Давайте прочитаем мини-батч и выведем формы изображений и меток в этом мини-батче. Форма мини-батча изображений (размер батча, количество каналов, высота, ширина) выглядит привычно: она такая же, как и в наших предыдущих задачах классификации изображений. Форма мини-батча меток имеет вид (размер батча, \(m\), 5), где \(m\) — это максимальное количество ограничивающих рамок, которое может содержать любое изображение в наборе данных.

Хотя вычисления в мини-батчах более эффективны, это требует, чтобы все примеры изображений содержали одинаковое количество ограничивающих рамок для формирования мини-батча путем конкатенации. Как правило, изображения могут иметь разное количество ограничивающих рамок; поэтому изображения с меньшим количеством, чем \(m\), ограничивающих рамок, будут дополнены недопустимыми ограничивающими рамками до тех пор, пока не будет достигнуто значение \(m\). Затем метка каждой ограничивающей рамки представляется массивом длины 5. Первый элемент массива — это класс объекта в ограничивающей рамке, где -1 указывает на недопустимую ограничивающую рамку, используемую для дополнения. Оставшиеся четыре элемента массива — это координаты (\(x\), \(y\)) верхнего левого и нижнего правого углов ограничивающей рамки (диапазон значений от 0 до 1). Для набора данных бананов, поскольку на каждом изображении есть только одна ограничивающая рамка, мы имеем \(m=1\).

batch_size, edge_size = 32, 256
train_iter, _ = load_data_bananas(batch_size)
batch = next(iter(train_iter))
batch[0].shape, batch[1].shape

14.6.3. Демонстрация

Давайте продемонстрируем десять изображений с их истинными размеченными ограничивающими рамками. Мы видим, что повороты, размеры и положения бананов варьируются на всех этих изображениях. Конечно, это всего лишь простой искусственный набор данных. На практике реальные наборы данных обычно гораздо сложнее.

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'])

14.6.4. Резюме

  • Собранный нами набор данных для обнаружения бананов можно использовать для демонстрации моделей обнаружения объектов.

  • Загрузка данных для обнаружения объектов аналогична загрузке для классификации изображений. Однако в обнаружении объектов метки также содержат информацию об истинных ограничивающих рамках, которой нет в классификации изображений.

14.6.5. Упражнения

  1. Продемонстрируйте другие изображения с истинными ограничивающими рамками в наборе данных обнаружения бананов. Чем они отличаются по ограничивающим рамкам и объектам?

  2. Предположим, мы хотим применить аугментацию данных, например случайное кадрирование, к обнаружению объектов. Чем это будет отличаться от подхода, используемого в классификации изображений? Подсказка: что делать, если на кадрированном изображении видна лишь небольшая часть объекта?

Обсуждения