Tech Blog

Topic Modelling con Gensim

Individuare in modo automatico i topic principali in un corpus di documenti

Daniele Caldarini
Data Scientist
5 minuti di lettura
machine learning, nlp, topic modelling, information extraction, gensim, unsupervised learning, python e nltk
Questo articolo è disponibile anche in English 🇬🇧

Questo articolo fa parte della serie dedicata ai più rilevanti task di Natural Language Processing. Approfondisci anche gli altri partendo dal primo articolo.

Ulteriore lettura che consiglio, riguarda un'introduzione sugli aspetti di base in ambito Machine Learning e nello specifico Machine learning e applicazioni per l'industria.

Cosa è il Topic Modelling

Con Topic Modelling si fa riferimento, appunto, ad uno specifico task di NLP (Natural Language Processing) che permette in modo automatico di individuare i principali argomenti trattati in un certo corpus documentale.
Si tratta di un task che segue un approccio di tipo non supervisionato, e come tale non necessita di un dataset etichettato.
Questo rende il Topic Modelling facile da realizzare e applicare.

Ogni topic individuato è descritto attraverso una serie di token e/o frasi rilevanti, ordinate per importanza, che ne descrivono la natura.
In questo contesto ogni documento è descritto da uno o più topic. Si parte dall'assunzione che ogni documento è rappresentato da una distribuzione statistica di argomenti, che si può ottenere "sommando" tutte le distribuzioni per tutti gli argomenti trattati.

I due principali metodi per realizzare approcci di Topic Modelling sono:

Vediamo come realizzare approcci di tipo Topic Modelling. Si procederà nel seguente modo:

  • Lettura e preprocessing dei contenuti testuali tramite l'ausilio della libreria NLTK
  • Costruzione di un Topic Model che utilizza la tecnica Latent Dirichlet Allocation, tramite l'utilizzo della libreria Gensim
  • Visualizzazione dinamica del risultato tramite la libreria pyLDAvis

Nel repository Github collegato alla serie di articoli NLP è presente un Jupyter Notebook che descrive l'intero processo.
Per questo esempio viene utilizzato un dataset di titoli di news, scaricabile da Kaggle in formato json.
Nel repository, per motivi di spazio, è presente solo un piccolo subset(in formato csv) dell'intero dataset, con news di specifiche categorie. Per utilizzare l'intero dataset, scaricarlo e utilizzare l'utility presente nel repository per trasformarlo in formato csv.

Preprocessing e trasformazione dei contenuti

Va inizialmente svolta una fase di preprocessing e trasformazione dei contenuti, per mettere il dataset nella forma migliore per l'addestramento del modello.

In ambito Topic Modelling è buona prassi realizzare i passi di trasformazione seguenti.

1. Tokenizzazione

Ogni documento va trasformato in una lista di token. Per fare ciò definiamo un'apposita funzione, utilizzando il Tokenizer della libreria NLTK.

nltk.download('punkt')

def tokenize(text):
    tokens = nltk.word_tokenize(text)
    return tokens
Source Code 1 - Tokenizzazione

2. Eliminazione delle stopword

Le stopword in un task come il Topic Modelling vanno eliminate, poiché non rappresenteranno in alcun modo informazione rilevante per l'individuazione e la descrizione dei topic.
Utilizziamo le stopword definite dalla libreria NLTK e definiamo una funzione che specifica se un token è una stopword.

nltk.download('stopwords')
en_stop = set(nltk.corpus.stopwords.words('english'))

def is_stopwords(token):
    if token in en_stop:
        return True
    return False
Source Code 2 - Eliminazione delle stopword

3. Lemmatizzazione

Risulta molto utile ricondurre ogni token di ogni documento alla propria radice. In caso contrario avremo token sintatticamente diversi, ma che esprimono lo stesso concetto semantico.
Per effettuare questo processo definiamo una funzione che ritorna la radice di una parola, e per farlo utilizziamo la versione NLTK dello Stemmer WordNetLemmatizer.

nltk.download('wordnet')
from nltk.stem.wordnet import WordNetLemmatizer

def get_lemma(token):
    return WordNetLemmatizer().lemmatize(token)
Source Code 3 - Lemmatizzazione

Mettiamo tutto insieme in un'unica funzione che verrà utilizzata per la trasformazione di ogni contenuto del dataset.
In aggiunta alle operazioni sopra indicate trasformiamo in carattere minuscolo ogni token e eliminiamo quelli di lunghezza minore o uguale a 3.

def transform(text):
    tokens = tokenize(summary)
    tokens = [token.lower() for token in tokens]
    tokens = [token for token in tokens if len(token) > 3]
    tokens = [token for token in tokens if token not in en_stop]
    tokens = [get_lemma(token) for token in tokens]
    return tokens
Source Code 4 - Funzione per la trasformazione dei dati

