Nei post precedenti della serie Fondamenti si è mostrato come un generico testo possa essere rappresentato dall’insieme delle sue parole, trascurando la grammatica e anche l’ordine delle parole stesse, ma mantenendo la loro molteplicità. Segue una breve sintesi.

Partendo da una collezione di documenti che chiamiamo corpus e indichiamo con la lettera C, estraiamo e filtriamo accuratamente un sottoinsieme di attributi1 che utilizziamo per generare un vocabolario V sul corpus C. In V, ogni attributo è associato ad un indice numerico, in altri termini V è una lista indicizzata di tutti gli attributi estratti e filtrati:

V = {t0, .. , ti, .., tM-1}, dim(V)=M

dove i=0,..,M-1 è l’indice numerico associato alla i’esimo attributo di V e M è il numero di attributi in V.

L’elenco degli attributi contenuti in V può essere utilizzato per rappresentare qualsiasi documento del corpus C. A tal proposito, una possibile rappresentazione di un documento è quella vettoriale. Il generico vettore che rappresenta un documento è chiamato vettore documento ed è così definito:

d = (w w ..  wM-1)

Ogni elemento di d è univocamente associato ad un attributo di V attraverso l’indice numerico (p.e. il primo elemento del vettore corrisponde al primo attributo di V, il secondo elemento al secondo attributo di V e così via). Il valore del generico wi è chiamato peso e corrisponde alla molteplicità, nel testo di d, dell’attributo ti ad esso associato. Per esempio, se nel testo del documento rappresentato da d, la parola ti compare 3 volte, allora wi=3. Questa rappresentazione vettoriale pesata è chiamata bag of words o più sinteticamente BOW.

Con la rappresentazione BOW, il numero di elementi di un qualsiasi vettore d è pari al numero di parole che compongono V.

Se N è il numero di documenti in C, avremo N vettori documento ciascuno di dimensione M. Assemblando questi vettori in una forma matriciale NxM otteniamo la matrice documento-termine DTM2. La trasposta di DTM, con dimensioni MxN, è chiamata matrice termine-documento TDM.

La TDM (o DTM) è la rappresentazione matriciale del corpus C. Una matrice TDM/DTM è sparsa ossia contiene molti elementi nulli (poiché non tutti gli attributi di V sono presenti in ogni documento). Come si è già anticipato in altri post della serie Fondamenti, la sparsità è un proprietà importante che condiziona profondamente lo sviluppo degli algoritmi.

Il BOW non è l’unico modello di rappresentazione vettoriale possibile e, a partire da esso, si possono ottenere (mediante opportune trasformazioni) altre rappresentazioni vettoriali dei documenti. Per esempio, TF-IDF (Term Frequency-Inverse Document Frequency) è un modello di rappresentazione in cui il generico peso wè un valore reale proporzionale al numero di volte che l’attributo i-esimo compare nel documento stesso e inversamente proporzionale alla frequenza dell’attributo i-esimo stesso nell’intero corpus. Ciò tiene conto del fatto che, da un lato l’attributo che si ripete in un documento tende a caratterizzarlo, ma che, d’altra parte, gli attributi più comuni in tutti i documenti del corpus sono quelli meno significativi per stabilire le differenze tra i diversi documenti.

Le dimensioni dei vettori documenti rappresentati secondo il modello TF-IDF sono identiche a quelle del modello BOW, cioè sono pari al numero M di attributi in V. La trasformazione di BOW in TF-IDF è detta paridimensionale.

Data una TDM di dimensioni MxN (com M numero delle parole e N numero dei documenti), le N colonne corrispondono ai vettori documenti. Questi vettori sono definiti nello spazio \mathbb{R}^{M}. Una trasformazione con riduzione di dimensionalità è un’operazione matematica che individua un sottospazio \mathbb{R}^{P} con P<M, chiamato spazio di uscita o anche spazio ridotto, ove i vettori documenti sono proiettati. Attraverso una trasformazione con riduzione, i vettori documenti assumono una dimensione inferiore rispetto alla rappresentazione BOW. I vettori che definiscono lo spazio ridotto sono detti vettori base3.

Come vedremo, la riduzione di dimensionalità genera inevitabilmente una perdita di informazioni. Generalmente si associa tale perdita con il “rumore” racchiuso nei dati e il cui contenuto informativo è trascurabile.

L’intuizione che è dietro agli schemi di rappresentazione vettoriale dei documenti è riconducibile alla cosiddetta ipotesi distribuzionale cioè il significato delle parole è determinato dal loro utilizzo e parole utilizzate in contesti simili tendono ad avere significati simili (correlazione semantica). Partendo da questo, l’area di ricerca della Distributional Semantics (DS) studia metodi e modelli per quantificare le similarità semantiche tra elementi linguistici analizzandone le proprietà distribuzionali (come sono distribuite) all’interno di una raccolta di documenti. La semantica distribuzionale utilizza l’algebra lineare come strumento di rappresentazione in cui ogni elemento linguistico in una raccolta di dati è proiettato in uno spazio vettoriale. La similarità semantica tra due elementi linguistici viene calcolata attraverso misure di similarità (come quella del coseno) tra le rispettive rappresentazioni vettoriali nello spazio. I metodi di costruzione degli spazi vettoriali e la loro riduzione di dimensionalità sono fattori che possono fortemente influenzare l’efficacia di questi modelli. In letteratura sono stati proposti diversi metodi per costruire spazi distribuzionali, BOW e TF-IDF sono due esempi basilari. A seguire, considereremo metodi più complessi che, partendo dai suddetti basilari, introducono diverse comode ottimizzazioni come la riduzione di dimensionalità.

Nello specifico, a seguire, tratteremo i modelli: Latent Semantic Analysis (LSA), Non-negative Matrix Factorization (NMF), Random Indexing (RI), Latent Dirichlet Allocation (LDA) e Hierarchical Dirichlet Process (HDP).


Caro lettore, in questo post, con una buona dose di spregiudicatezza (data la complessità della materia), ti propongo un “volo ad alta quota” sui territori particolarmente estesi e variegati del topic modeling. Lo scopo del post è introdurre le intuizioni che sono dietro ai vari modelli con brevi digressioni finalizzate esclusivamente all’introduzione di nozioni basilari. Ogni approfondimento è demandato alla tua sensibilità e per questo fornisco un’estesa quantità di link a risorse che possono aiutarti a focalizzare la tua attenzione. La matematica dietro ai modelli è appena accennata e volutamente semplificata, per fornire un’idea dei concetti fondanti e darti una maggiore consapevolezza dei parametri impostabili quando utilizzerai le implementazioni dei modelli. L’obiettivo della serie Fondamenti è quello di supportarti nella costruzione di un quadro operativo, mediante l’impiego trasversale di una estesa varietà di risorse di programmazione e analisi dei dati,  anche il presente post non si sottrae a questo obiettivo. Buona lettura.

Prima di procedere con un’overview dei modelli di rappresentazione definiamo il contesto tecnico-operativo su cui sperimentiamo le librerie gensim e sklearn.

Usiamo un corpus di 57 documenti in Italiano estratti da Wikipedia e incentrati su tematiche di intelligenza artificiale e collaterali varie. Rinvio ad altro post la descrizione dello script per la costruzione del corpus.

Il corpus è memorizzato in un lista serializzata in un file su disco. La lista si compone di tre elementi. Il primo elemento è l’elenco dei seed ossia degli argomenti principali utilizzati per interrogare Wikipedia. Il secondo elemento è l’elenco delle query generate sottoponendo i seed a Wikipedia. Il terzo elemento è la lista dei 57 documenti recuperati. Sui documenti, contestualmente alla fase di estrazione, è già stato eseguito un cleaning, per rimuovere i formati di gestione del testo di Wikipedia.

import pickle
# carico corpus di 57 articoli in italiano
C=pickle.load( open( "./datasets/itwiki/corpus-ML-57-082416.p", "rb" ) )

# minima introspezione del corpus
# visualizzo l'elenco dei seed utilizzati per recuperare i contenuti
for seed in C[0]: print seed

# memorizzo in variabile dedicata il corpus (è una lista di documenti)
C=C[2]

# visualizzo il numero di documenti nel corpus (dovrebbero essere 57!)
print len(C)

# calcolo in modo grezzo il numero di parole che compongono tutti i documenti del corpus
print sum([len(document.split()) for document in C])
apprendimento automatico
intelligenza artificiale
data mining
deep learning
reti neurali
rappresentazione della conoscenza
57
35446

Nel blocco precedente abbiamo deserializzato la lista contenente il corpus (pickle è una libreria per la serializzazione e deserializzazione di oggetti4). Abbiamo eseguito un minima introspezione sull’oggetto deserializzato, visualizzando il primo elemento della lista che consiste nell’elenco dei seed e visualizzando il numero di documenti nel corpus (il corpus è memorizzato nella terza posizione della lista deserializzata). Abbiamo anche effettuato una stima “grezza” nel numero totale di parole nel corpus. Il prossimo passo consiste nella tokenizzazione di ogni documento, nel filtraggio e nella costruzione delle rappresentazioni vettoriali BOW e TF-IDF (questi passaggi sono già stati introdotti in altri post della serie Fondamenti, dunque mi limiterò solo a riassumere le istruzioni per eseguirli).

Impostazioni comuni per le implementazioni con gensim e sklearn:

from stop_words import get_stop_words
# caricamento stop list Italiano
stoplist=get_stop_words('italian')

# aggiungo alcune stop word (dipendono dalla codifica Wiki)
stoplist.extend(['==','===','\displaystyle'])

Per gensim:

# librerie che useremo
import string
from gensim import corpora
from gensim import models
from nltk.tokenize import word_tokenize
from six import iteritems
from itertools import izip
import pyLDAvis

# tokenizzazione e filtraggio del corpus
filtered_corpus = [[word for word in word_tokenize(document.lower()) if word not in stoplist and word not in string.punctuation and len(word)>3 and not word.isdigit()] for document in C]

# generazione del vocabolario
V = corpora.Dictionary(filtered_corpus)
print V

# filtro le parole sulla occorrenza
once_ids = [tokenid for tokenid, docfreq in iteritems(V.dfs) if docfreq < 10]
V.filter_tokens(once_ids)
print V

# generazione della rappresentazione BOW e della matrice TDM 57x137
corpus_bow = [V.doc2bow(document) for document in filtered_corpus]

# generazione del modello tf-idf
schema_tfidf=models.TfidfModel(corpus_bow)

# gensim: trasformazione (paridimensionale) della rappresentazione BOW in TF-IDF
corpus_tfidf=schema_tfidf[corpus_bow]
Dictionary(7065 unique tokens: [u'motivazioni', u'h.265', u'h.264', u'macedone', u'sfruttando']...)

Dictionary(137 unique tokens: [u'rappresentazione', u'utilizzati', u'processo', u'cosxec', u'utilizzato']...)

Per sklearn:

# librerie che useremo
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.decomposition import TruncatedSVD
from sklearn.preprocessing import Normalizer
from sklearn.metrics import euclidean_distances
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import pyLDAvis

# estendo la stop list nativa di sklearn con le stop word in italiano
my_stop_words = text.ENGLISH_STOP_WORDS.union(get_stop_words('italian'))
# genero rappresentazione bow dei documenti e matrice TDM del corpus
vectorizer = CountVectorizer( strip_accents = 'unicode', stop_words=set(my_stop_words), token_pattern = r'\b(?![0-9]+)([0-9a-zA-Z]{3,})\b', min_df = 10)
corpus_bow=vectorizer.fit_transform(C)

# visualizzo dimensioni di TDM
corpus_bow.shape


# genero rappresentazione tf-idf
tfidf_vect = TfidfTransformer()
corpus_tfidf = tfidf_vect.fit_transform(corpus_bow)
(57, 154)

Nei due blocchi precedenti, a titolo di esercizio, si sono volutamente impiegate implementazioni diverse per il parsing. Il parsing include la tokenizzazione che nel caso di gensim impiega un tokenizer di NLTK (come mostrato in un post precedente della serie Fondamenti). E’ applicata una stop list per il filtraggio delle parole. Sono ignorati tutti gli attributi di lunghezza inferiore a 3 caratteri, gli attributi composti da sole cifre numeriche, gli attributi che compaiono in meno di 10 documenti, gli attributi composti da caratteri di punteggiatura. Il cut-off su 10 documenti riduce drasticamente il numero di attributi destinati a V di oltre il 98%, ossia da oltre 35446 token a 137 attributi con gensim e 154 attributi con sklearn (questo cut-off è particolarmente restrittivo, ma tuttavia idoneo per gli scopi del presente post).


[LSA]: Il modello Latent Semantic Analysis (LSA) si ottiene, per via matematica, a partire dalle rappresentazioni vettoriali TF-IDF o BOW, utilizzando la tecnica nota come decomposizione ai valori singolari (SVD da Singular Value Decomposition).

La riduzione delle dimensioni dello spazio vettoriale si basa sull’intuizione che un’appropriata trasformazione matematica potrebbe aiutare a raggruppare gli attributi di V definendo delle categorie (o classi) semantiche sul corpus. Queste categorie forniscono uno nuovo spunto di rappresentazione dei documenti ai fini della loro classificazione, ricerca, summarization, ecc. Le categorie semantiche, infatti, potrebbero identificare dei concetti chiave, detti topic, di cui trattano i documenti del corpus e potrebbero essere utilizzate per rappresentare più efficacemente i documenti stessi. Essendo generati sull’intero corpus raggruppando gli attributi usati nei documenti (secondo una varietà di approcci matematici, come vedremo), i topic rappresentano dei concetti latenti, ossia non direttamente esplicitati nei documenti ma da essi determinabili mediante un’analisi automatica. Per quanto appena scritto, dovrebbe ora essere chiaro il motivo per cui ci si riferisce ai topic chiamandoli anche attributi latenti.
In seguito useremo in modo interscambiabile le parole topic e attributo latente. I topic sono i vettori base dello spazio ridotto. Quando parleremo di topic intenderemo riferirci sempre alla loro rappresentazione vettoriale nello spazio ridotto.

Il modello LSA attende in input: la matrice TDM MxN basata su TF-IDF o BOW (la prima può essere preferibile); il numero P di topic da identificare. P è chiamato livello di troncamento e deve essere, per sua definizione, inferiore a M (numero degli attributi in V).

Come già anticipato, la riduzione di dimensionalità comporta inevitabilmente una perdita di informazione, ma la matematica fornisce gli strumenti che consentono di scegliere tra le tante possibili proiezioni sullo spazio ridotto quella che massimizza l’informazione trattenuta minimizzando quindi quella perduta. SVD cerca di proiettare dei dati multidimensionali in uno spazio di dimensione minore conservando al massimo la variazione dei dati originali.

Segue ora una presentazione del processo di riduzione di dimensionalità basato su SVD mediante l’impiego dell’ambiente per l’analisi statistica R.

Sia A una TDM così composta:

