Prima di cominciare a spiegare il piccolo codice di esempio è importante fare qualche piccola premessa. La fluidità dell’animazione dipende dai frame per secondo (fps). I frame per secondo massimi dipendono dal refresh dello schermo. Gli schermi moderni vanno all’incirca a 60Hz, e quindi che in un secondo lo schermo proietta 60 immagini. Questo significa che ogni secondo possiamo rappresentare al massimo 60 immagini differenti e, quindi, possiamo avere al massimo 60fps.

Andare a 60fps, inoltre, significa che in un sessantesimo di secondo il nostro pc deve calcolare tutti i vertici dell’immagine successiva, inviarli alla scheda video e renderizzarli. Nel caso in cui l’elaborazione necessita più di un sessantesimo di secondo dobbiamo inserirci nell’intervallo successivo, ovvero due sessantesimi di secondo. In questo caso la nostra animazione crolla a 30fps (60/2). Se nemmeno due sessantesimi di secondo sono sufficienti allora passiamo a tre sessantesimi facendo abbassare i nostri fps a 20 (60/3) e così via.

Data la presenza di questi intervalli fissi il tempo di calcolo che “avanza” alla funzione Display viene occupato dalla funzione Idle. Questa è la funzione che viene eseguita quando nessun’altra funzione deve essere eseguita.

Il secondo punto è la presenza del doppio buffer. Usando un solo buffer infatti c’è il rischio che i vertici da visualizzare vengano modificati ancor prima di essere completamente renderizzati sullo schermo. Questo rischia di creare un fastidioso “effetto-fantasma” causato dalla coesistenza sullo schermo di due frame incompleti. Per ovviare a questo tutte le schede video moderne implementano il doppio buffer: secondo questo sistema un buffer viene utilizzato per la visualizzazione mentre il secondo viene utilizzato dal programma per immagazzinare i vertici del prossimo frame. Quando arriva il momento della visualizzazione questi due buffer vengono scambiati. In questo modo è garantito che vengano visualizzati solo frame completi.

from OpenGL.GLUT import *
from OpenGL.GL import *

spin = 0

def init():
    glClearColor(0,0,0,0)
    glShadeModel(GL_FLAT)

def display():
    glClear(GL_COLOR_BUFFER_BIT)
    glPushMatrix()
    glRotatef(spin, 0, 0, 1)
    glColor3f(1, 1, 1)
    glRectf(-25, -25, 25, 25)
    glPopMatrix()
    glutSwapBuffers()

def reshape(w, h):
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(-50, 50, -50, 50, -1, 1)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

def spinDisplay():
    global spin
    spin += 0.5
    if spin > 360 :
        spin = spin - 360
    glutPostRedisplay()

def mouse(button, state, x, y) :
    if button == GLUT_LEFT_BUTTON :
        if state == GLUT_DOWN :
            glutIdleFunc(spinDisplay)
    elif button == GLUT_MIDDLE_BUTTON:
        if state == GLUT_DOWN :
            glutIdleFunc(None)

if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
    glutInitWindowSize(250, 250)
    glutInitWindowPosition(100, 100)
    glutCreateWindow("Quadrato Rotante")
    init()
    glutMouseFunc(mouse)
    glutDisplayFunc(display)
    glutReshapeFunc(reshape)
    glutMainLoop()

Questo programma fa due cose in più rispetto al precedente: fa ruotare il quadrato e permette di attivare/disattivare la rotazione con la pressione di due tasti del mouse (il centrale stoppa mentre il sinistro avvia).

Molte funzioni sono le stesse dell’ultima volta quindi non mi ripeterò. Vediamo le novità.

Innanzitutto potete notare che utilizzo GLUT_DOUBLE in DisplayMode. Questo serve ad attivare il doppio buffer.

Poi c’è la funzione glutMouseFunc() che serve ad impostare la funzione che servirà a gestire l’input del mouse. In questo caso ho usato una fantasiosa funzione mouse. Tale funzione prende quattro parametri:

  • button: indica quale bottone è stato premuto.
  • state: indica lo stato (premuto, alzato, click, etc…) vedremo più avanti queste cose in dettaglio.
  • x e y: sono, ovviamente, la posizione del puntatore.

Nella funzione mouse faccio una sola cosa: verifico quale tasto è stato premuto e a seconda del tasto modifico la funzione di Idle. Perché è la funzione IDLE che modifica i frame. La funzione display li mostra, la funzione IDLE li modifica (modificando i parametri utilizzati da display). Questo è il punto fondamentale.

