Efficiente segmentazione delle immagini utilizzando PyTorch Parte 2

Efficient image segmentation with PyTorch Part 2

Un modello basato su CNN

Questa è la seconda parte della serie di 4 parti per implementare la segmentazione delle immagini passo dopo passo da zero utilizzando le tecniche di deep learning in PyTorch. Questa parte si concentrerà sull’implementazione di un modello di segmentazione delle immagini di base basato su una rete neurale convoluzionale (CNN).

Scritto in collaborazione con Naresh Singh

Figura 1: Risultato dell'esecuzione della segmentazione delle immagini utilizzando una CNN. In ordine dall'alto verso il basso, immagini di input, maschere di segmentazione ground truth, maschere di segmentazione previste. Fonte: Autore(i)

Sommario dell’articolo

In questo articolo, implementeremo un’architettura basata su una rete neurale convoluzionale (CNN) chiamata SegNet che assegnerà ogni pixel di un’immagine di input a un animale domestico corrispondente come un gatto o un cane. I pixel che non appartengono a nessun animale domestico verranno classificati come pixel di sfondo. Costruiremo e addestreremo questo modello sul dataset Oxford Pets utilizzando PyTorch per sviluppare un senso di ciò che serve per fornire un’attività di segmentazione delle immagini di successo. Il processo di costruzione del modello sarà pratico in cui discuteremo dettagliatamente il ruolo di ogni livello nel nostro modello. L’articolo conterrà molte referenze a articoli e paper di ricerca per ulteriori approfondimenti.

In tutto questo articolo, faremo riferimento al codice e ai risultati di questo notebook . Se desideri riprodurre i risultati, avrai bisogno di una GPU per garantire che il notebook completi l’esecuzione in un tempo ragionevole.

Articoli in questa serie

Questa serie è per lettori di tutti i livelli di esperienza con il deep learning. Se vuoi imparare sulla pratica del deep learning e della visione artificiale insieme ad una solida teoria ed esperienza pratica, sei nel posto giusto! Questa serie dovrebbe essere composta da 4 parti con i seguenti articoli:

  1. Concepts and Ideas
  2. Un modello basato su CNN (questo articolo)
  3. Convoluzioni separabili in profondità
  4. Un modello basato su Vision Transformer

Iniziamo questa discussione con una breve introduzione ai livelli di convoluzione e ad alcuni altri livelli che sono tipicamente usati insieme come blocco di convoluzione.

Conv-BatchNorm-ReLU e Max Pooling/Unpooling

Un blocco di convoluzione, batch-normalization, ReLU è la santa trinità della visione artificiale. Lo vedrai usato frequentemente con i modelli di visione artificiale basati su CNN. Ciascuno di questi termini rappresenta un livello distinto implementato in PyTorch. Il livello di convoluzione è responsabile per eseguire un’operazione di cross-correlazione di filtri appresi sul tensore di input. La normalizzazione del batch centra gli elementi nel batch a media zero e varianza unitaria, e ReLU è una funzione di attivazione non lineare che mantiene solo i valori positivi nell’input.

Una CNN tipica riduce progressivamente le dimensioni spaziali dell’input man mano che i livelli sono impilati. La motivazione alla base della riduzione delle dimensioni spaziali è discussa nella sezione successiva. Questa riduzione viene ottenuta mediante il pooling dei valori vicini utilizzando una semplice funzione come max o media. Parleremo ulteriormente di questo nella sezione Max-Pooling. In problemi di classificazione, lo stack di blocchi Conv-BN-ReLU-Pool è seguito da una testa di classificazione che predice la probabilità che l’input appartenga ad una delle classi target. Alcuni insiemi di problemi come la segmentazione semantica richiedono una previsione per pixel. Per tali casi, uno stack di blocchi di upsampling viene aggiunto dopo i blocchi di downsampling per proiettare il loro output alla dimensione spaziale richiesta. I blocchi di upsampling non sono altro che blocchi Conv-BN-ReLU-Unpool che sostituiscono il livello di pooling con un livello di un-pooling. Parleremo ulteriormente di un-pooling nella sezione Max-Pooling.

