Sei sulla pagina 1di 31

Universita’ degli Studi di Siena

Facolta’ di Ingegneria dell’ Informazione

SVM Banconote

Rossinelli Emanuele

Anno Accademico 2010/2011


Indice
1 Introduzione 2

2 Hardware 4
2.1 Optoelettronica . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Meccanica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Progetto elettronico . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Software embedded . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Software 8
3.1 Raccolta dei dati dalla seriale . . . . . . . . . . . . . . . . . . 8
3.2 Rappresentazione della banconota . . . . . . . . . . . . . . . . 8
3.3 Labeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.4 Salvataggio e caricamento dei dati raccolti . . . . . . . . . . . 9
3.5 La libreria libsvm . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.6 Definizione delle Support Vector Machines . . . . . . . . . . . 11
3.7 Costruzione del Training Set . . . . . . . . . . . . . . . . . . . 12
3.8 Cross-validation . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.8.1 Il parametro γ . . . . . . . . . . . . . . . . . . . . . . 14
3.8.2 Il parametro C . . . . . . . . . . . . . . . . . . . . . . 15
3.8.3 Stima dei parametri con cross-validation . . . . . . . . 15
3.9 Learning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.10 Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Risultati 19
4.1 Rappresentazione delle banconote . . . . . . . . . . . . . . . . 19
4.2 Risultati Sperimentali . . . . . . . . . . . . . . . . . . . . . . 20
4.3 Vincolo di inserimento delle banconote . . . . . . . . . . . . . 21

5 Conclusioni e sviluppi futuri 23


5.1 Conclusioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.2 Sviluppi Futuri . . . . . . . . . . . . . . . . . . . . . . . . . . 23

A Appendice: Codice in linguaggio C del Software Embedded


25

1
1 Introduzione
Il task da svolgere era inizialmente il riconoscimento di banconote, senza fo-
calizzare l’attenzione su problemi di verifica. Grazie all’hardware utilizzato,
peró, il sistema si é rivelato in grado di discriminare banconote false. In real-
tá, non avendo a disposizione delle banconote contraffatte da professionisti
per fare i test, questa supposizione si basa sulle prove effettuate con semplici
fotocopie di banconote.

A causa della difficile reperibilitá di numerose banconote da 200 e da 500


Euro, la macchina é stata progettata per riconoscere solamente i tagli da 5,
10, 20, 50 e 100 Euro. Ad ogni modo, bastano poche modifiche al software
SVM per estendere il funzionamento alle banconote di tagli superiori, o piú
in generale, a sistemi di monetazione totalmente differenti da quello preso in
esame. Diciamo, quindi, che si tratta di una macchina general purpose.

La progettazione della macchina si é articolata in quattro fasi:

• progettazione e sviluppo della parte elettronica;

• progettazione e sviluppo della parte meccanica;

• progettazione e sviluppo della parte informatica embedded;

• progettazione e sviluppo della parte informatica riguardante l’intelli-


genza artificiale.

Ovviamente le diverse fasi non sono indipendenti, ma strettamente cor-


relate l’una all’altra. Una modifica nella parte meccanica comporta verosi-
milmente cambiamenti nelle parti elettronica e/o informatica. Nel seguito
non verrá data grande importanza alla parte meccanica, che verrá illustrata
insieme alla parte elettronica e alla parte informatica lato embedded, nel
capitolo Hardware.

2
3
2 Hardware
L’idea di base é quella di voler “leggere” una banconota. La lettura di tale
banconota viene fatta attraverso sensori di luce infrarossa. Utilizzando due
sensori ad infrarossi (uno sul lato destro e uno sul lato sinistro della bancono-
ta) e prelevando 60 campioni sulla lunghezza della banconota, si ottiene una
rappresentazione della banconota costituita da 120 numeri interi. L’utilizzo
della luce infrarossa permette di estrarre non solo l’informazione relativa al
colore della banconota, ma anche l’informazione relativa al tipo di carta uti-
lizzata per la stampa della banconota. É grazie a questo fatto che banconote
fotocopiate vengono facilmente identificate come false dal sistema.

2.1 Optoelettronica
Per prelevare i campioni ad infrarossi della banconota, sono stati utilizzati
componenti optoelettronici di basso costo, in particolare:

• LED ad emissione infrarossa modello Vishay TSAL7600;

• Fotodiodo rilevatore di luce infrarossa modello Optek OP999.

I due componenti vengono montati uno davanti all’altro, in modo che


la corrente diretta passante sul fotodiodo sia proporzionale alla quantitá
di radiazione infrarossa captata, a causa appunto dell’emissione del LED.
Inserendo ora una banconota tra il LED e il fotodiodo, il fascio di infrarossi
riesce ancora a passare e a raggiungere il rilevatore, ma con un’intensitá
molto minore, in funzione delle caratteristiche della banconota nel punto in
cui c’é passaggio di luce.
É immediato rendersi conto che amplificando tale segnale e utilizzando il
modulo ADC (convertitore analogico digitale) di un microcontrollore, sará
semplice ottenere informazioni significative riguardo alla banconota inserita.
Sono state usate due coppie Led-Fotodiodo per la lettura della banconota
(d’ora in avanti chiamate sensori di lettura), e un’ulteriore coppia per rilevare
l’inserimento della banconota stessa (d’ora in avanti chiamata sensore di
presenza), cosí da poter conoscere l’istante in cui avviare i motori che si
occuperanno del trascinamento.

2.2 Meccanica
Per permettere il campionamento della banconota, quest’ultima deve essere
necessariamente trascinata sotto i sensori ad infrarossi. Per il trascinamento
é stato utilizzato un motore elettrico in corrente continua alimentato a 3V,
unitamente a un rullo di scorrimento prelevato da una vecchia stampante.
Com’é noto, i motori in continua lavorano molto velocemente, approssima-
tivamente intorno ai 10.000 giri/min. Tale velocitá é senza dubbio troppo

4
elevata per i nostri scopi, ed é quindi regolata grazie all’utilizzo di un mo-
toriduttore (due coppie di ingranaggi che modificano il rapporto di uscita a
256:1).
Il motore utilizzato assorbe una corrente di circa 0.8 A; per fornire tale cor-
rente é stato progettato un amplificatore di corrente con un transistor BJT,
come illustrato nel seguito.

2.3 Progetto elettronico


Come abbiamo illustrato, il progetto si compone di due sensori di lettura e
di un sensore di presenza. I valori dei corrispondenti fotodiodi vengono letti
mediante una conversione analogico-digitale, effettuata dal microcontrollore
installato, un PICmicro PIC24F04KA201
R della Microchip. Il programma
eseguito dal microcontrollore é descritto nel capitolo successivo. Per il mo-
mento basti sapere che é utilizzato per effettuare le letture dei sensori (con
l’ADC), per gestire la temporizzazione (attraverso un timer), per comandare
il motore e per inviare dati al PC collegato mediante porta seriale/USB.

Lo schema seguente illustra il funzionamento di ciascuna coppia led-fotodiodo.

La resistenza R4, nel circuito trasmettente, serve per limitare la corren-


te sul diodo led, e quindi per evitare la rottura dello stesso. La radiazione
infrarossa trasmessa viene captata dal fotodiodo OP999 nel cirucito riceven-
te: la corrente che scorre su tale diodo sará proporzionale alla quantitá di
luce che irradia il componente. Per prelevare una differenza di potenziale
proporzionale a tale corrente é stata inserita la resistenza R1. Tale tensione
viene inviata all’amplificatore operazionale X1, montato in configurazione
non invertente con guadagno

R2
A=1+ ≈ 15 (1)
R3
Il che dovrebbe garantire un utilizzo dell’intero range convertibile dall’A-
DC: da 0 a 3.3V (tensione di alimentazione).

5
Come anticipato, per fornire al motore la corrente necessaria, é stato uti-
lizzato un transistor BJT NPN in grado di fornire fino a 0.8 A: il modello
BC337. Lo schema del circuito é illustrato in figura 2.3.

Il componente X1 rappresenta il motore, mentre il diodo D1 ha funzioni


protettive: impedisce alla tensione di flyback di distruggere il transistor.

Una volta che il microcontrollore ha elaborato i dati, non resta che inviarli
al PC. Il collegamento con il PC avviene tramite la porta seriale; se la seria-
le non fosse disponibile sul computer, é possibile effettuare il collegamento
con una porta USB utilizzando un emulatore di porta seriale (RS232 - USB
converter). I livelli logici previsti dal protocollo RS232 non sono compatibili
con i livelli logici TTL-compatibili forniti dal controllore. Per questo é stato
utilizzato un componente elettronico la cui funzione é proprio quella di in-
terfacciare i due protocolli: si tratta del Maxim MAX232 . R

Non resta che esaminare la parte software del progetto.

2.4 Software embedded


Possiamo schematizzare il funzionamento della macchina nei seguenti passi:

• La banconota viene inserita nella macchina e rilevata dal sensore di


presenza;

• Il motore viene avviato, per iniziare il processo di trascinamento;

• Non appena la banconota si trova per la prima volta sotto ai sensori


di lettura, si entra nella modalitá di cattura;

• Ad ogni interrupt sollevato dal timer si prelevano valori dei sensori di


lettura dall’ADC e si inviano al PC tramite l’UART;

• Sollevati 60 interrupt come appena descritto, la lettura é terminata;

6
• Si attende che il sensore di presenza torni libero (ovvero, si attende che
la banconota sia trascinata sufficientemente in avanti affinché non sia
piú rilevata dal sensore di presenza)

• Si attende un breve intervallo di tempo fissato, per permettere alla


banconota di essere espulsa;

• Il motore viene spento.

Data la semplicitá del programma eseguito dal microcontrollore, che so-


stanzialmente realizza i passi appena descritti, evitiamo di commentarlo e
rimandiamo il lettore a prendere visione del codice in appendice.
Come abbiamo giá accennato, una banconota viene rappresentata con 60
valori dei due sensori di lettura. Ogni volta che sono disponibili nuovi valori
dei due sensori, questi vengono inviati sulla seriale utilizzando il seguente
protocollo:

PIC → UART: * sens1 - sens2 - lettura *

dove sens1 e sens2 rappresentano i valori numerici (compresi tra 0 e 1023)


corrispondenti ai sensori di lettura, e lettura é un numero progressivo da 1
a 60. Quest’ultimo parametro non é indispensabile, ma puó essere usato per
eseguire un controllo di integritá dei dati ricevuti sul computer. I caratteri
asterisco servono per identificare l’inizio e la fine di ogni pacchetto, mentre
il carattere meno serve per separare i valori numerici da inviare.

