Маска R-CNN для обнаружения и сегментации судна

Одним из самых увлекательных применений глубокого обучения является способность машин к познанию образов. Фей-Фей Ли упомянул об этом как о дающем машинам “способность видеть”. Существует четыре основных класса проблем детектирования и сегментации, описанных в изображении (a) ниже .

https://miro.medium.com/max/1717/1*8Nwk_IdGpe235Nsfewpucg.png

Маска R-CNN

Маска R-CNN является расширением по сравнению с более быстрым R-CNN. Более быстрое R-CNN предсказывает ограничивающие рамки, а Mask R-CNN, по сути, добавляет еще одну ветку для параллельного предсказания маски объекта.

https://miro.medium.com/max/1485/1*zfUPBhMG9L_XlSM8C1PqdQ.png

https://miro.medium.com/max/1674/1*JzFsV3nJPhTpDm05KaCrxQ.png
Я не буду вдаваться в подробности того, как работает Mask R-CNN, но вот общие шаги, которым следует этот подход:

Магистральная модель: типичная конволюционная нейронная сеть, которая является экстрактором функций. Например, она превратит изображение 1024x1024x3 в карту функций 32x32x2048, которая вводится для последующих слоёв.

Сеть предложения региона (RPN): Используя регионы, определенные с 200-килобайтными якорями, RPN сканирует каждый регион и предсказывает, присутствует ли объект или нет. Одним из хороших преимуществ RPN является то, что не сканирует конкретное изображение, сеть сканирует карту функций, делая это намного быстрее.

Классификация областей интереса и граничное окно: на этом шаге алгоритм принимает области интереса, предложенные RPN в качестве входа и выхода, классификацию (софтмакс) и граничное окно (регрессор).

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

Обучение и вывод с помощью маски R-CNN

Вместо того, чтобы копировать весь алгоритм, поддерживаемый исследовательской работой, мы будем использовать потрясающую библиотеку Mask R-CNN, которую построил Matterport. Нам нужно будет A) сгенерировать наши наборы тренировок и девайсов, B) сделать некоторые зависания для загрузки в библиотеку, C) настроить нашу обучающую среду в AWS для тренировок, D) использовать трансфертное обучение для начала тренировок с кокосовых предустановленных весов, и E) настроить нашу модель для получения хороших результатов.

Шаг 1: Скачать данные Каггл и генерировать поезд и разделения устройств

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

Я настоятельно рекомендую использовать точечный экземпляр для загрузки информации с Kaggle, используя API Kaggle, и загрузить эти данные в ведро S3. Позже вы загрузите эти данные из S3 и распакуете их во время тренировки.

Kaggle предоставляет файл csv под названием train_ship_segmentations.csv с двумя колонками: ImageId и EncodedPixels (формат кодирования длины). Предполагая, что мы загрузили картинки по пути ./datasets/train_val/, мы разделим и переместим их в папки train и dev set с этим кодом: train_ship_segmentations_df = pd.read_csv(os.path.join(“./datasets/train_val/train_ship_segmentations.csv”)).

msk = np.random.rand(len(train_ship_segmentations_df)) < 0.8

train = train_ship_segmentations_df[msk]

test = train_ship_segmententations_df[~msk]

# Move train set

для индекса, строка в поезде. iterrows():

image_id = row[“ImageId”].

old_path = “./datasets/train_val/{}”.format(image_id)

new_path = “./datasets/train/{}”.format(image_id)

если os.path.isfile(old_path):

os.rename(old_path, new_path): os.rename(old_path, new_path): os.rename(old_path, new_path)

# Move dev set

для индекса, строка в test.iterrows():

image_id = row[“ImageId”].

old_path = “./datasets/train_val/{}”.format(image_id)

new_path = “./datasets/val/{}”.format(image_id)

если os.path.isfile(old_path):

os.rename(old_path, new_path): os.rename(old_path, new_path): os.rename(old_path, new_path)

Шаг 2: Загрузка данных в библиотеку

