Osservazioni e spunti sull'intelligenza umana e su quella artificiale senza un ordine preciso.
lunedì 11 marzo 2013
Ascolta, guarda e clicca
Leggo questo articolo su Repubblica e devo dire ne resto impressionato.
Ascolta, clicca e guarda ecco la nuova formula per insegnare ai ragazzi
http://www.flcgil.it/rassegna-stampa/nazionale/ascolta-clicca-e-guarda-ecco-la-nuova-formula-per-insegnare-ai-ragazzi.flc
Trovo paradossale l'entusiasmo con cui non solo ci arrendiamo, ma addirittura ci appassioniamo a questo nuovo metodo di comunicazione fatto di inquinamento mentale e caos. I ragazzi non sono capaci di mantenere la concentrazione cinque minuti sulla stessa cosa, perché studiano tra Facebook, le vibrazioni del cellulare, gli mp3 e la televisione? E allora si inventano modi caotici di studiare, per intercettare la loro attenzione. "Navigando nel V canto dell’Inferno nella piattaforma di My-LabLetteratura, collegata a un manuale scolastico cartaceo, ecco la voce di Gassman che recita la passione di Paolo e Francesca,mentre una “linea del tempo” sottolinea le date fondamentali della vita del poeta. E, sullo sfondo, scorrono i quadri ispirati al canto più celebre della Divina Commedia".
Ottimo, così siamo noi a certificare allo studente che tenere l'attenzione su una cosa per cinque minuti è una cosa inutile. E' più o meno come dire che dato che il mare è inquinato, tanto vale rovesciarvi dentro il petrolio e prendere atto, possibilmente con un certo entusiasmo, che il mare 2.0 è fatto al 50% di acqua e al 50% di petrolio.
La questione è semplice. Il learning by doing è una eccezionale forma di apprendimento, scarsamente utilizzata in Italia, che può funzionare molto bene col digitale. Anche gli stessi ebook, opportunamente collegati a dizionari, wikipedia, e strumenti di approfondimento, sono una ottima evoluzione degli strumenti di studio. Ma insegnare, invece, ai ragazzi che non serve saper leggere per un'ora di seguito, e attirare la loro attenzione su video, musica, animazioni e infografiche, è un boomerang. E' un danno cognitivo enorme.
La verità è il consumo culturale generale sta calando livello. Le edicole sono piene di spazzatura, le tv altrettanto. Internet, con la sua legge assoluta delle pageview, è l'impero del male da questo punto di vista. Le cose più lette, più viste, più cliccate, sono quasi sempre spazzatura (e infatti hanno quasi sempre a che fare con la tv). Il motivo è semplice. E' l'intrattenimento la cosa più consumata, perché è quello che non richiede impegno. Il problema è che l'intrattenimento dovrebbe essere neutro. Non dovrebbe impegnare la mente, ma neanche provocarle un danno. Invece ne provoca, e molto. Riempie la testa delle persone di informazioni ridondanti, contraddittorie, consuma capacità di memoria e di computazione. Il cervello finisce per restare inquinato. Tutto questo ha ovvi motivi commerciali, che si possono comprendere. Quello che è criminale è trasformare questo modo di comunicare nel sistema di apprendimento a cui vogliamo affidare la crescita dei nostri figli.
venerdì 15 febbraio 2013
Overfitting è memoria? Questo è il problema
Quasi sempre un problema rappresenta una soluzione a un problema precedente. In questo caso il problema dell'overfitting, esaminato nel post del 13 dicembre, rappresenta un inizio di soluzione a un problema precedente: la memoria. La questione è semplice: come memorizza il cervello? Dove mette le informazioni, come le organizza? Ci sono alcune caratteristiche che ci permettono di escludere una organizzazione a matrice sul modello di un hard disk.
Una è che il cervello ricerca per associazioni di idee e non è in grado di intercettare una singola informazione se non inserita in un contesto (il colore dell'altalena del campeggio lo ricordiamo solo se facciamo lo sforzo di ricordare tutti i dettagli del campeggio, ripercorrendone la pianta, l'organizzazione, le persone conosciute).
Allo stesso tempo, in generale è molto più facile per il cervello stabilire se una cosa è nota, piuttosto che ricordarne tutti i dettagli. Per esempio è più facile dire davanti a una foto se una certa faccia l'abbiamo già vista, piuttosto che visualizzare nella testa un viso e descriverne i tratti. In altre parole, è molto facilitante per il cervello avere davanti un pattern da riconoscere piuttosto che ricordarne uno completamente. Questa seconda cosa è esattamente la stessa che succede con l'overfitting. In effetti l'overfitting è una forma di memorizzazione. E' una forma un po' laterale di apprendimento, in cui invece che "capire la lezione" la si "impara a memoria". Insomma la cosa è comunque molto interessante. Ora, si può dire però che sia un modo compiuto di memorizzare solo se siamo poi in grado di estrarre l'informazione. Di fronte a una immagine, con il metodo illustrato il 1 dicembre (quello che ha il problema dell'overfitting), siamo in grado di dire se appartiene all'insieme di quelle memorizzate, o se si tratti di una nuova. La questione è: siamo anche in grado di estrarre le 40 immagini memorizzate e salvarle da qualche parte in maniera "tradizionale"? Se sì, abbiamo risolto il problema della memorizzazione del cervello. Attualmente un metodo che consenta di farlo non c'è. Trovarlo sarebbe un enorme passo avanti nella costruzione di un cervello artificiale.
Il tema della memoria è centrale per l'intelligenza artificiale: il suo rapporto con l'apprendimento è il cuore stesso del problema dell'intelligenza. Sull'argomento ho già scritto diversi post:
Rete a riconocimento del già visto
Memoria per associazioni? Per una fuga rapida
La memoria
Una è che il cervello ricerca per associazioni di idee e non è in grado di intercettare una singola informazione se non inserita in un contesto (il colore dell'altalena del campeggio lo ricordiamo solo se facciamo lo sforzo di ricordare tutti i dettagli del campeggio, ripercorrendone la pianta, l'organizzazione, le persone conosciute).
Allo stesso tempo, in generale è molto più facile per il cervello stabilire se una cosa è nota, piuttosto che ricordarne tutti i dettagli. Per esempio è più facile dire davanti a una foto se una certa faccia l'abbiamo già vista, piuttosto che visualizzare nella testa un viso e descriverne i tratti. In altre parole, è molto facilitante per il cervello avere davanti un pattern da riconoscere piuttosto che ricordarne uno completamente. Questa seconda cosa è esattamente la stessa che succede con l'overfitting. In effetti l'overfitting è una forma di memorizzazione. E' una forma un po' laterale di apprendimento, in cui invece che "capire la lezione" la si "impara a memoria". Insomma la cosa è comunque molto interessante. Ora, si può dire però che sia un modo compiuto di memorizzare solo se siamo poi in grado di estrarre l'informazione. Di fronte a una immagine, con il metodo illustrato il 1 dicembre (quello che ha il problema dell'overfitting), siamo in grado di dire se appartiene all'insieme di quelle memorizzate, o se si tratti di una nuova. La questione è: siamo anche in grado di estrarre le 40 immagini memorizzate e salvarle da qualche parte in maniera "tradizionale"? Se sì, abbiamo risolto il problema della memorizzazione del cervello. Attualmente un metodo che consenta di farlo non c'è. Trovarlo sarebbe un enorme passo avanti nella costruzione di un cervello artificiale.
Il tema della memoria è centrale per l'intelligenza artificiale: il suo rapporto con l'apprendimento è il cuore stesso del problema dell'intelligenza. Sull'argomento ho già scritto diversi post:
Rete a riconocimento del già visto
Memoria per associazioni? Per una fuga rapida
La memoria
giovedì 13 dicembre 2012
Riconoscimento volti artigianale: il fantasma dell'overfitting
Con l'esempio di rete neurale per il riconoscimento dei volti che ho pubblicato il primo dicembre c'è un problema: l'overfitting. La questione è presto spiegata: il modello è sovradimensionato, ci sono troppi neuroni di ingresso. Con un coefficiente per ciascun neurone, ci sono circa 100mila paramentri. Il risultato è che, con appena 40 esempi in ingresso, la sua capacità di individuare singole differenze tra una immagine e l'altra è troppo fine.
Dal momento che può confrontare ogni singolo pixel tra una immagine e l'altra, succede che prendono il sopravvento dettagli minori: se in un dato punto compaiono capelli o sfondo, se in un altro c'è la maglietta beige o azzurra, e via di questo passo. E' talmente ampio il numero di dettagli su cui il modello può giocare che è in grado di identificare le singole immagini.
Di fatto le memorizza. Il risultato è che riconosce perfettamente le immagini usate in fase di apprendimento ma poi non è in grado di riconoscerne di nuove. Si basa su dettagli insignificanti e in effetti non fa la cosa più importante: non generalizza, non individua le caratteristiche distintive fondamentali tra un volto e l'altro. Come si risolve questo problema? Con alcune tecniche statistiche che riducono l'immagine a pochi parametri sui quali poi la rete può fare apprendimento. Il primo e il più noto è Eigenface.
Il mio interesse in questo studio però era un altro.
A me non interessa studiare il modo più efficiente di riconoscere i volti in sé, mi interessa studiare le reti neurali come lontano parente del meccanismo di funzionamento del cervello. Il tema è come fa il cervello a riconoscere gli oggetti, le persone, e così via, e ad associarvi suoni (che a sua volta impara a distinguere per fonemi) costruendoci poi sopra un linguaggio.
Detto in altre parole, il problema non è solo riconoscere Piero da Carlo, ma anche riconoscere un temperamatite o una sedia da Carlo e perciò ridurre il problema a pochi parametri facciali con un procedimento analitico a monte non risolve affatto il problema, lo aggira. Farei io il lavoro che vorrei capire invece come fa a fare il cervello.
C'è da dire che probabilmente il cervello non ha grossi problemi di overfitting perché ha letteralmente milioni di esempi su cui fare riconoscimento del pattern, avendo a disposizione un flusso di immagini dagli occhi mediamente 16 ore su 24 per anni, e non i miseri 40 di quel modello. Perciò forse, oltre a trovare un modo di ridurre i parametri senza compromettere l'esperimento, c'è anche da introdurre un meccanismo di input a flusso video, in modo da poter campionare migliaia di immagini.
Non appena avrò le idee più chiare su come affrontare il problema lo scriverò qui. Ringrazio molto Raistlin, della lista hackmeeting, per aver segnalato il problema.
Dal momento che può confrontare ogni singolo pixel tra una immagine e l'altra, succede che prendono il sopravvento dettagli minori: se in un dato punto compaiono capelli o sfondo, se in un altro c'è la maglietta beige o azzurra, e via di questo passo. E' talmente ampio il numero di dettagli su cui il modello può giocare che è in grado di identificare le singole immagini.
Di fatto le memorizza. Il risultato è che riconosce perfettamente le immagini usate in fase di apprendimento ma poi non è in grado di riconoscerne di nuove. Si basa su dettagli insignificanti e in effetti non fa la cosa più importante: non generalizza, non individua le caratteristiche distintive fondamentali tra un volto e l'altro. Come si risolve questo problema? Con alcune tecniche statistiche che riducono l'immagine a pochi parametri sui quali poi la rete può fare apprendimento. Il primo e il più noto è Eigenface.
Il mio interesse in questo studio però era un altro.
A me non interessa studiare il modo più efficiente di riconoscere i volti in sé, mi interessa studiare le reti neurali come lontano parente del meccanismo di funzionamento del cervello. Il tema è come fa il cervello a riconoscere gli oggetti, le persone, e così via, e ad associarvi suoni (che a sua volta impara a distinguere per fonemi) costruendoci poi sopra un linguaggio.
Detto in altre parole, il problema non è solo riconoscere Piero da Carlo, ma anche riconoscere un temperamatite o una sedia da Carlo e perciò ridurre il problema a pochi parametri facciali con un procedimento analitico a monte non risolve affatto il problema, lo aggira. Farei io il lavoro che vorrei capire invece come fa a fare il cervello.
C'è da dire che probabilmente il cervello non ha grossi problemi di overfitting perché ha letteralmente milioni di esempi su cui fare riconoscimento del pattern, avendo a disposizione un flusso di immagini dagli occhi mediamente 16 ore su 24 per anni, e non i miseri 40 di quel modello. Perciò forse, oltre a trovare un modo di ridurre i parametri senza compromettere l'esperimento, c'è anche da introdurre un meccanismo di input a flusso video, in modo da poter campionare migliaia di immagini.
Non appena avrò le idee più chiare su come affrontare il problema lo scriverò qui. Ringrazio molto Raistlin, della lista hackmeeting, per aver segnalato il problema.
sabato 1 dicembre 2012
Come farsi in casa una rete neurale per il risconoscimento dei volti
[Nota posteriore: questo procedimento contiene un problema che ho descritto qui]
Cosa sono le reti neurali e perché sono tanto interessanti? Perché sono una trovata matematica grazie alla quale si rovescia la normale logica di programmazione e si può costruire una macchina che impara da sè. Per esempio, le reti neurali sono perfette per riconoscere i volti delle persone. Ecco un programma che, dati due volti, riconosce se abbiamo di fronte l'uno o l'altro. Ecco i due volti sui quali lavoreremo.
PROGRAMMAZIONE CLASSICA
Anche con un programma classico, a impostazione imperativa, si può fare riconocimento dei volti. Basta individuare le caratteristiche distintive delle due immagini. Il primo dei due ragazzi ha capelli più lunghi e più neri, sopracciglia più marcate, naso più lungo. Se prendiamo la matrice di pixel che compone l'immagine possiamo confrontare porzioni equivalenti e stabilire se appartengono all'uno o all'altro ragazzo in modo relativamente semplice. Qual è il problema? Che il programma funzionerà solo con queste due facce, non sarà per niente flessibile. Lo si può fare allora in modo decisamente più complesso, in modo che, dati due volti generici, il programma ne analizzi le porzioni equivalenti e memorizzi le differenze. Così sarà più flessibile, sarà in grado di esaminare qualsiasi faccia, ma il lavoro per scriverlo sarà molto complesso. Questa è programmazione con una impostazione imperativa classica. La programmazione con le reti neurali invece è radicalmente diversa. Di fatto noi non abbiamo bisogno di individuare le caratteristiche distintive tra le due immagini. E' la rete che lo fa.
E come lo fa? Correggendo la propria configurazione interna, in funzione degli esempi che le mettiamo davanti. Le diamo in pasto tante immagini del primo ragazzo, dicendole che è il primo ragazzo, e poi le diamo in pasto tante immagini del secondo, dicendole che è il secondo. Con un processo di correzione progressiva, che è quello viene definito apprendimento, la rete arriva a confingurarsi in modo tale da essere in grado di distingere i due volti.
COS'È UNA RETE NEURALE
Andiamo per ordine. Cos'è una rete? In effetti è ciò che il nome suggerisce: un insieme di nodi collegati tra loro. La caratteristica fondamentale di una rete neurale è quella di avere alcuni nodi di ingresso, alcuni nodi di uscita ed eventualmente alcuni nodi intermedi. Può esserci più di uno strato di nodi intermedi a seconda della complessità della rete.
Questa è una rete semplice, senza nodi intermedi:
Partendo dal generale per andare via via nel dettaglio e nel particolare, l'idea di base è questa: c'è uno scatolotto con alcuni ingressi e una uscita. Nel nostro caso gli ingressi rappresentano i valori dei pixel che compongono l'immagine (i volti sono dentro una bitmap di risoluzione 352x288, quindi abbiamo 101.376 ingressi, uno per pixel) mentre l'uscita sarà una sola e risulterà pari a un numero che identifica il volto: -1 per il primo, 1 per il secondo.
Nella prima fase, quella di apprendimento, forniamo alla rete sia l'ingresso che l'uscita. Le diamo tanti esempi del primo volto, a cui associamo l'uscita -1 e tanti esempi del secondo, dicendo che con quest'altro deve restituire invece +1. Esaurita la fase dell'apprendimento, lo scatolotto dovrebbe essere in grado, data una nuova foto che non aveva mai visto, di restituire l'uscita corretta.
Come lo fa? Generalizzando. Quello che succede è che la rete impara a distinguere le differenze significative da quelle non significative.
In pratica la cosa funziona così: ogni neurone di ingresso è collegato all'unico neurone di uscita. Il valore in arrivo al neurone di uscita sarà perciò la somma dei valori dei neuroni ingresso, vale a dire dei codici colore dei pixel dell'immagine, ognuno moltiplicato per un coefficiente.
Nella figura sono solo due, nel nostro caso sono 101.376, che vanno da x1 a x101376. Ciascun xi è moltiplicato per il rispettivo wi, cioè il relativo coefficiente. Il valore in ingresso al neurone di output è dato perciò da:
L'idea è che i pixel importanti, quelli che permettono di distinguere una immagine dall'altra (per esempio dove ci sono i capelli, che hanno colore diverso tra i due ragazzi), avranno coefficienti alti. Quelli meno importanti (per esempio quelli dello sfondo, la parete azzurrina), che non hanno alcuna importanza nell'individuare di quale dei due volti si tratti, avranno coefficienti molto bassi, prossimi allo zero. In questo modo si ottiene un valore che dà rilievo alle differenze tra le due immagini. Questi sono i pesi sinaptici. Il risultato, cioè a sommatoria complessiva dei valori dei pixel per i rispettivi pesi, poi, a sua volta passa attraverso una funzione di attivazione. Una funzione che si presta molto bene è la tangente iperbolica, che ha questo tipo di risposta:
Perché utilizziamo una funzione di attivazione, in particolare questa? Perché garantisce una uscita sempre compresa tra -1 e +1, qualsiasi sia il dato di partenza. Nel nostro caso, poiché la sommatoria di oltre 100mila ingressi, per quanto i coefficienti possano essere bassi, porta a un valore piuttosto alto, ho diviso - trovando empiricamente il valore vedendo un po' cosa usciva dal programma - il valore per 10.000. Il valore così trovato va in pasto alla tangente iperbolica che restituisce un valore prossimo a -1 o +1 e viene approssimato a quello più vicino.
L'APPRENDIMENTO
Una rete così realizzata è in grado di riconoscere un volto dall'altro se configurata con i coefficienti giusti. E il calcolo dei coefficienti giusti è quello che si definisce apprendimento. Come si calcolano i coefficienti giusti? Per correzioni progressive. All'inizio dell'apprendimento la rete calcola una uscita, in funzione degli ingressi che ha (al primo giro tutti i valori sinaptici valgono zero). Poi corregge i coefficienti in funzione della differenza tra l'uscita calcolata e quella teorica (cioè che noi vorremmo che restituisse, quella che noi abbiamo associato a quella determinata immagine: nel caso del primo volto -1, nel caso del secondo +1).
Dato:
t = risposta teorica
y = risposta calcolata dalla rete con gli attuali pesi sinaptici
xi = valore di ingresso (da 0 a 83.839)
La differenza è calcolata così:
(1 - y*y) rappresenta la derivata (y*y vuol dire "y al quadrato") della funzione di attivazione del neurone di uscita, cioè della tangente iperbolica.
Una volta calcolato la differenza, si correggono i pesi sinaptici così:
Per evitare di andare fuori strada correggendo il differenziale troppo a ogni nuova immagine sottoposta, si applica anche un ulteriore coefficiente, detto tasso di apprendimento, che serve a limitare la velocità del cambiamento. Il tasso di apprendimento è compreso tra 0 e 1 e in genere viene fissato intorno a 0,8. Se a è il tasso di apprendimento, il calcolo della differenza diventa allora:
La cosa migliore è dare in pasto alla rete le immagini in modo incrociato. Cioè prima un esempio del primo volto, poi un esempio del secondo, poi di nuovo uno del primo e così via. In questo modo l'apprendimento è più efficiente, perché a ogni passaggio emergono con più evidenza le differenze tra una e l'altra.
Una volta esaurito il compito, quello che abbiamo è di fatto una matrice di coefficienti, che possiamo salvare, per comodità, ancora in una bitmap, che si presta perfettamente allo scopo, essendo come formato una matrice di valori. Facendo così, poi, è possibile visualizzare l'immagine che ne viene fuori e il risultato è impressionante:
Non solo. Ma c'è anche un'altra cosa interessante. Questa è l'immagine che viene fuori se i pesi sono numeri float, cioè numeri con la virgola. Ma se utilizziamo come coefficienti dei numeri interi, allora l'immagine, pur molto simile, perde alcuni dettagli.
DEBOLEZZE DI QUESTA RETE NEURALE
La principale debolezza di questa rete neurale è il fatto che, avendo un solo strato, assegna di fatto un coefficiente a ogni pixel. Quindi le due facce devono trovarsi grosso modo sempre nella stessa posizione. Se in una immagine il volto invece di trovarsi al centro, si trova a destra o a sinistra, la rete perde la bussola. Per renderla in grado di individuare e riconoscere le somiglianze tra le facce anche se l'immagine si sposta, occorrerebbe uno strato di neuroni intermedio. In questo modo il primo strato di connessioni sinaptiche individuerebbero la posizione del volto e il secondo individuerebbe le caratteristiche del volto. L'apprendimento di una rete con uno strato intermedio è però molto diverso da una rete semplice come questa e lo vedremo in futuro.
IL PROGRAMMA
Questi di seguito c'è il programma in C per fare tutto questo.
Il primo file, neuralnet.c, è il programma che, prendendo una immagine, dice se si tratta del primo o del secondo volto.
Il secondo file, training.c, rappresenta il programma di apprendimento della rete e calcola i pesi sinaptici (attenzione: ci sono letteralmente milioni di moltiplicazioni da fare, il processore ci mette del tempo, potrebbe volerci anche qualche ora a completare il processo di apprendimento).
Il terzo file, neuralnet.h, è un file libreria e contiene tutte le funzioni utilizzate nei primi due file.
Se avete una distribuzione Linux i file si compilano così:
Avete ora così a disposizione i due programmi. Il training si avvia così:
Il programma impara da 40 immagini bitmap di risoluzione 352x288. Le immagini devono essere nella stessa cartella del programma, numerate, e chiamarsi da Image000.bmp a Image039.bmp (con la i maiuscola). Quelle pari, (0, 2, 4, ecc.) devono essere della prima faccia, quelle dispari della seconda. Il programma genera un file che si chiama pesi.bmp nel quale sono registrati tutti i pesi sinaptici.
Esaurito l'apprendimento, si può utilizzare la rete, con il seguente comando:
L'immagine da verificare deve essere una bitmap di risoluzione 352x288 che si chiama image2test.bmp ed è sempre nella stessa cartella.
CODICE SORGENTE IN C
NEURALNET.C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "./neuralnet.h"
#define num_pixel 76032
#define image_widht 352
int main(void)
{
unsigned int x[num_pixel]; // array dei neuroni dell'immagine di input
float w[num_pixel],x_float[num_pixel]; // array dei pesi sinaptici
unsigned char header_image[54];
char filename_immagine[20]="image2test.bmp"; // nome del file dell'immagine da testare
char filename_pesi[20]="pesi.bmp"; // nome del file dell'immagine dei pesi
float out_neurone[num_pixel];
float out_neurone_uscita = 0;
int j;
if (open_image_int_mono(x,header_image,filename_immagine)==1)
{printf("errore di apertura dell'immagine %s",filename_immagine);
exit (1);}
for (j=0;j<num_pixel;j++)
{
x_float[j] = x[j];
}
if (normalize(x_float)==1)
{printf("errore di normalizzazione dell'immagine %s",filename_immagine);
exit (1);}
if (open_image_float(w,header_image,filename_pesi)==1) // legge l'array dei pesi
{printf("errore di apertura dell'immagine %s",filename_pesi);
exit (1);}
/*
// fine della fase di apprendimento
// uso della rete
printf("\nTEST DELLA RETE\n");
printf("la rete risponde con -1 per un'immagine simile a una delle prime 50 immagini di training (0-49)\n");
printf("la rete risponde con +1 per un'immagine simile a una delle seconde 50 immagini di training (50-99)\n");
printf("(ATTENZIONE: i neuroni sono bipolari quindi i valori sono 1 e -1, non i soliti 1 e 0.)\n");
*/
for (j=0;j<num_pixel;j++)
{
out_neurone[j] = x_float[j] * w[j];
out_neurone_uscita = out_neurone_uscita + out_neurone[j];
}
out_neurone_uscita = out_neurone_uscita /100000;
printf("out_neurone_uscita non elaborata: %f\n",out_neurone_uscita);
out_neurone_uscita=tanhf(out_neurone_uscita);
printf("out_neurone_uscita con la tang iperbolica: %f\n",out_neurone_uscita);
/*
attenzione: se il neurone è bipolare come in questo caso e non continuo allora Ú necessario interpretare il valore raggiunto e verificare se supera la soglia, in modo da determinare se vale 1 oppure -1. un neurone bipolare non può avere un'uscita come 3 oppure -3 ma solo 1 e -1.
il comportamento di questi neuroni è:
y = 1 se { sommatoria di tutti i [valore di input della sinapsi per il relativo peso] > 0 }
y = 1 se { sommatoria di tutti i [valore di input della sinapsi per il relativo peso] <= 0 }
(è riportato a pagina 68 del manuale)
*/
if (out_neurone_uscita < 0) printf ("\nvolto 1\n");
if (out_neurone_uscita > 0) printf ("\nvolto 2\n");
//printf("\n y: %f\n",out_neurone_uscita);
}
TRAINING.C
#include <stdio.h>
#include <stdlib.h>
//#include <string.h>
#include <math.h>
#include "./neuralnet.h"
#define num_pixel 101376
#define image_widht 352
void main(void)
{
unsigned int array_image[288][image_widht]; // array dei pixel dell'immagine
unsigned int x[num_pixel]; // vettore monodimensionale che raccoglie i valori dell'array dell'immagine
float x_float[num_pixel]; // vettore che trasforma i valori in float
//unsigned int *punt;
unsigned char header_image[54];
float w[num_pixel]; // vettore dei pesi
unsigned int w_int[num_pixel]; // vettore dei pesi convertiti in intero
//fase di apprendimento
float apprendimento;
char filename[100][20];
char filenamew[20]="pesi.bmp"; // nome del file per salvare i pesi sinaptici calcolati
float t[100];
//float x[num_pixel];
float y,y_tot,y_diviso;
float delta_w;
int i,j,k,p,n;
// nb: num_pixel è il risultato di 288 x image_widht.
apprendimento=0.9;// tasso di apprendimento
strcpy(filename[0],"Image000.bmp"); t[0]=-1;
strcpy(filename[1],"Image001.bmp"); t[1]=1;
strcpy(filename[2],"Image002.bmp"); t[2]=-1;
strcpy(filename[3],"Image003.bmp"); t[3]=1;
strcpy(filename[4],"Image004.bmp"); t[4]=-1;
strcpy(filename[5],"Image005.bmp"); t[5]=1;
strcpy(filename[6],"Image006.bmp"); t[6]=-1;
strcpy(filename[7],"Image007.bmp"); t[7]=1;
strcpy(filename[8],"Image008.bmp"); t[8]=-1;
strcpy(filename[9],"Image009.bmp"); t[9]=1;
strcpy(filename[10],"Image010.bmp"); t[10]=-1;
strcpy(filename[11],"Image011.bmp"); t[11]=1;
strcpy(filename[12],"Image012.bmp"); t[12]=-1;
strcpy(filename[13],"Image013.bmp"); t[13]=1;
strcpy(filename[14],"Image014.bmp"); t[14]=-1;
strcpy(filename[15],"Image015.bmp"); t[15]=1;
strcpy(filename[16],"Image016.bmp"); t[16]=-1;
strcpy(filename[17],"Image017.bmp"); t[17]=1;
strcpy(filename[18],"Image018.bmp"); t[18]=-1;
strcpy(filename[19],"Image019.bmp"); t[19]=1;
strcpy(filename[20],"Image020.bmp"); t[20]=-1;
strcpy(filename[21],"Image021.bmp"); t[21]=1;
strcpy(filename[22],"Image022.bmp"); t[22]=-1;
strcpy(filename[23],"Image023.bmp"); t[23]=1;
strcpy(filename[24],"Image024.bmp"); t[24]=-1;
strcpy(filename[25],"Image025.bmp"); t[25]=1;
strcpy(filename[26],"Image026.bmp"); t[26]=-1;
strcpy(filename[27],"Image027.bmp"); t[27]=1;
strcpy(filename[28],"Image028.bmp"); t[28]=-1;
strcpy(filename[29],"Image029.bmp"); t[29]=1;
strcpy(filename[30],"Image030.bmp"); t[30]=-1;
strcpy(filename[31],"Image031.bmp"); t[31]=1;
strcpy(filename[32],"Image032.bmp"); t[32]=-1;
strcpy(filename[33],"Image033.bmp"); t[33]=1;
strcpy(filename[34],"Image034.bmp"); t[34]=-1;
strcpy(filename[35],"Image035.bmp"); t[35]=1;
strcpy(filename[36],"Image036.bmp"); t[36]=-1;
strcpy(filename[37],"Image037.bmp"); t[37]=1;
strcpy(filename[38],"Image038.bmp"); t[38]=-1;
strcpy(filename[39],"Image039.bmp"); t[39]=1;
for (i=0;i<num_pixel;i++) // inizializzazione dei pesi sinaptici, uno per ogni neurone di ingresso
{
w[i] = 0;
}
/* QUESTO È STATO COMMENTATO PERCHÉ CONVENIVA INCROCIARE LE IMMAGINI
for (i=0;i<20;i++) // impostazione dell'uscita desiderata: -1 per le immagini da 0 a 19.
{
t[i] = -1;
}
for (i=20;i<40;i++) // impostazione dell'uscita desiderata: 1 per le immagini da 20 a 39.
{
t[i] = 1;
}
*/
for (i=0;i<40;i++)
{
if (open_image_int_mono(x,header_image,filename[i])==1)
{printf("errore di apertura dell'immagine %s",filename[i]);
exit (1);}
for (j=0;j<num_pixel;j++)
{
x_float[j] = x[j];
}
if (normalize(x_float)==1)
{printf("errore di normalizzazione dell'immagine %s",filename[i]);
exit (1);}
/* VERBOSE MODE
for (j=0;j<num_pixel;j++)
{
printf("x_float[%i]: %f - x: %u\n",j,x_float[j],x[j]);
}
*/
y=0;
for (j=0;j<num_pixel;j++) // calcolo dell'uscita con i pesi correnti
{
y = y + (x_float[j] * w[j]);
}
y_tot=y;
y_diviso=(y/10000);
y=tanhf(y_diviso); // tangente iperbolica calcolata su un numero float
printf("i: %i - y totale: %f - y diviso: %f - y iperbolica: %f\n",i,y_tot,y_diviso,y);
for (j=0;j<num_pixel;j++)
{
delta_w = apprendimento * (t[i] -y) * x_float[j] * (1-y*y); // correzione con la differenza con
// il delta_w contiene tra i fattori la derivata della funzione di attivazione:
// per la tangente iperbolica corrisponde a (1-y*y). dato che la funzione però non
// è tang_iperb(x), ma tang_iperb(x/100000), si può scorporare un tang_iperb(1/100000),
// che è una costante, e quindi può essere mantenuta fattore costante. siccome è
// è un fattore costante, può essere considerato incorporato nel tasso di apprendimento.
w[j] = delta_w + w[j];
//if (i>=20) {printf("i: %i - w%i: %f - delta_w: %f - x_float: %f - y: %f - t: %f\n",i,j,w[j],delta_w,x_float[j],y,t[i]);}
}
}
/* VERIFICA DEI VALORI DEI PESI SINAPTICI CALCOLATI
printf("PESI SINAPTICI\n");
for (j=0;j<num_pixel;j++)
{
printf("w%i: %f\n",j,w[j]);
}
*/
/* SALVATAGGIO SU PESI2.BMP DEI PESI CON NUMERI INTERI
for (j=0;j<num_pixel;j++)
{
w_int[j]=(int)w[j];
}
if (write_image_int(w_int,header_image,"pesi2.bmp")==1)
{
printf("errore di scrittura dell'immagine pesi2.bmp");
exit(1);
}
*/
// salvataggio dei pesi sinaptici calcolati:
if (write_image_float(w,header_image,filenamew)==1)
{
printf("errore di scrittura dell'immagine %s",filenamew);
exit(1);
}
printf("\nI pesi sinaptici sono stati salvati nel file %s\n\n",filenamew);
}
NEURALNET.H
#define num_pixel 76032
#define image_widht 352
int open_image_int(unsigned int array[288][image_widht], unsigned char header[54], char filename[20]);
int open_image_int_mono(unsigned int array[num_pixel], unsigned char header[54], char filename[20]);
int open_image_float(float array[num_pixel], unsigned char header[54], char filename[20]);
int write_image_int(unsigned int array[num_pixel], unsigned char header[54], char filename[20]);
int write_image_float(float array[num_pixel], unsigned char header[54], char filename[20]);
int normalize(float array[num_pixel]);
int open_image_int(unsigned int array[288][image_widht], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[288][image_widht];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array[i][j]=array_f[i][j];
}
}
}
return 0;
}
int open_image_int_mono(unsigned int array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[num_pixel];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
}
return 0;
}
int open_image_float(float array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
float array_f[num_pixel];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
}
return 0;
}
int write_image_float(float array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
float array_f[num_pixel], *array_punt;
if ((fp=fopen(filename,"wb"))==NULL)
{
return 1;
}
else
{
for (i=0;i<54;i++)
{
header_f[i]=header[i];
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
/*
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array_f[i][j]=array[i][j];
}
}
*/
fwrite(&header_f,54,1,fp);
fwrite(&array_f,sizeof(array_f),1,fp);
fclose(fp);
}
return 0;
}
int write_image_int(unsigned int array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[num_pixel], *array_punt;
if ((fp=fopen(filename,"wb"))==NULL)
{
return 1;
}
else
{
for (i=0;i<54;i++)
{
header_f[i]=header[i];
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
/*
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array_f[i][j]=array[i][j];
}
}
*/
fwrite(&header_f,54,1,fp);
fwrite(&array_f,sizeof(array_f),1,fp);
fclose(fp);
}
return 0;
}
int normalize(float array[num_pixel])
{
int i;
float max=0;
float array_f[num_pixel];
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
for (i=0;i<num_pixel;i++)
{
if (array_f[i]>max) { max=array_f[i]; }
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=(array_f[i]/max)*(array_f[i]/max);
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
return 0;
}
DOWNLOAD
Scarica i sorgenti
Scarica i compilati
Cosa sono le reti neurali e perché sono tanto interessanti? Perché sono una trovata matematica grazie alla quale si rovescia la normale logica di programmazione e si può costruire una macchina che impara da sè. Per esempio, le reti neurali sono perfette per riconoscere i volti delle persone. Ecco un programma che, dati due volti, riconosce se abbiamo di fronte l'uno o l'altro. Ecco i due volti sui quali lavoreremo.
PROGRAMMAZIONE CLASSICA
Anche con un programma classico, a impostazione imperativa, si può fare riconocimento dei volti. Basta individuare le caratteristiche distintive delle due immagini. Il primo dei due ragazzi ha capelli più lunghi e più neri, sopracciglia più marcate, naso più lungo. Se prendiamo la matrice di pixel che compone l'immagine possiamo confrontare porzioni equivalenti e stabilire se appartengono all'uno o all'altro ragazzo in modo relativamente semplice. Qual è il problema? Che il programma funzionerà solo con queste due facce, non sarà per niente flessibile. Lo si può fare allora in modo decisamente più complesso, in modo che, dati due volti generici, il programma ne analizzi le porzioni equivalenti e memorizzi le differenze. Così sarà più flessibile, sarà in grado di esaminare qualsiasi faccia, ma il lavoro per scriverlo sarà molto complesso. Questa è programmazione con una impostazione imperativa classica. La programmazione con le reti neurali invece è radicalmente diversa. Di fatto noi non abbiamo bisogno di individuare le caratteristiche distintive tra le due immagini. E' la rete che lo fa.
E come lo fa? Correggendo la propria configurazione interna, in funzione degli esempi che le mettiamo davanti. Le diamo in pasto tante immagini del primo ragazzo, dicendole che è il primo ragazzo, e poi le diamo in pasto tante immagini del secondo, dicendole che è il secondo. Con un processo di correzione progressiva, che è quello viene definito apprendimento, la rete arriva a confingurarsi in modo tale da essere in grado di distingere i due volti.
COS'È UNA RETE NEURALE
Andiamo per ordine. Cos'è una rete? In effetti è ciò che il nome suggerisce: un insieme di nodi collegati tra loro. La caratteristica fondamentale di una rete neurale è quella di avere alcuni nodi di ingresso, alcuni nodi di uscita ed eventualmente alcuni nodi intermedi. Può esserci più di uno strato di nodi intermedi a seconda della complessità della rete.
Questa è una rete semplice, senza nodi intermedi:
Partendo dal generale per andare via via nel dettaglio e nel particolare, l'idea di base è questa: c'è uno scatolotto con alcuni ingressi e una uscita. Nel nostro caso gli ingressi rappresentano i valori dei pixel che compongono l'immagine (i volti sono dentro una bitmap di risoluzione 352x288, quindi abbiamo 101.376 ingressi, uno per pixel) mentre l'uscita sarà una sola e risulterà pari a un numero che identifica il volto: -1 per il primo, 1 per il secondo.
Nella prima fase, quella di apprendimento, forniamo alla rete sia l'ingresso che l'uscita. Le diamo tanti esempi del primo volto, a cui associamo l'uscita -1 e tanti esempi del secondo, dicendo che con quest'altro deve restituire invece +1. Esaurita la fase dell'apprendimento, lo scatolotto dovrebbe essere in grado, data una nuova foto che non aveva mai visto, di restituire l'uscita corretta.
Come lo fa? Generalizzando. Quello che succede è che la rete impara a distinguere le differenze significative da quelle non significative.
In pratica la cosa funziona così: ogni neurone di ingresso è collegato all'unico neurone di uscita. Il valore in arrivo al neurone di uscita sarà perciò la somma dei valori dei neuroni ingresso, vale a dire dei codici colore dei pixel dell'immagine, ognuno moltiplicato per un coefficiente.
Nella figura sono solo due, nel nostro caso sono 101.376, che vanno da x1 a x101376. Ciascun xi è moltiplicato per il rispettivo wi, cioè il relativo coefficiente. Il valore in ingresso al neurone di output è dato perciò da:
L'idea è che i pixel importanti, quelli che permettono di distinguere una immagine dall'altra (per esempio dove ci sono i capelli, che hanno colore diverso tra i due ragazzi), avranno coefficienti alti. Quelli meno importanti (per esempio quelli dello sfondo, la parete azzurrina), che non hanno alcuna importanza nell'individuare di quale dei due volti si tratti, avranno coefficienti molto bassi, prossimi allo zero. In questo modo si ottiene un valore che dà rilievo alle differenze tra le due immagini. Questi sono i pesi sinaptici. Il risultato, cioè a sommatoria complessiva dei valori dei pixel per i rispettivi pesi, poi, a sua volta passa attraverso una funzione di attivazione. Una funzione che si presta molto bene è la tangente iperbolica, che ha questo tipo di risposta:
Perché utilizziamo una funzione di attivazione, in particolare questa? Perché garantisce una uscita sempre compresa tra -1 e +1, qualsiasi sia il dato di partenza. Nel nostro caso, poiché la sommatoria di oltre 100mila ingressi, per quanto i coefficienti possano essere bassi, porta a un valore piuttosto alto, ho diviso - trovando empiricamente il valore vedendo un po' cosa usciva dal programma - il valore per 10.000. Il valore così trovato va in pasto alla tangente iperbolica che restituisce un valore prossimo a -1 o +1 e viene approssimato a quello più vicino.
L'APPRENDIMENTO
Una rete così realizzata è in grado di riconoscere un volto dall'altro se configurata con i coefficienti giusti. E il calcolo dei coefficienti giusti è quello che si definisce apprendimento. Come si calcolano i coefficienti giusti? Per correzioni progressive. All'inizio dell'apprendimento la rete calcola una uscita, in funzione degli ingressi che ha (al primo giro tutti i valori sinaptici valgono zero). Poi corregge i coefficienti in funzione della differenza tra l'uscita calcolata e quella teorica (cioè che noi vorremmo che restituisse, quella che noi abbiamo associato a quella determinata immagine: nel caso del primo volto -1, nel caso del secondo +1).
Dato:
t = risposta teorica
y = risposta calcolata dalla rete con gli attuali pesi sinaptici
xi = valore di ingresso (da 0 a 83.839)
La differenza è calcolata così:
differenza = (t - y) xi (1 - y*y)
(1 - y*y) rappresenta la derivata (y*y vuol dire "y al quadrato") della funzione di attivazione del neurone di uscita, cioè della tangente iperbolica.
Una volta calcolato la differenza, si correggono i pesi sinaptici così:
wi = wi + differenza
Per evitare di andare fuori strada correggendo il differenziale troppo a ogni nuova immagine sottoposta, si applica anche un ulteriore coefficiente, detto tasso di apprendimento, che serve a limitare la velocità del cambiamento. Il tasso di apprendimento è compreso tra 0 e 1 e in genere viene fissato intorno a 0,8. Se a è il tasso di apprendimento, il calcolo della differenza diventa allora:
differenza = a (t - y) xi (1 - y*y)
La cosa migliore è dare in pasto alla rete le immagini in modo incrociato. Cioè prima un esempio del primo volto, poi un esempio del secondo, poi di nuovo uno del primo e così via. In questo modo l'apprendimento è più efficiente, perché a ogni passaggio emergono con più evidenza le differenze tra una e l'altra.
Una volta esaurito il compito, quello che abbiamo è di fatto una matrice di coefficienti, che possiamo salvare, per comodità, ancora in una bitmap, che si presta perfettamente allo scopo, essendo come formato una matrice di valori. Facendo così, poi, è possibile visualizzare l'immagine che ne viene fuori e il risultato è impressionante:
Pesi calcolati con numeri float
Non solo. Ma c'è anche un'altra cosa interessante. Questa è l'immagine che viene fuori se i pesi sono numeri float, cioè numeri con la virgola. Ma se utilizziamo come coefficienti dei numeri interi, allora l'immagine, pur molto simile, perde alcuni dettagli.
Pesi calcolati con numeri interi
DEBOLEZZE DI QUESTA RETE NEURALE
La principale debolezza di questa rete neurale è il fatto che, avendo un solo strato, assegna di fatto un coefficiente a ogni pixel. Quindi le due facce devono trovarsi grosso modo sempre nella stessa posizione. Se in una immagine il volto invece di trovarsi al centro, si trova a destra o a sinistra, la rete perde la bussola. Per renderla in grado di individuare e riconoscere le somiglianze tra le facce anche se l'immagine si sposta, occorrerebbe uno strato di neuroni intermedio. In questo modo il primo strato di connessioni sinaptiche individuerebbero la posizione del volto e il secondo individuerebbe le caratteristiche del volto. L'apprendimento di una rete con uno strato intermedio è però molto diverso da una rete semplice come questa e lo vedremo in futuro.
IL PROGRAMMA
Questi di seguito c'è il programma in C per fare tutto questo.
Il primo file, neuralnet.c, è il programma che, prendendo una immagine, dice se si tratta del primo o del secondo volto.
Il secondo file, training.c, rappresenta il programma di apprendimento della rete e calcola i pesi sinaptici (attenzione: ci sono letteralmente milioni di moltiplicazioni da fare, il processore ci mette del tempo, potrebbe volerci anche qualche ora a completare il processo di apprendimento).
Il terzo file, neuralnet.h, è un file libreria e contiene tutte le funzioni utilizzate nei primi due file.
Se avete una distribuzione Linux i file si compilano così:
gcc neuralnet.c -o neuralnet -lm
gcc training.c -o training -lm
gcc training.c -o training -lm
Avete ora così a disposizione i due programmi. Il training si avvia così:
./training
Il programma impara da 40 immagini bitmap di risoluzione 352x288. Le immagini devono essere nella stessa cartella del programma, numerate, e chiamarsi da Image000.bmp a Image039.bmp (con la i maiuscola). Quelle pari, (0, 2, 4, ecc.) devono essere della prima faccia, quelle dispari della seconda. Il programma genera un file che si chiama pesi.bmp nel quale sono registrati tutti i pesi sinaptici.
Esaurito l'apprendimento, si può utilizzare la rete, con il seguente comando:
./neuralnet
L'immagine da verificare deve essere una bitmap di risoluzione 352x288 che si chiama image2test.bmp ed è sempre nella stessa cartella.
CODICE SORGENTE IN C
NEURALNET.C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "./neuralnet.h"
#define num_pixel 76032
#define image_widht 352
int main(void)
{
unsigned int x[num_pixel]; // array dei neuroni dell'immagine di input
float w[num_pixel],x_float[num_pixel]; // array dei pesi sinaptici
unsigned char header_image[54];
char filename_immagine[20]="image2test.bmp"; // nome del file dell'immagine da testare
char filename_pesi[20]="pesi.bmp"; // nome del file dell'immagine dei pesi
float out_neurone[num_pixel];
float out_neurone_uscita = 0;
int j;
if (open_image_int_mono(x,header_image,filename_immagine)==1)
{printf("errore di apertura dell'immagine %s",filename_immagine);
exit (1);}
for (j=0;j<num_pixel;j++)
{
x_float[j] = x[j];
}
if (normalize(x_float)==1)
{printf("errore di normalizzazione dell'immagine %s",filename_immagine);
exit (1);}
if (open_image_float(w,header_image,filename_pesi)==1) // legge l'array dei pesi
{printf("errore di apertura dell'immagine %s",filename_pesi);
exit (1);}
/*
// fine della fase di apprendimento
// uso della rete
printf("\nTEST DELLA RETE\n");
printf("la rete risponde con -1 per un'immagine simile a una delle prime 50 immagini di training (0-49)\n");
printf("la rete risponde con +1 per un'immagine simile a una delle seconde 50 immagini di training (50-99)\n");
printf("(ATTENZIONE: i neuroni sono bipolari quindi i valori sono 1 e -1, non i soliti 1 e 0.)\n");
*/
for (j=0;j<num_pixel;j++)
{
out_neurone[j] = x_float[j] * w[j];
out_neurone_uscita = out_neurone_uscita + out_neurone[j];
}
out_neurone_uscita = out_neurone_uscita /100000;
printf("out_neurone_uscita non elaborata: %f\n",out_neurone_uscita);
out_neurone_uscita=tanhf(out_neurone_uscita);
printf("out_neurone_uscita con la tang iperbolica: %f\n",out_neurone_uscita);
/*
attenzione: se il neurone è bipolare come in questo caso e non continuo allora Ú necessario interpretare il valore raggiunto e verificare se supera la soglia, in modo da determinare se vale 1 oppure -1. un neurone bipolare non può avere un'uscita come 3 oppure -3 ma solo 1 e -1.
il comportamento di questi neuroni è:
y = 1 se { sommatoria di tutti i [valore di input della sinapsi per il relativo peso] > 0 }
y = 1 se { sommatoria di tutti i [valore di input della sinapsi per il relativo peso] <= 0 }
(è riportato a pagina 68 del manuale)
*/
if (out_neurone_uscita < 0) printf ("\nvolto 1\n");
if (out_neurone_uscita > 0) printf ("\nvolto 2\n");
//printf("\n y: %f\n",out_neurone_uscita);
}
TRAINING.C
#include <stdio.h>
#include <stdlib.h>
//#include <string.h>
#include <math.h>
#include "./neuralnet.h"
#define num_pixel 101376
#define image_widht 352
void main(void)
{
unsigned int array_image[288][image_widht]; // array dei pixel dell'immagine
unsigned int x[num_pixel]; // vettore monodimensionale che raccoglie i valori dell'array dell'immagine
float x_float[num_pixel]; // vettore che trasforma i valori in float
//unsigned int *punt;
unsigned char header_image[54];
float w[num_pixel]; // vettore dei pesi
unsigned int w_int[num_pixel]; // vettore dei pesi convertiti in intero
//fase di apprendimento
float apprendimento;
char filename[100][20];
char filenamew[20]="pesi.bmp"; // nome del file per salvare i pesi sinaptici calcolati
float t[100];
//float x[num_pixel];
float y,y_tot,y_diviso;
float delta_w;
int i,j,k,p,n;
// nb: num_pixel è il risultato di 288 x image_widht.
apprendimento=0.9;// tasso di apprendimento
strcpy(filename[0],"Image000.bmp"); t[0]=-1;
strcpy(filename[1],"Image001.bmp"); t[1]=1;
strcpy(filename[2],"Image002.bmp"); t[2]=-1;
strcpy(filename[3],"Image003.bmp"); t[3]=1;
strcpy(filename[4],"Image004.bmp"); t[4]=-1;
strcpy(filename[5],"Image005.bmp"); t[5]=1;
strcpy(filename[6],"Image006.bmp"); t[6]=-1;
strcpy(filename[7],"Image007.bmp"); t[7]=1;
strcpy(filename[8],"Image008.bmp"); t[8]=-1;
strcpy(filename[9],"Image009.bmp"); t[9]=1;
strcpy(filename[10],"Image010.bmp"); t[10]=-1;
strcpy(filename[11],"Image011.bmp"); t[11]=1;
strcpy(filename[12],"Image012.bmp"); t[12]=-1;
strcpy(filename[13],"Image013.bmp"); t[13]=1;
strcpy(filename[14],"Image014.bmp"); t[14]=-1;
strcpy(filename[15],"Image015.bmp"); t[15]=1;
strcpy(filename[16],"Image016.bmp"); t[16]=-1;
strcpy(filename[17],"Image017.bmp"); t[17]=1;
strcpy(filename[18],"Image018.bmp"); t[18]=-1;
strcpy(filename[19],"Image019.bmp"); t[19]=1;
strcpy(filename[20],"Image020.bmp"); t[20]=-1;
strcpy(filename[21],"Image021.bmp"); t[21]=1;
strcpy(filename[22],"Image022.bmp"); t[22]=-1;
strcpy(filename[23],"Image023.bmp"); t[23]=1;
strcpy(filename[24],"Image024.bmp"); t[24]=-1;
strcpy(filename[25],"Image025.bmp"); t[25]=1;
strcpy(filename[26],"Image026.bmp"); t[26]=-1;
strcpy(filename[27],"Image027.bmp"); t[27]=1;
strcpy(filename[28],"Image028.bmp"); t[28]=-1;
strcpy(filename[29],"Image029.bmp"); t[29]=1;
strcpy(filename[30],"Image030.bmp"); t[30]=-1;
strcpy(filename[31],"Image031.bmp"); t[31]=1;
strcpy(filename[32],"Image032.bmp"); t[32]=-1;
strcpy(filename[33],"Image033.bmp"); t[33]=1;
strcpy(filename[34],"Image034.bmp"); t[34]=-1;
strcpy(filename[35],"Image035.bmp"); t[35]=1;
strcpy(filename[36],"Image036.bmp"); t[36]=-1;
strcpy(filename[37],"Image037.bmp"); t[37]=1;
strcpy(filename[38],"Image038.bmp"); t[38]=-1;
strcpy(filename[39],"Image039.bmp"); t[39]=1;
for (i=0;i<num_pixel;i++) // inizializzazione dei pesi sinaptici, uno per ogni neurone di ingresso
{
w[i] = 0;
}
/* QUESTO È STATO COMMENTATO PERCHÉ CONVENIVA INCROCIARE LE IMMAGINI
for (i=0;i<20;i++) // impostazione dell'uscita desiderata: -1 per le immagini da 0 a 19.
{
t[i] = -1;
}
for (i=20;i<40;i++) // impostazione dell'uscita desiderata: 1 per le immagini da 20 a 39.
{
t[i] = 1;
}
*/
for (i=0;i<40;i++)
{
if (open_image_int_mono(x,header_image,filename[i])==1)
{printf("errore di apertura dell'immagine %s",filename[i]);
exit (1);}
for (j=0;j<num_pixel;j++)
{
x_float[j] = x[j];
}
if (normalize(x_float)==1)
{printf("errore di normalizzazione dell'immagine %s",filename[i]);
exit (1);}
/* VERBOSE MODE
for (j=0;j<num_pixel;j++)
{
printf("x_float[%i]: %f - x: %u\n",j,x_float[j],x[j]);
}
*/
y=0;
for (j=0;j<num_pixel;j++) // calcolo dell'uscita con i pesi correnti
{
y = y + (x_float[j] * w[j]);
}
y_tot=y;
y_diviso=(y/10000);
y=tanhf(y_diviso); // tangente iperbolica calcolata su un numero float
printf("i: %i - y totale: %f - y diviso: %f - y iperbolica: %f\n",i,y_tot,y_diviso,y);
for (j=0;j<num_pixel;j++)
{
delta_w = apprendimento * (t[i] -y) * x_float[j] * (1-y*y); // correzione con la differenza con
// il delta_w contiene tra i fattori la derivata della funzione di attivazione:
// per la tangente iperbolica corrisponde a (1-y*y). dato che la funzione però non
// è tang_iperb(x), ma tang_iperb(x/100000), si può scorporare un tang_iperb(1/100000),
// che è una costante, e quindi può essere mantenuta fattore costante. siccome è
// è un fattore costante, può essere considerato incorporato nel tasso di apprendimento.
w[j] = delta_w + w[j];
//if (i>=20) {printf("i: %i - w%i: %f - delta_w: %f - x_float: %f - y: %f - t: %f\n",i,j,w[j],delta_w,x_float[j],y,t[i]);}
}
}
/* VERIFICA DEI VALORI DEI PESI SINAPTICI CALCOLATI
printf("PESI SINAPTICI\n");
for (j=0;j<num_pixel;j++)
{
printf("w%i: %f\n",j,w[j]);
}
*/
/* SALVATAGGIO SU PESI2.BMP DEI PESI CON NUMERI INTERI
for (j=0;j<num_pixel;j++)
{
w_int[j]=(int)w[j];
}
if (write_image_int(w_int,header_image,"pesi2.bmp")==1)
{
printf("errore di scrittura dell'immagine pesi2.bmp");
exit(1);
}
*/
// salvataggio dei pesi sinaptici calcolati:
if (write_image_float(w,header_image,filenamew)==1)
{
printf("errore di scrittura dell'immagine %s",filenamew);
exit(1);
}
printf("\nI pesi sinaptici sono stati salvati nel file %s\n\n",filenamew);
}
NEURALNET.H
#define num_pixel 76032
#define image_widht 352
int open_image_int(unsigned int array[288][image_widht], unsigned char header[54], char filename[20]);
int open_image_int_mono(unsigned int array[num_pixel], unsigned char header[54], char filename[20]);
int open_image_float(float array[num_pixel], unsigned char header[54], char filename[20]);
int write_image_int(unsigned int array[num_pixel], unsigned char header[54], char filename[20]);
int write_image_float(float array[num_pixel], unsigned char header[54], char filename[20]);
int normalize(float array[num_pixel]);
int open_image_int(unsigned int array[288][image_widht], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[288][image_widht];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array[i][j]=array_f[i][j];
}
}
}
return 0;
}
int open_image_int_mono(unsigned int array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[num_pixel];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
}
return 0;
}
int open_image_float(float array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
float array_f[num_pixel];
if ((fp=fopen(filename,"rb"))==NULL)
{
return 1;
}
else
{
fread(&header_f,sizeof(header_f),1,fp);
fread(&array_f,sizeof(array_f),1,fp);
fclose(fp);
for (i=0;i<54;i++)
{
header[i]=header_f[i];
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
}
return 0;
}
int write_image_float(float array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
float array_f[num_pixel], *array_punt;
if ((fp=fopen(filename,"wb"))==NULL)
{
return 1;
}
else
{
for (i=0;i<54;i++)
{
header_f[i]=header[i];
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
/*
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array_f[i][j]=array[i][j];
}
}
*/
fwrite(&header_f,54,1,fp);
fwrite(&array_f,sizeof(array_f),1,fp);
fclose(fp);
}
return 0;
}
int write_image_int(unsigned int array[num_pixel], unsigned char header[54], char filename[20])
{
FILE *fp;
int i,j;
unsigned char header_f[54];
unsigned int array_f[num_pixel], *array_punt;
if ((fp=fopen(filename,"wb"))==NULL)
{
return 1;
}
else
{
for (i=0;i<54;i++)
{
header_f[i]=header[i];
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
/*
for (i=0;i<288;i++)
{
for (j=0;j<image_widht;j++)
{
array_f[i][j]=array[i][j];
}
}
*/
fwrite(&header_f,54,1,fp);
fwrite(&array_f,sizeof(array_f),1,fp);
fclose(fp);
}
return 0;
}
int normalize(float array[num_pixel])
{
int i;
float max=0;
float array_f[num_pixel];
for (i=0;i<num_pixel;i++)
{
array_f[i]=array[i];
}
for (i=0;i<num_pixel;i++)
{
if (array_f[i]>max) { max=array_f[i]; }
}
for (i=0;i<num_pixel;i++)
{
array_f[i]=(array_f[i]/max)*(array_f[i]/max);
}
for (i=0;i<num_pixel;i++)
{
array[i]=array_f[i];
}
return 0;
}
DOWNLOAD
Scarica i sorgenti
Scarica i compilati
domenica 25 novembre 2012
Robot alla conquista del potere? Per Cambridge è un rischio serio
Londra (Regno Unito), 25 nov. (LaPresse/AP) - Un centro per valutare il rischio che in futuro robot intelligenti possano conquistare il potere, assoggettando gli umani. Non è il soggetto di un film ma il progetto di una delle università di più note e prestigiose del mondo, quella di Cambridge. L'idea è quella di mettere insieme un centro interdisciplinare che raggruppi esperti e accademici di diversi settori, per valutare i rischi che la rapida evoluzione dell'intelligenza artificiale può creare, incluso quello di "minacciare la nostra stessa esistenza".
Anche se l'idea che i robot possano prendere il potere, spiega Huw Price, professore di filosofia di Cambridge, può apparire lontana, vale la pena di prendere il rischio seriamente. E' difficile al momento, ammette, predire l'esatta natura dei rischi ma proprio questo dimostra, sottolinea, che è necessario capirne di più.
"Nel caso dell'intelligenza artificiale - spiega Price - sembra una ragionevole previsione che in un certo punto del tempo in questo o nel prossimo secolo l'intelligenza sfuggirà ai limiti della biologia". Quando questo accadrà, "non saremo più gli essere più intelligenti" sulla terra e saremo alla mercé di "macchine che non sono maliziose, ma i cui interessi non includono noi". "In genere - aggiunge - tende a essere considerata una preoccupazione poco seria, ma i rischi potenziali sono troppo seri per spazzarla semplicemente via". L'università ha annunciato che il centro sarà aperto nel corso del 2013 e si chiamerà Center for the Study of Existential Risk.
La questione in effetti è più seria di quanto non sembri e da tempo chi si occupa di intelligenza artificiale l'ha individuata. Il primo fu, nel 1965, il matematico inglese Irvin John Good, che la spiegò così: "Definiamo una macchina ultraintelligente come una macchina che può superare di molto tutte le attività intellettuali di una persona, per quanto intelligente. Dal momento che la progettazione delle macchine è una di queste attività intellettuali, una macchina ultraintelligente potrebbe progettare macchine ancora migliori; ci sarebbe allora senza dubbio una 'esplosione di intelligenza', e l'intelligenza dell'uomo sarebbe lasciata molto indietro. Di conseguenza la prima macchina ultraintelligente è l'ultima invenzione che l'uomo avrà bisogno di fare".
Raymond Kurzweil, un visionario futurologo, nel 2005 ha dato alle stampe un libro intitolato 'La singolarità è vicina'. Esperto informatico, Kurzweil si occupa di riconoscimento ottico dei caratteri, sintesi vocale, riconoscimento vocale e altre cose di questo genere, ma è diventato noto per i suoi numerosi libri. La "singolarità", parola presa a prestito dalla fisica, nella quale indica un particolare punto nello spazio-tempo in cui non si applicano le normali leggi della fisica, rappresenta per Kurzweil il momento in cui l'uomo costruirà il primo computer più intelligente delle persone. Lui sostiene che questo potrebbe accadere nel giro di qualche decennio e non appena questo succederà la vita sulla terra subirà un drastico cambiamento.
Ipotesi visionarie senza fondamento? Forse. Ma il fondatore della Microsoft Bill Gates, che lo ha invitato a cena a casa sua più di una volta, dice che Kurzweil è "la persona migliore che conosca nel predire il futuro dell'intelligenza artificiale".
Vi ricordate I Robot? Tolte le esplosioni, le americanate e le seplificazioni, il problema potrebbe anche essere più o meno in questi termini.
Meno inquietante ma altrettanto interessante: L'uomo bicentenario. Forse un po' troppo un Pinocchio contemporaneo, ma centra diversi temi importanti. Costruito in forma di commedia, un bel film.
Anche se l'idea che i robot possano prendere il potere, spiega Huw Price, professore di filosofia di Cambridge, può apparire lontana, vale la pena di prendere il rischio seriamente. E' difficile al momento, ammette, predire l'esatta natura dei rischi ma proprio questo dimostra, sottolinea, che è necessario capirne di più.
"Nel caso dell'intelligenza artificiale - spiega Price - sembra una ragionevole previsione che in un certo punto del tempo in questo o nel prossimo secolo l'intelligenza sfuggirà ai limiti della biologia". Quando questo accadrà, "non saremo più gli essere più intelligenti" sulla terra e saremo alla mercé di "macchine che non sono maliziose, ma i cui interessi non includono noi". "In genere - aggiunge - tende a essere considerata una preoccupazione poco seria, ma i rischi potenziali sono troppo seri per spazzarla semplicemente via". L'università ha annunciato che il centro sarà aperto nel corso del 2013 e si chiamerà Center for the Study of Existential Risk.
La questione in effetti è più seria di quanto non sembri e da tempo chi si occupa di intelligenza artificiale l'ha individuata. Il primo fu, nel 1965, il matematico inglese Irvin John Good, che la spiegò così: "Definiamo una macchina ultraintelligente come una macchina che può superare di molto tutte le attività intellettuali di una persona, per quanto intelligente. Dal momento che la progettazione delle macchine è una di queste attività intellettuali, una macchina ultraintelligente potrebbe progettare macchine ancora migliori; ci sarebbe allora senza dubbio una 'esplosione di intelligenza', e l'intelligenza dell'uomo sarebbe lasciata molto indietro. Di conseguenza la prima macchina ultraintelligente è l'ultima invenzione che l'uomo avrà bisogno di fare".
Raymond Kurzweil, un visionario futurologo, nel 2005 ha dato alle stampe un libro intitolato 'La singolarità è vicina'. Esperto informatico, Kurzweil si occupa di riconoscimento ottico dei caratteri, sintesi vocale, riconoscimento vocale e altre cose di questo genere, ma è diventato noto per i suoi numerosi libri. La "singolarità", parola presa a prestito dalla fisica, nella quale indica un particolare punto nello spazio-tempo in cui non si applicano le normali leggi della fisica, rappresenta per Kurzweil il momento in cui l'uomo costruirà il primo computer più intelligente delle persone. Lui sostiene che questo potrebbe accadere nel giro di qualche decennio e non appena questo succederà la vita sulla terra subirà un drastico cambiamento.
Ipotesi visionarie senza fondamento? Forse. Ma il fondatore della Microsoft Bill Gates, che lo ha invitato a cena a casa sua più di una volta, dice che Kurzweil è "la persona migliore che conosca nel predire il futuro dell'intelligenza artificiale".
Vi ricordate I Robot? Tolte le esplosioni, le americanate e le seplificazioni, il problema potrebbe anche essere più o meno in questi termini.
Meno inquietante ma altrettanto interessante: L'uomo bicentenario. Forse un po' troppo un Pinocchio contemporaneo, ma centra diversi temi importanti. Costruito in forma di commedia, un bel film.
sabato 24 novembre 2012
Alan Turing: questo è solo un'ombra di quello che sarà
Alan Turing aveva già capito tutto. Aveva già risposto a tutte le domande fondamentali dell'intelligenza artificiale, o meglio aveva già dato tutte le risposte necessarie a indicare la direzione nella quale andare, il che vale ancora di più. Sto leggendo un libro meraviglioso su Turing, noto matematico inglese che fu il padre dell'informatica, oltre al principale artefice della decriptazione del codice che usavano i tedeschi durante la seconda guerra mondiale per trasmettere tra di loro ordini e risultati delle operazioni militari. Nonostante i suoi eccezionali meriti, fu perseguitato per la sua omosessualità e si suicidò a 41 anni, mangiando una mela avvelenata col cianuro di potassio, probabilmente prendendo spunto dalla favola di Biancaneve, la cui versione Disney del 1938 lo aveva colpito particolarmente. Il libro, "L'uomo che sapeva troppo", di David Leavitt, Codice Edizioni, (da cui sono tratte tutte le citazioni di seguito), ripercorre tutta la vita di Turing.
TRATTARE LE MACCHINE IN MODO EQUO: L'INTELLIGENZA NON È INFALLIBILE
Tra gli anni Trenta e Quaranta scriveva ciò che ancora oggi resta alla base dello sviluppo tecnologico del prossimo secolo. Parlando di un suo progetto, l'Ace (Automatic Computing Engine), che stava sviluppando presso il National Physics Laboratory di Teddington (un sobborgo di Londra) scrisse:
Io sostengo che la macchina deve essere trattata in modo equo e leale. Invece di avere una situazione in cui essa a volte non dà risposte, potremmo aggiustare le cose in modo che dia ogni tanto risposte sbagliate. Anche il matematico umano prende qualche cantonata quando sperimenta nuove tecniche. E' facile per noi considerare queste sviste come non rilevanti e dare al ricercatore un'altra possibilità, ma alla macchina non viene riservata alcuna pietà. In altre parole, se si aspetta che la macchina sia infallibile, allora essa non può anche essere intelligente.
Per proseguire nel mio appello per un fair play nei confronti delle macchine nel verificare il loro quoziente di intelligenza, voglio osservare che ogni matematico umano è sempre sottoposto a un addestramento prolungato e che il suo effetto può essere considerato non dissimile dall'inserzione di tavole di istruzioni dentro a una macchina. Non ci si deve perciò aspettare che essa faccia molto nella direzione di costruire tavole di istruzioni sue proprie. Nessun uomo aggiunge granché al corpo generale delle conoscenze umane: perché dovremmo aspettarci di più da una macchina?
LE SCOPERTE DI UN ALLIVEVO BRILLANTE NON APPARTENGONO AL MAESTRO
Le posizioni di Turing accesero la curiosità della stampa e anche le obiezioni dell'opinione pubblica, che si divise sull'idea che fosse possibile costruire macchine intelligenti. Turing rispose elencando le cinque obiezioni principali (che sono sorprendentemente le stesse a settant'anni di distanza), e rispose a una per una:
Una riluttanza ad ammettere la possibilità che il genere umano possa avere rivali nei poteri intellettuali; [...] un sentimento religioso, secondo il quale ogni tentativo di costruire macchine del genere sia una sorta di empietà prometeica; [...] il potere molto limitato delle macchine usate fino ai tempi recenti (possiamo dire fino al 1940) [...] [che ha] incoraggiato la convinzione che le macchine fossero necessariamente limitate a compiti estremamente diretti, forse persino solo a quelli ripetitivi; [...] la scoperta che esistono casi in cui qualunque macchina è incapace di fornire una risposta [mentre] l'intelligenza umana sembra capace di trovare metodi di potenza sempre crescente per trattare tali problemi "trascendendo" i processi disponibili alle macchine; [...] [l'idea che] se e nella misura in cui una macchina può esibire intelligenza, la sua prestazione dev'essere considerata nulla più che un riflesso dell'intelligenza del suo creatore.
Le prime due obiezioni, scrisse, "sono puramente emotive e non richiedono davvero una replica. Se si sente la necessità di una loro confutazione preventiva, c'è ben poco da dire, anche se l'effettiva costruzione delle macchine avrebbe probabilmente un certo peso". Alla terza obiezione rispose che le macchine "possono compiere, ammesso che non si verifichino guasti, numeri immensi di operazioni non ripetitive". Ed è proprio questo, sottolineò, quarta risposta, che rende le macchine non infallibili. Anzi, disse che l'infallibilità non è necessariamente un "un requisito per l'intelligenza".
Per quanto riguarda la quinta obiezione, "il merito per le scoperte di un allievo dovrebbe essere assegnato al maestro. In caso di successo, l'insegnante sarebbe lieto della riuscita dei suoi metodi di educazione, ma non pretenderebbe di attribuirsi i risultati, a meno che non li avesse comunicati proprio lui all'allievo".
L'INIZIATIVA
E centrò il punto fondamentale sul quale i ricercatori dell'intelligenza artificiale si dannano l'anima:
Perché la mente non addestrata del neonato diventi intelligente, dovrà acquisire sia disciplina che capacità di iniziativa. [...] La disciplina da sola non è certamente sufficiente a produrre intelligenza: è richiesta, in aggiunta, anche l'iniziativa (e questa frase dovrà servire da definizione del concetto). Il nostro compito è quello di scoprire la natura di questo "residuo" che si presenta nell'uomo e cercare di "copiarlo" nelle macchine.
Non solo, ma introduce un concetto fondamentale, quello della comunicazione con gli umani, senza la quale l'intelligenza può risultare incomprensibile: "Alla macchina deve essere permesso di avere contatti con gli esseri umani affiché possa adattarsi ai loro criteri".
LA POESIA
Gli rispose Geoffry Jefferson, direttore del dipartimento di neurochirurgia dell'università di Manchester e fautore della lobotomia frontale:
Fino a quando una macchina non potrà scrivere un sonetto, o comporre un concerto, sulla base di pensieri e di emozioni sentite come tali e non per una casuale combinazione di simboli, cioè finché non solo possa farlo, ma sappia di averlo fatto, noi non accetteremo mai che possa essere l'eguale del cervello. Non c'è meccanismo capace di provare (e non semplicemente segnalare artificialmente, il che sarebbe facile da ottenere) piacere per i successi conseguiti, dolore quando le sue valvole bruciano, gioia quando riceve dei complimenti, tristezza per gli errori commessi, l'incanto del sesso, la collera o la delusione perché non riesce ad avere ciò che desidera.
La risposta di Turing è spiazzante e geniale, perché sposta il terreno:
Io non credo che si possa porre il limite di sonetti, sebbene il paragone non sia molto corretto perché un sonetto scritto da una macchina sarebbe meglio apprezzato da un'altra macchina.
QUESTO E' SOLO UN'OMBRA DEL FUTURO
E poi racconta come vede il futuro, mettendo giù quella che ha il sapore di una profezia:
Questo è solo un assaggio di quello che verrà, soltanto un'ombra di quello che sarà. Dobbiamo acquisire un po' di esperienza con la macchina prima di scoprire veramente le sue capacità. Potrebbero volerci degli anni prima che ci applichiamo sulle nuove possibilità, ma non vedo perché [la macchina] non dovrebbe accedere a qualunque campo normalmente dominato dall'intelletto umano e alla fine competere a pari condizioni.
E questo è iCub, un progetto nato a Genova che sembra uscito direttamente dalla testa di Turing... E' una una piattaforma aperta, una delle più promettenti che esistano.
TRATTARE LE MACCHINE IN MODO EQUO: L'INTELLIGENZA NON È INFALLIBILE
Tra gli anni Trenta e Quaranta scriveva ciò che ancora oggi resta alla base dello sviluppo tecnologico del prossimo secolo. Parlando di un suo progetto, l'Ace (Automatic Computing Engine), che stava sviluppando presso il National Physics Laboratory di Teddington (un sobborgo di Londra) scrisse:
Io sostengo che la macchina deve essere trattata in modo equo e leale. Invece di avere una situazione in cui essa a volte non dà risposte, potremmo aggiustare le cose in modo che dia ogni tanto risposte sbagliate. Anche il matematico umano prende qualche cantonata quando sperimenta nuove tecniche. E' facile per noi considerare queste sviste come non rilevanti e dare al ricercatore un'altra possibilità, ma alla macchina non viene riservata alcuna pietà. In altre parole, se si aspetta che la macchina sia infallibile, allora essa non può anche essere intelligente.
Per proseguire nel mio appello per un fair play nei confronti delle macchine nel verificare il loro quoziente di intelligenza, voglio osservare che ogni matematico umano è sempre sottoposto a un addestramento prolungato e che il suo effetto può essere considerato non dissimile dall'inserzione di tavole di istruzioni dentro a una macchina. Non ci si deve perciò aspettare che essa faccia molto nella direzione di costruire tavole di istruzioni sue proprie. Nessun uomo aggiunge granché al corpo generale delle conoscenze umane: perché dovremmo aspettarci di più da una macchina?
LE SCOPERTE DI UN ALLIVEVO BRILLANTE NON APPARTENGONO AL MAESTRO
Le posizioni di Turing accesero la curiosità della stampa e anche le obiezioni dell'opinione pubblica, che si divise sull'idea che fosse possibile costruire macchine intelligenti. Turing rispose elencando le cinque obiezioni principali (che sono sorprendentemente le stesse a settant'anni di distanza), e rispose a una per una:
Una riluttanza ad ammettere la possibilità che il genere umano possa avere rivali nei poteri intellettuali; [...] un sentimento religioso, secondo il quale ogni tentativo di costruire macchine del genere sia una sorta di empietà prometeica; [...] il potere molto limitato delle macchine usate fino ai tempi recenti (possiamo dire fino al 1940) [...] [che ha] incoraggiato la convinzione che le macchine fossero necessariamente limitate a compiti estremamente diretti, forse persino solo a quelli ripetitivi; [...] la scoperta che esistono casi in cui qualunque macchina è incapace di fornire una risposta [mentre] l'intelligenza umana sembra capace di trovare metodi di potenza sempre crescente per trattare tali problemi "trascendendo" i processi disponibili alle macchine; [...] [l'idea che] se e nella misura in cui una macchina può esibire intelligenza, la sua prestazione dev'essere considerata nulla più che un riflesso dell'intelligenza del suo creatore.
Le prime due obiezioni, scrisse, "sono puramente emotive e non richiedono davvero una replica. Se si sente la necessità di una loro confutazione preventiva, c'è ben poco da dire, anche se l'effettiva costruzione delle macchine avrebbe probabilmente un certo peso". Alla terza obiezione rispose che le macchine "possono compiere, ammesso che non si verifichino guasti, numeri immensi di operazioni non ripetitive". Ed è proprio questo, sottolineò, quarta risposta, che rende le macchine non infallibili. Anzi, disse che l'infallibilità non è necessariamente un "un requisito per l'intelligenza".
Per quanto riguarda la quinta obiezione, "il merito per le scoperte di un allievo dovrebbe essere assegnato al maestro. In caso di successo, l'insegnante sarebbe lieto della riuscita dei suoi metodi di educazione, ma non pretenderebbe di attribuirsi i risultati, a meno che non li avesse comunicati proprio lui all'allievo".
L'INIZIATIVA
E centrò il punto fondamentale sul quale i ricercatori dell'intelligenza artificiale si dannano l'anima:
Perché la mente non addestrata del neonato diventi intelligente, dovrà acquisire sia disciplina che capacità di iniziativa. [...] La disciplina da sola non è certamente sufficiente a produrre intelligenza: è richiesta, in aggiunta, anche l'iniziativa (e questa frase dovrà servire da definizione del concetto). Il nostro compito è quello di scoprire la natura di questo "residuo" che si presenta nell'uomo e cercare di "copiarlo" nelle macchine.
Non solo, ma introduce un concetto fondamentale, quello della comunicazione con gli umani, senza la quale l'intelligenza può risultare incomprensibile: "Alla macchina deve essere permesso di avere contatti con gli esseri umani affiché possa adattarsi ai loro criteri".
LA POESIA
Gli rispose Geoffry Jefferson, direttore del dipartimento di neurochirurgia dell'università di Manchester e fautore della lobotomia frontale:
Fino a quando una macchina non potrà scrivere un sonetto, o comporre un concerto, sulla base di pensieri e di emozioni sentite come tali e non per una casuale combinazione di simboli, cioè finché non solo possa farlo, ma sappia di averlo fatto, noi non accetteremo mai che possa essere l'eguale del cervello. Non c'è meccanismo capace di provare (e non semplicemente segnalare artificialmente, il che sarebbe facile da ottenere) piacere per i successi conseguiti, dolore quando le sue valvole bruciano, gioia quando riceve dei complimenti, tristezza per gli errori commessi, l'incanto del sesso, la collera o la delusione perché non riesce ad avere ciò che desidera.
La risposta di Turing è spiazzante e geniale, perché sposta il terreno:
Io non credo che si possa porre il limite di sonetti, sebbene il paragone non sia molto corretto perché un sonetto scritto da una macchina sarebbe meglio apprezzato da un'altra macchina.
QUESTO E' SOLO UN'OMBRA DEL FUTURO
E poi racconta come vede il futuro, mettendo giù quella che ha il sapore di una profezia:
Questo è solo un assaggio di quello che verrà, soltanto un'ombra di quello che sarà. Dobbiamo acquisire un po' di esperienza con la macchina prima di scoprire veramente le sue capacità. Potrebbero volerci degli anni prima che ci applichiamo sulle nuove possibilità, ma non vedo perché [la macchina] non dovrebbe accedere a qualunque campo normalmente dominato dall'intelletto umano e alla fine competere a pari condizioni.
E questo è iCub, un progetto nato a Genova che sembra uscito direttamente dalla testa di Turing... E' una una piattaforma aperta, una delle più promettenti che esistano.
mercoledì 21 novembre 2012
"Tutti non c'è". Ovvero: la correlazione non basta, lingua è regole
Il linguaggio ovviamente si costruisce un po' per volta. Ma finora ho sempre pensato che prima si apprendessero le parole per assonanza e solo molto dopo si arrivasse all'apprendimento delle regole di grammatica. Mi spiego: credevo che imparare a dire correttamente "il cane mangia" e "i cani mangiano", declinando correttamente il verbo, si apprendesse con un meccanismo di correlazione per rinforzo, per cui sentire tante volte "cane" associato a "mangia" e "cani" a "mangiano" (e mai "cane" a "mangiano") portasse a un collegamento rinforzato tra le due parole che ingegneristicamente potrebbe essere un coefficiente alto tra i due elementi oppure, in termini di reti neurali, pattern più fortemente associati nello schema di risposta di una rete, il che è lo stesso.
Ma forse non è così. Mi figlia, da poco passati i due anni, sta imparando a declinare correttamente i verbi sulla base di un evidente ragionamento di tipo grammaticale. Per esempio dice "puliscio", oppure "salo", per indicare la prima persona singolare rispettivamente del verbo pulire e salire. Non ha mai sentito da nessuno dire alcuna delle due parole. Le ha costruite lei, evidentemente estendendo la regola da parole sentite dire e imparate, come "gioco" o "mangio". Insomma, non ha imparato solo singole parole. E non ha solo imparato ad associarle a "io". Ha imparato anche che in generale a "io" si associano verbi (parole che indicano azioni) che finiscono con la lettera "o". E questa è una regola generale, non è una semplice correlazione tra due parole. Indica una correlazione tra classi e non tra singole parole, l'introduzione di un suono, di una variazione fonetica, che viene associato trasversalmente alle singole parole in funzione di un'altra parte della frase.
La conferma arriva da altri elementi. Qualche mese fa diceva frasi tipo "Tutti non c'è", per indicare che non c'era nessuno. Non aveva mai sentito nessuno dire una frase genere, in italiano chiunque dice "non c'è nessuno". Di nuovo era una sua costruzione. "Tutti" indica l'insieme composto da mamma, papà, cane, nonni e zia. "Tutti non c'è" voleva dire letteralmente - e il ragionamento è perfettamente corretto - che tutti gli elementi di questo insieme non ci sono. Solo più tardi ha imparato a dire, come dice ora, "non c'è nessuno". Il ragionamento ha preceduto l'apprendimento per correlazione, cioè la costruzione della frase per imitazione.
Allo stesso modo, diceva "non c'è ancora", per dire che una cosa non c'era più. Per esempio "non c'è ancora nonna", per dire che la nonna era andata via (non per dire che doveva ancora arrivare). Il ragionamento, di nuovo: a tavola "ancora pasta" vuol dire "datemi ancora pasta, ne voglio ancora". La negazione è "non voglio ancora pasta" (oppure "c'è ancora pasta" si nega con "non c'è ancora pasta"). Quindi la negazione di "c'è ancora la nonna" è "non c'è ancora". "Non c'è più" è una frase che ha imparato in seguito, e rappresenta in effetti, a rigore logico, un'eccezione al senso della parola. Non a caso in inglese si dice "some more" oppure "no more" per dire "ancora" e "basta, non più". Gli anglosassoni usano la stessa parola ed è più logico così.
Dal momento che mia figlia non ha mai sentito dire a nessuno "non c'è ancora" qualcosa per indicare che quella cosa "non c'è più", vuol dire che è stata lei a costruire la frase, sulla base di una costruzione linguistica che va dal particolare al generale, cioè con l'individuazione di regole.
In pratica questo cosa ci dice? Ci dice che non possiamo realizzare dei programmi in grado di parlare correttamente una lingua umana affidandoci soltanto o prevalentemente a una intelaiatura fatta di correlazioni rinforzate o indebolite tra le parole. Semplicemente non è così che funziona il cervello e di conseguenza il nostro programma non sarà in grado di emularne le capacità linguistiche. Il che, corollario, significa che i traduttori automatici da una lingua all'altra non saranno mai sufficientemente accurati finché si baseranno solo su correttivi basati su reti di correlazioni fonetiche. Occorrerà introdurre necessariamente anche delle capacità di apprendimento linguistico vero. Il sistema deve essere grado di andare dal particolare al generale e di individuare delle regole.
Ma forse non è così. Mi figlia, da poco passati i due anni, sta imparando a declinare correttamente i verbi sulla base di un evidente ragionamento di tipo grammaticale. Per esempio dice "puliscio", oppure "salo", per indicare la prima persona singolare rispettivamente del verbo pulire e salire. Non ha mai sentito da nessuno dire alcuna delle due parole. Le ha costruite lei, evidentemente estendendo la regola da parole sentite dire e imparate, come "gioco" o "mangio". Insomma, non ha imparato solo singole parole. E non ha solo imparato ad associarle a "io". Ha imparato anche che in generale a "io" si associano verbi (parole che indicano azioni) che finiscono con la lettera "o". E questa è una regola generale, non è una semplice correlazione tra due parole. Indica una correlazione tra classi e non tra singole parole, l'introduzione di un suono, di una variazione fonetica, che viene associato trasversalmente alle singole parole in funzione di un'altra parte della frase.
La conferma arriva da altri elementi. Qualche mese fa diceva frasi tipo "Tutti non c'è", per indicare che non c'era nessuno. Non aveva mai sentito nessuno dire una frase genere, in italiano chiunque dice "non c'è nessuno". Di nuovo era una sua costruzione. "Tutti" indica l'insieme composto da mamma, papà, cane, nonni e zia. "Tutti non c'è" voleva dire letteralmente - e il ragionamento è perfettamente corretto - che tutti gli elementi di questo insieme non ci sono. Solo più tardi ha imparato a dire, come dice ora, "non c'è nessuno". Il ragionamento ha preceduto l'apprendimento per correlazione, cioè la costruzione della frase per imitazione.
Allo stesso modo, diceva "non c'è ancora", per dire che una cosa non c'era più. Per esempio "non c'è ancora nonna", per dire che la nonna era andata via (non per dire che doveva ancora arrivare). Il ragionamento, di nuovo: a tavola "ancora pasta" vuol dire "datemi ancora pasta, ne voglio ancora". La negazione è "non voglio ancora pasta" (oppure "c'è ancora pasta" si nega con "non c'è ancora pasta"). Quindi la negazione di "c'è ancora la nonna" è "non c'è ancora". "Non c'è più" è una frase che ha imparato in seguito, e rappresenta in effetti, a rigore logico, un'eccezione al senso della parola. Non a caso in inglese si dice "some more" oppure "no more" per dire "ancora" e "basta, non più". Gli anglosassoni usano la stessa parola ed è più logico così.
Dal momento che mia figlia non ha mai sentito dire a nessuno "non c'è ancora" qualcosa per indicare che quella cosa "non c'è più", vuol dire che è stata lei a costruire la frase, sulla base di una costruzione linguistica che va dal particolare al generale, cioè con l'individuazione di regole.
In pratica questo cosa ci dice? Ci dice che non possiamo realizzare dei programmi in grado di parlare correttamente una lingua umana affidandoci soltanto o prevalentemente a una intelaiatura fatta di correlazioni rinforzate o indebolite tra le parole. Semplicemente non è così che funziona il cervello e di conseguenza il nostro programma non sarà in grado di emularne le capacità linguistiche. Il che, corollario, significa che i traduttori automatici da una lingua all'altra non saranno mai sufficientemente accurati finché si baseranno solo su correttivi basati su reti di correlazioni fonetiche. Occorrerà introdurre necessariamente anche delle capacità di apprendimento linguistico vero. Il sistema deve essere grado di andare dal particolare al generale e di individuare delle regole.
Iscriviti a:
Post (Atom)






