Maske R-CNN für Schiffsdetektion & Segmentierung

Eine der aufregendsten Anwendungen des Tiefenlernens ist die Fähigkeit von Maschinen, Bilder zu erkennen. Fei-Fei Li hat erwähnt, dass Maschinen dadurch die “Fähigkeit zu sehen” erhalten. Es gibt vier Hauptklassen von Problemen bei der Erkennung und Segmentierung, wie im Bild (a) unten beschrieben.

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

Maske R-CNN

Maske R-CNN ist eine Erweiterung über Schneller R-CNN. Faster R-CNN sagt Bounding Boxes voraus, und Mask R-CNN fügt im Wesentlichen einen weiteren Zweig für die parallele Vorhersage einer Objektmaske hinzu.

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

Ich werde nicht ins Detail gehen, wie Maske R-CNN funktioniert, aber hier sind die allgemeinen Schritte, denen der Ansatz folgt:

Backbone-Modell: ein typisches neuronales Faltungsnetzwerk, das ein Merkmalsextraktor ist. Es verwandelt beispielsweise ein 1024x1024x3-Bild in eine 32x32x2048-Merkmalskarte, die für nachfolgende Schichten eingegeben wird.

Regionales Vorschlagsnetz (RPN): Unter Verwendung von Regionen, die mit bis zu 200K Ankerkästchen definiert sind, scannt das RPZ jede Region und sagt voraus, ob ein Objekt vorhanden ist oder nicht. Einer der guten Vorteile des RPZ ist, dass nicht das bestimmte Bild gescannt wird, sondern das Netzwerk die Feature-Karte scannt, wodurch es viel schneller wird.

Regions of Interest Classification and Bounding Box: Während dieses Schritts nimmt der Algorithmus die von der RPZ vorgeschlagenen Regions of Interest als Input und gibt eine Klassifikation (Softmax) und eine Bounding Box (Regressor) aus.

Segmentierungsmasken: Im letzten Schritt nimmt der Algorithmus die positiven ROI-Regionen als Eingaben auf und erzeugt 28×28-Pixel-Masken mit Float-Werten als Ausgaben für die Objekte. Während der Inferenz werden diese Masken hochskaliert.

Training und Inferenz mit Maske R-CNN

Anstatt den gesamten Algorithmus zu replizieren, der in der Forschungsarbeit unterstützt wurde, verwenden wir die fantastische Masken-R-CNN-Bibliothek, die Matterport aufgebaut hat. Wir müssen A) unsere Trainings- und Entwicklungssets generieren, B) ein paar Handgriffe durchführen, um die Bibliothek zu laden, C) unsere Trainingsumgebung in AWS für das Training einrichten, D) Transfer-Lernen verwenden, um mit dem Training mit den vortrainierten Coco-Gewichten zu beginnen, und E) unser Modell so abstimmen, dass gute Ergebnisse erzielt werden.

Schritt 1: Laden Sie Kaggle-Daten herunter und erzeugen Sie Train- und Dev-Splits

Der von Kaggle zur Verfügung gestellte Datensatz besteht aus vielen Tausenden von Bildern, daher ist es am einfachsten, sie auf das AWS-Gerät herunterzuladen, auf dem wir unsere Schulung durchführen werden. Sobald wir sie heruntergeladen haben, müssen wir sie in Train- und Dev-Sets aufteilen, was durch ein Python-Skript nach dem Zufallsprinzip geschehen kann.

Ich empfehle dringend, eine Spot-Instanz zu verwenden, um die Informationen von Kaggle über die API von Kaggle herunterzuladen und die gezippten Daten in einen S3-Eimer hochzuladen. Später werden Sie diese Daten von S3 herunterladen und sie zur Schulungszeit entpacken.

Kaggle stellt eine csv-Datei namens train_ship_segmentations.csv mit zwei Spalten zur Verfügung: ImageId und EncodedPixels (Lauflängen-Kodierungsformat). Angenommen, wir haben die Bilder in den Pfad ./datasets/train_val/ heruntergeladen, dann teilen und verschieben wir die Bilder in die Ordner train und dev set mit folgendem Code: 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

Zug = Zug_Schiff_Segmentierungen_df[msk]

test = train_ship_segmentations_df[~msk]

# Zugsatz verschieben

für Index, Zeile in train.iterrows():

image_id = Zeile[“BildId”]

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

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

wenn os.path.isfile(alter_Pfad):

