Una guida completa all’architettura UNET | Padronanza della segmentazione delle immagini

Complete guide to UNET architecture | Mastering image segmentation

Introduzione

Nell’entusiasmante campo della computer vision, dove le immagini nascondono molti segreti e informazioni, distinguere e evidenziare gli elementi è cruciale. La segmentazione delle immagini, il processo di suddivisione delle immagini in regioni o oggetti significativi, è fondamentale in diverse applicazioni, dalla diagnostica medica alla guida autonoma e al riconoscimento degli oggetti. La segmentazione accurata e automatica è da tempo una sfida, con gli approcci tradizionali che spesso falliscono in termini di precisione ed efficienza. Entra in scena l’architettura UNET, un metodo intelligente che ha rivoluzionato la segmentazione delle immagini. Con il suo design semplice e le tecniche innovative, UNET ha aperto la strada a risultati di segmentazione più precisi e robusti. Che tu sia un neofita nel campo affascinante della computer vision o un professionista esperto alla ricerca di migliorare le tue capacità di segmentazione, questo articolo approfondito ti svelerà le complessità di UNET e ti fornirà una comprensione completa della sua architettura, dei suoi componenti e della sua utilità.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Comprensione delle reti neurali convoluzionali

Le CNN sono un modello di deep learning spesso utilizzato in compiti di computer vision, tra cui classificazione delle immagini, riconoscimento degli oggetti e segmentazione delle immagini. Le CNN sono principalmente utilizzate per apprendere ed estrarre informazioni rilevanti dalle immagini, rendendole estremamente utili nell’analisi dei dati visivi.

I componenti fondamentali delle CNN

  • Strati convoluzionali: Le CNN comprendono una serie di filtri apprendibili (kernel) convoluti con l’immagine di input o le mappe delle caratteristiche. Ogni filtro applica una moltiplicazione elemento per elemento e una somma per produrre una mappa delle caratteristiche che evidenzia modelli specifici o caratteristiche locali nell’input. Questi filtri possono catturare molti elementi visivi, come bordi, angoli e texture.

  • Strati di pooling: Creano le mappe delle caratteristiche ottenute dagli strati convoluzionali che vengono ridotte di dimensione utilizzando gli strati di pooling. Il pooling riduce le dimensioni spaziali delle mappe delle caratteristiche mantenendo le informazioni più importanti, riducendo la complessità computazionale dei livelli successivi e rendendo il modello più resistente alle fluttuazioni dell’input. L’operazione di pooling più comune è il max pooling, che prende il valore più grande all’interno di un dato vicinato.
  • Funzioni di attivazione: Introducono non linearità nel modello CNN utilizzando funzioni di attivazione. Vengono applicate agli output degli strati convoluzionali o di pooling elemento per elemento, consentendo alla rete di comprendere relazioni complesse e prendere decisioni non lineari. A causa della sua semplicità ed efficienza nel risolvere il problema del gradiente che svanisce, la funzione di attivazione Rectified Linear Unit (ReLU) è comune nelle CNN.
  • Strati completamente connessi: Gli strati completamente connessi, chiamati anche strati densi, utilizzano le caratteristiche ottenute per completare l’operazione finale di classificazione o regressione. Collegano ogni neurone in uno strato a ogni neurone nel successivo, consentendo alla rete di apprendere rappresentazioni globali e prendere decisioni di alto livello basate sull’input combinato degli strati precedenti.

La rete inizia con una serie di strati convoluzionali per catturare le caratteristiche di basso livello, seguiti dagli strati di pooling. Gli strati convoluzionali più profondi apprendono caratteristiche di livello superiore man mano che la rete evolve. Infine, si utilizzano uno o più strati completamente connessi per l’operazione di classificazione o regressione.

Necessità di una rete completamente connessa

Le CNN tradizionali sono generalmente concepite per compiti di classificazione delle immagini in cui viene assegnata un’unica etichetta all’intera immagine di input. D’altra parte, le architetture di CNN tradizionali hanno problemi con compiti più dettagliati come la segmentazione semantica, in cui ogni pixel di un’immagine deve essere classificato in diverse classi o regioni. Qui entrano in gioco le Fully Convolutional Networks (FCN).

Limitazioni delle architetture di CNN tradizionali nei compiti di segmentazione

Perdita di informazioni spaziali: Le CNN tradizionali utilizzano strati di pooling per ridurre gradualmente la dimensionalità spaziale delle mappe delle caratteristiche. Sebbene questo downsampling aiuti a catturare caratteristiche di alto livello, comporta una perdita di informazioni spaziali, rendendo difficile individuare e separare con precisione gli oggetti a livello di pixel.