A questo punto possiamo leggere il contenuto del nostro file csv, estrarre i titoli in una lista e trasformare ogni contenuto nella forma desiderata.

data.read_csv("../data/data.csv")

text_list = data["headline"].tolist()

cleaned_summary_list = [transorm(element) for element in text_list]
Source Code 5 - Lettura e trasformazione dei dati

LDA con Gensim

Dizionario e Corpus vettoriale

Per costruire il nostro Topic Model utilizziamo l'implementazione della tecnica LDA della libreria Gensim.
Come primo passo costruiamo un vocabolario partendo dai nostri dati trasformati. Segue la trasformazione dei dati in un modello vettoriale di tipo Tf-Idf.
Salviamo il dizionario e il corpus per utilizzi futuri.

dictionary = corpora.Dictionary(cleaned_summary_list)
corpus = [dictionary.doc2bow(text) for text in cleaned_summary_list]

tfidf = models.TfidfModel(corpus)
corpus_tfidf = tfidf[corpus]

pickle.dump(corpus_tfidf, open('../models/corpus.pkl', 'wb'))
dictionary.save('../models/dictionary.gensim')
Source Code 6 - Dizionario e Corpus vettoriale

Addestramento del modello

Trasformato il nostro corpus, passiamo all'addestramento del Topic Model.

Nell'applicazione della tecnica LDA, tra i diversi parametri che possono essere specificati, ve ne sono alcuni di maggiore rilevanza:

  • corpus: Corpus vettoriale di documenti da utilizzare per il training
  • num_topics : Numero di topic da estrarre
  • id2word : Dizionario che definisce il mapping degli id con le parole
  • passes : Numero di passaggi attraverso l'intero corpus durante il training

Vi sono altri parametri che per semplicità non specifichiamo e per i quali utilizziamo i valori di default.

Per tutte le informazioni circa il modello LDA, consultare il document Latent Dirichlet Allocation.

A questo punto passiamo ad addestrare il nostro modello.

import gensim

num_topics = 5

ldamodel = gensim.models.ldamodel.LdaModel(corpus, num_topics=num_topics, id2word=dictionary, passes=15)
ldamodel.save('model5.gensim')
Source Code 7 - Addestramento del modello

Terminato l'addestramento salviamo il modello e stampiamo per ogni topic individuato una rappresentazione data dalle keyword più rilevanti appartenenti al topic.

topics = ldamodel.print_topics(num_words=5)
for topic in topics:
    print(topic)


(0, '0.016*"shooting" + 0.012*"police" + 0.009*"killed" + 0.006*"prison" + 0.006*"photo" + 0.005*"suspect" + 0.005*"school" + 0.005*"arrested" + 0.005*"killing" + 0.005*"olympics"')
(1, '0.023*"apple" + 0.013*"space" + 0.012*"iphone" + 0.011*"photo" + 0.009*"video" + 0.008*"planet" + 0.007*"rumor" + 0.007*"science" + 0.006*"first" + 0.006*"earth"')
(2, '0.009*"found" + 0.007*"allegedly" + 0.007*"police" + 0.007*"woman" + 0.006*"study" + 0.006*"video" + 0.005*"player" + 0.004*"arrested" + 0.004*"football" + 0.004*"google"')
(3, '0.010*"facebook" + 0.010*"state" + 0.008*"video" + 0.006*"people" + 0.006*"world" + 0.006*"google" + 0.006*"final" + 0.005*"tournament" + 0.005*"player" + 0.004*"score"')
(4, '0.053*"video" + 0.015*"watch" + 0.010*"youtube" + 0.007*"show" + 0.005*"photo" + 0.005*"tiger" + 0.005*"world" + 0.005*"touchdown" + 0.004*"study" + 0.004*"lebron"')
Source Code 8 - Topic individuati

Utilizzo del modello addestrato

Abbiamo quindi addestrato un Topic Model, e salvato il risultato insieme al corpus e al dizionario. In questo modo possiamo riutilizzare nel nostro codice il nostro modello per estrarre i topic trattati in un nuovo documento.
Diamo in input al modello un titolo di test e otteniamo come risultato una distribuzione nello spazio dei topic.

new_headline = 'Two person murdered in New York'
new_headline = transform(new_headline)
new_headline_bow = dictionary.doc2bow(new_doc)

print(ldamodel.get_document_topics(new_doc_bow))

[(0, 0.3996785),
 (1, 0.06758379),
 (2, 0.39854106),
 (3, 0.06670547),
 (4, 0.06749118)]
Source Code 9 - Utilizzo del modello

Estraiamo a questo punto il topic con lo score maggiore. In questo caso è il topic con id 0. Ne restituiamo allora la rappresentazione tramite le principali keyword.

ldamodel.print_topic(0, topn=5)

'0.016*"shooting" + 0.012*"police" + 0.009*"killed" + 0.006*"prison" + 0.006*"photo" + 0.005*"suspect" + 0.005*"school" + 0.005*"arrested" + 0.005*"killing" + 0.005*"olympics"'
Source Code 10 - Topic principale

Aggiornamento del modello

In un contesto reale ci aspettiamo di avere a disposizione nel corso del tempo nuovi contenuti, i quali rappresentano un'informazione utile per migliorare le prestazioni del modello.
Potremmo scegliere di riaddestrare il modello da capo, utilizzando il corpus completato con i nuovi contenuti.
La libreria Gensim però ci offre anche la possibilità di riaddestrare il modello in maniera parziale e più rapida. Caricando il modello più recente da file, possiamo creare il corpus con i nuovi documenti e fare successivamente un update del modello, che possiamo salvare e utilizzare al posto della versione precedente.

temp_file = "../models/model.h5"

# Load a potentially pretrained model from disk.
lda = models.LdaModel.load(temp_file)

new_data = df.read_csv("../data/new_data.csv")
new_text_list = new_data["headline"].tolist()

new_cleaned_summary_list = [transform(element) for element in text_list]

dictionary = corpora.Dictionary(new_cleaned_summary_list)
new_corpus = [dictionary.doc2bow(text) for text in cleaned_summary_list]

tfidf = models.TfidfModel(new_corpus)  # step 1 -- initialize a model
corpus_tfidf = tfidf[new_corpus]

lda.update(corpus_tfidf)
Source Code 11 - Update del modello

Visualizzazione con pyLDAvis

PyLDAvis permette di visualizzare su un ambiente interattivo (un Jupyter Notebook ad esempio) quelli che sono i topic individuati dal Topic Model creato. Ci carichiamo il modello appena addestrato, insieme alla rappresentazione vettoriale del corpus e al dizionario, e chiediamo a pyLDAvis di visualizzare i risultati.

import pyLDAvis
import pyLDAvis.gensim_models as gensimvis

pyLDAvis.enable_notebook()

dictionary = gensim.corpora.Dictionary.load('dictionary.gensim')
corpus = pickle.load(open('corpus.pkl', 'rb'))

lda = gensim.models.ldamodel.LdaModel.load('model5.gensim')
lda_display = gensimvis.prepare(lda, corpus, dictionary, sort_topics=False)

pyLDAvis.display(lda_display)
Source Code 12 - Visualizzazione con pyLDAvis

Attraverso il metodo display della libreria generiamo un pannello interattivo che visualizza sulla sinistra una rappresentazione nello spazio dei topic, sulla destra una visualizzazione su quelli che sono i concetti e le parole chiavi principali per il topic selezionato.

I topic tra loro vicini o in sovrapposizione saranno topic simili tra loro.

Risulta possibile interagire con il pannello attraverso opportuni comandi che permettono di:

  • Scegliere quali e quanti topic visualizzare
  • Settare l'opportuna Saliency, cioè la misura di quanto il termine ti dice su quel topic
  • Settare l'opportuna Relevance, cioè una misura dell'importanza della parola per quel topic, rispetto al resto dei topic.
Figura 1 - Visualizzazione con pyLDAvis
Figura 1 - Visualizzazione con pyLDAvis

Conclusioni

Abbiamo potuto constatare come il Topic Modelling sia una tecnica molto semplice e facile da applicare, poiché non necessità di dati etichettati. Tutto quello di cui si ha bisogno è di un corpus di documenti a cui avere accesso sul quale poter addestrare il proprio modello.
Abbiamo visto inoltre come sia molto semplice riaddestrare un modello con i nuovi dati acquisiti. Molto spesso si può procedere attraverso raffinamenti molto rapidi e frequenti del modello considerando solo i nuovi dati, e solo ad intervalli più ampi decidere di riaddestrare il modello da capo.
Questa tecnica, come abbiamo visto, dà come risultato, dato un documento i topic trattati, e per ognuno di questi è possibile ottenere le keyword principali che lo descrivono.
Otteniamo in questo modo delle informazioni in maniera automatica e intelligente sulla natura del testo analizzato. Tutto ciò può rivelarsi molto utile per applicazioni come:

  • motori di ricerca cognitivi: si taggano i contenuti con le parole chiave dei topic principali, per sfruttarle poi in fase di ricerca
  • customer service: si riesce ad individuare i topic principali discussi dai clienti e capirne più facilmente e rapidamente i bisogni
  • social network: per individuare gli argomenti principali trattati dagli utenti
  • sentiment analysis: per individuare concetti che sono espressione del sentiment del contenuto
scritto da
Daniele Caldarini
Data Scientist
Ricercatore e Data Scientist in SMC. Laureato in Ingegneria Informatica all'Università di Roma Tre, ricerca e sviluppa in ambito intelligenza artificiale e machine learning con l'obiettivo di realizzare prodotti moderni e al passo con i tempi.

Potrebbero interessarti anche…