Migliorare il riconoscimento dei nomi con GLiNER e FastAPI

π Obiettivo: Estrarre nomi di persone da documenti lunghi riducendo i falsi positivi.
π Tecnologia usata: GLiNER, FastAPI, spaCy, Python
π Problema iniziale: Troppi falsi positivi
Abbiamo testato il modello GLiNER (DeepMount00/GLiNER_ITA_LARGE
) per riconoscere i nomi di persone in documenti lunghi.
π‘ Primo test: Il modello ha estratto 700+ falsi positivi da un documento con molte ripetizioni di parole comuni.
β Errori comuni rilevati:
"Lavoratore", "telelavoratore", "colleghi diretti" β Riconosciuti erroneamente come persone.
Frasi spezzate con perdita di contesto β Generazione di entitΓ inesatte.
π Soluzione 1: Segmentazione ottimizzata
π Problema: Il modello ha un limite di 384 token per input.
β
Soluzione: Abbiamo suddiviso il testo in paragrafi invece che in frasi singole, preservando il contesto.
Tecnologia usata:
import re
import spacy
nlp = spacy.load("it_core_news_sm")
def split_text_by_paragraphs(text: str, max_tokens: int = 384):
paragraphs = re.split(r'\n\s*\n+', text)
segments = []
start_index = 0
for para in paragraphs:
doc = nlp(para)
tokens = [token.text for token in doc]
token_count = len(tokens)
if token_count > max_tokens:
sentences = list(doc.sents)
current_segment = []
current_token_count = 0
for sent in sentences:
sent_tokens = [token.text for token in sent]
if current_token_count + len(sent_tokens) > max_tokens:
segment_text = " ".join(current_segment)
end_index = start_index + len(segment_text)
segments.append({"text": segment_text, "start": start_index, "end": end_index})
current_segment = []
current_token_count = 0
start_index = end_index + 1
current_segment.extend(sent_tokens)
current_token_count += len(sent_tokens)
if current_segment:
segment_text = " ".join(current_segment)
end_index = start_index + len(segment_text)
segments.append({"text": segment_text, "start": start_index, "end": end_index})
start_index = end_index + 1
else:
end_index = start_index + len(para)
segments.append({"text": para, "start": start_index, "end": end_index})
start_index = end_index + 1
return segments
π Risultato: Diminuzione drastica dei falsi positivi, ma ancora qualche errore.
π Soluzione 2: Validazione con il cognome
π Problema: Il modello riconosce ancora parole non valide come "persona".
π Soluzione: Abbiamo aggiunto una verifica sul cognome:
- Se un'entitΓ riconosciuta come "persona" non ha un cognome valido, viene scartata.
π Codice ottimizzato nella classe NamedEntityRecognizer
def predict_people(self, text: str):
predictions = self.model.predict_entities(
text, ["persona"], flat_ner=self.flat_ner, threshold=0.5, multi_label=self.multi_label
)
if not predictions:
return predictions
return self.validate_person_with_cognome(predictions)
def validate_person_with_cognome(self, predictions: List[dict]) -> List[dict]:
valid_people = []
for entity in predictions:
name = entity["text"]
cognome_predictions = self.model.predict_entities(
name, ["cognome"], flat_ner=self.flat_ner, threshold=0.5, multi_label=self.multi_label
)
if cognome_predictions:
valid_people.append(entity)
return valid_people
π Risultato: I falsi positivi sono scesi da 700 a 24, ma rimanevano ancora 2 errori.
π Soluzione 3: Doppia validazione con nome e cognome
π Problema: Alcuni termini con cognomi generici venivano riconosciuti erroneamente.
π Soluzione: Aggiunto un secondo controllo per verificare che l'entitΓ abbia sia un "nome" che un "cognome".
def validate_person_with_nome_cognome(self, predictions: List[dict]) -> List[dict]:
valid_people = []
for entity in predictions:
name = entity["text"]
cognome_predictions = self.model.predict_entities(
name, ["cognome"], flat_ner=self.flat_ner, threshold=0.5, multi_label=self.multi_label
)
nome_predictions = self.model.predict_entities(
name, ["nome"], flat_ner=self.flat_ner, threshold=0.5, multi_label=self.multi_label
)
if cognome_predictions and nome_predictions:
valid_people.append(entity)
return valid_people
π Risultato: Abbiamo eliminato i 2 falsi positivi residui. π―
π Risultato finale
Dopo tutte le ottimizzazioni, il modello Γ¨ passato da:
β 700 falsi positivi β β
24 risultati validi (solo 2 errori) β β
0 errori dopo il filtro finale
π Moduli richiesti
Per replicare l'intero processo, sono necessari i seguenti moduli Python:
pip install gliner fastapi spacy uvicorn
python -m spacy download it_core_news_sm
β
gliner
β Modello NER per l'estrazione dei nomi.
β
fastapi
β API per eseguire il modello su rete locale.
β
uvicorn
β Server per FastAPI.
β
spacy
+ it_core_news_sm
β Segmentazione e pre-processing del testo.
π Conclusione
Grazie a queste ottimizzazioni, abbiamo migliorato drasticamente l'accuratezza del riconoscimento di persone in documenti lunghi, eliminando quasi tutti i falsi positivi.
π Se hai un problema simile, prova queste strategie e gioca con il valore threshold
per trovare il bilanciamento perfetto! π
Subscribe to my newsletter
Read articles from DaT directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by