Subtotale: €364,30 (IVA incl.)
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).
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.
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.
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.
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ร .
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.
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.