Reti neurali convoluzionali, con Keras e TPU

1. Panoramica

In questo lab imparerai a combinare lo strato convoluzionale in un modello di rete neurale in grado di riconoscere i fiori. Questa volta, creerai il modello autonomamente da zero e utilizzerai la potenza della TPU per addestrarlo in pochi secondi e ripetere il suo design.

Questo lab include le spiegazioni teoriche necessarie sulle reti neurali convoluzionali ed è un buon punto di partenza per gli sviluppatori che vogliono imparare a utilizzare il deep learning.

Questo lab è la Parte 3 della serie "Keras on TPU". Puoi farlo nel seguente ordine oppure in modo indipendente.

ca8cc21f6838eccc.png

Obiettivi didattici

  • Per creare un classificatore di immagini convoluzionali utilizzando un modello Keras Sequential.
  • Per addestrare il modello Keras su TPU
  • Per ottimizzare il modello con una buona scelta di livelli con convoluzione.

Feedback

Se noti qualcosa di strano in questo lab, non esitare a comunicarcelo. I feedback possono essere inviati tramite i problemi di GitHub [link al feedback].

2. Guida rapida di Google Colaboratory

Questo lab utilizza Google Collaboratory e non richiede alcuna configurazione da parte tua. Colaboratory è una piattaforma di notebook online per scopi didattici. Offre formazione gratuita su CPU, GPU e TPU.

688858c21e3beff2.png

Puoi aprire questo blocco note di esempio ed eseguire un paio di celle per acquisire familiarità con Colaboratory.

c3df49e90e5a654f.png Welcome to Colab.ipynb

Seleziona un backend TPU

8832c6208c99687d.png

Nel menu Colab, seleziona Runtime > Cambia tipo di runtime, quindi seleziona TPU. In questo codelab utilizzerai una potente TPU (Tensor Processing Unit) supportata per l'addestramento con accelerazione hardware. La connessione al runtime avverrà automaticamente alla prima esecuzione oppure puoi utilizzare il pulsante "Connetti" nell'angolo in alto a destra.

Esecuzione di blocchi note

76d05caa8b4db6da.png

Esegui le celle una alla volta facendo clic su una cella e premendo Maiusc-Invio. Puoi anche eseguire l'intero blocco note con Runtime > Esegui tutto

Sommario

429f106990037ec4.png

Tutti i notebook hanno un sommario. Puoi aprirlo utilizzando la Freccia nera a sinistra.

Celle nascoste

edc3dba45d26f12a.png

Alcune celle mostreranno solo il titolo. Questa è una funzionalità del notebook specifica di Colab. Puoi fare doppio clic per visualizzare il codice al loro interno, ma in genere non è molto interessante. In genere supportano o le funzioni di visualizzazione. Devi comunque eseguire queste celle affinché le funzioni al loro interno vengano definite.

Autenticazione

cdd4b41413100543.png

Colab può accedere ai tuoi bucket Google Cloud Storage privati, a condizione che tu esegua l'autenticazione con un account autorizzato. Lo snippet di codice riportato sopra attiverà un processo di autenticazione.

3. [INFO] Che cosa sono le Tensor Processing Unit (TPU)?

In breve

f88cf6facfc70166.png

Il codice per l'addestramento di un modello su TPU in Keras (e il fallback su GPU o CPU se non è disponibile una TPU):

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

Oggi utilizzeremo le TPU per creare e ottimizzare un classificatore di fiori a velocità interattive (minuti per esecuzione di addestramento).

688858c21e3beff2.png

Perché le TPU?

Le GPU moderne sono organizzate in "core" programmabili, un'architettura molto flessibile che consente loro di gestire una serie di attività come il rendering 3D, il deep learning, le simulazioni fisiche e così via. Le TPU, invece, abbinano un processore vettoriale classico a un'unità di moltiplicazione della matrice dedicata ed eccellono in qualsiasi attività in cui dominano le moltiplicazioni matriciali di grandi dimensioni, come le reti neurali.

8eb3e718b8e2ed08.png

Illustrazione: un livello di rete neurale denso come moltiplicazione di matrici, con un batch di otto immagini elaborate contemporaneamente dalla rete neurale. Esegui la moltiplicazione di una riga per una colonna per verificare che venga effettivamente eseguita una somma ponderata di tutti i valori dei pixel di un'immagine. Anche gli strati convoluzionali possono essere rappresentati come moltiplicazioni matriciali, anche se è un po' più complicato ( spiegazione qui, nella sezione 1).

L'hardware

MXU e VPU

Un core TPU v2 è costituito da un'unità Matrix Multiply (MXU) che esegue moltiplicazioni matriciali e da una Vector Processing Unit (VPU) per tutte le altre attività, come attivazioni, softmax, ecc. La VPU gestisce i calcoli in float32 e int32. MXU, invece, funziona in un formato in virgola mobile a 16-32 bit a precisione mista.

7d68944718f76b18.png

Virgola mobile con precisione mista e bfloat16

L'unità MXU calcola le moltiplicazioni di matrici utilizzando input bfloat16 e output float32. Le accumulazioni intermedie vengono eseguite con precisione float32.

19c5fc432840c714.png

L'addestramento delle reti neurali è in genere resistente al rumore introdotto da una precisione a virgola mobile ridotta. In alcuni casi, il rumore aiuta addirittura l'ottimizzatore a convergere. La precisione in virgola mobile a 16 bit è stata tradizionalmente utilizzata per accelerare i calcoli, ma i formati float16 e float32 hanno intervalli molto diversi. La riduzione della precisione da float32 a float16 di solito comporta overflow e underflow. Esistono delle soluzioni, ma in genere è necessario un lavoro aggiuntivo per far funzionare float16.

Ecco perché Google ha introdotto il formato bfloat16 nelle TPU. bfloat16 è un valore float32 troncato con esattamente gli stessi bit dell'esponente e lo stesso intervallo di float32. Questo, insieme al fatto che le TPU calcolano le moltiplicazioni di matrici con precisione mista con input bfloat16, ma output float32, significa che, in genere, non sono necessarie modifiche al codice per trarre vantaggio dai miglioramenti delle prestazioni della precisione ridotta.

Array sistolico

L'MXU implementa moltiplicazioni matriciali nell'hardware utilizzando una cosiddetta architettura "array sistolica", in cui gli elementi dei dati fluiscono attraverso un array di unità di calcolo hardware. In medicina, "sistolica" si riferisce alle contrazioni cardiache e al flusso sanguigno, qui al flusso di dati.

L'elemento base di una moltiplicazione matriciale è un prodotto scalare tra una linea di una matrice e una colonna dell'altra (vedi l'illustrazione nella parte superiore di questa sezione). Per una moltiplicazione matriciale Y=X*W, un elemento del risultato sarebbe:

Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]

Su una GPU, questo prodotto scalare viene programmato in un "core" della GPU e poi eseguito su tutti i "core" disponibili in parallelo per provare a calcolare contemporaneamente tutti i valori della matrice risultante. Se la matrice risultante è grande 128x128, ciò richiederebbe la disponibilità dei "core" 128x128=16K, cosa che in genere non è possibile. Le GPU più grandi hanno circa 4000 core. Una TPU, invece, utilizza il minimo indispensabile di hardware per le unità di calcolo nella MXU: solo bfloat16 x bfloat16 => float32 moltiplicatori, nient'altro. Sono così piccole che una TPU può implementarne 16.000 in un'unità MXU 128 x 128 ed elaborare questa moltiplicazione di matrici in un'unica operazione.

f1b283fc45966717.gif

Illustrazione: l'array sistolica MXU. Gli elementi di calcolo sono accumulatori multipli. I valori di una matrice vengono caricati nell'array (punti rossi). I valori dell'altra matrice fluiscono attraverso l'array (punti grigi). Le linee verticali propagano i valori verso l'alto. Le linee orizzontali propagano le somme parziali. Viene lasciato come esercizio all'utente per verificare che man mano che i dati fluiscono attraverso l'array, si ottiene il risultato della moltiplicazione matriciale che esce dal lato destro.

