Questi giorni sono giorni di limbo almeno per quanto riguarda il mio lato “tecnico”. Tuttavia sono senza mezzi. Non ho il computer a mia disposizione e sono relegato a quello strumento dannato che è il netbook. E io col netbook ci riesco a fare veramente poco: ad esclusione delle cose per cui è nato (ovvero andare su internet e chattare) non ci so fare nulla. Gli IDE poi, con le loro innumerevoli finestre, finiscono per saturare tutto lo schermo lasciando ben poco spazio al codice. Ho dannatamente bisogno di un portatile degno di questo nome.

La sensazione di limbo per un programmatore è proprio questa. Il non avere la possibilità o l’idea giusta per mettere a frutto le proprie conoscenze. Perché senza stimoli e sfide io non ci so stare, mi impigrisco e alimento il mio malessere.

Io, per fortuna, le idee da portare avanti ce l’ho… tuttavia sono condannato a questo strumento demoniaco che mi succhia la vogllia di fare qualunque cosa.

Vabbé, pazienza. So aspettare.

 

Il 10 febbraio 1996 il computer Deep Blue batte per la prima volta il campione del mondo di scacchi Garry Kasparov. L’incontro finì comunque a favore del maestro russo per 4 a 2.

L’11 maggio dell’anno successivo tuttavia Deep Blue è stato in grado di aggiudicarsi la rivincita battendo Kasparov per 3.5 a 2.5. La data è da sempre citata come una delle tappe fondamentali dell’AI eppure spesso tale evento viene citato per lo più a sproposito.

Innanzitutto bisogna valutare se possiamo aggiudicare la vittoria alla mente del pc oppure semplicemente alla sua incredibile capacità di calcolare fino a 100 milioni di posizioni al secondo. Quest’ultima caratteristica è un capolavoro dell’ingegneria, dell’elettronica e dell’algoritmica ma mi terrei molto cauto a definirlo un capolavoro dell’AI.

Che gli algoritmi che analizzano ed esplorano i nodi dello spazio di ricerca di un problema assumano un ruolo fondamentale per molti aspetti dell’AI è fuori discussione. Ma essi sono una parte della disciplina, non la sua totalità.

Oltre a tutto questo anche le modalità della vittoria lasciano a desiderare. Il computer era situato a chilometri di distanza e i tecnici della IBM erano autorizzati a modificare il programma fra una partita e l’altra. Cosa che fecero per adattare manualmente il programma al gioco di Kasparov per evitare che cadesse nelle trappole sferrate dal russo e in cui Deep blue cadde per ben due volte.

IBM ritirò frettolosamente Deep blue dalle competizioni senza chiarire molti dei fatti sopracitati. Ma l’assedio al campione del mondo di scacchi non finì con Deep Blue ma proseguì ancora per anni. Ad esempio il motore scacchistico Fritz in una serie di quattro partite fra l’11 e il 18 novembre riuscì a strappare due pareggi e due sconfitte.

La mente umana quindi rimane ancora la cosa più elegante che popola il nostro sistema solare e trovo estremamente riduttivo dare del “più bravo” ad un entità in grado solamente di elaborare dati molto velocemente.

Il cervello umano è ancora insostituibile, ad esempio per inventare modi originali per avvantaggiare programmi per pc nelle sfide con i campioni di scacchi. La ricerca deve andare nella direzione dell’eleganza piuttosto che nella direzione dell’efficienza e del calcolo puro.

Perché forse la creatività è proprio il tentativo del nostro cervello di compensare la nostra inefficienza.

 

Quello che segue è un altro piccolo esempio di come si possano scrivere programmi che implementano agenti intelligenti in una manciata di righe.

Supponiamo di voler replicare digitalmente il comportamento di una formica che insegue una traccia di ferormone fino ad un obiettivo. La traccia, per semplicità, non si sovrappone ed è sempre divisa da almeno una casella da un altra porzione di traccia.

