Anno Accademico 2010/2011 Salvatore Alaimo 05 maggio 2011
Un algoritmo genetico è un metodo euristico di ottimizzazione e ricerca, ispirato al principio della selezione naturale di Charles Darwin che regola levoluzione biologica. Gli algoritmi genetici sono applicabili alla risoluzione di un'ampia varietà di problemi d'ottimizzazione non indicati per gli algoritmi classici, compresi quelli in cui la funzione obiettivo è discontinua, non derivabile, stocastica, o fortemente non lineare.
Un tipico algoritmo genetico parte da un certo numero di possibili soluzioni (individui) chiamate popolazione. Si provvede a far evolvere ogni soluzione nel corso dell'esecuzione. Levoluzione è ottenuta mediante: – Parziale ricombinazione delle soluzioni: ogni individuo trasmette parte del suo patrimonio genetico ai propri discendenti; – Mutazioni casuali nella popolazione di partenza: sporadicamente nascono individui con caratteristiche diverse dai genitori. In generale, per ciascuna iterazione (generazione): – Si selezionano alcuni individui della popolazione corrente; – Con essi si generano nuovi elementi della popolazione stessa (riproduzione o crossover, mutazione) – Gli elementi generati sostituiranno un pari numero d'individui già presenti – Si forma così la popolazione per la successiva iterazione.
La soluzione del problema è codificata attraverso una struttura (tipicamente stringa) detta Gene. Per stabilire la «bontà» di un gene, si definisce una funzione, detta Funzione di Fitness.
Crossover: è loperazione che definisce la programmazione di un gene sulla base di altri due geni detti genitori. Single-Point CrossoverTwo-Point Crossover Cut-Splice Crossover Uniform Crossover
Mutazione: è loperazione che mantiene la diversità genetica da una generazione alla successiva.
La successione di iterazioni evolverà verso una soluzione ottimale (locale o globale) del problema. Finita la fase di evoluzione, sono tenute solo le soluzioni che meglio risolvono il problema. Alla fine ci si aspetta di trovare una popolazione di soluzioni che risolva adeguatamente il problema posto.
Non vi è modo di decidere a priori se l'algoritmo sarà effettivamente in grado di trovare una soluzione accettabile. Utilizzo: problemi di ottimizzazione per i quali non si conoscono algoritmi di complessità lineare o polinomiale.
Elaborata fondamentalmente da John R. Koza È un metodo per la generazione automatica di programmi, a partire da una descrizione ad alto livello del task da svolgere Basato sul principio darwiniano della selezione naturale. Si avvale di operazioni capaci di alterare l'architettura di detti programmi e di prendere decisioni sull'uso delle subroutine, dei loop, della ricorsione e della memoria. Costituisce un'estensione degli algoritmi genetici al caso di popolazioni costituite da programmi di dimensione variabile. La popolazione risulta composta da un numero di programmi, i quali, mediante operatori genetici, producono, in un certo numero di generazioni, il programma che risolve al meglio un problema.
La struttura base è un ad albero, il cui corpo è costituito da funzioni (primitive), mentre i nodi terminali rappresentano variabili o costanti numeriche. + * 3^ * * 3^ g x * 3^ x2 5
Struttura generica di un algoritmo: – Costruzione di una popolazione di programmi iniziale a partire dalle primitive disponibili – Finchè non si ottiene una soluzione accettabile: Eseguire ogni programma e valutarne la «fitness»; Selezionare i programmi a cui applicare gli operatori genetici; Applicare gli operatori genetici ai programmi selezionati; Costruire una nuova popolazione che contiene: – Il miglior programma ottenuto fino ad ora (con alta probabilità) – Alcuni dei programmi aggiornati al passo precedente – Alcuni programmi costruiti casualmente per ottenere una popolazione di dimensione identica a quella iniziale. – Restituire il miglior programma ottenuto.
Le primitive dipendono dalla natura del problema da risolvere. Per semplici problemi numerici, esse sono tipicamente le funzioni aritmetiche. ({+, -, *, /}) Affinché il problema sia risolto correttamente, è, necessario che siano valide le seguenti proprietà per linsieme di primitive: – Sufficiency: esprimere una soluzione del problema solo in funzione delle primitive. (Es.: {AND, OR, NOT} per i problemi di combinatoria booleana) – Closure: si suddivide in altre due proprietà: Strong Type Consistency: tutte le primitive devono prendere in input e restituire in output valori dello stesso tipo, indipendentemente dal compito svolto. Evaluation Safety: ogni primitiva deve essere in grado di gestire eventuali errori e produrre un risultato valido in ogni situazione. (Es.: overflow numerici gestiti come wrap around, divisioni per zero gestite come infinito)
La funzione di fitness consente di valutare la bontà del programma in base allerrore commesso nella risoluzione del problema e alla complessità temporale, spaziale e/o computazionale. Principali operatori genetici: – Crossover o riproduzione: Sessuata Asessuata (Imitazione) – Mutazione
Crossover + * 3^ x2 5 * + 9x exp x + * 3^ x2 Exp x
Imitazione * + 9x exp x + * ax b
Mutazione * + 9x exp x * + 9cos x exp x
Problemi nellapproccio di Koza alla programmazione genetica: – Schema troppo generico in cui la manipolazione delle costanti risulta essere troppo oneroso. – Convergenza troppo rapida dellalgoritmo ad una soluzione locale, piuttosto che globale, del problema. – Maggiore complessità delle funzioni usate implica maggiori parametri da aggiustare, quindi, maggiore sensibilità al rumore. – Se la funzione di fitness richiede la pura aderenza ai dati, la convergenza potrebbe non essere mai raggiunta, provocando un aumento della complessità dei programmi.
Il problema: data una serie di valori, trovare quellespressione matematica che meglio rappresenta landamento dei dati. Aspetti problematici: – Complessità della funzione risultato; – Aderenza ai dati e rumore; – Misura della bontà del risultato. Approcci tradizionali: – Regressione – Interpolazione
Per valutare laderenza ai dati si è scelto di usare la misura dellerrore basata sullo scarto quadratico medio: – continua al variare della parametrizzazione – non continua al variare della forma della forma di f. La ricerca della parametrizzazione è una ricerca di un minimo di una funzione continua (o continua a tratti). La ricerca della forma è più simile ad una ricerca in ampiezza, utilizzando delle euristiche per limitare lo spazio di ricerca.
Per implementare lalbero di definizione di una funzione sono utilizzati due vettori: – Vettore «form»: descrive la forma della funzione (dalle foglie alla radice); – Vettore «constants»: contiene le costanti numeriche della funzione. Lalgoritmo di ricerca del minimo (globale e locale) ottimizza i valori del vettore «constants» per minimizzare lerrore. Lalgoritmo di ricerca dellapprossimazione altera il vettore «form» per trovare la migliore famiglia di funzioni che risolve il problema. Ogni elemento del vettore form contiene tre valori (FormEntry): – Operatore (eType); – Primo Operando (op1): è lindice, nel vettore form, della radice del sottoalbero sinistro radicato nellelemento corrente; – Secondo Operando (op2): è lindice, nel vettore form, della radice del sottoalbero destro radicato nellelemento corrente. Nel vettore form, il primo operando è posto prima del secondo operando; la radice di ogni sottoalbero è posta dopo ogni operando.
Operatori a due argomenti: – Addizione (0): f(a)+g(b) – Sottrazione (1): f(a)-g(b) – Moltiplicazione (2): f(a)*g(b) – Divisione (3): f(a)/g(b) – Potenza (4): f(a) k Operatori a un argomento: – Ripetizione (20): serve a rappresentare funzioni del tipo f(g(x)) senza ripetere più volte la funzione g(x) – Esponenziale (21): e f(x) – Seno (22): sen(f(x)) – Coseno (23): cos(f(x)) Costante (100): rappresenta una costante del vettore «constants» Variabile (101): rappresenta una variabile da sostituire durante la valutazione della funzione
Per calcolare f(x) si utilizza un vettore di comodo «tempCalc» della stessa dimensione di «forms». Il calcolo avviene mediante il seguente algoritmo: – Sia x il vettore dei dati – For i := 1 to size(tempCalc) If tipo(forms[i]) is «costante» then – tempCalc[i] := constants[op1(forms[i])] ElseIf tipo(forms[i]) is «variabile» then – tempCalc[i] := x[op1(forms[i])] Else – Applica loperazione tipo(forms[i]) ai valori tempCalc[op1(forms[i])] e, se necessario, tempCalc[op2(forms[i])] e poni il risultato in tempCalc[i] End – End – If errore di calcolo then return + – Return tempCalc[size(tempCalc)]
IndiceTipoOp1Op2 0CON0 0 1VAR0 0 2MUL01 3CON1 1 4SUM23 «forms» IndiceValore 03,0 12,0 «constants» «tempCalc»
È un algoritmo che data una funzione f(x,c) calcola la parametrizzazione «c» che minimizza lerrore Composto da due algoritmi genetici: – Algoritmo del minimo locale: sfrutta una piccola popolazione di parametrizzazioni per trovare un minimo locale della funzione errore; per velocizzare la convergenza esegue una analisi di sensibilità al fine di stabilire lincremento e la direzione di incremento – Algoritmo del minimo globale: sfrutta una popolazione di «minimi locali» per trovare il minimo globale dellerrore; Una popolazione di «minimi locali» è una parametrizzazione c 1 della funzione f(x, c 1 ) calcolata mediante lalgoritmo del minimo locale.
Data una funzione f(x, c) e una parametrizzazione iniziale, lalgoritmo dei «minimi locali» cerca di migliorare la parametrizzazione per minimizzare localmente la funzione errore. La velocità di convergenza dipende da: – numero di parametri presenti; – posizione dei parametri; – probabilità che scelta una parametrizzazione a caso, essa sia la migliore. Per migliorare la convergenza si utilizzano due operatori realizzati appositamente, piuttosto che le tecniche tipiche degli algoritmi genetici: – Mutazione gaussiana; – Ricerca nella direzione. Per migliorare le prestazioni, ad ogni iterazione, è mantenuta solo la miglior parametrizzazione, e le altre sono scartate.
Mutazione gaussiana: – Genera una nuova parametrizzazione in un intorno di quella di partenza, la cui distanza è funzione dellerrore. (a maggior errore corrisponde maggior distanza, e viceversa) – Per fare ciò si utilizza una distribuzione di probabilità gaussiana nello spazio dei parametri centrata nel punto corrispondente alla vecchia parametrizzazione. – Per calcolare la varianza della distribuzione si esegue una analisi di sensibilità. Si calcola una piccola variazione della parametrizzazione e si valuta il rapporto tra la distanza degli errori e la distanza delle parametrizzazioni. – Numeri casuali distribuiti come descritto precedentemente forniscono la variazione casuale della parametrizzazione.
Ricerca nella direzione: – Si costruiscono nuove parametrizzazioni nella direzione in cui lerrore diminuisce, fino a trovare il punto in cui lerrore inizia ad aumentare o si raggiunge il numero massimo di iterazioni. – Velocizza la convergenza quando le parametrizzazioni sono troppo lontane dal punto di minimo errore. – Difetto: La parametrizzazione inizia ad oscillare attorno alla posizione del minimo ma non lo raggiunge mai. f(x) = ax+b a b Parametrizzazione: c=(a,b)
Sia – f(x,c) una funzione con parametrizzazione c (casuale o trovata precedentemente) – e(f, P) una stima dellerrore di f sui punti del dataset P – n il numero di iterazioni oldC := c, oldE := e(f(x, oldC), P) For i := 1 to n – c := gaussianMutation(oldC) – e := e(f(x, c), P) – If oldE > e then d := c - oldC bestC := exploreDirection(d, c) oldC := bestC, oldE := e(f(x, bestC), p) – End End Return oldC
Data una funzione f(x) trovare la parametrizzazione «c» che minimizza lerrore sui punti del dataset P. Gli operatori genetici definiti per lalgoritmo sono: – Crossover – Mutazione Lalgoritmo del minimo globale è utilizzato nellalgoritmo di ricerca dellapprossimazione, come unico operatore che lavora sui parametri della funzione. Si ottengono buoni risultati anche con popolazioni di piccole dimensioni (Es. 12 individui e 6 sopravvissuti).
Crossover: – Date due parametrizzazione (genitori), si costruisce una nuova parametrizzazione (figlio) che, nello spazio dei parametri, risulta essere compresa tra i due. – Accelera la convergenza: Date due parametrizzazioni che stanno convergendo allo stesso minimo locale. Lalgoritmo riesce a produrre, con un singolo passo,una parametrizzazione molto più vicina al minimo rispetto alle due precedenti. – Rende lalgoritmo meno sensibile ai minimi locali nel caso in cui il minimo globale è circondato da tanti minimi locali. – Importante linizializzazione perché non cerca nellintero spazio dei parametri. Mutazione: – Si esegue lalgoritmo dei «minimi locali» per costruire una parametrizzazione che si avvicini il più possibile ai minimi dellerrore.
Sia – f(x) una funzione con valutazione dellerrore e(f,P) – n il numero di individui della popolazione – k il numero di sopravvissuti per generazione – m il numero di iterazioni dellalgoritmo dei «minimi globali» – t il numero di iterazioni dellalgoritmo dei «minimi locali» Poni nel vettore «pop» una n parametrizzazioni casuali. For i := 1 to m – For p in pop Aggiorna p con il risultato di localFinder( f(x,p), e(f,P), t) – End – Best := Elemento p di pop tale che e(f(x, pop), P) è minimo – Survivor := scegli a caso k elementi di pop dando priorità a quelli con errore più piccolo – Children := combina con loperatore crossover gli elementi di Survivor scegliendoli a caso – Aggiorna «pop» con la nuova popolazione composta da: Best Le parametrizzazioni contenute in Children n-(1+k/2) parametrizzazioni casuali. End Return Elemento p di pop tale che e(f(x, pop), P) è minimo
Mutazione: – Presa una funzione, od un sottoalbero del suo primo operatore, la combina ad una generata casualmente utilizzando un operatore di somma o prodotto. – Rende possibile la risoluzione di problemi più complessi, ma rende anche molto più instabile la convergenza. (Problema: a maggiore complessità equivale minor crescita della fitness) Imitazione: – Loperatore costruisce una nuova funzione con le seguenti caratteristiche rispetto a quella di partenza: Profondità dellalbero di definizione uguale o maggiore di una unità; Numero di costanti e variabili scelte casualmente; Numero di funzioni trascendenti al più uguale a quello della funzione di partenza. – Aumenta la probabilità di trovare la forma giusta della funzione con piccoli incrementi della complessità.
Sia – P un insieme di punti – e una valutazione dellerrore di una qualsiasi funzione f sullinsieme dei punti – n il numero di iterazioni – m il numero di funzioni per generazione – k il numero di sopravvissuti per generazione Costruisci il vettore «pop» con m funzioni generate casualmente For i := 1 to n – Aggiorna gli elementi di «pop» tramite lalgoritmo dei minimi globali – Best := Lelemento di «pop» per cui lerrore e risulta minimo – Survivors := SelectSurvivors(pop, k) – pop := CreateNewPopulation(Best, Survivors, n, k) End Return Lelemento di «pop» per cui lerrore e risulta minimo
SelectSurvivors(pop, k) – Survivors := vettore contenente i k migliori elementi di «pop» – For j := 1 to k p1 = rand(), p2=rand() If p1 < 0,7 and p2 < 0,5 then – Survivors[j] = mutate(Survivors[j]) ElseIf p1 = 0,5 then – Survivors[j] = imitate(Survivors[j]) End – End – Return Survivors
CreateNewPopulation(Best, Survivors, n, k) – Random := costruisci (n-k-1) funzioni casuali – p = rand() – If p < 0,98 then pop := + Survivors + Random – Else pop := Survivors + Random – End – Return pop
generateRandomFunction(n, k, t) Sia – n la profondità dellalbero di definizione della funzione – k il numero massimo di operazioni trascendenti – t il numero di variabili indipendenti p=rand() If n == 1 and p <0.5 then – Return f(x)=1.0 ElseIf n == 1 and p >= 0.5 then – Return f(x)=x(rand()*t) End If rand() < 0.5 then – Scegli a caso una operazione trascendente r(x) – Return f(x) = r(generateRandomFunction(n-1,k-1,t)) Else – Scegli a caso una operazione binaria b(f1, f2) – f1 = generateRandomFunction((1+rand()*(n-1)), k, t) – f2 = generateRandomFunction(n-1, k-1, t) – Return f(x) = b(f1, f2) End
Per provare il funzionamento dellalgoritmo sono state selezionate 11 funzioni per le quali è stato estratto un campione di 100 elementi nellintervallo [-10;10). Per rendere più verosimili i test, si è scelto di aggiungere al campione un rumore casuale distribuito secondo una normale gaussiana standardizzata. Literazione principale dellalgoritmo è stata eseguita al più 250 volte, con una popolazione di 30 individui e 6 sopravvissuti per generazione. Al fine di controllare la solidità dellalgoritmo si è scelto di ripeterlo 20 volte in modo da verificare quanto la convergenza sia dovuta ad una particolare catena di eventi, o a proprietà dellalgoritmo. Per verificare lefficienza del metodo si è misurato il tempo di esecuzione di ogni iterazione (minimo, massimo e medio), e lutilizzo di memoria. Non è stato scelto come parametro lutilizzo della CPU in quanto lalgoritmo è stato ottimizzato per saturare al massimo la risorsa in modo da ridurre i tempi di calcolo.
Le funzioni scelte sono:
Generazioni: 7 Funzioni testate: Tempo: – Medio: 9s – Minimo: 3s – Massimo: 23s Errore medio: 0,57 Solidità: 20 su 20 Utilizzo memoria: 0,01Gb
Generazioni: 31 Funzioni testate: Tempo: – Medio: 23s – Minimo: 4s – Massimo: 137s Errore medio: 0,57 Solidità: 20 su 20 Utilizzo memoria: 0,02Gb
Generazioni: 2 Funzioni testate: Tempo: – Medio: 3s – Minimo: 2,5s – Massimo: 11,4s Errore medio: 0,57 Solidità: 20 su 20 Utilizzo memoria: 0,01Gb
Generazioni: 39 Funzioni testate: Tempo: – Medio: 26s – Minimo: 3s – Massimo: 82s Errore medio: 0,59 Solidità: 20 su 20 Utilizzo memoria: 0,08Gb
Generazioni: 10 Funzioni testate: Tempo: – Medio: 9s – Minimo: 3s – Massimo: 22s Errore medio: 0,55 Solidità: 20 su 20 Utilizzo memoria: 0,04Gb
Generazioni: 61 Funzioni testate: Tempo: – Medio: 45s – Minimo: 2s – Massimo: 160s Errore medio: 0,53 Solidità: 19 su 20 Utilizzo memoria: 0,15Gb
Generazioni: 142 Funzioni testate: Tempo: – Medio: 118s – Minimo: 1s – Massimo: 141s Errore medio: 0,55 Solidità: 5 su 20 Utilizzo memoria: 0,06Gb
Generazioni: 142 Funzioni testate: Tempo: – Medio: 118s – Minimo: 1s – Massimo: 141s Errore medio: 0,55 Solidità: 5 su 20 Utilizzo memoria: 0,06Gb
Test con funzione probabilistica:
È un fenomeno astronomico che consiste in un lampo di radiazione gamma che avviene per: – Esplosione di una Supernova – Quasar – Altre cause legate alla morte di una stella Dura al più qualche centinaio di secondi per cui lunico modo per studiare il fenomeno è analizzare lafterglow. (la luminosità residua in conseguenza al fenomeno) Landamento dellafterglow nel tempo permette di identificare il tipo di GRB.
Si è scelto di approssimare landamento dellafterglow del GRB B. Motivazioni: – I dati sono molto corrotti da rumore in quanto nello stesso periodo sono stati rilevati 4 GRB; – Unico GRB ad aver emesso luce visibile e ad essere visibile ad occhio nudo nonostante la distanza (7,5 Gly)
Lapproccio al problema è molto interessante e valido soprattutto perché è: – Preciso – Veloce – Stabile – Flessibile
Alcuni problemi sono, però, stati tralasciati in fase di progettazione: – Diminuzione delle prestazioni allaumentare delle variabili – Loperatore di mutazione sulla forma rende lalgoritmo più veloce, diminuendo notevolmente la stabilità – La ricerca dei parametri avviene in tutto lo spazio dei parametri, scegliendo casualmente alcuni punti di partenza.