os.rename(alter_Pfad, neuer_Pfad)

~ Move dev set ~

für den Index, Zeile in test.iterrows():

image_id = Zeile[“BildId”]

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

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

wenn os.path.isfile(alter_Pfad):

os.rename(alter_Pfad, neuer_Pfad)

Schritt 2: Daten in die Bibliothek laden

Es gibt eine ausgewählte Konvention, der die Mask R-CNN-Bibliothek beim Laden von Datensätzen folgt. Wir möchten eine Kategorie ShipDataset erstellen, die die meisten benötigten Funktionen implementiert:

schiff.py

Klasse ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, Teilmenge):

def load_mask(self, image_id):

def image_reference(selbst, bild_id): def image_reference(selbst, bild_id):

Um eine lauflängencodierte Maske in eine Bildmaske (boolscher Tensor) umzuwandeln, verwenden wir diese Funktion unterhalb von rle_decode. Dies ist oft üblich, um die unteren Wahrheitsmasken zu erzeugen, die wir zum Training in unserer ShipDataset-Klasse in die Bibliothek laden.

schiff.py

Klasse ShipDataset(utils.Dataset):

def load_ship(self, dataset_dir, Teilmenge):

def load_mask(selbst, image_id):

def image_reference(selbst, bild_id): def image_reference(selbst, bild_id):

Schritt 3: Setup-Training mit P3-Spot-Instanzen und AWS-Batch

Angesichts des riesigen Datensatzes, mit dem wir gerne trainieren würden, müssen wir AWS GPU-Instanzen verwenden, um gute Leads zu einem praktischen Teil Ihrer Zeit zu machen. P3-Instanzen sind ziemlich teuer, aber wenn Sie Spot-Instanzen verwenden, erhalten Sie ein p3.2xgroßes p3.2x für ca. $0,9 / Std., was einer Ersparnis von ca. 70% entspricht. Der Schlüssel hier ist, effizient zu sein und den maximalen Betrag zu automatisieren, wie wir es tun werden, um keine Zeit/Geld in nicht schulungsbezogenen Aufgaben zu verschwenden, wie z.B. das Reparieren der Informationen usw. Um das zu versuchen, werden wir Shell-Skripte und Docker-Container verwenden und dann den großartigen AWS Batch-Dienst nutzen, um unsere Schulung zu planen.

Als erstes habe ich eine Deep Learning AMI erstellt, die für AWS Batch konfiguriert ist und Nvidia-Docker gemäß diesem AWS Guide verwendet. Die AMI-ID lautet ami-073682d8e65240b76 und es ist gastfreundlich in der Gemeinschaft. Dies ermöglicht es uns vielleicht, die Verwendung von Docker-Containern mit GPUs zu trainieren.

Als nächstes erstellen wir eine Dockerdatei, die alle Abhängigkeiten hat, die wir uns wünschen, auch weil die Shell-Skripte, die nach dem Herunterladen der Informationen Ausschau halten und das Training durchführen, die letzten drei Shell-Skripte in den Container kopiert haben: FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

WARTUNGSLEITER Gabriel Garza <garzagabriel@gmail.com>

# Essentials: Entwicklerwerkzeuge, Build-Tools, OpenBLAS

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

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

build-essential cmake \

libopenblas-dev

#

# Python 3.5

#

# Der Einfachheit halber alias (aber nicht Sym-Link) python & pip zu python3 & pip3, wie in empfohlen:

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

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

pip3 installieren pip==9.0.3 –aktualisieren && \

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

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

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

# Kissen und seine Abhängigkeiten

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

pip3 –no-cache-dir install Pillow

# Wissenschaftliche Bibliotheken und andere gemeinsame Pakete

RUN pip3 –no-cache-dir installieren \

numpy scipy sklearn scikit-image==0.13.1 pandas matplotlib Cython bittet um pandas imgaug

# AWS-CLI installieren

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

#

# Jupyter Notizbuch

#

# Zugriff von außerhalb des Containers erlauben und den Versuch, einen Browser zu öffnen, überspringen.

# HINWEIS: Deaktivieren Sie das Authentifizierungs-Token aus praktischen Gründen. TUN SIE DIES NICHT AUF EINEM ÖFFENTLICHEN SERVER.

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

mkdir /wurzel/.jupyter && \

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

“\nc.NotebookApp.open_browser = Falsch” \

“\nc.NotebookApp.token = ”” \