Il comportamento di questa formica può essere emulato facendo uso di un agente reattivo con un bit di stato. Questi agenti hanno dei dati in ingresso che elaborano e trasformano in una serie di azione seguendo una o più regole di produzione. La nostra formica, ad esempio, può percepire solo se nella casella di fronte a lei c’è la traccia di ferormone oppure no.

La nostra formica è capace di effettuare solo le seguenti azioni:

  • Avanzare: avanza di una casella.
  • Ruotare a destra: ruota a destra.
  • Ruotare a sinistra: ruota a sinistra.
  • On: attiva il bit di stato.
  • Off: disattiva il bi di stato.
  • Sensi: riceve le informazioni sensoriali (nel nostro caso solo la presenza di ferormone nella casella di fronte) mettendo a Vero o Falso la variabile ferormone

A questo corrispondono le seguenti regole di produzioni.

  • S1: Se ferormone = True e stato = 0 -> Avanzare
  • S2: Se ferormone = False e stato = 0 -> On e Ruota a destra
  • S3: Se ferormone = True e stato = 1 -> Off e Avanzare
  • S4: Se ferormone = False e stato = 1 -> Ruota a destra x 2 e Off

Tutto questo è impleementato nel seguente codice:

# -*- coding: utf-8 -*-
"""
Questo modulo implementa una formica in grado di seguire
un percorso più un metodo per generare un percorso
casuale.
"""

import random

def generate_map(x, y) :
  """
  Genera un percorso casuale su una mappa X x Y.

    Args:
      X (int) larghezza della mappa
      Y (int) altezza della mappa

    Return:
      Una griglia X x Y contenente un percorso per la formica digitale.
  """

  mappa = [[0 for i in range(x)] for j in range(y) ] # Inizializza mappa vuota.
  i , j = 1, 1 # Posizione iniziale.
  mappa[j][i] = 1
  avaiable = ['right'] # Il primo passo va eseguito sempre a destra per semplicità.

  while len(avaiable) > 0 :
    next = random.sample(avaiable,1)

    if next[0] == 'up' :
      j -= 1
    elif next[0] == 'down' :
      j += 1
    elif next[0] == 'left' :
      i -= 1
    elif next[0] == 'right' :
      i += 1

    mappa[j][i] = 1

    avaiable = pick_avaiable(mappa,i,j)

  else :

    mappa[j][i] = 2 # Alla fine setta il punto obiettivo.

  return mappa

def pick_avaiable(mappa, i, j) :
  """
  Funzione ausiliaria per la generazione della mappa.
  Data una mappa e la posizione attuale restituisce le direzioni
  percorribili in accordo con la regola di non sovrapposizione.

    Args:
      mappa (mappa) La mappa corrente
      i (int) attuale posizione x
      j (int) attuale posizione y

    Return:
      Una lista di possibili direzioni.
  """

  new_avaiable = []

  if j > 1 :
    t = mappa[j-1][i-1] == 0 and mappa[j-1][i] == 0 and mappa[j-1][i+1] == 0
    t = t and mappa[j-2][i-1] == 0 and mappa[j-2][i] == 0 and mappa[j-2][i+1] == 0
    if t:
      new_avaiable.append('up')
  if j < (len(mappa) - 2) :     t = mappa[j+1][i-1] == 0 and mappa[j+1][i] == 0 and mappa[j+1][i+1] == 0     t = t and mappa[j+2][i-1] == 0 and mappa[j+2][i] == 0 and mappa[j+2][i+1] == 0     if t:       new_avaiable.append('down')   if i &gt; 1 :
    t = mappa[j-1][i-1] == 0 and mappa[j][i-1] == 0 and mappa[j+1][i-1] == 0
    t = t and mappa[j-1][i-2] == 0 and mappa[j][i-2] == 0 and mappa[j+1][i-2] == 0
    if t:
      new_avaiable.append('left')
  if i < (len(mappa[0]) - 2) :
    t = mappa[j-1][i+1] == 0 and mappa[j][i+1] == 0 and mappa[j+1][i+1] == 0
    t = t and mappa[j-1][i+2] == 0 and mappa[j][i+2] == 0 and mappa[j+1][i+2] == 0
    if t:
      new_avaiable.append('right')

  return new_avaiable

