Non perdere anche il cheat sheet di Keras , che ti mostra i sei passaggi da seguire per costruire reti neurali in Python con esempi di codice!
Introduzione alle Reti Neurali Artificiali
Prima di approfondire Keras e come usarla per iniziare con il deep learning in Python, dovresti conoscere un paio di cose sulle reti neurali. Come hai letto brevemente nella sezione precedente, le reti neurali traggono ispirazione dalla biologia, dove il termine “rete neurale” può essere usato anche per i neuroni. Il cervello umano è un esempio di rete neurale, composta da un certo numero di neuroni.
E, come tutti sappiamo, il cervello è capace di eseguire calcoli piuttosto complessi, ed è da qui che nasce l’ispirazione per le Reti Neurali Artificiali. La rete nel suo insieme è un potente strumento di modellazione.
Perceptron
La rete neurale più semplice è il “perceptron” che, nella sua forma più elementare, consiste in un singolo neurone. Proprio come i neuroni biologici, che hanno dendriti e assoni, il neurone artificiale singolo è una semplice struttura ad albero con nodi di input e un singolo nodo di output, collegato a ciascun nodo di input. Ecco un confronto visivo tra i due:
Come vedi dall’immagine, ci sono sei componenti nei neuroni artificiali. Da sinistra a destra, sono:
Nodi di input . Ogni nodo di input è associato a un valore numerico, che può essere un qualsiasi numero reale. Ricorda che i numeri reali coprono l’intero spettro: possono essere positivi o negativi, interi o decimali.
Connessioni . Allo stesso modo, ogni connessione che parte dal nodo di input ha un peso associato, anch’esso un numero reale.
Successivamente, tutti i valori dei nodi di input e i pesi delle connessioni vengono riuniti: sono usati come input per una somma pesata : \(y = f(\sum_{i=1}^{D} w_i*x_i)\) , o, in altri termini, \(y = f(w_1*x_1 + w_2*x_2 + ... w_D*x_D)\) .
Questo risultato sarà l’input per una funzione di trasferimento o di attivazione . Nel caso più semplice ma banale, questa funzione di trasferimento sarebbe una funzione identità, \(f(x)=x\) o \(y=x\) . In questo caso, \(x\) è la somma pesata dei nodi di input e delle connessioni. Tuttavia, proprio come un neurone biologico si attiva solo quando viene superata una certa soglia, anche il neurone artificiale si attiverà solo quando la somma degli input supera una soglia, diciamo per esempio 0. Questo non accade con una funzione identità! Il modo più intuitivo è ideare un sistema come il seguente:
\(f(x) = 0\) se \(x<0\) \(f(x) = 0.5\) se \(x=0\) \(f(x) = 1\) se \(x>0\)
Ovviamente, puoi già immaginare che l’output non sarà una linea liscia: sarà una funzione discontinua. Poiché questo può creare problemi nell’elaborazione matematica, spesso si usa una variante continua, la funzione sigmoide. Un esempio di sigmoide che potresti già conoscere è la funzione logistica. Usandola si ottiene un risultato molto più regolare!
Come risultato, hai il nodo di output , associato alla funzione (come la sigmoide) della somma pesata dei nodi di input. Nota che la funzione sigmoide è una funzione matematica che genera una curva a forma di “S”; ne leggerai di più più avanti.
Infine, il perceptron può avere un parametro aggiuntivo, chiamato bias , che puoi considerare come il peso associato a un ulteriore nodo di input impostato permanentemente a 1. Il valore di bias è fondamentale perché ti permette di traslare la funzione di attivazione a sinistra o a destra, cosa che può determinare il successo dell’apprendimento.
Nota che la conseguenza logica di questo modello è che i perceptron funzionano solo con dati numerici. Questo implica che dovresti convertire eventuali dati nominali in un formato numerico.
Ora che sai che i perceptron lavorano con soglie, il passo verso il loro uso per la classificazione non è lontano: il perceptron può stabilire che qualsiasi output sopra una certa soglia indichi che un’istanza appartiene a una classe, mentre un output al di sotto della soglia può indicare che l’input appartenga all’altra classe. La retta in cui l’output eguaglia la soglia è allora il confine tra le due classi.
Perceptron multistrato
Reti di perceptron sono perceptron multistrato, ed è proprio ciò che questo tutorial implementerà in Python con l’aiuto di Keras! I perceptron multistrato sono noti anche come “reti neurali feed-forward”. Come avrai intuito, sono reti più complesse del perceptron, poiché consistono di più neuroni organizzati in livelli. Il numero di livelli è solitamente limitato a due o tre, ma teoricamente non c’è limite!
I livelli si comportano molto come i neuroni biologici di cui hai letto sopra: gli output di un livello fungono da input per il livello successivo.
Tra i livelli puoi distinguere un livello di input, livelli nascosti e un livello di output. I perceptron multistrato sono spesso completamente connessi. Questo significa che c’è una connessione da ciascun perceptron in un dato livello a ciascun perceptron nel livello successivo. Anche se la connessione completa non è un requisito, è tipicamente il caso.
Nota che mentre il perceptron poteva rappresentare solo separazioni lineari tra classi, il perceptron multistrato supera tale limitazione e può rappresentare confini decisionali più complessi.
Prevedere i tipi di vino in Python con Keras
Per questo tutorial userai il wine quality data set che puoi trovare nel wine quality data set dell’UCI Machine Learning Repository. Idealmente, faresti deep learning su dataset più grandi, ma per lo scopo di questo tutorial userai uno più piccolo. Questo principalmente perché l’obiettivo è farti iniziare con la libreria e familiarizzare con il funzionamento delle reti neurali.
Potresti già conoscere questo dataset, poiché è uno dei più popolari per iniziare a imparare a risolvere problemi di machine learning. In questo caso, ti servirà per iniziare con il deep learning in Python con Keras.
Diamoci dentro!
Capire i dati
Prima però di caricare i dati, può essere una buona idea verificare quanto ne sai davvero di vino (in relazione al dataset, ovviamente). La maggior parte saprà che, in generale, ci sono due tipi molto popolari di vino: rosso e bianco.
(Sono sicuro che ce ne siano molti altri, ma per semplicità e per la mia conoscenza limitata di vini, mi fermo qui. Scusa se deludo i veri intenditori tra voi :)).
Saperlo è già qualcosa, ma se vuoi analizzare questi dati, dovrai sapere un po’ di più.
Per prima cosa, dai un’occhiata alla cartella con la descrizione dei dati per vedere quali variabili sono state incluse. Questo è di solito il primo passo per comprendere i dati. Vai a questa pagina per consultare la descrizione o continua a leggere per conoscere meglio i tuoi dati.
I dati consistono in due dataset relativi alle varianti rossa e bianca del vino portoghese “Vinho Verde”. Come indicato nella descrizione, in questo set troverai incluse solo variabili fisico-chimiche e sensoriali. Il file di descrizione dei dati elenca le 12 variabili incluse, ma per chi, come me, non è davvero un esperto di chimica, ecco una breve descrizione di ciascuna:
Acidità fissa : gli acidi sono proprietà fondamentali del vino e contribuiscono in modo determinante al gusto. Di solito, l’acidità totale si divide in due gruppi: acidi volatili e acidi non volatili o fissi. Tra gli acidi fissi che si possono trovare nei vini ci sono: tartarico, malico, citrico e succinico. In questo dataset, questa variabile è espressa in g(\(acido\ tartarico\) )/\(dm^3\) .
Acidità volatile : l’acidità volatile è sostanzialmente il processo per cui il vino si trasforma in aceto. Negli Stati Uniti, i limiti legali di acidità volatile sono 1,2 g/L per il vino rosso da tavola e 1,1 g/L per il vino bianco da tavola. In questi dataset, l’acidità volatile è espressa in g(\(acido\ acetico\) )/\(dm^3\) .
Acido citrico è uno degli acidi fissi che troverai nei vini. È espresso in g/\(dm^3\) nei due dataset.
Zucchero residuo si riferisce tipicamente allo zucchero rimanente dopo che la fermentazione si ferma, o viene fermata. È espresso in g/\(dm^3\) nei dati red e white.
Cloruri possono contribuire in modo significativo alla sapidità del vino. Qui sono espressi in g(\(cloruro\ di\ sodio\) )/\(dm^3\) .
Anidride solforosa libera : la parte dell’anidride solforosa aggiunta a un vino che si lega è detta “bound”, mentre la parte attiva è detta “free”. L’enologo cercherà sempre di ottenere la proporzione più alta di solforosa libera rispetto a quella legata. Questa variabile è espressa in mg/\(dm^3\) nei dati.
Anidride solforosa totale è la somma di quella legata e di quella libera (SO2). Qui è espressa in mg/\(dm^3\) . Ci sono limiti legali per i livelli di solforosa nei vini: nell’UE, i rossi possono avere solo 160 mg/L, mentre i bianchi e i rosati circa 210 mg/L. Ai vini dolci è consentito arrivare a 400 mg/L. Negli USA il limite legale è 350 mg/L e in Australia 250 mg/L.
Densità è generalmente usata come misura della conversione dello zucchero in alcol. Qui è espressa in g/\(cm^3\) .
pH o potenziale di idrogeno è una scala numerica per specificare l’acidità o basicità del vino. Come saprai, soluzioni con pH inferiore a 7 sono acide, mentre con pH superiore a 7 sono basiche. Con pH pari a 7, l’acqua pura è neutra. La maggior parte dei vini ha un pH tra 2,9 e 3,9 ed è quindi acida.
Solfati sono per il vino ciò che il glutine è per il cibo. Potresti già conoscere i solfiti per i mal di testa che possono causare. Sono parte regolare della vinificazione in tutto il mondo e sono considerati necessari. In questo caso, sono espressi in g(\(solfato\ di\ potassio\) )/\(dm^3\) .
Alcol : il vino è una bevanda alcolica e, come sai, la percentuale di alcol può variare da un vino all’altro. Non stupisce che questa variabile sia inclusa nei dataset, dove è espressa in % vol.
Qualità : esperti di vino hanno valutato la qualità su una scala da 0 (pessimo) a 10 (eccellente). Il valore finale è la mediana di almeno tre valutazioni effettuate dagli stessi esperti.
Queste naturalmente sono informazioni molto basilari che potresti aver bisogno di conoscere per iniziare. Se sei un vero intenditore, probabilmente conosci tutto questo e molto di più!
Ora è il momento di ottenere i tuoi dati!
Caricare i dati
Si può fare facilmente con la libreria di manipolazione dati di Python, Pandas. Segui la convenzione d’import e importa il pacchetto con l’alias pd.
Quindi, usa la funzione read_csv() per leggere i file CSV in cui sono memorizzati i dati. Inoltre, usa l’argomento sep per specificare che il separatore, in questo caso, è un punto e virgola e non una virgola.
Provalo nel blocco DataCamp Light qui sotto:
# Import pandas
import pandas as pd
# Read in white wine data
white = ___________("http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv", sep=';')
# Read in red wine data
red = ___________("http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv", sep=';')
Fantastico! È stato un gioco da ragazzi, vero?
Probabilmente l’hai già fatto mille volte, ma è sempre un passaggio essenziale per iniziare. Ora sei pronto per esplorare, manipolare e modellare i tuoi dati!
Esplorazione dei dati
Con i dati alla mano, è facile imparare di più su questi vini! Una delle prime cose che probabilmente vorrai fare è dare un’occhiata veloce a entrambi i tuoi DataFrame:
# Print info on white wine
_____(white.____())
# Print info on red wine
_____(red.____())
Ora è il momento di verificare se l’import è andato a buon fine: controlla che i dati contengano tutte le variabili promesse dal file di descrizione dell’UCI Machine Learning Repository. Oltre al numero di variabili, controlla anche la qualità dell’import: i tipi di dato sono corretti? Tutte le righe sono state importate? Ci sono valori nulli di cui dovresti tenere conto quando pulisci i dati?
Potresti voler esaminare i dati con qualcosa in più di info():
# First rows of `red`
red.____()
# Last rows of `white`
white.____()
# Take a sample of 5 rows of `red`
red.______(5)
# Describe `white`
white.________()
# Double check for null values in `red`
pd.isnull(red)
Un breve riepilogo di queste funzioni di pandas: head(), tail() e sample() sono ottime perché ti offrono un modo rapido di ispezionare i dati senza complicazioni.
Successivamente, describe() offre statistiche riassuntive che possono aiutarti a valutare la qualità dei dati. Noti che alcune variabili hanno grande differenza tra i valori min e max. Te ne occuperai più avanti, ma per ora è importante esserne consapevoli.
Infine, hai ricontrollato la presenza di valori nulli in red con l’aiuto di isnull(). È una funzione che torna sempre utile quando hai ancora dubbi dopo aver letto i risultati di info().
Consiglio : controlla anche se i dati del vino contengono valori nulli. Puoi farlo usando la shell IPython del blocco DataCamp Light qui sopra.
Ora che hai già ispezionato i dati per vedere se l’import è stato corretto, è il momento di scavare un po’ più a fondo.
Visualizzare i dati
Un modo per farlo è osservare la distribuzione di alcune variabili del dataset e creare scatter plot per vedere possibili correlazioni. Ovviamente, potresti portare tutto questo a un livello molto più alto se usassi questi dati per un tuo progetto.
Alcol
Una variabile che potresti trovare interessante a prima vista è alcohol. È probabilmente una delle prime cose che cattura l’attenzione quando ispezioni un dataset di vini. Puoi visualizzare le distribuzioni con qualsiasi libreria di visualizzazione, ma in questo caso il tutorial usa matplotlib per tracciare rapidamente le distribuzioni:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2)
ax[0].hist(red.alcohol, 10, facecolor='red', alpha=0.5, label="Red wine")
ax[1].hist(white.alcohol, 10, facecolor='white', ec="black", lw=0.5, alpha=0.5, label="White wine")
fig.subplots_adjust(left=0, right=1, bottom=0, top=0.5, hspace=0.05, wspace=1)
ax[0].set_ylim([0, 1000])
ax[0].set_xlabel("Alcohol in % Vol")
ax[0].set_ylabel("Frequency")
ax[1].set_xlabel("Alcohol in % Vol")
ax[1].set_ylabel("Frequency")
#ax[0].legend(loc='best')
#ax[1].legend(loc='best')
fig.suptitle("Distribution of Alcohol in % Vol")
plt.show()
Come puoi vedere nell’immagine qui sotto, i livelli di alcol tra vino rosso e bianco sono per lo più simili: hanno circa il 9% di alcol. Naturalmente, ci sono anche un numero considerevole di osservazioni con il 10% o l’11% di alcol.
Nota che puoi ricontrollare usando la funzione histogram() del pacchetto numpy per calcolare l’istogramma dei dati white e red, così:
import numpy as np
print(np.histogram(red.alcohol, bins=[7,8,9,10,11,12,13,14,15]))
print(np.histogram(white.alcohol, bins=[7,8,9,10,11,12,13,14,15]))
Se ti interessano i tutorial su matplotlib, dai un’occhiata al tutorial per principianti di DataCamp su Matplotlib e al tutorial Viewing 3D Volumetric Data , che mostra come usare l’API degli event handler di Matplotlib.
Solfati
Successivamente, una cosa che mi interessa è la relazione tra solfati e qualità del vino. Come hai letto sopra, i solfati possono causare mal di testa e mi chiedo se questo influenzi la qualità del vino. Inoltre, spesso sento dire che le donne in particolare non vogliono bere vino proprio perché provoca mal di testa. Forse questo influenza le valutazioni del vino rosso?
Diamo un’occhiata.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(8, 4))
ax[0].scatter(red['quality'], red["sulphates"], color="red")
ax[1].scatter(white['quality'], white['sulphates'], color="white", edgecolors="black", lw=0.5)
ax[0].set_title("Red Wine")
ax[1].set_title("White Wine")
ax[0].set_xlabel("Quality")
ax[1].set_xlabel("Quality")
ax[0].set_ylabel("Sulphates")
ax[1].set_ylabel("Sulphates")
ax[0].set_xlim([0,10])
ax[1].set_xlim([0,10])
ax[0].set_ylim([0,2.5])
ax[1].set_ylim([0,2.5])
fig.subplots_adjust(wspace=0.5)
fig.suptitle("Wine Quality by Amount of Sulphates")
plt.show()
Come puoi vedere nell’immagine qui sotto, il vino rosso sembra contenere più solfati del vino bianco, che ne ha pochi sopra 1 g/\(dm^3\) . Per il vino bianco, sembrano esserci solo un paio di eccezioni poco sopra 1 g/\(dm^3\) , mentre questo è sicuramente più frequente per i vini rossi. Questo potrebbe forse spiegare il detto comune che il vino rosso provochi mal di testa, ma che dire della qualità?
Si vede chiaramente che c’è vino bianco con una quantità relativamente bassa di solfati che ottiene un punteggio di 9, ma per il resto, al momento è difficile interpretare correttamente i dati.
Ovviamente, devi considerare che la differenza nelle osservazioni potrebbe anche influenzare i grafici e il modo in cui potresti interpretarli.
Acidità
A parte i solfati, l’acidità è una delle caratteristiche principali e vitali del vino necessarie per ottenere vini di qualità. I grandi vini bilanciano spesso acidità, tannino, alcol e dolcezza. Alcune ricerche mi hanno insegnato che in quantità di 0,2–0,4 g/L l’acidità volatile non influisce sulla qualità del vino. A livelli più alti, però, l’acidità volatile può dare al vino una sensazione tattile pungente, acetosa. Un’acidità volatile estrema indica un vino seriamente difettoso.
Mettiamo alla prova i dati e creiamo uno scatter plot che metta in relazione alcol e acidità volatile. I punti dati dovrebbero essere colorati in base alla loro etichetta di valutazione o quality:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(570)
redlabels = np.unique(red['quality'])
whitelabels = np.unique(white['quality'])
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(8, 4))
redcolors = np.random.rand(6,4)
whitecolors = np.append(redcolors, np.random.rand(1,4), axis=0)
for i in range(len(redcolors)):
redy = red['alcohol'][red.quality == redlabels[i]]
redx = red['volatile acidity'][red.quality == redlabels[i]]
ax[0].scatter(redx, redy, c=redcolors[i])
for i in range(len(whitecolors)):
whitey = white['alcohol'][white.quality == whitelabels[i]]
whitex = white['volatile acidity'][white.quality == whitelabels[i]]
ax[1].scatter(whitex, whitey, c=whitecolors[i])
ax[0].set_title("Red Wine")
ax[1].set_title("White Wine")
ax[0].set_xlim([0,1.7])
ax[1].set_xlim([0,1.7])
ax[0].set_ylim([5,15.5])
ax[1].set_ylim([5,15.5])
ax[0].set_xlabel("Volatile Acidity")
ax[0].set_ylabel("Alcohol")
ax[1].set_xlabel("Volatile Acidity")
ax[1].set_ylabel("Alcohol")
#ax[0].legend(redlabels, loc='best', bbox_to_anchor=(1.3, 1))
ax[1].legend(whitelabels, loc='best', bbox_to_anchor=(1.3, 1))
#fig.suptitle("Alcohol - Volatile Acidity")
fig.subplots_adjust(top=0.85, wspace=0.7)
plt.show()
Nota che i colori in questa immagine sono scelti casualmente con l’aiuto del modulo random di NumPy. Puoi sempre cambiarli passando una lista alle variabili redcolors o whitecolors. Assicurati però che siano uguali (tranne che per 1 perché i dati del vino bianco hanno un valore quality unico in più rispetto al vino rosso), altrimenti le legende non corrisponderanno!
Guarda il grafico completo qui:
Nell’immagine sopra, vedi che i livelli di cui hai letto prima valgono in particolare per il vino bianco: la maggior parte dei vini con etichetta 8 ha livelli di acidità volatile di 0,5 o inferiori, ma se abbia o meno un effetto sulla qualità è troppo difficile da dire, poiché tutti i punti dati sono molto fitti verso un lato del grafico.
Questa è solo una rapida esplorazione dei dati. Se fossi interessato ad approfondire questo passaggio nei tuoi progetti, considera i post di DataCamp sull’esplorazione dei dati, come i tutorial Python Exploratory Data Analysis e Python Data Profiling , che ti guideranno attraverso le basi dell’EDA.
Conclusione dell’analisi esplorativa
Forse è stato tanto da assimilare, quindi non è mai troppo tardi per un piccolo riepilogo di ciò che hai visto durante l’EDA e che potrebbe essere importante per il proseguo del tutorial:
Alcune variabili nei tuoi dataset hanno valori molto distanti tra loro. Potrai e vorrai affrontare questo aspetto nella prossima sezione del tutorial.
Hai uno scenario ideale: non ci sono valori nulli nei dataset.
La maggior parte dei vini inclusi nel dataset ha circa il 9% di alcol.
Il vino rosso sembra contenere più solfati del vino bianco, che ne ha meno oltre 1 g/\(dm^3\) .
Hai visto che la maggior parte dei vini aveva un’acidità volatile di 0,5 o inferiore. Al momento, non c’è una relazione diretta con la qualità del vino.
Finora hai guardato separatamente i dati del vino bianco e rosso. I due sembrano differire in parte osservando da vicino alcune variabili e, in altri casi, sembrano molto simili. Pensi che potrebbe esserci un modo per classificare le voci in base alle loro variabili in vino bianco o rosso?
C’è un solo modo per scoprirlo: preprocessa i dati e modella in modo da poter vedere cosa succede!
Preprocessare i dati
Ora che hai esplorato i dati, è il momento di agire in base alle intuizioni ottenute! Facciamo il preprocessing in modo da poter iniziare a costruire la tua rete neurale!
# Add `type` column to `red` with value 1
___['type'] = 1
# Add `type` column to `white` with value 0
_____['type'] = 0
# Append `white` to `red`
wines = red.______(white, ignore_index=True)
In questo caso imposti ignore_index su True perché non vuoi mantenere le etichette di indice di white quando aggiungi i dati a red: vuoi che le etichette continuino da dove erano rimaste in red, non duplicare etichette di indice unendo i due dataset.
Intermezzo: matrice di correlazione
Ora che hai il dataset completo, è una buona idea fare anche una rapida esplorazione; già conosci alcune cose dall’osservazione separata dei due dataset, ed è ora di ottenere magari qualche insight più solido.
Poiché può essere un po’ difficile interpretare i grafici, è anche una buona idea tracciare una matrice di correlazione. Ti darà più rapidamente informazioni su quali variabili sono correlate:
import seaborn as sns
corr = wines.corr()
sns.heatmap(corr,
xticklabels=corr.columns.values,
yticklabels=corr.columns.values)
sns.plt.show()
Come ti aspetteresti, ci sono variabili che correlano, come density e residual sugar. Anche volatile acidity e type sono più connesse di quanto avresti potuto immaginare guardando separatamente i due dataset, ed era prevedibile che free sulfur dioxide e total sulfur dioxide correlassero.
Molto interessante!
Train e Test set
Per dati sbilanciati si intende in genere un problema nei compiti di classificazione in cui le classi non sono rappresentate in modo uguale. La maggior parte dei dataset di classificazione non ha esattamente lo stesso numero di istanze in ciascuna classe, ma una piccola differenza spesso non conta. Devi quindi assicurarti che entrambe le classi di vino siano presenti nel modello di training. Inoltre, il numero di istanze dei due tipi di vino dovrebbe essere più o meno uguale, così da non favorire una classe rispetto all’altra nelle previsioni.
In questo caso sembra esserci uno sbilanciamento, ma per il momento procederai così. Successivamente potrai valutare il modello e, se le prestazioni saranno scarse, potrai ricorrere a undersampling o oversampling per compensare la differenza nelle osservazioni.
Per ora, importa train_test_split da sklearn.model_selection e assegna dati ed etichette target alle variabili X e y. Vedrai che devi appiattire l’array delle etichette target per essere completamente pronto a usare le variabili X e y come input per la funzione train_test_split(). Al lavoro: inizia nel blocco DataCamp Light qui sotto!
Sei già a buon punto per costruire la tua prima rete neurale, ma c’è ancora una cosa di cui devi occuparti! Ricordi cosa avevi scoperto guardando i sommari dei dataset white e red?
Esatto, alcuni valori erano piuttosto distanti. Potrebbe avere senso standardizzare.
Standardizzare i dati
La standardizzazione è un modo per gestire valori molto distanti tra loro. Il pacchetto scikit-learn offre un modo rapido per standardizzare i dati: importa il modulo StandardScaler da sklearn.preprocessing e sei pronto per scalare train e test set!
from sklearn. preprocessing import StandardScaler
scaler = StandardScaler( ) . fit( X_train)
X_train = scaler. transform( X_train)
X_test = scaler. transform( X_test) Offerto da
Ora che i tuoi dati sono preprocessati, puoi passare al lavoro vero e proprio: costruire la tua rete neurale per classificare i vini.
Modellare i dati
Prima di iniziare a modellare, torna alla domanda iniziale: puoi prevedere se un vino è rosso o bianco guardando le sue proprietà chimiche, come acidità volatile o solfati?
Dato che hai solo due classi, bianco e rosso, farai una classificazione binaria. Come puoi immaginare, “binaria” significa 0 o 1, sì o no. Poiché le reti neurali lavorano solo con dati numerici, hai già codificato rosso come 1 e bianco come 0.
Un tipo di rete che funziona bene su un problema del genere è un perceptron multistrato. Come hai letto all’inizio del tutorial, questo tipo di rete neurale è spesso completamente connessa. Ciò significa che cercherai di costruire una pila piuttosto semplice di livelli fully-connected per risolvere questo problema. Quanto alla funzione di attivazione, è meglio usare una delle più comuni per familiarizzare con Keras e le reti neurali, ovvero la funzione di attivazione relu.
Come inizi a costruire il tuo perceptron multistrato? Un modo rapido è usare il modello Sequenziale di Keras: è uno stack lineare di livelli. Puoi creare facilmente il modello passando un elenco di istanze di layer al costruttore, che imposti eseguendo model = Sequential().
Poi è meglio ripensare alla struttura del perceptron multistrato come descritta all’inizio del tutorial: hai un livello di input, alcuni livelli nascosti e un livello di output. Quando crei il modello, è importante considerare che il primo livello deve chiarire la forma dell’input. Il modello deve sapere quale forma di input aspettarsi ed è per questo che troverai sempre gli argomenti input_shape, input_dim, input_length o batch_size nella documentazione dei layer e negli esempi pratici.
In questo caso userai un livello Dense, che è un livello completamente connesso. I livelli Dense implementano l’operazione: output = activation(dot(input, kernel) + bias). Nota che senza la funzione di attivazione, il tuo livello Dense consisterebbe solo di due operazioni lineari: un prodotto scalare e un’addizione.
Nel primo livello, l’argomento activation assume il valore relu. Vedi anche che è stato definito input_shape. Questo è l’input dell’operazione appena vista: il modello accetta in input array di forma (12,), o (*, 12). Infine, vedi che il primo livello ha 12 come primo valore per l’argomento units di Dense(), che è la dimensionalità dello spazio di output, ovvero 12 unità nascoste. Questo significa che il modello produrrà array di forma (*, 12): è la dimensionalità dello spazio di output. Non preoccuparti se non è tutto chiarissimo ora: ne leggerai ancora!
Le units rappresentano in realtà il kernel della formula sopra, ovvero la matrice dei pesi, composta da tutti i pesi assegnati a tutti i nodi di input, creata dal livello. Nota che nell’esempio sotto non includi alcun bias, poiché non hai incluso l’argomento use_bias impostandolo su TRUE, cosa comunque possibile.
Il livello intermedio usa anch’esso la funzione di attivazione relu. L’output di questo livello sarà un array di forma (*,8).
Chiudi la rete con un livello Dense di dimensione 1. Il livello finale userà anche una funzione di attivazione sigmoide così che l’output sia effettivamente una probabilità; Significa che otterrai un punteggio tra 0 e 1 che indica quanto è probabile che il campione abbia il target “1”, ovvero quanto è probabile che il vino sia rosso.
# Import `Sequential` from `keras.models`
from keras.models import Sequential
# Import `Dense` from `keras.layers`
from keras.layers import Dense
# Initialize the constructor
model = Sequential()
# Add an input layer
model.add(Dense(12, activation='relu', input_shape=(11,)))
# Add one hidden layer
model.add(Dense(8, activation='relu'))
# Add an output layer
model.add(Dense(1, activation='sigmoid'))
Nel complesso, vedi che ci sono due decisioni architetturali chiave da prendere per realizzare il modello: quante layer usare e quante “unità nascoste” scegliere per ciascun layer.
In questo caso, hai scelto 12 unità nascoste per il primo layer del modello: come sopra, è la dimensionalità dello spazio di output. In altre parole, stai impostando il grado di libertà che consenti alla rete quando apprende le rappresentazioni. Se consentissi più unità nascoste, la rete potrebbe apprendere rappresentazioni più complesse ma l’operazione sarebbe anche più costosa e soggetta a overfitting.
Ricorda che l’overfitting si verifica quando il modello è troppo complesso: descrive l’errore casuale o il rumore e non la relazione sottostante che deve descrivere. In altre parole, i dati di training sono modellati fin troppo bene!
Nota che quando non hai molti dati di training, è meglio usare una rete piccola con pochissimi layer nascosti (tipicamente uno solo, come nell’esempio sopra).
Se vuoi ottenere informazioni sul modello creato, puoi usare l’attributo output_shape o la funzione summary(), tra le altre. Alcune delle più basilari sono elencate sotto.
Prova a eseguirle per vedere esattamente quali risultati ottieni e cosa ti dicono sul modello che hai appena creato:
# Model output shape
model.___________
# Model summary
model.__________
# Model config
model.get_config()
# List all weight tensors
model.get_weights()
Compilare e fare fit
Ora è il momento di compilare il modello e adattarlo ai dati: ancora una volta, usa compile() e fit() per farlo.
model. compile ( loss= 'binary_crossentropy' ,
optimizer= 'adam' ,
metrics= [ 'accuracy' ] )
model. fit( X_train, y_train, epochs= 20 , batch_size= 1 , verbose= 1 ) Offerto da
Nella compilazione, configuri il modello con l’ottimizzatore adam e la funzione di perdita binary_crossentropy. Inoltre, puoi monitorare l’accuratezza durante l’addestramento passando ['accuracy'] all’argomento metrics.
optimizer e loss sono due argomenti richiesti per compilare il modello. Alcuni tra gli algoritmi di ottimizzazione più popolari sono lo Stochastic Gradient Descent (SGD), ADAM e RMSprop. A seconda dell’algoritmo scelto, dovrai regolare alcuni parametri, come il learning rate o il momentum. La scelta della funzione di perdita dipende dal compito: ad esempio, per un problema di regressione userai di solito il Mean Squared Error (MSE). Come vedi in questo esempio, hai usato binary_crossentropy per il problema di classificazione binaria di determinare se un vino è rosso o bianco. Infine, con la classificazione multiclasse, userai categorical_crossentropy.
Dopo, puoi addestrare il modello per 20 epoche o iterazioni su tutti i campioni in X_train e y_train, in batch da 1 campione. Puoi anche specificare l’argomento verbose. Impostandolo su 1, indichi di voler vedere la barra di avanzamento del training.
In altre parole, devi addestrare il modello per un numero specificato di epoche o esposizioni al dataset di training. Un’epoca è un singolo passaggio su tutto il training set, seguito dal test sul validation set. La dimensione del batch specificata nel codice sopra definisce il numero di campioni propagati attraverso la rete. Inoltre, in questo modo ottimizzi l’efficienza perché ti assicuri di non caricare in memoria troppi pattern di input alla volta.
Predire i valori
Mettiamo all’opera il tuo modello! Puoi usarlo per fare previsioni sulle etichette del test set. Usa semplicemente predict() e passagli il test set per prevedere le etichette dei dati. In questo caso, il risultato è memorizzato in y_pred:
y_pred = model. predict( X_test) Offerto da
Prima di valutare il modello, puoi già farti un’idea rapida dell’accuratezza controllando come y_pred e y_test si confrontano:
y_pred[ : 5 ] Offerto da
array( [ [ 0 ] ,
[ 1 ] ,
[ 0 ] ,
[ 0 ] ,
[ 0 ] ] , dtype= int32) Offerto da
y_test[ : 5 ] Offerto da
array( [ 0 , 1 , 0 , 0 , 0 ] ) Offerto da
Vedi che questi valori sembrano coincidere, ma cos’è tutto questo senza qualche numero concreto?
Valutare il modello
Ora che hai costruito il modello e l’hai usato per fare previsioni su dati che il modello non aveva ancora visto, è il momento di valutarne le prestazioni. Puoi confrontare visivamente le previsioni con le etichette di test (y_test), oppure usare vari tipi di metriche per determinare le prestazioni effettive. In questo caso, userai evaluate(). Passa i dati e le etichette di test e, se vuoi, imposta l’argomento verbose a 1. Vedrai più log comparire così.
score = model. evaluate( X_test, y_test, verbose= 1 )
print ( score) Offerto da
[ 0.025217213829228164 , 0.99487179487179489 ] Offerto da
Lo score è una lista che contiene la combinazione di loss e accuracy. In questo caso, entrambe sembrano ottime, ma ricorda che i dati erano un po’ sbilanciati: avevi più osservazioni di vino bianco che di vino rosso. L’accuratezza potrebbe semplicemente riflettere la distribuzione delle classi, perché il modello potrebbe predire white poiché quelle osservazioni sono molto presenti!
Prima di riorganizzare i dati e metterli insieme in modo diverso, è sempre una buona idea provare metriche di valutazione differenti. Per questo, puoi fare affidamento su scikit-learn (che importi come sklearn, proprio come prima quando creavi i train e test set).
In questo caso proverai alcune tecniche base di valutazione per la classificazione, come:
La confusion matrix, che suddivide le previsioni in una tabella che mostra le previsioni corrette e i tipi di errori commessi. Idealmente, vedrai numeri solo sulla diagonale, il che significa che tutte le previsioni sono corrette!
La precision, una misura dell’esattezza del classificatore. Più alta è la precisione, più accurato è il classificatore.
Il recall, una misura della completezza del classificatore. Più alto è il recall, più casi il classificatore copre.
La F1 Score o F-score è una media pesata di precision e recall.
La Kappa o kappa di Cohen è l’accuratezza di classificazione normalizzata per lo sbilanciamento delle classi nei dati.
from sklearn. metrics import confusion_matrix, precision_score, recall_score, f1_score, cohen_kappa_score
confusion_matrix( y_test, y_pred) Offerto da
array( [ [ 1585 , 3 ] ,
[ 8 , 549 ] ] ) Offerto da
precision_score( y_test, y_pred) Offerto da
0.994565217391 Offerto da
recall_score( y_test, y_pred) Offerto da
0.98563734290843807 Offerto da
f1_score( y_test, y_pred) Offerto da
0.99008115419296661 Offerto da
cohen_kappa_score( y_test, y_pred) Offerto da
0.98662321692498967 Offerto da
Tutti questi punteggi sono molto buoni! Hai creato un modello piuttosto accurato, nonostante il fatto che tu abbia molte più righe del tipo vino bianco.
Ottimo lavoro!
Alcuni altri esperimenti
Hai costruito con successo il tuo primo modello, ma puoi andare ancora oltre. Perché non provare le seguenti cose e vedere che effetto hanno? Come hai letto sopra, le due decisioni architetturali chiave riguardano i layer e i nodi nascosti. Sono ottimi punti di partenza:
Hai usato 1 livello nascosto. Prova a usarne 2 o 3;
Usa layer con più o meno unità nascoste;
Prendi la colonna quality come etichette target e il resto dei dati (inclusa la colonna type codificata!) come dati. Ora hai un problema di classificazione multiclasse!
Perché non provare anche a cambiare la funzione di attivazione? Invece di relu, prova a usare la funzione di attivazione tanh e vedi che risultati ottieni!
Prevedere la qualità del vino
Il tuo modello di classificazione ha funzionato alla grande per una prima prova!
Ma c’è molto altro che puoi fare oltre a salire di livello e provare strutture più complesse del percettrone multistrato. Perché non provare a creare una rete neurale per prevedere la qualità del vino?
In questo caso, il tutorial assume che quality sia una variabile continua: il compito non è quindi una classificazione binaria ma una regressione ordinale. È un tipo di regressione usata per prevedere una variabile ordinale: il valore di quality esiste su una scala arbitraria in cui l’ordinamento relativo tra i diversi valori di quality è significativo. Su questa scala, la scala di qualità 0–10 da “pessimo” a “ottimo” è un esempio.
Nota che potresti anche considerare questo tipo di problema come un problema di classificazione e considerare le etichette di qualità come etichette di classe fisse.
In ogni caso, questa impostazione significa che le etichette target saranno la colonna quality nei tuoi DataFrame red e white per la seconda parte di questo tutorial. Ciò richiederà un po’ di preprocessing aggiuntivo.
Preprocessare i dati
Poiché la variabile quality diventa la tua classe target, ora dovrai isolare le etichette di qualità dal resto del dataset. Metterai wines.quality in una variabile diversa y e inserirai i dati dei vini, con eccezione della colonna quality, in una variabile x.
Successivamente, sei pronto per suddividere i dati in train e test set, ma in questo caso non seguirai questo approccio (anche se potresti!). In questa seconda parte del tutorial, userai la validazione k-fold, che richiede di suddividere i dati in K partizioni. Di solito, K è impostato a 4 o 5. Quindi, istanzi modelli identici e ne addestri ciascuno su una partizione, valutando anche sulle partizioni rimanenti. Il punteggio di validazione per il modello è poi la media dei K punteggi ottenuti.
Vedrai come farlo più avanti. Per ora, usa StandardScaler per assicurarti che i dati siano a posto prima di adattarli al modello, proprio come prima.
# Isolate target labels
y = wines.________
# Isolate data
X = wines.drop('quality', axis=_)
Ricorda che devi eseguire nuovamente lo scaling perché avevi molte differenze in alcuni valori dei dati red, white (e di conseguenza anche wines).
Provalo nel blocco DataCamp Light qui sotto. Tutte le librerie necessarie sono già caricate!
# Scale the data with `StandardScaler`
X = ________________.fit_transform(X)
Ora sei di nuovo al punto di prima. Puoi ricominciare a modellare la rete neurale!
Progettare l’architettura della rete neurale
Ora che hai nuovamente preprocessato i dati, è di nuovo il momento di costruire un modello di rete neurale, un perceptron multistrato. Anche se lo userai per un compito di regressione, l’architettura potrebbe essere molto simile, con due layer Dense.
Non dimenticare che il primo layer è il tuo livello di input. Dovrai passargli la forma dei dati in input. In questo caso, vedi che userai input_dim per passare le dimensioni dei dati in input al layer Dense.
# Import `Sequential` from `keras.models`
from keras.models import Sequential
# Import `Dense` from `keras.layers`
from keras.layers import Dense
# Initialize the model
model = Sequential()
# Add input layer
model.add(Dense(64, input_dim=12, activation='relu'))
# Add output layer
model.add(Dense(1))
Nota ancora che il primo layer definito è quello di input. Questo layer deve conoscere le dimensioni di input dei dati. Passi le dimensioni di input, che in questo caso sono 12 (non dimenticare che conti anche la colonna Type che hai generato nella prima parte del tutorial!). Usi di nuovo la funzione di attivazione relu, ma ancora una volta non c’è bias. Il numero di unità nascoste è 64.
La tua rete termina con un’unica unità Dense(1) e non include un’attivazione. Questa è una configurazione tipica per la regressione scalare, in cui cerchi di prevedere un singolo valore continuo).
Compilare il modello, fare fit dei dati
Con il modello pronto, puoi di nuovo compilarlo e adattargli i dati. Ma aspetta. Non ti servono le partizioni di validazione K-fold di cui hai letto prima? Esatto.
import numpy as np
from sklearn. model_selection import StratifiedKFold
seed = 7
np. random. seed( seed)
kfold = StratifiedKFold( n_splits= 5 , shuffle= True , random_state= seed)
for train, test in kfold. split( X, Y) :
model = Sequential( )
model. add( Dense( 64 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 1 ) )
model. compile ( optimizer= 'rmsprop' , loss= 'mse' , metrics= [ 'mae' ] )
model. fit( X[ train] , Y[ train] , epochs= 10 , verbose= 1 ) Offerto da
Usa la funzione compile() per compilare il modello e poi fit() per adattarlo ai dati. Per compilare il modello, assicurati di definire almeno gli argomenti optimizer e loss. In questo caso, puoi usare rsmprop, uno degli algoritmi di ottimizzazione più popolari, e mse come funzione di perdita, tipica per problemi di regressione come il tuo.
L’argomento aggiuntivo metrics che definisci è in realtà una funzione usata per valutare le prestazioni del modello. Per i problemi di regressione, è comune usare la Mean Absolute Error (MAE) come metrica. Ne leggerai di più nella prossima sezione.
Passa i dati e le etichette di training a fit(), determina quante epoche vuoi eseguire, la dimensione del batch e, se vuoi, imposta l’argomento verbose a 1 per avere più log, perché potrebbe richiedere un po’ di tempo.
Valutare il modello
Come prima, dovresti anche valutare il modello. Oltre ad aggiungere y_pred = model.predict(X[test]) al resto del codice sopra, potrebbe anche essere una buona idea usare alcune metriche di valutazione di sklearn, come hai fatto nella prima parte del tutorial.
Per farlo, puoi usare il Mean Squared Error (MSE) e il Mean Absolute Error (MAE). Il primo, detto anche “mean squared deviation” (MSD), misura la media dei quadrati degli errori o deviazioni. In altre parole, quantifica la differenza tra lo stimatore e ciò che viene stimato. In questo modo conosci meglio la qualità del tuo stimatore: è sempre non negativo e valori più vicini a zero sono migliori.
La seconda misura di valutazione, MAE, sta per Mean Absolute Error: quantifica quanto le previsioni siano vicine agli esiti finali.
Aggiungi queste righe al blocco di codice precedente e fai attenzione all’indentazione:
mse_value, mae_value = model. evaluate( X[ test] , Y[ test] , verbose= 0 )
print ( mse_value)
0.522478731072
print ( mae_value)
0.561965950103 Offerto da
Nota che oltre ai punteggi MSE e MAE, potresti anche usare lo score R2 o la regression score function. Qui dovresti puntare a un punteggio di 1.0, che è il migliore. Tuttavia, il punteggio può anche essere negativo!
from sklearn. metrics import r2_score
r2_score( Y[ test] , y_pred)
0.3125092543 Offerto da
A prima vista, sono numeri piuttosto pessimi, giusto? La cosa buona, però, è che ora puoi sperimentare ottimizzando il codice affinché i risultati migliorino un po’.
È proprio di questo che tratta la prossima e ultima sezione!
Fine-tuning del modello
Il fine-tuning del modello è probabilmente qualcosa che farai spesso, perché non tutti i problemi sono così lineari come quello visto nella prima parte del tutorial. Come hai letto sopra, ci sono già due decisioni cruciali che probabilmente vorrai regolare: quanti layer userai e quante “unità nascoste” sceglierai per ciascun layer.
All’inizio, sarà davvero un bel viaggio.
Aggiungere layer
Cosa succederebbe se aggiungessi un altro layer al modello? E se fosse così?
model = Sequential( )
model. add( Dense( 64 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 64 , activation= 'relu' ) )
model. add( Dense( 1 ) ) Offerto da
Unità nascoste
Prova anche l’effetto di aggiungere più unità nascoste all’architettura del modello e studiane l’impatto sulla valutazione, ad esempio così:
model = Sequential( )
model. add( Dense( 128 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 1 ) ) Offerto da
Nota ancora che, in generale, poiché non hai una tonnellata di dati, l’overfitting può e sarà peggiore. Ecco perché dovresti usare una rete piccola.
Altri esperimenti: parametri di ottimizzazione
Oltre ad aggiungere layer e giocare con le unità nascoste, puoi anche provare a regolare (alcuni) parametri dell’algoritmo di ottimizzazione passato alla funzione compile(). Finora hai sempre passato una stringa, come rmsprop, all’argomento optimizer.
Ma non deve essere sempre così!
Prova, per esempio, a importare RMSprop da keras.models e regolare il learning rate lr. Puoi anche cambiare i valori predefiniti impostati per gli altri parametri di RMSprop(), ma non è consigliato. Puoi trovare più informazioni qui .
from keras. optimizers import RMSprop
rmsprop = RMSprop( lr= 0.0001 )
model. compile ( optimizer= rmsprop, loss= 'mse' , metrics= [ 'mae' ] ) Offerto da
Prova anche a sperimentare con altri algoritmi di ottimizzazione, come lo Stochastic Gradient Descent (SGD). Noti qualche effetto?
from keras. optimizers import SGD, RMSprop
sgd= SGD( lr= 0.1 )
model. compile ( optimizer= sgd, loss= 'mse' , metrics= [ 'mae' ] ) Offerto da
Vai oltre!
Questo tutorial è solo un inizio del tuo percorso nel deep learning con Python e Keras. C’è ancora molto da coprire, quindi perché non seguire il corso di DataCamp Deep Learning in Python ? Nel frattempo, assicurati anche di consultare la documentazione di Keras , se non l’hai già fatto. Troverai più esempi e informazioni su tutte le funzioni, argomenti, altri layer, ecc. Sarà senza dubbio una risorsa indispensabile mentre impari a lavorare con le reti neurali in Python! Se invece ti va di leggere un libro che spiega le basi del deep learning (con Keras) insieme al suo uso pratico, dovresti assolutamente leggere il libro di François Chollet Deep Learning in Python .
A questo punto potresti già conoscere il machine learning, un ramo dell’informatica che studia la progettazione di algoritmi in grado di apprendere. Oggi ti concentrerai sul deep learning, un sottoinsieme del machine learning che raccoglie algoritmi ispirati alla struttura e al funzionamento del cervello. Questi algoritmi sono solitamente chiamati Artificial Neural Networks (ANN). Il deep learning è uno dei campi più caldi della data science, con molti casi d’uso che hanno dato risultati sorprendenti in robotica, riconoscimento di immagini e Intelligenza Artificiale (IA).
Una delle librerie Python più potenti e facili da usare per sviluppare e valutare modelli di deep learning è Keras; incapsula le librerie di calcolo numerico efficienti Theano e TensorFlow. Il vantaggio principale è che puoi iniziare a lavorare con le reti neurali in modo semplice e divertente.
Il tutorial di oggi su Keras per principianti ti introdurrà alle basi del deep learning in Python: