top of page
Team I.A. Italia

Reti Neurali con python tutorial completo

Introduzione alle reti neurali con Python In questo articolo, vedremo come costruire reti neurali con Python e capiremo meglio l'apprendimento profondo creando una spiegazione per le previsioni del modello.

Prima della lettura vi consigliamo un bel Caffè
Prima della lettura vi consigliamo un bel Caffè

Il Deep Learning è un tipo di apprendimento automatico che imita il modo in cui gli umani acquisiscono determinati tipi di conoscenza ed è diventato più popolare nel corso degli anni rispetto ai modelli standard. Mentre gli algoritmi tradizionali sono lineari, i modelli di Deep Learning, generalmente Reti Neurali, sono impilati in una gerarchia di crescente complessità e astrazione (quindi il "profondo" in Deep Learning).


Le reti neurali si basano su un insieme di unità collegate (neuroni), che, proprio come le sinapsi nel cervello, in grado di trasmettere un segnale ad altri neuroni, modo che, comportandosi come le cellule cerebrali interconnesse, possono imparare e prendere decisioni in un modo più umano.

Oggi il Deep Learning è così popolare che molte aziende vogliono usarlo anche se non lo comprendono pienamente. Spesso i data scientist devono prima semplificare questi complessi algoritmi per il Business, e poi spiegare e giustificare i risultati dei modelli, cosa non sempre semplice con le Reti Neurali. Penso che il modo migliore per farlo sia attraverso la visualizzazione.

Ci siamo presi un po di tempo per creare questo articolo con del codice Python utile che può essere facilmente applicato in altri casi simili (basta copiare, incollare, adattare) e percorrere ogni riga di codice con commenti in modo da poter replicare gli esempi.


In particolare, vedremo:

  1. Configurazione dell'ambiente, tensorflow vs pytorch.

  2. Scomposizione delle reti neurali artificiali, input, output, strati nascosti, funzioni di attivazione.

  3. Deep Learning con reti neurali profonde.

  4. Progettazione del modello con tensorflow/keras .

  5. Visualizzazione di reti neurali con Python.

  6. Formazione e test del modello.

  7. Spiegare i modelli con shap.



Scarica GRATUITAMENTE il pdf dell'articolo sulle reti NEURALI e PYTHON




Configurazione ambiente per creare e sviluppare Reti neurali o Modelli di Deep Learning

Esistono due librerie principali per la creazione di reti neurali:

  1. TensorFlow (sviluppato da Google)

  2. PyTorch (sviluppato da Facebook)


Queste due librerie sono preferite dalla community e dalle aziende perché possono sfruttare la potenza delle GPU NVIDIA. Ciò è molto utile, e talvolta necessario, per elaborare grandi set di dati come un corpus di testo o una galleria di immagini.

Per questo tutorial, useremo TensorFlow e Keras , un modulo di livello superiore molto più intuitivo rispetto a TensorFlow e PyTorch puri , anche se un po' più lenti.

Il primo passo è installare TensorFlow tramite il terminale:


pip install tensorflow #python <3
pip3 install tensorflow #python >=3
!pip install tensorflow #google colab

Se vuoi abilitare il supporto GPU, puoi leggere la documentazione ufficiale. Dopo averlo configurato, le tue istruzioni Python verranno tradotte in CUDA dalla tua macchina ed elaborate dalle GPU, quindi i tuoi modelli funzioneranno incredibilmente più velocemente.

Ora possiamo importare sul nostro notebook i moduli principali da TensorFlow Keras e iniziare a codificare:


from tensorflow.keras import models, layers, utils, backend as K
import matplotlib.pyplot as plt
import shap

Reti neurali artificiali cosa sono