class Ant(object) :
  """
  Questa classe implementa la formica digitale.
  """


  VERSO = {'up': 0, 'right': 1, 'down': 2, 'left':3}

  def __init__(self, mappa) :
    self.x = 1
    self.y = 1
    self.mappa = mappa
    self.bit = False
    self.verso = self.VERSO['right']

  def r_rotate(self) :
    """
    Ruota la formica a destra.
    """

    self.verso += 1
    if self.verso == 4 :
      self.verso = 0

  def l_rotate(self) :
    """
    Ruota la formica a sinistra. Non usato attualmente.
    """

    self.verso -= 1
    if self.verso == -1 :
      self.verso = 3

  def forward(self) :
    """
    Fa avanzare di una casela la formica.
    """

    if self.verso == self.VERSO['up'] :
      self.y -= 1
    if self.verso == self.VERSO['down'] :
      self.y += 1
    if self.verso == self.VERSO['left'] :
      self.x -= 1
    if self.verso == self.VERSO['right'] :
      self.x += 1    

  def est_ferormone(self) :
    """
    Restituisce 1 se la casella daavanti al formica contiene
    del ferormone, 2 se è la casella obiettivo e 0 se la cella è vuota.
    """

    if self.verso == self.VERSO['up'] :
      return self.mappa[self.y-1][self.x]
    if self.verso == self.VERSO['down'] :
      return self.mappa[self.y+1][self.x]
    if self.verso == self.VERSO['left'] :
      return self.mappa[self.y][self.x -1]
    if self.verso == self.VERSO['right'] :
      return self.mappa[self.y][self.x + 1]

  def move(self) :
    """
    Cervello della formica. Elabora il comportamento in base
    alle regole di produzione sotto elencate.
    """

    if self.est_ferormone() == 2 :
      self.forward()
      return 'COMPLETE'
    if self.est_ferormone() and not self.bit :
      self.forward()
      return 'S1'
    if not self.est_ferormone() and not self.bit :
      self.bit = True
      self.r_rotate()
      return 'S2'
    if self.est_ferormone() and self.bit :
      self.bit = False
      self.forward()
      return 'S3'
    if not self.est_ferormone() and self.bit :
      self.r_rotate()
      self.r_rotate()
      self.bit = False
      return 'S4'

  def print_map_ant(self) :
    """
    Stampa la mappa del percorso contrassegnando
    la formica con una X.
    """

    print "-"*(len(self.mappa[0])*3)
    for j in range(len(self.mappa)) :
      for i in range(len(self.mappa[j])) :
        if i == self.x and j == self.y :
          print 'X ',
        elif self.mappa[j][i] == 0 :
          print "  ",
        else :
          print "%d " % self.mappa[j][i],
      print ":"
    print "-"*(len(self.mappa[0])*3)

Il codice contiene, oltre alla classe Ant che implementa la formica, una funzione per generare un percorso casuale che rispetta le regole che abbiamo fissato. Ad ogni chiamata del metodo move la formica digitale elabora i dati a sua disposizione e restituisce la regola di produzione prescelta (oppure COMPLETE se la formica ha raggiunto l’obbiettivo posto alla fine della traccia).

Inoltre la formica ha un metodo che stampa a schermo l’intera mappa del mondo, con la traccia e la posizione attuale della stessa (segnata con una X).

È un esempio carino che magari può essere espanso a piacimento, sia offrendo un output grafico più carino sia rendendo auomatica l’esecuzione dei vari move.

Sperando possiate trovare il codice interessante vi auguro una buona serata. :D

UPDATE:

Potete scaricare il file da qui:Ant.py

 

Questa mattina stavo rivedendo lo script che ho postato ieri. L’ho ottimizzato un po’ e ho tolto una 30-ina di righe. Dopodiché mi sono messo a fare alcuni test prestazionali provando ad addestrare una rete di circa 5000 neuroni disposti in vari modi (da 3 strati da 2000 fino a 250 strati da 20).

