Компьютерное зрение и обнаружение объектов

Понимание и внедрение Faster R-CNN: пошаговое руководство

Демистификация обнаружения объектов

Я впервые познакомился с обнаружением объектов через Tensorflow Object Detection API. Это было просто в использовании. Я передал изображение пляжа, а взамен API нарисовал прямоугольники над распознанными объектами. Это казалось волшебным. Мне стало любопытно и захотелось разобрать API, чтобы понять, как оно на самом деле работает под капотом. Было тяжело, и я потерпел неудачу. API Tensorflow Object Detection поддерживает самые современные модели, которые являются результатом десятилетий исследований. Они замысловато вплетены в код, подобно тому, как часовщик собирает крошечные маленькие шестеренки, которые движутся слаженно.

Тем не менее, большинство современных современных моделей построены на фундаменте, заложенном моделью Faster-RCNN, которая даже сегодня остается одной из самых цитируемых статей в области компьютерного зрения. Поэтому очень важно понять это.

В этой статье мы разберем документ Faster-RCNN, поймем его работу и по частям создадим его в PyTorch, чтобы понять нюансы.

Более быстрый обзор R-CNN

Для обнаружения объектов нам нужно построить модель и научить ее учиться как распознавать, так и локализировать объекты на изображении. В модели Faster R-CNN используется следующий подход: изображение сначала проходит через магистральную сеть, чтобы получить выходную карту объектов, а ограничивающие прямоугольники истинного изображения проецируются на карту объектов. Магистральная сеть обычно является плотной сверточной сетью, такой как ResNet или VGG16. Выходная карта объектов представляет собой пространственно плотный тензор, который представляет изученные функции изображения. Затем мы рассматриваем каждую точку на этой карте объектов как якорь. Для каждого якоря мы генерируем несколько блоков разных размеров и форм. Целью этих якорных блоков является захват объектов на изображении.

Мы используем сверточную сеть 1x1 для прогнозирования категории и смещений всех блоков привязки. Во время обучения мы отбираем блоки привязки, которые больше всего перекрываются с проецируемыми блоками истинности. Они называются позитивными или активированными блоками привязки. Мы также отбираем блоки отрицательных привязок, которые практически не пересекаются с блоками исходной информации. Положительным полям привязки назначается категория object, а отрицательным полям назначается background. Сеть учится классифицировать блоки привязки, используя бинарную кросс-энтропийную потерю. Теперь поля положительной привязки могут не точно совпадать с проецируемыми наземными рамками истины. Таким образом, мы обучаем аналогичную 1x1 свёрточную сеть, чтобы научиться предсказывать смещения от наземных блоков истинности. Эти смещения, примененные к полям привязки, приближают их к полям истинности земли. Мы используем потери регрессии L2, чтобы узнать смещения. Блоки привязки преобразуются с использованием предсказанных смещений и называются предложениями по регионам, а описанная выше сеть называется сеть предложений по регионам. Это этап 1 детектора. Faster-RCNN — двухэтапный детектор. Есть еще один этап.

Входными данными для этапа 2 являются предложения по регионам, созданные на этапе 1. На этапе 2 мы учимся предсказывать категорию объекта в предложении по региону с помощью простой сверточной сети. Теперь необработанные предложения регионов имеют разные размеры, поэтому мы используем метод, называемый объединением ROI, чтобы изменить их размер перед передачей через сеть. Эта сеть учится предсказывать несколько категорий, используя кросс-энтропийную потерю. Мы используем другую сеть для прогнозирования смещений предложений регионов от наземных блоков истинности. Эта сеть также пытается согласовать предложения регионов с наземными ящиками правды. Это использует потери регрессии L2. Наконец, мы берем взвешенную комбинацию обоих убытков, чтобы вычислить окончательный убыток. На этапе 2 мы учимся предсказывать как категорию, так и смещения. Это называется многозадачным обучением.