/root/.jupyter/jupyter_notebook_config.py

AUSSTELLUNG 8888

#

# Tensorflow 1.6.0 – GPU

#

# TensorFlow installieren

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

# Port für TensorBoard freigeben

AUSSTELLUNG 6006

#

~ OpenCV 3.4.1 ~

#

# Abhängigkeiten

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

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

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

liblapacke-dev checkinstall

RUN pip3 opencv-python installieren

#

# Keras 2.1.5

#

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

#

# PyCocoTools

#

# Verwendung einer Abspaltung des Originals, die eine Korrektur für Python 3 enthält.

# Ich habe einen PR zum ursprünglichen Repo eingereicht (https://github.com/cocodataset/cocoapi/pull/50)

# aber es scheint nicht mehr aktiv zu sein.

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

COPY setup_project_and_data.sh /home

COPY train.sh /home

COPY vorhersagen.sh /home

WORKDIR “/home

setup_project_and_data.sh -> klont unsere Maske R-CNN Repo, lädt unsere Daten aus S3 herunter und entpackt sie, teilt die Informationen in Zug- und Dev-Sets auf, lädt die neuesten Gewichte herunter, die wir in S3 gespeichert haben

train.sh -> lädt die letzten Gewichte, führt den Zugbefehl python3 aus ./ship.py train –dataset=./datasets –weights=last, lädt die trainierten Gewichte nach Trainingsende auf S3 hoch

predict.sh -> lädt den Kaggle-Challenge-Testdatensatz herunter (der für die Einreichung Ihres Beitrags zur Challenge verwendet wird), generiert Vorhersagen für jedes der Bilder, konvertiert Masken in Lauflängenkodierung und lädt die CSV-Datei der Vorhersagen auf S3 hoch.

Schritt 3: Trainieren des Modells mit AWS Batch

Das Schöne an AWS Batch ist, dass Sie einfach eine Compute-Umgebung erstellen können, die eine Spot-Instanz verwendet und die Beschäftigung unter Verwendung Ihres Docker-Containers ausführt und dann Ihre Spot-Instanz beendet, sobald Ihr Auftrag beendet ist.

Ich werde hier keine großen Details eintragen (dies könnte ein anderer Beitrag werden), aber im Wesentlichen erstellen Sie Ihr Bild, laden es in AWS ECR hoch, und in AWS Batch planen Sie dann Ihren Trainings- oder Inferenzjob so, dass er mit dem Befehl bash predict.sh oder bash train.sh ausgeführt wird und warten, bis er beendet ist (Sie können den Fortschritt verfolgen, indem Sie sich die Protokolle in AWS Watch ansehen). Die resultierenden Dateien (trainierte Gewichte oder Vorhersagen csv) werden von unserem Skript auf S3 hochgeladen.

Beim ersten Mal, wenn wir trainieren, passieren wir innerhalb des Coco-Arguments (in train.sh), um Transfer-Lernen zu verwenden und unser Modell auf dem bereits trainierten Coco-Datensatz zu trainieren:

python3 ./ship.py Zug –dataset=./datasets –gewichte=coco

Sobald wir unseren ersten Trainingslauf beendet haben, gehen wir innerhalb des letzten Arguments an die Zugführung über, so dass wir mit dem Training dort beginnen, wo wir aufgehört haben:

python3 ./ship.py Zug –dataset=./datasets –weights=last

Wir können unser Modell mit der ShipConfig-Klasse tunen und die Standardeinstellungen überschreiben. Die Einstellung der Nicht-Max-Unterdrückung auf 0 war wichtig, um darauf zu drängen, die Vorhersage überlappender Schiffsmasken zu vermeiden (was die Kaggle-Herausforderung nicht erlaubt). class ShipConfig(Config):

“””Konfiguration für das Training am Spielzeugdatensatz.

Leitet sich von der Basisklasse Config ab und setzt einige Werte außer Kraft.

“””

# Geben Sie der Konfiguration einen erkennbaren Namen

NAME = “Schiff”.

# Wir verwenden eine GPU mit 12 GB Speicher, die zwei Bilder aufnehmen kann.

# Passen Sie nach unten, wenn Sie eine kleinere GPU verwenden.

BILDER_PER_GPU = 1

# Anzahl der Klassen (einschließlich Hintergrund)

NUM_CLASSES = 1 + 1 # Hintergrund + Schiff

# Anzahl der Ausbildungsschritte pro Epoche