Dimensione di input fissa: Le architetture di CNN sono spesso progettate per accettare immagini di dimensioni specifiche. Tuttavia, le immagini di input potrebbero avere dimensioni diverse nei compiti di segmentazione, rendendo difficile gestire input di dimensioni variabili con le CNN tradizionali.

Precisione di localizzazione limitata: Le CNN tradizionali spesso utilizzano livelli completamente connessi alla fine per fornire un vettore di output di dimensioni fisse per la classificazione. Poiché non mantengono informazioni spaziali, non possono localizzare con precisione oggetti o regioni all’interno dell’immagine.

Reti completamente convoluzionali (FCN) come soluzione per la segmentazione semantica

Lavorando esclusivamente su livelli convoluzionali e mantenendo informazioni spaziali in tutto il network, le Reti completamente convoluzionali (FCN) affrontano i vincoli delle architetture classiche delle CNN nei compiti di segmentazione. Le FCN sono destinate a fare previsioni pixel per pixel, assegnando a ciascun pixel dell’immagine in input un’etichetta o una classe. Le FCN consentono la costruzione di una mappa di segmentazione densa con previsioni a livello di pixel mediante l’upsampling delle feature map. Le convoluzioni trasposte (anche note come deconvoluzioni o livelli di upsampling) vengono utilizzate per sostituire i livelli completamente connessi dopo la progettazione delle CNN. La risoluzione spaziale delle feature map aumenta grazie alle convoluzioni trasposte, consentendo loro di avere le stesse dimensioni dell’immagine in input.

Durante l’upsampling, le FCN generalmente utilizzano connessioni di skip, bypassando specifici livelli e collegando direttamente le feature map di livello inferiore con quelle di livello superiore. Queste relazioni di skip aiutano a preservare dettagli fini e informazioni contestuali, aumentando la precisione di localizzazione delle regioni segmentate. Le FCN sono estremamente efficaci in varie applicazioni di segmentazione, compresa la segmentazione di immagini mediche, il parsing di scene e la segmentazione di istanze. Ora possono gestire immagini di input di varie dimensioni, fornire previsioni a livello di pixel e mantenere informazioni spaziali in tutto il network sfruttando le FCN per la segmentazione semantica.

Segmentazione dell’immagine

La segmentazione dell’immagine è un processo fondamentale nella computer vision in cui un’immagine viene divisa in molte parti o segmenti significativi e separati. A differenza della classificazione delle immagini, che fornisce un’unica etichetta a un’intera immagine, la segmentazione aggiunge etichette a ciascun pixel o gruppo di pixel, suddividendo essenzialmente l’immagine in parti semanticamente significative. La segmentazione dell’immagine è importante perché consente una comprensione più dettagliata dei contenuti di un’immagine. Possiamo estrarre informazioni considerevoli sui confini degli oggetti, le forme, le dimensioni e le relazioni spaziali suddividendo un’immagine in più parti. Questa analisi dettagliata è fondamentale in vari compiti di computer vision, consentendo applicazioni migliorate e supportando interpretazioni di dati visivi di livello superiore.

Comprensione dell’architettura UNET

Tecnologie tradizionali di segmentazione dell’immagine, come l’annotazione manuale e la classificazione dei pixel, presentano vari svantaggi che le rendono inefficienti e difficili per lavori di segmentazione precisi ed efficaci. A causa di questi vincoli, sono state sviluppate soluzioni più avanzate, come l’architettura UNET. Vediamo quali sono i difetti dei metodi precedenti e perché UNET è stato creato per superare questi problemi.

  • Annotazione manuale: L’annotazione manuale comporta il tracciamento e il marcamento dei confini o delle regioni di interesse dell’immagine. Sebbene questo metodo produca risultati di segmentazione affidabili, è lento, richiede molto lavoro ed è suscettibile a errori umani. L’annotazione manuale non è scalabile per grandi dataset ed è difficile mantenere la coerenza e l’accordo tra gli annotatori, specialmente in compiti di segmentazione complessi.
  • Classificazione pixel per pixel: Un altro approccio comune è la classificazione pixel per pixel, in cui ogni pixel di un’immagine viene classificato indipendentemente, generalmente utilizzando algoritmi come alberi decisionali, support vector machines (SVM) o foreste casuali. La classificazione pixel per pixel, tuttavia, fatica a catturare il contesto globale e le dipendenze tra i pixel circostanti, causando problemi di sovra- o sotto-segmentazione. Non può considerare le relazioni spaziali e spesso non riesce a offrire confini precisi degli oggetti.