Для загрузки наборов данных существует выбранное соглашение, которому следует библиотека Mask R-CNN. Мы хотим сделать категорию ShipDataset, которая будет реализовывать наибольшее количество необходимых функций:

ship.py

класс ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, subset):

def load_mask(self, image_id):

def image_reference(self, image_id):

Для преобразования кодированной маски Run Length Encoded Mask в маску изображения (булевый тензор) мы используем эту функцию ниже rle_decode. Часто это делается для того, чтобы сгенерировать нижние маски истины, которые мы загружаем в библиотеку для обучения в нашем классе ShipDataset.

ship.py

классе ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, subet):

def load_mask(self, image_id):

def image_reference(self, image_id):

Шаг 3: Обучение установке с P3 Spot Instances и AWS Batch

Учитывая массив данных, с которыми мы хотели бы тренироваться, нам придется использовать экземпляры AWS GPU, чтобы добиться хороших результатов, которые позволят вам провести много времени на практике. P3 экземпляры довольно дороги, но при использовании Spot Instances вы получите p3.2xlarge примерно за $0.9/час, что составляет около 70% экономии. Ключом здесь является эффективность и автоматизация максимальной суммы, как мы будем, чтобы не тратить время/деньги на не-тренировочные задачи, такие как исправление информации и т.д., чтобы попытаться это сделать, мы будем использовать скрипты оболочки и докер-контейнеры, а затем использовать удивительный AWS Batch сервис, чтобы запланировать наше обучение.

Первое, что я сделал, это создал Deep Learning AMI, настроенный для AWS Batch, который использует nvidia-docker, следуя этому AWS Guide. ID AMI – ami-073682d8e65240b76, и это гостеприимство сообщества. Это может позволить нам тренироваться, используя докер-контейнеры с графическими процессорами.

Далее мы создадим docker-файл, который будет содержать все зависимости, которые нам нужны еще и потому, что скрипты оболочки, которые будут искать загрузку информации и запускать обучение. Обратите внимание на последние три скрипта оболочки, скопированные в контейнер: FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

MAINTAINER Габриэль Гарза <garzagabriel@gmail.com>

# Основы: инструменты для разработчиков, инструменты для сборки, OpenBLAS

RUN apt-get update && apt-get install -y –no-install-recommends \.

apt-utils git curl vim unzip openssh-client wget \

встроенный модуль \

либопенблас-дев

#

# Питон 3.5

#

# Для удобства, псевдоним (но не sym-link) python & pip to python3 & pip3, как рекомендовано в:

# http://askubuntu.com/questions/351318/changing-symlink-python-to-python3-causes-problems

RUN apt-получить установку -y –no-install-рекомендует python3.5 python3.5-dev python3-pip python3-tk &&COPY2.

pip3 install pip==9.0.3 –upgrade && \

pip3 install –no-cache-dir –upgrade setuptools && \.

echo “alias python=’python3”” >> /root/.bash_aliases && \.

echo “alias pip=’pip3′” >> /root/.bash_aliases

# Подушка и зависимости

RUN apt-получить установку -y –no-install-рекомендует libjpeg-dev zlib1g-dev && \.

Пип3 — не-кэш-дир установка Подушка

# Научные библиотеки и другие общие пакеты

RUN pip3 –no-cache-dir install \

numpy scipy sklearn scikit-image==0.13.1 pandas matplotlib Cython просит pandas imgaug

# Установите AWS CLI

RUN pip3 –no-cache-dir install awscli –upgrade

#

# Джупитер Блокнот

#

# Разрешите доступ извне контейнера и пропустите попытку открыть браузер.

# ПРИМЕЧАНИЕ: для удобства отключите маркер аутентификации. НЕ ДЕЛАЙТЕ ЭТОГО НА ОБЩЕДОСТУПНОМ СЕРВЕРЕ.

RUN pip3 –no-cache-dir install jupyter & && \.

mkdir /root/.jupyter && \

эхо “c.NotebookApp.ip = ‘*'” \

“\nc.NotebookApp.open_browser = False” \.