Ora, approfondiamo ulteriormente la motivazione alla base dei livelli di convoluzione.

Convoluzione

Le convoluzioni sono i blocchi di costruzione di base dei modelli di visione artificiale. Sono utilizzati pesantemente nella visione artificiale e storicamente sono stati utilizzati per implementare trasformazioni della visione come:

  1. Rilevamento dei bordi
  2. Sfocatura e nitidezza dell’immagine
  3. Relievo
  4. Intensificazione

Un’operazione di convoluzione è una moltiplicazione elemento per elemento e l’aggregazione di due matrici. Un esempio di operazione di convoluzione è mostrato nella Figura 2.

Figura 2: Un'illustrazione dell'operazione di convoluzione. Fonte: Autore(i)

In un contesto di deep learning, la convoluzione viene effettuata tra una matrice di parametri n-dimensionale chiamata filtro o kernel su un input di dimensioni maggiori. Ciò viene ottenuto facendo scorrere il filtro sull’input e applicando la convoluzione alla sezione corrispondente. L’estensione dello scorrimento viene configurata utilizzando un parametro di stride. Uno stride di uno significa che il kernel scorre di un passo per operare sulla sezione successiva. A differenza degli approcci tradizionali in cui viene utilizzato un filtro fisso, il deep learning apprende il filtro dai dati utilizzando la retropropagazione.

Quindi come aiutano le convoluzioni nel deep learning?

Nel deep learning, un layer di convoluzione viene utilizzato per rilevare le caratteristiche visive. Un modello CNN tipico contiene uno stack di tali layer. I livelli inferiori dello stack rilevano le caratteristiche semplici come linee e bordi. Man mano che ci spostiamo verso l’alto nello stack, i livelli rilevano caratteristiche sempre più complesse. I livelli intermedi nello stack rilevano combinazioni di linee e bordi e i livelli superiori rilevano forme complesse come una macchina, un volto o un aereo. La figura 3 mostra visualmente l’output dei livelli superiori e inferiori per un modello addestrato.

Figura 3: Cosa imparano a identificare i filtri convoluzionali. Fonte: Convolutional Deep Belief Networks for Scalable Unsupervised Learning of Hierarchical Representations

Un layer di convoluzione ha un insieme di filtri apprendibili che agiscono su piccole regioni dell’input per produrre un valore rappresentativo per ogni regione. Ad esempio, un filtro 3×3 opera su una regione di dimensioni 3×3 e produce un valore rappresentativo della regione. L’applicazione ripetuta di un filtro su regioni di input produce un output che diventa l’input per il layer successivo nello stack. In modo intuitivo, i livelli più in alto hanno una visione di una regione di input più ampia. Ad esempio, un filtro 3×3 nel secondo layer di convoluzione opera sull’output del primo layer di convoluzione in cui ogni cella contiene informazioni sulla regione di dimensioni 3×3 nell’input. Se assumiamo un’operazione di convoluzione con stride = 1, il filtro nel secondo layer “vedrà” la regione di dimensioni 5×5 dell’input originale. Questo è chiamato campo recettivo della convoluzione. L’applicazione ripetuta di layer convoluzionali riduce progressivamente le dimensioni spaziali dell’immagine di input e aumenta il campo visivo dei filtri che consente loro di “vedere” forme complesse. La figura 4 mostra il processing di un input 1-D da una rete di convoluzione. Un elemento nello strato di output è rappresentativo di un pezzo di input relativamente più grande.

Figura 4: Campo recettivo di una convoluzione 1d con dimensione del kernel = 3, applicato 3 volte. Supponiamo stride = 1 e nessun padding. Dopo la terza applicazione successiva del kernel convoluzionale, un singolo pixel è in grado di vedere 7 pixel nell'immagine di input originale. Fonte: Autore(i)