Superamento delle sfide

L’architettura UNET è stata sviluppata per affrontare questi limiti e superare le sfide affrontate dagli approcci tradizionali alla segmentazione dell’immagine. Ecco come UNET affronta questi problemi:

  • Apprendimento end-to-end: UNET adotta una tecnica di apprendimento end-to-end, il che significa che apprende a segmentare le immagini direttamente da coppie di input-output senza annotazione dell’utente. UNET può estrarre automaticamente caratteristiche chiave ed eseguire una segmentazione accurata addestrandosi su un ampio dataset etichettato, eliminando la necessità di annotazione manuale laboriosa.
  • Architettura completamente convoluzionale: UNET si basa su un’architettura completamente convoluzionale, il che significa che è interamente composta da livelli convoluzionali e non include alcun livello completamente connesso. Questa architettura consente a UNET di funzionare su immagini di input di qualsiasi dimensione, aumentando la sua flessibilità e adattabilità a vari compiti di segmentazione e variazioni di input.
  • Architettura a forma di U con connessioni di skip: L’architettura caratteristica della rete include un percorso di codifica (percorso di contrazione) e un percorso di decodifica (percorso di espansione), consentendo di raccogliere informazioni locali e contesto globale. Le connessioni di skip colmano il divario tra i percorsi di codifica e decodifica, mantenendo informazioni critiche dai livelli precedenti e consentendo una segmentazione più precisa.
  • Informazioni contestuali e localizzazione: UNET utilizza le connessioni di skip per aggregare mappe di feature multiscala da più livelli, consentendo alla rete di assorbire informazioni contestuali e catturare dettagli a diversi livelli di astrazione. Questa integrazione delle informazioni migliora la precisione di localizzazione, consentendo confini degli oggetti esatti e risultati di segmentazione accurati.
  • Aumento dei dati e regolarizzazione: UNET utilizza tecniche di aumento dei dati e di regolarizzazione per migliorare la resilienza e la capacità di generalizzazione durante l’addestramento. Per aumentare la diversità dei dati di addestramento, l’aumento dei dati comporta l’aggiunta di numerose trasformazioni alle immagini di addestramento, come rotazioni, capovolgimenti, ridimensionamenti e deformazioni. Tecniche di regolarizzazione come dropout e batch normalization prevengono l’overfitting e migliorano le prestazioni del modello su dati sconosciuti.

Panoramica dell’Architettura UNET

UNET è un’architettura di rete neurale completamente convoluzionale (FCN) sviluppata per applicazioni di segmentazione delle immagini. È stata proposta per la prima volta nel 2015 da Olaf Ronneberger, Philipp Fischer e Thomas Brox. UNET è spesso utilizzata per la sua precisione nella segmentazione delle immagini ed è diventata una scelta popolare in varie applicazioni di imaging medico. UNET combina un percorso di codifica, anche chiamato percorso di contrazione, con un percorso di decodifica chiamato percorso di espansione. L’architettura prende il nome dalla sua forma a U quando viene rappresentata in un diagramma. Grazie a questa architettura a forma di U, la rete può registrare sia caratteristiche locali che contesto globale, ottenendo risultati di segmentazione precisi.

Componenti Critici dell’Architettura UNET

  • Percorso di Contrazione (Percorso di Codifica): Il percorso di contrazione di UNET è composto da strati convoluzionali seguiti da operazioni di max pooling. Questo metodo cattura caratteristiche ad alta risoluzione e a basso livello riducendo gradualmente le dimensioni spaziali dell’immagine di input.
  • Percorso di Espansione (Percorso di Decodifica): Vengono utilizzate convoluzioni trasposte, note anche come convoluzioni inverse o strati di upsampling, per aumentare le dimensioni delle feature map dal percorso di codifica nel percorso di espansione di UNET. La risoluzione spaziale delle feature map viene aumentata durante la fase di upsampling, consentendo alla rete di ricostituire una mappa di segmentazione densa.
  • Connessioni di Salto: Le connessioni di salto vengono utilizzate in UNET per collegare gli strati corrispondenti dal percorso di codifica al percorso di decodifica. Questi collegamenti consentono alla rete di raccogliere sia dati locali che globali. La rete conserva informazioni spaziali essenziali e migliora la precisione della segmentazione integrando le feature map degli strati precedenti con quelle nel percorso di decodifica.
  • Concatenazione: La concatenazione viene comunemente utilizzata per implementare le connessioni di salto in UNET. Le feature map dal percorso di codifica vengono concatenate alle feature map di upsampling dal percorso di decodifica durante la procedura di upsampling. Questa concatenazione consente alla rete di incorporare informazioni multiscala per una segmentazione appropriata, sfruttando il contesto di alto livello e le caratteristiche di basso livello.
  • Strati Completamente Convoluzionali: UNET comprende strati convoluzionali senza strati completamente connessi. Questa architettura convoluzionale consente a UNET di gestire immagini di dimensioni illimitate preservando le informazioni spaziali in tutta la rete, rendendola flessibile e adattabile a varie attività di segmentazione.

Il percorso di codifica, o percorso di contrazione, è un componente essenziale dell’architettura UNET. È responsabile dell’estrazione di informazioni di alto livello dall’immagine di input riducendo gradualmente le dimensioni spaziali.

Strati Convoluzionali

Il processo di codifica inizia con un insieme di strati convoluzionali. Gli strati convoluzionali estraggono informazioni a diverse scale applicando un insieme di filtri apprendibili all’immagine di input. Questi filtri operano sul campo recettivo locale, consentendo alla rete di individuare modelli spaziali e caratteristiche minori. Con ogni strato convoluzionale, la profondità delle feature map aumenta, consentendo alla rete di apprendere rappresentazioni più complesse.

Funzione di Attivazione

Dopo ogni strato convoluzionale, viene applicata una funzione di attivazione come la Rectified Linear Unit (ReLU) elemento per elemento per indurre non linearità nella rete. La funzione di attivazione aiuta la rete a imparare correlazioni non lineari tra le immagini di input e le caratteristiche estratte.

Strati di Pooling

Gli strati di pooling vengono utilizzati dopo gli strati convoluzionali per ridurre la dimensionalità spaziale delle feature map. Le operazioni, come il max pooling, dividono le feature map in regioni non sovrapposte e conservano solo il valore massimo all’interno di ogni zona. Ciò riduce la risoluzione spaziale mediante il downsampling delle feature map, consentendo alla rete di catturare dati più astratti e di livello superiore.

Il compito del percorso di codifica è quello di catturare caratteristiche a varie scale e livelli di astrazione in modo gerarchico. Il processo di codifica si concentra sull’estrazione di contesto globale e informazioni di alto livello man mano che le dimensioni spaziali diminuiscono.

Connessioni di Salto

La presenza di connessioni di salto che collegano livelli appropriati dal percorso di codifica al percorso di decodifica è una delle caratteristiche distintive dell’architettura UNET. Questi collegamenti di salto sono fondamentali per mantenere dati chiave durante il processo di codifica.

Le feature map degli strati precedenti raccolgono dettagli locali e informazioni dettagliate durante il percorso di codifica. Queste feature map vengono concatenate alle feature map di upsampling nel percorso di decodifica utilizzando connessioni di salto. Ciò consente alla rete di incorporare dati multiscala, caratteristiche di basso livello e contesto di alto livello nel processo di segmentazione.

Conservando le informazioni spaziali degli strati precedenti, UNET può localizzare in modo affidabile gli oggetti e mantenere dettagli più fini nei risultati di segmentazione. Le connessioni di salto di UNET aiutano ad affrontare il problema della perdita di informazioni causata dal downsampling. I collegamenti di salto consentono un’integrazione delle informazioni locali e globali più eccellente, migliorando complessivamente le prestazioni di segmentazione.

Per riassumere, l’approccio di codifica UNET è fondamentale per catturare le caratteristiche di alto livello e ridurre le dimensioni spaziali dell’immagine di input. Il percorso di codifica estrae rappresentazioni astratte progressivamente tramite strati convoluzionali, funzioni di attivazione e strati di pooling. Introdurre collegamenti di salto consente di preservare informazioni spaziali critiche, facilitando risultati di segmentazione affidabili.

Percorso di decodifica in UNET

Un componente fondamentale dell’architettura UNET è il percorso di decodifica, noto anche come percorso di espansione. È responsabile per l’upsampling delle feature map del percorso di codifica e per la costruzione della maschera di segmentazione finale.

Strati di upsampling (convoluzioni trasposte)