Come potete vedere sposto la funzione di idle con il comando glutIdleFunc. La imposto a None (o NULL in C) per fermare il quadrato e alla funzione spinDisplay per ruotarlo. Quest’ultima funzione non fa altro che incrementare una variabile spin che rappresenta i gradi di rotazione.

La funzione display è del tutto simile a quella dell’esempio precedente. Viene aggiunta però la funzione glRotate la quale non fa altro che ruotare di spin gradi tutti i vertici che verranno inseriti fra glPushMatrix e glPopMatrix.

Un altra cosa accessoria è la funzione glutReshapeFunc che imposta la funzione da chiamare nel momento in cui la finestra viene ridimensionata. In questo caso la funzione evita solamente che vengano alterate le proporzioni.

EDIT: È disponibile la versione in C del codice di questa puntata. Lo trovate questo indirizzo

 

Ho lasciato ubuntu all’incirca due anni fa più attratto dalla curiosità di esplorare nuove cose che per reale necessità. Ho vagato per due anni in molti altri porti fino ad approdare folgorato alle porte di Sidux. Ma come per un viaggiatore lontano da casa, prima o poi, la nostalgia si fa sentire. A stimolare maggiormente questo senso latente di “radici” sono state le notizie e le notevoli rivoluzioni di cui ho sentito parlare in questo tempo di vagabondaggio.

Non sono mancate le critiche. Anzi, spesso noto un certo piacere perverso nel fare recensioni negative sulla nuova versione uscita (spesso infarcite di luoghi comuni che mi hanno da tempo stancato) ma, come diceva il mio saggio nonno “chi spala merda sulle persone è perché ne ha le scarpe piene” quindi precisiamo alcune obbiezioni base alle critiche che ciclicamente cicciano fuori ad ogni release:

  • Bug? Tirare fuori una versione ogni 6 mesi non è una passeggiata. La soluzione? Allungare il tempo di rilascio o accontentarsi. Io sinceramente considero 6 mesi un tempo giusto come compromesso fra stabilità e novità del parco software. Ed è proprio quello che cerca l’utente medio. Se volete usare Ubuntu in ambienti iper-produttivi vi spostate di LTS in LTS in modo da toccare i due anni canonici di distribuzioni basate sulla stabilità come Debian. Inoltre 6 mesi sono anche un bel tempo a livello di marketing perché danno un notevole senso di dinamismo.
  • Problemi con l’hardware? Questo è un problema che riguarda ben poco la distribuzione. Poi, guarda caso, i problemi maggiori si riscontrano con quell’hardware che si impunta a rilasciare driver proprietari. Quindi non è un difetto di Ubuntu (o di qualunque altra distro) ma delle case produttrici di hardware. Inoltre ho installato Ubuntu su 10.4 su 3 PC e su tutti questi dispositivi l’hardware è stato riconosciuto al primo colpo. Per tutti gli altri casi basta informarsi preventivamente.
  • Grafica/Modifiche/Programmi? Ogni ritocco grafico o del software di default o di qualunque altro aspetto fa contente dieci persone e ne scontenta mille. La grafica si cambia. I programmi si installano. Non mi pare il caso di drammatizzare su queste cose. Anche per me alcune scelte sono dubbie, ad esempio i pulsanti delle finestre a sinistra mi sanno vagamente di bimbominkiata da fanboy mac, anche perché non ne vedo l’utilità pratica. Lasciare spazio a destra? Ma non era uguale avere spazio a sinistra? Non lo so. Staremo a vedere.

