In questo tutorial vedremo come trainare un modello Word2Vec per la lingua italiana sul corpus di Wikipedia.
Esistono moltissimi modelli pretrainati per l’italiano (ad esempio ce ne sono diversi inclusi in Spacy), ma penso che, almeno una volta, possa essere interessante vedere come si fa ad allenarne uno da zero.
Il train di un modello Word2Vec è molto semplice, eppure in rete si trovano pochissimi tutorial che spiegano come fare (e ovviamente nessuno di questi è in italiano), di conseguenza ho pensato che potesse essere utile scrivere questo post.
Non parlerò del modello in sè, sia per non rendere questo articolo eccessivamente lungo (magari in futuro ne scriverò uno ad hoc), sia perché in rete ci sono già moltissime risorse sull’argomento.
Requisiti
Useremo la libreria gensim, che puoi installare sia con pip che con conda:
pip install gensim -y
:: oppure:
conda install -c conda-forge gensimIl train del modello è oneroso sia dal punto di vista computazionale che (soprattutto) da quello della memoria. Per questo motivo sconsiglierei di utilizzare un computer con meno di 16GB di RAM.
Corpus
Wikimedia fornisce dei dumps periodici di tutte le versioni di Wikipedia (e non solo). I dump della versione italiana si possono scaricare al seguente indirizzo:
https://dumps.wikimedia.org/itwiki/latest/
Nella repository ci sono molti files contenenti diversi tipi di backup. Quello che ci interessa è il file completo dell’ultimo snapshot della Wikipedia in itaiano, itwiki-latest-pages-articles.xml.bz2:

Possiamo scaricarlo nella nostra cartella locale usando la libreria nativa requests:
import os
import urllib.request
wikipedia_dump_url = 'https://dumps.wikimedia.org/itwiki/latest/itwiki-latest-pages-articles.xml.bz2'
local_filename = os.path.join(os.getcwd(), 'itwiki-latest-pages-articles.xml.bz2')
urllib.request.urlretrieve(wikipedia_dump_url, local_filename)Il file è molto grande (circa 3.4 GB mentre scrivo, ma cresce ogni giorno), per cui ci vorrà un po’ di tempo. Al termine avremo, nella nostra cartella locale, un file xml zippato chiamato itwiki-latest-pages-articles.xml.bz2.
Pre-processing
Abbiamo ottenuto il dump completo di wikipedia, completo di testo, hyperlink, collegamenti esterni, note, link ad immagini, etc.
Per renderlo digeribile al modello dovremo processare il file al fine di estrarne il solo testo. Ho scritto una funzione, basata sulla classe WikiCorpus di gensim, che semplifica al massimo questa operazione:
import logging
from gensim.models import Word2Vec
logger = logging.getLogger()
def make_corpus(in_f, out_f):
"""
Funzione per convertire il file dump di Wikipedia in puro testo.
Parameters:
- in_f: path al file di input
- out_f: path al file di output
Return:
- esito: True/False
"""
logger.info("Opening dump file")
output = open(out_f, 'w')
logger.info("Parsing the data")
wiki = WikiCorpus(in_f)
i = 0
for text in wiki.get_texts():
output.write(bytes(' '.join(text), 'utf-8').decode('utf-8') + '\n')
i = i + 1
if (i % 100000 == 0):
logger.info(f'Processed {str(i)} articles')
output.close()
logger.info('Processing complete!')
return True
make_corpus('itwiki-latest-pages-articles.xml.bz2',
'Wikipedia.txt')Al termine di questa operazione otterremo, sempre nella nostra cartella di lavoro, un file chiamato Wikipedia.txt.
Questo file conterrà l’intero testo di wikipedia, un articolo su ogni riga, senza la punteggiatura e tutto in minuscolo.
Train
Possiamo passare all’allenamento vero e proprio. Anche in questa fase ci viene in aiuto la libreria gensim, nello specifico la classe Word2Vec, che possiamo inizializzare nel modo seguente:
model = Word2Vec(LineSentence('Wikipedia.txt'), # Corpus preprocessato
vector_size=100, # Dimensione dello spazio di embedding
window=5, # Allena considerando una finestra di 5 parole sul testo
min_count=5, # esclude tutti i termini che compaiono meno di 5 volte
epochs=10, #Numero di epoche su cui allenare
workers=multiprocessing.cpu_count()) #usa tutti i processoriGli argomenti forniti al costruttore in questo codice di esempio sono:
- Il corpus. Ho utilizzato la funzione LineSentence per creare un iteratore che legga e passi al modello una frase per volta, per questioni di RAM
- vector_size => la dimensione dello spazio vettoriale sul quale saranno definite le rappresentazioni vettoriali delle parole
- window => la dimensione del contesto da utilizzare
- min_count => ignora ogni parola del corpus che compaia meno di min_count volte
- epochs => numero di epoche
- workers => numero di core su cui parallelizzare il train
Per una lista completa delle opzioni che si possono settare consiglio di dare un’occhiata alla documentazione ufficiale.
Valutazione dei risultati
Il modello Word2Vec definisce una ipersfera sulla cui superficie sono definite le rappresentazioni vettoriali delle parole del dizionario. In altri termini, ogni parola è rappresentata da un vettore di modulo unitario.
Tali vettori possono essere ricavati facilmente dal modello appena allenato:
word_vectors = model.wv
word_vectorsCi aspettiamo che la topologia definita su questo spazio vettoriale sia rappresentativa delle relazioni semantiche tra le parole, cioé che termini di significato simile (quindi “vicini tra loro” in un’ipotetico spazio semantico) siano vicine anche nello spazio vettoriale definito dal Word2Vec, e viceversa.
Ciò può essere verificato qualitativamente scegliendo alcune parole e verificando le relazioni che esistono tra i rispettivi vettori.
Nel codice di questo tutorial ho scritto due funzioni:
- most_similar => data una parola, restituisce le 10 paroli del dizionario i cui vettori hanno la massima cosine similarity con la parola in esame.
- find_analogies => verifica la consistenza tra le analogie tra due coppie di parole e le corrispondenti operazioni vettoriali tra le loro rappresentazioni.
Codice
Trovate il codice descritto in questo post a questo link.