Una volta che un layer di convoluzione può rilevare questi oggetti ed è in grado di generare le loro rappresentazioni, possiamo utilizzare queste rappresentazioni per la classificazione delle immagini, la segmentazione delle immagini e il rilevamento e la localizzazione degli oggetti. In generale, le CNN aderiscono ai seguenti principi generali:

  1. Un layer di convoluzione mantiene il numero di canali di output (©) intatto o lo raddoppia.
  2. Mantiene le dimensioni spaziali intatte utilizzando uno stride = 1 o le riduce della metà utilizzando uno stride = 2.
  3. È comune raggruppare l’output di un blocco di convoluzione per cambiare le dimensioni spaziali di un’immagine.

Un layer di convoluzione applica il kernel in modo indipendente a ogni input. Ciò potrebbe causare una variazione del suo output per diversi input. Un layer di normalizzazione batch segue tipicamente un layer di convoluzione per affrontare questo problema. Vediamo il suo ruolo in dettaglio nella prossima sezione.

Normalizzazione Batch

Il livello di normalizzazione batch normalizza i valori del canale nell’input batch per avere una media zero e una varianza unitaria. Questa normalizzazione viene eseguita in modo indipendente per ogni canale nel batch per garantire che i valori del canale per gli input abbiano la stessa distribuzione. La normalizzazione batch ha i seguenti vantaggi:

  1. Stabilizza il processo di formazione impedendo che i gradienti diventino troppo piccoli.
  2. Permette una convergenza più veloce sulle nostre attività.

Se avessimo solo una pila di livelli di convoluzione, sarebbe essenzialmente equivalente a una rete a singolo livello di convoluzione a causa dell’effetto cascata delle trasformazioni lineari. In altre parole, una sequenza di trasformazioni lineari può essere sostituita con una singola trasformazione lineare che ha lo stesso effetto. In modo intuitivo, se moltiplichiamo un vettore per una costante k₁ seguita da una moltiplicazione per un’altra costante k₂, è equivalente a una singola moltiplicazione per una costante k₁k₂. Pertanto, per le reti ad essere realisticamente profonde, devono avere una non linearità per impedirne il collasso. Discuteremo ReLU nella prossima sezione che viene spesso utilizzata come non linearità.

ReLU

ReLU è una semplice funzione di attivazione non lineare che taglia i valori di input più bassi in modo che siano maggiori o uguali a 0. Aiuta anche con il problema dei gradienti che svaniscono limitando le uscite a essere maggiori o uguali a 0. Il livello ReLU è tipicamente seguito da un livello di pooling per ridurre le dimensioni spaziali nel sottorete di ridimensionamento verso il basso o un livello di un-pooling per aumentare le dimensioni spaziali nella sottorete di ridimensionamento verso l’alto. I dettagli sono forniti nella prossima sezione.

Pooling

Un livello di pooling viene utilizzato per ridurre le dimensioni spaziali dei nostri input. Il pooling con stride=2 trasformerà un input con dimensioni spaziali (H, W) in (H/2, W/2). Il max-pooling è la tecnica di pooling più comunemente usata nei deep CNNs. Proietta il valore massimo in una griglia di (ad esempio) 2×2 sull’output. Quindi, scivoliamo la finestra di pooling 2×2 alla sezione successiva in base alla stride simile alle convoluzioni. Fare ciò ripetutamente con una stride=2 produce un output che è la metà dell’altezza e della larghezza dell’input. Un altro livello di pooling comunemente usato è il livello di media-pooling, che calcola la media invece del massimo.

L’inverso di un livello di pooling viene chiamato livello un-pooling. Prende un’input con dimensione (H, W) e lo converte in un’output con dimensione (2H, 2W) per stride=2. Un ingrediente necessario di questa trasformazione è la selezione della posizione nella sezione 2×2 dell’output per proiettare il valore di input. Per fare questo, abbiamo bisogno di una mappa degli indici di max-unpooling che ci dice le posizioni di destinazione nella sezione di output. Questa mappa di unpooling è prodotta da una precedente operazione di max-pooling. La figura 5 mostra esempi di operazioni di pooling e un-pooling.

