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.
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.
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.


