Maska R-CNN do detekcji i segmentacji statków

Jednym z najbardziej ekscytujących zastosowań głębokiego uczenia się jest zdolność maszyn do poznania obrazów. Fei-Fei Li wspomniała o tym jako o nadaniu maszynom “zdolności widzenia”. Istnieją cztery główne klasy problemów z wykrywaniem i segmentacją, opisane w obrazie (a) poniżej.

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

Maska R-CNN

Maska R-CNN to rozszerzenie nad Faster R-CNN. Faster R-CNN przewiduje pola ograniczające, a Maska R-CNN zasadniczo dodaje kolejną gałąź do przewidywania maski obiektu równolegle.

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

https://miro.medium.com/max/1674/1*JzFsV3nJPhTpDm05KaCrxQ.png
Nie mam zamiaru wprowadzać szczegółów na temat działania Maski R-CNN, ale oto ogólne kroki, jakie należy podjąć:

Model szkieletowy: typowa konwolucyjna sieć neuronowa, która jest ekstraktorem cech. na przykład, zamieni obraz a1024x1024x3 w mapę cech 32x32x2048, która jest wejściowa dla kolejnych warstw.

Sieć Propozycja Regionu (RPN): Używając regionów zdefiniowanych z 200-kilometrowymi polami kotwiczącymi, RPN skanuje każdy region i przewiduje czy dany obiekt jest obecny czy nie. Jedną z dobrych zalet RPN jest to, że nie skanuje konkretnego obrazu, sieć skanuje mapę obiektu, dzięki czemu jest znacznie szybsza.

Region Interest Classification and Bounding Box: podczas tego kroku algorytm przyjmuje regiony zainteresowania proponowane przez RPN jako wejścia i wyjścia klasyfikacji (softmax) i ogranicznika (regressor).

Maski segmentacji: w ostatnim etapie algorytm przyjmuje jako wejścia dodatnie regiony ROI, a maski 28×28 pikseli z wartościami zmiennoprzecinkowymi są generowane jako wyjścia dla obiektów. Podczas wnioskowania, maski te są skalowane w górę.

Trening i wnioskowanie z maską R-CNN

Zamiast powielać cały algorytm wspierający pracę badawczą, użyjemy niesamowitej biblioteki Mask R-CNN, którą zbudował Matterport. Będziemy musieli A) wygenerować nasze zestawy treningowe i devów, B) zrobić trochę zamieszania, aby załadować do biblioteki, C) skonfigurować nasze środowisko treningowe w AWS do treningu, D) użyć transferu uczenia się, aby rozpocząć trening z kokosowych wstępnie wytrenowanych ciężarów, oraz E) dostroić nasz model, aby uzyskać dobre wyniki.

Krok 1: Pobierz dane Kaggle i wygeneruj podziały na treningi i urządzenia

Zestaw danych dostarczonych przez Kaggle składa się z wielu tysięcy zdjęć, dlatego najłatwiej jest je pobrać na maszynę AWS, gdzie będziemy odbywać nasze szkolenia. Kiedy już je pobierzemy, będziemy musieli podzielić je na zestawy szkoleniowe i dev, które mogą być wykonane losowo za pomocą skryptu Pythona.

Gorąco polecam wykorzystanie instancji spotowej do pobrania informacji z Kaggle’a używając API Kaggle’a i wgrania tych danych w postaci zipowanej do wiadra S3. Później ściągniesz te dane z S3 i rozpakujesz je w czasie szkolenia.

Kaggle dostarcza plik csv o nazwie train_ship_segmentations.csv z dwoma kolumnami: ImageId i EncodedPixels (format kodowania długości działania). Zakładając, że pobraliśmy zdjęcia do pliku ./datasets/train_val/ścieżki, podzielimy je i przeniesiemy do folderów train i dev set z tym kodem: 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_segmentations_df[~msk]

# Move train set

dla indeksu, wiersz w train.iterrows():