Le prestazioni non erano mozzafiato e questo lo sapevo già, il python non è il linguaggio più indicato per applicazioni fortemente CPU bound.

Tuttavia la sorpresa l’ho avuta quando ho testato lo script con la nuova versione di Python (la 3.1) e costatando una diminuzione delle prestazioni di circa il 50%.

Cinquantapercento! Mica bruscolini!

Ho rifatto il conto con vari test (sempre utilizzando il mio script) e il risultato era sempre di questa entità. Le cose sono due:

  1. L’interprete di python3.1 è proprio più lento di suo. In questo caso il problema verrà fuori quando si passerà definitivamente da 2.6 a 3.
  2. Alcune funzioni che nella 2.6 vanno a meraviglia nella 3.1 fanno schifo. In questo caso la cosiddetta migrazione da 2.6 a 3 potrebbe essere meno facile del previsto.

In attesa  di risposte vi chiedo se voi sapete qualcosa di più al riguardo. Se volete poi posso fornire altri dettagli sui vari test.

UPDATE 19/07/10

Ho ripetuto i test su un’altra distribuzione (sidux) e questa volta i risultati sono un po’ diversi. La versione 3 di python è sempre più lenta ma in percentuali accettabili (5% circa).

Inoltre ho provato anche con psyco ottimizzando del 75% le prestazioni del programma.

 

Si pensa spesso che grandi rivoluzioni siano accompagnate da grandi e complicate implementazioni. Tuttavia la storia ci insegna che le grandi idee spesso sono le più semplici.

Nell’IA sono proprio queste idee semplici a dare i migliori risultati. Fra queste un ottimo esempio è dato dall Reti Neurali. Questo modello matematico consiste, fondamentalmente, nell’interconnessione di unità a soglia.

Non entrerò nei dettagli matematici di una rete neurale ma voglio mostrare un codice di 290 righe (commenti e linee vuote incluse)  che implementano una semplicissima rete neurale non ricorsiva addestrata a con retropropagazione.

Non  credo sia la versione più “elegante” ne la più ottimizzata ma funziona discretamente.

# -*- coding: utf-8 -*-
"""
Modulo che contiene classi e metodi per rappresentare
una rete neurale non ricorsiva.

Modulo Didattico
"""


import math

class Sigmoid :
  """
  Rappresenta una singola unità sigmoide.
  """


  def __init__(self, num_in = 0) :
    """
    Costruttore.

      Args:
        num_in (int) Numero di ingressi della rete neurale.
    """

    self.w = [1.0]*num_in
    self.delta_value = [0,0]

  def __repr__(self) :
    return repr(self.w)

  @staticmethod
  def scalar(v1, v2) :
    """
    STATIC. Prodotto scalare fra i vettori v1 e v2.

      Args:
        v1 (vector) Vettore 1
        v2 (vector) Vettore 2

      Returns:
        Il prodotto scalare fra v1 e v2. (double)
    """

    res = 0
    for i in range(len(v1)) :
      res += v1[i]*v2[i]
    return res

  def sigma(self, ingresso) :
    """
    Ritorna il valore della funzione sigma del prodotto scalare fra
    ingresso e i pesi del sigmoide.

      Args:
        ingresso (vector) Vettore d'igresso

      Returns:
        Il valore della funzione sigma. (double)
    """

    s = Sigmoid.scalar(self.w, ingresso)
    return 1 / (1 + math.e**(-s))

  def activate(self, ingresso) :
    """
    Ritorna il valore di soglia per il prodotto scalare fra
    l'ingresso e i pesi del sigmoide.

      Args:
        ingresso (vector) Vettore d'ingresso.

      Returns:
        1 se il prodotto scalare è maggiore di zero, 0 altrimenti.
    """

    s = Sigmoid.scalar(self.w, ingresso)
    if s &gt; 0 : return 1
    return 0

  def add_in(self, w = 0) :
    """
     Aggiunge un ingresso al sigmoide.

      Args:
        w (double) Peso del nuovo ingresso.
    """

    self.w.append(w)

  def change(self, i, new) :
    """
    Cambia il valore del peso dell'ingresso i-esimo.

      Args:
        i (int) Numero dell'ingresso.
        new (double) Nuovo peso per l'ingresso.
    """

    if i &gt; len(self.w) :
      print "ERROR: "
      return
    self.w[i] = new
    self.delta_value[0] = 0

