Maschera R-CNN per il rilevamento e la segmentazione delle navi

Una delle principali applicazioni entusiasmanti dell’apprendimento profondo è la capacità delle macchine di conoscere le immagini. Fei-Fei Li ha parlato di questo come di dare alle macchine la “capacità di vedere”. Ci sono quattro classi principali di problemi nella rilevazione e nella segmentazione come descritto all’interno dell’immagine (a) qui sotto.

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

Maschera R-CNN

Maschera R-CNNN è un’estensione su R-CNNN più veloce. R-CNNN più veloce prevede le caselle di delimitazione e Maschera R-CNNN aggiunge essenzialmente un altro ramo per prevedere una maschera di oggetto in parallelo.

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

Non ho intenzione di entrare nel dettaglio di come funziona Mask R-CNNN, ma ecco i passi complessivi che l’approccio segue:

Modello Backbone: una tipica rete neurale convoluzionale tipica che è un estrattore di feature. per esempio, trasforma un’immagine 1024x1024x1024x3 in una feature map 32x32x2048 che viene inserita per i livelli successivi.

Region Proposal Network (RPN): Utilizzando regioni definite con un massimo di 200K box di ancoraggio, la RPN scansiona ogni regione e predice se un oggetto è presente o meno. uno dei buoni vantaggi della RPN è che non scansiona la particolare immagine, la rete scansiona la feature map, rendendola molto più veloce.

Classificazione delle regioni di interesse e bounding box: durante questa fase l’algoritmo prende le regioni di interesse proposte dalla RPN come input e output una classificazione (softmax) e un bounding box (regressor).

Maschere di segmentazione: all’interno del passo finale, l’algoritmo prende le regioni di ROI positive come input e genera maschere di 28×28 pixel con valori float come output per gli oggetti. Durante l’inferenza, queste maschere vengono scalate.

Addestramento e Inferenza con Maschera R-CNN

Invece di replicare l’intero algoritmo supportato dal documento di ricerca, useremo la fantastica libreria Mask R-CNN che Matterport ha costruito. Avremo bisogno di A) generare il nostro treno e i nostri set di dev, B) fare un po’ di lotta per caricare la libreria, C) impostare il nostro ambiente di addestramento in AWS per l’addestramento, D) utilizzare il transfer learning per iniziare l’addestramento dai pesi preallenati del coco, ed E) sintonizzare il nostro modello per sollecitare buoni risultati.

Fase 1: Scaricare i dati di Kaggle e generare le divisioni di Train e Dev

Il set di dati fornito da Kaggle è composto da molte migliaia di immagini, quindi la cosa più semplice è scaricarle sulla macchina AWS dove faremo il nostro training. Una volta che le abbiamo scaricate, dovremo dividerle in set di addestramento e set di dev, che possono essere fatti in modo casuale attraverso uno script di python.

Raccomando vivamente di utilizzare un’istanza spot per scaricare le informazioni da Kaggle utilizzando l’API di Kaggle e caricare i dati zippati in un secchio S3. In seguito scaricherete i dati da S3 e li decomprimerete al momento dell’allenamento.

Kaggle fornisce un file csv chiamato train_ship_segmentations.csv con due colonne: ImageId e EncodedPixels (formato di codifica della lunghezza di esecuzione). Supponendo di aver scaricato le immagini nel percorso ./datasets/train_val/train, le divideremo e le sposteremo nelle cartelle di train_ship_segmentations_df = pd.read_csv(os.path.join(“./datasets/train_val/train_ship_segmentations.csv”)).

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

treno = treno_nave_segmentazioni_df[msk]

test = treno_nave_ship_segmentations_df[~msk]

# Spostare il treno

per indice, riga in train.iterrows():

image_id = riga[“ImageId”]

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

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

se os.path.isfile(old_path):

rinominare(vecchio_percorso, nuovo_percorso)

# Move dev set

per indice, riga in test.iterrows():

image_id = riga[“ImageId”]

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

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

se os.path.isfile(old_path):

rinominare(vecchio_percorso, nuovo_percorso)

Fase 2: caricare i dati nella libreria

C’è una convenzione selezionata che la libreria Mask R-CNN segue per il caricamento dei dataset. vorremmo fare una categoria ShipDataset che implementerà il maggior numero di funzioni richieste:

ship.py

classe ShipDataset(utils.Dataset):

def load_ship(auto, dataset_dir, sottoinsieme):

def load_mask(self, image_id):

def riferimento_immagine(self, image_id):

Per convertire una Run Length Encoded Mask in una maschera di immagine (tensore booleano) usiamo questa funzione sotto rle_decode. questo è spesso usato per generare le maschere di verità di fondo che carichiamo nella libreria per l’addestramento nella nostra classe ShipDataset.

ship.py

classe ShipDataset(utils.Dataset):

def load_ship(auto, dataset_dir, sottoinsieme):

def load_mask(self, image_id):

def riferimento_immagine(self, image_id):

Fase 3: Setup Training con P3 Spot Instances e AWS Batch

