ОБНОВЛЕНИЕ: этот метод больше не нужен для Torchvision 0.3+, который увеличивает рентабельность инвестиций. Для Torchvision 0.3+ требуется PyTorch 1.1+. Эта статья по-прежнему публикуется для людей, которым необходимо использовать ROI с PyTorch 1.0, и представляет собой обзор того, как работает объединение ROI в целом.

Если вы когда-либо хотели выполнить проект глубокого обучения, связанный с компьютерным зрением или обработкой изображений, вы, возможно, встречали слои ROI Pool и ROI Align. Первоначально созданные для обнаружения объектов, варианты ROI Pool также полезны для извлечения информации из локализованных областей изображения. Например, вы можете захотеть извлечь определенные части тела из человека:

Я нашел много полезных статей, объясняющих концептуальную работу ROI Pooling и ROI Align (спасибо этим авторам!). Однако я не нашел никаких четких руководств по кодированию слоев ROI Pooling / Alignment в моих нейронных сетях.

К сожалению, ROI Pooling (и его варианты) не встроены в PyTorch. Конечно, вы можете реализовать слои самостоятельно. Но чтобы создать практическую реализацию, совместимую с графическим процессором, вам придется потратить время на кодирование на CUDA. Более практичный вариант - использовать стороннюю библиотеку. Тем не менее, большинство этих библиотек удручающе недокументированы.

В этом посте я суммирую то, что я узнал за несколько недель исследований, экспериментов и борьбы за работу с недокументированными библиотеками. Я объясню установку и компиляцию сторонней реализации для использования в вашем проекте, а также API того, как использовать предоставленные слои ROI. Надеюсь, с помощью этого руководства я смогу сэкономить другим много времени!

Установка

Я использовал реализацию слоя ROI из https://github.com/jwyang/faster-rcnn.pytorch. Это самый популярный репозиторий Faster-RCNN PyTorch на GitHub, поэтому он представляет собой хороший выбор. Кстати, я считаю, что здесь на часть кода слоя ROI сильно повлиял код из репозитория maskrcnn-benchmark Facebook. (Maskrcnn представил улучшенный вариант, ROI Align!)

Примечание. Я использовал Python 3.7, но он должен работать с любой версией Python 2.7 или выше. Я также использую PyTorch 1.0, но пользователи PyTorch 0.4 должны иметь возможность следовать вместе с некоторыми незначительными корректировками.

Сначала клонируйте репозиторий jwyang Fast-rcnn.pytorch. Затем обязательно проверьте ветку pytorch-1.0. Это важно! Шаги компиляции различаются в ветвях master (для PyTorch 0.4) и pytorch-1.0.

git clone https://github.com/jwyang/faster-rcnn.pytorch.git
cd faster-rcnn.pytorch
git checkout pytorch-1.0

Как скопировано из инструкций в README, установите требования с помощью pip, затем соберите и скомпилируйте с помощью инструментов установки Python:

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

pip install -r requirements.txt
cd lib/pycocotools
# FOLLOW THE INSTRUCTIONS HERE!!
cd ..  # go back to the lib folder
python setup.py build develop

Важно: чтобы иметь возможность использовать слои ROI-Pool и ROI-Align, в вашей среде Python ДОЛЖНЫ быть установлены зависимости в requirements.txt. В противном случае вы столкнетесь с ошибками segfault. Если вы используете conda, убедитесь, что активированная среда такая же, как та, которая использовалась для компиляции библиотеки. (Пользователи Conda, если вы столкнулись с ошибками, связанными с * .o: файл не распознан и не можете инициализировать статус распаковки для раздела .debug_info, ознакомьтесь с этим решением.)

Чтобы убедиться, что установка прошла успешно, откройте приглашение Python и введите:

>>> import sys
>>> sys.path.append(“/[location_to]/faster-rcnn.pytorch/lib”)
>>> from model.roi_layers import ROIPool  # PyTorch 1.0 specific!
>>> roi_pool = ROIPool((2,2), 1)

… Где [location_to] - это место, где в вашей системе клонируется репозиторий Fast-rcnn.pytorch. Оператор sys.path.append добавляет скомпилированную библиотеку в PATH Python, что позволяет нам импортировать «ROIPool».

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

Примечание: оператор импорта специфичен для PyTorch 1.0. Если вы используете PyTorch 0.4, правильный оператор импорта следующий:
›from model.roi_pooling.modules import roi_pool # PyTorch 0.4

Основное использование

Большой! Теперь, когда мы скомпилировали библиотеку и проверили ее работу, как на самом деле использовать ROIPool и ROIAlign?

Таким образом, использование:

# your own implementations to load data
image = get_image()  # returns a (batch×channel×height×width) tensor
rois = get_rois()    # returns a (batch×n×5) tensor
# init the ROI layers
roi_pool = ROIPool((width, height), spatial_scale)
roi_align = ROIAlign((width, height), spatial_scale, sampling_ratio)
# turn our (batch_size×n×5) ROI into just (n×5)
rois = rois.view(-1, 5)
# reset ROI image-ID to align with the 0-indexed minibatch
rois[:, 0] = rois[:, 0] - rois[0, 0]
# feed-forward our data
pooled_output = roi_pool(image, rois)
aligned_output = roi_align(image, rois)

