Gattari Gabriele 192861 Concetti base: L'idea su cui si basa il lavoro “Bagging Predictors” di Ross Quinlan è quella di utilizzare più predittori in luogo di uno solo. Questo al fine di aumentare l'accuratezza della previsione. Applicazioni di Intelligenza Artificiale L-S Bagging Predictors Ross Quinlan Chiameremo L il Training Set, cioè l'insieme di esempi che ci servono per costruire il nostro modello di predizione; ogni singolo esempio è composto da un insieme di attributi Xk e una classe di appartenenza Y. Il predittore che vogliamo costruire φ(x,L) vuole essere in grado di predire y dato in ingresso x, inteso come collezione di attributi. Fino a questo punto non ci sarebbe nulla di nuovo rispetto ad un qualsiasi predittore, l'idea è che se noi abbiamo a disposizione k diversi Training Set (Lk), indipendenti tra di loro, possiamo costruire k diversi predittori φk(x,L) e non affidarci alla previsione di un solo predittore ma mediare i risultati ottenuti da tutti. Come costruire il risultato aggregato dipende dalla natura della classe; se la classe è numerica viene fatta semplicemente la media altrimenti si procede per votazione, ovvero il risultato predetto da più predittori viene considerato quello giusto. Il problema sorge dal fatto che non possiamo considerarci così fortunati da avere k Training Set indipendenti a disposizione. Per ovviare a questo problema dobbiamo ricorrere alla costuzione di Training Set replicati, ovvero ottenuti dal training set originale. Saranno dei sottoinsiemi contenenti alcuni esempi più volte mentre altri esempi saranno omessi. Algoritmo: 1) I dati vengono divisi randomicamente in Training Set T e Learning Set L. Il training Set è costituito dal 10% di dati effettivi il resto sono replicati. 2) Un albero decisionale è costruito a partire da L utilizzando la 10-Fold Cross-Validation. Utilizzare T come Test Set per ottenere il misclassification rate. 3) Selezionare un esempio di Bootstrap Lb da L e costruire un albero decisionale. Il Training Set originale è usato come Test Set per selezionare il Best Pruned Sub Tree. Questa operazione è ripetuta 50 volte al fine di creare i predittori Φ1 , Φ2...Φ50. 4) La predizione finale è quella più votata da tutti i predittori. Se c'è un pareggio viene presa la classe di indice più basso. La proporzione delle volte che la predizione differisce dalla classe vera è il misclassification rate. 5) La divisione randomica tra L e T viene ripetuta 100 volte e i misclassification rate finali sono la media delle 100 iterazioni. Cosa vogliamo ottenere? Il fine ultimo della classificazione è di estrarre/creare conoscenza da un insieme di dati. Queste tecniche vengono utilizzate in ambiti dove è necessario poter classificare delle situazioni, dei prodotti in base a fattori noti. Ad esempio possiamo utilizzare tecniche di classificazione in ambito economico; supponiamo di essere una banca e di volere decidere se concedere un prestito, in base a informazioni come: immobili posseduti, credenziali bancarie, garanti ecc. Posso decidere se conferire il prestito o meno. Inoltre avendo a disposizione dei dati storici possiamo prevedere andamenti futuri. gli elementi della distribuzione X che valgono x. Un altro esempio: Questo è il numero di bit che devo trasferire se sia mittente che ricevente sono a conoscenza del valore di X. Ora possiamo fare l'ultimo passo, quanti bit posso risparmiare nella trasmissione di Y se entrambi conoscono la distribuzione X, questo è l'information gain Information Gain mi consente di decidere quale tra tante variabili X mi dà più indicazioni nell'ottica di prevedere il valore di Y. Questo è proprio quello che ci serve nella costruzione degli alberi decisionali. Metodo 10-Fold Cross-Validation avendo a disposizione le informazioni riportate in tabella (esempi precedentemente elaborati) se mi si presenta un utente di 32 anni potrò facilmente prevedere che con un probabilità del 73% è una persona povera. Un po' di teoria dell'informazione: Supponiamo di voler trasferire lungo una linea binaria seriale una distribuzione di variabili. Se i simboli che voglio trasferire ( ad esempio Q,W,E,R,T,Y ) sono equiprobabili non posso fare altro che codificare ogni simbolo con 3 bit Q=000, W=001... Ma se, ad esempio, le lettera E,R sono molto frequenti nella mia trasmissione e le lettere T,Y molto rare allora posso codificare con meno bit E ed R e più bit Y e T (E=0, R=1; Y=1110, T=1111) risparmiando mediamente dei bit. Supponendo che la probabilità sia così distribuita: p(Q)=p1, p(W)=p2... Qual'è il minor numero di bit, in media, per simbolo necessario per trasmettere una sequenza della distribuzione? Chiamiamo questa Entropia della distribuzione e la indichiamo con H(X). Più questo valore è basso più la distribuzione è varia e quindi più bit posso risparmiare, più è alto più la distribuzione è uniforme e posso risparmiare di meno. Facciamo un ulteriore passo, supponiamo di voler predire il valore di un variabile Y conoscendo il valore di un'altra variabile X. Dobbiamo definire cos'è l'entropia condizionale H(Y|X=x), cioè l'entropia della distribuzione Y per Per capire il metodo 10-Fold convine per prima cosa introdurre un metodo più semplice che però è alla base del 10-Fold, il metodo holdout. Questo metodo consiste nel dividere i dati a nostra disposizione in due, il primo chiamato Training Set e il secondo Test Set. L'albero decisionale ( o un qualsiasi altro strumento di apprendimento automatico) viene addestrato, costruito, solo con i dati nel Training Set. Per testare il modello così costruito viene utilizzato il Test Set, insieme di dati che il nostro sistema non ha mai visto. Per questo metodo ci sono dei pro e dei contro. Pro • • Evito di sovrastimare il modello. Può avvenire che il modello creato è troppo legato al Training set. Quindi se tento di testare la bontà del modello con quegli stessi dati potrei avere risultati migliori di quanto non sia capace il modello in realtà. Testando il modello con dati che non ha mai visto non si presenta questo problema. Rapido Contro • Il risultato può dipendere troppo dai dati scelti. Cioè scelte diverse di divisione in Training Set e Test Set possono portare a modelli troppo differenti. Proprio per rimediare a questo problema viene introdotto il metodo K-Fold Cross-Validation. Questo metodo suggerisce di dividere i dati a nostra disposizione in k sottoparti e ripetere il metodo holdout per k volte. Una delle k parti è utilizzata come Test set mentre le altre sono congiunte per creare il Training Set. Anche questo metodo ha dei pro e dei contro: Pro • Il modello non dipende più da come i dati sono divisi in training set e test set. Contro • L'algoritmo è più lento dovendo essere ripetuto per k volte. Schema concetti generali: Emergono subito tre macro blocchi in cui è possibile dividere il sistema. Sottosistema di Input - Deputato all'acquisizione dei dati da file. Sottosistema di Presentazione – Deputato alla presentazione dei dati all'utente. Sottosistema di Creazione Predittori – Deputato alla creazione degli Alberi mediante le due tecniche citate. Sottosistema di Classificazione – Deputato all'utilizzo dei predittori per la classificazione di esempi Implementazione algoritmo: Il progetto si divide in due parti la prima parte svolge le predizioni seguendo il metodo k-fold crossvalidation mentre la seconda parte utilizza la tecnica dei bagging predictors. Nonostante ciò la parte del caricamento dati è in comune in quanto i dati su cui vanno a lavorare i due algoritmi sono i medesimi. 1) Caricamento dati: Grazie all'interfaccia grafica posso selezionare un file dal FileSystem proprio come in una qualsiasi applicazione grafica cioè attraverso una finestra di scelta file. I dati vengono caricati tutti insieme senza fare distinzioni a priori tra Training Set e Test Set. Le fonti dati sono dei file non strutturati in cui vengo riportati in maniera ordinata i valori degli attributi dei vari esempi. Vengono riportate su un file diverso le intestazioni dei vari attributi. La classe riportata sopra ha il compito di leggere questi file, grazie all'ausilio del FileInputStream, e di costruire il Training Set (qui con TrainingSet si intende l'insieme di dati a disposizione da questo collettivo verranno poi estrapolati i veri TrainingSet e Test Set in base alle varie Tecniche). Nella fattispecie il metodo: • BuildType consente di leggere le intestazioni degli attributi; • getElement consente di dividere i vari elementi letti dal file; • Build consente di costruire il TrainingSetElement secondo la sottostante struttura. La classe TrainingSet è una semplice collezione di TrainingSetElement. Il TrainingSetElement è la naturale trasposizione del concetto di esempio utilizzato per l'addestramento di un albero decisionale; ovvero contiene un identificativo univoco, una classe che rappresenta appunto la classificazione che vogliamo ottenere e una serie di attributi. 2) Divisione dei dati in Training Set e Test Set (e Validation Set) A questo punto i dati sono stati caricati dai file e dobbiamo procedere alla divisione. C'è però prima da fare una precisazione, nell'articolo in esame non si parla esplicitamente di Validation Set. Per quanto riguarda il K-Fold Cross-Validation occorre semplicemente dividere tutti i dati a nostra disposizione in k sottoparti di eguale dimensione per poi effettuare una fusione di k-1 di queste parti ottenendo così il Training Set e utilizzando poi la parte restante come Test Set. Questo viene svolto dal metodo SplitK. Invece per i motivi già descritti in precedenza la metodologia basata sui bagging ha bisogno di una fase di potatura e per effettuare questa potatura c'è bisogno di un set di dati non proveniente ne dal Training Set ne dal Test Set. Quindi a tutti gli effetti occorre dividere i dati a nostra disposizione in tre parti. Questa mansione viene svolta dal metodo Split Il metodo SplitK è personalizzabile e consente di decidere quale percentuale dei dati deve essere riversata nel Training Set e quanta nel Validation Set i dati rimanenti compongono il Test Set. L'assegnazione di un esempio ad un gruppo viene fatta randomicamente. BestIG che determina l'attributo con l'information gain più alto. Il metodo Freq serve per raggruppare i TrainingSetElement in base alla classe. Sapere quanti TrainingSetElement, e quindi quanti esempi, appartengono ad una certa classe è utile al momento del calcolo dell'entropia. L'elaborazione parte da un TrainingSet passato come parametro. Il metodo EntropyTS prende come input la struttura dati calcolata in precedenza e grazie a questa calcola l'entropia del Training Set Il metodo Select crea delle partizioni,ovvero divide i TrainingSetElement in base ai valori di un certo attributo che viene passato come parametro. Il metodo IsNumericdetermina se l'attributo in questione è un attributo numerico oppure un attributo alfanumerico. Il metodo Entropy calcola l'entropia condizionale rispetto ad un particolare valore di un attributo; è il passo intermedio per la determinazione dell'entropia. Quest'ultimo metodo viene ripetuto per tutti i valori dell'attributo. Algoritmo k - fold cross - validation A questo punto ho i dati divisi in modo da poter costruire i Training Set e i Test set come spiegato in precedenza. Il compito di costruire l'albero decisionale è affidato alla classe TNode. Il modello sopra rappresenta i nodi dell'albero decisionale. Come si vede si è scelto di utilizzare un pattern composite che risulta essere molto comodo in questo frangente in quanto mette in evidenza le differenze tra i nodi Foglia (cioè quelli che non hanno successori e quindi quelli da cui leggiamo la previsione) e i nodi intermedi Tnode che devono gestire le problematiche legate ai figli. Ogni nodo ha un suo TreeBuilder che ha il compito di determinare l'attributo migliore per creare i figli. Il TreeBuilder ha il compito di calcolare il migliore information gain tra tutti gli attribuiti in modo da poter così generare i nuovi figli che mi conducano il più velocemente possibile ad una classificazione. Viene quindi creato un nodo che è la radice dell'albero decisionale, e poi viene invocato il metodo Build del Tnode che costruisce ricorsivamente l'albero. Il metodo Build non fa altro che costruire materialmente l'albero secondo le indicazioni della classe TreeBuilder e nello specifico del metodo Attributi numerici: Un'ulteriore considerazione deve essere fatta per gli attributi numerici, infatti essi devono essere trattati come appartenenti a degli intervalli e non come valori a se stanti onde evitare l'esplosione dei figli nei casi in cui viene scelto un attributo numerico per la generazione di un nuovo livello. La scelta che si è fatta in questo caso è quella di cercare una soglia che potesse dividere gli esempi in due insiemi. La soglia viene scelta in base ai valori degli attributi, il metodo NumericPartition ha il compito di selezionare la soglia migliore ovvero quella che porta ad avere il miglior information gain. Valori sconosciuti: Ancora occorre fare attenzione nel caso ci siano dei valori sconosciuti. In questo caso si è scelto di splittare l'esempio. Ovvero se stiamo effettuando un test su un certo attributo A e l'esempio che stiamo prendendo in esame in questo momento ad esempio E ha per quell'attributo un valore non noto; si sceglie di inserire quell'esempio in tutti i possibili figli del nodo in questione. A questo punto è necessario però introdurre un meccanismo per cui sia possibile considerare il contributo di questi esempi come parziale, ovvero quando compio quest'azione commetto inevitabilmente un errore e quindi cerco di limitare quest'errore. In altre parole se dal test su A risulta che il nodo in questione avrà due figli differenziati in base A=”cane” B=”gatto” noi mettiamo l'esempio E sia nel nodo “cane” che in quello “gatto” pur sapendo che uno dei due è irrimediabilmente sbagliato. A questo punto occorre associare agli esempi un peso che mi dice quanto quell'esempio contribuisce al fine della classificazione. Un esempio senza attributi nulli avrà un peso di 1 infatti siamo sicuri dei valori dei suoi attributi e non è stato necessario effettuare gli split di cui sopra, un esempio che ha subito degli split avrà un valore di peso minore di uno e calcolabile al momento della classificazione in base al valore degli altri attributi che fanno parte del test. Riprendendo l'esempio di prima, se ci sono 5 esempi “cane” e 2 “gatto” l'esempio E che viene messo nel sottonodo “cane” avrà peso 5/7 mentre il sottonodo “gatto” 2/7. Classificazione: Dopo aver costruito gli alberi è giunto il momento di utilizzare gli esempi ancora intonsi facenti parte del Test Set. Questa operazione è affidata al metodo ParsingTest della classe Visual. Il compito di questa classe è quello di costruire degli oggetti Classifier che prendono in pasto un albero decisionale e un Test Set ed effettuano la classificazione degli esempi test attraverso l'albero decisionale, restituendo il numero di classificazioni errate. Dopo aver costruito l'oggetto Classifier tramite il metodo ricorsivo Build, si analizzano via via tutti i test e si scelgono i figli dell'albero da visitare in base ai valore degli attributi dell'esempio del Test Set in esame fino a che non si arriva ad una foglia e a questo punto si può decidere, confrontando la classe predetta dalla foglie e quella effettiva dell'esempio, se l'albero ha classificato bene l'esempio o meno. Anche in questo caso occorre trattare con i guanti gli attributi nulli. Se nel momento in cui vado ad effettuare un test il valore dell'esempio inerente al test risulta ignoto devo procedere ad uno splittaggio. Ovvero proseguo la classificazione lanciando quest'ultima su tutti i nodi figli del nodo in cui sto' facendo i test e riassumendo alla fine tutti i risultati dati dalle sotto-classificazioni. Algoritmo Bagging Predictor: A questo punto abbiamo a disposizione i dati divisi nei tre sottogruppi che ci interessano: Training Set, Test Set e Validation Set. Viene di seguito creata la classe BootRepli che ha il compito di creare i Training Set Replicati e i predittori secondo le specifiche dell'algoritmo. Grazie al metodo BuildReplica possiamo creare una replica del training set che conterrà lo stesso numero di esempi contenuti dal training set originale ma alcuni esempi saranno presenti più volte mentre altri non saranno presenti affatto. Ancora una volta la scelta di quali esempi devono andare a far parte del Training set viene fatta randomicamente. Ora avendo a disposizione il Training Set possiamo costruire un predittore cioè un albero decisionale. L'albero viene costruito con la tecnica degli information gain esattamente come veniva costruito nella parte precedente dei k-fold. L'algoritmo consiglia poi di eseguire una potatura degli alberi così costruiti in modo da eliminare eventuali dettagli che potrebbero portare all'overfitting Potatura: La potatura dell'albero ha come scopo di migliorare l'accuratezza della classificazione che si otterrà con quel predittore. Infatti l'albero potrebbe contenere dei rami che nascono dalle peculiarità del training set e non dalle effettive caratteristiche dei dati. La tecnica utilizzata per la potature consiste nel prendere in considerazione un nodo alla volta. Per ogni nodo dell'albero si tenta di sostituire al sottoalbero, di cui questo nodo è radice, una foglia. Per ogni modifica fatta sull'albero è necessario testare se questa modifica abbia portato ad un miglioramento o ad un peggioramento. Ovviamente nel caso in cui ci sia stato un non peggioramento della situazione il sottoalbero viene tramutato in nodo altrimenti il sottoalbero viene lasciato. Ora però si pone il problema di come misurare i miglioramenti/peggioramenti dell'algoritmo, questo viene fatto ancora attraverso una classificazione, ovvero si prende un ulteriore set di dati, chiamato Validation Set, non facente parte ne de Training Set ne del Test Set. L'indipendenza dal set di generazione dell'albero e l'indipendenza dal set di test ci assicura che non ci sia overfitting. Questo compito è svolta dalla classe Pruner. Effettuata la potatura i predittori sono pronti, uno alla volta sono chiamati in causa e determinare la classe di appartenenza degli esempi presenti nel Test Set. Al termine della classificazione (che avviene nella maniera tradizionale della navigazione dell'albero) si confrontano i risultati dati dai vari predittori e si procede alla generalizzazione del risultato. Schema Generale: Bagging Predictors WaveForm Glass Soybean 21,7% 31,05% 12,66% Come si evince dalla tabella i risultati mostrano un'effettiva convenienza nell'utilizzo dell'algoritmo dei bagging predictors rispetto all'algoritmo K-Fold Cross-Validation ancorchè il peso a livello computazionale di quest'ultimo risulti molto minore. Considerazioni: I risultati ottenuti sono in linea con quelli riscontrati da Quinlann nel suo articolo. In alcuni casi le percentuali ottenute dal sistema in esame sono state più scarse di quelle ottenute attraverso c4.5 strumento utilizzato per la creazione di alberi decisionali. Questo è causato forse dalla condizione di terminazione, che viene implementata in maniera sofisticata in c4.5 mentre nel sistema in esame si procede semplicemente fino in fondo creando anche foglie con un solo elemento. Inoltre sicuramente un sistema commerciale come c4.5 introdurrà delle ottimizzazioni che non erano l'obbiettivo di questo sistema. Sì è notato effettuando dei test di quanto sia importante l'utilizzo dei bagging predictors specialmente per la loro caratteristica di ripetere le classificazioni molte volte. Infatti nonostante i Training Set tra loro siamo molto simili, partono tutti dalla stessa matrice, i risultati prodotti dai predittori sono molto altalenanti anche di svariati punti percentuale. Questo porta a dire che anche una predizione effettuata con l'algoritmo k-fold cross validation, che pure tende a limitare questo effetto, è nulla in confronto all'affidabilità offerta dall'algoritmo bagging. Nello schema generale non sono presenti le classi che riguardano la gestione grafica dell'applicazione in quanto queste non rivestono un ruolo di interesse nell'ambio del progetto pur essendo fondamentali per la comoda e corretta visualizzazione e interpretazione dei risultati ottenuti. Da ulteriori test eseguiti è evidente come la ripartizione del data set in Training, Test e Validation set è fondamentale per la avere dei buoni risultati. La configurazione migliore, secondo i test effettuati, è quella che vuole l'80% dei dati disponibili riservati al Training Set, il 10% al Test Set e il restate 10% al Validation set. Ovviamente questi valori non sono assoluti ma soggetti ad oscillazioni dettate essenzialmente dalla tipicità dei Data Set utilizzati. Risultati ottenuti: Per prima cosa occorre fare delle precisazioni, al fine di ottenere un software più snello e quindi più facilmente presentabile si è scelto di non eseguire l'algoritmo K-Fold Cross-Validation cento volte come viene suggerito dal paper di Quinlan. Infatti l'algoritmo viene eseguito una volta solamente al solo scopo di mostrare le differenze tra gli alberi costruiti con un algoritmo e con l'altro. Resta comunque il fatto che i dati riportati sotto risultati dei test sono la media di numerosi test sull'algoritmo K-fold e quindi attendibili a livello statistico. Il motivo per cui si è scelto di fare ciò è stato solo per motivi di tempi nei test e nelle presentazioni. Sistema Software: La prima operazione che si può eseguire è il settaggio dei parametri. Nello screenshot sotto si può notare come tutti possibili parametri dei due algoritmi siamo personalizzabili, questo comporta che Un'ulteriore precisazione, sebbene il test riguardo all'algoritmo Bagging venga svolto come richiesto dal paper, cioè costruendo cinquanta predittori ed utilizzandoli per il test, e ripetendo questi test per 100 volte, non vengono tenute tracce dei vari predittori costruiti ne dei training Set Test Set Relativi. Questo sempre al fine di non caricare il sistema di un'enormità di informazioni irrilevanti, infatti viene messo a disposizione dell'utente il risultato dell'ultima iterazione, e i dati di sintesi di tutte le altre iterazioni. sia possibile effetture dei test che evidenzino il variare delle performance al variare dei parametri. K-Fold Cross-Validation WaveForm Glass Soybean 29,02% 32,76% 15,32% Nella figura sottostante viene mostrata la videata di selezione dei file, come si nota è possibile scegliere un file dal file system come in ogni applicazione grafica. Purtroppo i dati a disposizione sono dati immagazzinati in semplici file di testo. Questo ha portato a dover scrivere una procedura ad-hoc per ogni data set. Immediatamente alla selezione del file di esempi da utilizzare viene lanciato l'algoritmo k-fold. Nello screenshot sottostante si possono vedere le informazioni più importanti riguardo ai risultati dell'algoritmo k-fold. Grazie ad un intuitivo sistema di visualizzazione è possibile nella videata i vari alberi costruiti con i l'algoritmo e si possono conoscere anche gli esempi contenuti nei singoli nodi, semplicemente selezionando un nodo dall'albero. Per ogni nodo vengono specificati sia l'attributo su cui viene fatto il test sia le soglie che dividono gli esempi nei vari figli. Ad esempio nella videata sottostante vediamo come sia stata eseguita un 10-Fold Cross-Validation, e come si presenti l'albero decisionale. Si possono notare, in basso, gli elementi presenti nel nodo denominato “167” appartenente all'albero decisionale denominato “tree3” Grazie ad altre videate possiamo accedere a tutti gli altri dati importanti per la comprensione dell'algoritmo. Ad esempio nella videata sottostante possiamo notare come siano visualizzati (a seconda dell'albero selezionato precedentemente) gli esempi presenti nel Training Set e nel Test Set. A questo punto semplicemente premendo sul tasto Operazioni -> Bagging si lancia il secondo algoritmo. Come si può notare l'interfaccia è del tutto simile a quella costruita e descritta per l'algoritmo k-fold cross-validation. Le ultime informazioni visualizzate, riguardanti l'algoritmo k-fold, sono relative agli errori di predizione riscontrati in fase di Test. Nella videata sotto si possono notare sulla destra tutti gli alberi decisionali prodotti con associati i valori di errori (su base unitaria). Selezionando uno di questi alberi vengono mostrate le informazioni aggregate sulla destra. Queste informazioni mostrano quali esempi siano stati predetti da quell'albero, l'esito della predizione e il precorso all'interno dell'albero decisionale. Grazie a queste informazioni è possibile ricostruire tutto l'iter di predizione a partire dall'elemento del test set in questione (è presente l'identificativo univoco), passando per i vari nodi dell'albero (vengono tracciati gli id dei nodi) fino ad arrivare alla classe predetta e quindi al risultato della predizione sintetizzato con il simbolo “V” nel caso la predizione sia andata a buon fine e con il simbolo “X” in caso contrario. Sotto nella videata viene mostrata la percentuale riassuntiva di tutti k responsi dei vari alberi. Tramite le altre videate è possibile accedere ad altri dati riguardanti l'algoritmo come il Training Set replicato utilizzato per la costruzione dell'albero... ...oppure conoscere i path all'interno di ogni albero per ogni training set element... ... o per finire visualizzare gli errori commessi dai vari alberi in fase di test.