Finito questo vi elenco quali scelte, a mio parere, sono azzeccatissime e rendono Ubuntu un sistema competitivo. Tenete presente che il mio paragone è con Ubuntu di due anni fa, di quando lo lasciai in cerca di fortuna.

  • Grafica. Non avrei mai pensato che GNOME potesse diventare così accattivante (ovviamente è provocatorio). Tuttavia la scelta delle icone monocromatiche, il tema dark, e la leggerezza che assumono le due barre sono un bel lavoro di minimalismo. Certo, molte cose possono essere migliorate (lo sfondo di default mi ha quasi fatto ribaltare dalla sedia con quel viola sfocato che non sa né di carne né di pesce). I pulsanti a sinistra sono una scelta che non ho ben compreso (spero non sia uno scimmiottamento del Mac) ma ci si abitua in 5,5 secondi. Il problema è Chrome. Che con la sua interfaccia li mantiene a destra e quindi con Chrome aperto è un gran casino. Ma Crhome ce l’ho messo io quindi è colpa mia. :D
  • Logo. Anche il logo nuovo a mio avviso abbraccia maggiormente quella ventata di minimalismo ben curato che ha travolto Ubuntu negli ultimi anni. Lineare, semplice e con pochi colori. Molti blogger l’hanno massacrato. Che dire. I gusti sono gusti. Notiamo inoltre che avvio/login/desktop assume una maggior uniformità rispetto alla versione precedente.
  • Software Center. Allora… GNU ha inventato lo “Apple Store”. APT esiste da anni. Solo che la Apple se n’è appropriato, almeno nella mente della gente tant’è che quando spiego a qualcuno come funzionano i repository di Debian/Ubuntu molti mi dicono “Ah! Come con l’iPhone!”. Ecco, questo mi da sui nervi. Il software center ridisegnato funziona molto meglio di quello vecchio e, soprattutto, ha un nome meno anonimo. Lo so, il nome fa solo figo ma THIS IS MARKETING! Bisogna pensarci. La Canonical è la prima azienda che fa marketing su linux per desktop. Diciamo che queste cose cozzano con lo zoccolo duro degli utenti Linux ma bisogna farci un po’ il callo. ;)
  • Ubuntu One. Sembra una cazzata ma senza Ubuntu One forse avrei ritardato il ritorno a Ubuntu di qualche anno. Questo servizio ha due vantaggi: offre un sistema di cloud privato di default per gli utenti (che trovo fondamentale usando almeno 3 computer diversi) e permette a Canonical di guadagnare sui servizi di valore aggiunto. Come, ad esempio, il negozio di musica o l’espansione a 50Gb del proprio spazio web alla cifra piuttosto onesta di 10$ al mese. La Canonical dopotutto deve far soldi. E’ giusto così e incoraggio queste scelte. Il negozio musicale, per la cronaca, funziona piuttosto bene. Ho appena comprato due brani per provare e sono già belli pronti sul mio PC e condivisi fra tutti i PC del cloud. Inoltre posso comprare musica senza finanziare l’odiatissima Apple.
  • Integrazione fra Applicazioni. La sensazione che pervade l’esperienza desktop in questa nuova release rispetto alle ultime che ho provato è l’armoniosa integrazione delle applicazioni con il desktop. Chat, social network, email e notifiche di svariate applicazioni tutte unificate e a portata di click. Anche qui alcune cose le migliorerei (ad esempio voglio un dannato tool per personalizzare le notifiche) ma fondamentalmente questo senso di unione è una cosa del tutto nuova nel panorama Linux. Da un senso di completezza immediato che nessun’altra distribuzione che ho provato ha mai dato. Amo la bustina nella barra. L’adoro.
  • Open Source. Ovviamente non intendo il sistema operativo. Intendo dire che apprezzo che Ubuntu non sia caduta nel tranello di preinstallare roba proprietaria (cosa che, inoltre, fa la fortuna di almeno una decina di distribuzioni farsesche) come codec, driver e software vario. Ovviamente ti aiuta se vuoi installarlo ma la scelta di non farlo in automatico è encomiabile. Questo perché nonostante il tentativo di imporsi sul mercato non sceglie di abboccare al canto delle sirene del software proprietario. Scelta coraggiosa e che, spero, venga mantenuta fino in fondo.

In conclusione dopo due anni di assenza mi ritrovo una distribuzione molto cambiata ma cambiata tanto gradualmente che Guido fa bene a chiamarla “rivoluzione silenziosa”. Questa è la prima versione di Ubuntu che mi sento di installare/consigliare senza farmi troppi scrupoli. Inoltre, dalle voci di corridoio, sembra che il futuro sia ancora più promettente.

Dopo due anni sono tornato a casa. Forse ripartirò, forse no. Per questi sei mesi però mi godo l’intimità casalinga. :D

 

Stavo ripassando gran parte delle API delle OpenGL. Stavo facendo piccoli schemi e riassunti ed allora ho pensato: perché non condividere un po’ di questo lavoro? Così ci trascrivo qualche piccolo cenno di OpenGL senza nessuna intenzione di portare alla luce chissà quale serie di tutorial. Se poi esce qualcosa di appetibile meglio così.

Innanzitutto devo fare due appunti: il primo è che, per mia semplicità, userò il Python come linguaggio per il codice, tuttavia le API sono identiche sia in pyOpenGL che nell’OpenGL standard quindi anche se programmate in C potete facilmente convertire il tutto nel linguaggio a voi più familiare. Secondo, le libreria OpenGL prese da sole sono un po’ complicate, infatti di solito ci si appoggia alle librerie collegate GLUT. Queste librerie si incaricano di fare tutto il lavoro “esterno” alla visualizzazione grafica come gestione degli input e integrazione nel sistema a finestre. Usare GLUT è proprio l’approccio che userò anche io.

