Caricare documenti e articoli online 
INFtub.com è un sito progettato per cercare i documenti in vari tipi di file e il caricamento di articoli online.


 
Non ricordi la password?  ››  Iscriviti gratis
 

RELAZIONE SUL PROGETTO DI UN RICONOSCITORE DI CARATTERI (OCR) - IMPLEMENTATO MEDIANTE UNA RETE NEURALE A TRE STRATI

tecnica



Relazione sul progetto di un

Riconoscitore di Caratteri (OCR)

Implementato mediante una rete neurale a tre strati


1. Descrizione del programma


Questo programma è stato realizzato come esercitazione per il corso di Ingegneria della Conoscenza e Sistemi Esperti. È un riconoscitore di caratteri ovvero un programma che riceve in ingresso una rappresentazione, non necessariamente precisa, di uno o più caratteri ed è in grado di riconoscere di quale carattere si tratti a patto che sia stato addestrato a riconoscerlo.


Il programma si appoggia ad una rete neurale multistrato (tre strati in questo caso) e funge da interfaccia tra l'utente e tale rete. Ciò che fa esattamente è permettere a chi lo usa di addestrare tale rete secondo le proprie esigenze (caratteri da riconoscere e grado di precisione richiesto) e proporle un insieme di caratteri da riconoscere. È inoltre possibile caricare e salvare le configurazioni ottenute dall'addestramento. A titolo dimostrativo è possibile anche lasciare al programma il compito di 'sporcare' un set di caratteri senza errori in modo da verificare la precisione di un addestramento.




Prima di procedere ad una descrizione dell'interfaccia all'utente, è importante spiegare l'uso dei file che il programma usa per espletare le proprie funzioni e della rappresentazioni dei caratteri all'interno degli stessi file:


I caratteri


I caratteri sono rappresentati mediante matrici 9 X 7 che contengono un punto ('.') al posto di uno spazio e un asterisco ('*') per indicare un punto pieno. Ad es, per rappresentare una 'A':












I file


I file importanti per il corretto funzionamento del programma sono quattro, ognuno di essi può essere esaminato e modificato da un qualunque editor di testo.


'rete.ocr' è il file in cui sono salvati i dati di una configurazione del programma. Non occorre che l'utente modifichi questo file in quanto esso è gestito interamente dal programma.


'training.set' contiene i caratteri da insegnare al programma. Esso è composto da una matrice carattere nella forma prima discussa e dal carattere che essa rappresenta, seguiti dal simbolo ';'. L'ultimo carattere del set deve essere seguito, invece, dal carattere '\'.


'test.set' e'validate.set' contengono i caratteri da riconoscere. Essi sono uguali al file 'training.set' tranne che per il fatto di non contenere il carattere che le matrici rappresentano.

Per far esaminare dei caratteri creati dall'utente è sufficiente copiarli nel secondo file, mentre per poter fare uso dell'opzione Confondi del programma devono essere modificati entrambi.

ATTENZIONE: Per confondere un set, il programma copia i caratteri da test.set a validate.set dopo averli modificati distruggendo quindi il contenuto del secondo file.


Le opzioni


Menu di inizializzazione.


Aiuto: mostra un piccolo riassunto di quanto descritto qui.


Inizializzare la rete: Cancella l'attuale configurazione del programma permettendo di riaddestrarlo da zero. Attenzione: utilizzare il riconoscitore senza addestrarlo darà risultati imprevedibili.


Caricare configurazione salvata: Richiama l'ultima configurazione salvata dell'OCR. I dati che vengono recuperati sono nel file 'rete.ocr'.


Procedere con l'addestramento: Permette di addestrare la rete secondo l'errore richiesto dall'utente a riconoscere i caratteri contenuti nel file 'training.set'. Viene chiesto se continuare l'addestramento ogni 1000 passi, ma è possibile disabilitarlo rispondendo 'a' (always). Se la risposta è un no, l'addestramento è considerato come buono a prescindere dall'errore raggiunto.


Menu di utilizzo.


Aiuto: come sopra.


Procedere al riconoscimento di un set: Inizia la procedura di riconoscimento dal file 'validate.set' e mostra all'utente i caratteri riconosciuti.


Confondere il test set: Modifica i caratteri da riconoscere cambiando da 0 a 9 punti della rappresentazione di un carattere. Il numero dei punti è deciso dall'utente mentre quali punti è determinato casualmente dal programma. Attenzione: un grado di confusione troppo alto può dare risultati non precisi, specialmente se si usano molti caratteri diversi.



