È un po’ che ci penso su. L’ala di SlashCode dedicata all’intelligenza artificiale sta aumentando a dismisura sia come numero di post, sia come attrattiva nei miei interessi. Studio e leggo svariati argomenti di AI e neuroscienze praticamente ogni giorno e ora che ho ricominciato a seguire le lezioni (tutte a tema, ovviamente) il tempo per il resto è ulteriormente diminuito.

Allo stesso tempo mi sono fatto sempre più l’idea che il target di questo blog non può dividersi in entrambi gli argomenti in modo uniforme: chi è interessato al mondo OpenSource e alla programmazione “generale” non è detto che sia interessato ad AI e materie collegate, e viceversa. Tanto vale spezzare in due il blog separando la parte di AI da quella più semplice e generalista che tratta di informatica e opensource.

A questo punto però alzo un po’ il tiro. La mia idea era di aprire un blog (forse anche forum) dedicato alla materia, aperto ovviamente anche a contributi esterni. Una sorta di rivista online dedicata al mondo delle scienze cognitive. Per ora è solo un progetto vago su cui possiamo discutere.

Se qualcuno è interessato alle intelligenze artificiale, la robotica o altro, ma anche persone lontane dal mondo dell’informatica interessate alla neurofisiologia, ed è interessata a questo progetto mi contatti. Mi farebbe piacere riunire molte persone con questo interesse in comune. Sono sicuro che ne uscirebbe qualcosa di buono. :)

 

Abbiamo già parlato delle reti neurali. Una rete neurale è, in sintesi, una fitta interconnessione di elementi semplici chiamati percettroni. Tali unità, disposte in due o più strati, sono in grado di adattarsi tramite algoritmi particolari in modo tale che dato un ingresso X la rete “apprenda” l’uscita Y. Non entrerò oltre nel dettaglio, la materia è infatti piuttosto complicata acnhe trattata in modo superficiale.

A questo punto è bene precisare che questa procedura è quasi esclusivamente una mia particolare speculazione. Non ho letto nessuna pubblicazione al riguardo ma dai miei piccolissimi test sembra funzionare correttamente. Ma veniamo al punto.

Supponiamo di avere una rete neurale come quella in figura:

Abbiamo una rete con 4 ingressi binari e altrettante uscite. Poi, subito prima dell’uscita e subito dopo l’ingresso, ci sono due blocchi nascosti. Tali blocchi sono composti da uno o più strati della rete, l’importante è che le due sotto-reti siano simmetriche. Infine abbiamo due strati centrali, l’importante è che tali strati contengano un numero di unità minori di quelle in ingresso e uscita.

A questo punto addestriamo la rete in modo tale che ad un ingresso X corrisponde un uscita Y del tutto uguale all’uscita. Vi chiederete, a che diavolo serve una rete neurale che non faccia altro che dare in uscita lo stesso valore in ingresso? A nulla, ovviamente, ma proviamo a separare la rete in due reti all’altezza dello strato centrale. In questo modo abbiamo due reti: una con 4 ingressi e 2 uscite e una con 2 ingressi e 4 uscite.

Per il modo in cui la rete è stata addestrata possiamo trattare la prima rete come un coder e  la seconda rete è un decoder. L’utilizzo di questa rete può essere, ad esempio, di codificare un’immagine spezzandola a blocchi di 4 bit e passandola nel coder. Il risultato sarà un file ridotto del 50%. La decodifica, analogamente, avviene spezzando il file codificato in blocchi da 2 bit per riottenere i 4 bit originali.

È bene precisare che questa compressione è solitamente lossy (quindi ha utilità solo per codifiche video/audio/immagini) in quanto le reti neurali solitamente sono “approssimazioni” oltre che statisticamente fonte di errore. Inoltre, il caso di 4 bit ridotti a 2 è poco efficente, solitamente andrebbero usato un ingresso molto più numeroso e un fattore di compressione variabile. Più il valore di compressione è alto più, ovviamente, la possibilità di errori e l’approssimazione aumenta.

Il vantaggio di questa tecnica è che, poiché la rete può essere facilmente addestrata partendo da pezzi del file da codificare, la rete apprende la migliore codifica da N a M per quello specifico file.

Questo è un piccolo esempio di come sia possibile utilizzare una tecnologia nata dall’intelligenza artificiale per compiti che apparentemente non hanno nulla a cui spartire con essa come la codifica multimediale.

 