Inoltre, mentre i prodotti scalari vengono calcolati in un'unità di calcolo MXU, le somme intermedie passano semplicemente da un'unità di calcolo adiacente all'altra. Non è necessario archiviarli e recuperarli nella/dalla memoria o persino in un file di registro. Il risultato finale è che l'architettura dell'array di sistolica TPU ha un significativo vantaggio in termini di densità e potenza, oltre a un vantaggio in termini di velocità non trascurabile rispetto a una GPU, quando si calcolano le moltiplicazioni delle matrici.

Cloud TPU

Quando richiedi una "Cloud TPU v2" su Google Cloud Platform, ottieni una macchina virtuale (VM) con una scheda TPU collegata al PCI. La scheda TPU ha quattro chip TPU dual-core. Ogni core TPU è dotato di una VPU (Unità di elaborazione vettoriale) e di un'unità di moltiplicazione a matrice (MXU) 128 x 128. In genere, questa "Cloud TPU" viene connessa tramite la rete alla VM che l'ha richiesta. Il quadro completo sarà quindi simile a questo:

dfce5522ed644ece.png

Illustrazione: la tua VM con un acceleratore "Cloud TPU" collegato alla rete. La "Cloud TPU" stessa è composta da una VM con una scheda TPU collegata su PCI con quattro chip TPU dual-core.

Pod TPU

Nei data center di Google, le TPU sono collegate a un'interconnessione HPC (computing ad alte prestazioni) che può farle apparire come un unico acceleratore molto grande. Google li chiama pod e possono comprendere fino a 512 core TPU v2 o 2048 core TPU v3.

2ec1e0d341e7fc34.jpeg

Illustrazione: un pod TPU v3. Schede e rack TPU connessi tramite interconnessione HPC.

Durante l'addestramento, i gradienti vengono scambiati tra i core TPU utilizzando l'algoritmo all-reduce (qui puoi trovare una buona spiegazione di all-reduce). Il modello in fase di addestramento può sfruttare l'hardware eseguendo l'addestramento su batch di grandi dimensioni.

d97b9cc5d40fdb1d.gif

Illustrazione: sincronizzazione dei gradienti durante l'addestramento utilizzando l'algoritmo All-Reduce sulla rete HPC mesh toroidale 2-D di Google TPU.

Il software

Addestramento di grandi dimensioni del batch

La dimensione del batch ideale per le TPU è di 128 elementi di dati per core TPU, ma l'hardware può già mostrare un buon utilizzo da 8 elementi di dati per core TPU. Ricorda che una Cloud TPU ha 8 core.

In questo codelab utilizzeremo l'API Keras. In Keras, il batch specificato è la dimensione del batch globale per l'intera TPU. I batch verranno automaticamente suddivisi in 8 core e eseguiti su 8 core della TPU.

da534407825f01e3.png

Per ulteriori suggerimenti sul rendimento, consulta la guida al rendimento delle TPU. Per dimensioni dei batch molto grandi, potrebbe essere necessaria un'attenzione particolare in alcuni modelli. Per ulteriori dettagli, consulta LARSOptimizer.

Dietro le quinte: XLA

I programmi TensorFlow definiscono i grafici di calcolo. La TPU non esegue direttamente il codice Python, ma esegue il grafico di calcolo definito dal tuo programma TensorFlow. Un compilatore chiamato XLA (Accelerated Linear Algebra compiler) trasforma il grafico TensorFlow dei nodi di calcolo in codice macchina TPU. Questo compilatore esegue anche molte ottimizzazioni avanzate sul codice e sul layout della memoria. La compilazione avviene automaticamente quando il lavoro viene inviato alla TPU. Non è necessario includere in modo esplicito XLA nella catena di build.

edce61112cd57972.png