Le ANN sono costituite da strati con una dimensione di input e una di output. Quest'ultimo è determinato dal numero di neuroni (detti anche “nodi”), un'unità computazionale che collega gli input pesati tramite una funzione di attivazione (che aiuta il neurone ad accendersi/spegnersi). I pesi , come nella maggior parte degli algoritmi di apprendimento automatico, vengono inizializzati e ottimizzati casualmente durante l'addestramento per ridurre al minimo una funzione di perdita. I livelli possono essere raggruppati come:

  • Il livello di input ha il compito di passare il vettore di input alla rete neurale. Se abbiamo una matrice di 3 caratteristiche (forma N x 3), questo livello prende 3 numeri come input e passa gli stessi 3 numeri al livello successivo.

  • I livelli nascosti rappresentano i nodi intermedi, applicano diverse trasformazioni ai numeri per migliorare l'accuratezza del risultato finale e l'output è definito dal numero di neuroni.

  • Livello di output che restituisce l'output finale della rete neurale. Se stiamo eseguendo una semplice classificazione binaria o regressione, il livello di output avrà solo 1 neurone (in modo che restituisca solo 1 numero). Nel caso di una classificazione multiclasse con 5 classi diverse, il livello di output deve avere 5 neuroni.

La forma più semplice di ANN è il Perceptron , un modello con un solo strato, molto simile al modello di regressione lineare. Chiedere cosa succede all'interno di un Perceptron equivale a chiedere cosa succede all'interno di un singolo nodo di una Rete Neurale multistrato… scomponiamolo.

Supponiamo di avere un set di dati di N righe, 3 funzioni e 1 variabile di destinazione (ovvero 1/0) binario:


set di dati di N righe, 3 feature e 1 variabile di target
set di dati di N righe, 3 feature e 1 variabile di target


Proprio come in ogni altro caso d'uso di apprendimento automatico, addestreremo un modello per prevedere l'obiettivo utilizzando le funzionalità riga per riga.


Partiamo dalla prima riga
Partiamo dalla prima riga

Cosa significa "Creare un modello"?

Cercare dei migliori parametri in una formula matematica che minimizzi l'errore delle tue previsioni. Nei modelli di regressione (es. regressione lineare) bisogna trovare i pesi migliori, nei modelli ad albero (es. random forest) si tratta di trovare i migliori punti di splitting...

Si inizializzano dei pesi w1, w2, w3
Si inizializzano dei pesi w1, w2, w3


Di solito, i pesi vengono inizializzati in modo casuale e quindi modificati man mano che l'apprendimento procede. Qui li imposterò tutti come 1:

adesso abbiamo semplicemente assegnato a w1,w2,w3 = 1 e successivamente fatto x1*w1 + x2*w2 + x3*w3
adesso abbiamo semplicemente assegnato a w1,w2,w3 = 1 e successivamente fatto x1*w1 + x2*w2 + x3*w3

Finora non abbiamo fatto nulla di diverso da una regressione lineare (che è abbastanza semplice da capire per l'azienda). Ora, passiamo da un modello lineare

Σ( xi*wi )=Y ad uno non lineare f( Σ(xi*wi) )=Y … entra nella funzione di attivazione.


Ora, passiamo da un modello lineare  Σ( xi*wi )=Y ad uno non lineare f( Σ(xi*wi) )=Y
Ora, passiamo da un modello lineare Σ( xi*wi )=Y ad uno non lineare f( Σ(xi*wi) )=Y

La funzione di attivazione definisce l'output di quel nodo. Ce ne sono molte e si possono anche creare alcune funzioni personalizzate, puoi trovare i dettagli nella documentazione ufficiale e dare un'occhiata a questo cheat sheet . Se impostassimo una semplice funzione lineare nel nostro esempio, non avremmo alcuna differenza da un modello di regressione lineare.


Funzione identità
Funzione identità

Ma noi invece useremo una funzione di attivazione del passo binario che restituisce solo 1 o 0:


Funzione passo binario
Funzione passo binario


Mettiamo insieme tutto quello che abbiamo visto fin ora
Mettiamo insieme tutto quello che abbiamo visto fin ora

Abbiamo l'output del nostro Perceptron, una rete neurale a strato singolo che prende alcuni input e restituisce 1 output. Ora l'addestramento del modello continuerebbe confrontando l'output con l'obiettivo, calcolando l'errore e ottimizzando i pesi, ripetendo l'intero processo ancora e ancora.


Ecco cosa accade dentro un neurone
Ecco cosa accade dentro un neurone

Ed ecco la rappresentazione comune di un neurone:


 rappresentazione comune di un neurone
rappresentazione comune di un neurone


Reti neurali profonde

Si potrebbe dire che tutti i modelli di Deep Learning sono Reti Neurali ma non tutte le Reti Neurali sono modelli di Deep Learning. In generale, l'apprendimento "profondo" si applica quando l'algoritmo ha almeno 2 livelli nascosti (quindi 4 livelli in totale inclusi input e output).

Immagina di replicare il processo del neurone 3 volte contemporaneamente: poiché ogni nodo (somma pesata e funzione di attivazione) restituisce un valore, avremmo il primo strato nascosto con 3 output.


replicare il processo del neurone 3 volte
replicare il processo del neurone 3 volte

Ora ripetiamo usando quei 3 output come input per il secondo livello nascosto , che restituisce 3 nuovi numeri. Infine, aggiungeremo un livello di output (solo 1 nodo) per ottenere la previsione finale del nostro modello.


3 output come input per il secondo livello nascosto , che restituisce 3 nuovi numeri. Infine, aggiungeremo un livello di output
3 output come input per il secondo livello nascosto , che restituisce 3 nuovi numeri. Infine, aggiungeremo un livello di output

Ricorda che i livelli possono avere un diverso numero di neuroni e una diversa funzione di attivazione, e in ogni nodo vengono allenati dei pesi per ottimizzare il risultato finale. Ecco perché più livelli aggiungi, maggiore è il numero di parametri addestrabili.

Ora puoi rivedere il processo completo di una rete neurale, Ti consigliamo di prenderti un paio di minuti e osservare più e più volte.


Ora puoi rivedere il processo completo di una rete neurale, Ti consigliamo di prenderti un paio di minuti e osservare più e più volte.
Ora puoi rivedere il processo completo di una rete neurale, Ti consigliamo di prenderti un paio di minuti e osservare più e più volte.

Si prega di notare che, per mantenerlo il più semplice possibile, non ho menzionato alcuni dettagli che potrebbero non essere di interesse per il Business, ma di cui uno scienziato dei dati dovrebbe assolutamente essere a conoscenza. In particolare:

  • Bias : all'interno di ogni neurone, la combinazione lineare di input e pesi include anche un bias, simile alla costante in un'equazione lineare, quindi la formula completa di un neurone è

f( (Xi * Wi ) + bias )


  • Backpropagation : durante l'addestramento, il modello apprende propagando nuovamente l'errore nei nodi e aggiornando i parametri (pesi e bias) per ridurre al minimo la perdita.



  • Gradient Descent : l'algoritmo di ottimizzazione utilizzato per addestrare le reti neurali che trova il minimo locale della funzione di perdita effettuando ripetuti passaggi nella direzione della discesa più ripida.



Progettazione del modello di Deep Learning

Il modo più semplice per costruire una rete neurale con TensorFlow è con la classe Sequential di Keras. Usiamolo per creare il Perceptron del nostro esempio precedente, quindi un modello con un solo strato Denso. È lo strato più elementare in quanto fornisce tutti i suoi input a tutti i neuroni, ciascuno dei quali fornisce un output.


model = models.Sequential(name="Perceptron", layers=[    layers.Dense(             #a fully connected layer
          name="dense",
          input_dim=3,        #with 3 features as the input
          units=1,            #and 1 node because we want 1 output
          activation='linear' #f(x)=x
    )
])
model.summary()

Output codice sopra scritto
Output codice sopra scritto

La funzione di riepilogo fornisce un'istantanea della struttura e delle dimensioni (in termini di parametri da addestrare). In questo caso, abbiamo solo 4 (3 pesi e 1 inclinazione), quindi è piuttosto leggero.

Se vuoi usare una funzione di attivazione che non è già inclusa in Keras , come la funzione di step binario che ho mostrato nell'esempio visivo, devi sporcarti le mani con TensorFlow grezzo :


# definiamo la funzione
import tensorflow as tfdef binary_step_activation(x):
    ##return 1 if x>0 else 0 
    return K.switch(x>0, tf.math.divide(x,x), tf.math.multiply(x,0))
# build the model
model = models.Sequential(name="Perceptron", layers=[
      layers.Dense(             
          name="dense",
          input_dim=3,        
          units=1,            
          activation=binary_step_activation
      )
])

Ora proviamo a passare dal Perceptron a una rete neurale profonda. Probabilmente ti starai facendo alcune domande:

  1. Quanti strati devo usare? La risposta giusta è "provare diverse varianti e vedere cosa funziona". Di solito lavoro con 2 livelli nascosti Dense con Dropout, una tecnica che riduce l'overfitting impostando casualmente gli input a 0. I livelli nascosti sono utili per superare la non linearità dei dati, quindi se non hai bisogno di non linearità puoi evitare strati nascosti. Troppi livelli nascosti porteranno al sovradattamento.


provare diverse varianti e vedere cosa funziona
provare diverse varianti e vedere cosa funziona


  1. Quanti neuroni? Il numero di neuroni nascosti dovrebbe essere compreso tra la dimensione del livello di input e la dimensione del livello di output. La mia regola pratica è (numero di ingressi + 1 uscita)/2 .

  2. Quale funzione di attivazione? Ce ne sono tanti e non si può dire che uno sia assolutamente migliore. Comunque la più usata è ReLU , una funzione lineare a tratti che restituisce l'output solo se è positivo, ed è usata principalmente per i layer nascosti. Inoltre, il livello di output deve avere un'attivazione compatibile con l'output previsto. Ad esempio, la funzione lineare è adatta per problemi di regressione mentre il Sigmoide è spesso utilizzato per la classificazione.



Presumo un set di dati di input di N funzionalità e 1 variabile di destinazione binaria (molto probabilmente un caso d'uso di classificazione).


n_features = 10model = models.Sequential(name="DeepNN", layers=[
    ### hidden layer 1
    layers.Dense(name="h1", input_dim=n_features,
                 units=int(round((n_features+1)/2)), 
                 activation='relu'),
    layers.Dropout(name="drop1", rate=0.2),
    
    ### hidden layer 2
    layers.Dense(name="h2", units=int(round((n_features+1)/4)), 
                 activation='relu'),
    layers.Dropout(name="drop2", rate=0.2),
    
    ### layer output
    layers.Dense(name="output", units=1, activation='sigmoid')
])
model.summary()


Si prega di notare che la classe Sequential non è l'unico modo per costruire una rete neurale con Keras . La classe Model offre maggiore flessibilità e controllo sui livelli e può essere utilizzata per creare modelli più complessi con più input/output. Ci sono due grandi differenze:

  • Il layer Input deve essere specificato mentre nella classe Sequential è implicito nella dimensione di input del primo layer Dense.

  • I layer vengono salvati come oggetti e possono essere applicati agli output di altri layer come: output = layer(…)(input)


Ecco come puoi usare la classe Model per costruire il nostro Perceptron e DeepNN:

# Perceptron
inputs = layers.Input(name="input", shape=(3,))
outputs = layers.Dense(name="output", units=1, 
                       activation='linear')(inputs)
model = models.Model(inputs=inputs, outputs=outputs, 
                     name="Perceptron")
# DeepNN
### layer input
inputs = layers.Input(name="input", shape=(n_features,))### hidden layer 1
h1 = layers.Dense(name="h1", units=int(round((n_features+1)/2)), activation='relu')(inputs)
h1 = layers.Dropout(name="drop1", rate=0.2)(h1)### hidden layer 2
h2 = layers.Dense(name="h2", units=int(round((n_features+1)/4)), activation='relu')(h1)
h2 = layers.Dropout(name="drop2", rate=0.2)(h2)### layer output
outputs = layers.Dense(name="output", units=1, activation='sigmoid')(h2)model = models.Model(inputs=inputs, outputs=outputs, name="DeepNN")

Si può sempre verificare se il numero di parametri nel riepilogo del modello è lo stesso di quello di Sequential .


Visualizzare i modelli di apprendimento profondo

Ricorda, stiamo raccontando una storia al business e la visualizzazione è il nostro miglior alleato. Ho preparato una funzione per tracciare la struttura di una rete neurale artificiale dal suo modello TensorFlow , ecco il codice completo:

Proviamolo sui nostri 2 modelli, prima il Perceptron usando questa semplice riga:


visualize_nn (model, description=True, figsize=(10,8))

Visualizzare i modelli di apprendimento profondo
Visualizzare i modelli di apprendimento profondo


quindi la rete neurale profonda:

Visualizzare i modelli di apprendimento profondo
Visualizzare i modelli di apprendimento profondo

TensorFlow fornisce anche uno strumento per tracciare la struttura del modello, potresti volerlo usare per reti neurali più complesse con livelli più complicati (CNN, RNN, …). Usando questa riga di codice potrai visualizzare la struttura della rete.



utils.plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)

Come visualizzare la struttura di una rete neurale
Come visualizzare la struttura di una rete neurale

E adesso Vediamo come Allenare e Valutare le nostre reti neurali

Infine, è il momento di addestrare il nostro modello di Deep Learning. Affinché possa funzionare, dobbiamo "compilare", o per dirla in un altro modo, dobbiamo definire l' ottimizzatore, la funzione di perdita e le metriche . Di solito uso l' ottimizzatore Adam , un algoritmo di ottimizzazione sostitutivo per la discesa del gradiente (il migliore tra gli ottimizzatori adattivi). Gli altri argomenti dipendono dal caso d'uso.

Nei problemi di classificazione (binaria), è necessario utilizzare una perdita di entropia incrociata ( binaria) che confronta ciascuna delle probabilità previste con l'output effettivo della classe. Per quanto riguarda le metriche, mi piace monitorare sia la Precisione che il punteggio F1, una metrica che combina Precisione e Richiamo (quest'ultimo deve essere implementato in quanto non è già incluso in TensorFlow ).



# define metriche
 def Recall(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
    recall = true_positives / (possible_positives + K.epsilon())
    return recall

def Precision(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
    precision = true_positives / (predicted_positives + K.epsilon())
    return precision

def F1(y_true, y_pred):
    precision = Precision(y_true, y_pred)
    recall = Recall(y_true, y_pred)
    return 2*((precision*recall)/(precision+recall+K.epsilon()))

# compila la rete neurale
model.compile(optimizer='adam', loss='binary_crossentropy', 
              metrics=['accuracy',F1])

D'altra parte, nei problemi di regressione, di solito imposto il MAE come perdita e R-quadrato come metrica.


# definisci le metriche
def R2(y, y_hat):
    ss_res =  K.sum(K.square(y - y_hat)) 
    ss_tot = K.sum(K.square(y - K.mean(y))) 
    return ( 1 - ss_res/(ss_tot + K.epsilon()) )
# compile the neural network
model.compile(optimizer='adam', loss='mean_absolute_error', 
              metrics=[R2])



Prima di iniziare l'addestramento, dobbiamo anche decidere le Epoche e i Batch : poiché il set di dati potrebbe essere troppo grande per essere elaborato tutto in una volta, viene suddiviso in batch (maggiore è la dimensione del batch, maggiore è lo spazio di memoria necessario). La backpropagation ed il conseguente aggiornamento dei parametri avviene ad ogni batch. Un'epoca è un passaggio sull'intero set di allenamento. Quindi, se hai 100 osservazioni e la dimensione del batch è 20, ci vorranno 5 batch per completare 1 epoca. La dimensione del batch dovrebbe essere un multiplo di 2 (comune: 32, 64, 128, 256) perché i computer di solito organizzano la memoria con una potenza di 2. Tendo a iniziare con 100 epoche con una dimensione del batch di 32.

Durante l'addestramento, ci aspetteremmo di vedere le metriche migliorare e la perdita diminuire epoca per epoca. Inoltre, è buona norma conservare una parte dei dati (20%-30%) per la convalida. In altre parole, il modello metterà da parte questa frazione di dati per valutare la perdita e le metriche alla fine di ogni epoca, al di fuori dell'addestramento. Supponendo che tu abbia i tuoi dati pronti in alcuni array X e y (in caso contrario puoi semplicemente generare dati casuali come



import numpy as np
X = np.random.rand(1000,10)
y = np.random.choice([1,0], size=1000)


è possibile avviare e visualizzare la formazione come segue:




# train/validation
training = model.fit(x=X, y=y, batch_size=32, epochs=100, shuffle=True, verbose=0, validation_split=0.3)
# plot
metrics = [k for k in training.history.keys() if ("loss" not in k) and ("val" not in k)]    
fig, ax = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(15,3))
       
## training    
ax[0].set(title="Training")    
ax11 = ax[0].twinx()    
ax[0].plot(training.history['loss'], color='black')    ax[0].set_xlabel('Epochs')    
ax[0].set_ylabel('Loss', color='black')    
for metric in metrics:        
    ax11.plot(training.history[metric], label=metric)    ax11.set_ylabel("Score", color='steelblue')    
ax11.legend()
        
## validation    
ax[1].set(title="Validation")    
ax22 = ax[1].twinx()    
ax[1].plot(training.history['val_loss'], color='black')    ax[1].set_xlabel('Epochs')    
ax[1].set_ylabel('Loss', color='black')    
for metric in metrics:          
    ax22.plot(training.history['val_'+metric], label=metric)    ax22.set_ylabel("Score", color="steelblue")    
plt.show()


Questi grafici sono presi da due casi d'uso reali che confrontano algoritmi di apprendimento automatico standard con reti neurali.


Vediamo ora come Spiegare il nostro Modello

Abbiamo addestrato e testato il nostro modello, ma non abbiamo ancora convinto il Business dei risultati… cosa possiamo fare? Facile, costruiamo una spiegazione per mostrare che il nostro modello di Deep Learning non è una scatola nera.

Trovo che Shap funzioni molto bene con le Reti Neurali: per ogni previsione, è in grado di stimare il contributo di ogni caratteristica al valore previsto dal modello. Fondamentalmente, risponde alla domanda " perché il modello dice che questo è un 1 e non uno 0? ”. Puoi usare il seguente codice:



def explainer_shap(model, X_names, X_instance, X_train=None, task="classification", top=10):


## create explainer
### machine learning 

if X_train is None: 
	explainer=shap.TreeExplainer(model)
	shap_values=explainer.shap_values(X_instance)

### deep learning

else:
	explainer=shap.DeepExplainer(model, data=X_train[:100])
	shap_values=explainer.shap_values(X_instance.reshape(1,-1))[0].reshape(-1)

## plot

### classification

if task=="classification":
	shap.decision_plot(explainer.expected_value, shap_values, link='logit', feature_order='importance',features=X_instance, feature_names=X_names, feature_display_range=slice(-1,-top-1,-1))

### regression
else:
	shap.waterfall_plot(explainer.expected_value[0], shap_values, features=X_instance, feature_names=X_names, max_display=top)

Tieni presente che questa funzione può essere eseguita anche su altri modelli di Machine Learning (ad es. Regressione lineare, foresta casuale), non solo sulle reti neurali. Come puoi leggere dal codice, se l' argomento X_train viene mantenuto come None, la mia funzione presuppone che non sia Deep Learning.

Proviamolo sugli esempi di classificazione e regressione:



i = 1
explainer_shap(model, 
               X_names=list_feature_names, 
               X_instance=X[i], 
               X_train=X, 
               task="classification", #task="regression"
               top=10)


Esempio di classificazione.
Esempio di classificazione.


Esempio di regressione.
Esempio di regressione.

Abbiamo Finito


Scarica GRATUITAMENTE il pdf dell'articolo sulle reti NEURALI e PYTHON


Questo articolo è stato un tutorial per dimostrare come progettare e costruire reti neurali artificiali, profonde e non.

Nella seconda parte del tutorial, abbiamo utilizzato TensorFlow per creare alcune reti neurali, dal Perceptron a una più complessa. Quindi, abbiamo addestrato il modello di Deep Learning e valutato la sua spiegabilità sia per i casi d'uso di classificazione che di regressione. Speriamo davvero ti sia piaciuto! Commenta o condividi il Post.


Altro sulle reti neurali :



Vuoi mettere in pratica quello che hai letto ?

Oppure segui i tutorial correlati :