Per aumentare la risoluzione spaziale delle feature map, il metodo di decodifica UNET include strati di upsampling, spesso realizzati utilizzando convoluzioni trasposte o deconvoluzioni. Le convoluzioni trasposte sono essenzialmente l’opposto delle convoluzioni regolari. Aumentano le dimensioni spaziali anziché diminuirle, consentendo l’upsampling. Costruendo un kernel sparso e applicandolo alla feature map di input, le convoluzioni trasposte imparano ad aumentare le feature map. Durante questo processo, la rete impara a riempire gli spazi tra le posizioni spaziali attuali, aumentando così la risoluzione delle feature map.

Concatenazione

Le feature map degli strati precedenti vengono concatenate con le feature map di upsampling durante la fase di decodifica. Questa concatenazione consente alla rete di aggregare informazioni multiscala per una segmentazione corretta, sfruttando il contesto di alto livello e le caratteristiche di basso livello. Oltre all’upsampling, il percorso di decodifica UNET include collegamenti di salto dai livelli comparabili del percorso di codifica.

La rete può recuperare e integrare caratteristiche dettagliate perse durante la codifica mediante la concatenazione delle feature map dai collegamenti di salto. Ciò consente una localizzazione e delineazione degli oggetti più precisa nella maschera di segmentazione.

Il processo di decodifica in UNET ricostruisce una mappa di segmentazione densa che si adatta alla risoluzione spaziale dell’immagine di input mediante l’upsampling progressivo delle feature map e l’inclusione di collegamenti di salto.

La funzione del percorso di decodifica è quella di recuperare le informazioni spaziali perse durante il percorso di codifica e rifinire i risultati di segmentazione. Combina dettagli di codifica a basso livello con contesto di alto livello ottenuto dagli strati di upsampling per fornire una maschera di segmentazione precisa e completa.

UNET può aumentare la risoluzione spaziale delle feature map utilizzando convoluzioni trasposte nel processo di decodifica, consentendo così di eseguire l’upsampling per adattarle alle dimensioni dell’immagine originale. Le convoluzioni trasposte aiutano la rete a generare una maschera di segmentazione densa e dettagliata imparando a riempire gli spazi e ad espandere le dimensioni spaziali.

In sintesi, il processo di decodifica in UNET ricostruisce la maschera di segmentazione aumentando la risoluzione spaziale delle feature map tramite strati di upsampling e collegamenti di salto. Le convoluzioni trasposte sono fondamentali in questa fase perché consentono alla rete di eseguire l’upsampling delle feature map e costruire una maschera di segmentazione dettagliata che corrisponde all’immagine di input originale.

Percorsi di contrazione ed espansione in UNET

L’architettura UNET segue una struttura “encoder-decoder”, in cui il percorso di contrazione rappresenta l’encoder e il percorso di espansione rappresenta il decoder. Questo design assomiglia all’incapsulamento delle informazioni in una forma compressa e successiva decodifica per ricostruire i dati originali.

Percorso di contrazione (Encoder)

L’encoder in UNET è il percorso di contrazione. Estrae il contesto e comprime l’immagine di input diminuendo gradualmente le dimensioni spaziali. Questo metodo include strati convoluzionali seguiti da procedure di pooling come il max pooling per ridurre le dimensioni delle feature map. Il percorso di contrazione è responsabile dell’ottenimento di caratteristiche di alto livello, apprendimento del contesto globale e riduzione della risoluzione spaziale. Si concentra sulla compressione e astrazione dell’immagine di input, catturando efficientemente informazioni rilevanti per la segmentazione.

Percorso di espansione (Decoder)

Il decoder in UNET è il percorso di espansione. Attraverso l’upsampling delle feature map del percorso di contrazione, recupera le informazioni spaziali e genera la mappa di segmentazione finale. Il percorso di espansione comprende strati di upsampling, spesso realizzati con convoluzioni trasposte o deconvoluzioni per aumentare la risoluzione spaziale delle feature map. Il percorso di espansione ricostruisce le dimensioni spaziali originali tramite collegamenti di salto integrando le feature map di upsampling con le mappe equivalenti del percorso di contrazione. Questo metodo consente alla rete di recuperare caratteristiche dettagliate e localizzare correttamente gli elementi.

Il design UNET cattura il contesto globale e i dettagli locali mescolando i percorsi di contrazione ed espansione. Il percorso di contrazione comprime l’immagine di input in una rappresentazione compatta, decisa per costruire una mappa di segmentazione dettagliata tramite il percorso di espansione. Il percorso di espansione riguarda la decodifica della rappresentazione compressa in una mappa di segmentazione densa e precisa. Ricostruisce le informazioni spaziali mancanti e rifinisce i risultati di segmentazione. Questa struttura encoder-decoder consente una segmentazione precisa utilizzando contesto di alto livello e informazioni spaziali dettagliate.

In sintesi, le vie contrattive ed espansive di UNET assomigliano ad una struttura “encoder-decoder”. Il percorso espansivo è il decoder, recupera le informazioni spaziali e genera la mappa finale di segmentazione. Al contrario, il percorso contrattivo funge da encoder, catturando il contesto e comprimendo l’immagine di input. Questa architettura consente a UNET di codificare e decodificare le informazioni in modo efficace, consentendo una segmentazione accurata e dettagliata dell’immagine.

Connessioni di Salto in UNET

Le connessioni di salto sono essenziali per il design di UNET perché consentono alle informazioni di viaggiare tra i percorsi contrattivi (encoding) ed espansivi (decoding). Sono fondamentali per mantenere le informazioni spaziali e migliorare l’accuratezza della segmentazione.

Preservazione delle Informazioni Spaziali

Alcune informazioni spaziali possono andare perse durante il percorso di encoding poiché le mappe delle caratteristiche subiscono procedure di downsampling come il max pooling. Questa perdita di informazioni può portare a una minore accuratezza di localizzazione e una perdita di dettagli fini nella maschera di segmentazione.

Stabilendo connessioni dirette tra i livelli corrispondenti nei processi di encoding e decoding, le connessioni di salto aiutano a risolvere questo problema. Le connessioni di salto proteggono le informazioni spaziali vitali che altrimenti verrebbero perse durante il downsampling. Queste connessioni consentono alle informazioni dal flusso di encoding di evitare il downsampling e di essere trasmesse direttamente al percorso di decoding.

Fusione delle Informazioni a Diverse Scale

Le connessioni di salto consentono la fusione di informazioni a diverse scale provenienti da molteplici livelli di rete. I livelli successivi del processo di encoding catturano il contesto ad alto livello e le informazioni semantiche, mentre i livelli precedenti catturano i dettagli locali e le informazioni fini. UNET può combinare con successo informazioni locali e globali collegando queste mappe delle caratteristiche dal percorso di encoding ai livelli corrispondenti nel percorso di decoding. Questa integrazione di informazioni a diverse scale migliora complessivamente l’accuratezza della segmentazione. La rete può utilizzare dati a basso livello dal percorso di encoding per perfezionare i risultati della segmentazione nel percorso di decoding, consentendo una localizzazione più precisa e una migliore delimitazione dei confini degli oggetti.

Combina Contesto ad Alto Livello e Dettagli a Basso Livello

Le connessioni di salto consentono al percorso di decoding di combinare contesto ad alto livello e dettagli a basso livello. Le mappe delle caratteristiche concatenate dalle connessioni di salto includono le mappe delle caratteristiche ri campionate dal percorso di decoding e le mappe delle caratteristiche del percorso di encoding.

Questa combinazione consente alla rete di sfruttare il contesto ad alto livello registrato nel percorso di decoding e le caratteristiche dettagliate catturate nel percorso di encoding. La rete può incorporare informazioni di diverse dimensioni, consentendo una segmentazione più precisa e dettagliata.

UNET può sfruttare informazioni a diverse scale, preservare dettagli spaziali e combinare contesto ad alto livello con dettagli a basso livello mediante l’aggiunta di connessioni di salto. Di conseguenza, l’accuratezza della segmentazione migliora, la localizzazione degli oggetti migliora e le informazioni dettagliate nella maschera di segmentazione vengono mantenute.

In conclusione, le connessioni di salto in UNET sono fondamentali per mantenere le informazioni spaziali, integrare informazioni a diverse scale e migliorare l’accuratezza della segmentazione. Forniscono un flusso di informazioni diretto lungo i percorsi di encoding e decoding, consentendo alla rete di raccogliere dettagli locali e globali, con risultati di segmentazione più precisi e dettagliati.

Funzione di Loss in UNET

È fondamentale selezionare una funzione di loss appropriata durante l’addestramento di UNET e l’ottimizzazione dei suoi parametri per compiti di segmentazione delle immagini. UNET utilizza frequentemente funzioni di loss friendly per la segmentazione come il coefficiente di Dice o la loss di cross-entropy.

Loss del Coefficiente di Dice

Il coefficiente di Dice è una statistica di similarità che calcola l’overlap tra le maschere di segmentazione anticipate e quelle effettive. La loss del coefficiente di Dice, o loss di Dice soft, viene calcolata sottraendo uno dal coefficiente di Dice. Quando le maschere anticipate e quelle di ground truth si allineano bene, la loss si riduce al minimo, con conseguente aumento del coefficiente di Dice.

La loss del coefficiente di Dice è particolarmente efficace per dataset sbilanciati in cui la classe di sfondo ha molti pixel. Penalizzando i falsi positivi e i falsi negativi, promuove la rete a dividere accuratamente le regioni sia del primo piano che dello sfondo.

Loss di Cross-Entropy

Utilizza la funzione di loss di cross-entropy per compiti di segmentazione delle immagini. Misura la dissimilarità tra le probabilità di classe previste e le etichette di ground truth. Tratta ogni pixel come un problema di classificazione indipendente nella segmentazione delle immagini, e la loss di cross-entropy viene calcolata pixel per pixel.

La loss di cross-entropy incoraggia la rete ad assegnare alte probabilità alle etichette di classe corrette per ogni pixel. Penalizza le deviazioni dalla ground truth, promuovendo risultati di segmentazione accurati. Questa funzione di loss è efficace quando le classi del primo piano e dello sfondo sono bilanciate o quando sono coinvolti più classi nel compito di segmentazione.

La scelta tra la loss del coefficiente di Dice e la loss di cross-entropy dipende dai requisiti specifici del compito di segmentazione e dalle caratteristiche del dataset. Entrambe le funzioni di loss hanno vantaggi e possono essere combinate o personalizzate in base alle esigenze specifiche.

1: Importazione delle librerie

import tensorflow as tf
import os
import numpy as np
from tqdm import tqdm
from skimage.io import imread, imshow
from skimage.transform import resize
import matplotlib.pyplot as plt
import random

2: Dimensioni dell’immagine – Impostazioni

IMG_WIDTH = 128
IMG_HEIGHT = 128
IMG_CHANNELS = 3

3: Impostazione della casualità

seed = 42
np.random.seed = seed

4: Importazione del dataset

# Dati scaricati da - https://www.kaggle.com/competitions/data-science-bowl-2018/data 
# Importazione dei dataset
TRAIN_PATH = 'stage1_train/'
TEST_PATH = 'stage1_test/'

5: Lettura di tutte le immagini presenti nella sottocartella

train_ids = next(os.walk(TRAIN_PATH))[1]
test_ids = next(os.walk(TEST_PATH))[1]

6: Allenamento

X_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)
Y_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)

7: Ridimensionamento delle immagini

print('Ridimensionamento delle immagini di allenamento e delle maschere')
for n, id_ in tqdm(enumerate(train_ids), total=len(train_ids)):   
    path = TRAIN_PATH + id_
    img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNELS]  
    img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)
    X_train[n] = img  # Riempimento di X_train vuoto con i valori di img
    mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)
    for mask_file in next(os.walk(path + '/masks/'))[2]:
        mask_ = imread(path + '/masks/' + mask_file)
        mask_ = np.expand_dims(resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant',  
                                      preserve_range=True), axis=-1)
        mask = np.maximum(mask, mask_)  
            
    Y_train[n] = mask   

8: Test delle immagini

# Immagini di test
X_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)
sizes_test = []
print('Ridimensionamento delle immagini di test') 
for n, id_ in tqdm(enumerate(test_ids), total=len(test_ids)):
    path = TEST_PATH + id_
    img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNELS]
    sizes_test.append([img.shape[0], img.shape[1]])
    img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)
    X_test[n] = img

print('Fatto!')

9: Controllo casuale delle immagini

image_x = random.randint(0, len(train_ids))
imshow(X_train[image_x])
plt.show()
imshow(np.squeeze(Y_train[image_x]))
plt.show()

10: Costruzione del modello

inputs = tf.keras.layers.Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))
s = tf.keras.layers.Lambda(lambda x: x / 255)(inputs)

11: Percorsi

# Percorso di contrazione
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(s)
c1 = tf.keras.layers.Dropout(0.1)(c1)
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu',
 kernel_initializer='he_normal', padding='same')(c1)
p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)

c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(p1)
c2 = tf.keras.layers.Dropout(0.1)(c2)
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(c2)
p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)
 
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(p2)
c3 = tf.keras.layers.Dropout(0.2)(c3)
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu',
 kernel_initializer='he_normal', padding='same')(c3)
p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3)
 
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(p3)
c4 = tf.keras.layers.Dropout(0.2)(c4)
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(c4)
p4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4)
 
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(p4)
c5 = tf.keras.layers.Dropout(0.3)(c5)
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', 
kernel_initializer='he_normal', padding='same')(c5)

12: Percorsi di espansione

u6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)
u6 = tf.keras.layers.concatenate([u6, c4])
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(u6)
c6 = tf.keras.layers.Dropout(0.2)(c6)
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(c6)
 
u7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)
u7 = tf.keras.layers.concatenate([u7, c3])
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(u7)
c7 = tf.keras.layers.Dropout(0.2)(c7)
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(c7)
 
u8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)
u8 = tf.keras.layers.concatenate([u8, c2])
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(u8)
c8 = tf.keras.layers.Dropout(0.1)(c8)
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(c8)
 
u9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)
u9 = tf.keras.layers.concatenate([u9, c1], axis=3)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(u9)
c9 = tf.keras.layers.Dropout(0.1)(c9)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', 
padding='same')(c9)

13: Uscite

uscite = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9)

14: Riepilogo

modello = tf.keras.Model(inputs=[inputs], outputs=[outputs])
modello.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
modello.summary()

15: Controllo del modello

checkpointer = tf.keras.callbacks.ModelCheckpoint('modello_per_nuclei.h5', 
verbose=1, save_best_only=True)

callbacks = [
        tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
        tf.keras.callbacks.TensorBoard(log_dir='registri')]

risultati = modello.fit(X_train, Y_train, validation_split=0.1, batch_size=16, epochs=25, 
callbacks=callbacks)

16: Ultima fase – Predizione

idx = random.randint(0, len(X_train))

preds_train = modello.predict(X_train[:int(X_train.shape[0]*0.9)], verbose=1)
preds_val = modello.predict(X_train[int(X_train.shape[0]*0.9):], verbose=1)
preds_test = modello.predict(X_test, verbose=1)


preds_train_t = (preds_train > 0.5).astype(np.uint8)
preds_val_t = (preds_val > 0.5).astype(np.uint8)
preds_test_t = (preds_test > 0.5).astype(np.uint8)

# Esegui un controllo di coerenza su alcuni campioni di addestramento casuali
ix = random.randint(0, len(preds_train_t))
imshow(X_train[ix])
plt.show()
imshow(np.squeeze(Y_train[ix]))
plt.show()
imshow(np.squeeze(preds_train_t[ix]))
plt.show()

# Esegui un controllo di coerenza su alcuni campioni di convalida casuali
ix = random.randint(0, len(preds_val_t))
imshow(X_train[int(X_train.shape[0]*0.9):][ix])
plt.show()
imshow(np.squeeze(Y_train[int(Y_train.shape[0]*0.9):][ix]))
plt.show()
imshow(np.squeeze(preds_val_t[ix]))
plt.show()

Conclusione

In questo post completo del blog, abbiamo trattato l’architettura UNET per la segmentazione delle immagini. Affrontando i vincoli delle metodologie precedenti, l’architettura UNET ha rivoluzionato la segmentazione delle immagini. Le sue rotte di encoding e decoding, le connessioni di salto e altre modifiche, come U-Net++, Attention U-Net e Dense U-Net, si sono dimostrate molto efficaci nel catturare il contesto, mantenere le informazioni spaziali e migliorare l’accuratezza della segmentazione. Il potenziale per una segmentazione accurata e automatica con UNET offre nuove vie per migliorare la computer vision e oltre. Incoraggiamo i lettori a saperne di più su UNET e a sperimentarne l’implementazione per massimizzarne l’utilità nei loro progetti di segmentazione delle immagini.

Punti chiave

1. La segmentazione delle immagini è essenziale nelle attività di computer vision, permettendo la divisione delle immagini in regioni o oggetti significativi.

2. Gli approcci tradizionali alla segmentazione delle immagini, come l’annotazione manuale e la classificazione pixel-wise, presentano limitazioni in termini di efficienza e accuratezza.

3. Sviluppare l’architettura UNET per affrontare queste limitazioni e ottenere risultati di segmentazione accurati.

4. È una rete neurale convoluzionale completa (FCN) che combina un percorso di encoding per catturare le caratteristiche di alto livello e un metodo di decoding per generare la maschera di segmentazione.

5. Le connessioni di salto in UNET preservano le informazioni spaziali, migliorano la propagazione delle caratteristiche e migliorano l’accuratezza della segmentazione.

6. Ha trovato applicazioni di successo nell’imaging medico, nell’analisi delle immagini satellitari e nel controllo di qualità industriale, raggiungendo notevoli traguardi e riconoscimenti in competizioni.

Domande frequenti

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e sono utilizzati a discrezione dell’autore.