A = Schermata 2016-07-24 alle 14.54.31.png

In R bastano poche istruzioni per creare questa matrice TDM:

A = matrix(data=c(2,0,8,6,0,3,1,
1,6,0,1,7,0,1,
5,0,7,4,0,5,6,
7,0,8,5,0,8,5,
0,10,0,0,7,0,0), ncol=7, byrow=TRUE)
rownames(A) <- c('doctor','car','nurse','hospital','wheel')

Fattorizzando A mediante SVD, il risultato che si ottiene è:

 AMxN = UMxM * SMxM * (VNxM)T

L’obiettivo dell’SVD consiste nell’individuare opportune trasformazioni lineari delle variabili osservate che siano facilmente interpretabili e capaci di evidenziare e sintetizzare l’informazione insita nella matrice iniziale A. Tale strumento risulta utile soprattutto quando si ha a che fare con un numero di attributi considerevole da cui si vogliono estrarre le maggiori informazioni possibili pur lavorando con un set più ristretto di attributi.

In R:

s=svd(A)
U=s$u
S=diag(s$d)
V=s$v

U5×5=Schermata 2016-07-25 alle 00.09.15.png

S5×5Schermata 2016-07-25 alle 00.10.36

[(V5×7)T]7×5=Schermata 2016-07-25 alle 00.12.58.png

U e V sono definite rispettivamente matrice dei vettori termini (ogni attributo è associato ad una riga) e matrice dei vettori documenti (ogni documento è associato ad una colonna, le righe corrispondono ai topic).

I topic sono associati alle colonne di U: ogni vettore colonna definisce un raggruppamento in cui ogni attributo è pesato secondo il suo grado di co-occorrenza con gli altri attributi. Per esempio nel secondo vettore colonna, le parole “car” e “wheel” hanno coefficienti negativi mentre “doctor”, “nurse” e “hospital” hanno coefficienti positivi ad indicare che questo è un raggruppamento in cui “wheel” occorre solo con “car”.

Il primo vettore colonna (topic) è la combinazione lineare degli M attributi di partenza avente la massima varianza. Il secondo topic è la combinazione lineare degli M attributi di partenza avente la varianza immediatamente inferiore a quella del precedente topic e così via. Se gli M attributi di partenza sono molto correlati, allora un numero P<M può bastare per tener conto di una quota elevata di varianza totale per cui i primi P topic fornirebbero una buona descrizione della struttura dei dati. In altri termini, i topic sono statisticamente incorrelati (cioè non dipendono l’uno dall’altro) e sono ordinati in accordo al loro contenuto informativo. Quindi se i topic sono in numero pari a P, possiamo immaginare che sul primo topic siano “spalmate” le parole originali in modo che ciascuna contribusica a conferire un contenuto informativo globale che sia maggiore del contributo del secondo topic e così via. 

Da un punto di vista geometrico, i vettori colonna in U sono gli autovettori che definiscono lo spazio ridotto e sui cui sono proiettati i vettori documenti di partenza (i vettori base dello spazio ridotto).

Detta P=3 la dimensione dello spazio ridotto, se riduciamo le matrici U, S, V come mostrato nella figura seguente:

Schermata 2016-07-25 alle 01.png

otteniamo le matrici ridotte Ur, Sr, Vr. Questo processo è chiamato troncamento di SVD.

In R:

Sr<-S[-4:-5,-4:-5]
Ur<-U[,-4:-5]
Vr<-V[,-4:-5]
require (MASS) # libreria necessaria per la successiva funzione ginv
Ar<-ginv(Sr) %*% t(Ur) %*% A

La TDM sullo spazio ridotto è allora ottenuta colcolando:

Ar PxN = Sr-1 * UrT * A

Ar PxN = Schermata 2016-07-25 alle 01.23.22

Da notare che la matrice Ar non è più sparsa a differenza della TDM di partenza.

Utilizzando la formula precedente è possibile che due documenti qualsiasi, originariamente differenti nello spazio M dimensionale, possano essere mappati sullo stesso vettore dello spazio ridotto (topic); l’insieme dei vettori base dello spazio P dimensionale, rappresenta in un certo senso l’insieme dei concetti o dei diversi significati che i vari documenti possono assumere, quindi un generico documento nello spazio P-dimensionale è rappresentabile come combinazione lineare dei concetti o equivalentemente dei vettori base dello spazio stesso.

Nell’ambito dell’Information Retrieval (recupero di documenti a partire da query), il modello LSA è chiamato Latent Semantic Indexing (LSI). La ricerca basata su LSI assume che la query fatta dall’utente è rappresentabile come un insieme finito di attributi, quindi considerata alla stregua di un normale documento, e perciò rappresentabile, nello spazio P-dimensionale, come un vettore. Quindi una volta che la query è stata analizzata e rappresentata come uno pseudo-documento, può essere semplicemente comparata con tutti gli altri documenti. Il vettore colonna della query (q), di dimensione Mx1, è rappresentabile matematicamente come uno pseudo documento qr, dello spazio P-dimensionale, grazie a questa formula:

qr Px1 = Sr-1 * UrT * q

In questo modo i documenti concettualmente più vicini alla query possono essere calcolati una volta che si è scelto il modo per misurare la loro “vicinanza”. Una semplice e ovvia misura di vicinanza è per esempio il valore del coseno dell’angolo compreso tra due vettori (ossia tra due rappresentazioni vettoriali ridotte di documenti).

La trasformazione di riduzione della dimensionalità (SVD) sottostante a LSA introduce alcune limitazioni. Innanzitutto non è conosciuto un metodo per calcolare il livello di troncamento ottimale; solitamente il valore di P è scelto con funzioni euristiche oppure attraverso tecniche di validazione che prevedono di stimare e confrontare tanti modelli con diversi livelli di troncamento per scegliere quello con prestazioni migliori. Un’altra limitazione discende dal fatto che i vettori corrispondenti ai topic (vettori colonna di U) sono ortogonali esprimendo così il fatto che essi rappresentano aspetti mutuamente esclusivi. In altri termini, i topic rappresentano dei concetti astratti o argomenti che tendono a monopolizzare gli attributi nel senso che un attributo che ha un’elevata co-occorrenza in un argomento sarà penalizzato su altri argomenti. LSA tende a ridimensionare occorrenze multiple di un attributo in differenti topic e dunque non può essere utilizzato in modo efficace per risolvere aspetti relativi alla polisemia (p.e. parole che hanno più di un significato a seconda dell’argomento) sebbene questa caratteristica sia altresì molto conveniente per riconoscere e trattare la sinonimia (parole con lo stesso significato). In altri termini, LSA gestisce bene la sinonimia ma è debole con la polisemia.

LSA trova applicazioni su quei dataset in cui i documenti sono scritti con lo stesso stile di scrittura e sono focalizzati su un argomento centrale ben specifico. Nel caso di più argomenti, i documenti dovrebbero essere stati redatti in modo tale che le parole utilizzate frequentemente nell’ambito di un argomento siano utilizzate meno frequentemente su altri argomenti. Per esempio, se abbiamo documenti che parlano di Java come linguaggio di programmazione e Java come categoria di caffè, potrebbero esserci problemi con LSA.[/themify_box]

Solitamente il valore di P è scelto nell’intervallo 100-300.

Un approccio (derivato dalla PCA) suggerisce di scegliere P come il più piccolo intero tale che:

\left(\frac{\sum_{i=1}^P s_{ii}}{\sum_{i=1}^M s_{ii}}\right)\geq0.99

dove sii sono gli elementi della diagonale della matrice S (autovalori).