image_id = row[“ImageId”]

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

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

jeśli os.path.isfile(old_path):

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

# Move dev set

dla indeksu, wiersz w test.iterrows():

image_id = row[“ImageId”]

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

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

jeśli os.path.isfile(old_path):

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

Krok 2: Załadowanie danych do biblioteki

Istnieje wybrana konwencja, którą stosuje biblioteka Mask R-CNN do ładowania zbiorów danych. Chcielibyśmy stworzyć kategorię ShipDataset, która zaimplementuje większość wymaganych funkcji:

ship.py

klasa ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, podzbiór):

def load_mask(self, image_id): def load_mask(self, image_id):

def image_reference(self, image_id): def image_reference(self, image_id):

Aby przekonwertować zakodowaną maskę długości biegu na maskę obrazu (boolean tensor) używamy tej funkcji poniżej rle_decode. często nie ma możliwości wygenerowania dolnych masek prawdy, które ładujemy do biblioteki w celu szkolenia w naszej klasie ShipDataset.

ship.py

klasy ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, subset):

def load_mask(self, image_id):

def image_reference(self, image_id): def image_reference(self, image_id):

Krok 3: Trening konfiguracji z P3 Instancje punktowe i partia AWS

Biorąc pod uwagę ogromny zbiór danych, z którym chcielibyśmy współpracować, będziemy musieli wykorzystać instancje AWS GPU, aby nakłonić dobrych prowadzi do praktycznej ilości czasu. Instancje P3 są dość drogie, ale używając Spot Instances dostaniesz p3.2xwięcej za około $0.9 / h, co stanowi około 70% oszczędności. Kluczem jest tutaj wydajność i automatyzacja maksymalnej kwoty, ponieważ będziemy tak, aby nie tracić czasu / pieniędzy na zadania nieszkoleniowe, takie jak naprawianie informacji , itp. aby spróbować do tego, będziemy używać skryptów powłoki i kontenerów dokera, a następnie korzystać z awesome AWS Batch usługi do planowania naszych szkoleń.

Pierwszą rzeczą jaką zrobiłem jest stworzenie Deep Learning AMI skonfigurowanego dla AWS Batch, który używa nvidia-docker zgodnie z tym AWS Guide. AMI ID to ami-073682d8e65240b76 i jest gościnny dla społeczności. może to pozwolić nam na trenowanie przy użyciu kontenerów dokerów z procesorami graficznymi.

Następnie tworzymy plik dockerfile, który zawiera wszystkie zależności, które chcielibyśmy mieć również dlatego, że skrypty powłoki, które będą zwracać uwagę na pobieranie informacji i uruchamianie szkoleń.Zapamiętaj trzy ostatnie skrypty powłoki skopiowane do kontenera: FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

Główny programista Gabriel Garza <garzagabriel@gmail.com>

# Essentials: developer tools, build tools, OpenBLAS

RUN apt-get update && apt-get install -y –no-install-rekomendacje \

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

Niezwykle istotne jest to, że

libopenblas-dev

#

# Python 3.5

#

# Dla wygody, pseudonim (ale nie symuluj) python & pip do python3 & pip3 zgodnie z zaleceniami:

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

RUN apt-get install -y –no-install-rekomendacje python3.5 python3.5-dev python3-pip python3-tk && \

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

# Pillow and it’s dependencies

RUN apt-get install -y –no-install-recommends libjpeg-dev zlib1g-dev && \

pip3 –no-cache-dir install Pillow

# Biblioteki naukowe i inne popularne pakiety

RUN pip3 –no-cache-dir install \

numpy scipy sklearn scikit-image==0.13.1 pandas matplotlib Cython prosi o pandas imgaug

# Install AWS CLI

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

#

# Jupyter Notebook

#

# Allow access from outside the container, and skip trying to open a browser. #

# UWAGA: dla wygody wyłączyć token uwierzytelniający. NIE RÓB TEGO NA PUBLICZNYM SERWERZE.