Все это происходит во время тренировки. Во время вывода мы передаем изображение через магистральную сеть и генерируем блоки привязки — так же, как и раньше. Однако на этот раз мы выбираем только первые ~300 ящиков, которые получают высокий классификационный балл на этапе 1, и квалифицируем их для этапа 2. На этапе 2 мы прогнозируем окончательные категории и смещения. Кроме того, мы выполняем дополнительный этап постобработки для удаления повторяющихся ограничивающих рамок с помощью метода, который называется немаксимальное подавление. Если все работает должным образом, детектор распознает и закрашивает объекты на изображении прямоугольниками, как показано ниже:

Это краткий обзор двухэтапной сети Faster-RCNN. В следующих разделах мы углубимся в каждую из частей.

Настройка среды

Весь используемый код можно найти в этом репозитории GitHub. Нам не нужно много зависимостей, так как мы будем строить с нуля. Достаточно будет только библиотеки PyTorch, установленной в стандартной среде anaconda.

Вот основной блокнот, с которым мы будем работать. Просто просмотрите его. Мы рассмотрим это шаг за шагом в следующих разделах.

Подготовка и загрузка данных

Во-первых, нам понадобятся несколько образцов изображений для работы. Вот я скачал два изображения в высоком разрешении отсюда.

Далее нам нужно пометить эти изображения. CVAT — один из популярных инструментов маркировки с открытым исходным кодом. Скачать его можно бесплатно отсюда.

Вы можете просто загрузить изображения в инструмент, нарисовать прямоугольники вокруг соответствующих объектов и отметить их категорию, как показано ниже:

После этого вы можете экспортировать аннотации в предпочтительный формат. Здесь я экспортировал их в формате CVAT for images 1.1 xml.

Файлы аннотаций содержат всю информацию об изображении, помеченных классах и координатах ограничивающей рамки.

Набор данных PyTorch и DataLoader

В PyTorch считается лучшей практикой создавать класс, который наследуется от класса Dataset PyTorch для загрузки данных. Это даст нам больше контроля над данными и поможет сохранить модульность кода. Кроме того, мы можем создать PyTorch DataLoader из экземпляра набора данных, который может автоматически позаботиться о пакетной обработке, перетасовке и выборке данных.

В приведенном выше классе мы определили функцию с именем get_data, которая загружает файл аннотаций и анализирует его для извлечения путей к изображениям, помеченных классов и координат ограничивающей рамки, которые затем преобразуются в объект PyTorch Tensor. Изображения преобразуются в фиксированный размер.

Обратите внимание, что мы дополняем ограничивающие рамки. Это, в сочетании с resize, позволяет нам объединять изображения вместе.

Если в пакете более двух изображений с переменным количеством объектов в каждом из них, мы рассматриваем максимальное количество объектов в любом изображении и дополняем остальные значением -1, чтобы соответствовать максимальной длине, как показано на рисунке выше. Мы дополняем координаты ограничивающей рамки, а также категории.

Мы можем взять несколько изображений из DataLoader и визуализировать их, как показано ниже:

Прохождение через магистральную сеть

Здесь мы будем использовать ResNet 50 в качестве магистральной сети. Помните, что один блок в ResNet 50 состоит из стеков узких мест. Изображение уменьшается вдвое после каждого блока по пространственному измерению, а количество каналов удваивается. Слой узкого места состоит из трех сверточных слоев вместе с пропускным соединением, как показано ниже:

Мы будем использовать первые четыре блока ResNet 50 в качестве магистральной сети.

Как только изображение проходит через магистральную сеть, оно подвергается субдискретизации по пространственному измерению. Результатом является многофункциональное представление изображения.

Если мы пропустим изображение размера (640, 480) через магистральную сеть, мы получим выходную карту объектов размером (15, 20). Таким образом, изображение было уменьшено на (32, 32).

Создание опорных точек

Мы рассматриваем каждую точку на карте объектов как точку привязки. Таким образом, точки привязки будут просто массивом, представляющим координаты по ширине и высоте.

Чтобы визуализировать эти опорные точки, мы можем просто спроецировать их на пространство изображения, умножив их на масштабные коэффициенты ширины и высоты.