Vi avevo parlato già degli alberi decisionali in relazione al famoso gioco 20q. Ne avevo parlato in questo articolo facendovi vedere come un semplice albero binario potesse mostrare caratteristiche di apprendimento molto ampie. Questa volta voglio invece mostrarvi un concetto molto utilizzato negli alberi decisionali per migliorare l’efficienza dell’albero stesso: l’entropia. Prima di questo però è necessario che precisare che esistono due metodologie di apprendimento.

APPRENDIMENTO GLOBALE E APPRENDIMENTO STOCASTICO

Il gioco 20q è un ottimo esempio di apprendimento stocastico. In questo caso infatti l’algoritmo apprende domanda per domanda e si aggiorna progressivamente verso la forma definitiva. L’apprendimento avviene alla fine di ogni esempio.

L’apprendimento globale invece si svolge in maniera diversa. Si prende un insieme numeroso di esempi, li si da al programma tutti insieme, e il programma apprende. In questo caso l’apprendimento avviene globalmente alla fine di tutti gli esempi.

L’apprendimento stocastico è solitamente il più usato nella pratica (specie nelle reti neurali) per la sua migliore efficienza, adattabilità e possibilità di successo. Nonostante ciò in questo esempio useremo un apprendimento globale per semplificarci i conti.

L’ENTROPIA

Supponiamo di dover scrivere un applicazione in grado di apprendere quale sono le giornate ideali per andare a giocare a tennis. La prima cosa da fare è elencare alcuni attributi della giornata: tempo, temperatura, umidità e vento. A questo punto forniamo al programma 10 “giornate” e indichiamo se la giornata è adatta al tennis o meno. Ad esempio:

  1. sole, caldo, secco, lieve = si
  2. sole, caldo, umido, lieve = no
  3. nuvoloso, freddo, umido, forte = no
  4. nuvoloso, caldo, secco, lieve = si
  5. pioggia, caldo, umido, forte = no
  6. pioggia, freddo, umido, lieve = no
  7. sole, freddo, secco, lieve = si
  8. sole, freddo, umido, forte = no
  9. nuvoloso, caldo, secco, forte = no
  10. sole, caldo, secco, forte = si

A questo punto dobbiamo costruire un albero a partire da questi dati. Ma quale attributo testiamo alla radice? Ovviamente dobbiamo testare l’attributo la cui risposta ci da il maggior numero di informazioni (detto diversamente, quella che ci permette di eliminare il maggior numero di ipotesi). Per far questo utilizziamo l’entropia. Essa infatti ci è molto utile per calcolare l’information gain ovvero il grado di informazione associato ad ogni attributo.

L’entropia è espressa dalla seguente formula:

Dove c è il numero di possibili risposte (nel nostro caso sono due, “si” e “no”), e i vari p sono la frazione di esempi che hanno come risultato l’i-esima risposta. Ad esempio nella nostra lista di 10 giornate ce ne sono 4 con esito positivo e 6 con esito negativo. L’entropia del nostro insieme sarà:

Cioè circa 0.97.

INFORMATION GAIN

A questo punto dobbiamo cacolare l’information gain per ogni attributo. Secondo questa formula:

La formula sembra complicata ma non lo è. Vediamo infatti un rapido esempio. Cominciamo trovando il gain dell’attributo tempo. Tale attributo ha 3 valori possibili: sole, nuvoloso e pioggia.  La prima cosa da fare è trovare il sottoinsieme S(v) per ogni valore di tempo, cominciamo con S(sole). Per fare ciò dobbiamo identificare tutte le giornale che hanno come attributo di tempo il valore “sole” e fra queste contare quante sono associate ad una giornata “si” e quante ad una giornata “no”.

Il risultato è 3 “si” (la 1, la 7 e la 10) e 2 “no” (2,8). A questo punto calcoliamo l’entropia di questo sotto-insieme e moltiplichiamola per la frazione di giornate assolate (5/10). Ottenendo: 0.48. Facciamo lo stesso per tutti gli altri valori ottenendo che il gain per tempo vale esattamente 0.23.

Facciamo lo stesso per tutti gli altri attributi ottenendo:

  • tempo = 0.23
  • temperatura = 0.05
  • umidità =  0.61
  • vento = 0.13