2. Descrizione della rete


Il cuore del programma, o più precisamente, il suo cervello, è una rete neurale a tre strati.


Il primo (strato di ingresso) è formato da 63 neuroni che ricevono un valore binario per ognuno dei punti da cui è formata la rappresentazione del carattere.


Seguono 20 neuroni che formano il cosiddetto strato nascosto che riceve ed elabora gli ingressi per poi presentarli allo strato di uscita. Questo strato mantiene stati intermedi dell'elaborazione dando così alla rete una grande potenzialità rispetto a reti più semplici.  


Infine lo strato di uscita compie un'ultima elaborazione dei dati passatigli dallo strato precedente e ritorna al programma otto valori che rappresentano la codifica binaria del codice ascii del carattere riconosciuto.


Il passaggio delle informazioni tra strati avviene secondo le regole delle reti multistrato, che qui riassumiamo:


q   Ogni neurone non può comunicare che con i neuroni dello strato immediatamente successivo (esclusi, naturalmente i neuroni di ingresso e uscita che, rispettivamente, ricevono e trasmettono dati all'esterno).


q   Ogni neurone riceve ingresso e uscite di tutti i neuroni dello strato precedente (con le eccezioni di cui sopra).


q   L'uscita di un neurone dipende dai suoi ingressi, ciascuno dei quali ha una importanza che dipende da valori, detti pesi, che contraddistinguono le connessioni tra neurone e neurone. L'elaborazione degli ingressi avviene tramite la funzione sigmoide che è una funzione non lineare (altrimenti la nostra rete multistrato sarebbe equivalente ad una con due strati).


Il procedimento di insegnamento segue il metodo della back propagation e determina il successo dell'apprendimento sulla base dell'errore globale. L'inizializzazione della rete, cioè i valori iniziali dei pesi di ogni neurone, avviene con valori casuali che ricadono nell'intervallo di sicurezza consigliato.


3. Descrizione della classe ocr e delle sue componenti


Parte privata


double Wih[q][p]


Questa matrice contiene i pesi dei collegamenti tra i neuroni di uscita e quelli nascosti. Ogni riga i della matrice contiene i pesi delle connessioni tra il neurone nascosto i e tutti i neuroni di uscita. Le costanti  q e p contengono il numero di neuroni, rispettivamente, dei neuroni nascosti e di quelli di uscita.


double Wji[r][q]


Come la precedente, questa è una matrice di pesi. Ogni riga i contiene i pesi delle connessioni tra l'i-esimo neurone di uscita e tutti i neuroni nascosti.


double learnrate


Il coefficiente di apprendimento determina la rapidità nonché la precisione del processo di addestramento della rete. Il valore di questa variabile viene fissato automaticamente dalla funzione di inizializzazione (vedere più avanti) ma può essere cambiato da un programma che usi questa classe.


double sigmoide(double x)


Per comodità il calcolo della funzione sigmoide viene fatto da questo membro invece che all'interno delle funzioni che lo usano.


void   encode(char carattere, int *vector)

char   decode(int ingresso[r])


Poiché la rete lavora, internamente, con codici ascii rappresentati da otto numeri binari vengono fornite anche due funzioni che convertono un carattere in codifica ascii entro un vettore di otto interi (ha comunque solo valori binari) e viceversa. Nel primo caso vengono passati in ingresso il carattere da convertire ed il vettore in cui andrà il risultato e la funzione non restituisce niente. Nel secondo caso in ingresso va il vettore della rappresentazione ascii e la funzione restituisce il carattere rappresentato.


Parte pubblica


void   inizializzala()


Prima di poter addestrare una rete è necessario inizializzarla. Questo viene fatto ponendo tutti gli elementi delle due matrici dei pesi a valori casuali entro i limiti consigliati per questo tipo di reti.

Inoltre la variabile learnrate, che rappresenta il coefficiente di apprendimento viene inizializzato a 0,5.

Notare che, poiché questa rete è disegnata per avere dimensioni fisse e non variabili, usare una funzione qualsiasi invece del costruttore per inizializzare la rete permetterà poi ad un programma che la usi di reinizializzare le matrici senza dover creare una nuova classe ocr.


char   interrogala(const char matrice[altezza_carattere],

[larghezza_carattere])


Questa funzione membro è quella che si occupa dell'effettivo riconoscimento dei caratteri. Essa riceve in ingresso una matrice di caratteri che rappresenta un carattere da riconoscere secondo le regole esposte nel punto 1. La matrice in questione ha dimensioni definite da due costanti che per l'implementazione attuale danno una matrice 9x7. In uscita la funzione restituisce il carattere riconosciuto.


double lezione(const char ingresso[altezza_carattere]

[larghezza_carattere], const char teacher)


La funzione di addestramento della rete viene svolta da questo membro della classe. Essa, in effetti, esegue un solo passo dell'algoritmo di apprendimento per un solo carattere. In ingresso riceve la matrice che rappresenta il carattere da imparare e il carattere che essa rappresenta; in uscita abbiamo l'errore relativo a tale carattere commesso prima dell'apprendimento. Tale funzione è disegnata per essere chiamata iterativamente per ogni carattere da imparare e fino a che l'errore (globale o medio) non è a livelli accettabili.


void   salvala()

void   caricala()


Queste due funzioni si occupano del salvataggio e del caricamento delle informazioni della rete su file. Entrambe lavorano sul file 'rete.ocr'.


double mostraeta();

void   nuovoeta(const double valore);


Il valore del coefficiente di apprendimento può essere controllato e modificato tramite queste due funzioni membro. E' possibile, tramite esse modificare il coefficiente tramite programma per implementare, ad esempio, una legge di diminuzione del learnrate col tempo.


Una nota sull'implementazione: L'implementazione scelta per i neuroni di uscita, un po' diversa da quella intuitiva (ogni neurone specializzato al riconscimento di un singolo carattere) ha il pregio, a nostro avviso, di essere più flessibile. Infatti per cambiare la rete per riconoscere caratteri diversi o in più basta modificare il file 'training.set' e non serve ritoccare la classe o il programma. È inoltre possibile scegliere il numero di caratteri da imparare, fino a 256. Il rovescio della medaglia è, naturalmente, che la rete richiede maggior precisione, e quindi tempo per l'addestramento.


4. Un esempio di interfaccia per la classe ocr: 'intocr.cc'.


Il programma che viene fornito come esempio d'uso della classe ocr è quello descritto all'inizio di questa relazione. Esso usa dei file per gestire l'ingresso di caratteri da riconoscere o per l'addestramento, mentre il salvataggio ed il caricamento della rete sono gestiti già dalla classe.

Il programma lavora su un unico oggetto classe chiamato 'occie'.


Vediamo ora, in dettaglio le funzioni che esso usa:


Una buona metà del programma si occupa dell'interfaccia all'utente e non riguardano lo scopo di questa relazione. La parte interessante comincia dalla funzione:


void riconosci( )


Questa funzione si occupa di caricare, uno alla volta, le matrici carattere da riconoscere dal file 'validate.set' e passarle alla funzione membro occie.interrogala per poi stampare sullo schermo i caratteri risultanti.


void addestramento( )


La fase di addestramento è gestita da quesrta funzione. Essa comincia chiedendo all'utente il valore dell'errore gobale che deve ritenere accettabile poi preleva dal file 'training.set' le matrici dei caratteri da insegnare alla rete, una alla volta, e il carattere che ne dovrebbe risultare, le passa alla funzione membro occie.addestrala e tiene il conto dell'errore globale. L'addestramento per ogni carattere continua fino al raggiungimento dell'errore richiesto. Per evitare lunghe attese non volute viene chiesto all'utente di confermare ogni 1000 passi.


void confondi( )


Questa funzione genera casualmente errori nel file 'validate.set'. In pratica essa 'sporca' ogni carattere cambiando casualmente da zero a nove punti. Il numero dei punti viene scelto dall'utente. La funzione confondi non fa altro che copiare una ad una le matrici carattere dal file test.set, cambiare il numero richiesto di punti e metterlo nel file 'validate.set', che verrà poi effettivamente usato dal riconoscitore. Questa azione distrugge il contenuto precedetne di quest'ultimo file. Le matrici da cambiare vengono prese da un file che non viene modificato, quindi confondere un set due volte non somma il numero di punti cambiati. Confondere con grado zero un set in pratica lo riporta alla forma originale.


5. Implementazione del riconoscitore di caratteri.


Il nostro programma è implementato tramite una classe (class ocr) scritta nei due file ocr.cc e ocr.h. L'interfaccia d'esempio è invece contenuta nel file intocr.cc. I file rete.ocr, training.set, test.set e validate.set sono utilizzati in vari modi dal programma, come fin qui spiegato.


Ecco i listati del programma:


Classe

ocr.h


#include <fstream.h>

#include <math.h>

#include <stdlib.h>


const double teta=0.0;

const int p=63, q=20, r=8,altezza_carattere=9,larghezza_carattere=7;

class ocr



ocr.cc


#include "ocr.h"


double ocr::sigmoide(double x)


void ocr::encode(char carattere, int *vector)




char ocr::decode(int ingresso[r])


return (char) codascii;



void ocr::inizializzala()


for(int j=0;j<r;j++)

for(int i=0;i<q;i++)




char ocr::interrogala(const char matrix[altezza_carattere][larghezza_carattere])

// Z contiene gli ingressi,


//passo2

for (int i=0;i<q;i++)


// X contiene i net dello strato nascosto

for(int i=0;i<q;i++) X[i]=sigmoide(X[i]);


// X contiene gli ingressi per lo strato di uscita

for (int j=0;j<r;j++)


// Y contiene i net dello strato di uscita

for(int j=0;j<r;j++) Y[j]=sigmoide(Y[j]);


for(int j=0;j<r;j++)

if (Y[j]>=0.5) uscita[j]=1; else uscita[j]=0;


return decode(uscita);



double ocr::lezione(const char matrix[altezza_carattere][larghezza_carattere], const char teacher)

//nota=> restituisce Ek

// attualmente p=63, q=20, r=8

// Z contiene gli ingressi,


//passo2

for (int i=0;i<q;i++)


// X contiene i net dello strato nascosto

for(int i=0;i<q;i++) X[i]=sigmoide(X[i]);


// X contiene gli ingressi per lo strato di uscita

for (int j=0;j<r;j++)


// Y contiene i net dello strato di uscita

for(int j=0;j<r;j++) Y[j]=sigmoide(Y[j]);


//passo 3

//calcolo deltaj per lo strato di uscita

for(int j=0;j<r;j++) deltaj[j]=(atteso[j]-Y[j])*Y[j]*(1.0-Y[j]);

//calcolo deltaWji per lo strato di uscita

for(int j=0;j<r;j++)

for(int i=0;i<q;i++)

deltaWji[j][i]=learnrate*deltaj[j]*X[i];


//passo4

//calcolo deltai

for (int i=0;i<q;i++)


//calcolo deltaWih per lo strato nascosto

for(int i=0;i<q;i++)

for(int h=0;h<p;h++)

deltaWih[i][h]=learnrate*deltai[i]*Z[h];


// passo 5

//aggiornamento della matrice Wji

for (int j=0;j<r;j++)

for(int i=0;i<q;i++)

Wji[j][i]+=deltaWji[j][i];

//aggiornamento della matrice Wih

for (int i=0;i<q;i++)

for(int h=0;h<p;h++)

Wih[i][h]+=deltaWih[i][h];


// passo 6

Ek=0;

for(int j=0;j<r;j++) Ek+=((atteso[j]-Y[j])*(atteso[j]-Y[j]));

return Ek/2.0;



void ocr::salvala()



void ocr::caricala()


double ocr::mostraeta()



void ocr::nuovoeta(const double valore)



Interfaccia

intocr.cc


#include "ocr.h"

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#include <math.h>

#include <fstream.h>


const int car1   = (int)'*';

const int su = 72;

const int giu    = 80;

const int dx = 77;

const int sx = 75;

const int invio  = 13;

const int spazio = 32;

const int bckspc =  8;


ocr occie;

int ver=1,pk=0;


int menu0(int qua=0)



void aiuto()



void genericmenu()



int menu1()



void menu2()


void menu3()



int menu4()// ritorna una scelta tra 0 e 5, -1 se si esce



void riconosci()


while(result!='\\');

result=getch();

test_file.close();


void confondi()


for(int i=0;i<altezza_carattere;i++)


file_out << carattere << endl;

}

while(carattere!='\\');

gotoxy(3,15);

cout << "Premi un tasto per continuare"<< endl;

carattere=getch();

file_in.close();

file_out.close();

}



void partenza()


}

while(ciclo);



void addestramento()


while(controllo!='\\');

file.close();

gotoxy(40,5);

cout << errattuale << endl;

if (errattuale<=errchiesto) ciclo=0;

conteggio+=1;

if ((conteggio%1000==0)&&(always==1))


}

while (ciclo);

gotoxy(3,23);

cout << "Fine apprendimento, premere un tasto per continuare" << endl;

carattere=getch();

partenza();

}



main()


}

while(ciclo0);





Privacy




Articolo informazione


Hits: 2522
Apprezzato: scheda appunto

Commentare questo articolo:

Non sei registrato
Devi essere registrato per commentare

ISCRIVITI



Copiare il codice

nella pagina web del tuo sito.


Copyright InfTub.com 2024