Dato l’enorme set di dati con cui vorremmo allenarci, dovremo usare le istanze delle GPU AWS per sollecitare dei buoni risultati per una quantità pratica del vostro tempo. Le istanze P3 sono abbastanza costose, ma utilizzando le istanze Spot si ottiene un p3.2xlarge per circa $0.9 / hr che rappresenta circa il 70% di risparmio. La chiave qui è essere efficienti e automatizzare la quantità massima, in modo da non sprecare tempo e denaro in attività non formative come la correzione delle informazioni, ecc. Per cercare di farlo, useremo script shell e container docker, poi useremo l’impressionante servizio AWS Batch per programmare la nostra formazione.

La prima cosa che ho fatto è stato creare un AMI di apprendimento profondo configurato per AWS Batch che utilizza nvidia-docker seguendo questa Guida AWS. L’ID AMI è ami-073682d8e65240b76 ed è ospitale per la comunità. questo potrebbe permetterci di allenarci usando contenitori docker con GPU.

Il prossimo è la creazione di un dockerfile che ha tutte le dipendenze che vorremmo anche perché gli script di shell che lookout di scaricare le informazioni ed eseguire l’allenamento.Nota gli ultimi tre script di shell copiato nel contenitore: DA nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

MAINTAINER Gabriel Garza <garzagabriel@gmail.com>

# Essenziali: strumenti per sviluppatori, strumenti per la costruzione, OpenBLAS

RUN apt-get update && apt-get install -y –no-install-recommends \\\\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144}-Installazione

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

Costruire-essenziale cmake \\\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144}-

libopenblas-dev

#

# Pitone 3.5

#

# Per comodità, alias (ma non sym-link) python & pip to python3 & pip3 come raccomandato in:

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

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

pip3 installate 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_alias

# Cuscino e le sue dipendenze

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

pip3 –no-cache-dir installare Pillow

# Biblioteche scientifiche e altri pacchetti comuni

RUN pip3 –no-cache-dir installare \\\code(011)\code(011)\code(011)\code(011)\code(011)\code(011)\code(011)\code(011)\code(011)

scipy scipy sklearn scikit-image==0,13,1 pandas matplotlib Cython richieste pandas imgaug

# Installare AWS CLI

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

#

# Taccuino Jupyter

#

# Consentire l’accesso dall’esterno del contenitore e saltare il tentativo di aprire un browser.

# NOTA: disattivare il token di autenticazione per comodità. NON FARLO SU UN SERVER PUBBLICO.

Esegui pip3 –no-cache-dir install jupyter && \ \ \ }.

mkdir /root/.jupyter &&& \\\x22

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

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

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

> /root/.jupyter/jupyter_notebook_config.py

ESPOSIZIONE 8888

#

# Tensorflow 1.6.0 – GPU

#

# Installare TensorFlow

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

# Porta di esposizione per TensorBoard

ESPOSIZIONE 6006

#

# OpenCV 3.4.1

#

# Dipendenze

RUN apt-get install -y –no-install-raccomanda \\\\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144)\code(0144}-Installazione

libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev \\\ \\ }Bibjpeg8-dev libtiff5-dev libtiff5-dev libjasper-dev libpng12-dev

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

liblapacke-dev checkinstall

RUN pip3 installa opencv-python

#

# Keras 2.1.5

#

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

#

# PyCocoTools

#

# Usando una forchetta dell’originale che ha un fix per Python 3.

# Ho presentato una PR al repo originale (https://github.com/cocodataset/cocoapi/pull/50)

# ma non sembra più attivo.

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

COPY setup_progetto_e_dati.sh /home

COPY train.sh /home

COPY predict.sh /home

WORKDIR “/casa”

setup_project_and_data.sh -> clona il nostro repo Mask R-CNN, scarica e decomprime i nostri dati da S3, suddivide le informazioni in treno e dev set, scarica i pesi più recenti che abbiamo salvato in S3

train.sh -> carica gli ultimi pesi, esegue il comando treno python3 ./ship.py train –dataset=./datasets –weights=last, carica i pesi addestrati su S3 dopo la fine dell’addestramento

predict.sh -> scarica il dataset del test Kaggle Challenge (che viene utilizzato per sottoporre il tuo ingresso alla sfida), genera previsioni per ogni immagine, converte le maschere per la codifica della lunghezza di esecuzione e carica il file CSV delle previsioni in S3.

Fase 3: Allenare il modello utilizzando AWS Batch

Il bello di AWS Batch è che si può semplicemente creare un ambiente di calcolo che utilizza una Spot Instance e che esegue l’impiego utilizzando il vostro container docker, per poi terminare la vostra Spot Instance non appena il vostro lavoro finisce.

Non entrerò in grande dettaglio qui (potrebbe essere un altro post), ma essenzialmente si costruisce la propria immagine, la si carica in AWS ECR, poi in AWS Batch si programma il proprio lavoro di addestramento o di inferenza per eseguire con il comando bash predict.sh o bash train.sh e si attende che finisca (si può seguire il progresso guardando i log in AWS Watch). I file risultanti (pesi addestrati o previsioni csv) vengono caricati in S3 dal nostro script.