“\nc.NotebookApp.token = ”” \.

> /root/.jupyter/jupyter_notebook_config.py

EXPOSE 8888

#

# Tensorflow 1.6.0 – GPU

#

# Установите TensorFlow

RUN pip3 –no-cache-dir install tensorflow-gpu

# Выставьте порт для TensorBoard.

ЭКСПОЗ 6006

#

# OpenCV 3.4.1

#

# Зависимости

RUN apt-получить установку -y –no-install-рекомендует \.

libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev \

libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libgtk2.0-dev \

либлапак-дева проверка

RUN pip3 установить opencv-python

#

# Керас 2.1.5

#

RUN pip3 install –no-cache-dir –upgrade h5py pydot_ng keras

#

# PyCocoTools

#

# Используя вилку оригинала, которая имеет исправление для Python 3.

# Я отправил PR в оригинальное репо (https://github.com/cocodataset/cocoapi/pull/50).

# но, похоже, он больше не активен. #

RUN pip3 install –no-cache-dir git+https://github.com/waleedka/coco.git#subdirectory=PythonAPI

COPY setup_project_and_data.sh /home

Поезд COPY.sh /home

COPY предсказывает.

РАБОТА “/ДОМ”

setup_project_and_data.sh -> клонирует репо маски R-CNN, загружает и распаковывает данные из S3, разбивает информацию на наборы поездов и устройств, загружает новейшие веса, сохраненные в S3.

train.sh -> загружает последние веса, запускает поезд команду python3 ./ship.py поезд –dataset=./datasets –weights=last, загружает тренированные веса в S3 после окончания обучения.

predict.sh -> скачать Kaggle Challenge тестовый набор данных (который используется для отправки вашей записи в вызов), генерирует прогнозы для каждой из картинок, преобразует маски для запуска длины кодирования, и загружает предсказания CSV-файл в S3.

Шаг 3: Обучение модели с использованием AWS Batch

Прелесть пакета AWS заключается в том, что вы можете просто создать вычислительную среду, которая использует Spot Instance, и она будет работать, используя ваш докер-контейнер, а затем прекратить работу Spot Instance, как только ваша работа закончится.

Я не буду вдаваться в подробности (возможно, это будет еще одно сообщение), но, по сути, вы строите свой образ, загружаете его в AWS ECR, затем в AWS Batch вы планируете ваше обучение или умозаключение, чтобы запустить с помощью команды bash predict.sh или bash train.sh и ждать, когда оно закончится (вы можете следить за прогрессом, просматривая журналы в AWS Watch). Полученные файлы (обученные веса или предсказания csv) загружаются нашим скриптом в S3.

В первый раз, когда мы тренируемся, мы проходим внутри аргумента coco (в train.sh)для того, чтобы использовать Transfer Learning и тренировать нашу модель поверх уже тренированного набора данных coco:

python3 ./ship.py поезд –dataset=./datasets –weights=coco

После того, как мы закончили начальный тренировочный заезд, мы передаем последний аргумент команде поезда, так что мы начинаем тренировку с того места, на котором остановились:

python3 ./ship.py поезд –dataset=./datasets –weights=last

Мы можем настроить нашу модель, используя класс ShipConfig и перезаписав настройки по умолчанию. Установка Non-Max Suppression на 0 была важна, чтобы избежать предсказания перекрывающихся масок корабля (что вызов Kaggle не позволяет). класс ShipConfig(Config):

“Конфигурация для тренировки на наборе данных игрушки.”

Получает из базового класса Config и переопределяет некоторые значения.

“””

# Дайте конфигурации узнаваемое имя.

Фамилия = “корабль”

# Мы используем GPU с 12 ГБ памяти, который может поместить два изображения.

# Отрегулируйте вниз, если вы используете GPU меньшего размера.

IMAGES_PER_GPU = 1

# Количество классов (включая фон)

NUM_CLASSES = 1 + 1 # фон + корабль

# Количество тренировочных шагов за эпоху

ШАГИ_ПЕР_ЭПОК = 500