Iniziamo mostrando un piccolo programma che disegna un quadrato bianco su uno sfondo nero.

from OpenGL.GLUT import *
from OpenGL.GL import *

def init():
glClearColor(0,0,0,0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0,1,0,1,-1,1)

def display():
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(1,1,1)
glBegin(GL_POLYGON)
glVertex3f(0.25, 0.25, 0)
glVertex3f(0.75, 0.25, 0)
glVertex3f(0.75, 0.75, 0)
glVertex3f(0.25, 0.75, 0)
glEnd()
glFlush()

if __name__ == '__main__' :
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(250, 250)
glutInitWindowPosition(100, 100)
glutCreateWindow("Hello World")
init()
glutDisplayFunc(display)
glutMainLoop()

Partiamo quindi a spiegare il codice cominciando da l main. La prima funzione che incontriamo è glutInit(). Questa funzione si incarica di inizializzare GLUT. La versione canonica di questa funzione prende per argomento gli argomenti del main (argc e argv per chi conosce il C).

La seconda funzione è glutInitDisplayMode(). Questa funzione prende per parametri delle opzioni di visualizzazione. Nel nostro caso GLUT_SINGLE e GLUT_RGB i quali impostano la visualizzazione a singolo buffer (capiremo la differenza con il doppio buffer la prossima volta) e la visualizzazione RGB (ovvero con i colori tradizionalmente usati nei pc).

Gli altri tre comandi invece creano la finestra impostandone dimensione, posizione e titolo.

A questo punto entriamo in init(). In questa funzione vengono preimpostate alcune variabili di OpenGL. Non entrerò nel dettaglio ora, vi basti sapere che glClearColor() è la funzione per il colore di “sfondo” ovvero per quello usato durante la cancellazione. Questa funzione prende 4 parametri (rosso, verde, blu e trasparenza).

Un altra funzione importantissima è glutDisplayFunc() questa è la funzione che indica al motore grafico quale funzione chiamare nel momento in cui c’è bisogno di disegnare sullo schermo. In questo esempio passiamo semplicemente la funzione display la quale non fa altro che disegnare un quadrato bianco.

Come? Semplice. Per disegnare qualcosa bisogna passare al sistema le coordinate dei vertici secondo un algoritmo del tipo:

  • Si imposta il colore.
  • Si avverte il sistema che stiamo inserendo i vertici con glBegin().
  • Si passano le coordinate dei vertici con glVertex3f()
  • Si avverte il sistema che abbiamo terminato con glEnd()

Il parametro di glBegin è molto importante ma capiremo meglio il suo significato successivamente.

La funzione glFlush() è inutile per molte applicazioni. Ma voi mettetela. Così sarete sicuri che il vostro codice funzioni, ad esempio, in applicazioni di rete.

Alla fine, per chiudere in bellezza si lancia glutMainLoop() per avviare il programma.

Questo è solo un piccolo esempio. Disegnare un quadrato immobile non è ne bello ne utile. Ma questo esempio, nella sua semplicità, racchiude l’essenza di ogni programma OpenGL. La prossima volta vedremo come far ruotare questo dannato quadrato.

 

Quando si lavora con complesse strutture dati, specie quando queste strutture sono generate a partire da dati offuscati, è estremamente importante utilizzare uno strumento in grado di rappresentare visivamente tali grafi. Io stesso ho perso alcune settimane dietro i problemi matematici che si nascondono dietro la visualizzazione dei grafi. Un esempio fra i tanti riguarda il problema di disporre i nodi in modo tale da minimizzare gli accavallamenti degli archi.

Non avevo però la pretesa di riscrivere da capo una libreria che disegnasse grafi e alberi in quanto ero sicuro che esistesse già qualcosa di pronto nel marasma del codice libero.

La risposta è iGraph. Fra tutte le librerie che ho visto e provato questa è sicuramente la più interessante. Il punto di forza di questa libreria consiste nel numero di piattaforme e linguaggi che supporta: C/C++, Python e anche Ruby più altre piattaforme di statistica. Inoltre, dando un occhiata alle API e alla documentazione, sembra molto semplice, intuitiva e ben tenuto.

Allora lo installo sfruttando i repository di Launchpad (ho utilizzato karmic anche se uso Sidux e pare che tutto funzioni bene). Scrivo un codicillo di prova e ho la brutta sorpresa:

NotImplementedError: showing plots is not implemented on this platform: Linux