7
3 Software
I dati prodotti dalla macchina vengono raccolti ed elaborati da un software in
esecuzione su un PC, scritto in linguaggio C#. In questo capitolo verranno
illustrate le varie parti del software, dalla lettura dei dati in arrivo sulla
seriale fino all’utilizzo degli algoritmi di Kernel Machines per classificare le
banconote date in pasto al sistema.
Il problema di riconoscimento delle banconote prevede una fase iniziale in
cui viene raccolto un quantitativo non definito di banconote: il training set.
Questi dati vengono quindi utilizzati nel processo di cross-validation, per
stimare buoni valori dei parametri in gioco. Quindi, si addestra il software
utilizzando i dati raccolti e i parametri definiti al passo precedente. A questo
punto non rimane che utilizzare il programma ai fini del riconoscimento.

3.1 Raccolta dei dati dalla seriale


All’avvio del software, é necessario specificare su quale porta seriale é col-
legata la macchina. Quindi, connettersi al dispositivo. Da ora in avanti il
software é sempre in ascolto su questa porta, in attesa di dati da processare.
L’elaborazione dei dati ricevuti viene fatta da un thread apposito, che la-
vora in background. In particolare, i pacchetti (come illustrato nel capitolo
2.4) vengono analizzati e, se non presentano anomalie, vengono trasforma-
ti in un oggetto di tipo “lettura”, che rappresenta l’i-esima parte dell’intera
banconota, per i = 1, 2, ..., 60.
struct lettura
{
public int sensore1;
public int sensore2;
public int indice;
};

L’insieme delle letture viene memorizzato in una lista cosí definita:


private List<lettura> TempBanconota = new List<lettura>();

Quando questa lista raggiunge 60 elementi, l’intera banconota é stata


acquisita, e puó essere quindi creato l’oggetto banconota (il pattern), come
descritto nella sezione successiva.

3.2 Rappresentazione della banconota


Una banconota é rappresentata da un oggetto di tipo “banconota”, definito
come segue:
struct banconota
{
public List<lettura> letture;
public int[] y;
};

8
che non é nient’altro che una lista di letture (per l’esattezza, 60 letture),
associato a un vettore di interi detto y (target), la cui utilitá diverrá chiara
nel seguito.
Quando un’intera banconota risulta acquisita, viene creato un oggetto di
questo tipo a partire dalla lista di letture “TempBanconota”, e memorizzato
nella variabile “lastbanconota”, che rappresenta il pattern. Quando questa
variabile é associata a un valore (ovvero, é diversa da null), é possibile utiliz-
zare questa informazione per fare il labeling della banconota stessa (il lavoro
del supervisor) oppure, se la fase di apprendimento é giá stata eseguita, si
puó richiedere di fare riconoscimento della banconota.

3.3 Labeling
Il labeling é il processo in cui un supervisore del sistema associa a ciascun
pattern il target desiderato. Nel nostro caso, il supervisore introduce nella
macchina un certo numero di esempi, indicando per ogni esempio la classe di
appartenenza (ovvero il taglio della banconota), cosí che la macchina riesca
ad apprendere il modello desiderato. L’interfaccia utente del software per-
mette, data una banconota, di specificare se si tratta di 5 EUR, 10 EUR, 20
EUR, 50 EUR, 100 EUR, Falso.
Supponiamo di aver appena acquisito una banconota: la variabile “lastban-
conota” é associata a un valore di tipo “banconota”. Supponiamo adesso di
cliccare sul tasto 20 EUR. Allora i target y relativi alla banconota vengono
impostati nel seguente modo:
y[0] = -1; //5 EUR
y[1] = -1; //10 EUR
y[2] = +1; //20 EUR
y[3] = -1; //50 EUR
y[4] = -1; //100 EUR

Se la banconota acquisita viene etichettata come un falso, tutti i target


vengono messi a -1.
Ogni nuova banconota acquisita ed etichettata viene aggiunta ad una lista
di banconote mantenuta in memoria con la seguente struttura:
private List<banconota> banconote = new List<banconota>();

3.4 Salvataggio e caricamento dei dati raccolti


Per permettere all’utente di aggiugere nuovi pattern etichettati all’elenco
di banconote acquisite in qualsiasi momento, é previsto il salvataggio e il
caricamento della lista “banconote”. Questo risultato si ottiene facilmente
dichiarando le struct sopra illustrate con l’attributo [Serializable]. In questo
modo, é possibile utilizzare la serializzabilitá degli oggetti, in combinazione
con la classe BinaryFormatter:

9
FileInfo f = new FileInfo(@"temp.dat");
FileStream s = f.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
BinaryFormatter b = new BinaryFormatter();

b.Serialize( s, banconote );

s.Close();

Analogamente, il caricamento dei dati salvati si traduce nelle seguenti


righe di codice:
FileInfo f = new FileInfo(@"temp.dat");
FileStream s = f.Open(FileMode.Open, FileAccess.Read, FileShare.None);
BinaryFormatter b = new BinaryFormatter();

this.banconote = (List<banconota>)b.Deserialize(s);

s.Close();

3.5 La libreria libsvm


LIBSVM é una libreria sviluppata per utilizzare gli algoritmi di intelligenza
artificiale relativi alle Support Vector Machines, che é disponibile anche in
linguaggio C#. La libreria si compone delle seguenti strutture:

• svm_parameter: rappresenta i parametri utilizzati dalla SVM per


l’apprendimento, come C, γ e il tipo di kernel;

• svm_problem: rappresenta il training set del problema che vogliamo


risolvere;

• svm_node: rappresenta ciascun elemento del pattern, ed é costituito


da un valore intero x (la lettura del sensore nel nostro caso) e un valore
intero index (il numero progressivo di lettura);