Implementazioni di LSA sono presenti sia in gensim sia in sklearn.

Per gensim:

# imposto il livello di troncamento
P=10

# generazione del modello LSA
schema_lsi = models.LsiModel(corpus_tfidf, id2word=V, num_topics=P)

# proiezione della tf-idf sullo spazio ridotto LSA (da 137 dimensioni a 10!)
corpus_lsi = schema_lsi[corpus_tfidf]

# per visualizzare la rappresentazione vettoriale dei documenti nello spazio ridotto
for l,t in izip(corpus_lsi,C): print l,"#",t.encode("utf8")[0:35]+".."+"\n"

# visualizzo i topic
schema_lsi.show_topics()
[(0,
u'-0.250*"rete" + -0.229*"dati" + -0.211*"apprendimento" + -0.176*"artificiale" + -0.154*"reti" + -0.151*"algoritmi" + -0.142*"automatico" + -0.141*"mining" + -0.136*"data" + -0.130*"essere"'),
(1,
u'-0.464*"rete" + 0.419*"dati" + 0.248*"mining" + 0.245*"algoritmi" + 0.220*"data" + -0.187*"reti" + -0.186*"artificiale" + 0.185*"apprendimento" + -0.167*"stato" + 0.157*"conoscenza"'),
(2,
u'-0.409*"rete" + -0.345*"apprendimento" + 0.287*"conoscenza" + -0.220*"automatico" + -0.201*"algoritmi" + 0.201*"artificiale" + -0.200*"reti" + 0.197*"intelligenza" + -0.179*"neurali" + 0.157*"base"'),
(3,
u'0.409*"automatico" + 0.371*"apprendimento" + 0.370*"conoscenza" + -0.265*"mining" + -0.243*"dati" + -0.223*"data" + -0.180*"database" + -0.177*"rete" + -0.141*"standard" + 0.137*"artificiale"'),
(4,
u'0.665*"conoscenza" + 0.344*"rete" + 0.300*"base" + -0.163*"stato" + -0.157*"intelligenza" + -0.147*"algoritmi" + -0.145*"apprendimento" + -0.130*"artificiale" + -0.125*"primo" + 0.122*"rappresentazione"'),
(5,
u'-0.551*"learning" + 0.337*"funzione" + -0.274*"programmazione" + -0.256*"machine" + -0.228*"linguaggio" + 0.171*"modello" + -0.166*"stato" + -0.162*"collegamenti" + -0.157*"esterni" + -0.125*"note"'),
(6,
u'0.377*"database" + -0.275*"funzione" + 0.264*"dati" + -0.237*"funzioni" + 0.209*"artificiale" + 0.200*"mondo" + -0.181*"livello" + 0.172*"rete" + 0.160*"computer" + 0.155*"intelligenza"'),
(7,
u'0.300*"stato" + 0.287*"meno" + 0.243*"viene" + 0.236*"dopo" + -0.201*"relazioni" + -0.197*"database" + 0.197*"teoria" + -0.195*"intelligenza" + -0.192*"artificiale" + 0.161*"inglese"'),
(8,
u'-0.356*"database" + 0.312*"dati" + 0.251*"stato" + -0.203*"tipo" + -0.190*"file" + 0.181*"scienze" + -0.175*"esempio" + -0.173*"automatico" + 0.156*"informazioni" + 0.150*"conoscenza"'),
(9,
u'0.365*"database" + -0.299*"dati" + -0.276*"standard" + 0.227*"teoria" + 0.198*"mondo" + 0.180*"neurali" + -0.179*"computer" + 0.175*"mining" + 0.171*"reti" + 0.168*"stato"')]

Nel blocco precedente, mediante il comando show_topics, sono visualizzati tutti i topic. Ogni topic è una combinazione lineare pesata degli attributi di V (di default sono visualizzati solo 10 attributi dello spazio di origine, comunque, il limite è parametrizzabile impostando il parametro num_words di show_topics). Geometricamente i topic (o, ricordiamo, attributi latenti) sono i vettori base dello spazio ridotto su cui sono proiettati i vettori documento dello spazio di orifine.

Per sklearn:

# imposto il livello di troncamento
P=10

# proiezione da tf-idf allo spazio ridotto LSA
svd = TruncatedSVD(n_components = P, algorithm='arpack')
corpus_lsa = svd.fit_transform(corpus_tfidf)

# visualizzazione della rappresentazione dei documenti nello spazio ridotto
pd.DataFrame(corpus_lsa,index=[elem[:35] for elem in C], columns=["topic "+ str(idx) for idx in range(0,P)])

