Download src/graph/entity_resolver.py from NextGenTech/AutomatedSemanticDiscovery: direct link, hf CLI and curl.
- Browser
- Download file 3.23 kB
-
https://huggingface.co/spaces/NextGenTech/AutomatedSemanticDiscovery/resolve/4e0fedeea3cd755f6a7b0a52cb511ba401d292c2/src/graph/entity_resolver.py
- Command line
-
hf download hf://spaces/NextGenTech/AutomatedSemanticDiscovery@4e0fedeea3cd755f6a7b0a52cb511ba401d292c2/src/graph/entity_resolver.py
-
curl -L -o entity_resolver.py https://huggingface.co/spaces/NextGenTech/AutomatedSemanticDiscovery/resolve/4e0fedeea3cd755f6a7b0a52cb511ba401d292c2/src/graph/entity_resolver.py
3.23 kB
| import numpy as np | |
| from sklearn.cluster import DBSCAN | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| from collections import Counter | |
| class EntityResolver: | |
| def __init__(self, model_name="all-MiniLM-L6-v2", similarity_threshold=0.85): | |
| """ | |
| Inizializza il modello per il calcolo delle similarità. | |
| similarity_threshold: quanto devono essere vicini i vettori (0-1). | |
| Convertito in 'eps' per DBSCAN. | |
| """ | |
| print("🧩 Inizializzazione Entity Resolver (DBSCAN)...") | |
| self.embedding_model = HuggingFaceEmbeddings(model_name=model_name) | |
| # DBSCAN usa la distanza, non la similarità. Distanza = 1 - Similarità. | |
| # Se threshold è 0.85 (alta similarità), eps deve essere 0.15 (bassa distanza). | |
| self.eps = 1 - similarity_threshold | |
| def resolve_entities(self, triples): | |
| """ | |
| Prende una lista di triple (GraphTriple) e normalizza i nomi delle entità. | |
| """ | |
| if not triples: | |
| return [] | |
| # 1. Estrazione di tutte le entità uniche (Soggetti e Oggetti) | |
| all_entities = set() | |
| for t in triples: | |
| all_entities.add(t.subject) | |
| all_entities.add(t.object) | |
| unique_entities = list(all_entities) | |
| print(f" Analisi di {len(unique_entities)} entità uniche per deduplica...") | |
| if len(unique_entities) < 2: | |
| return triples | |
| # 2. Calcolo Embeddings | |
| embeddings = self.embedding_model.embed_documents(unique_entities) | |
| X = np.array(embeddings) | |
| # 3. Clustering DBSCAN | |
| # metrica='cosine' è fondamentale per vettori semantici | |
| clustering = DBSCAN(eps=self.eps, min_samples=1, metric='cosine').fit(X) | |
| labels = clustering.labels_ | |
| # 4. Creazione Mappa {Variante -> Canonico} | |
| # Raggruppiamo le entità per Cluster ID | |
| cluster_map = {} | |
| for entity, label in zip(unique_entities, labels): | |
| if label not in cluster_map: | |
| cluster_map[label] = [] | |
| cluster_map[label].append(entity) | |
| # Per ogni cluster, eleggiamo il "Canonico" (es. la stringa più lunga) | |
| entity_replacement_map = {} | |
| for label, variants in cluster_map.items(): | |
| if len(variants) > 1: | |
| # Euristiche di canonicalizzazione: | |
| # 1. Preferisci quella che inizia con maiuscola | |
| # 2. Preferisci la più lunga (spesso più descrittiva: "San Marco" vs "Basilica di San Marco") | |
| canonical = sorted(variants, key=len, reverse=True)[0] | |
| print(f" ✨ Deduplica: {variants} -> '{canonical}'") | |
| for v in variants: | |
| entity_replacement_map[v] = canonical | |
| else: | |
| entity_replacement_map[variants[0]] = variants[0] | |
| # 5. Riscrittura Triple | |
| resolved_triples = [] | |
| for t in triples: | |
| # Sostituiamo soggetto e oggetto con le versioni canoniche | |
| t.subject = entity_replacement_map.get(t.subject, t.subject) | |
| t.object = entity_replacement_map.get(t.object, t.object) | |
| resolved_triples.append(t) | |
| return resolved_triples |