La prima volta che ci alleniamo, passiamo all’interno dell’argomento coco (in train.sh) per utilizzare il Transfer Learning e addestrare il nostro modello in aggiunta al set di dati coco già addestrati:

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

Una volta terminato l’allenamento iniziale, passeremo entro l’ultimo argomento al comando del treno, così inizieremo l’allenamento da dove abbiamo lasciato:

python3 ./ship.py treno –dataset=./dataset — pesi=ultimo

Possiamo sintonizzare il nostro modello usando la classe ShipConfig e sovrascrivendo le impostazioni di default. Impostare la soppressione Non-Max a 0 è stato importante per evitare la sovrapposizione di maschere di navi sovrapposte (che la sfida di Kaggle non permette). classe ShipConfig(Config):

“””Configurazione per l’addestramento sul set di dati del giocattolo.

Derivante dalla classe Config di base e sovrascrive alcuni valori.

“””

# Dare alla configurazione un nome riconoscibile

NOME = “nave”.

# Usiamo una GPU con 12GB di memoria, che può contenere due immagini.

# Regolare verso il basso se si usa una GPU più piccola.

IMAGES_PER_GPU = 1

# Numero di classi (incluso lo sfondo)

NUM_CLASSES = 1 + 1 # Sfondo + nave

# Numero di passi di formazione per epoca

PASSI_PER_EPOCA = 500

# Salta le rilevazioni con una confidenza < 95%.

DETECTION_MIN_CONFIDENCE = 0.95

# Soglia di soppressione non massima per il rilevamento

DETECTION_NMS_THRESHOLD = 0.0

IMAGE_MIN_DIM = 768

IMAGE_MAX_DIM = 768

Fase 4: prevedere le segmentazioni delle navi

Per generare le nostre previsioni, tutto quello che dobbiamo cercare di fare è eseguire il nostro contenitore in AWS Batch con il comando bash predict.sh. Questo può usare lo script all’interno di generate_predictions.py, ecco un frammento di ciò che sembra l’inferenza:

classe InferenceConfig(config.__class__):

# Eseguire il rilevamento su un’immagine alla volta

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)

RILEVAMENTO_MAX_SOSTANZE = 20

# Creare un oggetto modello in modalità inferenza.

config = InferenceConfig()

model = modellib.MaskRCNNN(mode=”inferenza”, model_dir=MODEL_DIR, config=config)

# Set di dati istantanei

dataset = nave.ShipDataset()

# Pesi di carico

Pesi.del.modello.di.carico(os.path.join(ROOT_DIR, SHIP_WEIGHTS_PATH), per_nome=True)

class_names = [‘BG’, ‘nave’]

# Esegui il rilevamento

# Caricare gli ID delle immagini (nomi dei file) e la lunghezza di esecuzione dei pixel codificati

images_path = “set di dati/test”.

sample_sub_csv = “sample_submission.csv” = “sample_submission.csv”.

# images_path = “datasets/val”.

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

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

unique_image_ids = campione_submission_df.ImageId.unique()

out_pred_rows = []

conteggio = 0

per image_id in unique_image_id:

percorso_immagine = os.path.join(percorso_immagine, immagine_id)

se os.path.isfile(percorso_immagine):

conta += 1

stampa (“Passo: “, conteggio)

# Inizia a contare il tempo di previsione

tic = tempo.clock()

immagine = skimage.io.imread(image_path)

risultati = model.detect([immagine], verbose=1)

r = risultati[0]

# Prima immagine

re_encoded_to_rle_list = []

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

maschera_booleana = r[‘maschere’][:,:,i]

re_encoded_to_rle = dataset.rle_encode(maschera_booleana)

re_codificato_a_lista_rle.append(re_codificato_a_rle)

se len(re_encoded_to_rle_list) == 0:

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

stampa (“Nave trovata: “, “NO”)

altro:

per rle_mask in re_encoded_to_list:

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

stampa(“Nave trovata: “, rle_mask)

toc = tempo.clock()

stampa (“Tempo di previsione: “,toc-tic)

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

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

submission_df.to_csv(nome file, indice=Falso)

Ho visto diversi casi impegnativi, come le onde e le nuvole all’interno delle immagini, che il modello inizialmente pensava fossero navi. per vincere questa sfida, ho modificato le dimensioni della proposta di rete di ancoraggio RPN_ANCHOR_SCALES della rete regionale per renderla più piccola, questo ha migliorato notevolmente i risultati perché il modello non prevedeva che le piccole onde fossero navi.

Risultati

Si possono ottenere risultati decenti dopo circa 30 epoche (definite in ship.py). Mi sono allenato per 160 epoche ed ero pronto ad ottenere una precisione dell’80,5% nella mia presentazione di Kaggle.

Ho incluso un Quaderno Jupyter chiamato inspect_shyp_model.ipynb che permette di eseguire il modello e di fare previsioni su qualsiasi immagine localmente sul computer.