Accidenti! La funzione principe del programma non è ancora stata implementata!? Non vi nascondo che ci sono rimasto malissimo. Anche perché era una libreria di cui avevo bisogno per visualizzare alcuni grafi di stato di piccoli progetti di prova di IA.

Pazienza. Guarderò il codice e cercherò di contattare gli sviluppatori per sapere come procede l’implementazione di questa funzionalità. Magari posso dare anche una mano. Alla fine è questo il bello del software libero.

UPDATE:

HO RISOLTO IL PROBLEMA! Ora funziona bene anche la visualizzazione. Vi aggiornerò a breve!

SEGUIMI SU TWITTER

 


Python dispone di numerosissimi strumenti che facilitano e completano l’esperienza di programmazione. Il problema è, appunto, che sono troppi. Ogni volta bisogna ricercare i migliori fra la sterminata costellazione di strumenti di sviluppo.

E quindi ho deciso di fare una lisa. Soprattutto per me, dato che ogni volta che mi capita di reinstallare il sistema perdo un bel po’ di tempo a ricordarmi quali fossero tutti i tools che avevo installato.

IDE

La scelta dell’IDE è sempre la più importante. Sebbene Python non necessiti di chissà quale IDE avanzato, un po’ di comodità in più non fa certo male. La mia scelta ricade a pari merito su due IDE molto importanti:

Per il momento sto utilizzando la seconda configurazione ma in passato ho usato la prima. A voi la scelta.

CHECKER

Python è un linguaggio interpretato, ragion per cui gran parte degli errori di sintassi vengono fuori solamente in fase di esecuzione. Ci sono però programmi in grado di individuarli da subito.

Questa è la mia scelta. Fa tutte le cose di PyChecker e, in aggiunta, fa anche alcuni controlli riguardo le convenzioni stilistiche di Python. Un ottimo “tutor”. Inoltre è integrato con PyDev, quindi se usate Eclipse è veramente una mano santa.

DEBUGGER

Il debug in python è integrato. Basta importare il modulo pdb e lanciare il programma con:

<span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;">pdb.run('mymodule.test()')

Vi rimando alla guida ufficiale per maggiori informazioni.

TESTING FRAMEWORK

Gli ambienti di test sono strumenti che permettono di scrivere dei test per verificare il buon funzionamento di una classe o di un modulo che abbiamo appena scritto. In Java io utilizzavo JUnit. È quindi naturale che in Python abbia scelto:

PyUnit ricalca molto da vicino la sintassi di JUnit. Inoltre è integrato con Eclipse. Per test semplici è veramente intuitivo, per roba più complessa basta dare una letta alla documentazione.

CODE COVERAGE

Gli strumenti di code coverage sono tools che tengono traccia di quale parte di codice eseguiamo durante un test e di quale non eseguiamo. È molto facile infatti che durante un test capiti di trovarsi ad eseguire, ad esempio, solamente un ramo di un IF e che, come al solito, l’errore si nasconda nel ramo che non eseguiamo mai. I programmi di code coverage ci segnalano proprio questa eventualità.

Questo è il programma di coverage in Python per eccellenza. Integrata con PyDev. Esiste anche un espansione (figleaf) che però non ho mai avuto modo di provare.

DOCUMENTAZIONE

Scrivere la documentazione è essenziale. Generare la documentazione dai commenti del codice sorgente è ancora più essenziale in quanto ci risparmia una marea di tempo. In Java esiste il famoso javadoc. Ovviamente il Python non è da meno.

Epydoc, di cui ho scritto anche una rapida guida tempo fa, è un tool fenomenale. Non è perfettamente integrato con Python 3 (almeno nell’ultima versione che ho usato) ma fa dannatamente bene il suo lavoro.

Credo che per il momento la lista sia piuttosto esaustiva. Spero vi sia utile.

Alla prossima. :)

 

Segnalo ancora un nuovo aggiornamento. Arrivato in ritardo ma è arrivato. :D Purtroppo sono stato distratto da interessanti argomenti di Intelligenza Artificiale e mi è passato tutto il resto di mente! :D

Fra le modifiche che ho fatto segnalo che ho completato la parte sui modelli astratti di algoritmi (forse approfondisco un po’ le macchine a registri) e ho corretto alcune sviste.

Ecco il solito link:

DIGITAL MIND

Alla prossima.

 

