Implementare il Controllo Semantico del Linguaggio Italiano in NLP: Fase 1 – Preprocessing Avanzato e Gestione della Variabilità Lessicale e Morfologica

Il problema fondamentale del preprocessing nel NLP italiano: gestire la variabilità lessicale e morfologica

Il controllo semantico in sistemi NLP basati sull’italiano richiede una fase preliminare di preprocessing estremamente raffinata, poiché la lingua italiana presenta una ricchezza morfologica e lessicale che sfida i pipeline standard. La presenza di contrazioni (es. “nonché” vs “nè”), varianti flesse (es. “banca” vs “banche”, “dall” vs “dal”), e allomorfie lessicali complica l’estrazione del significato semantico, poiché modelli generici spesso falliscono nel riconoscere le relazioni contestuali precise. Un approccio superficiale a questa fase introduce errori cascata che compromettono la fedeltà della rappresentazione semantica successiva.

Per affrontare questa sfida, la fase 1 del Tier 2 – analisi semantica – si basa su tre pilastri fondamentali: tokenizzazione contesto-aware, normalizzazione morfologica contestuale e rimozione attiva del rumore linguistico.

1. Tokenizzazione e normalizzazione contestuale: oltre la semplice suddivisione in parole

La tokenizzazione italiana non può limitarsi alla semplice divisione per spazi o punteggiatura: è essenziale riconoscere le contrazioni morfologiche e lessicali come unità semantiche coerenti. Strumenti come CamemBERT, mediante la lemmatizzazione contestuale, permettono di mappare varianti lessicali (es. “dall” → “dall”, “nè” → “non”) al lemma base “da” o “ne”, preservando il significato originale senza perdita di informazione.

Ma la normalizzazione va oltre: si tratta di riconoscere e unificare forme alternative che condividono lo stesso nucleo semantico, ad esempio:

– Contrazioni e abbreviazioni: “dall” → “dall”, “nè” → “non”, “fino a” → “fino”
– Forme flesse aggettivali e sostantive: “banca” → “banca” (sostantivo), “bancario” → “bancario” (aggettivo), ma con leggettizzazione contestuale per evitare sovra-segmentazione
– Varianti ortografiche dialettali o regionali: “citta” → “città” (ma in contesti formali normalizzare a “città”, in contesti technical a “città” per coerenza)

Un esempio concreto: nel testo “Il cliente si è rivolto alla banca per un bonifico in dall”, una pipeline avanzata normalizza “dall” a “dall” (mantenendo contrazione), riconosce “bonifico” come entità semantica chiave e “città” non presente, ma identifica il nesso logico tra “cliente” (agente) e “bonifico” (oggetto azionale).

Implementazione pratica:
from camemBERT.tokenization import CamemBERTTokenizer
import re

tokenizer = CamemBERTTokenizer()

def normalizza_token(token):
if token.lower() in [“dall”, “dall'”]: return “dall”
if token.lower() in [“nè”]: return “non”
if token.lower() in [“citta”, “città”]: return “città”
# Regole per altre contrazioni e forme
# …
return token.lower().strip()

text = “Il cliente ha depositato il bonifico in dall, richiedendo informazioni alla banca a Milano.”
tokens = tokenizer(text, return_tensors=”pt”, add_special_tokens=True)
normalized = [normalizza_token(t) for t in tokens[‘input_ids’][0].tolist()]
print(“Token normalizzati:”, normalized)

Questa fase garantisce che il flusso semantico riceva input omogenei e semanticamente fedele, riducendo falsi positivi nella fase successiva di estrazione del significato.

2. Rimozione attiva del rumore linguistico: dialetti, slang e varianti non standard

Il corpus italiano è ricco di varianti regionali, gergo giovanile, e forme informali che possono distorcere l’analisi semantica se non filtrate con attenzione. La rimozione del rumore non deve procedere con filtri rigidi che eliminano valore contestuale, ma con strategie basate su regole contestuali e modelli di rilevamento statistico.

Esempi di rumore tipico italiano:

– Slang giovanile: “finta”, “manchi”, “truccare” → spesso usati come aggettivi o verbi contestuali, non devono essere rimossi in analisi semantica
– Contrazioni informali: “lui c’è”, “noi siamo”, “voi avete” → valutare contesto: in frasi modali o enfatiche possono essere semantici
– Errori ortografici frequenti: “bonifico” → “bonifico”, “cliente” → “cli”, “dalla” → “dall”

Una pipeline efficace combina:

– Dizionari contestuali di slang (es. da corpus social o forum italiani)
– Modelli di correzione ortografica addestrati su testi italiani reali (es. CamemBERT fine-tuned su dati annotati)
– Filtri basati su frequenza di uso in contesti specifici (es. parole rare <0.1% in corpus standard)

Implementazione esempio con correzione contestuale:
from textblob import TextBlob
from camemBERT.tokenization import CamemBERTTokenizer

tokenizer = CamemBERTTokenizer()

def correggi_slang(tokens):
slang_dict = {
“finta”: “finta”, “manchi”: “manchi”, “truccare”: “truccare”,
“lui c’è”: “lui c’è”, “noi siamo”: “noi siamo”, “voi avete”: “voi avete”
}
return [slang_dict.get(t, t) for t in tokens]

text = “Il cliente c’è, ha visto la banca, ma non ha fatto nulla.”
tokens = tokenizer(text, return_tensors=”pt”)
corrected = [slang_dict.get(t, t) for t in tokens[‘input_ids’][0].tolist()]
print(corrected)

Questa fase assicura che il modello semantico riceva input puliti, ma semanticamente ricchi, senza perdere sfumature culturali.

3. Integrazione con pipeline semantica: da token a grafo concettuale

La normalizzazione e pulizia del testo alimentano una pipeline semantica multilivello (Tier 2), che trasforma il testo in un grafo concettuale strutturato. Ogni nodo rappresenta un concetto (es. “cliente”, “bonifico”, “dalla”) e ogni arco una relazione semantica (es. “cliente → effettua → bonifico”, “bonifico → processato_in → dalla”).

Un’implementazione pratica:
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

model = SentenceTransformer(‘camembert-base’)

def crea_raffinamento_ganz_attenzionale(tokens, contesto=None):
# Embedding del testo con attenzione contestuale (es. cross-word attention)
emb = model.encode([contesto or “effettua bonifico con dalla”], convert_to_tensor=True)

# Identifica entità chiave nel contesto
entitàsoggetto = [t for t in tokens if t in [“cliente”, “cli”, “tuo”]]
entitàoggetto = [t for t in tokens if t in [“bonifico”, “bonifico”, “servizio”]]

grafo = {‘nodi’: [], ‘archi’: []}

for sg in entitàsoggetto:
nodo = {‘id’: f'{sg}’, ‘tipo’: ‘agente’, ’embedding’: emb[0][sentence_start(sg)]}
grafo[‘nodi’].append(nodo)
for o in entitàoggetto:
archio = {
‘source’: sg,
‘target’: o,
‘relazione’: ‘effettua’,
’embedding’: emb[0][sentence_end(o)]
}
grafo[‘archi’].

Add Your Comment