Генерация блоков привязки

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

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

Опять же, чтобы визуализировать блоки привязки, мы проецируем их на пространство изображения, умножая на коэффициенты масштабирования ширины и высоты.

Вот как это выглядит, если мы визуализируем все поля привязки для всех точек привязки:

Подготовка данных

В этом разделе мы обсудим подготовку данных для обучения.

Положительные и отрицательные якорные блоки

Нам нужно только попробовать несколько якорных ящиков для обучения. Мы пробуем как положительные, так и отрицательные якорные блоки. Блоки положительной привязки содержат объект, а блоки отрицательной привязки — нет. Чтобы отобрать блоки положительной привязки, мы выбираем блоки привязки, которые имеют IoU более 0,7 с любым из основных блоков истинности или те, которые имеют самый высокий IoU для каждого основного блока истинности. Когда якорные блоки генерируются плохо, условие 1 не выполняется, поэтому на помощь приходит условие 2, поскольку оно выбирает один положительный блок для каждого основного блока истинности. Чтобы сэмплировать отрицательные поля привязки, мы выбираем поля привязки, которые имеют IoU менее 0,3 с любым из полей наземной истины. Обычно количество отрицательных образцов намного больше, чем положительных. Поэтому мы случайным образом выбираем несколько, чтобы соответствовать количеству положительных образцов. IoU — это метрика, которая измеряет перекрытие между двумя ограничивающими прямоугольниками.

Вышеприведенная функция вычисляет матрицу IoU, которая содержит IoU каждого блока привязки со всеми блоками наземной истины на изображении. Он принимает блоки привязки формы (B, w_amap, h_amap, n_anc_boxes, 4) и блоки истинности формы (B, max_objects, 4) в качестве входных данных и возвращает матрицу формы (B, anc_boxes_tot, max_objects), где обозначения следующие:

B - Batch Size
w_amap - width of the output activation map
h_wmap - height of the output activation map
n_anc_boxes - number of anchor boxes per an anchor point
max_objects - max number of objects in a batch of images
anc_boxes_tot - total number of anchor boxes in the image i.e, w_amap * h_amap * n_anc_boxes

Функция по существу сглаживает все поля привязки и вычисляет IoU для каждого поля наземной истины, как показано ниже:

Проецирование ящиков правды на землю

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

Теперь, когда мы делим координаты на коэффициент масштабирования, мы округляем значение до ближайшего целого числа. По сути, это означает, что мы «привязываем» поле истинности к ближайшей сетке в пространстве признаков. Поэтому, если разница в масштабе пространства изображения и пространства признаков велика, мы не получим точных проекций. Следовательно, при обнаружении объектов важно работать с изображениями высокого разрешения.

Вычисление смещения

Блоки положительной привязки не совсем совпадают с блоками наземной истины. Таким образом, мы вычисляем смещения между положительными якорными ячейками и наземными полями истинности и обучаем нейронную сеть для изучения этих смещений. Смещения можно вычислить следующим образом:

tx_ = (gt_cx - anc_cx) / anc_w
ty_ = (gt_cy - anc_cy) / anc_h
tw_ = log(gt_w / anc_w)
th_ = log(gt_h / anc_h)
Where:
gt_cx, gt_cy - centers of ground truth boxes
anc_cx, anc_cy - centers of anchor boxes
gt_w, gt_h - width and height of ground truth boxes
anc_w, anc_h - width and height of anchor boxes

Для вычисления того же можно использовать следующую функцию:

Если вы заметили, мы учим сеть узнавать, насколько поле привязки отличается от поля истины. Мы не заставляем его предсказывать точное местоположение и масштаб блока привязки. Следовательно, смещения и преобразования, полученные сетью, не зависят от местоположения и масштаба.

Пошаговое руководство по коду

Давайте пройдемся по коду подготовки данных. Это, наверное, самая важная функция во всем репозитории.

Основными входными данными для этой функции являются сгенерированные поля привязки и спроецированные поля истинности для пакета изображений.