SCHRITTE_PRO_EPOCHE = 500

# Erkennungen mit < 95%iger Sicherheit überspringen

ERKENNUNG_MIN_VERTRAUEN = 0,95

# Nicht-maximale Unterdrückungsschwelle für Erkennung

DETEKTION_NMS_SCHWELLWERT = 0.0

BILD_MIN_DIM = 768

BILD_MAX_DIM = 768

Schritt 4: Schiffssegmentierungen vorhersagen

Um unsere Vorhersagen zu generieren, müssen wir nur versuchen, unseren Container in AWS Batch mit dem Befehl bash predict.sh auszuführen. Dies kann das Skript innerhalb von generate_predictions.py verwenden, hier ist ein Ausschnitt davon, wie eine Schlussfolgerung aussieht:

Klasse SchlußfolgerungKonfiguration(config.__class__):

# Erkennung auf einem Bild nach dem anderen ausführen

GPU_COUNT = 1

BILDER_PER_GPU = 1

ERKENNUNG_MIN_VERTRAUEN = 0,95

ERKENNUNGSSCHWELLE_NMS_THRESHOLD = 0,0

IMAGE_MIN_DIM = 768

BILD_MAX_DIM = 768

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

NACHWEIS_MAX_INSTANZEN = 20

# Modellobjekt im Inferenzmodus erstellen.

konfig = InferenzKonfig()

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

# Datensatz instanziieren

Datensatz = Schiff.SchiffDatensatz()

# Gewichte laden

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

Klassen_Namen = [‘BG’, ‘Schiff’]

# Erkennung ausführen

# Lade Bild-IDs (Dateinamen) und lauflängencodierte Pixel

images_path = “Datensätze/Test

beispiel_unter_csv = “beispiel_vorlage.csv” = “beispiel_vorlage.csv”.

# Bilder_Pfad = “Datensätze/Val”

# sample_sub_csv = “val_ship_segmentations.csv” # sample_sub_csv = “val_ship_segmentations.csv”

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

unique_image_ids = sample_submission_df.ImageId.unique()

out_pred_rows = []

Anzahl = 0

für image_id in unique_image_ids:

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

wenn os.path.isfile(image_path):

zähle += 1

Drucken (“Schritt: “, Zählen)

# Beginn der Zählung der Vorhersagezeit

tic = Zeit.Uhr()

Bild = skimage.io.imread(Bild_Pfad)

Ergebnisse = model.detect([Bild], verbose=1)

r = Ergebnisse[0]

# Erstes Bild

re_codiert_zur_rle_liste = []

für i in np.arange(np.array(np.array(r[‘masks’])).shape[-1]):

boolsche_maske = r[‘masken’][:,::,i]

re_encoded_to_rle = dataset.rle_encode(boolean_mask)

re_encoded_to_rle_list.append(re_encoded_to_rle)

wenn len(re_encoded_to_rle_list) == 0:

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

drucken (“Gefundenes Schiff: “, “NEIN”)

sonst:

für rle_mask in re_encoded_to_rle_list:

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

drucken (“Gefundenes Schiff: “, rle_mask)

toc = Zeit.Uhr()

drucken (“Vorhersagezeit: “,toc-tic)

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

Dateiname = “{}{:%Y%m%dT%H%M}.csv”.format(“./Einreichungen/Vorlage_”, datetime.datetime.now())

submission_df.to_csv(dateiname, index=Falsch)

Ich sah mehrere herausfordernde Fälle, wie Wellen und Wolken innerhalb der Bilder, die das Modell zunächst für Schiffe hielt. Um diese Herausforderung zu meistern, modifizierte ich die Ankerkastengrößen RPN_ANCHOR_SCALES des Regionsvorschlagsnetzes so, dass sie kleiner waren, was die Ergebnisse dramatisch verbesserte, da das Modell kleine Wellen nicht als Schiffe vorhersagte.

Ergebnisse

Sie können anständige Ergebnisse nach etwa 30 Epochen (definiert in ship.py) erhalten. Ich habe 160 Epochen lang trainiert und war bereit, bei meiner Kaggle-Vorlage eine Genauigkeit von 80,5% zu erreichen.

Ich habe ein Jupyter-Notebook namens inspect_shyp_model.ipynb beigefügt, mit dem Sie das Modell ausführen und Vorhersagen für jedes Bild lokal auf Ihrem Computer machen können.