# visualizzazione dei topic
pd.DataFrame(svd.components_,index=[["topic "+ str(idx) for idx in range(0,P)], columns=vectorizer.get_feature_names()).transpose()

Con riferimento al blocco precedente, le figure sottostanti mostrano un estratto della visualizzazione della rappresentazione dei documenti nello spazio ridotto e un estratto della visualizzazione dei topic. Dalla rappresentazione dei documenti si evince che il “topic 0” ha un maggior peso e quindi i documenti sono maggiormente catalizzati da questo topic specifico.

Immagine2

Immagine1

Ovviamente i topic generati da sklearn rappresentano delle categorie semantiche diverse da quelle generate con la libreria gensim.

La generazione di uno schema di rappresentazione LSA in sklearn è eseguita istanziando l’oggetto TruncatedSVD che include due possibili implementazioni della SVD troncata5 selezionabili a discrezione del programmatore: un risolutore SVD randomico e un algoritmo “naive” basato sulla libreria ARPACK, nella figura seguente è possibile avere un’idea delle differenze prestazionali delle decomposizioni ottenute dalle due implementazioni troncate (linee verde e viola) anche in comparazione con la SVD non troncata:

index[Source]

Un modo per valutare graficamente gli effetti della riduzione di dimensionalità, consiste nel determinare le matrici delle distanze euclidee di entrambi gli spazi origine e ridotto, quindi, calcolare la matrice differenza (avendo cura di considerare i valori assoluti degli elementi) e visualizzarla mediante una mappa di calore (heatmap).

# calcolo distanze nello spazio di origine
org_dist=euclidean_distances(corpus_tfidf)
# calcolo distanze nello spazio ridotto
red_dist=euclidean_distances(corpus_lsa)
# calcolo differenze e genero matrice delle differenze
diff_dist=abs(org_dist-red_dist)

# visualizzo la heatmap
plt.figure()
plt.pcolor(diff_dist)
plt.colorbar()
plt.show()

heatmap1

La scala di colore della mappa fornisce un’indicazione dell’intervallo di variazione dei valori delle differenze. Questi valori, nel caso in esame, spaziano in un range tra 0 e 1.05 e con una maggiore concentrazione nel più piccolo segmento 0 e 0.70. Una colorazione bluastra rappresenta un minore effetto distorsivo sulle distanze e quindi una maggiore capacità di mantenere le caratteristiche del corpus. Le variazioni delle differenze entro piccoli intervalli sono da considerare come un segnale di conservatività della trasformazione (con il topic modeling, tale conservatività, a seconda dell’obiettivo, potrebbe comunque non essere necessaria).

Per ulteriori approfondimenti:


[NMF]: Anche il modello Non-negative Matrix Factorization (NMF) è basato sulla fattorizzazione della matrice TDM (o DTM) e si ottiene per via matematica a partire dalle rappresentazioni vettoriali TF-IDF o BOW. Essendo la matrice TDM non negativa (cioè tutti i suoi elementi sono ≥0), NMF ne esegue un tipo particolare di fattorizzazione che produce ancora due matrici non negative più piccole WMxP e HPxN. Quando moltiplichiamo assieme queste matrici otteniamo un’approssimazione della matrice originale. L’algoritmo su cui si basa NMF si sviluppa iterativamente con l’obiettivo di determinare la coppia W e H affinchè l’errore tra la matrice approssimata e la matrice originale risulti minimo. Come per il modello LSA, anche per l’NMF, il livello di troncamento P è uno dei parametri di input, un altro parametro è il numero di iterazioni. La matrice W è una matrice MxP (attributi x topic) le cui colonne sono i vettori della base generata da NMF. Quindi gli elementi della j-esima colonna di W, indicata con wj, corrispondono ai pesi di ciascun attributo rispetto al topic j-esimo. Ordinando i valori dei pesi e selezionando gli attributi con i pesi più alti possiamo ottenere una descrizione del topic j-esimo. Ogni topic è rappresentato come combinazione lineare degli attributi con maggior peso. Un’interpretazione simile può essere data alla matrice H di dimensioni PxN (topic x documenti) avente colonne sparse e non negative. Il j-esimo elemento della i-esima colonna di H indica in che misura il j-esimo topic è presente nell’i-esimo documento. Quindi, ogni documento sarebbe rappresentato come combinazione lineare di basi non negative (ossia dei topic). Se l’input è una matrice TDM l’interprestazione di W e H è invertita.

La fattorizzazione non negativa è preferita ad altri metodi di approssimazione grazie a due caratteristiche fondamentali: interpretabilità e basso utilizzo di memoria che non aumentano i costi computazionali del metodo. D’altra parte però, si ha anche qualche svantaggio. A differenza della SVD, la NMF non è supportata da una base teorica che ne assicura la convergenza ad un minimo assoluto ma gli algoritmi della fattorizzazione non negativa portano, quando c’è convergenza, solo a minimi locali che quindi sono legati, anche in maniera importante, al punto di inizializzazione (ossia a come è inizializzata la matrice W). Sono stati proposti differenti meccanismi di inizializzazione che permettono di ottenere un’alta riduzione dell’errore e una più veloce convergenza degli algoritmi NMF adottati. A differenza di LSA i vettori dei topic appresi da NMF non sono ortogonali tra loro, ciò indica che i topic possono non essere ben separati. Esistono tuttavia delle varianti di NMF che impongono un vingolo di “quasi ortogonalità” ai vettori base.

NMF vanta una estesa varietà di impieghi: nel text-mining appunto, per il riconoscimento di SPAM, per l’information filtering basato sulle preferenze dell’utente, classificazione di documenti, creazione automatica di riassunti, analisi delle immagini per il riconoscimento di volti e (ma non solo) object detection, analisi degli spettri sonori, financial data mining, etc.

Per sklearn:

# genero proiezione da tf-idf allo spazio ridotto NMF
from sklearn.decomposition import NMF
nmf = NMF(n_components=P, init='random', random_state=0)
# oppure nmf = NMF(n_components=P, init='nndsvdar', random_state=0)
corpus_nmf = nmf.fit_transform(corpus_tfidf)

# per visualizzazare i topic
pd.DataFrame(nmf.components_,index=["topic "+ str(idx) for idx in range(0,P)], columns=vectorizer.get_feature_names()).transpose()

# per visualizzare i documenti nello spazio ridotto
pd.DataFrame(corpus_nmf,index=[elem[:35] for elem in C], columns=["topic "+ str(idx) for idx in range(0,P)])

# per visualizzare la mappa di calore delle differenze
# (per il blocco completo di istruzioni si veda blocco della mappa di calore di LSA)
# ..
# red_dist=euclidean_distances(corpus_nmf)
# ...

L’implementazione della trasformazione NMF in sklearn prevede la possibilità di specificare, attraverso il parametro init di NMF diversi metodi di inizializzazione dell’algoritmo sottostante. Nella figura seguente sono rappresentate le mappe di calore ottenute assumendo due diversi algoritmi di inizializzazione: random (non-negative random matrices) e nndsvdar (non-negative Double Singular Value Decomposition NNDSVD with zeros filled with small random values).

NMF con init=random

NMF con init=random

NMF con init=nndsvdar

NMF con init=nndsvdar

Per approfondimenti:


I modelli LSA e NMF si fondano su specifici trattamenti matematici della matrice DTM (o TDM). Sul piano computazionale questi trattamenti possono essere particolarmente onerosi. Per contenere il peso computazionale si applicano delle ipotesi semplificatorie che introducono delle approssimazioni nei risultati rispetto a quanto ottenibile con il modello originale, queste ipotesi rientrano nel bagaglio degli algoritmi e loro rispettive varianti che ogni modello ha come dote.

Ad ogni modo, LSA e NMF sono document-dependent nel senso che l’individuazione dei vettori base che definiscono lo spazio ridotto si fonda su operazioni matematiche applicate alla DTM. Maggiore è la dimensione del corpus e, quindi, di conseguenza la dimensione della matrice DTM, maggiore sarà il carico computazionale generato dai modelli per l’individuazione dei vettori base. Inoltre, l’aggiunta di nuovi documenti richiede la riesecuzione dei calcoli (sebbene vi siano varianti algoritmiche in grado di supportare l’aggiunta di nuovi documenti ma con progressiva penalizzazione dell’ortogonalità dei vettori base).

[RI]: il modello Random Indexing (RI) (o Random Projections) propone un approccio document-independent. In particolare, detto P il livello di troncamento acquisito come parametro di input, ad ogni parola del vocabolario è assegnato un vettore sparso di P elementi (detto index-vector) contenente un piccolo insieme di valori +1 e -1 distribuiti casualmente (diverse implementazioni del modello possono ricorrere ad altri valori casuali). La composizione dei vettori conferisce agli stessi una “quasi ortogonalità” che implica una separabilità di topic ossia questi possono assumersi sufficientemente distanti gli uni dagli altri e pertanto accettabilmente esclusivi. Difatti, il prodotto scalare tra due vettori base sarà un numero molto piccolo e dunque essi si possono assumere quasi ortogonali (nearly orthogonal). I vettori ottenuti possono essere combinati per ottenere una matrice FMxP (attributo x topic) detta matrice di proiezione. Ogni riga della matrice è un vettore che rappresenta un attributo del vocabolario nello spazio ridotto. Questa matrice è dunque ottenuta senza nessuna riduzione della DTM e ciò spiega la definizione document-independent. Il passo successivo di RI consiste nel proiettare i vettori riga della matrice DTM, qui indicata con ANxM, sullo spazio ridotto costruito al punto precedente. La versione ridotta della DTM (contenente tutti i vettori documenti sullo spazio ridotto) sarà dunque definita dal prodotto matriciale ANxM * FMxP.

Comparato con LSA, RI è anzitutto un modello incrementale che consente un ampliamento del corpus (aggiunta di nuovi documenti) senza la ripetizione di costose riduzioni sulla matrice TDM. Inoltre la costruzione dello spazio ridotto consente di poter eseguire analisi di similarità senza necessariamente una preventiva elaborazione di tutti i vettori documenti. Il modello RSI si basa sulla tecnica incrementale Random Projections.

RI non è esente da limiti. La quasi ortogonalità dei vettori base può indurre distorsioni sulla similarità soprattutto quando si scelgono piccoli valori di P per rappresentare corpus di grandi dimensioni. La costruzione della matrice di proiezione assume quindi rilievo centrale ed è guidata da una serie di risultati ampiamente recepiti nelle implementazioni correnti del modello (come per esempio le distribuzioni semplificate di Achlioptas, 2001). Diversi studi in letteratura dimostrano come le prestazioni di RI siano riconducibili a quelli di LSA. Come LSA, RI può risentire del problema della polisemia.

RI è stato usato per una varietà di scopi inclusa la processazione delle immagini. Inoltre nel caso dei documenti di testo, diversi risultati dimostrano che un prefiltraggio con lemmatizzazione può garantire risultati superiori anche a LSA.

Con gensim l’implementazione della trasformazione RI si ottiene così:

# generazione del modello RP
rp = models.RpModel(corpus_tfidf, num_topics=P, id2word=V)
# proiezione della rappresentazione vettoriale tf-idf sullo spazio ridotto RP
corpus_rp=rp[corpus_tfidf]

# per visualizzare le rappresentazioni vettoriali dei documenti nello spazio ridotto
for reduced_doc in corpus_rp: print reduced_doc

Con sklearn:

# genero proiezione dallo spazio di origine tf-idf sullo spazio ridotto RP
from sklearn import random_projection
rp = random_projection.GaussianRandomProjection(n_components=P)
corpus_rp= rp.fit_transform(corpus_tfidf)

# per visualizzazare i topic
pd.DataFrame(rp.components_,index=["topic "+ str(idx) for idx in range(0,10)], columns=vectorizer.get_feature_names()).transpose()

# per visualizzare i documenti nello spazio ridotto
pd.DataFrame(corpus_rp,index=[elem[:35] for elem in C], columns=["topic "+ str(idx) for idx in range(0,10)])

# per visualizzare la mappa di calore delle differenze
# (per il blocco completo di istruzioni si veda blocco precedente per la generazione della mappa di calore di LSA
# ..
# red_dist=euclidean_distances(corpus_rp)
# ...

Nel caso di sklearn si è usata l’implementazione Gaussian Projection Random (Proiezione Gaussiana Casuale) che riduce la dimensionalità proiettando lo spazio di origine su una matrice generata casualmente i cui elementi sono assegnati mediante la seguente distribuzione:

N(0, \frac{1}{n_{components}})

La mappa di calore per RI è mostrata nella figura sottostante.

hmrigauss

Link per approfondimenti:


A differenza dei modelli descritti in precedenza, il prossimo, l’LDA, è un modello completamente generativo in cui le assegnazioni dei topic per i documenti sono calcolate a partire da una distribuzione di probabilità definita su parametri casuali piuttosto che essere vincolate alla matrice delle distribuzioni (DTM) valutata empiricamente sui documenti di training.
Dato un certo insieme di dati osservabili relativi ad un fenomeno, un modello probabilistico generativo si basa sull’assunto che i dati siano generati da un qualche processo casuale parametrizzato. Il focus è dunque tutto sullo studio di questo processo. Calcolato il set di parametri del processo che meglio si adatta ai dati a disposizione, si può utilizzare il modello per predire o simulare valori per tutte le variabili del modello. In generale, l’approccio generativo si basa sulla creazione di un modello dei dati che poi viene utilizzato per predire le risposte desiderate (o dati di uscita). Un approccio discriminativo, al contrario, cercherebbe di modellare direttamente la relazione tra dati in entrata e quelli in uscita, in modo da minimizzare una qualche funzione di perdita (loss function).
Per una comprensione più approfondita di LDA, potrebbe aiutare la consultazione del ripasso generale che propongo nel post Sintesi su teoria della probabilità, inferenza bayesiana, modelli e distribuzioni.

[LDA]: Latent Dirichlet Allocation (LDA) è un modello probabilistico generativo che si sviluppa attorno all’idea di modellare il processo che è alla base della creazione dei documenti, per poter poi inferire l’individuazione dei topic latenti.

Supponiamo di voler scrivere un documento pescando a caso da un’urna uno o più biglietti con sopra scritto un argomento. Ipotizziamo ora di conoscere, per ogni argomento estratto, in quale proporzione scegliere le parole dal vocabolario per comporre il documento. Questa è l’idea di processo casuale su cui si basa il modello generativo di LDA. Ovviamente si tratta di un’idea semplificata (rispetto alla realtà) che aiuta però a dominare la complessità del fenomeno reale e a creare un modello matematico trattabile.

Chiarita l’intuizione, più formalmente possiamo dire che in LDA ogni documento è considerato come una mistura casuale di topic sui quali è definita una distribuzione di probabilità (in seguito indicata con θd). I topic sono indipendenti dal documento e per ciascuno di essi esiste una distribuzione di probabilità sugli attributi di un vocabolario (in seguito indicata con φp): ogni attributo può essere generato dai topic per mezzo di distribuzioni condizionate fissate. Ciascun topic contiene diversi attributi, ognuno con un proprio valore di probabilità.

Dato un corpus C di N documenti, i dati osservabili sono gli M attributi estratti dai documenti e che compongono il vocabolario V. Data la rappresentazione BOW dei documenti, la generica variabile wd,n indica che l’attributo n è presente nel documento d. Le idee alla base di LDA ci suggeriscono che ogni attributo è generato da un topic, quindi, introduciamo delle variabili che contengono l’assegnazione dei topic agli attributi, per esempio una variabile zd,n indica l’assegnazione di un topic per l’attributo n nel documento d. wd,n dipende da zd,n. Quello che a noi interessa sono le proporzioni in cui i P topic sono presenti in un documento d e questo può essere modellato da una distribuzione di probabilità che indichiamo con θd6. zd,n dipende da θd. Un’altra cosa che ci interessa sono le proporzioni di assegnazione di ogni attributo di un documento in un topic; anche queste proporzioni possono essere modellate da una distribuzione di probabilità che indichiamo con φp7.  wd,n dipende da φk. Riassumendo abbiamo una relazione di dipendenze che possiamo così sinteticamente rappresentare:

θd –> zd,n –> wd,n <– φp

Se conoscessimo le distribuzioni di probabilità θd (per ogni d) e φp (per ogni p) allora il nostro modello sarebbe già pronto per l’uso in quanto sapremmo con quale proporzione assegnare i topic ai documenti e le parole ai topic. Purtroppo però le due distribuzioni sono incognite e dobbiamo determinarle. Quello che possiamo fare è ricorrere alla teoria della probabilità: se consideriamo zw come variabili casuali multinomiali e le loro corrispondenti θ, φ come distribuzioni di Dirichlet allora, in virtù di una speciale relazione tra multinomiale e Dirichlet, è possibile calcolare con buona approssimazione le incognite θ e φ.

La distribuzione di Dirichlet (distribuzione a priori coniugata della multinomiale) quando utilizzata per modellare le probabilità a priori incognite di una distribuzione discreta multinomiale fornisce delle probabilità a posteriori che sono correlate secondo specifici parametri (detti iperparametri) alle stesse probabilità incognite a priori. Dunque i valori di probabilità che otteniamo approssimano i valori incogniti.

A questo punto possiamo delineare i passaggi chiave di un algoritmo di LDA:

  1. inizializzazione dei parametri:
    • numero dei topic (P)
    • iperparametri delle Dirichlet8: α per la distribuzione θ, β (o talvolta η) per la distribuzione φ
    • numero di iterazioni sul corpus (talvolta chiamato “passi sul corpus” o passes)
  2. si assegnano in modo casuale tutti gli attributi dei documenti ai topic (ossia si valorizzano in modo casuale tutte le zd,n)
  3. si genera la matrice CwtMxP in cui gli elementi contano il numero di volte che ogni attributo è assegnato ad un topic
  4. si genera la matrice CdtNxP in cui gli elementi contano il numero di volte che ogni documento è assegnato ad un topic
  5. si considera ogni documento del corpus e per ogni documento si considera a turno ogni singolo attributo
    • si calcola la probabilità di assegnazione dell’attributo su tutti i topic e in funzione di queste probabilità (si seleziona il topic con la probabilità più alta) si aggiorna l’assegnazione dell’attributo ad un topic (cioè si aggiorna l’associazione fatta al punto 1 precedente)
    • si riaggiornano i conteggi in CwtMxP
    • si riaggiornano i conteggi in CdtNxP

Il passaggio 5 e relativi sottopassaggi, sono chiamati ciclo di campionamento di Gibbs e sono ripetuti sull’intero corpus un numero di volte pari al numero di iterazioni impostato come parametro di input. Possiamo pensare ad ogni documento come ad un insieme disordinato di attributi; i topic di un documento vengono individuati osservando le occorrenze degli attributi al suo interno e confrontandole con le distribuzioni degli attributi per ciascun topic.

La probabilità calcolata nel ciclo di Gibbs è così definita:

P( z_i=j| z_{-i}, w_i, d_i) \propto \frac{ {C_{ d_ij }^{DT}}+ \alpha }{ \sum_{t=1}^{P}C_{ d_it }^{DT} + P \cdot \alpha} \cdot \frac{ {C_{ w_ij }^{WT}}+ \beta }{ \sum_{w=1}^{M}C_{ wj }^{WT} + M \cdot \beta}

La notazione a sinistra indica indica la probabilità che l’attributo i è assegnato al topic j considerando le assegnazioni sui topic di tutti gli altri attributi (ad eccezione dell’attributo i); wi e di sarebbero gli indici di attributo e documento correntemente selezionati nel ciclo in altre parole rappresentano l’iterazione corrente del ciclo. Nella parte a sinistra abbiamo il prodotto di due probabilità: la prima esprime la probabilità di assegnazione del topic j al documento d, la seconda è la probabilità di assegnazione dell’attributo corrente i al topic j. Il denominatore della prima probabilità corrisponde al numero totale di occorrenze del documento corrente su tutti i topic (ad eccezione del topic j corrente) più il numero di parole M moltiplicato per α, il denominatore della seconda probabilità corrisponde al numero totale di occorrenze di parole sul topic in esame (ad eccezione del documento i corrente) addizionato al numero di documenti moltiplicato per β. Il risultato è un vettore di probabilità (una per topic). Queste probabilità regolano le proporzioni di assegnazione dello specifico attributo ad ogni topic. L’attributo viene quindi riassegnato al topic più probabile (aggiornamento della allocazione casuale definita al punto 1).

Le matrici ΘNxP e ΦPxM – che rappresentano il complesso delle incognite (dette anche descrittori dei documenti) e sono anche l’output atteso da LDA – sono ottenute così:

\theta_{d_ij} = \frac{ {C_{ d_ij }^{DT}}+ \alpha }{ \sum_{t=1}^{P}C_{ d_it }^{DT} + \alpha}
\phi_{w_ij} = \frac{ {C_{ w_ij }^{WT}}+ \beta }{ \sum_{w=1}^{M}C_{ wj }^{WT} +  \beta}

Il modello LDA è ora completo: abbiamo un modello probabilistico della distribuzione dei topic nel corpus basato su una tecnica non supervisionata (in quanto non esiste nessuna conoscenza ed etichettatura dei topic durante la fase di addestramento).

LDA ha parecchi punti degni di nota. Per iniziare le probabilità delle parole vengono massimizzate distribuendo le parole tra gli argomenti; questo significa che non può accadere che si assegni a tutte le parole in ciascun topic un valore di probabilità proporzionale al numero di occorrenze che si misura su tutto il corpus in quanto l’evidenza farà sì che le parole si distribuiscano “a blocchi” tra i diversi argomenti. Un altro punto è che, in LDA, l’uso della distribuzione di Dirichlet sulle proporzioni di topic incoraggia la dispersione: un documento sarà penalizzato in caso contenga molti argomenti diversi; più i valori di α si avvicinano a 0, maggiore è la probabilità che sia presente un solo argomento per documento. Questi aspetti portano ad avere raggruppamenti di attributi che molto probabilmente compariranno sempre insieme conferendo ai topic un “spessore semantico” potenzialmente apprezzabile per l’utilizzatore umano. Quindi, in definitiva, considerando i documenti come frutto di un processo generativo che porta a scegliere gli attributi in base a dei topic di interesse per il documento stesso, LDA stabilisce quali insiemi di parole hanno più probabilità di co-occorrenza e così facendo individua gli argomenti che hanno generato il documento.

Il modello LDA produce classificazioni più accurate dell’LSA e più robuste nei confronti di polisemia e rappresentazione su differenti livelli di astrazione (presenza di più topic). A differenza di LSA e dei modelli non probabilistici precedentemente analizzati, LDA consente di catturare le statistiche inter- ed intra-documento. Nel lavoro Comparing Latent Dirichlet Allocation and Latent Semantic Analysis as Classifier, sono forniti utili spunti comparativi.

Algoritmi basati sulla ricerca di topic latenti come LDA, a partire da una descrizione di tipo bag of words visuali di immagini, sono stati utilizzati con successo anche per applicazioni di classificazione, segmentazione e image retrieval. Grazie ad essi è possibile ottenere una riduzione della dimensionalità nella rappresentazione delle immagini e al tempo stesso inferire informazioni di più alto livello.

Per gensim:

# generazione del modello LDA
schema_lda = models.LdaModel(corpus_bow, num_topics=P, id2word=V)

# per visualizzare l'elenco dei topic
schema_lda.show_topics()

# per la visualizzazione grafica interattiva
vis_data = pyLDAvis.gensim.prepare(schema_lda, corpus_bow, V)
# oppure vis_data = pyLDAvis.gensim.prepare(schema_lda, corpus_bow, V, mds='tsne')
pyLDAvis.display(vis_data)

Nel blocco precedente la generazione del modello LDA ha richiesto una semplice istruzione. La visualizzazione dei topic, come negli esempi precedenti, può essere ottenuta mediante la funzione show_topics.

Per l’esplorazione visuale dei topic appare particolarmente conveniente l’impiego del tool pyLDAvis che mediante scalatura multidimensionale (MultiDimensional Scaling, MDS) propone una visualizzazione in due dimensioni dello spazio ridotto LDA e dei suoi topic. Il tool è in grado di supportare diversi algoritmi MDS, come per esempio la scalatura multidimensionale classica (classical multidimensional scaling) utilizzata come default o anche la t-distributed stochastic neighbor embedding (t-SNE), selezionabili impostando il parametro mds nella funzione prepare. Ulteriori approfondimenti su t-SNE possono essere trovati in Wikipedia su t-distributed stochastic neighbor embedding.

Per utilizzare pyLDAvis: fare click su un cerchio nel pannello di sinistra per selezionare un topic e il grafico a barre nel pannello di destra visualizzerà i 30 attributi più rilevanti9 per il topic selezionato. Le lunghezze delle barre rosse (proporzionali a p (w | t)) rappresentano la frequenza di un attributo in un dato topic; le lunghezza delle barre blu (proporzionali a p (w)) rappresentano la frequenza di un attributo nell’intero corpus. Modificando il valore di λ è possibile cambiare la classifica dei 30 attributi: piccoli valori di λ (vicino a 0) evidenziano gli attributi potenzialmente rari, ma esclusivi per il topic selezionato; grandi valori di λ (vicino a 1) evidenziano attributi frequenti, ma non necessariamente esclusivi, per il topic selezionato. Uno studio empirico proposto dagli ideatori di pyLDAvis, suggerisce che l’impostazione di λ a 0.6 può talvolta aiutare nell’interpretazione dei topic, ma questo ovviamente può variare con il dataset e i topic individuati da LDA. Per ulteriori approfondimenti su pyLDAvis, rinvio ad un post dedicato.

Fermo restando l’importanza dai dettagli matematici ed implementativi dei singoli algoritmi MDS, la scelta del tipo di scalatura può essere fatta, in prima istanza, sulla base delle visualizzazione risultante che meglio favorisce la nostra analisi. A titolo esemplificativo nelle figure seguenti sono mostrate le schermate di pyLDAvis applicate su un modello LDA generato da gensim, con MDS classica (prima figura) e t-SNE (seconda figura).

pyldavis-gensim-PCA-600

pyldavis-gensim-tsne-600

LDA in sklearn è così accessibile:

from sklearn.decomposition import LatentDirichletAllocation

def print_top_words(model, feature_names, n_top_words):
	for topic_idx, topic in enumerate(model.components_):
		print("Topic #%d:" % topic_idx)
		print(" ".join([feature_names[i] for i in topic.argsort()[:-n_top_words - 1:-1]]))
		print()

# generazione modello e proiezione del corpus BOW sullo spazio ridotto LDA (CASO 1)
lda = LatentDirichletAllocation(n_topics=P, random_state=0)
corpus_lda=lda.fit_transform(corpus_bow)

# generazione modello e proiezione del corpus TD-IDF sullo spazio ridotto LDA (CASO 2)
lda_tfidf = LatentDirichletAllocation(n_topics=P, random_state=0)
lda_tfidf.fit(corpus_tfidf)

# per la visualizzazione dei topic
print("\nTopics in LDA model (BOW projection):")
tf_feature_names = vectorizer.get_feature_names()
print_top_words(lda, tf_feature_names, 10)

print("\nTopics in LDA model (TF-IDF projection):")
print_top_words(lda_tfidf, tf_feature_names, 10)

# per la visualizzazione grafica interattiva
pyLDAvis.display(pyLDAvis.sklearn.prepare(lda, corpus_bow, vectorizer))
# oppure pyLDAvis.display(pyLDAvis.sklearn.prepare(lda_tfidf, corpus_tfidf, vectorizer))
Topics in LDA model (BOW projection):
Topic #0:
rete reti apprendimento neurali funzione essere puo piu modo modello
Topic #1:
sistemi approccio piu teoria rete collegamenti note com esempio ricerca
Topic #2:
funzione modello progetti vista rete relazioni neurali applicazioni solo funzioni
Topic #3:
inoltre prima rispetto ogni stato serie supporto funzioni piu essere
Topic #4:
modello contiene essere campo nome puo data mining informazioni piu
Topic #5:
conoscenza base inglese essere linguaggio esempio file isbn puo web
Topic #6:
data dati mining analisi essere ricerca tecniche informazioni applicazioni piu
Topic #7:
learning correlate voci esterni collegamenti machine linguaggio programmazione simile file
Topic #8:
apprendimento algoritmi dati automatico sistema esempi funzione insieme essere metodi
Topic #9:
intelligenza artificiale essere piu computer viene umano sistemi ricerca apprendimento

Topics in LDA model (TF-IDF projection):
Topic #0:
funzione caso analisi insieme possibile alcuni chiamato punto esempio inoltre
Topic #1:
rete reti learning collegamenti esterni note neurali standard automatico modello
Topic #2:
progetti modello funzione standard relazioni sistema vista tecniche funzioni ogni
Topic #3:
vista punto attivita stati scienze caso neurali comportamento livello piu
Topic #4:
file processo meno database contiene wikimedia progetti commons immagini piu
Topic #5:
conoscenza dati web base essere intelligenza stato piu artificiale esempio
Topic #6:
dati data mining modello utilizzato piu viene concetto informatica web
Topic #7:
network database inglese learning machine tipo contiene approccio programmazione risultati
Topic #8:
apprendimento algoritmi dati automatico insieme mining data ricerca relazioni esempi
Topic #9:
intelligenza artificiale primo viene inoltre john stato dopo prima rispetto

Come mostrato nel blocco precedente, il tool pyLDAvis può essere agevolmente usato anche sul modello LDA generato da sklearn. A titolo esemplificativo nelle figure seguenti è mostrata la schermata di pyLDAvis su un modello LDA generato da sklearn (con scalatura classica).

pyldavis-sklearn-PCA

[HDP]: In LDA, il parametro P è uno dei parametri di input del modello e la sua determinazione introduce un grado di incertezza ed esige prove. Nella variante di LDA chiamata Hierarchical Dirichlet Process (HDP), il numero P è determinato automaticamente dai dati, in particolare, è usata una distribuzione di Dirichlet anche per catturare l’incertezza su P. Non approfondiremo ulteriormente il modello, rinviando a Online Variational Inference for the Hierarchical Dirichlet Process per ulteriori dettagli.

Per gensim:

# generazione del modello HDP
schema_hdp = models.HdpModel(corpus_bow, id2word=V)

# proiezione di BOW sullo spazio ridotto di HDP
corpus_hdp=schema_hdp[corpus_bow]

# visualizzo l'elenco dei primi 10 topic (e per ogni topic i 10 attributi più rilevanti)
schema_hdp.print_topics(topics=10,topn=10)

# per la visualizzazione grafica interattiva
pyLDAvis.display(gensimvis.prepare(schema_hdp, corpus_bow, V))

Per approfondimenti:

  1. Le parole testuali (word token) sono un sottoinsieme di questi attributi.
  2. Nella locuzione “matrice documento-termine” la parola “termine” deve essere intesa in modo equivalente ad “attributo”.
  3. La base è un insieme di vettori grazie ai quali possiamo: ricostruire tutti i vettori dello spazio vettoriale mediante combinazioni lineari; costruire tutti i vettori in modo unico.
  4. pickle — Python object serialization
  5. Il nome SVD troncata (truncated SVD) descrive esattamente l’approccio, precedentemente analizzato, secondo cui non si considerano tutti i valori singolari scaturiti dalla SVD, ma solo i primi P (ossia i valori più grandi). I restanti valori singolari vengono invece posti a zero.
  6. Se N è il numero di documenti e P il numero di topic, avremo N distribuzioni di probabilità θd che possono essere assemblate in una matrice ΘNxP.
  7. Se M è il numero di attributi del vocabolario e P il numero di topic, avremo P distribuzioni di probabilità φp che possono essere assemblate in una matrice ΦPxM.
  8. In Probabilistic Topic Models di Steyvers e Griffiths si suggeriscono, su base sperimentale, le seguenti assegnazioni α=50/P e β=0.01 (dove P è il numero di topic). Per ogni distribuzione θ si utilizza il parametro α, invece, β è applicato in ogni distribuzione φ. I valori degli iperparametri rappresentano l’ipotetica distribuzione di probabilità a priori. Porre a 1 un iperparametro vuol dire assumere una distribuzione delle probabilità uniforme a priori. I due iperparametri
  9. Dato il parametro di pesatura 0 ≤ λ ≤ 1, la rilevanza è definita come λ * (p (w | t)) + (1 – λ) * log (p (w | t) / p (w)), dove p(w | t) e la probabilità di assegnazione dell’attributo w al topic t e p (w) è la probabilità dell’attributo w su tutti i topic.

Pubblicato da lorenzo

Full-time engineer. I like to write about data science and artificial intelligence.

Vuoi commentare?