Сначала мы вычисляем матрицу IoU, используя функцию, описанную выше. Затем из этой матрицы мы берем IoU наиболее перекрывающегося поля привязки для каждого поля наземной истины. Это условие 1 для выборки положительных якорных блоков. Мы также применяем условие 2 и выбираем поля привязки, у которых IoU превышает пороговое значение для любого поля наземной истины на изображении. Мы комбинируем условия 1 и 2 и пробуем поля положительной привязки для всех изображений.

Каждое изображение будет иметь разное количество положительных образцов. Чтобы избежать этого несоответствия во время обучения, мы сглаживаем пакет и объединяем положительные образцы со всех изображений. Более того, мы можем отслеживать, откуда берется каждый положительный образец, используя torch.where.

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

Чтобы выполнить это сопоставление, мы сначала расширяем поля истинности основания, чтобы они соответствовали полям общей привязки, используя Tensor.expand. Затем для каждого якорного поля мы выбираем основное поле истинности, с которым оно больше всего перекрывается. Для этого мы берем максимальные индексы IoU для всех блоков привязки из матрицы IoU, а затем «собираем» по этим индексам, используя torch.gather. Наконец, мы выравниваем партию и фильтруем положительные образцы. Процесс проиллюстрирован ниже:

Мы выполняем тот же процесс с категориями, чтобы назначить их каждому положительному образцу.

Теперь, когда у нас есть поле истинности, сопоставленное для каждой положительной выборки, мы можем вычислить смещения, используя функцию, описанную выше.

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

Вот как выглядят поля положительной и отрицательной привязки:

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

Построение модели

Модуль предложения

Начнем с модуля предложения. Как мы уже говорили, каждая точка на карте объектов считается якорем, и каждый якорь создает блоки разных размеров и форм. Мы хотим классифицировать каждый из этих ящиков как object или background. Более того, мы хотим предсказать их смещения от соответствующих полей истинности. Как мы можем сделать это? Решение состоит в использовании 1x1 слоев свертки. Теперь сверточный слой 1x1 не увеличивает рецептивное поле. Их функция не в том, чтобы изучать особенности уровня изображения. Они скорее используются для изменения количества фильтров или в качестве головы регрессии или классификации.

Итак, мы берем два сверточных слоя 1x1 и используем один из них для классификации каждой ячейки привязки как object или background. Назовем это головой уверенности. Таким образом, имея карту объектов размером (B, C, w_amap, h_amap), мы сворачиваем ядро ​​размером 1x1, чтобы получить результат размером (B, n_anc_boxes, w_amap, h_amap). По сути, каждый выходной фильтр представляет классификационную оценку поля привязки.

Аналогичным образом другой сверточный слой 1x1 берет карту объектов и создает выходные данные размером (B, n_anc_boxes * 4, w_amap, h_amap), где выходные фильтры представляют предсказанные смещения блоков привязки. Это называется головкой регрессии.

Во время обучения мы выбираем поля положительной привязки и применяем предсказанные смещения для создания предложений по регионам. Предложения по регионам можно рассчитать следующим образом:

где верхний индекс p обозначает предложение региона, верхний индекс a обозначает поля привязки, а t обозначает прогнозируемые смещения.

Следующая функция реализует вышеуказанные преобразования и генерирует предложения регионов:

Региональная сеть предложений

Сеть предложений регионов — это этап 1 детектора, который берет карту объектов и создает предложения регионов. Здесь мы объединяем магистральную сеть, модуль выборки и модуль предложения в региональную сеть предложений.

Как во время обучения, так и во время вывода RPN производит оценки и смещения для всех полей привязки. Однако во время обучения мы выбираем только поля положительной и отрицательной привязки для вычисления потери классификации. Чтобы вычислить потери регрессии L2, мы учитываем только смещения положительных выборок. Окончательный проигрыш представляет собой взвешенную комбинацию обоих этих проигрышей.