Illustrazione: per l'esecuzione su TPU, il grafico di calcolo definito dal programma Tensorflow viene prima tradotto in una rappresentazione XLA (Accelerated Linear Algebra compiler), quindi compilato da XLA nel codice macchina TPU.

Utilizzo delle TPU in Keras

Le TPU sono supportate tramite l'API Keras a partire da TensorFlow 2.1. Il supporto Keras funziona su TPU e pod di TPU. Ecco un esempio che funziona su TPU, GPU e CPU:

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

In questo snippet di codice:

  • TPUClusterResolver().connect() trova la TPU sulla rete. Funziona senza parametri sulla maggior parte dei sistemi Google Cloud (job AI Platform, Colaboratory, Kubeflow, VM di deep learning creati tramite l'utilità "ctpu up"). Questi sistemi sanno dove si trova la loro TPU grazie a una variabile di ambiente TPU_NAME. Se crei una TPU manualmente, imposta la variabile di ambiente TPU_NAME sulla VM da cui la utilizzi oppure chiama TPUClusterResolver con parametri espliciti: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy è la parte che implementa la distribuzione e l'algoritmo di sincronizzazione del gradiente "all-reduce".
  • La strategia viene applicata tramite un ambito. Il modello deve essere definito all'interno di scope().
  • La funzione tpu_model.fit prevede un oggetto tf.data.Dataset per l'input per l'addestramento delle TPU.

Attività comuni di porting su TPU

  • Sebbene esistano molti modi per caricare i dati in un modello TensorFlow, per le TPU è necessario l'utilizzo dell'API tf.data.Dataset.
  • Le TPU sono molto veloci e l'importazione dei dati spesso diventa un collo di bottiglia quando vengono eseguite. Nella guida al rendimento delle TPU sono disponibili strumenti che puoi utilizzare per rilevare i colli di bottiglia dei dati e altri suggerimenti per il rendimento.
  • I numeri int8 o int16 vengono trattati come int32. La TPU non ha un hardware intero che opera su meno di 32 bit.
  • Alcune operazioni di TensorFlow non sono supportate. L'elenco è disponibile qui. La buona notizia è che questo limite si applica solo al codice di addestramento, ovvero al passaggio in avanti e indietro attraverso il modello. Puoi comunque utilizzare tutte le operazioni TensorFlow nella pipeline di input dei dati perché verranno eseguite sulla CPU.
  • tf.py_func non è supportato su TPU.

4. [INFO] Introduzione ai classificatori di reti neurali

In sintesi

Se conosci già tutti i termini in grassetto del paragrafo successivo, puoi passare all'esercizio successivo. Se hai appena iniziato il deep learning, ti diamo il benvenuto e continua a leggere.

Per i modelli creati come sequenza di strati, Keras offre l'API Sequential. Ad esempio, un classificatore di immagini che utilizza tre strati densi può essere scritto in Keras come:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
    tf.keras.layers.Dense(500, activation="relu"),
    tf.keras.layers.Dense(50, activation="relu"),
    tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

688858c21e3beff2.png

Rete neurale densa

Questa è la rete neurale più semplice per la classificazione delle immagini. È costituito da "neuroni" disposti in strati. Il primo strato elabora i dati di input e alimenta gli output in altri strati. È chiamato "densa" perché ogni neurone è connesso a tutti i neuroni dello strato precedente.

c21bae6dade487bc.png

Puoi inserire un'immagine in una rete di questo tipo, suddividendo i valori RGB di tutti i suoi pixel in un vettore lungo e utilizzandolo come input. Non è la tecnica migliore per il riconoscimento delle immagini, ma verrà migliorata in seguito.

Neuroni, attivazioni, RELU

Un "neurone" calcola una somma ponderata di tutti i suoi input, aggiunge un valore chiamato "bias" e alimenta il risultato tramite una cosiddetta "funzione di attivazione". Inizialmente, le ponderazioni e il bias sono sconosciuti. Verranno inizializzate in modo casuale e "apprese" addestrando la rete neurale con molti dati noti.

644f4213a4ee70e5.png

La funzione di attivazione più diffusa è denominata RELU per l'unità lineare rettificata. Si tratta di una funzione molto semplice, come puoi vedere nel grafico in alto.

Attivazione di softmax

La rete precedente termina con uno strato a 5 neuroni perché classifichiamo i fiori in 5 categorie (rosa, tulipano, dente di leone, margherita, girasole). I neuroni negli strati intermedi vengono attivati utilizzando la classica funzione di attivazione RELU. Nell'ultimo livello, però, vogliamo calcolare numeri compresi tra 0 e 1 che rappresentano la probabilità che questo fiore sia una rosa, un tulipano e così via. A questo scopo, utilizzeremo una funzione di attivazione chiamata "softmax".

L'applicazione della funzione softmax su un vettore viene eseguita prendendo l'esponenziale di ciascun elemento e quindi normalizzando il vettore, tipicamente usando la norma L1 (somma dei valori assoluti) in modo che i valori sommano 1 e possano essere interpretati come probabilità.

ef0d98c0952c262d.png d51252f75894479e.gif

Perdita di entropia incrociata

Ora che la nostra rete neurale produce previsioni dalle immagini di input, dobbiamo misurarne la qualità, ovvero la distanza tra ciò che ci dice la rete e le risposte corrette, spesso chiamate "etichette". Ricorda che abbiamo le etichette corrette per tutte le immagini nel set di dati.

Qualsiasi distanza andrebbe bene, ma per i problemi di classificazione la cosiddetta "distanza di entropia incrociata" è la più efficace. Chiameremo questa funzione di errore o "loss":

7bdf8753d20617fb.png

Discesa del gradiente

"Addestrare" la rete neurale significa in realtà utilizzare immagini ed etichette di addestramento per regolare ponderazioni e bias in modo da minimizzare la funzione di perdita di entropia incrociata. Ecco come funziona.

L'entropia incrociata è una funzione di pesi, bias e pixel dell'immagine di addestramento e della relativa classe nota.

Se calcoliamo le derivate parziali dell'entropia di crociera rispetto a tutti i pesi e a tutti i bias, otteniamo un "gradiente", calcolato per una determinata immagine, etichetta e valore attuale di pesi e bias. Ricorda che possiamo avere milioni di ponderazioni e bias, quindi il calcolo del gradiente richiede molto lavoro. Fortunatamente, Tensorflow lo fa al posto nostro. La proprietà matematica di un gradiente è che punta verso l'alto. Poiché vogliamo andare dove l'entropia incrociata è bassa, andiamo nella direzione opposta. Aggiorniamo le ponderazioni e i bias di una frazione del gradiente. Poi ripetiamo la stessa cosa più volte utilizzando i batch successivi di immagini e etichette di addestramento, in un ciclo di addestramento. Si spera che questo converga a un punto in cui l'entropia di crociera è minima, anche se nulla garantisce che questo minimo sia univoco.

discesa gradiente2.png

Mini-batching e momentum

Puoi calcolare il gradiente solo su un'immagine di esempio e aggiornare immediatamente le ponderazioni e i bias, ma farlo per un batch di immagini con 128 formati, ad esempio, fornisce un gradiente che rappresenta meglio i vincoli imposti da diverse immagini di esempio e ha quindi la probabilità di convergere verso la soluzione più velocemente. La dimensione del mini-batch è un parametro regolabile.

Questa tecnica, a volte chiamata "discesa del gradiente stocastico", ha un altro vantaggio più pragmatico: lavorare con i batch significa anche lavorare con matrici più grandi, che in genere sono più facili da ottimizzare su GPU e TPU.

La convergenza può essere comunque un po' caotica e può persino arrestarsi se il vettore del gradiente è costituito da tutti gli zeri. Significa che abbiamo trovato il minimo? Non sempre. Un componente di gradiente può essere pari a zero per un valore minimo o massimo. Con un vettore gradiente con milioni di elementi, se sono tutti zeri, la probabilità che ogni zero corrisponda a un minimo e nessuno di questi a un punto massimo è piuttosto piccola. In uno spazio di molte dimensioni, i punti di inserimento sono abbastanza comuni e non vogliamo fermarli.

52e824fe4716c4a0.png

Illustrazione: un punto di attacco. La pendenza è 0, ma non è un minimo in tutte le direzioni. (Attribuzione dell'immagine Wikimedia: By Nicoguaro - Own work, CC BY 3.0)

La soluzione è aggiungere slancio all'algoritmo di ottimizzazione, in modo che possa superare i punti di sella senza fermarsi.

Glossario

batch o mini-batch: l'addestramento viene sempre eseguito su batch di dati ed etichette di addestramento. In questo modo favorirai la convergenza dell'algoritmo. La dimensione "batch" è in genere la prima dimensione dei tensori di dati. Ad esempio, un tensore di forma [100, 192, 192, 3] contiene 100 immagini di 192 x 192 pixel con tre valori per pixel (RGB).

Perdita di entropia incrociata: una funzione di perdita speciale spesso utilizzata nei classificatori.

strato denso: uno strato di neuroni in cui ogni neurone è collegato a tutti i neuroni dello strato precedente.

features: gli input di una rete neurale vengono a volte chiamati "features". L'arte di capire quali parti di un set di dati (o combinazioni di parti) alimentare in una rete neurale per ottenere buone previsioni è chiamata "feature engineering".

labels: un altro nome per "classi" o risposte corrette in un problema di classificazione supervisionata

tasso di apprendimento: frazione del gradiente con cui i pesi e i bias vengono aggiornati a ogni iterazione del ciclo di addestramento.

Logit: gli output di uno strato di neuroni prima dell'applicazione della funzione di attivazione sono chiamati "logit". Il termine deriva dalla "funzione logistica", anche nota come "funzione sigmoidea", che in precedenza era la funzione di attivazione più diffusa. "Output dei neuroni prima della funzione logistica" è stato abbreviato in "logit".

loss: la funzione di errore che confronta gli output della rete neurale con le risposte corrette

neuron: calcola la somma ponderata dei suoi input, aggiunge un bias e fornisce il risultato attraverso una funzione di attivazione.

Codifica one-hot: la classe 3 su 5 è codificata come un vettore di 5 elementi, tutti zeri tranne il terzo che è 1.

relu: unità lineare rettificata. Una funzione di attivazione molto diffusa per i neuroni.

sigmoid: un'altra funzione di attivazione molto diffusa che è ancora utile in casi speciali.

softmax: una funzione di attivazione speciale che agisce su un vettore, aumenta la differenza tra il componente più grande e tutti gli altri e normalizza il vettore in modo che abbia una somma pari a 1 in modo che possa essere interpretato come un vettore di probabilità. Utilizzato come ultimo passaggio nelle categorie di classificazione.

tensore: un "tensore" è simile a una matrice, ma con un numero arbitrario di dimensioni. Un tensore 1D è un vettore. Un tensore di 2 dimensioni è una matrice. Poi si possono avere tensori con 3, 4, 5 o più dimensioni.

5. [NUOVE INFORMAZIONI] Reti neurali convoluzionali

In breve

Se conosci già tutti i termini in grassetto nel paragrafo successivo, puoi passare all'esercizio successivo. Se hai appena iniziato con le reti neurali convoluzionali, continua a leggere.

convolutional.gif

Illustrazione: applicazione di due filtri successivi composti ciascuno da 4x4x3=48 pesi apprendibili.

Ecco come appare una semplice rete neurale convoluzionale in Keras:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

688858c21e3beff2.png

Nozioni di base sulle reti neurali convoluzionali

In un livello di una rete convoluzionale, un "neurone" esegue una somma ponderata dei pixel appena sopra, in una piccola regione solo dell'immagine. Aggiunge un bias e alimenta la somma tramite una funzione di attivazione, proprio come farebbe un neurone in un normale livello denso. Questa operazione viene poi ripetuta in tutta l'immagine utilizzando gli stessi pesi. Ricorda che negli strati densi ogni neurone aveva i propri pesi. In questo caso, una singola "patch" di pesi scorre sull'immagine in entrambe le direzioni (una "convoluzione"). L'output ha tanti valori quanti sono i pixel dell'immagine (è necessario un po' di spaziatura ai bordi). Si tratta di un'operazione di filtro che utilizza un filtro di 4 x 4 x 3=48 pesi.

Tuttavia, 48 pesi non saranno sufficienti. Per aggiungere più gradi di libertà, ripetiamo la stessa operazione con un nuovo insieme di pesi. Viene prodotta una nuova serie di output del filtro. Chiamiamolo un "canale" di output per analogia con i canali R, G, B nell'immagine di input.

Screenshot 2016-07-29 at 16.02.37.png

I due (o più) insiemi di pesi possono essere sommati come un unico tensore aggiungendo una nuova dimensione. Questo ci fornisce la forma generica del tensore dei pesi per un livello di convoluzione. Poiché il numero di canali di input e di output sono parametri, possiamo iniziare a sovrapporre e concatenare gli strati convoluzionali.

d1b557707bcd1cb9.png

Illustrazione: una rete neurale convoluzionale trasforma dei "cubi" di dati in altri "cubi" di dati.

Convoluzioni con stride, pooling massimo

Eseguendo le convoluzioni con passo 2 o 3, è possibile anche ridurre il cubo di dati risultante nelle sue dimensioni orizzontali. Esistono due modi comuni per farlo:

  • Convoluzione con passo: un filtro scorrevole come sopra, ma con un passo > 1
  • Pooling massimo: finestra scorrevole applicando l'operazione MAX (tipicamente su patch 2x2, ripetute ogni 2 pixel)

2b2d4263bb8470b.gif

Illustrazione: lo scorrimento della finestra di calcolo di 3 pixel comporta un numero inferiore di valori di output. Le convoluzioni striate o il massimo pooling (max su una finestra 2x2 che scorre di un passo di 2) sono un modo per ridurre il cubo di dati nelle dimensioni orizzontali.

Cclassificatore onvoluzionale

Infine, aggiungiamo una testa di classificazione appiattindo l'ultimo cubo di dati e alimentandolo attraverso uno strato denso attivato dalla tecnologia softmax. Un tipico classificatore convoluzionale può avere il seguente aspetto:

4a61aaffb6cba3d1.png

Illustrazione: un classificatore di immagini che utilizza strati convoluzionali e softmax. Utilizza filtri 3x3 e 1x1. I livelli maxpool prendono il numero massimo di gruppi di punti dati 2x2. La testa di classificazione è implementata con un livello denso con attivazione softmax.

In Keras

Lo stack convoluzionale illustrato sopra può essere scritto in Keras in questo modo:

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)    
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

