1. Panoramica
Questo tutorial è stato aggiornato per Tensorflow 2.2.
In questo codelab imparerai a creare e addestrare una rete neurale che riconosce le cifre scritte a mano. Lungo il percorso, man mano che migliori la tua rete neurale per ottenere un'accuratezza del 99%, scoprirai anche gli strumenti professionali che i professionisti del deep learning utilizzano per addestrare i propri modelli in modo efficiente.
Questo codelab utilizza il set di dati MNIST, una raccolta di 60.000 cifre etichettate che ha mantenuto occupate generazioni di dottori di ricerca per quasi due decenni. Per risolvere il problema devi utilizzare meno di 100 righe di codice Python / TensorFlow.
Obiettivi didattici
- Che cos'è una rete neurale e come addestrarla
- Come creare una rete neurale a 1 livello di base utilizzando tf.keras
- Come aggiungere altri livelli
- Come impostare una pianificazione dei tassi di apprendimento
- Come creare reti neurali convoluzionali
- Come utilizzare le tecniche di regolarizzazione: abbandono, normalizzazione dei batch
- Che cos'è l'overfitting
Che cosa ti serve
Solo un browser. Questo workshop può essere eseguito interamente con Google Colaboratory.
Feedback
Facci sapere se noti qualcosa che non va in questo lab o se pensi che dovrebbe essere migliorato. Gestiamo i feedback tramite i problemi di GitHub [link per il feedback].
2. Guida rapida di Google Colaboratory
Questo lab utilizza Google Colaboratory e non richiede alcuna configurazione da parte tua. Puoi eseguirlo da un Chromebook. Apri il file di seguito ed esegui le celle per acquisire familiarità con i blocchi note di Colab.
Di seguito sono riportate ulteriori istruzioni:
Seleziona un backend GPU
Nel menu Colab, seleziona Runtime > Modifica il tipo di runtime, quindi seleziona GPU. La connessione al runtime avverrà automaticamente alla prima esecuzione oppure puoi utilizzare il pulsante nell'angolo in alto a destra.
Esecuzione di blocchi note
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
Tutti i blocchi note hanno un sommario. Puoi aprirlo utilizzando la freccia nera a sinistra.
Celle nascoste
Alcune celle mostreranno solo il titolo. Si tratta di una funzionalità del blocco note specifica per Colab. Puoi fare doppio clic sopra per vedere il codice al loro interno, ma di solito non è molto interessante. In genere supportano o le funzioni di visualizzazione. Devi comunque eseguire queste celle per definire le funzioni all'interno.
3. Addestramento di una rete neurale
Prima osserveremo l'addestramento di una rete neurale. Apri il blocco note in basso ed esamina tutte le celle. Non fare ancora attenzione al codice, perché inizieremo a spiegarlo più tardi.
Quando esegui il blocco note, concentrati sulle visualizzazioni. Vedi di seguito per le spiegazioni.
Dati di addestramento
Abbiamo un set di dati di cifre scritte a mano libera che sono state etichettate in modo da sapere cosa rappresenta ciascuna immagine, ad esempio un numero compreso tra 0 e 9. Nel blocco note, vedrai un estratto:
La rete neurale che creeremo classifica le cifre scritte a mano libera nelle loro 10 classi (0, .., 9). Lo fa sulla base di parametri interni che devono avere un valore corretto affinché la classificazione funzioni correttamente. Questo "valore corretto" viene appreso attraverso un processo di addestramento che richiede un "set di dati etichettati" con immagini e le risposte corrette associate.
Come faccio a sapere se la rete neurale addestrata funziona bene o meno? Utilizzare il set di dati di addestramento per testare la rete è una truffa. Lo ha già visto più volte durante l'addestramento e sicuramente le prestazioni migliori. Abbiamo bisogno di un altro set di dati etichettato, mai visto durante l'addestramento, per valutare il "mondo reale" le prestazioni della rete. È chiamato "set di dati di convalida"
Addestramento
Man mano che l'addestramento procede, un batch di dati di addestramento alla volta, i parametri interni del modello vengono aggiornati e il modello migliora sempre di più nel riconoscimento delle cifre scritte a mano. Puoi vederla nel grafico di addestramento:
A destra, la "precisione" è semplicemente la percentuale di cifre correttamente riconosciute. Sale con il progredire dell'addestramento, il che va bene.
A sinistra, possiamo vedere la "perdita". Per guidare l'addestramento, definiremo una "perdita" funzione, che rappresenta il grado di riconoscimento delle cifre da parte del sistema e cerca di ridurle al minimo. Qui si vede che la perdita cala sia sui dati di addestramento che su quelli di convalida man mano che l'addestramento progredisce. Tutto bene. Significa che la rete neurale è in fase di apprendimento.
L'asse X rappresenta il numero di "epoche" o iterazioni nell'intero set di dati.
Previsioni
Quando il modello viene addestrato, possiamo utilizzarlo per riconoscere le cifre scritte a mano. La visualizzazione successiva mostra il rendimento su poche cifre visualizzate dai caratteri locali (prima riga) e poi dalle 10.000 cifre del set di dati di convalida. La classe prevista viene visualizzata sotto ogni cifra, in rosso se era errata.
Come si può vedere, questo modello iniziale non è molto buono, ma riconosce comunque alcune cifre correttamente. La sua accuratezza di convalida finale è di circa il 90%, il che non è poi così male per il modello semplicistico con cui stiamo iniziando,ma significa comunque che manca 1000 cifre di convalida su 10.000. Questa quantità di dati che può essere visualizzata è molto maggiore, motivo per cui sembra che tutte le risposte siano sbagliate (in rosso).
Tensori
I dati vengono archiviati in matrici. Un'immagine in scala di grigi di 28 x 28 pixel si adatta a una matrice bidimensionale di 28 x 28 pixel. Tuttavia, per un'immagine a colori, sono necessarie più dimensioni. Ci sono 3 valori di colore per pixel (rosso, verde, blu), quindi sarà necessaria una tabella tridimensionale con le dimensioni [28, 28, 3]. Per archiviare un gruppo di 128 immagini a colori, è necessaria una tabella quadridimensionale con le dimensioni [128, 28, 28, 3].
Queste tabelle multidimensionali sono chiamate "tensori" e l'elenco delle loro dimensioni è la loro "forma".
4. [INFO]: nozioni di base sulle reti neurali
In breve
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=[28, 28, 1]),
tf.keras.layers.Dense(200, activation="relu"),
tf.keras.layers.Dense(60, activation="relu"),
tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 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, ... )
Un singolo strato denso
Le cifre scritte a mano nel set di dati MNIST sono immagini in scala di grigi di 28 x 28 pixel. L'approccio più semplice per classificarle consiste nell'utilizzare 28x28=784 pixel come input per una rete neurale a 1 strato.
Ogni "neurone" in una rete neurale genera una somma ponderata di tutti i suoi input e aggiunge una costante chiamata "bias" e invia il risultato tramite una "funzione di attivazione" non lineare. "Ponderazioni" e "bias" sono parametri che verranno determinati attraverso l'addestramento. Inizialmente vengono inizializzati con valori casuali.
L'immagine qui sopra rappresenta una rete neurale a 1 strato con 10 neuroni di output, poiché vogliamo classificare le cifre in 10 classi (da 0 a 9).
Con una moltiplicazione matriciale
Ecco come uno strato della rete neurale, che elabora una raccolta di immagini, può essere rappresentato da una moltiplicazione matriciale:
Usando la prima colonna di pesi nella matrice dei pesi W, calcoliamo la somma ponderata di tutti i pixel della prima immagine. Questa somma corrisponde al primo neurone. Utilizzando la seconda colonna di pesi, facciamo lo stesso per il secondo neurone e così via fino al decimo neurone. Possiamo quindi ripetere l'operazione per le 99 immagini rimanenti. Se chiamiamo X la matrice contenente le nostre 100 immagini, tutte le somme ponderate per i nostri 10 neuroni, calcolate su 100 immagini, sono semplicemente X,W, una moltiplicazione matriciale.
Ora ogni neurone deve aggiungere il proprio bias (una costante). Poiché abbiamo 10 neuroni, abbiamo 10 costanti di bias. Chiameremo questo vettore di 10 valori (b. Deve essere aggiunto a ogni riga della matrice calcolata in precedenza. Utilizzo di un pizzico di magia chiamata "trasmissione" lo scriveremo con un semplice segno più.
Infine applichiamo una funzione di attivazione, ad esempio "softmax". (spiegata di seguito) e ottenere la formula che descrive una rete neurale a 1 strato, applicata a 100 immagini:
In Keras
Con le librerie di reti neurali di alto livello come Keras, non avremo bisogno di implementare questa formula. Tuttavia, è importante capire che un livello della rete neurale è solo un insieme di moltiplicazioni e aggiunte. In Keras, uno strato denso verrebbe scritto come:
tf.keras.layers.Dense(10, activation='softmax')
Approfondisci
È banale concatenare gli strati della rete neurale. Il primo livello calcola le somme ponderate dei pixel. Gli strati successivi calcolano le somme ponderate degli output degli strati precedenti.
L'unica differenza, a parte il numero di neuroni, sarà la scelta della funzione di attivazione.
Funzioni di attivazione: relu, softmax e sigmoidale
In genere, utilizzi la funzione "relu" di attivazione per tutti i livelli tranne l'ultimo. L'ultimo strato, in un classificatore, utilizza "softmax" dell'attivazione.
Di nuovo, un "neurone" calcola una somma ponderata di tutti gli input e aggiunge un valore chiamato "bias" e fornisce il risultato tramite la funzione di attivazione.
La funzione di attivazione più comune è "RELU" per l'unità lineare rettificata. Si tratta di una funzione molto semplice, come puoi vedere nel grafico in alto.
La funzione di attivazione tradizionale nelle reti neurali era la "sigmoid" ma la "relu" ha dimostrato di avere proprietà di convergenza migliori quasi ovunque e ora è preferito.
Attivazione di softmax per la classificazione
L'ultimo strato della nostra rete neurale ha 10 neuroni perché vogliamo classificare le cifre scritte a mano in 10 classi (0,..9). Dovrebbe produrre 10 numeri compresi tra 0 e 1 che rappresentano la probabilità che questa cifra sia 0, 1, 2 e così via. A questo scopo, nell'ultimo livello, utilizzeremo una funzione di attivazione chiamata "softmax".
L'applicazione della funzione softmax su un vettore si ottiene prendendo l'esponenziale di ciascun elemento e poi normalizzando il vettore, tipicamente dividendolo per la sua "L1" (ovvero la somma dei valori assoluti) in modo che i valori normalizzati sommano 1 e possano essere interpretati come probabilità.
L'output dell'ultimo strato, prima dell'attivazione, a volte è chiamato "logits". Se questo vettore è L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9], allora:
Perdita con entropia incrociata
Ora che la nostra rete neurale produce previsioni a partire dalle immagini di input, dobbiamo misurare la loro qualità, cioè 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 questo errore o "perdita" :
Discesa del gradiente
"Addestramento" la rete neurale prevede in realtà l'utilizzo di 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 delle ponderazioni, dei bias, dei pixel dell'immagine di addestramento e della sua classe nota.
Se calcoliamo le derivate parziali dell'entropia incrociata relativamente a tutte le ponderazioni e a tutti i bias, otteniamo un "gradiente", calcolato per una determinata immagine, etichetta e valore attuale di ponderazioni e bias. Ricorda che possiamo avere milioni di ponderazioni e bias, quindi il calcolo del gradiente richiede molto lavoro. Fortunatamente, TensorFlow lo fa per noi. 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. Facciamo quindi la stessa cosa ancora e ancora usando i batch successivi di immagini ed etichette di addestramento, in un loop di addestramento. Si spera che questo converge in un punto in cui l'entropia incrociata è minima, anche se nulla garantisce che questo minimo sia unico.
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 stocastica del gradiente" ha un altro vantaggio più pragmatico: lavorare con i batch significa anche lavorare con matrici più grandi, che di solito 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 gradiente può essere pari a zero al minimo o al 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.
Illustrazione: un punto di attacco. Il gradiente è 0 ma non è il 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. Il "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 con entropia incrociata: una funzione di perdita speciale spesso usata 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 sono a volte chiamati "caratteristiche". L'arte di capire quali parti di un set di dati (o combinazioni di parti) alimentare in una rete neurale per ottenere buone previsioni si chiama "feature engineering".
labels: un altro nome per "classi" o le risposte corrette in un problema di classificazione supervisionato
tasso di apprendimento: frazione del gradiente in base alla quale le ponderazioni e i bias vengono aggiornati a ogni iterazione del loop di addestramento.
logits: gli output di uno strato di neuroni prima dell'applicazione della funzione di attivazione sono chiamati "logit". Il termine deriva dalla "funzione logistica" anche noto come "funzione sigmoidea" che era la funzione di attivazione più diffusa. "Output del neurone prima della funzione logistica" è stato abbreviato in "logits".
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 popolare ma 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.
tensor: un "tensore" è simile a una matrice, ma con un numero arbitrario di dimensioni. Un tensore monodimensionale è un vettore. Un tensore bidimensionale è una matrice. Poi si possono avere tensori con 3, 4, 5 o più dimensioni.
5. Analizziamo il codice
Torniamo al blocco note di studio e questa volta leggiamo il codice.
Esaminiamo l'intera cella di questo blocco note.
"Parametri" della cella
Qui vengono definiti la dimensione del batch, il numero di epoche di addestramento e la posizione dei file di dati. I file di dati sono ospitati in un bucket Google Cloud Storage (GCS) ed è per questo che il loro indirizzo inizia con gs://
"Importazioni" della cella
Tutte le librerie Python necessarie vengono importate qui, tra cui TensorFlow e matplotlib per le visualizzazioni.
Cella "utility di visualizzazione [RUN ME]****"
Questa cella contiene un codice di visualizzazione non interessante. Per impostazione predefinita è compresso, ma puoi aprirlo e controllare il codice quando hai tempo facendoci doppio clic.
Cella "tf.data.Dataset: analizzare i file e preparare i set di dati di addestramento e convalida".
Questa cella ha utilizzato l'API tf.data.Dataset per caricare il set di dati MNIST per i file di dati. Non è necessario trascorrere troppo tempo su questa cella. Se ti interessa l'API tf.data.Dataset, ecco un tutorial che spiega: pipeline di dati a velocità TPU. Per ora, gli aspetti di base sono:
Le immagini e le etichette (risposte corrette) del set di dati MNIST vengono archiviate in record di lunghezza fissa in quattro file. I file possono essere caricati con la funzione di registrazione fissa dedicata:
imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)
Ora abbiamo un set di dati di byte immagine. Devono essere decodificati in immagini. Definiamo una funzione per farlo. L'immagine non è compressa, quindi la funzione non deve decodificare nulla (decode_raw
praticamente non fa nulla). L'immagine viene quindi convertita in valori con rappresentazione in virgola mobile compresi tra 0 e 1. Potremmo rimodellarla qui come un'immagine 2D, ma in realtà la manteniamo come un array piatto di pixel di dimensioni 28*28 perché questo è ciò che si aspetta il nostro strato denso iniziale.
def read_image(tf_bytestring):
image = tf.io.decode_raw(tf_bytestring, tf.uint8)
image = tf.cast(image, tf.float32)/256.0
image = tf.reshape(image, [28*28])
return image
Applichiamo questa funzione al set di dati utilizzando .map
e otteniamo un set di dati di immagini:
imagedataset = imagedataset.map(read_image, num_parallel_calls=16)
Eseguiamo lo stesso tipo di lettura e decodifica per le etichette e .zip
immagini ed etichette insieme:
dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))
Ora abbiamo un set di dati di coppie (immagine, etichetta). Questo è ciò che si aspetta il nostro modello. Non siamo ancora pronti a utilizzarlo nella funzione di addestramento:
dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)
L'API tf.data.Dataset dispone di tutte le funzioni di utilità necessarie per preparare i set di dati:
.cache
memorizza nella cache il set di dati nella RAM. Questo è un set di dati molto piccolo, quindi funzionerà. .shuffle
esegue lo shuffling con un buffer di 5000 elementi. È importante che i dati di addestramento vengano sottoposti a shuffling corretto. .repeat
esegue il loop del set di dati. Lo faremo più volte (più epoche). .batch
estrae più immagini ed etichette in un mini-batch. Infine, .prefetch
può utilizzare la CPU per preparare il batch successivo mentre quello attuale è in fase di addestramento sulla GPU.
Il set di dati di convalida viene preparato in modo simile. Ora siamo pronti a definire un modello e a utilizzare questo set di dati per addestrarlo.
Cella "Keras Model"
Tutti i nostri modelli saranno sequenze di strati dritti in modo da poter utilizzare lo stile tf.keras.Sequential
per crearli. Inizialmente, in questo caso è presente un singolo strato denso. Ha 10 neuroni perché le cifre scritte a mano libera vengono classificate in 10 classi. Utilizza il protocollo "softmax" perché è l'ultimo strato di un classificatore.
Un modello Keras deve anche conoscere la forma dei suoi input. È possibile utilizzare tf.keras.layers.Input
per definirla. Qui, i vettori di input sono vettori piatti di valori in pixel di lunghezza 28*28.
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='sgd',
loss='categorical_crossentropy',
metrics=['accuracy'])
# print model layers
model.summary()
# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)
La configurazione del modello viene eseguita in Keras utilizzando la funzione model.compile
. Qui utilizziamo l'ottimizzatore di base 'sgd'
(discesa stocastica del gradiente). Un modello di classificazione richiede una funzione di perdita di entropia incrociata, chiamata 'categorical_crossentropy'
in Keras. Infine, chiediamo al modello di calcolare la metrica 'accuracy'
, che è la percentuale di immagini classificate correttamente.
Keras offre l'utilissima utilità model.summary()
che stampa i dettagli del modello che hai creato. Il tuo insegnante ha aggiunto l'utilità PlotTraining
(definita nella cella "utilità di visualizzazione") che mostrerà varie curve di addestramento durante l'addestramento.
Cella "Addestra e convalida il modello"
È qui che avviene l'addestramento, chiamando model.fit
e passando sia il set di dati di addestramento che quello di convalida. Per impostazione predefinita, Keras esegue un ciclo di convalida alla fine di ogni epoca.
model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
validation_data=validation_dataset, validation_steps=1,
callbacks=[plot_training])
In Keras, è possibile aggiungere comportamenti personalizzati durante l'addestramento utilizzando i callback. È così che è stato implementato il diagramma di addestramento con aggiornamento dinamico per questo workshop.
Cella "Visualizza previsioni"
Una volta addestrato il modello, possiamo ottenere previsioni chiamando model.predict()
:
probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)
In questo caso abbiamo preparato una serie di cifre stampate con il rendering dei caratteri locali, come prova. Ricorda che la rete neurale restituisce un vettore di 10 probabilità dal suo "softmax" finale. Per ottenere l'etichetta, dobbiamo scoprire qual è la probabilità più alta. np.argmax
della libreria numpy lo fa.
Per capire perché è necessario il parametro axis=1
, ricorda che abbiamo elaborato un batch di 128 immagini e pertanto il modello restituisce 128 vettori di probabilità. La forma del tensore di output è [128, 10]. Stiamo calcolando il valore argmax in base alle 10 probabilità restituite per ogni immagine, quindi axis=1
(il primo asse è 0).
Questo semplice modello riconosce già il 90% delle cifre. Non male, ma ora migliorerai notevolmente.
6. Aggiungere livelli
Per migliorare l'accuratezza del riconoscimento, aggiungeremo altri strati alla rete neurale.
Manteniamo la funzione di attivazione softmax sull'ultimo strato perché è ciò che funziona meglio per la classificazione. Sui strati intermedi, tuttavia, utilizzeremo la funzione di attivazione più classica: la funzione sigmoidea:
Ad esempio, il tuo modello potrebbe avere il seguente aspetto (non dimenticare le virgole, tf.keras.Sequential
richiede un elenco di livelli separati da virgole):
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(200, activation='sigmoid'),
tf.keras.layers.Dense(60, activation='sigmoid'),
tf.keras.layers.Dense(10, activation='softmax')
])
Cerca il "riepilogo" del modello. Ora ha un numero di parametri 10 volte superiore. Dovrebbe essere 10 volte migliore! Ma, per qualche motivo, non è ...
Sembra che la perdita sia già caduta anche nel tetto. Si è verificato un problema.
7. Assistenza speciale per le reti profonde
Hai appena sperimentato le reti neurali, come le persone erano abituate a progettarle negli anni '80 e '90. Non sorprende che abbiano rinunciato all'idea, inaugurando il cosiddetto "inverno dell'IA". In effetti, man mano che aggiungi strati, le reti neurali hanno sempre più difficoltà a convergere.
Si è scoperto che le reti neurali profonde con molti strati (20, 50, anche 100 oggi) possono funzionare molto bene, fornendo un paio di trucchi matematici sporchi per farli convergere. La scoperta di questi semplici trucchi è uno dei motivi della rinascita del deep learning negli anni 2010.
Attivazione RELU
La funzione di attivazione sigmoidea è in realtà piuttosto problematica nelle reti profonde. Schiaccia tutti i valori compresi tra 0 e 1 e, se lo fai ripetutamente, gli output neuroni e i relativi gradienti possono scomparire completamente. È stato menzionato per motivi storici, ma le reti moderne utilizzano la funzione RELU (Rectified Linear Unit) che ha il seguente aspetto:
La relu d'altra parte ha una derivata di 1, almeno sul lato destro. Con l'attivazione RELU, anche se i gradienti provenienti da alcuni neuroni possono essere zero, ce ne saranno sempre altri che forniscono un gradiente diverso da zero e l'addestramento può continuare a un buon ritmo.
Un ottimizzatore migliore
In spazi ad altissima dimensionalità come qui, abbiamo nell'ordine di 10.000 ponderazioni e bias, i "punti di sella" sono frequenti. Questi sono punti che non sono minimi locali, ma in cui il gradiente è comunque zero e l'ottimizzatore della discesa del gradiente rimane bloccato lì. TensorFlow dispone di una gamma completa di ottimizzatori, alcuni dei quali funzionano con una certa inerzia e riescono a superare in sicurezza i punti di sella.
Inizializzazioni casuali
L'arte di inizializzare i bias dei pesi prima della formazione è un'area di ricerca in sé, con numerosi articoli pubblicati sull'argomento. Qui puoi trovare tutti gli inizializzatori disponibili in Keras. Fortunatamente, Keras fa la cosa giusta per impostazione predefinita e utilizza l'inizializzatore 'glorot_uniform'
, che è la migliore in quasi tutti i casi.
Non devi fare nulla, dato che Keras fa già la cosa giusta.
NaN ???
La formula di entropia incrociata coinvolge un logaritmo e il log(0) è Non un numero (NaN, un arresto numerico se preferisci). L'input dell'entropia incrociata può essere pari a 0? L'input proviene dalla funzione softmax, che è essenzialmente un valore esponenziale, mentre un esponenziale non è mai zero. Quindi siamo al sicuro!
Davvero? Nel meraviglioso mondo della matematica saremmo al sicuro, ma nel mondo dei computer l'exp(-150), rappresentato in formato float32, è pari a ZERO e l'entropia incrociata si blocca.
Fortunatamente, non devi fare nulla anche qui, poiché Keras si occupa di questo e calcola il softmax seguito dall'entropia incrociata in un modo particolarmente attento per garantire la stabilità numerica ed evitare i temuto NaN.
Operazione riuscita?
Ora la precisione dovrebbe essere del 97%. L'obiettivo di questo workshop è superare significativamente il 99%, quindi continuiamo.
Se sei bloccato, ecco la soluzione a questo punto:
8. Decadimento del tasso di apprendimento
Forse possiamo provare ad allenarci più velocemente? Il tasso di apprendimento predefinito nell'ottimizzatore Adam è 0,001. Proviamo ad aumentarlo.
Andare più velocemente non sembra aiutare molto. Che cos'è tutto questo rumore?
Le curve di addestramento sono molto rumoroso e osservano entrambe le curve di convalida: stanno saltando in alto e in basso. Ciò significa che stiamo andando troppo velocemente. Potremmo tornare alla velocità precedente, ma c'è un modo migliore.
La buona soluzione è iniziare velocemente e diminuire in modo esponenziale il tasso di apprendimento. In Keras, puoi eseguire questa operazione con il callback tf.keras.callbacks.LearningRateScheduler
.
Codice utile da copiare e incollare:
# lr decay function
def lr_decay(epoch):
return 0.01 * math.pow(0.6, epoch)
# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)
# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)
Non dimenticare di utilizzare il lr_decay_callback
che hai creato. Aggiungila all'elenco dei callback in model.fit
:
model.fit(..., callbacks=[plot_training, lr_decay_callback])
L'impatto di questa piccola modifica è spettacolare. Puoi notare che la maggior parte del rumore è sparita e che ora la precisione del test è superiore al 98% in modo significativo.
9. Interruzione, overfitting
Il modello sembra convergere bene ora. Proviamo ad andare ancora più a fondo.
Sono utili?
Non esattamente, l'accuratezza è ancora bloccata al 98% e vediamo la perdita di convalida. Sta salendo! L'algoritmo di apprendimento funziona solo sui dati di addestramento e ottimizza di conseguenza la perdita di addestramento. Non rileva mai i dati di convalida, quindi non sorprende che dopo un po' di tempo il suo lavoro non abbia più un effetto sulla perdita di convalida, che smette di perdere e a volte torna indietro.
Ciò non influisce immediatamente sulle capacità di riconoscimento nel mondo reale del tuo modello, ma ti impedirà di eseguire molte iterazioni ed è generalmente un segno che l'addestramento non ha più un effetto positivo.
Questa disconnessione solitamente si chiama "overfitting" e, quando è visibile, puoi provare ad applicare una tecnica di regolarizzazione chiamata "dropout". La tecnica di dropout spara neuroni casuali a ogni iterazione di addestramento.
Ha funzionato?
Riappare il rumore (non sorprende che, dato il funzionamento dell'abbandono). La perdita di convalida non sembra più insidiosi, ma nel complesso è più alta rispetto a quella senza abbandono. E la precisione della convalida è diminuita un po'. Questo è un risultato piuttosto deludente.
Sembra che l'abbandono non sia la soluzione corretta o forse "overfitting" è un concetto più complesso e alcune delle sue cause non sono suscettibili di un "abbandono" correggere?
Che cos'è l'"overfitting"? L'overfitting si verifica quando una rete neurale apprende "malamente", in un modo che funziona per gli esempi di addestramento, ma non così bene con i dati del mondo reale. Esistono tecniche di regolarizzazione come l'abbandono che possono costringerlo ad apprendere meglio, ma anche l'overfitting ha radici più profonde.
L'overfitting di base si verifica quando una rete neurale ha troppi gradi di libertà per il problema in questione. Immagina di avere così tanti neuroni che la rete può archiviare tutte le nostre immagini di addestramento e poi riconoscerle con la corrispondenza di pattern. che fallirebbe completamente sui dati del mondo reale. Una rete neurale deve essere in qualche modo vincolata in modo da essere costretta a generalizzare ciò che apprende durante l'addestramento.
Se i dati di addestramento sono pochi, anche una rete di piccole dimensioni può apprenderli a memoria e capita di verificare l'overfitting. In generale, per addestrare le reti neurali sono sempre necessari molti dati.
Infine, se hai già fatto tutto, sperimentato con diverse dimensioni di rete per assicurarti che i suoi gradi di libertà siano vincolati, applicato l'abbandono e addestrato su molti dati, potresti comunque essere bloccato a un livello di prestazioni che nulla sembra essere in grado di migliorare. Ciò significa che la tua rete neurale, nella sua forma attuale, non è in grado di estrarre maggiori informazioni dai tuoi dati, come nel nostro caso.
Ricordi come usiamo le nostre immagini, riunite in un unico vettore? Era davvero una pessima idea. Le cifre scritte a mano libera sono fatte di forme e le informazioni relative alla forma vengono eliminate quando abbiamo appiattito i pixel. Tuttavia, esiste un tipo di rete neurale che può sfruttare le informazioni sulla forma: le reti convoluzionali. Proviamoli.
Se sei bloccato, ecco la soluzione a questo punto:
10. [INFO] reti convoluzionali
In breve
Se conosci già tutti i termini in grassetto del paragrafo successivo, puoi passare all'esercizio successivo. Se hai appena iniziato con le reti neurali convoluzionali, continua a leggere.
Illustrazione: filtrare un'immagine con due filtri successivi composti da 4 x 4 x 3=48 pesi utilizzabili ciascuno.
Ecco come appare una semplice rete neurale convoluzionale in Keras:
model = tf.keras.Sequential([
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10, activation='softmax')
])
In uno strato di una rete convoluzionale, un "neurone" esegue una somma ponderata dei pixel appena sopra, in una piccola area solo dell'immagine. Aggiunge un bias e alimenta la somma attraverso una funzione di attivazione, proprio come farebbe un neurone in uno strato denso regolare. Questa operazione viene poi ripetuta in tutta l'immagine utilizzando gli stessi pesi. Ricorda che, in 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 (è però necessaria una spaziatura interna ai bordi). Si tratta di un'operazione di filtro. Nell'illustrazione qui sopra, utilizza un filtro di 4x4x3=48 pesi.
Tuttavia, 48 pesi non saranno sufficienti. Per aggiungere più gradi di libertà, ripetiamo la stessa operazione con un nuovo insieme di pesi. Questo produce un nuovo insieme di output di filtro. Chiamiamolo "canale" di uscite per analogia con i canali R,G,B nell'immagine di ingresso.
I due (o più) insiemi di pesi possono essere sommati come un unico tensore aggiungendo una nuova dimensione. Questo ci dà la forma generica del tensore delle ponderazioni per uno strato convoluzionale. Poiché il numero di canali di input e di output sono parametri, possiamo iniziare a sovrapporre e concatenare gli strati convoluzionali.
Illustrazione: una rete neurale convoluzionale trasforma i "cubi" di dati in altri "cubi" quantità di dati.
Convoluzioni allungate, max pooling
Eseguendo le convoluzioni con passo 2 o 3, è possibile anche ridurre il cubo di dati risultante nelle sue dimensioni orizzontali. Ci sono due modi comuni per farlo:
- Convoluzione allungata: un filtro scorrevole come sopra, ma con passo >1
- Pooling massimo: finestra scorrevole applicando l'operazione MAX (tipicamente su patch 2x2, ripetute ogni 2 pixel)
Illustrazione: se si fa scorrere la finestra di calcolo di 3 pixel, si riducono i 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.
Livello finale
Dopo l'ultimo livello convoluzionale, i dati sono sotto forma di "cubo". Esistono due modi per alimentarlo attraverso lo strato denso finale.
La prima consiste nel creare un cubo di dati in un vettore e poi alimentarlo allo strato softmax. A volte, è anche possibile aggiungere uno strato denso prima dello strato softmax. Questo approccio è tendenzialmente costoso in termini di numero di pesi. Uno strato denso alla fine di una rete convoluzionale può contenere più della metà dei pesi dell'intera rete neurale.
Anziché utilizzare uno strato denso costoso, possiamo anche suddividere il "cubo" di dati in entrata in tante parti quante sono le classi, calcolarne la media dei valori e inserirli attraverso una funzione di attivazione softmax. Questo modo di creare la testa di classificazione ha un costo di 0 pesi. In Keras, esiste un livello per questo scopo: tf.keras.layers.GlobalAveragePooling2D()
.
Passa alla sezione successiva per creare una rete convoluzionale per il problema in questione.
11. Una rete convoluzionale
Creiamo una rete convoluzionale per il riconoscimento delle cifre scritte a mano. Useremo tre strati convoluzionali in alto, il nostro tradizionale strato di lettura softmax in basso, e li colleghiamo a un unico strato completamente connesso:
Nota che il secondo e il terzo strato convoluzionale hanno un passo di due, il che spiega perché portano il numero di valori di output da 28x28 a 14x14 e quindi a 7x7.
Scriviamo il codice Keras.
È necessaria un'attenzione particolare prima del primo livello convoluzionale. In effetti, si aspetta un "cubo" 3D di dati, ma finora il nostro set di dati è stato impostato per strati densi e tutti i pixel delle immagini sono appiattiti in un vettore. Dobbiamo rimodellarli in immagini 28x28x1 (1 canale per le immagini in scala di grigi):
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))
Puoi utilizzare questa linea al posto del livello tf.keras.layers.Input
che avevi fino a ora.
In Keras, la sintassi per uno strato convoluzionale attivato da "relu" è:
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')
Per una convoluzione strisciata, potresti scrivere:
tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)
Per appiattire un cubo di dati in un vettore in modo che possa essere utilizzato da uno strato denso:
tf.keras.layers.Flatten()
Per lo strato denso, la sintassi non è cambiata:
tf.keras.layers.Dense(200, activation='relu')
Il tuo modello ha superato la barriera di accuratezza del 99%? Ci siamo quasi... ma osservate la curva di perdita della convalida. Tutto questo ti è familiare?
Guarda anche le previsioni. Per la prima volta, dovresti vedere che la maggior parte delle 10.000 cifre di test è stata riconosciuta correttamente. Rimangono solo circa 4 righe e 1/2 di errori di rilevamento (circa 110 cifre su 10.000)
Se sei bloccato, ecco la soluzione a questo punto:
12. Abbandona di nuovo
L'addestramento precedente mostra chiari segni di overfitting (e non raggiunge ancora il 99% di precisione). Dovremmo riprovare ad abbandonare il gioco?
Com'è andata questa volta?
Sembra che questa volta l'abbandono abbia funzionato. La perdita di convalida non sta più aumentando e l'accuratezza finale dovrebbe essere molto superiore al 99%. Complimenti!
La prima volta che abbiamo provato ad applicare l'abbandono, pensavamo di avere un problema di overfitting, mentre in realtà il problema riguardava l'architettura della rete neurale. Senza i livelli convoluzionali non potremmo andare oltre e l'abbandono potrebbe non far nulla in questo senso.
Questa volta sembra che la causa del problema sia stato l'overfitting e l'abbandono in realtà è stato di aiuto. Ricorda che ci sono molte cose che possono causare una disconnessione tra le curve di perdita di addestramento e convalida, con l'aumento della perdita di convalida. L'overfitting (troppi gradi di libertà, usato male dalla rete) è solo uno di questi. Se il set di dati è troppo piccolo o l'architettura della rete neurale non è adeguata, potresti notare un comportamento simile nelle curve di perdita, ma l'abbandono non aiuterà.
13. Normalizzazione batch
Infine, proviamo ad aggiungere la normalizzazione del batch.
Questa è la teoria. In pratica, basta ricordare un paio di regole:
Giochiamo per ora e aggiungiamo uno strato di normalizzazione batch su ogni livello della rete neurale, tranne l'ultimo. Non aggiungerlo all'ultimo "softmax" livello di sicurezza. Non sarebbe utile lì.
# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),
Com'è la precisione adesso?
Con un po 'di ritocchi (BATCH_SIZE=64, parametro di decadimento del tasso di apprendimento 0.666, tasso di abbandono sullo strato denso 0.3) e un po' di fortuna, puoi arrivare al 99,5%. Gli aggiustamenti del tasso di apprendimento e dell'abbandono sono stati effettuati seguendo le "best practice" per utilizzare la norma batch:
- La norma batch consente alle reti neurali di convergere e di solito consente un'addestramento più veloce.
- La norma batch è un regolarizzatore. In genere puoi ridurre la quantità di abbandono utilizzata o persino non utilizzare affatto l'abbandono.
Il blocco note della soluzione ha un'esecuzione di addestramento del 99,5%:
14. Addestramento nel cloud su hardware potente: AI Platform
Puoi trovare una versione pronta per il cloud del codice nella cartella MLengine su GitHub, insieme alle istruzioni per eseguirla su Google Cloud AI Platform. Prima di poter eseguire questa parte, dovrai creare un account Google Cloud e abilitare la fatturazione. Le risorse necessarie per completare il lab dovrebbero essere inferiori a un paio di dollari (supponendo 1 ora di tempo di addestramento su una GPU). Per preparare l'account:
- Crea un progetto sulla piattaforma Google Cloud ( http://cloud.google.com/console).
- Abilitare la fatturazione.
- Installa gli strumenti a riga di comando di Google Cloud ( qui puoi trovare l'SDK Google Cloud).
- Crea un bucket Google Cloud Storage (inserisci la regione
us-central1
). Verrà utilizzato per organizzare in fasi il codice di addestramento e archiviare il modello addestrato. - Abilita le API necessarie e richiedi le quote necessarie (esegui il comando di addestramento una volta e dovresti ricevere messaggi di errore che indicano cosa abilitare).
15. Complimenti!
Hai creato la tua prima rete neurale e l'hai addestrata fino al 99% di accuratezza. Le tecniche apprese lungo il percorso non sono specifiche del set di dati MNIST, anzi sono ampiamente utilizzate quando si lavora con le reti neurali. Come regalo di addio, ecco "gli appunti dello scalpello" per il laboratorio, in versione a cartone animato. Puoi utilizzarlo per ricordare ciò che hai imparato:
Passaggi successivi
- Dopo le reti completamente connesse e convoluzionali, dovresti dare un'occhiata alle reti neurali ricorrenti.
- Per eseguire l'addestramento o l'inferenza nel cloud su un'infrastruttura distribuita, Google Cloud fornisce AI Platform.
- Infine, ci piacerebbe ricevere feedback. Facci sapere se noti qualcosa che non va in questo lab o se pensi che dovrebbe essere migliorato. Gestiamo i feedback tramite i problemi di GitHub [link per il feedback].
L'autore: Martin GörnerTwitter: @martin_gorner |
Copyright di tutte le immagini dei cartoni animati presenti in questo lab: alexpokusay / foto stock 123RF