• svm_model: rappresenta il modello prodotto dalla fase di learning


della SVM.

e viene usata tramite le seguenti funzioni:

• svm_cross_validation: funzione utilizzata per ottenere la stima dei


parametri mediante cross-validation;

• svm_train: esegue il learning sui dati del training set e restituisce il


modello svm_model.

10
3.6 Definizione delle Support Vector Machines
Per risolvere il problema é stato deciso di definire una SVM per ogni taglio
di banconote con cui si intende lavorare. Quindi, sono state definite 5 SVM,
ognuna delle quali si comporta come un classificatore binario. Tutte le SVM
lavorano sull’intero training set, ma con target adeguati al task che singo-
larmente deve essere svolto.
La libreria SVM é utilizzata in modo statico, quindi non é necessario defini-
re 5 istanze della classe. Sono stati definiti 5 problemi, ovvero training set
relativi a ciascun taglio di banconote:
private svm_problem []problem = new svm_problem[c];

dove c é una costante assegnata al valore 5, ovvero il numero di tasks da


svolgere.

Mentre alcuni parametri (in particolare, C e γ) devono essere appresi con


cross-validation, altri parametri possono ragionevolmente essere fissati al
momento della creazione delle SVM.
svm_parameter param = new svm_parameter();
param.svm_type = svm_parameter.C_SVC;
param.kernel_type = svm_parameter.RBF;
param.cache_size = 100; //Use 100 MB Cache
param.eps = 0.01;

Come tipo di SVM é stato scelto C_SVC, in quanto vogliamo usare la


macchina per fare classificazione.
Il parametro epsilon rappresenta la precisione con cui calcolare la soluzione
approssimata del problema di ottimizzazione vincolata (risolto con approccio
Lagrangiano). Impostare un basso valore per epsilon significa richiedere che
l’iperpiano di separazione calcolato sia il piú vicino possibile a quello della
soluzione teorica della formulazione di separazione robusta.
Il kernel scelto é il kernel gaussiano, o anche RBF (Radial Basis Function). Si
tratta di una scelta tipica, in quanto generalmente i kernel gaussiani riescono
a risolvere qualsiasi tipo di problema, a dispetto di quanto succede per kernel
lineari o polinomiali. Per esempio, pattern molto differenti da quelli presenti
nel training set (per esempio un volantino pubblicitario in un training set
composto da sole banconote) finiscono inevitabilmente molto lontano dalle
zone in cui gli elementi sono addensati (possiamo parlare di elementi molto
lontani dai centroidi dei cluster, se il problema fosse attaccato con algoritmi
di clustering). In questo scenario kernel lineari o polinomiali tendono al
fallimento: la risposta che danno potrebbe risultare totalmente casuale.

11
Come illustrato nell’esempio (in cui i cerchietti sono elementi apparte-
nenti alla classe di interesse), il nuovo pattern (molto diverso dagli elementi
a cui la macchina é stata addestrata) verrebbe classificato come apparte-
nente alla stessa classe dei cerchi, pur non avendo niente in comune, data
la distanza. La propensione al verificarsi di simili situazioni é intuibilmente
molto alta in uno spazio 120-dimensionale, come nel nostro caso.
Queste situazioni non generano anomalie con l’utilizzo di kernel gaussiani,
in quanto la separazione non avviene in modo lineare, ma con curve chiu-
se. Cosí facendo, il pattern da classificare non potrebbe essere considerato
appartenente alla classe dei cerchi.

3.7 Costruzione del Training Set


Come abbiamo visto, la libreria SVM esegue il learning a partire da un ogget-
to di tipo svm_problem. Per questioni pratiche, le banconote che vengono
acquisite o caricate da un file sono memorizzate nella lista “banconote”. La
funzione illustrata di seguito costruisce il svm_problem i-esimo a partire
dalla lista “banconote”.
Per poter trattare tutti i problemi allo stesso modo, dobbiamo inserire co-
me primo pattern nel problema i-esimo un pattern appartenente alla classe
in questione. Questo é necessario perché internamente la libreria libsvm si
costruisce una lista di target (nel nostro caso 1 e -1) man mano che li trova
nel problema.