class NeuralNet :
  """
  Classe che rappresenta una rete neurale generica non ricorsiva.
  """


  def __init__(self, n_in) :
    """
    Costruttore.

    Il numero di ingressi NON comprende l'ingresso fittizio
    che simula la soglia.

      Args:
        n_in (int) Numero di ingressi della rete.
    """

    self.units = []
    self.n_in = n_in

  def __repr__(self) :
    return repr(self.units)

  def add_strate(self) :
    """
    Aggiunge uno strato alla rete.
    """

    self.units.append([])

  def add_unit(self, j, w = None) :
    """
    Aggiunge un unità sigmoide allo strato j.

      Args:
        j (int) Numero di strato.
        w (vector) Vettore dei pesi della nuova unità.
    """

    if j &gt;= len(self.units) :
      print "ERROR: "
      return

    # Se è il primo strato i sigmoidi hanno un numero di ingressi
    # pari al numero di ingressi della rete più  l'ingresso fittizio.
    # Altrimenti è pari al numero dei sigmoidi dello strato precedente
    # più l'ingresso fittizio.
    if j == 0 :
      sig = Sigmoid(self.n_in + 1)
    else :
      sig = Sigmoid(len(self.units[j-1]) + 1)
    self.units[j].append(sig)

    # Aggiorna il peso del nuovo sigmoide con i pesi nel vettore W.
    if w != None and len(w) == len(sig.w):
      p = 0
      for i in w :
        sig.change(p, i)
        p+=1

    # Se NON è l'ultimo strato bisogna aggiungere un ingresso a tutti
    # i sigmoidi dello strato successivo.
    if j != ( len(self.units) - 1 ) :
      for i in self.units[j+1] :
        i.add_in()

  def activate(self, ingresso) :
    """
    Attiva la rete neurale sull'ingresso 'ingresso'. Il
    vettore di ingresso NON contiene il vettore fittizio.

      Args:
        ingresso (vector) vettore di ingresso.

      Returns:
        uscita (vector) vettoore di uscita.
    """

    if len(ingresso) != self.n_in :
      print "ERRORE"
      return

    current = ingresso[:]
    current.append(1)
    for j in self.units :
      next_in = []
      for i in j :
        next_in.append(i.activate(current))
      current = next_in[:]
      current.append(1)
    return next_in

  def list_f(self, ingresso) :
    """
    Lista le uscite di ogni sigmoide della rete in base
    all'uscita della funzione sigmoide.

      Args:
        ingresso (vector) Vettore di ingresso.

      Returns:
        Il vettore contenente il risultato dell'applicazione di un
        vettore di ingresso PER OGNI sigmoide.
    """

    if len(ingresso) != self.n_in :
      print "ERRORE"
      return

    current = ingresso[:]
    current.append(1)
    res=[]
    for j in self.units :
      next_in = []
      for i in j :
        next_in.append(i.sigma(current))
      current = next_in[:]
      current.append(1)
      res.append(next_in)
    return res

  def delta(self, list_out, strato, sigmoide, desire) :
    """
    Calcola il valore delta per l'i-esimo sigmoide del j-esimo strato.

    La foruma del delta è:

      delta(i,j) = f * (1 - f) * SUM ( delta(j+1, l) )

    dove l varia per ogni sigmoide dello strato successivo.

    Se j = strato finale allora

      delta(i,j) = (desire - f) * f * (1 - f)

      Args:
        list_out (vector) Lista delle uscite della rete.
        strato (int) Numero dello strato.
        sigmoide (int) Numero del sigmoide.
        desire (double) Valore desiderato in uscita per l'ingresso.

      Returns:
        Il valore delta cercato.
    """

    f = list_out[strato][sigmoide]
    sig = self.units[strato][sigmoide]
    if sig.delta_value[0] == 1 :
      return sig.delta_value[1]
    if strato == ( len(self.units) - 1 ) :
      ris = ( desire - f) * f * (1 - f)
    else :
      ris = f*(1-f)
      cumulator = 0
      for l in range(len(self.units[strato + 1])) :
        wijl = self.units[strato+1][l].w[sigmoide]
        cumulator += self.delta(list_out, strato+1, l, desire) * wijl
      ris = ris * cumulator
    sig.delta_value = [1, ris]
    return ris

  def delta_reset(self) :
    """
    Invalida i delta_value dei sigmoidi.
    """

    for j in self.units :
      for i in j :
        i.delta_value[0] = 0

  def train(self, ingresso, desire, c = 1.0) :
    """
    Addestra una rete neurale sull'ingresso inserito.

    La formula dii retro propagazione è

      W(i,j) = W(i,j) + c * delta(i,j) * X(i,j)

    doeve W e X sono vettori.

      Args:
        ingresso (vector) Vettore in ingresso.
        desire (double) Valore desiderato in uscita.
        c (double) Velocità di apprendimento.
    """

    f = self.list_f(ingresso)
    x = f[:]    # Questa lista è come 'f' ma include anche l'ingresso.
                # Sia x che f NON includono gli ingressi fittizi.
    x.insert(0, ingresso)
    for j in range(len(self.units)) :
      for i in range(len(self.units[j])) :
        wij = self.units[j][i].w[:] # Sono i pesi del sigmoide (i,j)
        delta = self.delta(f, j, i, desire)
        for k in range(len(wij)) :
          if k == ( len(x[j]) ) : # Compensa la mancanza dell'ingresso
            xx = 1                # fittizio.
          else :
            xx = x[j][k]
          new = self.units[j][i].w[k] + c * delta * xx
          self.units[j][i].change(k, new)
    self.delta_reset()