Если вы уже знаете, как загружать файлы ROI и что означают значения space_scale и sampling_ratio, готово! Просто обратите внимание, что значение sampling_ratio означает, что ROIAlign будет отбирать sampling_ratio² точек на ячейку; например sampling_ratio = 2 будет производить выборку 4 точек на ячейку с помощью билинейной интерполяции, а затем усреднять точки.

Если вы не понимаете, что означают эти слова, читайте дальше!

Детали

Во-первых, если вы еще не знаете, как работает ROI Pool концептуально, прочтите руководство здесь.

Пул ROI занимает 1) изображение и 2) области интереса (ROI) для извлечения. Изображение простое - это просто ваш стандартный тензор. Результат вашего DataLoader дает тензор формы формы (пакет × канал × высота × ширина). Но как нам работать с рентабельностью инвестиций? На что они похожи?

Как выглядят данные о рентабельности инвестиций

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

image_id, upper_left_x, upper_left_y, lower_right_x, lower_right_y
0, 10, 10, 20, 20
0, 15, 15, 25, 25
1, 27, 10, 36, 20
1, 15, 15, 25, 25
...

В приведенном выше примере у нас есть две области интереса для идентификатора изображения 0, а две области интереса для идентификатора изображения - 1.

Обычно все области интереса хранятся в одном файле csv. Мы можем загрузить его в массив numpy с помощью Pandas, а затем преобразовать этот массив в тензор PyTorch. Наконец, создайте собственный набор данных и загрузчик данных, чтобы передать изображение + ROI в вашу нейронную сеть.

Когда мы загружаем данные в слои ROI, входные размеры должны выглядеть следующим образом:

def roi_pool(image: torch.Tensor, rois: torch.Tensor):
    """
    image: a (batch_size×channel×height×width) tensor input to pool
           from
    rois:  a (n×5) tensor to represent the regions of interest, 
           where n is the number of rois
    """

Проблемы с размерами ROI и Image-ID

Однако вы можете заметить, что загрузчики данных всегда добавляют дополнительное измерение для размера пакета. Например, если размер вашего мини-пакета равен 4, DataLoader выдаст тензор ROI (4 × n × 5). Но roi_pool и roi_align только работают с тензорами (n × 5). Что мы делаем?

Решение состоит в том, чтобы изменить форму нашего тензора ROI с помощью функции PyTorch view ():

# turn our (batch_size×n×5) ROI into just (n×5)
rois = rois.view(-1, 5)

Другая проблема заключается в том, что идентификатор изображения НЕ будет выровнен с индексом партии. Это связано с тем, что каждый идентификатор изображения в вашем наборе данных уникален, но индекс пакета равен 0 – batch_size. Поэтому мы должны вручную «сбросить» ID изображения:

# reset ROI image-ID to align with the 0-indexed minibatch
rois[:, 0] = rois[:, 0] - rois[0, 0]

Параметры инициализации уровня ROI

Мы строим слои ROI, как показано ниже, но что означают эти параметры?

# init the layers
roi_pool = ROIPool((width, height), spatial_scale)
roi_align = ROIAlign((width, height), spatial_scale, sampling_ratio)

Ширина и высота просты; это просто желаемые выходные размеры. Итак, давайте начнем с space_scale, посмотрев на типичный CNN. Вот схема VGG16:

CNN эффективно уменьшает масштаб изображения по мере его прохождения по сети. Этот масштабный коэффициент является пространственным масштабом. Например, пространственный масштаб четвертого слоя (28 × 28) относительно входа (224 × 224) составляет 28/224 = 0,125. Если бы мы использовали пул рентабельности инвестиций на четвертом уровне, мы бы передали 0,125 параметру Space_scale.

Как насчет sampling_ratio в выравнивании ROI? Чтобы понять это, нам нужно немного понять, как работает выравнивание ROI. Страница 3 из этого источника дает прекрасное объяснение.

Значение каждого «бина» в выходном размере слоя выравнивания области интереса определяется путем усреднения выборок билинейной интерполяции. На изображении слева на ячейку приходится 4 образца (синие точки).

Параметр sampling_ratio определяет, насколько широкое поле выборки. Например, если sampling_ratio = 2, в поле выборки будет 2 × 2 = 4 точки. (Чтобы убедиться в этом сами, взгляните на исходный код C, лежащий в основе реализации.)

Заключение

Надеюсь, теперь вы понимаете, как добавлять слои ROI в свои нейронные сети в PyTorch. Мы рассмотрели, как установить реализацию ROI из репозитория jwyang, поработать со слоями и ROI в коде, а также объяснили параметры инициализации. Если у вас есть вопросы, дайте мне знать в комментариях. Удачного кодирования!

P.S. Это мой первый урок! Если вы порекомендуете какие-либо улучшения, дайте мне знать :)