Bene. A questo punto sappiamo cosa scegliere: umidità. Possiamo quindi porre come nodo radice la domanda “è umido o secco?”. Inoltre notiamo che l’entropia per l’insierme S(umido) è esattamente zero. Questo ci indica che se la risposta è “umido” possiamo già dare la risposta: no.

Se invece la risposta è “secco” dobbiamo continuare. Per fare ciò dobbiamo togliere dall’insieme tutte le giornate umide. A questo punto rifacciamo tutto il calcolo da capo, ricalcoliamo il nuovo gain per ogni attributo restante (tempo, temperatura e vento) e scegliamo nuovamente il maggiore. Così fino alla fine.

Ovviamente, tutti questi conti che abbiamo fatto a mano possono essere facilmente automatizzati, altrimenti non parleremmo di apprendimento automatico ;)   In un prossimo post infatti presenterò un piccolo script che dato un insieme di esempi calcola l’albero decisionale migliore in base ai dati forniti con tanto di dati sull’entropia del sistema.

 

Supponiamo di trovarci in una città a noi sconosciuta e di avere un appuntamento importante con una stupenda ragazza (o ragazzo a seconda dei casi). La cosa più semplice da fare è chiedere indicazioni ai passanti. Purtroppo però siamo sfortunati e quel giorno diluvia che dio la manda e non c’è anima viva in giro. Non c’è speranza.

LA RICERCA IN AMPIEZZA

In questo caso l’unica soluzione che avete è di tentare tutte le strade. Partite dal punto A e verificate tutti gli incroci a cui si arriva direttamente da A, se non siete arrivati tornate ad A e verificate tutti gli incroci a cui si arriva partendo da A e attraversando un solo incrocio, e così via, aumentanto ad ogni passo la profondità della ricerca. Questo tipo di ricerca prende il nome di ricerca in ampiezza. Funziona? Sicuro, prima o poi arriverete a destinazione. Tuttavia la domanda sorge spontanea: quanto poi?

Supponendo, come avviene spesso in realtà, che ad ogni incrocio si dipanino 3 strade (oltre a quella da cui siete venuti) se disgraziatamente la ragazza si trova a solo 10 incroci di distanza dovrete testare circa 59.049 incroci e un numero maggiore di strade. Un compito decisamente eccessivo anche per il più motivato.

Dobbiamo trovare un alternativa. Appare evidente che abbiamo bisogno di un informazione che ci permetta di scartare le strade che sicuramente non ci portano a destinazione. Chiamiamo quindi la ragazza e scopriamo che è una nerd d’alta categoria. Ella infatti vole metterci alla prova. Invece di dirci la strada ci promette solo di dirci la distanza in linea retta che separa ogni incrocio che vogliamo da lei. È sufficiente? Si, e non solo troviamo una strada che ci conduce da lei ma troviamo la migliore.

LA RICERCA GOLOSA

La prima soluzione che ci viene in mente è semplice: fra tutti gli incroci che posso raggiungere da A scelgo quello che è più vicino alla ragazza e così via per ogni incrocio su cui vado a finire. Questo tipo di ricerca (che prende il nome di ricerca golosa) è sicuramente migliore della ricerca in ampiezza tuttavia ha due grossi problemi: non trova la strada più breve e, nel caso di grafo con cicli, può finire per ciclare all’infinito.

LA RICERCA A*

Scegliamo questa strategia: assegno a tutti gli incroci che posso raggiungere dall’incrocio in cui sono un numero N corrispondente alla somma della strada percorsa fino a quel punto più la distanza in linea retta che separa quell’incrocio dalla ragazza. A questo punto scelgo l’incrocio che ha il numero N più basso fra quelli che ho assegnato fin’ora. Questa ricerca prende il nome di A* ed è l’algoritmo di ricerca più efficiente  su un grafo pesato (come ad esempio una mappa stradale).

I punti a favore di A* sono fondamentalmente due: l’efficienza computazionale e il fatto che, in alcune condizioni, il percorso trovato è ottimo.

EURISTICHE AMMISSIBILI

Ma quali sono queste condizioni? Torniamo all’informazione che ci forniva la ragazza: la distanza in linea retta di ogni incrocio da lei. Questa prende il nome di funzione euristica. In parole povere è una funzione che esprime una stima della “distanza” dall’obiettivo. Bene, affinché la ricerca di A* sia ottima è necessario che la funzione euristica non sovrastimi mai la distanza reale dall’obiettivo! Quando questa condizione è soddisfatti si dice che la funzione euristica è ammissibile.

Nel nostro caso la funzione euristica soddisfa questa condizione in quanto la distanza in linea retta fra un incrocio e la ragazza non può mai essere maggiore della distanza reale (per definizione la retta è la distanza più breve fra due punti). La funzione è quindi ammissibile soluzione che troveremo sarà quindi ottima.

La non ammissibilità di una funzione non è però un limite stringente, spesso infatti trovare una funzione euristica ammissibile necessita di un quantitativo di calcoli troppo elevato che vanificherebbe i vantaggi di A*. In questi casi si può scegliere di usare una funzione non ammissibile ma di facile calcolo a patto di accontentarsi di una soluzione che potrebbe non essere ottimale.

Abbiamo imparato tanto in questa uscita. La ragazza al nostro arrivo sarà sicuramente soddisfatta.

 

È da qualche giorno che ho fra le mani il nuovo portatile. L’ho acquistato per sopperire alle carenze del netbook nei casi in cui mi trovavo a dover scrivere lunghi testi o a programmare intensamente lontano da casa. Sono applicazioni per cui il netbook non è adatto, almeno per me.

Il suddetto portatile è un Acer Extensa 5635Z, dotato di un dual core di fascia bassa (ha alcune funzionalità di risparmio energetico disattivate), 2Gb di ram e un paio di cento Gb di hard disk. Perfettamente adatto ad essere un supporto “lavorativo” esterno in grado di sostituire il desktop in situazioni di necessità. Il sistema di default era Linpus, una distribuzione poco interessante ma almeno mi ha evitato di pagare la tassa Microsoft sui portatili e perdere tempo in rimborsi. La domanda spontanea è: perché le distribuzioni preinstallate sono sempre distribuzioni pessime e/o poco conosciute? È una situazione di cui parleremo in un altra sede.

La prima cosa che ho fatto è testare alcune distribuzioni fra cui la nuova Ubuntu 10.10. Il sistema funziona perfettamente senza alcuna modifica (ad esclusione della spia sul bottone del wi-fi che nonostante ciò funziona perfettamente). La magagna è arrivata però a sorpesa: c’è un bug nella versione 2.6.35 del kernel che affligge proprio il chipset video del portatile in questione. Un tempismo certamente sfortunato.

L’errore in questione genere un errore del tipo  [drm:init_ring_common] a pochi secondi dall’avvio. Quest’errore crea alcune difficoltà al sistema grafico che quindi risulta inusabile a tratti: il mouse si congela e va estremamente a scatti per 5-6 secondi in modo casuale ma tuttavia abbastanza frequente da rendere la cosa irritante.

Ho segnalato il bug e per il momento ho rattoppato il problema installando una versione precedente del kernel. Nonè una soluzione definitiva ma mi permette di utilizzare il pc senza prenderlo a pugni.

Ad esclusione di questo problema particolarmente sfortunato tutto funziona a meraviglia. Se volete un portatile a poco prezzo (circa 350€) e volete far funzionare linux senza troppi sbattimenti questo Acer Extensa 5635Z potrebbe essere la scelta che fa per voi.

 

È vero. È tanto che non posto nulla e voglio essere sincero: era solo mancanza di voglia. Ho passato questo mese a fare cose che piacevano a me senza curarmi di doverne trarre informazioni da condividere su questo blog a ritmi regolari. Se pensate che questa sia una mancanza di rispetto verso voi che seguite Slashcode vi chiedo scusa, ma io sono uno e  volevo passare l’ultimo mese prima di ricominciare l’università in una specie di sonno rigeneratore che mi permetta di ricaricare le idee (almeno in ambito informatico).

Detto questo, siamo pronti a ricominciare. Le prime cose che vorrei fare è terminare il mini tutorial su Django presentando i template e riprendere in mano la guida sulle OpenGL. Inoltre tornerò a parlare insistentemente di tecnologia e intelligenza artificiale dato che è questa la facoltà che faccio. :D

Se volete che affronti qualche tema in particolare ditelo pure nei commenti e vedrò di fare il possibile.

Un abbraccio a chi mi segue ancora nonostante la pausa :) ci vediamo insieme in questo nuovo anno di slashcode. :)

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha