Scoprire anomalie e pattern interessanti in dati testuali

Il TF-IDF è una tecnica di vettorizzazione molto conosciuta e documentata nel data science. La vettorizzazione è l'atto di convertire un dato in un formato numerico in modo tale che un modello statistico possa interpretarlo e fare le predizioni.

In questo articolo vedremo come convertire un corpus di testi in formato numerico e applicheremo degli algoritmi di apprendimento automatico per far emergere pattern e anomalie interessanti.

Metodologia

Utilizzeremo un dataset fornito da Sklearn per avere un corpus di testo replicabile. Dopodiché, useremo l'algoritmo KMeans per raggruppare i vettori generati dal TF-IDF. Useremo poi la Principal Component Analysis per visualizzare i nostri gruppi e far emergere caratteristiche comuni o inusuali dei testi presenti nel nostro corpus.

Ecco una scaletta

  1. Importiamo il dataset
  2. Applichiamo preprocessing al nostro corpus, così da rimuovere parole e simboli che, se convertiti in formato numerico, non aggiungono valore al nostro modello
  3. Usiamo il TF-IDF come algoritmo di vettorizzazione
  4. Applichiamo KMeans per raggruppare i nostri dati
  5. Applichiamo PCA per ridurre la dimensionalità dei nostri vettori a 2 per visualizzazione
  6. Interpreteremo i dati e inseriremo le nostre considerazioni in un report finale

L'Analisi

Il Dataset

Per questo esempio useremo l'API di Scikit-Learn, sklearn.datasets che permette di accedere ad un famoso dataset per analisi linguistiche, il 20 newsgroups. Un newsgroup è un gruppo di discussione di utenti online, ad esempio un forum. Sklearn permette di accedere a diverse categorie di contenuto. Useremo i testi che hanno a che vedere con la tecnologia, la religione e lo sport.

Se accediamo al primo elemento con dataset['data'][0] vediamo

They tried their best not to show it, believe me. I'm surprised they couldn't find a sprint car race (mini cars through pigpens, indeed!) on short notice.
George

√ą possibile che il vostro dato sia diverso a causa del shuffle=True, che randomizza l'ordine degli elementi del dataset. Il numero di elementi nel nostro dataset √® 3451.

Creiamo un dataframe Pandas dal nostro dataset

Il nostro dataset di lavoro

Notiamo come siano presenti "\n", "===" e altri simboli che andrebbero rimossi per addestrare correttamente il nostro modello.

Preprocessing

Insieme ai simboli menzionati, vogliamo anche le stopword. Quest'ultime sono una serie di parole che non aggiungono informazioni al nostro modello. Un esempio di stopword in inglese sono gli articoli, le congiunzioni e così via.
Useremo la libreria NLTK e importiamo le stopword per visualizzarle

>>> ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're"]

Ora creiamo una funzione preprocess_text che prende in input un testo e restituisce una versione pulita dello stesso.

Ecco un lo stesso documento precedente, stavolta pulito

tried best show believe im surprised couldnt find sprint car race mini cars pigpens indeed short notice george

Applichiamo la funzione a tutto il dataframe

Serie aggiunta al nostro data frame con il testo pulito

Ora siamo pronti alla vettorizzazione.

Vettorizzazione con TF-IDF

Il TF-IDF converte in un formato numerico il nostro corpus facendo emergere termini specifici, pesando diversamente i termini molto rari o molto comuni in modo da assegnare loro un punteggio basso.
TF sta per term frequency, mentre IDF sta per inverse document frequency. Il valore TF-IDF aumenta proporzionalmente al numero di volte che una parola appare nel documento ed è compensato dal numero di documenti nel corpus che contengono quella parola.

Visualizzazione del TF-IDF: il termine in verde ottiene un punteggio alto poiché specifico

Con questa tecnica di vettorizzazione siamo quindi in grado di raggruppare i nostri documenti considerando i termini pi√Ļ importanti che li costituiscono. Per leggere di pi√Ļ su come funziona il TF-IDF, leggere qui.

√ą facile applicare il TF-IDF con Sklearn:

X è la matrice di vettori che verrà usata per addestrare il modello KMeans. Il comportamento predefinito di Sklearn è quello di creare una matrice sparsa. La vettorizzazione genera dei vettori simili a questo

vettore_a = [1.204, 0, 0, 0, 0, 0, 0, ..., 0]

Il vettore è composto da un singolo valore non uguale a 0. In Sklearn, una matrice sparsa non è altro che una matrice che indicizza la posizione dei valori e degli 0 invece di memorizzarla come una qualsiasi altra matrice. Questo è un meccanismo che serve a risparmiare RAM e potenza di calcolo. La comodità è che la matrice sparsa è accettata dalla maggior parte degli algoritmi di machine learning, come anche il KMeans. Infatti quest'ultimo userà i dati presenti nella matrice sparsa per trovare gruppi e pattern.

Se usiamo X.toarray() vediamo di fatto la matrice completa, non sparsa.

Rappresentazione vettoriale della matrice sparsa

Implementazione del KMeans

Il KMeans √® uno degli algoritmi non supervisionati pi√Ļ conosciuti e usati nel mondo del data science e serve a raggruppare in un numero definito di gruppi un insieme di dati. L'idea alla base √® molto semplice: l'algoritmo inizializza delle posizioni a caso (chiamati centroidi, i punti rossi, blu e verdi nello screenshot in basso) nel piano vettoriale e assegna il punto al centroide pi√Ļ vicino.

Primo step del KMeans - inizializzazione dei centroidi
Screenshot preso da https://www.youtube.com/watch?v=R2e3Ls9H_fc

L'algoritmo calcola la posizione media (o, se aiuta l'interpretazione, il "centro di gravità") dei punti e sposta il rispettivo centroide in quella posizione e aggiorna il gruppo di appartenenza di ogni punto. L'algoritmo converge quando tutti i punti sono alla distanza minima dal rispettivo centroide.

Convergenza del KMeans - scoperta dei gruppi
Screenshot preso da https://www.youtube.com/watch?v=R2e3Ls9H_fc

Fatta questa doverosa introduzione, continuiamo con il nostro progetto andando a usare nuovamente Sklearn

Con questo snippet di codice abbiamo addestrato il KMeans con i vettori restituiti dal TF-IDF e abbiamo assegnato i gruppi che ha trovato ad alla variabile clusters

Ora possiamo procedere alla visualizzazione dei nostri gruppi e valutare la loro segmentazione e/o presenza di anomalie.

Riduzione della dimensionalità e visualizzazione

Abbiamo la nostra X dal TF-IDF e abbiamo un modello KMeans e relativi cluster. Ora vogliamo mettere insieme questi due pezzi per visualizzare in quale gruppo va quale testo.

Come ben sappiamo, un grafico si presenta solitamente in 2 dimensioni e raramente in 3. Sicuramente non possiamo visualizzarne di pi√Ļ. Se andiamo a vedere la dimensionalit√† di X con X.shape vediamo che essa √® (3451, 7390). Ci sono 3451 vettori (uno per testo), ognuno con 7390 dimensioni. Impossibile visualizzarle!

Fortunatamente per noi, esiste una tecnica che si chiama PCA (Principal Component Analysis) che riduce la dimensionalità di un set di dati ad un numero arbitrario preservando la maggior parte delle informazioni contenute in esse.

‚ÄćCome per il TF-IDF, non andr√≤ nel dettaglio del suo funzionamento, e potete leggere di pi√Ļ qui sul suo funzionamento. Al fine di questo articolo, ci basti sapere che la PCA¬†tende a preservare le dimensioni che meglio riassumono la variabilit√† totale dei nostri dati, andando a rimuovere dimensioni che contribuiscono poco a quest'ultima.

La nostra X andrà da 7390 dimensioni a 2. Sklearn.decomposition.PCA è quello che ci occorre.

Le due componenti principali generati dalla PCA

Se ora vediamo la dimensionalità di x0 e x1 vediamo che sono rispettivamente (3451,), quindi un punto (x0,x1) per testo. Questo ci da la possibilità di creare un grafico a dispersione.

Visualizzare i gruppi

Prima di creare il nostro grafico andiamo ad organizzare meglio il nostro dataframe andando a creare una colonna cluster, x0, x1

Dataset pronto per la visualizzazione dopo KMeans e PCA

Vediamo quali sono le keyword pi√Ļ rilevanti per ogni centroide

Keyword per gruppo

Bene! Vediamo come il KMeans abbia correttamente creato 3 gruppi distinti, uno per ogni categoria presente nel dataset. Il cluster 0 si riferisce allo sport, il cluster 2 al software / tech, il cluster 3 alla religione. Applichiamo questa mappatura

Procediamo con la libreria Seaborn per visualizzare in maniera molto semplice i nostri testi raggruppati.

Raggruppamento di testi usando TF-IDF e KMeans. Ogni punto è un testo vettorizzato appartenente ad una categoria definita

Come è possibile vedere, il clustering ha funzionato bene: tre gruppi distinti come lo sono i gruppi definiti a priori dal nostro dataset. Immaginate la potenza di questo approccio nel trovare gruppi in dati non etichettati a priori!

Interpretazione

L'interpretazione è abbastanza semplice: non sono presenti anomalie particolari, tranne per il fatto che ci sono dei testi appartenenti alla categoria tecnologia che si mischiano leggermente con quelli dello sport, tra il confine blu scuro e verde acceso. Questo è dovuto alla presenza di termini comuni tra alcuni di questi testi che quando vettorizzati ottengono valori uguali per alcune dimensioni.

Come follow-up sarebbe interessante andare ad investigare come sono scritti questi testi e capire se la motivazione ipotizzata sia fondata o meno.

Il Codice

Ecco tutto il codice completo, in formato copia-incolla

‚Äć

Andrea D'Agostino
Ciao, sono Andrea D'Agostino e sono un data scientist con 6 anni di esperienza nel campo della business intelligence. Applico tecniche statistiche e di machine learning per aiutare i clienti a trovare e risolvere problemi nei loro asset digitali e a sfruttare le debolezze dei competitor a loro vantaggio.

Sono il fondatore e l'autore di questo blog, il cui obiettivo √® raccogliere le informazioni pi√Ļ importanti che ho imparato durante il mio percorso lavorativo e accademico al fine di poter aiutare il lettore a migliorare le sue analisi.