RUN pip3 –no-cache-dir zainstaluj jupyter && \

mkdir /root/.jupyter &&

echo “c.NotebookApp.ip = ‘*'” \

“NotebookApp.open_browser = Fałszywy”.

\”NotebookApp.token =”\

> /root/.jupyter/jupyter_notebook_config.py

EXPOSE 8888

#

# Tensorflow 1.6.0 – GPU

#

# Zainstaluj TensorFlow

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

# Expose port for TensorBoard

EXPOSE 6006

#

# OpenCV 3.4.1

#

# Zależności

RUN apt-get install -y –no-install-rekomendacje \

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

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

liblapacke-dev checkinstall

RUN pip3 zainstalować opencv-python

#

# Keras 2.1.5

#

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

#

# PyCocoTools

#

# Using a widek of the original that has a fix for Python 3.

# I submitted a PR to the original repo (https://github.com/cocodataset/cocoapi/pull/50)

# ale nie wydaje się już być aktywny.

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

COPY setup_project_and_data.sh /home

COPY train.sh /home

COPY predict.sh /home

WORKDIR “/home”

setup_project_and_data.sh -> klonuje naszą Maskę R-CNN repo, pobiera i rozpakowuje nasze dane z S3, dzieli informacje na zestawy pociągów i urządzeń, pobiera najnowsze wagi zapisane w S3.

train.sh -> ładuje najnowsze wagi, uruchamia polecenie train python3 ./ship.py train –dataset=./datasets –weights=last, ładuje wytrenowane wagi do S3 po zakończeniu treningu

predict.sh -> pobierz zestaw danych testowych Kaggle Challenge (który jest wykorzystywany do przesłania wpisu do konkursu), generuje przewidywania dla każdego ze zdjęć, konwertuje maski na kodowanie długości i przesyła plik CSV przewidywań do S3.

Krok 3: Pociągnięcie modelu za pomocą AWS Batch

Piękno AWS Batch polega na tym, że możesz po prostu stworzyć środowisko obliczeniowe, które wykorzystuje Spot Instance i uruchamia pracę przy użyciu kontenera dokującego, a następnie zakończyć pracę, gdy tylko zakończysz pracę.

Nie podam tu zbyt wielu szczegółów (może to być kolejny post), ale zasadniczo budujesz swój obraz, wrzucasz go do AWS ECR, a następnie w AWS Batch planujesz swoje szkolenie lub pracę wnioskową, aby uruchomić komendę bash predict.sh lub bash train.sh i czekać na jej zakończenie (możesz śledzić postępy oglądając logi w AWS Watch). Pliki wynikowe (wytrenowane wagi lub predykcje csv) są wysyłane do S3 przez nasz skrypt.

Kiedy pierwszy raz trenujemy, przechodzimy w ramach argumentu coco (w train.sh), aby użyć Transferu Uczenia się i trenować nasz model na już wytrenowanym zestawie danych coco:

python3 ./ship.py train –dataset=./datasets –weights=coco

Kiedy skończymy nasz wstępny trening, w ciągu ostatniego argumentu przekażemy go dowództwu pociągu, więc rozpoczynamy trening od miejsca, w którym go zakończyliśmy:

python3 ./ship.py train –dataset=./datasets –weights=lasty

Możemy dostroić nasz model za pomocą klasy ShipConfig i nadpisać ustawienia domyślne. Ustawienie Non-Max Suppression na 0 było ważne, aby uniknąć przewidywania nakładania się masek statków (na co nie pozwala wyzwanie Kaggle). klasa ShipConfig(Config):

“””Konfiguracja dla szkolenia na zbiorze danych zabawek.

Wywodzi się z klasy bazowej Config i nadpisuje niektóre wartości.

“””

# Nadaj tej konfiguracji rozpoznawalną nazwę

NAME = “statek”