6. La tua rete convoluzionale personalizzata

Attività pratica

Creiamo e addestriamo una rete neurale convoluzionale da zero. L'utilizzo di una TPU ci consentirà di eseguire l'iterazione molto rapidamente. Apri il blocco note seguente, esegui le celle (Maiusc-Invio) e segui le istruzioni ovunque vedi l'etichetta "LAVORO OBBLIGATORIO".

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

L'obiettivo è superare l'accuratezza del 75% del modello di trasferimento dell'apprendimento. Questo modello aveva un vantaggio, in quanto era stato preaddestrato su un set di dati di milioni di immagini, mentre qui abbiamo solo 3670 immagini. Riesci almeno a abbinarlo?

Ulteriori informazioni

Quanti livelli, quanto è grande?

La scelta delle dimensioni dei livelli è più un'arte che una scienza. Devi trovare il giusto equilibrio tra avere pochi e troppi parametri (pesi e bias). Con un numero troppo ridotto di pesi, la rete neurale non può rappresentare la complessità delle forme dei fiori. Se sono troppe, il modello può essere soggetto a "overfitting", ovvero specializzarsi nelle immagini di addestramento e non essere in grado di generalizzare. Con molti parametri, l'addestramento del modello sarà anche lento. In Keras, la funzione model.summary() visualizza la struttura e il conteggio dei parametri del modello:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 192, 192, 16)      448       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 192, 192, 30)      4350      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 96, 96, 60)        16260     
_________________________________________________________________
 ... 