Figura 5: Max pooling e un-pooling. Fonte: DeepPainter: Painter Classification Using Deep Convolutional Autoencoders

Possiamo considerare il max-pooling come un tipo di funzione di attivazione non lineare. Tuttavia, è stato segnalato che usarlo al posto di una non linearità come ReLU influisce sulle prestazioni della rete. Al contrario, il pooling medio non può essere considerato come una funzione non lineare poiché utilizza tutti i suoi input per produrre un output che è una combinazione lineare dei suoi input.

Ciò copre tutti i blocchi di base dei deep CNNs. Ora, mettiamoli insieme per creare un modello. Il modello che abbiamo scelto per questo esercizio è chiamato SegNet. Lo discuteremo nella prossima sezione.

SegNet: un modello basato su CNN

SegNet è un modello deep CNN basato sui blocchi fondamentali che abbiamo discusso in questo articolo. Ha due sezioni distinte. La sezione inferiore, anche chiamata codificatore, ridimensiona l’input per generare caratteristiche rappresentative dell’input. La sezione superiore del decodificatore ridimensiona le caratteristiche per creare una classificazione per pixel. Ogni sezione è composta da una sequenza di blocchi Conv-BN-ReLU. Questi blocchi incorporano anche livelli di pooling o un-pooling nei percorsi di ridimensionamento verso il basso e verso l’alto rispettivamente. La figura 6 mostra l’organizzazione dei livelli in modo più dettagliato. SegNet utilizza gli indici di pooling dall’operazione di max-pooling nel codificatore per determinare quali valori copiare durante l’operazione di max-unpooling nel decodificatore. Mentre ogni elemento di un tensore di attivazione è di 4 byte (32 bit), un offset all’interno di un quadrato 2×2 può essere archiviato utilizzando solo 2 bit. Questo è più efficiente in termini di memoria utilizzata poiché queste attivazioni (o indici nel caso di SegNet) devono essere archiviate mentre il modello viene eseguito.

Figura 6: L'architettura del modello SegNet per la segmentazione delle immagini. Fonte: SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation

Questo notebook contiene tutto il codice per questa sezione.

Questo modello ha 15.27M di parametri addestrabili.

È stata utilizzata la seguente configurazione durante l’addestramento e la validazione del modello.

  1. Le tecniche di data augmentation, ovvero la rotazione orizzontale casuale e la modifica casuale del colore, vengono applicate all’insieme di addestramento per evitare l’overfitting
  2. Le immagini vengono ridimensionate a 128×128 pixel senza preservare l’aspetto originale
  3. Non viene applicata alcuna normalizzazione di input alle immagini; invece, viene utilizzato uno strato di normalizzazione batch come primo strato del modello
  4. Il modello viene addestrato per 20 epoche utilizzando l’ottimizzatore Adam con un LR di 0,001 e uno scheduler StepLR che riduce il tasso di apprendimento del 0,7 ogni 7 epoche
  5. Viene utilizzata la funzione di perdita di entropia incrociata per classificare un pixel come appartenente a un animale domestico, allo sfondo o al bordo di un animale domestico

Il modello ha raggiunto una precisione di validazione dell’88,28% dopo 20 epoche di addestramento.

Abbiamo creato un gif che mostra come il modello sta imparando a prevedere le maschere di segmentazione per 21 immagini nell’insieme di validazione.

Figura 6: Un gif che mostra come il modello SegNet sta imparando a prevedere le maschere di segmentazione per 21 immagini nell'insieme di validazione. Fonte: Autore(i)

Le definizioni di tutte le metriche di validazione sono descritte nella Parte 1 di questa serie.