# Używamy procesora graficznego z 12GB pamięci, który może zmieścić dwa obrazy.

# Wyreguluj w dół, jeśli używasz mniejszej jednostki GPU.

IMAGES_PER_GPU = 1

# Liczba klas (łącznie z tłem)

NUM_CLASSES = 1 + 1 # Tło + statek

# Liczba kroków szkoleniowych na epokę

STEPS_PER_EPOCH = 500

# Pomiń wykrywanie z < 95% pewnością

DETECTION_MIN_CONFIDENCE = 0,95

# Nie-maksymalny próg tłumienia dla wykrywania

DETECTION_NMS_THRESHOLD = 0.0

IMAGE_MIN_DIM = 768

IMAGE_MAX_DIM = 768

Etap 4: Przewidywanie segmentacji statków

Aby wygenerować nasze przewidywania, musimy tylko spróbować uruchomić nasz kontener w AWS Batch za pomocą komendy bash predict.sh. To może użyć skryptu wewnątrz generate_predictions.py, oto fragment tego, jak wygląda wnioskowanie:

klasa InferenceConfig(config.__class__):

# Uruchom wykrywanie na jednym obrazie na raz

GPU_COUNT = 1

IMAGES_PER_GPU = 1

DETECTION_MIN_CONFIDENCE = 0,95

DETECTION_NMS_THRESHOLD = 0,0

IMAGE_MIN_DIM = 768

IMAGE_MAX_DIM = 768

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

DETECTION_MAX_INSTANCES = 20

# Stwórz obiekt modelu w trybie wnioskowania.

config = InferenceConfig()

model = modellib.MaskRCNN(mode=”inference”, model_dir=MODEL_DIR, config=config)

# Instantiate dataset

dataset = ship.ShipDataset()

# Load weights

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

class_names = [“BG”, “statek”]

# Wykrycie biegu

# Wczytaj obraz ids (nazwy plików) i długość przebiegu zakodowane piksele

images_path = “datasets/test”

sample_sub_csv = “sample_submission.csv”

# images_path = “datasets/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 = []

Zliczanie = 0

dla image_id in unique_image_ids:

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

jeśli os.path.isfile(image_path):

licznik += 1

print(“Step: “, count”)

# Zacznij liczyć czas przewidywania

tic = time.clock()

image = skimage.io.imread(image_path)

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

r = wyniki[0]

# First Image

re_encoded_to_rle_list = []

dla i w np.arange(np.array(r[‘maski’]).shape[-1]):

boolean_mask = r[‘maski’][:,:,i]

re_encoded_to_rle = dataset.rle_encode(boolean_mask)

re_encoded_to_rle_list.append(re_encoded_to_rle)

if len(re_encoded_to_rle_list) == 0:

out_pred_rows += [{“ImageId”: image_id, “EncodedPixels”: None}]]

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

Inaczej:

dla rle_maski w 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”]]]

fileename = “{}{:%Y%m%dT%H%M}.csv”.format(“./submissions/submission_”, datetime.datetime.now())

submission_df.to_csv(nazwa pliku, index=False)

Widziałem kilka trudnych przypadków, takich jak fale i chmury na zdjęciach, które model początkowo uważał za statki. aby pokonać to wyzwanie, zmodyfikowałem rozmiary skrzynek kotwicznych RPN_ANCHOR_SCALES, aby były mniejsze, co znacznie poprawiło wyniki, ponieważ model nie przewidywał małych fal jako statków.

Wyniki

Przyzwoite wyniki można uzyskać po około 30 epokach (zdefiniowanych w ship.py). Szkoliłam się przez 160 epok i byłam gotowa na uzyskanie 80.5% dokładności w moim zgłoszeniu Kaggle.

Dołączyłam Notatnik Jupytera o nazwie inspect_shyp_model.ipynb, który pozwala Ci uruchomić model i tworzyć przewidywania na dowolnym obrazie lokalnie na Twoim komputerze.