# Пропустить детекции с < 95% уверенностью.

DETECTION_MIN_CONFIDENCE = 0.95

# Не-максимальный порог подавления для детектирования

DETECTION_NMS_THRESHOLD = 0.0

IMAGE_MIN_DIM = 768

ИЗОБРАЖЕНИЕ_MAX_DIM = 768

Шаг 4: Предсказать сегментацию корабля

Чтобы сгенерировать наши предсказания, все, что нам нужно попробовать, это запустить наш контейнер в AWS Batch с помощью команды bash predict.sh. Это может использовать скрипт внутри generate_predictions.py, вот фрагмент того, как выглядит умозаключение:

класс InferenceConfig(config.__class__):

# Запускаем детектирование на одном изображении за раз.

GPU_COUNT = 1

IMAGES_PER_GPU = 1

DETECTION_MIN_CONFIDENCE = 0.95

DETECTION_NMS_THRESHOLD = 0.0

ИЗОБРАЖЕНИЕ_МИН_ДИМ = 768

ИЗОБРАЖЕНИЕ_MAX_DIM = 768

RPN_ANCHOR_SCALES = (64, 96, 128, 256, 512)

DETECTION_MAX_INSTANCES = 20

# Создайте объект модели в режиме вывода.

config = InferenceConfig()

model = modellib.MaskRCNN(mode=”вывод”, model_dir=MODEL_DIR, config=config)

# Instantiate dataset

dataset = ship.ShipDataset()

# Грузовые веса

model.load_weights(os.path.join(ROOT_DIR, SHIP_WEIGHTS_PATH), by_name=True).

class_names = [‘BG’, ‘ship’ ]

# Обнаружение бега

# Загрузите идентификаторы изображений (имена файлов) и запустите кодированные по длине пиксели.

images_path = “наборы данных/тест”.

sample_sub_csv = “sample_submission.csv”.

# images_path = “наборы данных/val”

# sample_sub_csv = “val_ship_segmentations.csv”

sample_submission_df = pd.read_csv(os.path.join(images_path,sample_sub_csv)))

unique_image_ids = sample_submission_df.ImageId.unique()

out_pred_rows = []

счёт = 0

для image_id в unique_image_ids:

image_path = os.path.join(image_path, image_id)

если os.path.isfile(image_path):

счёт += 1

Печать (“Шаг:”, считай)

# Начните подсчёт времени предсказания

тик = время.часы()

изображение = skimage.io.imread(image_path)

results = model.detect([image], verbose=1)

r = результаты[0]

# Первое изображение

re_encoded_to_rle_list = []

для i в np.arange(np.array(r[‘masks’]).shape[-1]):

boolean_mask = r[‘masks’][:,:,i]).

re_encoded_to_rle = dataset.rle_encode(boolean_mask)

re_encoded_to_rle_list.append(re_encoded_to_rle)

если len(re_encoded_to_rle_list) == 0:

out_pred_rows += [{‘ImageId’: image_id, ‘EncodedPixels’: None}]]

print(“Found Ship: “, “NO”)

Иначе:

для rle_mask в списке re_encoded_to_rle_list:

out_pred_rows += [{‘ImageId’: image_id, ‘EncodedPixels’: rle_mask}]].

print(“Found Ship: “, rle_mask)

toc = time.clock()

print(“Prediction time: “,toc-tic”)

submission_df = pd.DataFrame(out_pred_rows)[[‘ImageId’, ‘EncodedPixels’]].

имя файла = “{}{:%Y%m%dT%H%M}.csv”.format(“./submissions/submission_”, datetime.datetime.now()).

submit_df.to_csv(имя файла, индекс=False)

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

Результаты

Приличные результаты можно получить примерно через 30 эпох (определяется на ship.py). Я тренировался в 160 эпох и был готов получить 80,5% точности в моем представлении Kaggle.

Я включил ноутбук Jupyter под названием inspect_shyp_model.ipynb, который позволяет запускать модель и делать прогнозы на любом изображении локально на вашем компьютере.