Neuro.dll
Esempi
di implementazione.
La libreria, in questa versione, deve essere utilizzata in modalità "run time", quindi utilizzando le funzioni LoadLibrary e GetProcAddress di Windows. Includere le seguenti dichiarazioni:
#include <windows.h> typedef short int INTG; typedef float REAL; typedef void NET; /* Che significa?!? Che ci serve solo il puntatore... */ NET* (FAR PASCAL *GenerateNetwork)(INTG nlayers, INTG* laycnt, REAL alpha, REAL eta, REAL gain); void (FAR PASCAL *InitializeRandoms)(INTG s); void (FAR PASCAL *RandomWeights)(NET* Net); void (FAR PASCAL *SetInput)(NET* Net, REAL* Input); void (FAR PASCAL *GetOutput)(NET* Net, REAL* Output); REAL (FAR PASCAL *GetError)(NET* Net); void (FAR PASCAL *SetParam)(NET* Net, REAL alpha, REAL eta, REAL gain); void (FAR PASCAL *SaveNet)(NET* Net, char* FileName); NET* (FAR PASCAL *LoadNet)(char* FileName); void (FAR PASCAL *SimulateNet)(NET* Net, REAL* Input, REAL* Output, REAL* Target, INTG Training); void (FAR PASCAL *Informations)(void);
All'interno della funzione che carica la libreria (il main di solito...) includere:
/* Dichiarare queste variabili */ HINSTANCE hLib; NET *net; hLib = LoadLibrary("Neuro.dll"); if (hLib!=NULL) { printf("\nLibreria trovata e caricata..."); /* questo è opzionale ovviamente */ GenerateNetwork =(NET* (FAR PASCAL *)(INTG, INTG*, REAL, REAL, REAL)) GetProcAddress(hLib, "GenerateNetwork"); InitializeRandoms =(void (FAR PASCAL *)(INTG)) GetProcAddress(hLib, "InitializeRandoms"); RandomWeights =(void (FAR PASCAL *)(NET*)) GetProcAddress(hLib, "RandomWeights"); SetInput =(void (FAR PASCAL *)(NET*, REAL*)) GetProcAddress(hLib, "SetInput"); GetOutput = (void (FAR PASCAL *)(NET*, REAL*))GetProcAddress(hLib, "GetOutput"); GetError = (REAL (FAR PASCAL *)(NET*))GetProcAddress(hLib, "GetError"); SetParam = (void (FAR PASCAL *)(NET*, REAL, REAL, REAL)) GetProcAddress(hLib, "SetParam"); SaveNet = (void (FAR PASCAL *)(NET*, char*)) GetProcAddress(hLib, "SaveNet"); LoadNet = (NET* (FAR PASCAL *)(char*)) GetProcAddress(hLib, "LoadNet"); SimulateNet = (void (FAR PASCAL *)(NET*, REAL*, REAL*, REAL*, INTG)) GetProcAddress(hLib, "SimulateNet"); Informations = (void (FAR PASCAL *)(void)) GetProcAddress(hLib, "Informations"); } else printf("\nLibreria non trovata!!!"); /* anche questo è opzionale... */
A questo punto le funzioni possono essere
chiamate normalmente.
Ad esempio:
net = GenerateNetwork(3, layers, 0, 0.5, 1); /* genera la rete */ RandomWeights(net); /* inizializza i pesi */ SimulateNet(net, inp, outp, targ, 1); /* addestra la rete */ err = GetError(net); /* legge l'errore */
Dove (in questo caso) layers
è un array di dimensione 3 che contiene il numero di neuroni in
ognuno dei 3 strati della rete, inp,
outp e targ, sono sempre degli array di
dimensione opportuna che rappresentano gli ingressi, le uscite e
l'uscita desiderata.
Il seguente programmino dimostra l'utilizzo della
rete in C/C++, e consiste nell'addestramento della rete al fine
di farle apprendere la funzione Sen(x) nell'intervallo [0, PI].
Per provare il programma inserire tutto il codice sottostante in
una funzione o anche nel main, ed aggiungere
opzionalmente le funzioni per il plotting dei dati (diverse a
secondo l'ambiente di sviluppo che si sta utilizzando...).
Il risultato è visibile, assieme ad alcuni commenti nel
paragrafo "Osservazioni
importanti".
INTG layers[3]={1,4,1}; // numero neuroni per ogni layer REAL inp[1], outp[1], targ[1]; // array di input, output e target int i; net = GenerateNetwork(3, layers, 0, 2, 1); // genera la rete RandomWeights(net); // inizializza i pesi // *** Addestramento della rete: 10000 esempi! *** for(i=0; i<10000; i++) { inp[0]=((REAL) random(100))/100; // ingresso: angolo casuale normalizz. targ[0]=sin(M_PI*inp[0]); // target: seno dell'angolo SimulateNet(net, inp, outp, targ, 1); // addestramento rete // Opzionalmente: // Plotting di inp[0]: ingresso rete // Plotting di outp[0]: uscita rete // Plotting di GetError(net): errore rete } // *** Simulazione della rete *** for(i=0; i<500; i++) { inp[0]=((REAL) i) / 500; // input: angolo normalizzato //targ[0]=sin(M_PI*inp[0]); // target: seno (solo per il plotting) SimulateNet(net, inp, outp, targ, 0); // Opzionalmente: // Plotting di inp[0]: ingresso rete // Plotting di outp[0]: uscita rete // Plotting di GetError(net): errore rete } |
Implementazione in Visual Basic
Per utilizzare la libreria basta dichiarare le seguenti funzioni in un modulo:
Declare Function GenerateNetwork Lib "Neuro.dll" (ByVal n As Integer, ByRef ulc As Integer, ByVal alpha As Single, ByVal eta As Single, ByVal gain As Single) As Long Declare Sub InitializeRandoms Lib "Neuro.dll" (ByVal Seed As Integer) Declare Sub RandomWeights Lib "Neuro.dll" (ByVal Net As Long) Declare Sub SetInput Lib "Neuro.dll" (ByVal Net As Long, ByRef i As Single) Declare Sub GetOutput Lib "Neuro.dll" (ByVal Net As Long, ByRef o As Single) Declare Sub SaveNet Lib "Neuro.dll" (ByVal Net As Long, ByVal FileName As String) Declare Function LoadNet Lib "Neuro.dll" (ByVal FileName As String) As Long Declare Sub SimulateNet Lib "Neuro.dll" (ByVal Net As Long, ByRef i As Single, ByRef o As Single, ByRef t As Single, ByVal tflag As Integer) Declare Function GetError Lib "Neuro.dll" (ByVal Net As Long) As Single Declare Sub Informations Lib "Neuro.dll" ()
Se la libreria risiede in un altra directory è necessario
includere l'intero percorso.
Le funzioni possono ora essere chiamate normalmente (o quasi...).
Il problema è che il VB non permette di utilizzare in maniera
esplicita i puntatori (o forse non ho ancora capito come si fa!),
per cui ho utilizzato il seguente metodo per passare gli array:
dichiarare un passaggio per riferimento (ByRef)
e passare, nella chiamata di funzione il primo elemento dell'array.
Pensavo che ci fosse un problema del genere anche per le stringhe,
che invece sembrano comportarsi bene...
Un esempio è il seguente:
' La rete creata avrà 3 layer di 4, 3 e 2 neuroni rispettivamente Dim NetPt As Long ' Puntatore alla rete Dim Nlay(2) As Integer ' Array contenente il numero di neuroni per layer Dim Inp(3) As Single ' Array di input Dim Outp(1) As Single ' Array di output Dim Targ(1) As Single ' Array di Target Dim NomeFile As String ' Stringa Nlay(0) = 4 ' Numero di neuroni in ciascun layer Nlay(1) = 3 Nlay(2) = 2 NomeFile="MiaRete.ann" ' Nome del file in cui salvare la rete NetPt = GenerateNetwork(3, Nlay(0), 0, 2, 1) ' notare il passaggio di Nlay(0) come array Call RandomWeights(NetPt) ... Call SimulateNet(NetPt, Inp(0), Outp(0), Targ(0), 1) Call SaveNet(NetPt, NomeFile)
Applicazione di esempio in VisualBasic
Il seguente programmino dimostra l'utilizzo della
rete in VisualBasic, e consiste nell'addestramento della rete al
fine di farle apprendere la funzione Sen(x) nell'intervallo [0,
PI].
Per provare il programma inserire il codice sottostante in una
funzione qualsiasi, ad esempio in Form_Click(), e
modificare opzionalmente le funzioni per il plotting (PSet)
secondo le preferenze.
Il risultato è visibile, assieme ad alcuni commenti nel
paragrafo "Osservazioni
importanti".
' Variabili relative alla rete Dim NetPt As Long ' Puntatore alla rete Dim Nlay(2) As Integer ' Array contenente il numero di neuroni per layer Dim Inp(0) As Single ' Array di input Dim Outp(0) As Single ' Array di output Dim Targ(0) As Single ' Array di Target Dim NomeFile As String ' Stringa ' Variabili ausiliarie Dim angolo As Single Dim seno As Single Dim pi As Single Dim esempi As Integer Nlay(0) = 1 ' Numero di neuroni del layer 1 (ingresso) Nlay(1) = 8 ' Numero di nueroni del layer 2 (nascosto) Nlay(2) = 1 ' Numero di neuroni del nayer 3 (output) pi = 4 * Atn(1) ' Valore del Pi greco esempi = 10000 ' Numero esempi per l'addestramento ' Creazione della rete: ' 3 layer di lunghezza Nlay(i) ' Alpha=0 ("momentum") ' Eta=2 ("learning rate") ' Gain=1 (guadagno funzione sigmoide) NetPt = GenerateNetwork(3, Nlay(0), 0, 2, 1) ' Inizializzazione casuale dei pesi Call RandomWeights(NetPt) ' *** Addestramento rete (10000 esempi!) *** For t = 0 To esempi ' Dati per l'addestramento angolo = Rnd ' angolo normalizzato seno = Sin(angolo * pi) ' seno dell'angolo ' Training set Inp(0) = angolo Targ(0) = seno ' Training Call SimulateNet(NetPt, Inp(0), Outp(0), Targ(0), 1) ' Plotting errore 'PSet (10 + 490 * t / esempi, 100 - 200 * GetError(NetPt)), vbRed 'PSet (10 + 490 * t / esempi, 200 - 50 * seno), vbGreen 'PSet (10 + 490 * t / esempi, 300 - 50 * Outp(0)), vbMagenta Next ' *** Simulazione rete *** For t = 0 To 400 ' Training set angolo = t / 400 seno = Sin(angolo * pi) ' Simulazione rete Inp(0) = angolo Call SimulateNet(NetPt, Inp(0), Outp(0), Targ(0), 0) ' Plotting funzione 'PSet (t, 350 - 50 * seno), vbGreen 'PSet (t, 350 - 50 * Outp(0)), vbCyan 'PSet (t, 350 - 50 * GetError(NetPt)), vbRed Next |
Le reti neurali sono degli oggetti complessi e difficili da
utilizzare in genere. Questa difficoltà non è dovuta soltanto
alla complessità del codice che le implementa (che in realtà è
abbastanza semplice), ma al fatto che è difficile riuscire ad
ottenere i risultati voluti. Una prima grossa difficoltà
consiste nel determinare la topologia della rete migliore per l'applicazione
che si vuole implementare: non esistono teorie riguardo alla
scelta ottima del numero di layer e del numero di neuroni per
ciascun layer. In genere si procede per esperienza, rifacendosi
ad applicazioni simili o per tentativi. Una scelta sbagliata in
questa fase può portare a pessime prestazioni o alla necessità
di addestramenti lunghisimi.
Una seconda difficoltà è data dalla scelta dei parametri della
simulazione: il coefficiente di momentum, il learning rate e il
guadagno della funzione sigmoide. Anche in questo caso si procede
di solito per esperienza o per tentatvi.
L'ultima difficoltà, forse quella più grande, è data dalla
scelta del training set e soprattutto dal modo di presentarlo
alla rete nella fase di addestramento. Se in questa fase non si
procede nel modo corretto la rete non impara affatto o impara
male.
Si possono fare inoltre diverse altre considerazioni. A tale scopo consideriamo il programma di esempio riportato sopra (apprendimento della funzione sen(x)), nel modo in cui è stato impostato si ottengono i seguenti risultati:
Si può notare che:
Le coppie di esempio (angolo, seno) sono
state scelte in maniera casuale (con
distribuzione di probabilità uniforme) tra tutte quelle
possibili. Se si fossero mostrate coppie in ordine
crescente di angolo ad esempio, la rete si sarebbe "assestata"
di volta in volta sugli ultimi valori forniti, "dimenticando"
quelli passati.
Non è sempre così comunque, ogni tipo di problema
necessita di un addestramento studiato su misura (cioè
di diverse prove per stabilire quale sia il migliore!)
I valori forniti alla rete devono essere normalizzati,
cioè espressi da un numero compreso tra 0 e 1. Per fare
questo è sufficiente dividere tutti i valori di ingresso
per il valore massimo presente nell'insieme i
addestramento. Anche in uscita si ottengono dei valori
normalizzati (a causa del codominio della funzione
sigmoide), è quindi opportuno riscalarli.
Sono necessari circa 7000 esempi
prima che l'errore inizi a diminuire visibilmente. Questo
numero può variare ampiamente comunque (vista la natura
stocastica dell'algoritmo di apprendimento e della scelta
dei pesi iniziali).
L'uscita della rete ha un errore maggiore quando il valore teorico che dovrebbe restituire è 0 o 1 (o vicino), questo è dovuto al fatto la funzione sigmoide vale 0 o 1 soltanto asintoticamente. Per diminuire questo effetto spesso è sufficiente aumentare il parametro di "guadagno", o se non bastasse aumentare anche il numero di neuroni.
Sto
usando la tua libreria, ma la rete non impara affatto!!!
Forse non funziona bene...
E' vero che la
libreria è ancora una versione sperimentale, però ti
assicuro che funziona! :-)
In genere se la rete non impara è quasi sempre per un
uso errato della rete stessa. Leggi bene a tal proposito
il paragrafo "Osservazioni
importanti"...
La rete
funziona, ma a volte commette degli errori vistosi...
E' normale. In genere questo dipende dal fatto che il
training set non è stato scelto bene o non è completo,
oppure è il caso di prolungare un pò la fase di
addestramento.
Questo problema può dipendere anche dalla natura dei
dati che si vogliono fare imparare alla rete. Se non sono
sufficientemente regolari può aiutare una rete con più
layer nascosti.
Che
topologia, parametri ed addestramento devo usare per la
mia applicazione particolare?
Non lo so!!!! E probabilmente non lo sa nessuno! Si può
risolvere il problame solamente per tentativi. Si creano
due training set, uno per l'addestramento e l'altro per
la verifica, e si fanno un grande numero di prove...
Vorrei
applicare la rete alla previsione dell'andamento delle
azioni di borsa: cosa ne pensi?
Non so che risultati si possono ottenere, sono dell'opinione
che in genere algoritmi di predizione più puramente
"matematici" (interpolazione polinomiale,
analisi armonica...) possano funzionare molto meglio.
Inoltre l'apprendimento di serie temporali richiede un
addestramento ed una struttura della rete un pò diversi
da quelli normalmente usati.