Questo articolo mostrerà come preparare dei testi per applicazioni basate su reti neurali.

Dato un corpus di dati testuali, applicheremo il seguente processo

  1. mappatura della singola parola del testo (token) ad un indice
  2. creazione di token specifici per fine frase, padding e simboli non appartenenti al vocabolario
  3. conversione dei termini presenti in dizionario in tensori

Il vocabolario organizzerà i nostri dati in formato chiave: valore dove la chiave sarà il termine e il valore sarà un indice numerico intero associato a quel termine. Saranno presenti dei token speciali che avranno queste caratteristiche

  • __PAD__: indica il simbolo del padding
  • </e>: indica end of line (fine della frase)
  • __UNK__: indica un simbolo sconosciuto, non appartenente al dizionario

Questo lavoro sarà molto utile perché fornirà un template per creare l'input al nostro generatore di dati (data generator in gergo del deep learning) in maniera facile ed efficiente.

Il Dataset

Useremo il dataset fornito da Sklearn, 20newsgroups, per avere rapido accesso ad un corpus di dati testuali. A scopo dimostrativo, userò solo un campione di 10 testi.

import numpy as np
from sklearn.datasets import fetch_20newsgroups

# categorie dalle quali prenderemo i nostri dati
categories = [
 'comp.graphics',
 'comp.os.ms-windows.misc',
 'rec.sport.baseball',
 'rec.sport.hockey',
 'alt.atheism',
 'soc.religion.christian',
]

# primi 10 elementi
dataset = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, remove=('headers', 'footers', 'quotes'))
corpus = [item for item in dataset['data'][:10]]
corpus

Su questi testi applicheremo un preprocessing semplice, per pulire le frasi da stop word e caratteri speciali.

Il Processo

Funzione di preprocessing

La funzione di preprocessing è la seguente

import re
import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
nltk.download('punkt')

def preprocess_text(text: str) -> str:
    """Funzione che pulisce il testo in input andando a
    - rimuovere i link
    - rimuovere i caratteri speciali
    - rimuovere i numeri 
    - rimuovere le stopword
    - trasformare in minuscolo
    - rimuovere spazi bianchi eccessivi
    Argomenti:
        text (str): testo da pulire
    Restituisce:
        str: lista di token puliti
    """
    # rimuovi link
    text = re.sub(r"http\S+", "", text)
    # rimuovi numeri e caratteri speciali
    text = re.sub("[^A-Za-z]+", " ", text)
    # rimuovere le stopword
    # 1. crea token
    tokens = nltk.word_tokenize(text)
    # 2. controlla se è una stopword
    tokens = [w.lower() for w in tokens if not w in stopwords.words("english")]
  
    return tokens

Questa funzione è usabile per qualsivoglia problema di elaborazione del linguaggio naturale, non solo questo presente nell'articolo.

Creazione del vocabolario

Lavorare con un vocabolario ci permette di mappare termine ad indice. L'indice sarà l'elemento che verrà convertito a tensore.

def get_vocab(training_corpus):

    # includiamo caratteri speciali aggiuntivi
    # padding, fine di linea, termine sconosciuto
    vocab = {'__PAD__': 0, '__</e>__': 1, '__UNK__': 2} 

    # costruiremo il vocabolario solo con i dati di training
    for item in training_corpus: # iteriamo nel nostro corpus
        processed_text = preprocess_text(item) # applichiamo preprocessing al testo
        for word in processed_text: # per ogni parola contenuto nel testo (token)
          if word not in vocab: # se la parola non è presente nel dizionario
              vocab[word] = len(vocab) # crea una chiave che è uguale al termine, e il suo valore è uguale alla lunghezza del vocabolario
    return vocab

vocab = get_vocab(corpus)

L'output sarà il seguente

Da testo a tensori

Scriveremo una funzione che utilizzerà il dizionario per creare una rappresentazione numerica dei termini presenti in quest'ultimo. Questi numeri saranno inseriti in una lista e rappresenteranno i tensori da applicare in un task di deep learning.

Non utilizzeremo TensorFlow, PyTorch o Numpy per questo esempio, così da spiegare fondamentalmente come avviene il processo.

Partiremo dalla funzione text_to_tensor

def text_to_tensor(text: str, vocab_dict: dict, unk_token='__UNK__', verbose=False):
    '''
    Argomenti: 
        text - stringa contenente il testo
        vocab_dict - il dizionario di termini
        unk_token - il carattere speciale usato per identificare i termini sconosciuti
        verbose - stampaggio di messaggi di debug
    Restituisce:
        tensor_l - una lista di indici che rappresentano numericamente il nostro testo
    '''     
    word_l = preprocess_text(text)
    
    if verbose:
        print("Lista delle parole presenti nel testo:")
        print(word_l)
        
    # Inizializziamo una lista vuota che conterrà i tensori
    tensor_l = [] 
    
    # Prendiamo il valore di __UNK__ token 
    unk_ID = vocab_dict[unk_token]
    
    if verbose:
        print(f"Il valore di UNK è {unk_ID}")
        
    # per ogni parola nella lista:
    for word in word_l:
        # prendiamo il suo indice
        # e se la parola non è presente nel dizionario, usiamo UNK ID
        word_ID = vocab_dict.get(word, unk_ID)
        # inseriamo il valore nella lista finale
        tensor_l.append(word_ID)

    return tensor_l

Ora possiamo procedere a usare i nostri tensori per creare un batch_generator e un modello di deep learning, come una LSTM.