Se desideri vedere un modello completamente convoluzionale per la segmentazione di immagini di animali domestici implementato utilizzando Tensorflow, consulta il Capitolo 4: Architetture efficienti del libro Efficient Deep Learning.

Osservazioni dall’apprendimento del modello

In base allo sviluppo delle previsioni che il modello addestrato effettua dopo ogni epoca, possiamo osservare quanto segue.

  1. Il modello è in grado di apprendere abbastanza da rendere l’output simile all’animale domestico nell’immagine già dopo la prima epoca di addestramento
  2. I pixel del bordo sono più difficili da segmentare poiché stiamo utilizzando una funzione di perdita non pesata che tratta ogni successo (o fallimento) allo stesso modo, quindi sbagliare i pixel del bordo non costa molto al modello in termini di perdita. Ti consigliamo di indagare questo aspetto e di verificare quali strategie potresti provare per risolvere questo problema. Prova ad utilizzare la Focal Loss e vedi come si comporta
  3. Il modello sembra imparare anche dopo 20 epoche di addestramento. Ciò suggerisce che potremmo migliorare la precisione di validazione se addestrassimo il modello più a lungo
  4. Alcune delle etichette di verità di terreno stesse sono difficili da capire, ad esempio la maschera del cane nella riga centrale, ultima colonna, ha molti pixel sconosciuti nell’area in cui il corpo del cane è oscurato dalle piante. Questo è molto difficile da capire per il modello, quindi ci si aspetta sempre una perdita di precisione per tali esempi. Tuttavia, questo non significa che il modello non stia facendo bene. Si dovrebbero sempre controllare le previsioni per sviluppare un senso del comportamento del modello oltre a guardare le metriche di validazione complessive.
Figura 7: Un esempio di maschera di segmentazione di verità di terreno che contiene molti pixel sconosciuti. Questo è un input molto difficile per qualsiasi modello di apprendimento automatico. Fonte: Autore(i)

Conclusione

Nella Parte 2 di questa serie, abbiamo appreso i blocchi di costruzione di base delle reti neurali convoluzionali profonde per l’intelligenza artificiale nella visione. Abbiamo visto come implementare il modello SegNet da zero in PyTorch e abbiamo visualizzato come il modello addestrato su successive epoche si comporta su 21 immagini di validazione. Questo dovrebbe aiutare a comprendere quanto rapidamente i modelli possano apprendere abbastanza da rendere l’output simile all’immagine originale. In questo caso, possiamo vedere maschere di segmentazione che somigliano approssimativamente alla maschera di segmentazione effettiva già dalla prima epoca di addestramento!

Nella prossima parte di questa serie, vedremo come possiamo ottimizzare il nostro modello per l’elaborazione on-device e ridurre il numero di parametri addestrabili (e quindi la dimensione del modello) mantenendo circa la stessa accuratezza di validazione.

Ulteriori letture

Leggi di più sulle convoluzioni qui:

  1. Il corso intitolato “Antichi segreti della visione artificiale” presso l’Università di Washington tenuto da Joseph Redmon ha un eccellente set di video sulle convoluzioni (soprattutto i capitoli 4, 5 e 13), che consigliamo vivamente di guardare
  2. Una guida alla matematica delle convoluzioni per il deep learning (altamente consigliata)
  3. https://towardsdatascience.com/computer-vision-convolution-basics-2d0ae3b79346
  4. Il livello Conv2d in PyTorch (documentazione)
  5. Cosa imparano le convoluzioni?
  6. Visualizzatore di convoluzioni

Leggi di più sulla normalizzazione del batch qui:

  1. Normalizzazione del batch: Wikipedia
  2. Normalizzazione del batch: Machine learning mastery
  3. Livello BatchNorm2d in PyTorch qui.

Leggi di più sulle funzioni di attivazione e ReLU qui:

  1. ReLU: Machine learning mastery
  2. ReLU: Wikipedia
  3. ReLU: Quora
  4. API ReLU in PyTorch