Skip to content

Instantly share code, notes, and snippets.

@me-suzy
Created July 27, 2026 17:10
Show Gist options
  • Select an option

  • Save me-suzy/d0e244ba6b5a96a90cec2d35db4200e2 to your computer and use it in GitHub Desktop.

Select an option

Save me-suzy/d0e244ba6b5a96a90cec2d35db4200e2 to your computer and use it in GitHub Desktop.
Similaritate Centrata.py
import os
os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1"
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.cluster import AgglomerativeClustering
model = SentenceTransformer("intfloat/multilingual-e5-base")
# ----------------------------------------------------------------------
# CORPUS de test: paragrafe medicale în limbaj vechi.
# Eticheta e doar pentru verificarea rezultatelor la final.
# ----------------------------------------------------------------------
corpus = [
("Bolnavul prezintă o arsură mare de sânge și fierbințeală a trupului.", "febră"),
("L-au apucat frigurile și tremura de i se clănțăneau dinții în gură.", "febră"),
("Pântecele îi era umflat și simțea junghiuri ascuțite sub coaste.", "durere"),
("Se plângea de o durere surdă în stomac, ca o rosătură necurmată.", "durere"),
("Rețeta cere trei picături de ulei de mentă dizolvate în apă caldă.", "rețetă"),
("Se dă bolnavului fiertură de tei cu miere, seara la culcare.", "rețetă"),
("Rana i s-a înnegrit la margini și puroiul curgea neîncetat.", "infecție"),
("Buba se umflase și scotea o materie gălbuie, urât mirositoare.", "infecție"),
("Nu putea prinde somnul și se zvârcolea în așternut toată noaptea.", "insomnie"),
("Ochii îi erau duși în fundul capului de atâta nesomn și veghe.", "insomnie"),
]
texte = [t for t, _ in corpus]
etichete = [e for _, e in corpus]
# ----------------------------------------------------------------------
# 1. ENCODARE brută (cu prefixul "passage:" cerut de E5)
# ----------------------------------------------------------------------
emb = model.encode(
[f"passage: {t}" for t in texte],
normalize_embeddings=True,
convert_to_numpy=True,
)
# ----------------------------------------------------------------------
# 2. PROBLEMA: la E5, similaritatea brută are un prag de bază foarte înalt.
# Toate paragrafele par ~90% similare, deci scorul nu discriminează.
# ----------------------------------------------------------------------
def statistici(matr):
off = matr[~np.eye(len(matr), dtype=bool)] # valorile din afara diagonalei
return off.min(), off.mean(), off.max()
sim_brut = emb @ emb.T
lo, me, hi = statistici(sim_brut)
print("SIMILARITATE BRUTĂ (problema):")
print(f" interval: {lo:.3f} ... {hi:.3f} (medie {me:.3f})")
print(" -> totul pare similar, pragul nu poate separa temele\n")
# ----------------------------------------------------------------------
# 3. CORECȚIA: centrarea vectorilor. Scădem media corpusului (componenta
# comună tuturor textelor) și renormalizăm. Similaritățile se împrăștie.
# ----------------------------------------------------------------------
media = emb.mean(axis=0)
emb_c = emb - media
emb_c = emb_c / np.linalg.norm(emb_c, axis=1, keepdims=True)
sim_centrat = emb_c @ emb_c.T
lo, me, hi = statistici(sim_centrat)
print("SIMILARITATE CENTRATĂ (corecția):")
print(f" interval: {lo:.3f} ... {hi:.3f} (medie {me:.3f})")
print(" -> acum diferențele sunt reale și utilizabile\n")
# ----------------------------------------------------------------------
# 4. CĂUTARE SEMANTICĂ cu vectori centrați.
# Întrebarea se centrează cu ACEEAȘI medie a corpusului.
# ----------------------------------------------------------------------
def cauta(intrebare, k=3):
q = model.encode(f"query: {intrebare}", normalize_embeddings=True, convert_to_numpy=True)
q = q - media
q = q / np.linalg.norm(q)
scoruri = emb_c @ q
top = np.argsort(-scoruri)[:k]
print(f"Întrebare: {intrebare}")
for rang, i in enumerate(top, 1):
print(f" {rang}. [{scoruri[i]:+.3f}] ({etichete[i]}) {texte[i]}")
print()
cauta("Ce fac dacă pacientul are temperatură ridicată?")
cauta("Tratament pentru o rană infectată cu puroi")
cauta("Pacientul nu poate dormi noaptea")
# ----------------------------------------------------------------------
# 5. CLUSTERING CONCEPTUAL: grupăm paragrafele care spun același lucru,
# chiar cu vocabular diferit. Aglomerativ pe distanța cosinus.
# ----------------------------------------------------------------------
N_GRUPURI = 5
clustering = AgglomerativeClustering(n_clusters=N_GRUPURI, metric="cosine", linkage="average").fit(emb_c)
print(f"GRUPURI CONCEPTUALE (aglomerativ, {N_GRUPURI} grupuri):")
for c in sorted(set(clustering.labels_)):
membri = [i for i in range(len(texte)) if clustering.labels_[i] == c]
teme = {etichete[i] for i in membri}
marca = "PUR" if len(teme) == 1 else "mixt"
print(f" Grup {c} [{marca}]:")
for i in membri:
print(f" - ({etichete[i]}) {texte[i]}")
print()
# ----------------------------------------------------------------------
# 6. CALITATE: câte grupuri conțin o singură temă reală?
# ----------------------------------------------------------------------
pure = sum(1 for c in set(clustering.labels_)
if len({etichete[i] for i in range(len(texte)) if clustering.labels_[i] == c}) == 1)
print(f"Grupuri pure: {pure}/{N_GRUPURI}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment