L’italiano, come lingua romanza, presenta una morfologia fortemente flessa, con aggettivi, pronomi, congiunzioni e verbi che cambiano radicalmente forma a seconda di genere, numero, tempo e modale. Questa caratteristica complica la tokenizzazione automatica, poiché una semplice divisione per spazi o regole lessicali standard non preserva radici né affissi critici. A differenza delle lingue germaniche, dove la tokenizzazione è spesso basata su spazi, l’italiano richiede approcci morfologici avanzati per evitare frammentazioni errate o perdita di semanticità.
Per preservare il significato, è fondamentale adottare stemming morfologicamente consapevole, che mantiene le radici morfemiche anche quando vengono applicati suffissi o aggettivazioni. Librerie come *Stanza* (basata su spaCy) o *Lexia* integrano modelli linguistici specifici per il italiano, riconoscendo radici di verbi, aggettivi e pronomi con alta precisione. Ad esempio, il lemma di “dà**va**” → “dare” e “stati” → “stato” garantisce che le forme flesse siano normalizzate senza perdere la connessione semantica.
Per una tokenizzazione avanzata, si consiglia di: 1. Caricare un modello linguisticamente arricchito per l’italiano (es. `it_news_sm` o `it_core_news_sm`). 2. Applicare una fase di stemming limitato tramite regole o *Rule-based Stemmer* per radici morfemiche, evitando l’over-stemming su forme flesse. 3. Utilizzare *subword tokenization* tramite *SentencePiece* addestrato su corpora italiani (es. *Italian Wikipedia*), con parametri adattati per preservare morfemi chiave. 4. Integrare un dizionario personalizzato per contrazioni comuni: “al’” → “al’”, “d’oggi” → “d’oggi”, gestito con espressioni regolari (regex) e match espliciti prima della tokenizzazione.
import re import spacy from sentencepiece import SentencePieceTokenizer
# Carica modello spaCy italiano nlp = spacy.load(“it_news_sm”)
# Tokenizzazione base con spaCy doc = nlp(“È arrivato al centro e ha parlato con i cittadini di ieri.”)
tokens_raw = [token.text for token in doc]
# Regola regex per normalizzare contrazioni comuni pattern = re.compile(r'(al’|d’)oggi|è stato|viene|viene)’, re.IGNORECASE) def normalize_contraction(match): return match.group(1) + match.group(2) if match.group(1) else match.group(0)
tokens_normalized = [pattern.sub(normalize_contrazione, token) for token in tokens_raw]
# Tokenizzazione subword con SentencePiece (esempio parametri) sp = SentencePieceTokenizer(“italian_tokenizer.model”, model_type=”bPE”) tokens_sub = [token for token in sp.encode(tokens_normalized, out_type=str)]
# Filtro lunghezza minima (escludere token < 2 caratteri) tokens_filtered = [t for t in tokens_sub if len(t) >= 2]
# Disambiguazione contesto semantico (esempio: “è stato” → “è” + “stato”) fase3 = {} for i, token in enumerate(doc): if token.text == “è” and doc[i+1].text in [“centro”, “tempo”, “stato”]: if i+1 < len(doc): fase3[f”token_{i}_fixed”] = f”{token.text}{doc[i+1].text}” # Aggiornamento documento virtuale (non modificato fisicamente) doc[i+1].lemma_ = fase3[f”token_{i}_fixed”] else: doc[i+1].lemma_ = token.lemma_
# Risultato finale: # “È arrivato al centro e ha parlato con i cittadini di ieri.” # → [“È”, “arrivato”, “al”, “centro”, “e”, “ha”, “parlato”, “con”, “i”, “cittadini”, “di”, “ieri”]
– **Errore**: Token split errato di “città” in “citt” + “a” per conta di regex generiche. **Soluzione**: regex specifiche per forme italiane: `città(?=s)` → token unico “città” invece di “citt” + “a”. – **Errore**: “viene” tokenizzato come “vene” in contesti verbi. **Soluzione**: filtro contestuale tramite parser morfologico o modelli linguistici addestrati su corpora reali (es. *Corpus del Parlante*). – **Errore**: abbreviazioni non riconosciute (“Via” → “Viale” senza contesto). **Soluzione**: mappatura contestuale basata su geolocalizzazione o dizionari di contesto (es. “Via Roma” → “Viale Roma” solo se indicato).
Fase 1: **Estrazione morphosignature con parser morfologici** Utilizzando strumenti come *Linguametrica* o *Lexia*, si estraggono le morphosignature di ogni token: radice, genere, numero, tempo, modo. Ad esempio, “è stato” → (radice: “stato”, genere: maschile, numero: singolare, tempo: passato prossimo). Questo permette di identificare con precisione i ruoli grammaticali e guidare la tokenizzazione contestuale.
Fase 2: **Filtro di lunghezza minima token** Si esclude qualsiasi token con meno di 2 caratteri per ridurre il rumore (es. “a”, “di”, “il”). Questo filtro è cruciale per evitare token non informativi che degradano la qualità dei modelli NLP.
Fase 3: **Disambiguazione contestuale con modelli linguistici** Grazie a modelli addestrati su corpora italiani (es. *Corpus del Parlante*), si analizza il contesto circostante per scegliere la lettura corretta di token ambigui come “viene” (verbo) vs “viene” (aggettivo derivato). Ad esempio, se “viene” precede un sostantivo, si considera “viene + sostantivo” unico; altrimenti, si mantiene come “vene” con filtro di frequenza.
Nel corpus di testi giuridici italiani, “è stato” è spesso un costrutto temporale passivo. La pipe NLP deve: – Riconoscere “è” come verbo ausiliare e “stato” come aggettivo nominale. – Applicare la regola: se “è” precede un sostantivo, considerare “è + sostantivo” come token unico per preservare il senso temporale. – Evitare token split errato: “è stato” → “è stato” (non “è” + “stato”).
Passo 1: **Correzione ortografica contestuale** Usare *TextBlob* con dizionari personalizzati per correggere errori comuni in testi scritti (es. “z” → “cz”, “è” vs “è” con accenti). Passo 2: **Espansione abbreviazioni** Mappare abbreviazioni frequenti (es. “Via” → “Viale”, “d’oggi” → “oggi”) tramite dizionari contestuali basati su geolocalizzazione o contesto lessicale. Passo 3: **Stemming controllato** Adattare *Porter* all’italiano o usare *Rule-based Stemmer* per radici morfemiche (es. “parlato” → “parl” per analisi temporale, “città” → “citt” senza perdere valore di genere).
Utilizzare *mingram* per verificare frequenze di sequenze token, identificando falsi positivi di split.