Per capire a fondo il codice è necessario conoscere un po’ di teoria sulle reti neurali, per tutti gli altri vi basti notare come una delle più importanti metodologie dell’AI può essere replicata nei suoi tratti essenziali in meno di 300 righe.

 

I mondiali di calcio sono già finiti da qualche giorno e con loro si accingono a spegnersi le feroci critiche agli allenatori che, come sappiamo bene in prima persona, non hanno dato i risultati attesi.

Ed è proprio  dal paese dei nuovi campioni del mondo, ed in particolare dalla Universidad Carlos III de Madrid, i ricercatori hanno pubblicato alcuni risultati su un metodo di analisi automatica delle prestazioni sportive.

Il progetto si prefigge lo scopo di estrapolare degli indicatori sulle prestazioni e sulle tattiche dei giochi di squadra semplicemente analizzando il video di una partita. I ricercatori sono però fiduciosi di ottenere, in futuro, la possibilità di effettuare tali analisi in real-time in modo da poter migliorare e correggere le prestazioni sportive durante la partita. Cosa che, converrete con me, è molto più utile che farla a partita conclusa. :D

Il progetto, per il momento, è stato testato su alcune partite di basket ma è facilmente estensibile ad altri sport di squadra.

Forse fra qualche anno ci ritroveremo a “riconfigurare” un allenatore invece di esonerarlo. :D

Fonte: sciencedaily.com

 

Fra circa 15 ore sarà disponibile la versione 11.3. Tuttavia sono riuscito a reperire quella che al 99% sarà la versione rilasciata domani pomeriggio. Ovviamente non è molto dissimile alla RC2.

Il mio interessamento ad OpenSuse nasce dalla ricerca di una distribuzione KDE-centrica che non sia per utenti esperti come Sidux ma allo stesso tempo non sia un accrocco purulento come Kubuntu. È proprio rispetto a quest’ultimo che baserò la mia breve anteprima.