Probabilmente fra tutti gli esempi di applicazioni dell’Intelligenza Artificiale la più semplice e famosa consiste sicuramente nel gioco 20q. Il gioco in questione funziona così: il giocatore pensa ad un oggetto o ad un animale e il marchingegno tenta di indovinare di cosa si tratta in meno di 20 domande a cui il giocatore può rispondere si o no. Potete trovare una versione online del gioco all’indirizzo http://www.20q.net/. Potrete constatare che in alcuni casi il programma sembra proprio leggervi nella mente con risultati quasi spaventosi.

La versione usata dal sito è piuttosto avanzata ed utilizza le reti neurali e permette quindi una gamma di riposte più vasto (c’è anche forse, probabilmente, a volte, ecc…). Il programma è inoltre in grado di apprendere mentre si gioca: ogni qual volta si gioca, o il programma si arrende richiedendo di inserire l’oggetto a cui si pensava, l’insieme di risposte appena date plasma la struttura dati interna del programma in modo tale che la prossima volta l’oggetto venga riconosciuto più velocemente. È un modello di apprendimento molto basilare ma che è analogo a quello umano percezione-previsione-modifica: il programma riceve dati percettivi (le risposte alle domande), formula un ipotesi (i tentativi di indovinare) e modifica la struttura dati in base all’esito della previsione.

Sarei curioso di sapere se il programma del sito ha qualche contromisura contro il problema del sovra-adattamento: questo tipo di problema affligge gran parte dei modelli decisionali, comprese le reti neurali, ed è causato da un eccessivo adattamento della rete a certi tipi di dati piuttosto che ad altri. Facciamo un esempio. Nel gioco 20q è molto più probabile che il giocatore pensi a cose “banali” come “gatto”, “cane”, “fiore” e simili piuttosto che a “antilope”, “spinterogeno” e “neutrino”. Questo significa che nel corso dell’esistenza del gioco il programma verrà addestrato alla perfezione a riconoscere cani e gatti mentre riceverà poche informazioni riguardo antilopi e neutrini. Possiamo quindi dire che “gatto” e “fiore” e le parole comuni facciano parte dell’insieme di addestramento di una rete. Bene, è possibile dimostrare che una rete sovra-adattata al suo insieme di addestramento indovina, in media, molto meno volte oggetti che non appartengono a tale insieme rispetto ad una rete che è solo parzialmente adattata.

Rimandiamo però questi approfondimenti sulle reti neurali. Il motivo per cui ho scritto questo articolo era di mostrare come sia possibile in una manciata di righe scrivere un programma che emuli in modo semplice il gioco di 20q. Per questa approssimazione consideriamo che sia possibile dare alle domande soltanto le risposte si o no.

Il metodo che vi mostrerò non utilizza nessuna tecnica propria dell’IA bensì dei semplicissimi alberi binari. Questi alberi binari li indicheremo come alberi decisionali binari. Nel nostro albero decisionale definaiamo:

  • I nodi interni dell’albero sono domande.
  • I nodi foglia dell’albero sono gli oggetti da indovinare.
  • Il sotto-albero destro di un nodo corrisponde al caso in cui la risposta alla domanda sia si.
  • Il sotto-albero sinistro di un nodo corrisponde al caso in cui la risposta alla domanda sia no.

L’algoritmo del gioco consiste in una cosa del tipo:

  • Il programma formula la prima domanda che trova (cominciando con il nodo radice).
  • Se non c’è più nessuna domanda il programma si arrende e chiede al giocatore di inserire 1) l’oggetto a cui stava pensando. 2) una domanda che rappresenta l’oggetto 3) la risposta a questa domanda.
  • Il programma aggiunge questa domanda e la relativa risposta al sotto albero in cui si era fermato (oppure come nodo radice se l’albero era vuoto).
  • Se invece la domanda c’è il programma formula la domanda e scende nel sotto-albero relativo alla risposta.
  • Il programma ricomincia dal punto 1.

Tutto qui. Tale algoritmo è effettivamente molto grezzo e sono possibili un gran numero di migliorie (ad esempio usare dei modelli di albero più “elastici” per evitare alberi troppo sbilanciati) ma svolge decentemente il proprio lavoro. Dopo che l’avete fatto girare per qualche tempo vedrete che inizierà ad imparare e ad indovinare un discreto numero di parole. Ovviamente potete partire da un albero vuoto oppure pre-costruire un albero di base con alcune domande comuni.

Un altra cosa che può farci capire questo semplice programma è una considerazione che mi ha fatto notare un gentile commentatore: la capacità degli algoritmi di AI non dipende dalla dimensione del programma ovvero dal suo numero di righe di codice allo stesso modo di come le capacità celebrali umane non dipendono dalla dimensione del cervello (una balena ha il cervello molto più voluminoso di un essere umano eppure fa molte meno cose). Le capacità cognitive di un programma dipendono solamente dalle relazioni che si auto-generano fra le strutture dati del programma (nel nostro caso tali relazioni sono i collegamenti ad albero fra i nodi).

