Implementazione di Reti Neurali su Arduino: Panoramica, Modelli e Applicazioni Pratiche

Facciamo una panoramica sulle Reti Neurali, sui modelli che le sostengono e valutiamone lโ€™implementazione attraverso la piattaforma Arduino.

Dopo aver introdotto il concetto di Intelligenza Artificiale nellโ€™articolo sui sensori pubblicato nel fascicolo precedente, vediamo come applicare le tecniche di AI allโ€™hardware Arduino, Raspberry Pi ed anche ai tradizionali microcontrollori.

La necessitร  di queste tecniche nasce dalla considerazione che la programmazione classica diventa impraticabile quando si ha a che fare con molti input sensoriali di tipo analogico (ovvero che forniscono valori continui) e con attuatori non di tipo ON/OFF come motori e servocomandi; in questi casi รจ conveniente orientarsi verso Reti Neurali.

In queste pagine cerchiamo di chiarire cosa sono e come possono essere utilizzate in un hardware minimale come Arduino. Le utilizzeremo a cominciare da un semplice sistema di โ€œobstacle avoidanceโ€ ossia di movimento evitando ostacoli e rimanendo in perimetri definiti. Per questo esempio utilizzeremo la piattaforma robotica โ€œArdusumoโ€ ma nulla vieta di impiegare altri robot semoventi come โ€œAlphabotโ€.

Reti Neurali

Le Reti Neurali traggono origine dalla definizione del Percettrone, che Frank Rosenblatt creรฒ nel 1958 ispirandosi al funzionamento dei neuroni.

La loro applicabilitร  risale perรฒ allโ€™introduzione delle funzioni di trasferimento non lineari e dellโ€™algoritmo di correzione degli errori โ€œError Back Propagationโ€ (Rumelhart and McClelland, 1986) riconducibili agli anni โ€œottantaโ€ del secolo scorso.

Delle Artificial Neural Network (ANN) o piรน brevemente NN, รจ poi nata una branca chiamata โ€œconnessionismoโ€. Il nome รจ abbastanza autoesplicativo, in quanto la tecnica delle NN prevede una computazione distribuita su diversi nodi interconnessi.

Una Rete Neurale รจ composta, in genere, da almeno tre strati di nodi connessi in catena: il primo รจ sostanzialmente un semplice buffer di input, mentre il terzo realizza un buffer di output. In realtร , poichรฉ lo strato di input non ha una funzione che non sia quella di propagare lโ€™input verso il primo vero strato elaboratore, molto spesso le reti di questo tipo vengono chiamate reti a due soli strati. Venendo al nostro esempio con โ€œArdusumoโ€, il primo strato raccoglie gli input sensoriali e lโ€™ultimo lโ€™azionamento dei motori (Fig. 1). In mezzo cโ€™รจ un importantissimo strato, in genere chiamato strato nascosto (hidden) perchรฉ non รจ in rapporto diretto con lโ€™esterno.

Naturalmente esistono versioni di NN molto piรน complesse (Fig. 2), con piรน strati intermedi, con collegamenti inter-strato (convolution NN) o con buffer di memoria temporanea per la simulazione di sistemi dinamici, in cui lo stato precedente ha la sua importanza (recurrent NN); per il momento ci riferiremo alla versione base delle NN, che puรฒ giร  rappresentare un ottimo approssimatore universale di funzioni (in->out).

Fig. 1 Semplice rete neurale utilizzata per pilotare Ardusumo.

 

Fig. 2 Alcune tipologie di NN.

 

Lโ€™elemento fondamentale della rete neurale รจ il nodo. Questo raccoglie lโ€™output dei nodi dello strato precedente ognuno moltiplicato per il peso del link corrispondente.

Nella struttura classica tutti questi valori vengono sommati a formare un valore, chiamato attivazione del nodo, che viene passato ad una funzione di trasferimento che fornisce lโ€™output effettivo del nodo.

Questo output sarร  propagato ai nodi dello strato successivo. Lo scopo della funzione di trasferimento (chiamata anche funzione di attivazione) รจ cruciale. Infatti proprio lโ€™adozione di funzioni non lineari ha permesso alle reti neurali di diventare degli approssimatori universali, in quanto, insieme al metodo intelligente di correzione degli errori, permettono di trasformare lo spazio dei valori di input in uno spazio fittizio che puรฒ risolvere qualunque associazione input->output, a patto di avere un numero adeguato di nodi intermedi.

Per permettere questa proiezione da uno spazio vettoriale ad un altro, la funzione di attivazione รจ in genere di tipo โ€œsquashingโ€, cioรจ comprime lโ€™output entro valori massimi e minimi, in modo da costringere i pesi delle connessioni ad evolvere in modo complesso.

Le funzioni di attivazione che sono state proposte sono molte, ma le piรน utilizzate sono quella sigmoide (detta anche logistica) (1/(1+exp(-x)), la tangente iperbolica e quella rettificatrice (ReLU), insieme ovviamente a quella lineare unitaria utile in certe situazioni (Fig. 3).

Le reti neurali sono un sistema Feed Forward (in avanti dallโ€™input allโ€™output) ad apprendimento supervisionato, ovvero vengono inizialmente costruite con pesi casuali nelle connessioni e poi addestrate tramite un numero consistente di coppie di valori input->output. In questo modo finiscono per approssimare, interpolare ed estrapolare una funzione che lega un input multidimensionale ad un output multidimensionale anche in modo complesso; in sostanza, compiono una sorta di regressione statistica multivariata.

Il meccanismo รจ noto con il nome di Error Back Propagation (EBP); in pratica, a partire dallโ€™output prodotto spontaneamente dalla rete in seguito a certi valori di input, si determina lโ€™errore quadratico commesso da ogni nodo di output e si determina la sua derivata in funzione dei singoli pesi afferenti al nodo. A questo punto si correggono i pesi applicando ad essi una variazione proporzionale allโ€™opposto della derivata cosรฌ calcolata.

Lโ€™efficacia del metodo sta nel fatto che lโ€™errore quadratico si puรฒ riportare indietro allo strato precedente applicando la regola della catena delle derivate; in questo modo si aggiornano i pesi di tutte le connessioni ottimizzando lโ€™errore quadratico medio della rete complessiva.

Non รจ garantito il raggiungimento dellโ€™ottimo assoluto, ma sicuramente lโ€™errore scende fino a quando puรฒ fermarsi a un valore minimo. Se il numero dei nodi di input รจ stabilito dal numero delle grandezze di ingresso e quello dei nodi di output รจ definito dalle grandezze di uscita, i nodi dello strato intermedio sono da decidere, in fase progettuale, un poโ€™ per esperienza, un poโ€™ in funzione della complessitร  della funzione da approssimare ed un poโ€™ per tentativi.

Troppo pochi possono non far scendere lโ€™errore in modo soddisfacente, mentre troppi possono portare la rete a non sintetizzare in modo efficiente il legame input->output che si vuole approssimare. Una volta addestrata la rete puรฒ essere utilizzata, per eseguire la funzionalitร  che ha imparato, fornendo un input e rilevando lโ€™output.

Cโ€™รจ da notare che non si tratta di ripetere gli esempi, ma di poter contare sulla generalizzazione di una funzione multidimensionale anche complessa, appresa a partire da un numero limitato di esempi prototipali.

Fig. 3 Nodo e funzioni di attivazione comuni.

Tabella 1 Funzioni della libreria NNet.

Libreria NNet per Arduino

Benchรฉ esista in Internet qualche esempio di libreria di reti neurali per Arduino, si รจ preferito svilupparne una per cercare di renderla molto leggera e di facile utilizzo. La libreria occupa meno di una decina di kilobyte ed รจ abbastanza veloce.

La libreria inoltre puรฒ funzionare su Arduino ma anche su un PC; infatti esiste un โ€œdefineโ€ che, disabilitato, permette alcune funzionalitร  in genere non disponibili su Arduino, come il salvataggio della rete o lโ€™output su file.

La libreria permette di definire reti a tre strati con numero di nodi a piacere. Il primo strato (Layer 0) ha il compito di ricevere i valori di input ed รจ in realtร  fittizio perchรฉ ha il solo scopo di propagare questi valori allo strato successivo tramite le sue connessioni ad esso. Il secondo strato (Layer 1), puรฒ avere una funzione di attivazione scelta fa quelle disponibili (NodeLin, NodeSigm, NodeTanh, NodeReLU).

Infine il terzo strato (Layer 2) รจ quello da cui si preleva lโ€™output e puรฒ avere anchโ€™esso una funzione di attivazione scelta fra quelle disponibili. I nodi vengono tutti collegati automaticamente con quelli dello strato successivo con pesi casuali molto piccoli.

La libreria permette anche di caricare una rete giร  addestrata e quindi con i valori dei pesi ben definiti. Una funzione di โ€œLearnโ€ permette di addestrare la rete ed una funzione di โ€œForwardโ€ permette di utilizzarla quando รจ addestrata.

Infine sono presenti funzioni di stampa per salvare/evidenziare la struttura della rete. Nella Tabella 1 vedete le funzioni della libreria.

Esempio con la funzione XOR

Un modo classico per evidenziare il funzionamento delle reti neurali Feed Forward รจ quello di addestrarli a simulare la funzione XOR (Fig. 4).

La scelta di questa funzione, che รจ comunque non analogica, si basa sul fatto che รจ una funzione non lineare degli input.

Ovvero, non cโ€™รจ modo di separare i risultati 0 ed 1 dai valori di input con un semipiano.

Nella libreria รจ compreso questo esempio che puรฒ essere lanciato su Arduino.

Dopo un paio di migliaia di presentazioni di esempi (qualche secondo su Arduino), la rete risponde correttamente. Dalla Fig. 5 si vede in che modo una rete neurale puรฒ essere pensata come un sistema che proietta spazi su spazi, anche deformandoli.

Fig. 4 Rete Neurale per funzione XOR.

Fig. 5 Interpretazione geometrica del funzionamento della rete oer lo XOR.

Rete Neurale per pilotare Ardusumo

Ardusumo (Fig. 6) รจ un rover che puรฒ contare fra lโ€™altro su due sensori di distanza a infrarossi con una portata da 40 cm a 5 cm circa e su due motori che comandano due ruote, la cui potenza viene regolata con il sistema PWM attraverso il classico circuito a ponte TB6612FNG.

I due sensori IR (orientati a circa ยฑ30ยฐ rispetto alla direzione di marcia) forniscono un segnale inversamente proporzionale alla distanza come descritto dalla Fig. 7.

Fig. 6 Il robot Ardusumo.

Fig. 7 Risposta del sensore ad infrarosso Sharp 2D120X.

 

Abbiamo quindi due sensori analogici e due motori pilotabili con continuitร  per andare indietro, curvare o procedere in avanti. Come potremmo fare a pilotare il rover con lโ€™esigenza di fargli evitare gli ostacoli? Una prima possibilitร  รจ quella di utilizzare delle soglie di distanza e dei livelli di potenza discreti. Poi in base a questi valori digitali costruire una strategia basata sostanzialmente in una โ€œlook tableโ€ o una serie di โ€œifโ€.

Unโ€™alternativa piรน elegante รจ utilizzare direttamente i livelli analogici per esempio mediante una struttura in logica โ€œFuzzyโ€, oppure, come in questo esempio, con una rete neurale. La struttura utilizzata รจ una rete con 2 nodi di input, 3 nodi hidden e 2 nodi di output.

La funzione di attivazione dello strato hidden รจ la tangente iperbolica, cosi come per lo strato di output (piรน che altro per mantenere lโ€™escursione della potenza dei motori tra -1 e +1). Lโ€™input riceve direttamente i valori di tensione normalizzati (0 -:- 1); mentre lโ€™output viene scalato moltiplicandolo per 255 (massimo valore PWM). Quindi lโ€™istanza della rete sarร :

NNet net(2,3,โ€NodeTanhโ€,2,โ€NodeTanhโ€);

Ora sorge il problema di come addestrarla e allo scopo ci sarebbero due modi:
1. per imitazione, vale a dire pilotandola con un comando remoto mentre apprende;
2. fornendo una serie di esempi prototipali studiati per coprire le situazioni principali.

Proveremo ad utilizzare il secondo, riferendoci alla Tabella 2, dove sono elencati i 16 esempi di addestramento. Essi non devono essere in ordine, anzi รจ sempre opportuno sottoporre gli esempi scegliendoli a caso dalla lista.

Dopo alcune decine di migliaia di cicli di addestramento (pochi secondi su Arduino, e quasi istantaneamente su PC), si ottiene un errore quadratico medio abbastanza basso.

A questo punto potrebbe sorgere la domanda: quando la rete puรฒ considerarsi addestrata? Questo รจ un punto delicato per le reti neurali.

Se cโ€™รจ una misura dellโ€™errore che puรฒ discriminare in maniera effettiva, allora basta mettere una soglia e fermarsi al raggiungimento di questโ€™ultima.

Ma in molti casi questo non รจ possibile, allora si puรฒ ricorrere allo stratagemma di preparare un secondo set di esempi differenti; presentarli alla rete per lโ€™elaborazione e vedere se i risultati corrispondono a quelli aspettati. In alternativa รจ possibile verificare direttamente la funzione appresa se, come in questo (raro) caso, รจ possibile graficarla in virtรน della ridotta dimensionalitร .

ย Tabella 2 Esempi di training per Ardusumo.

 

Per realizzare i grafici si รจ alimentata la rete addestrata con valori consecutivi per lโ€™input sinistro e destro e si sono raccolti i valori di output (per praticitร  รจ stato utilizzato un PC).

In Fig. 8 sono visualizzate le superfici corrispondenti ai valori per i motori sinistro e destro in funzione dei valori caricati sugli input.

In Fig. 9 sono invece mostrate le stesse superfici ma in funzione delle distanze (che sono inversamente proporzionali ai valori di tensione).

La semplice rete di Fig. 10 ha prodotto funzioni continue complesse difficilmente realizzabili con altri sistemi; anche questo esempio รจ incluso nella libreria, sotto forma di rete addestrata. Ma ovviamente รจ possibile addestrare la rete con diversi o piรน numerosi esempi ottenendo comportamenti che possono essere anche piรน efficienti.

La libreria NNet รจ scaricabile dal sito https://github.com/open-electronics/Artificial_Intelligence. Dal sito รจ possibile anche scaricare un breve filmato di Ardusumo pilotato dalla rete neurale, cosรฌ da evidenziare il tipo di funzionalitร  messa in atto concretamente dalla rete.

Fig. 8 Valori del motore sinistro e destro in funzione della tensione.

Fig. 9 Valore del motore sinistro e destro in funzione delle distanze.

 

Fig. 10 Rete addestrata che pilota Ardusumo.

Dimensioni di reti possibili su Arduino Uno

Vediamo ora quali dimensioni di reti possiamo caricare su un Arduino Uno.

La libreria Neural Network occupa meno di 8Kbyte, quindi solo circa il 22% della memoria di programma di Arduino Uno.

Il problema รจ la memoria di lavoro, sulla quale va definita la rete e le altre variabili del programma, che รจ molto scarsa. In pratica non possiamo allocare piรน di circa 300 parametri (nodi + link).

In termini concreti possiamo creare al massimo una rete di 6 input, 24 nodi hidden e 8 nodi di output; oppure 10 nodi di input, 14 nodi hidden e 10 nodi di output; oppure 2 nodi di input, 50 nodi hidden e 2 nodi di output; o qualunque altra combinazione intermedia.

Va comunque tenuto presente che con queste dimensioni massime rimane pochissimo spazio per altre eventuali variabili di programma.

Conclusioni

In questo articolo vi abbiamo spiegato come sia possibile utilizzare la tecnologia delle reti neurali anche con Arduino. Al prezzo di un certo lavoro nel definire la rete e gli esempi da sottoporre per lโ€™addestramento e, soprattutto, a fronte di qualche difficoltร  nel definire il completamento dellโ€™addestramento, si possono utilizzare semplici e rapidissime strutture che, una volta addestrate, ricreano complesse funzioni multidimensionali continue.

Lascia un commento

Il tuo indirizzo email non sarร  pubblicato.

Menu