12
private void create_TrainSet(int k)
{
problem[k] = new svm_problem();
problem[k].x = new svm_node[l][];
problem[k].y = new double[l];
problem[k].l = l; //variabile globale

/* Attenzione:
* vogliamo che il problema k-esimo prenda come primo esempio
* un esempio in cui l’elemento appartiene alla classe in questione.
* quindi il primo esempio che mettiamo nel problema 0 (dei 5 euro)
* deve essere una banconota da 5 euro
*/

bool firsttime = true;


int firstgoodex = -1;

for (int j = 0, h = 0; h < l; h++)


{
if (firsttime == false &&
firstgoodex == h) continue;

if (firsttime == true &&


banconote.ElementAt(h).y[k] == -1) continue;
else if(firsttime==true){
firsttime = false;
firstgoodex = h;
}

problem[k].x[j] = new svm_node[n];


for (int i = 0, s = 0; i < n; i+=2, ++s)
{
problem[k].x[j][i] = new svm_node();
problem[k].x[j][i+1] = new svm_node();

problem[k].x[j][i].index =
banconote.ElementAt(h).letture.ElementAt(s).indice;
problem[k].x[j][i+1].index =
banconote.ElementAt(h).letture.ElementAt(s).indice + 1;

problem[k].x[j][i].value_Renamed =
banconote.ElementAt(h).letture.ElementAt(s).sensore1;
problem[k].x[j][i+1].value_Renamed =
banconote.ElementAt(h).letture.ElementAt(s).sensore2;

problem[k].y[j] = banconote.ElementAt(h).y[k];
j++;

if (firstgoodex == h) h = -1;
}
}

Alla fine della procedura il svm_problem k-esimo é stato creato. In


particolare x rappresenta l’insieme degli esempi, y rappresenta l’insieme dei
target (per ciascun esempio), ed l rappresenta il numero totale di esempi.

13
3.8 Cross-validation
La cross-validation é una tecnica statistica che consiste nella suddivisione
del dataset totale in k parti e, ad ogni passo, la parte k-esima del dataset
viene ad essere il validation dataset, mentre la restante parte costituisce il
training dataset. Cosí, per ognuna delle k parti si allena il modello, evitan-
do quindi problemi di overfitting, ma anche di campionamento asimmetrico
del training dataset, tipico della suddivisione del dataset in due sole parti
(ovvero training e validation dataset).
Viene utilizzata per la stima dei migliori parametri C e γ evitando l’overfit-
ting.
É indispensabile per ottenere risposte significative dalle SVM; procedere per
tentativi di C e γ é una pratica assolutamente fallimentare, visto che i va-
lori sono strettamente correlati al particolare task da risolvere, e quindi non
predicibili. Cercare i valori ottimi per C e γ senza usare cross-validation, ma
semplicemente addestrando il classificatore con il training set e misurandone
le prestazioni sul test set (il dataset é in questo caso l’unione del training set e
del test set), porterebbe probabilmente a scegliere modelli in cui l’overfitting
la fa da padrone.

3.8.1 Il parametro γ
L’unico parametro che é possibile impostare relativamente ai kernel gaussiani
é il γ, che rappresenta l’inverso della varianza nelle curve gaussiane, come
evidente dall’espressione del kernel:

2 ||xi −xj ||2


K(xi , xj ) = e−γ||xi −xj || = e− 2σ 2 (2)
In particolare, tanto piú grande é il valore di γ e tanto piú piccole sono
le curve di separazione. Quindi aumentando γ si chiede alla SVM di esse-
re molto selettiva, con conseguente minor generalizzazione. Avere curve di
separazione piccole si traduce in un vantaggio dal punto di vista della non
accettazione di falsi e della errata classificazione: le SVM tenderanno a dare
risposte positive solo se il pattern é molto simile agli elementi della stessa
classe appresi durante il learning. Per contro, data l’alta imprecisione della
nostra macchina (a causa dell’hardware di basso costo), scegliere un γ ecces-
sivamente grande porterebbe ad un difficile riconoscimento delle banconote.
Il software tenderebbe a non accettare la maggior parte delle banconote.

14
Scegliendo un piccolo valore di γ si ottiene il comportamento oppo-
sto: il software diverrebbe molto morbido nella classificazione dei pattern
in ingresso, dando risultati poco sensati.

3.8.2 Il parametro C
Il parametro C é utilizzato nella risoluzione del problema di ottimizzazione
vincolata, ed esprime la propensione della SVM a tollerare gli errori. La sua
introduzione é fondamentale in quanto permette la soluzione di problemi
i cui dati non sono linearmente separabili nello spazio delle features, una
situazione questa possibile perché

• non possiamo essere sicuri che il kernel utilizzato riesca effettivamente


a separare linearmente i dati;

• non possiamo escludere errori durante il labeling dei pattern da parte


del supervisore.

Maggiore é il valore di C, minore é la tolleranza agli errori, conseguente-


mente la SVM commetterá un basso numero di errori, a discapito peró della
generalizzazione.

3.8.3 Stima dei parametri con cross-validation


Come é stato illustrato, utilizzare buoni valori per i parametri é di fondamen-
tale importanza per ottenere risultati soddisfacenti. La stima dei parametri
avviene secondo il seguente schema (grid search):

1. Per ogni coppia (Ci , γj ), i=1, 2, .. |C| , j = 1, 2, .. |γ|

(a) Cross-validation sul dataset utilizzando la coppia (Ci , γj )


(b) Valutazione della percentuale di errori commessi dal modello

2. Salvataggio della coppia (Ch , γk ) per cui la percentuale di errori com-


messi é minima.

15
Dove i vettori C e γ sono

C = {2−5 , 2−3 , 2−1 , 2, 23 , 25 , 27 , 29 , 211 , 213 } (3)

γ = {2−15 , 2−13 , 2−11 , 2−9 , 2−7 , 2−5 , 2−3 , 2−1 , 2, 22 } (4)

3.9 Learning
Ora che abbiamo i valori di C e γ adatti alla risoluzione del nostro problema,
non ci resta che usarli per addestrare la SVM.
model[i] = new svm_model();
model[i] = svm.svm_train(problem[i], param[i]);

Per ogni problema viene creato un modello, ovvero il classificatore vero


e proprio. Tale modello sintetizza la soluzione calcolata dal problema di
ottimizzazione vincolata con l’approccio Lagrangiano, ovvero i coefficienti
αi presenti nella funzione
l
X
f (x) = αi yi K(xi , x) + b (5)
i=1

Tale funzione viene usata per fare la predizione come illustrato nel pa-
ragrafo successivo. In realtá, il modello non lavora sull’intero training set,
ma solo sull’insieme dei support vector, ovvero quegli elementi che risultano
significativi per la definizione delle curve di separazione. Il calcolo puó essere
riscritto come
|S|
X
f (x) = αi yi K(xi , x) + b (6)
i=1

dove S rappresenta l’insieme dei vettori di supporto.

16
3.10 Test
Una volta finito l’apprendimento del training set, i modelli dei classifica-
tori sono pronti per essere usati. Con la seguente funzione una banconota
acquisita viene trasformata in un pattern nel formato atteso dal classificatore.
private svm_node[] BNtoPattern(banconota bn) {

svm_node[] pattern = new svm_node[n];


for (int i = 0, j = 0; i < n; i += 2, ++j)
{
pattern[i] = new svm_node();
pattern[i + 1] = new svm_node();

pattern[i].index = bn.letture[j].indice;
pattern[i + 1].index = bn.letture[j].indice + 1;

pattern[i].value_Renamed = bn.letture[j].sensore1;
pattern[i + 1].value_Renamed = bn.letture[j].sensore2;
}
return pattern;
}

Quindi, tale pattern puó essere confrontato dal classificatore i-esimo con
la seguente istruzione
y[i] = svm.svm_predict(model[i], pattern);

y[i] é un valore numerico compreso tra -1 e 1. Tanto piú tale valore é


vicino a 1, e tanto piú alta é la confidenza per cui il pattern appartiene alla
categoria i-esima. Calcolati quindi y[i] ∀i = 1, 2, 3, 4, 5, la banconota viene

17
accettata e riconosciuta come appartenente alla categoria j-esima se e solo
se

∃ ! y[j] > 0
(7)
y[h] ≤ 0 ∀h 6= j

18
4 Risultati
Data la semplicitá della macchina dal punto di vista sia dell’elettronica, sia
della meccanica (basata su componenti di basso costo e componenti riciclati),
i risultati ottenuti sono assolutamente rispettabili.

4.1 Rappresentazione delle banconote


Per valutare la precisione della macchina, sono state fatte delle prove inse-
rendo piú volte la stessa banconota e comprando le diverse rappresentazioni
ottenute. Si vede che le rappresentazioni di una stessa banconota sono molti
simili tra loro, ma molto diverse dalle rappresentazioni di banconote differen-
ti. Questo é un indizio della possibilitá di risolvere con successo il problema
del riconoscimento.

19
4.2 Risultati Sperimentali
Le prime prove di classificazione sono state eseguite facendo learning su un
training set costituito da 50 esempi, 10 per ogni taglio di banconota. Il
sistema funzionava bene eseguendo i test con le banconote appartenenti al
training set, mentre utilizzando banconote non appartenenti al training set
si verificavano delle classificazioni errate. Giá con un training set doppio le
prestazioni si sono rivelate significativamente migliori.
Provando ad inserire un falso, questo viene scambiato per una banconota
reale. Facendo learning con il falso nel training set, questo comportamento
non si verifica piú. Questo significa che le curve di separazione sono troppo
ampie, e si dovrebbe aumentare γ per ridurle. Quest’operazione peró, se la
macchina non é abbastanza precisa, porterebbe a rifiutare anche banconote
autentiche, in quanto le rappresentazioni di banconote dello stesso taglio nel
training set differiscono tra loro.

I valori dei parametri stimati con cross-validation sono stati leggermente


modificati in modo manuale (fase di tuning), ottenendo le coppie:

γ C
5 EUR 2−13 512
10 EUR 2−13 512
20 EUR 2−13 8192
50 EUR 2−13 512
100 EUR 2−13 512
I valori di γ sono sempre molto piccoli, il che significa che le curve di
separazione sono piuttosto grandi. Questo ci permette di asserire che la
macchina non é sufficientemente precisa: rappresentazioni della stessa ban-
conota sono distanti tra loro e la SVM é costretta ad allargare le superfici
di separazione per generalizzare. Invece, i valori di C sono molto alti: per lo

20
stesso motivo illustrato, potrebbe significare che data la diversitá di pattern
relativi ad una stessa categoria, la SVM tende a fare molti errori, per cui C
viene innalzato per costringerla a non tollerarli.

Le prestazioni della macchina, utilizzando i valori di C e γ illustrati, possono


essere riassunte come segue:

• Percentuale di classificazioni errate (banconote scambiate): ≈ 0.03%

• Percentuale di banconote autentiche non accettate: ≈ 10%

• Percentuale di banconote false accettate: ≈ 0.01%

L’alta percentuale di banconote autentiche non accettate non preoccu-


pa, in quanto il reinserimento della banconota porta quasi sempre ad un
riconoscimento corretto.

4.3 Vincolo di inserimento delle banconote


Come ultima prova, sono state incluse nel training set banconote inserite nel-
la macchina nei quattro modi possibili, e non solamente con inserimento “di
testa”. I valori di γ stimati con cross-validation sono risultati molto maggiori
rispetto al caso precedente. I risultati sono stati tragici: la macchina non
era piú in grado di riconoscere le banconote, task che svolgeva egregiamente
prima di inserire nel training set esempi di banconote inserite in qualsiasi
verso.
Questo calo di prestazioni é ragionevole, in quanto separare le diverse classi
risulta ovviamente piú difficile visto che ogni banconota puó essere rappre-
sentata in quattro modi diversi, a seconda di come viene inserita. Non solo le
curve di separazione di ciascuna categoria devono essere suddivise in quattro
superfici di separazione distinte, ma potrebbero avvicinarsi pericolosamente
alle curve delle altre categorie. A dimostrazione di questo ci sono i valori di
γ stimati con cross-validation, molto alti, che portano le superfici di separa-
zione ad essere molto strette, perdendo generalizzazione.
Inoltre l’accuracy calcolata con la cross-validation ha dato valori piú bassi
rispetto al caso precedente (siamo passati dal 95% circa al 80%), quindi la
separazione é difficile da ottenere. In realtá con kernel gaussiani si riesce
sempre a separare, ma si fa overfitting, e questo é dimostrato anche dagli
alti valori di γ.

21
22
5 Conclusioni e sviluppi futuri
5.1 Conclusioni
Il riconoscimento e talvolta la verifica di banconote é un problema con cui
abbiamo a che fare quotidianamente. L’uomo fa riconoscimento principal-
mente utilizzando la vista, mentre per la verifica utilizza anche il senso del
tatto. In poche parole, la quantitá di informazione utilizzata dall’uomo per
risolvere questo problema é enorme se paragonata a quella che é sufficien-
te per questo riconoscitore automatico. La macchina infatti si basa su 120
numeri per ogni banconota; il problema del riconoscimento basato su rappre-
sentazioni numeriche di questo tipo é improponibile per l’uomo. Chi svolge
meglio questa operazione?
Possiamo concludere che data l’imprecisione della macchina (che porta ad
avere rappresentazioni della stessa banconota piuttosto lontane spazialmen-
te) si riesce bene a fare riconoscimento solo nel caso in cui le banconote
vengano inserite di testa, e quindi che la SVM sia in grado di fare grande ge-
neralizzazione (basso valore di γ). La verifica dell’autenticitá delle banconote
risulta un task difficile proprio a causa dei bassi valori di γ. Il riconoscimento
di banconote inserite in qualsiasi modo non é trattabile, proprio perché que-
sto task puó essere risolto stringendo molto le curve di separazione, soluzione
questa che rende praticamente nulla la generalizzazione, e conseguentemente
molto rara l’accettazione di banconote autentiche.

5.2 Sviluppi Futuri


Al fine di ottenere prestazioni migliori é possibile effettuare alcune modifiche
alla macchina.

• Creare un sistema che allinea la banconota inserita sul lato sinistro;

• Aumentare la precisione dello scorrimento, utilizzando motori passo-


passo;

• Montare un rullo di scorrimento dopo i sensori di lettura, per trasci-


nare completamente la banconota: in questo modo si riesce ad inserire
implicitamente nella rappresentazione della banconota l’informazione
relativa alla sua lunghezza (i sensori eseguiranno un numero di letture
a vuoto proporzionale all’inverso della lunghezza).

Aumentare il numero di campioni o il range numerico dei valori ottenuti


dalla lettura dei sensori porterebbe probabilmente ad avere un modello con
maggiore complessitá senza trarre benefici significativi. Lo spazio delle fea-
tures diverrebbe troppo grande, e i dati dovrebbero essere sottoposti ad una
operazione di scaling per diminuire il tempo di calcolo necessario.

23
Applicando le modifiche illustrate la macchine diverrebbe molto piú preci-
sa rispetto allo stato attuale. In questo scenario le rappresentazioni delle
banconote dello stesso taglio sarebbero molto simili tra loro (i grafici visti
al capitolo 4.1 sarebbero molto piú puliti), conseguentemente si potrebbero
stringere maggiormente le curve di separazione attorno agli addensamenti
di esempi (aumentando il valore di γ). Questo si traduce in una maggiore
accuratezza delle predizioni e in una maggiore capacitá di riconoscere falsi.

24
A Appendice: Codice in linguaggio C del Software
Embedded
//*************************************************
// Author: Rossinelli Emanuele
// Author Contact: erossinelli@tiscali.it
// Project Start Date: 16 Dec 2010
// Last Modify Date: 14 Jan 2011
// SVMBanconote Firmware
// PicMicro (R): PIC24F04K201
// Clock: 8MHz (High Frequency Internal Oscillator)
// Data Packet: * sensore1 - sensore2 - #progressivo *
//*************************************************

#include <htc.h>
#include <stdlib.h>
#include <string.h>

#include "usart.c"
#include "mydelay.c"

// Fuses di configurazione
__CONFIG(FWDT, WDTDIS); //Watchdog disabled
__CONFIG(FGS, GWRPU); //Unprotected

/* COSTANTI */
#define SENSOR_VOID 250 //minimo valore dei sensori a vuoto
#define N_CAMPIONI 60

/* Variabili Globali */
int mode = 0;
/* 0: attesa della banconota 1: capturing 2: espulsione */
int nlettura = 0;

void SettingADC( int input ){

//input = 2 ==> AN2 (pin4)


//input = 3 ==> AN3 (pin5)
//input = 4 ==> AN4 (pin7)

if( input == 2 ){

//Configure port pins as analog inputs - AN2 (pin4)


AD1CHS = 0b0000000000000010;
}

if( input == 3 ){

//Configure port pins as analog inputs - AN3 (pin5)


AD1CHS = 0b0000000000000011;
}

if( input == 4 ){

//Configure port pins as analog inputs - AN4 (pin7)


AD1CHS = 0b0000000000000100;
}
}

int ReadingADC( int input ){

25
int value = 0;
SettingADC( input );

//reading adc
AD1CON1bits.ADON = 1;
DelayBigMs( 50 );
while(AD1CON1bits.DONE != 1) ;
value = ADC1BUF0;
AD1CON1bits.ADON = 0;

return value;
}

void main(void)
{

/******** SETTING CPU *********/


//Selezione della freq. con osc. interno a 8 Mhz OSCCON register
OSCCON = 0b0000000010000000;
CLKDIV = 0b0000000000000000;

/******** SETTING UART *********/


//Set data mode - Simplex - 8 Bit No Parity - 1 Bit Stop
U1MODE = 0b1000100000000000;

//Enable Transmit ( NOW & MASK ) | NEW CONF


U1STA = ( U1STA & 0b0000001100011111 ) | 0b1000000011000000;

//Set Baud Rate to 9600 bps (Fcy = Fosc/2 = 4 MHz)


U1BRG = 25; //25

//Output port
TRISB &= 0b1111111111111110;

//Enable UART TX
U1STAbits.UTXEN = 1;

/****** SETTING PORT OUTPUT *******/


//Porta di uscita del PWM (OC1, pin14, port A6)
TRISAbits.TRISA6 = 0;
PORTAbits.RA6 = 0;

/******** (Initial) SETTING ADC *********/


//Select voltage reference source
AD1CON2 = 0b0000000000000000; //Do not scan inputs

//Select the analog conversion clock to match the desired data rate
AD1CON3 = 0b0001111100000000; //Clock: Tcy/2

//Select the appropriate sample/conversion sequence and Turn on AD Module


AD1CON1 = 0b0000000011100110;

AD1CSSL = 0b0000000000000000;

AD1PCFG = 0b0000000000000000;

//Disattiva l’interrupt ADC


IEC0bits.AD1IE = 0;

/******** SETTING TIMER *********/


T1CON = 0; //timer reset
T1CONbits.TCS = 0; //internal clock

26
T1CONbits.TGATE = 0;
T1CONbits.TCKPS = 0b11; //set prescaler 1:256 ==> 1 oscill / 64u
T1CONbits.TSYNC = 0;

PR1 = 2150; //Timer1 period! ==> interrupt chiamato circa 10 times/sec

IFS0bits.T1IF = 0; //Reset Timer1 interrupt flag


IEC0bits.T1IE = 1; //Enable Timer1 interrupt

//No nested interrupt


INTCON1bits.NSTDIS = 1;

/******** START TIMER ***********/


T1CONbits.TON = 1;

/******** Loop infinito *******/


while(1)
{

} // End main

void SendPacket( int sens1, int sens2, int nlett ){

char pacchetto[15] = "";


char buffer[5] = "";
int i = 0, tmp;

//start packet
pacchetto[i++] = ’*’;

//sens1
itoa( buffer, sens1 );
tmp = strlen(buffer);
i += tmp;
strncat( pacchetto, buffer, tmp );

//boundary
pacchetto[i++] = ’-’;

//sens2
itoa( buffer, sens2 );
tmp = strlen(buffer);
i += tmp;
strncat( pacchetto, buffer, tmp );

//boundary
pacchetto[i++] = ’-’;

//nletture
itoa( buffer, nlett );
tmp = strlen(buffer);
i += tmp;
strncat( pacchetto, buffer, tmp );

//end packet
pacchetto[i++] = ’*’;
pacchetto[i] = ’\0’;

uart_putstring( pacchetto );

27
}

//INTERRUPT ROUTINE
void interrupt ISR( void ) @ T1_VCTR
{

if( IFS0bits.T1IF == 1 ){

long sens0 = 0, sens1 = 0, sens2 = 0;

//Logica
if( mode == 0 )
{

//non sto facendo capture; decidi se entrare o meno


sens0 = ReadingADC( 4 );

if( sens0 < SENSOR_VOID )


{
mode = 1; //inizia il capturing

//accendi motori
PORTAbits.RA6 = 1;
}
}

if( mode == 1 )
{
//siamo in capture mode

sens1 = ReadingADC( 2 );

sens2 = ReadingADC( 3 );

//il motore sta girando.


//Attendi che la banconota arrivi sotto ai sensori,
//se ancora non e’ arrivata
if( nlettura != 0 || ( sens1 < SENSOR_VOID && sens2 < SENSOR_VOID ) )
{
nlettura++;

//invia pacchetto
SendPacket( sens1, sens2, nlettura );

//abbiamo finito?
if( nlettura == N_CAMPIONI )
{
//vogliamo assicuriarci di aver espulso la banconota
mode = 2;

//resetta var globali


nlettura = 0;

}
}
}

if( mode == 2 ){

//spegni i motori quando il sensore di presenza e’ libero

28
sens0 = ReadingADC( 4 );

if( sens0 >= SENSOR_VOID )


{
mode = 0; //torna in fase di attesa nuova banconota

//tieni i motori accesi per un poco,


//in modo da espellere totalmente la banconota
DelayBigMs( 500 );

//spegni motori
PORTAbits.RA6 = 0;
}
}

IFS0bits.T1IF = 0; //reset interrupt


}//end if
}

29
Riferimenti bibliografici
[1] P. Priami A. Frosini, M. Gori, “A neural network-based model for paper
currency recognition and verification,” IEEE Transactions on Neural
Networks, Vol. 7, No. 6, 1996.

[2] Wikipedia, “Cross-validation,” http://en.wikipedia.org/wiki/


Cross-validation_(statistics).

[3] dtreg.org, “Svm - support vector machines,” http://www.dtreg.com/


svm.htm.

[4] Chih-Jen Lin Chih-Wei Hu, Chih-Chung Chang, “A practical guide to


support vector classification,” http://www.csie.ntu.edu.tw/~cjlin/
papers/guide/guide.pdf.

30