jQueryTilesGallery

mercoledì 10 marzo 2010

Natural Language Processing with Python

E' arrivato! :-)



Improving Bayesian Opinion Mining

Mi ha molto interessato questo articolo di Ian Barber in cui si mostra come applicare l'analisi bayesiana su una raccolta di "opinioni" per istruire un programma che sia successivamente in grado di "dire" se un'espressione come:
"Io ed Annie" è un film molto divertente, a volte c'è un calo di tensione ma tutto sommato è sicuramente uno dei migliori Allen in circolazione
sia un'opinione positiva o negativa riguardo ad un film. Detto in modo un poco più formale, abbiamo uno spazio campionario S={Positivo, Negativo} che rappresenta i possibili valori dei giudizi sui film e vogliamo calcolare la probabilità condizionata P(X|Y) dove X è il giudizio ("positivo" o "negativo") e Y l'opinione. L'inferenza bayesiana viene applicata considerando un database di opinioni già classificate positive o negative, stiamo parlando quindi di un modello supervisionato.

L'idea di fondo, in buona sostanza, consiste nell'estrazione di ogni singola parola dell'opinione che vogliamo analizzare, e confrontarle con le parole contenute nelle opinioni classificate. Ad esempio, per tornare all'opinione "'Io ed Annie' è un film molto divertente ecc..." riscontreremo, molto probabilmente, che la parola divertente compare molto spesso nelle opinioni classificate come "positive" e questo fatto concorrerà a spostare l'ago della bilancia su "positivo". Ma cosa significa "spostare l'ago della bilancia" ?

Dal teorema di Bayes sappiamo che:
P(X|Y) P(Y) = P(Y|X) P(X)

(ricordo che X per noi rappresenta il giudizio e Y l'opinione) a noi interessa trovare P(X|Y) per cui lo ricaviamo in questo modo:
P(X|Y) = P(Y|X) P(X) / P(Y)

quindi => P(giudizio|opinione) = P(opinione|giudizio) P(giudizio) / P(opinione)

Ian Barber preferisce eliminare la divisione per P(Y) in quanto in effetti a noi non interessa ottenere un risultato in termini statistici ma piuttosto un valore che sia un semplice indicatore, il famoso "ago della bilancia", quindi in definitiva utilizzeremo questa formula:
P(giudizio|opinione) = P(opinione|giudizio) P(giudizio)

Per quanto riguarda il codice PHP potete verderlo sulla pagina di Ian che vi ho linkato. Effettuando un training su una base di 5000 opinioni positive e 5000 opinioni negative il codice di Ian produce dei risultati con un'accuratezza di 0.83 (calcolato su 5000 opinioni) che è certamente un buon risultato, ma si può fare meglio?

Migliorare i risultati (?)

Ci sono almeno due fattori che Ian tralascia consapevolmente nel suo algoritmo:

i rapporti fra le parole all'interno di una stessa opinione: come lui stesso dichiara: «The thing that makes this a "naive" Bayesian process is that we make a big assumption about how we can calculate at the probability of the document occurring: that it is equal to the product of the probabilities of each word within it occurring. This implies that there is no link between one word and another word. This independence assumption is clearly not true: there are lots of words which occur together more frequently that either do individually, or with other words, but this convenient fiction massively simplifies things for us, and makes it straightforward to build a classifier.»;

vengono classificate anche parole senza contenuto lessicale: quindi, oltre a nomi, verbi, aggettivi e avverbi vengono considerati anche articoli, preposizioni e pronomi.

Riguardo al primo punto si tratta certamente di una questione molto interessante che sicuramente approfondirò in futuro. Invece sul secondo punto è abbastanza semplice e veloce implementare una modifica al codice di Ian per escludere dai calcoli le parole appartenenti alle categorie grammaticali chiuse e che non hanno valore lessicale. Alla classe Opinion di Ian aggiungiamo quindi un array contenente le parole da escludere:
private $exclude = array(
'conjuctions' => array(
'and', 'now', 'but', 'still', 'so', 'only', 'therefore', 'moreover', 'besides', 'consequently',
'nevertheless', 'for', 'however', 'hence', 'either', 'or', 'neither', 'nor', 'both', 'also',
'while', 'then', 'who', 'wich', 'that', 'although', 'though', 'since', 'until', 'as', 'if',
'after', 'before', 'how', 'once', 'when', 'lest', 'why', 'unless', 'because', 'till', 'where', 'whether'
),
'articles' => array(
'the', 'a', 'an'
),
'prepositions' => array(
'about', 'above', 'across', 'after', 'against', 'along', 'among', 'around', 'at', 'before', 'behind',
'below', 'beneath', 'beside', 'between', 'beyond', 'but', 'by', 'despite', 'down', 'during', 'except',
'for', 'from', 'in', 'inside', 'into', 'like', 'near', 'of', 'off', 'on', 'onto', 'out', 'outside', 'over',
'past', 'since', 'through', 'throughout', 'till', 'to', 'toward', 'under', 'underneath', 'until', 'up', 'upon',
'with', 'within', 'without'
),
'pronouns' => array(
'who', 'whom', 'whose', 'which', 'that', 'this', 'these', 'those', 'I', 'Me', 'You', 'He', 'him', 'she',
'her', 'it', 'we', 'us', 'they', 'them', 'Mine', 'Yours', 'His', 'Hers', 'Its', 'Ours', 'Theirs', 'whoever',
'whomever', 'whichever', 'myself', 'yourself', 'himself', 'herself', 'itself', 'ourselves', 'yourselves', 'themselves'
)
);

private function toExclude($token) {
if(in_array($token, $this->excluded))
return true;

foreach($this->exclude as $cat) {
if(in_array($token, $cat)) {
$this->excluded[] = $token;
return true;
}
}
return false;
}

Proviamo quindi ad effettuare un nuovo training ed una nuova analisi scartando questa volte le parole che abbiamo inserito e confrontiamo i risultati:

successo dell'analisi di Ian Barber (tutte le parole indiscriminatamente): 0.826747720365

successo dell'analisi escludendo congiunzioni, pronomi e preposizioni: 0.829787234043

come potete vedere un certo miglioramento c'è, anche se poco apprezzabile. D'altronde, se da una parte è vero che le parole senza valore lessicale creano nella nostra analisi un "rumore" di fondo, è anche vero che questo "rumore" si presenterà più o meno nella stessa misura in tutto il corpus utilizzato per il training, quindi sia nelle opinioni positive che in quelle negative.

Certamente se considerassimo anche le relazioni esistenti fra le parole che occorrono all'interno di una stessa frase potremmo aumentare ancora di più i successi dell'analisi, ad esempio, abbiamo voluto escludere le parole senza contenuto lessicale come la parola "but" (trad.: ma), ma nel caso di frasi come: "Funny but short" (trad.: divertente ma breve) sicuramente la congiunzine but fa assumere alla frase un significato diverso rispetto, ad esempio, alla frase: "Funny and short" (trad.: divertente e breve), pertanto anche questa metodologia non può rappresentare una soluzione definitiva.