_________________________________________________________________
global_average_pooling2d (Gl (None, 130)               0         
_________________________________________________________________
dense (Dense)                (None, 90)                11790     
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 455       
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________

Ecco alcuni suggerimenti:

  • La presenza di più livelli rende efficaci le reti neurali "profonde". Per questo semplice problema di riconoscimento dei fiori, sono consigliati da 5 a 10 livelli.
  • Usa piccoli filtri. In genere, i filtri 3 x 3 sono adatti ovunque.
  • Puoi utilizzare anche i filtri 1 x 1, che sono economici. Non "filtrano" nulla, ma calcolano combinazioni lineari di canali. Alternali con filtri veri. Scopri di più sulle "convoluzioni 1x1" nella sezione successiva.
  • Per un problema di classificazione come questo, esegui spesso il sottocampionamento con strati di max-pooling (o convoluzioni con passo >1). Non ti interessa dove si trova il fiore, ma solo che sia una rosa o un dente di leone, quindi la perdita delle informazioni x e y non è importante e filtrare aree più piccole è più economico.
  • Il numero di filtri di solito diventa simile al numero di classi alla fine della rete (perché? vedi il trucco "pooling medio globale" di seguito). Se esegui la classificazione in centinaia di classi, aumenta progressivamente il numero di filtri nei livelli consecutivi. Per il set di dati di fiori con 5 classi, non sarebbe sufficiente applicare solo 5 filtri. Puoi utilizzare lo stesso numero di filtri nella maggior parte dei livelli, ad esempio 32, e diminuirlo verso la fine.
  • Gli strati densi finali sono costosi. Possono avere più pesi di tutti gli strati convoluzionali combinati. Ad esempio, anche con un output molto ragionevole dall'ultimo cubo di dati di 24x24x10 punti dati, uno strato denso di 100 neuroni costerebbe 24x24x10x100=576.000 pesi. Prova a fare attenzione o prova il pooling medio globale (vedi sotto).

Pooling medio globale

Invece di utilizzare un costoso livello denso alla fine di una rete neurale convoluzionale, puoi suddividere il "cubo" di dati in arrivo in altrettante parti quante sono le classi, calcolare la media dei relativi valori e inserirli in una funzione di attivazione softmax. Questo modo di creare l'attributo principale di classificazione non ha costi. In Keras, la sintassi è tf.keras.layers.GlobalAveragePooling2D().

93240029f59df7c2.png

Soluzione

Ecco il blocco note della soluzione. Se non riesci a procedere, puoi utilizzarla.

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

Argomenti trattati

  • 🤔 Giocato con livelli convoluzionali
  • 🤓 Hai sperimentato il massimo pooling, i passi, la media globale del pooling...
  • 😀 Eseguito l'iterazione su un modello reale in modo rapido su TPU

Dedica qualche istante a leggere questo elenco di controllo.

7. Complimenti!

Hai creato la tua prima rete neurale convoluzionale moderna e l'hai addestrata con un'accuratezza superiore all'80%, eseguendo l'iterazione della sua architettura in pochi minuti grazie alle TPU. Vai al lab successivo per scoprire di più sulle architetture convoluzionali moderne:

TPU nella pratica

Le TPU e le GPU sono disponibili sulla Cloud AI Platform:

Infine, ci piace ricevere feedback. Facci sapere se noti qualcosa di strano in questo lab o se ritieni che debba essere migliorato. Il feedback può essere fornito tramite i problemi di GitHub [link per il feedback].

HR.png

Martin Görner ID small.jpg
L'autore: Martin Görner
Twitter: @martin_gorner

tensorflow logo.jpg
www.tensorflow.org