Se qualcuno avesse voglia di creare questo programmino usando il minor numero di righe possibili sarei felice di pubblicarlo. Inoltre potreste anche usarlo per sbalordire amici, parenti e vicini di casa! XD

Alla prossima!

 

Ebbene si… Dato che sono frequentatore di social network (se per alcuni è una grave colpa chiedo scusa anticipatamente) ho deciso in una mezz’ora di noia di integrare Slashcode ai vari socialnetwork. Per chi è interessato può trovare SC anche su:

TWITTER: https://twitter.com/slashcode

FACEBOOK: http://www.facebook.com/pages/SlashCode/101937993183122

In pratica svolgono le stesse funzioni dei Feed ma potrei twitterare anche delle cose “flash” per cui non ritengo necessario un intero articolo di blog. :)

Per chi mi seguirà anche li, un bel grazie.

 

Per chi volesse approfondire un po’ le meccaniche di funzionamento della mente umana vi consiglio caldamente il libro Inventare la Mente di Frith Chris, professore specializzato in neuroimaging all’Università di Londra. Questo libro fa veramente capire, in modo anche molto leggero, come percezione, apprendimento e azione siano sempre filtrati dalla nostra mente mettendo in evidenza la soggettività di molti eventi che noi riteniamo comuni.

ISBN: 8860301904

ISBN-139788860301901

 

L’Intelligenza Artificiale offre sempre spunti interessanti per riflettere su noi stessi. Per prima cosa è bene che sappiate che io sono un fermo sostenitore dell’ IA forte e pienamente convinto che, prima o poi, sarà possibile replicare artificialmente una mente  cosciente di se. Questo non implica che tali agenti intelligenti debbano per forza avere meccanismi di pensiero simili ai nostri, dopotutto la storia scientifica ci insegna che tutte le idee umanocentriche sono sempre state demolite dall’evidenza dei fatti, è quindi poco furbo ipotizzare che il cervello umano sia l’unica forma di “coscienza” possibile nell’universo. Tuttavia il cervello umano è senza dubbio l’unico che conosciamo attualmente e, quindi, è naturale che gran parte dell’IA si affidi a quello come musa ispiratrice e come traguardo.

A sostegno di questa tesi c’è poi la constatazione che, in più di un caso isolato, l’AI è riuscita ad anticipare scoperte che le neuroscienze cognitive avrebbero fatto solo alcuni anni dopo. Un caso fra i tanti è quello delle reti neurali. Una rete neurale non è altro che un sistema di neuroni artificiali (che non sono altro che sommatori pesati collegati ad un meccanismo “a soglia”, ovvero la cui uscita si attiva solamente se l’ingresso supera un certo valore) strettamente collegati fra loro. Tale meccanismo, sebbene composto da elementi relativamente semplici, è in grado di svolgere compiti estremamente complessi come, ad esempio, guidare un auto per centinaia di miglia  (si veda il progetto ALVINN). Queste reti vengono addestrate tramite il meccanismo di retro-propagazione. Questa tecnica consiste, per farla breve, nel modificare i pesi in ingresso ai neuroni artificiali in base al valore di predizione dell’intera rete: ho un dato ingresso x, la rete risponde y, se il valore esatto è y allora la rete non subisce variazioni, se invece il valore giusto era z la rete viene modificata in proporzione alla differenza fra il valore predetto e il valore desiderato. Bene, questo processo è del tutto simile al meccanismo alla base dell’apprendimento nella gran parte degli agenti viventi (non solo l’uomo):  è stato infatti constatato come il cervello rilasci dopamina a seconda se un evento previsto si verifichi o no. L’apprendimento tramite il modello errore-predizione è stato quindi sviluppato dall’AI alcuni anni prima che tale modello venisse formalizzato dai neuroscienziati.

L’AI inoltre ci permette anche di rivalutare molte nostre capacità. Tutti noi sappiamo riconoscere volti o comprendere il linguaggio naturale, ragion per cui consideriamo queste due attività delle cose “facili”. Diversamente solo poche persone sanno giocare bene a scacchi e quindi concludiamo che questo sia un compito “difficile”. In realtà dopo oltre 50 anni di ricerca nelle intelligenze artificiali  abbiamo decine di programmi in grado di giocare stupendamente a scacchi ma ben pochi programmi in grado di riconoscere un volto con prestazioni paragonabili a quelle umane. Questo quindi ci mostra come, in realtà, il giudizio che diamo ai vari problemi sia spesso invertito nel caso delle AI.

Perché? Una prima risposta consiste nel valutare l’importanza di tali problemi. Riconoscere un volto e capire il linguaggio sono cose essenziali a livello evolutivo. Animali che non siano in grado di riconoscere un potenziale predatore o preda da un sasso o da una pozza di lava difficilmente raggiungono l’età adulta, è quindi prevedibile che nel corso dell’evoluzione si siano formati cervelli abilissimi a riconoscere figure forme e colori e meno a giocare a scacchi. Ipoteticamente, infatti, se uccidessimo in fasce tutti i bambini che non siano in grado di giocare a scacchi ad un livello accettabile ci ritroveremmo, fra qualche milione di anni, davanti a esseri dotati di un cervello incredibilmente bravo a giocare a scacchi e, probabilmente, meno bravi a riconoscere un volto.

Il problema dell’evoluzione è quindi cruciale quando cerchiamo di imitare il cervello umano. Dobbiamo sempre tenere presente che il nostro cervello è quello che è perché c’è stata, nel corso delle ere, una pressione evolutiva che lo ha spinto ad avere certe capacità e a non averne altre.

Il secondo punto risiede proprio nella rappresentazione e nel modo in cui essa viene generata. Spesso infatti pretendiamo troppo dalle macchine che progettiamo. Prendiamo ad esempio la percezione visiva. Quando ci troviamo difronte alla percezione visiva ci rendiamo subito conto del problema fondamentale che la divide da problemi “semplici” come il gioco degli scacchi: il rumore. Il gioco degli scacchi è infatti facilmente rappresentabile in termini puramente matematici, è possibile cioè togliere semplicemente tutti gli aspetti rumorosi, ovvero che non danno alcuna informazione aggiuntiva tipo la forma degli scacchi o il colore della scacchiera. Quando invece tentiamo di semplificare la visione vengono alla luce notevoli problemi e domande. Ad esempio il concetto di forma e di bordo: che cos’è una forma? Quand’è che un quadrato a cui arrotondiamo via via gli angoli, smette di essere un quadrato e diventa un cerchio? Come definiamo il bordo di un oggetto data la sua immagine? Sappiamo che le risposte a queste domande sono essenziali per la visione ma non abbiamo idea di come il cervello umano le calcoli.

Abbiamo un gran numero di algoritmi in grado di darci i contorni e  la forma di un oggetto ma tali algoritmi sono spesso complessi, macchinosi ed utilizzano equazioni matematiche non banali sull’intera matrice dell’immagine. Ma, oltre al fatto che tali algoritmi sono piuttosto imprecisi, non è così che funziona l’occhio umano. L’occhio, ad esempio, è in gran parte ceco ad eccezione di una piccola zona centrale. L’impressione di avere un unica grande immagine è del tutto illusoria e prodotta dalla mente. L’immagine quindi è un prodotto della mente non un input percettivo. Perché allora vogliamo che le macchine funzionino direttamente sull’immagine? Non sarebbe meglio concentrarsi su come creare questa immagine partendo da una migliore copia dell’occhio umano? Ad esempio utilizzando i meccanismi basilari dell’uomo quali visione spot (ovvero concentrata al centro)  e rilevatore di movimento nella periferia dell’occhio (che serve a direzionare istintivamente il punto “vedente” dell’occhio in direzione di qualcosa che è cambiato per aggiornare l’immagine mentale). Queste cose potrebbero insegnarci nuovi modi per valutare  figure e bordi (un bordo potrebbe essere segnalato da una variazione repentina della visione (spot) che si avverte mentre l’occhio si muove oppure la forma di un oggetto può essere elaborata anche dal modo in cui l’occhio percorre il suo bordo (infatti quando osserviamo un quadrato, capiamo che è un quadrato percorrendo con gli occhi il bordo del quadrato, non lo facciamo consciamente ma lo facciamo).

Insomma, la neuroscienza ha molto da imparare dall’intelligenza artificiale, ma anche  l’intelligenza artificiale apprende molto dalla prima. Le due discipline si trascinano a vicenda verso nuovi traguardi e molto possiamo aspettarci da entrambe nei prossimi anni. E’ con questa consapevolezza che potremo arrivare un giorno a trovare la chiave, il punto di svolta, quella scoperta che ci farà capire che la soluzione ce l’avevamo da sempre sotto gli occhi. O forse dietro.

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha