Newsgroup: it.comp.ia
Titolo: Newbie: codice rete neurale da controllare

Salve a tutti... probabilmente fra un paio d'anni darò la mia tesi di
laurea in Ingegneria Informatica sulle reti neurali (o qualcosa di
simile)... quindi ho cominciato ad accostarmi a queste, ma riscontro
dei problemi nei miei programmi... evidentemente sbaglio qualche cosa.
Ho creato una classe in C++, ma c'è qualcosa che non funziona.
Vi posto il codice della funzione di backpropagation, secondo me
l'errore è qui
void TNN::backpropagate(std::vector<float> RightOutput, float
LearnRate)
{
    float delta, *thiserrvector, *nexterrvector;
    TConnection *conn;
    for (int layerindex = iOUTPUT; layerindex > 0; layerindex--) {
        if (layerindex == iOUTPUT) {
            for (unsigned int i = 0; i < Neurons[layerindex].size();
i++) {
                delta = (RightOutput[i] -
Neurons[layerindex][i]->fValue) * Neurons[layerindex][i]->fValue * (1
- Neurons[layerindex][i]->fValue);
                nexterrvector = new float[Neurons[layerindex -
1].size()];
                memset(nexterrvector, 0, sizeof(float) *
Neurons[layerindex - 1].size());
                for (unsigned int j = 0; j < Neurons[layerindex -
1].size(); j++) {
                    conn = getconnection(layerindex - 1, j,
layerindex, i);
                    nexterrvector[j] += delta * conn->fWeight;
                    conn->fWeight += LearnRate * delta *
Neurons[layerindex - 1][j]->fValue;
                }
                Neurons[layerindex][i]->fBiasConnectionWeight +=
LearnRate * delta * 1;
            }
        }
        else {
            thiserrvector = nexterrvector;
            for (unsigned int i = 0; i < Neurons[layerindex].size();
i++) {
                delta = thiserrvector[i] *
Neurons[layerindex][i]->fValue * (1 - Neurons[layerindex][i]->fValue);
                nexterrvector = new float[Neurons[layerindex -
1].size()];
                memset(nexterrvector, 0, sizeof(float) *
Neurons[layerindex - 1].size());
                for (unsigned int j = 0; j < Neurons[layerindex -
1].size(); j++) {
                    conn = getconnection(layerindex - 1, j,
layerindex, i);
                    nexterrvector[j] += delta * conn->fWeight;
                    conn->fWeight += LearnRate * delta *
Neurons[layerindex - 1][j]->fValue;
                }
                Neurons[layerindex][i]->fBiasConnectionWeight +=
LearnRate * delta * 1;
            }
            delete[] thiserrvector;
        }
    }
}
Penso che il codice sia abbastanza autoesplicativo... se avete domande
fatemele... se volete che vi invio via e-mail tutto il codice (si
tratta di un progetto in C++Builder 3, che attualmente ha anche una
interfaccia grafica... si tratta di un OCR che per adesso dovrebbe
riconoscere le prime cinque lettere dell'alfabeto), fatemelo sapere.
MadMage


Ciao, io non posso risponderti ma forse so chi puo' farlo, ed e'
un certo Alessandro Monti che mi sembrava se ne intendeva parecchio
sulle NN tipo BP , ma adesso bazzica  ehm...  l'ho visto mandare messaggi
sul NG it.scienza.informatica.
ciao
Morfeus

 sul NG it.scienza.informatica.
Sono tornato, invece!
Sono stato un tantino preso dal lavoro (anche ora, a dire il vero) e da un
modem che non funzionava.
Per quanto riguarda il codice, l'ho guardato con molta superficialità e non
ci ho capito molto. Aiuterebbe molto sapere cosa è che non funziona (si
pianta il programma, fornisce un output scorretto, non compila, non
apprende, etc...), altrimenti bisogna prima guardare la sintassi del
programma, poi immaginarsi tutto il framework (che tra l'altro non è
presente nel codice), poi le formule usate, poi l'algoritmo, etc etc...
insomma, un lavoraccio!
Se l'autore fornisce qualche altra informazione, sono più che disponibile
(nel poco tempo che ho) a dare una mano.
Ciao!
P.S.
Ma Morfeus sta a vigilare tutti i NG?


Morfeus sa tutto....  hehehe.... :-)
Scherzo, mi piace visitare tutti quei NG dove
si parla di informatica e di programmazione a tutti i livelli,
SviluppoGiochi,IA,LINUX,VB,Java,HTML,HW,SW,ecc.. ecc..
ogni tanto per curiosita' faccio anche
qualche domandina su Biologia,Filosofia,Fisica,ecc...
leggo anche Egroups e anche altri NG in inglese.
ciao
Morfeus

Ma Morfeus sta a vigilare tutti i NG?
In effetti in tutti i ng in cui sono "iscritto" c'è sempre un
"morfeus" che risponde continuamente... mah... :-P
Ti mando il nuovo codice, più pulito, più leggibile e te lo spiego:
Allora, la classe si chiama TNN e va beh, questa funzione fa il
back-propagation, utilizzando l'output già presente nella rete (è
stato propagato il valore dell'input, prima di entrare in questa
funzione).
I neuroni sono organizzati in layer (il primo, con indice 0, è quello
di input, l'ultimo, quello con indice iOUTPUT è quello di output)
Neurons[<numero_layer>][<numero_neurone>].fValue dà il valore del
neurone
Connections[<numero_layer_partenza>][<numero_neurone_partenza>][<numero_neurone_arrivo].fWeight
dà il valore del peso della connessione che parte dal layer indicato e
dal neurone di partenza al neurone di arrivo.
Il vettore TargetOutput dà, come dice il nome, l'output obiettivo
(mentre l'output generato dalla rete è nel vettore Neurons[iOUTPUT])
void TNN::backpropagate(std::vector<float> TargetOutput, float
LearnRate)
{
    float delta, *thiserrvector, *nexterrvector;
    TConnection *conn;
    for (int layerindex = iOUTPUT; layerindex > 0; layerindex--) {
        for (unsigned int i = 0; i < Neurons[layerindex].size(); i++)
{
            if (layerindex == iOUTPUT) {
		    delta = (TargetOutput[i] -
Neurons[layerindex][i].fValue) * 
				Neurons[layerindex][i].fValue * 
				(1 - Neurons[layerindex][i].fValue);
		}
		else {
	          delta = thiserrvector[i] * 
				Neurons[layerindex][i].fValue * 
				(1 - Neurons[layerindex][i].fValue);
		}
    		nexterrvector = new float[Neurons[layerindex -
1].size()];
            memset(nexterrvector, 0, sizeof(float) *
Neurons[layerindex - 1].size());
            for (unsigned int j = 0; j < Neurons[layerindex -
1].size(); j++) {
                nexterrvector[j] += delta * Connections[layerindex -
1][j][i].fWeight;
                Connections[layerindex - 1][j][i].fWeight += LearnRate
* delta * Neurons[layerindex - 1][j].fValue;
            }
            Neurons[layerindex][i].fBiasConnectionWeight += LearnRate
* delta * 1.0f;
        }
        if (layerindex != iOUTPUT) delete[] thiserrvector;
    }
}
Spero stavolta di essere stato più preciso... grazie ancora...
]\/[ad]\/[age

Ascolta, CARO Alex... :-DDDD
Ho sistemato il programma, il tutto adesso non è più grande di 10K di
codice e comunque dubito che per sistemare questo problema si abbia a
che fare con tutto il codice.
Se vuoi te lo invio, così mi dici se riesci a vedere subito dov'è
l'errore. Ho notato che la mia rete con due ingressi e una uscita e
senza unità nascoste, non riesce ad imparare l'AND, se non dopo 1000
iterazioni, e uso 0.1 come coefficiente di apprendimento.
Insomma, la mia rete QUALCOSA apprende, ma mi sembra decisamente
troppo lenta!
Cmq, prima che ti posto il codice, magari se mi dici che questo è
normale il discorso finisce qui. Ma mi sembra un po' eccessivo 1000
iterazioni (epoche) per imparare un AND.
]\/[ad]\/[age
"Da grande creerò mondi in cui le persone, giocando, vivranno le vite che hanno sempre sognato di vivere" (MadMage)
(
www.madmagelab.cjb.net
)

 iterazioni, e uso 0.1 come coefficiente di apprendimento.
Ok, nella norma :-)
Dovrei fare una prova con la mia (anche io sono partito con AND, OR e XOR),
ma credo di ricordare che se non erano 1'000 erano 500 o giù di lì (dipende
comunque dall'errore residuo che vuoi ottenere.
Tieni presente, però, che molto dipende dalla configurazione della rete. Se
usi una rete piccola (ad esempio 2-1 neuroni rispettivamente per ingresso ed
uscita) avrai una convergenza piuttosto rapida per una data funzione (per
l'AND, ad esempio), mentre con una rete più grossa avrai comunque tempi più
lunghi.
Come saprai, inoltre, per lo XOR occorrono 3 strati (1 nascosto): io con una
rete 2-4-1 ottenevo la convergenza dopo qualche migliaio di iterazioni e
mica sempre! Anche il tuo problema potrebbe richiedere più strati. In
generale, più strati aggiungi, però, più tempo ci vorrà per l'apprendimento
e comunque oltre i 2 strati nascosti è inutile andare anche per motivi
derivanti dalla teoria. Nel tuo caso (l'OCR) potrebbero persino bastare 2
strati (ingresso-uscita), o al massimo 1 nascosto.
La difficoltà maggiore del tuo compito, però, mi pare quella di dover
fornire un numero sufficiente di esempi e contro-esempi alla rete, in modo
da addestrarla a riconoscere e non-riconoscere le lettere. Se fornire esempi
diversi di ogni lettera può essere facile (basta "storpiare" un po' le
figure), per i contro-esempi è difficile dare una regola. Una V rovesciata è
una A? Se non fornisci dei validi esempi alla rete rischi che riconosca in
maniera errata dei semplici segni senza senso (ammesso che questo sia un
problema).
Infine, se posso permettermi di darti un consiglio per quanto riguarda
l'implementazione della rete, ti direi di provare il tuo codice con gli
esempi più semplici e con diverse configurazioni. Quando sarai
ragionevolmente sicuro che il codice è affidabile, potrai dedicarti
completamente al compito di riconoscimento. Insomma, separa i 2 problemi:
avere un codice che implementa correttamente una NN e avere una NN che
assolva al compito.
Per fare le prove, basta che usi le classiche funzioni AND, OR e XOR con
diverse reti che differiscano per numero di strati e numero di neuroni per
strato.
Se vuoi provare configurazioni più complesse (più di 2 neuroni di ingresso
e/o più neuroni di uscita) puoi usare comporre funzioni a più dimensioni
combinando gli stessi operatori. Ad esmpio:
O = A and B or C
(O = output, A,B,C sono 2 variabili di ingresso = 3 neuroni)
O1 = A and B
O2 = A or B
(O1 e O2 sono 2 uscite = 2 neuroni)
Spero di essere stato utile.
Ciao e buon lavoro
--
Alessandro Monti


Ciao e buon lavoro
Grazie.


 uscite).
Se vuoi, ti posso mandare una quantità impressionante di dati e grafici
relativi al mio lavoro. Se serve, scrivimi in e-mail.
Ciao
--
Alessandro Monti