Во время вывода мы выбираем поля привязки с оценками выше заданного порога и генерируем предложения, используя предсказанные смещения. Мы используем сигмовидную функцию для преобразования необработанных логитов модели в оценки вероятности.

Предложения, сгенерированные в обоих случаях, передаются на второй этап детектора.

Модуль классификации

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

Вот более разумный способ изменения размера: мы делим предложения на примерно равные субрегионы и применяем к каждому из них операцию максимального объединения, чтобы получить выходные данные одинакового размера. Это называется объединением ROI и показано ниже:

Максимальное объединение — это дифференцируемая операция, мы постоянно используем их в сверточных нейронных сетях.

Нам не нужно реализовывать объединение ROI с нуля, библиотека torchvision.ops предоставляет это для нас.

После изменения размера предложений с использованием пула ROI мы пропускаем их через сверточную нейронную сеть, состоящую из сверточного слоя, за которым следует слой среднего пула, за которым следует линейный слой, который производит оценки категорий.

Во время вывода мы прогнозируем категорию объекта, применяя функцию softmax к необработанным логитам модели и выбирая категорию с наивысшей оценкой вероятности. Во время обучения мы вычисляем потери классификации, используя кросс-энтропию.

В полномасштабной реализации мы также включаем категорию background на втором этапе, но давайте оставим это в этом уроке.

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

Немаксимальное подавление

На последнем этапе вывода мы удаляем повторяющиеся ограничивающие рамки, используя технику, называемую немаксимальным подавлением. В этом методе мы сначала рассматриваем ограничивающую рамку с наивысшим классификационным показателем. Затем мы вычисляем IoU всех остальных блоков с этим блоком и удаляем те, которые имеют высокий показатель IoU. Это дубликаты ограничивающих рамок, которые перекрываются с «оригинальными». Мы повторяем этот процесс и для оставшихся ящиков, пока не будут удалены все дубликаты.

Опять же, нам не нужно реализовывать его с нуля. Библиотека torchvision.ops предоставляет его нам. Этап обработки NMS реализован в регрессионной сети этапа 1, описанной выше.

Модель Faster-RCNN

Мы собрали сеть региональных предложений и модуль классификации, чтобы построить окончательную сквозную модель Faster-RCNN.

Обучение модели

Сначала давайте перенастроим сеть на небольшой выборке данных, чтобы убедиться, что все работает как положено. Мы используем стандартный цикл обучения с оптимизатором Adam и скоростью обучения 1e-3.

Вот результаты:

Поскольку мы тренировались на небольшом подмножестве данных, модель не изучила особенности уровня изображения, поэтому результаты неточны. Это можно улучшить, тренируясь на большом наборе данных.

Заключение

В полномасштабной реализации мы обучаем сеть на стандартном наборе данных, таком как MS-COCO или PASCAL VOC, и оцениваем результаты, используя такие показатели, как средняя средняя точность или площадь под кривой ROC. Однако цель этого руководства — понять модель Faster-RCNN, поэтому мы оставим оценочную часть.

За прошедшие годы в этой области были достигнуты значительные успехи, и было создано множество новых сетей. Примеры включают YOLO, EfficientDet, DETR и Mask-RCNN. Однако большинство из них построены на основе модели Faster-RCNN, которую мы обсуждали в этом руководстве.

Надеюсь, вам понравилась статья. Код доступен на GitHub. Давайте подключимся. Вы также можете связаться со мной в LinkedIn или Twitter.

Подтверждение набора данных

Два изображения, используемые в этой статье, взяты из набора данных DIV2K. Набор данных находится под лицензией CC0: Public Domain.

@InProceedings{Agustsson_2017_CVPR_Workshops,
	author = {Agustsson, Eirikur and Timofte, Radu},
	title = {NTIRE 2017 Challenge on Single Image Super-Resolution: Dataset and Study},
	booktitle = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR) Workshops},
	month = {July},
	year = {2017}
}

Кредиты изображений

Если источник не указан явно в подписи, все изображения в этом руководстве принадлежат автору.

Рекомендации