Prima di tutto l’istallazione, il liveCD e tutto il percorso boot/primo-avvio è mediamente più curato a livello grafico. L’installazione è decisamente semplice e basilare (anche troppo).

KWin non arranca. La prima cosa che ho verificato al primo avvio è stata la fluidità degli effetti grafici. Su Sidux sono abituato ad una reattività extra-scattante nonostante l’estrema limitatezza delle risorse hardware. Kubuntu, invece, come ben sapete ha gli effetti kwin più osceni fra tutte le distribuzioni kde-based che ho provato.

Il gestore pacchetti funziona. Il gestore pacchetti funziona e questo fondamentalmente mi basta. Non è paragonabile alla “bellezza” del software center di Ubuntu ma, almeno, funziona senza uscirsene con errori strampalati. Usabile, semplice e coinciso. Non estremamente user-friendly ma funzionale.

Le Gtk non ci fanno schifo. Differentemente dalla politica dissennata di Kubuntu al riguardo, in OpenSuse (in assenza di alternative valide) le applicazioni Gtk vengono tranquillamente utilizzate di default. Questo permette di avere come browser preinstallato Firefox (al posto dell’ormai inusabile Konqueror). Nonché OpenOffice (anche se KOffice, dati i suoi progressi, potrebbe presto sostituire la suite di casa Oracle).

Tuttavia OpenSuse contiene anche una miriade di piccole applicazioni, icone, collegamenti spesso inutili all’utente finale di una certa esperienza. Altra nota, per me, negativa è la disposizione di YaST che allo stato attuale sembra un pannello di controllo nel pannello di controllo. Sarebbe carino poter integrare le sue varie funzioni direttamente nel system-setting di KDE.

Insomma, OpenSuse al momento rimane l’unica distribuzione KDE-centrica di un certo livello che può essere indicata per un utente poco esperto.

 

“Ma che stai a fa mo?”  La domada mi arriva con cadenza periodica da parte di mia nonna ogni volta che sto a cena da lei.

“Intelligenza Artificiale.” rispondo io “Te l’ho già detto trecento volte!”. Rispondo sempre allo stesso modo e allo stesso modo ricevo sempre la stessa risposta: “Intelliche? Che sarebbe?”.

A questo punto arranco una risposta cercando di far minimamente intuire qualcosa sul comportamento intelligente di agenti artificiali ad una donna di 80 anni che non sa sintonizzare il digitale terrestre. La discussione ovviamente termina con il suo sguardo vuoto e allo stesso tempo contento che suo nipote faccia qualcosa di così complicato. La difficoltà fondamentale non consiste però nello spiegare di che cosa si occupa, in linea generale, l’AI, piuttosto il problema sta nel far capire perché sia così difficile da fare.

Se nelle altre scienze ci si occupa di cose tremendamente complicate, di campi gravitazionali, di reazioni chimiche, di strutture tridimensionali delle proteine e ricombinazione genetica. Nessuno che non abbia conoscenze specifiche si mette a discutere di queste cose con chi invece studia la materia da anni. Nell’AI, invece, da 50 anni si discute di cose chiare anche ad un bambino di 6 anni. Le persone quindi ti considerano o un fesso o un pazzo che cerca di replicare cose che solo il buon dio può donare e fare (come se i nostri cerveli fossero entità sovrannaturali).

Le cose cadono. Il passare del tempo. L’erba cresce. La differenza fra animali e piante. La morale. Sono tutti piccoli esempi di cose che qualunque persona o bambino sa quasi in modo innato ma che risultano terribilmente complicate da spiegare ad un robot.

Questa conoscenza di base prende il nome di conoscenza comune. Non esistono attualmente agenti intelligenti dotati di conoscenza comune. Gli unici tentativi in tale senso (ad esempio mindpixel) consistono nella creazione di enormi database contenenti tutta la conoscenza comune umana sotto forma di proposizioni logiche inserite manualmente.

Questa metodologia è valida come soluzione temporanea ma penso sia inadatta ad essere una soluzione definitiva (oltre ad essere poco elegante e affascinante). Dopotutto quant’è la conoscenza comune di un neonato? Poca. Quasi nulla. Questo dimostra che un sistema dotato della giusta base, dei giusti stimoli sensoriali e della giusta capacità di apprendere, può sviluppare autonomamente, tramite l’esperienza, la propria conoscenza comune.

“Ah! Famme il robot che pulisce pe casa!”. dopo aver afferrato cosa faccio mia nonna se ne esce spesso con questa frase. Ma anche molte altre persone, pensando alle applicazioni dell’AI, si perdono nel sogno del “robot maggiordomo”. Tuttavia anche questa applicazione ha il forte bisogno di una buona conoscenza comune. Essa è infatti necessaria per l’interpretazione del linguaggio parlato e per poter adempiere in modo autonomo ad una serie di compiti molto variegati quali cucinare, lavare i piatti e falciare il prato. Un tale robot dovrebbe sapere che il cibo lasciato troppo sul fornello brucia mentre lasciato poco resta crudo, che i piatti che cadono si rompono, che l’acqua calda lava meglio di quella fredda, che la  falciatrice si trova nel ripostiglio e via dicendo ma allo stesso tempo non necessita di essere esperto in chimica e termodinamica per poter valutare l’effetto del fuoco sul cibo ne calcolare campi gravitazionali e forze per sapere che un piatto che cade si rompe o che girarsi troppo velocemente con un bicchiere in mano può far cadere l’acqua.

La conoscenza comune potrebbe essere anche utilizzata per far rendere conto l’agente stesso dei propri limiti favorendo così l’apprendimento di nuove procedure specifiche.

Insomma, la ricerca sull’AI, se escludiamo tutto il lavoro di logica e di ricerca su grafi, si riduce proprio a questo: la ricerca di un modo per rappresentare ed apprendere la conoscenza comune.

Tutte cose che sanno tutti in modo inconscio e naturale. Quindi anche se mia nonna non sa e non saprà mai nulla sull’AI, per ora, sa già molto più di tutti noi che ci lavoriamo su.

 

Oggi dovevo scrivere una guida che rendesse persone digiune di git capaci di gestire un progetto multi utente distribuito. Non so se sono riuscito nell’interno ma dato che ci ho messo due ore per scrivere queste 6-7 pagine di documentazione vorrei condividerla con tutti.

L’ho scritta di fretta, alcune informazioni sono carenti e altre mancanti, ma credo che nel complesso sia uscita una cosa molto carina e istruttiva. Quantomeno, dopo aver letto questa guida, saprete dove mettere le mani per fare qualcosa. Penso che la aggiornerò ancora nei prossimi giorni quindi magari ripassateci. :D

Eccola qui:

GUIDA MEGAGALATTICA EXPRESS PER GIT

EDIT 13-07-2011: Ieri sera ho riletto tutta la guida e ho corretto alcune imprecisioni, oltre ad aver sistemato qualche carenza stilistica e grafica. Vi ricordo che suggerimenti e commenti sono molto apprezzati! :)

 

Le energie e le idee nell’open-source vengono sempre disperse. Si perdono in un mare di caos, scarsa documentazione, programmatori persi e spauriti che non sanno cosa fare, gente volenterosa costretta alla resa e ignorantoni che riempiono il web di script spazzatura. Ma a questo cerca di ovviare OpenHatch.

Questo sito è una sorta di social network per gente intenzionata a contribuire nell’open source. Non è necessario essere programmatori, ci sono compiti adatti a tutti, dal bug tracking alla documentazione alle traduzioni. Graficamente gradevole e ben funzionante.

Io vi consiglio calorosamente di iscrivervi e fare un po’ di pubblicità. Mi sembra l’idea giusta per ridare un po di vita ad una comunità open-source un po’ stanca e dispersa.

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha