init
This commit is contained in:
67
backend/embedding.py
Normal file
67
backend/embedding.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Ähnlichkeit für Merge-KANDIDATEN — entscheidet nie selbst (das tut das Panel).
|
||||
sentence-transformers wenn vorhanden, sonst lexikalischer Jaccard-Fallback.
|
||||
Verglichen werden DEFINITIONEN, nie Titel (Lektion: Titel-Embeddings rauschen)."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
log = logging.getLogger("creator2.embedding")
|
||||
|
||||
_modell = None
|
||||
_geladen = False
|
||||
|
||||
|
||||
def _lade_modell():
|
||||
global _modell, _geladen
|
||||
if _geladen:
|
||||
return _modell
|
||||
_geladen = True
|
||||
try:
|
||||
from sentence_transformers import SentenceTransformer
|
||||
_modell = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
|
||||
log.info("Embedding-Modell geladen")
|
||||
except Exception:
|
||||
log.warning("kein Embedding-Modell — lexikalischer Jaccard-Fallback"
|
||||
" (findet Paraphrasen-Dubletten deutlich schlechter);"
|
||||
" Fix: pip install sentence-transformers")
|
||||
_modell = None
|
||||
return _modell
|
||||
|
||||
|
||||
_WORT = re.compile(r"[a-zäöüß0-9]+")
|
||||
|
||||
|
||||
def _tokens(text: str) -> set[str]:
|
||||
return set(_WORT.findall(text.lower()))
|
||||
|
||||
|
||||
def _jaccard(a: str, b: str) -> float:
|
||||
ta, tb = _tokens(a), _tokens(b)
|
||||
if not ta or not tb:
|
||||
return 0.0
|
||||
return len(ta & tb) / len(ta | tb)
|
||||
|
||||
|
||||
def aehnlichkeit_matrix(texte: list[str]) -> list[list[float]]:
|
||||
"""Paarweise Ähnlichkeit [0..1]. Symmetrisch, Diagonale 1."""
|
||||
modell = _lade_modell()
|
||||
n = len(texte)
|
||||
if modell is not None:
|
||||
import numpy as np
|
||||
emb = modell.encode(texte, normalize_embeddings=True)
|
||||
return (emb @ emb.T).tolist()
|
||||
return [[1.0 if i == j else _jaccard(texte[i], texte[j]) for j in range(n)] for i in range(n)]
|
||||
|
||||
|
||||
def kandidaten_paare(texte: list[str], floor: float,
|
||||
fallback_floor: float | None = None) -> list[tuple[int, int, float]]:
|
||||
"""Alle Index-Paare mit Ähnlichkeit ≥ Schwelle, absteigend sortiert.
|
||||
Im Jaccard-Fallback gilt fallback_floor: echte Paraphrasen messen dort nur
|
||||
~0.3–0.5 — die Kosinus-Schwelle ließe sie alle durchrutschen (aak Lauf 8:
|
||||
13 Panel-Calls bei 26 überlebenden Dubletten-Gruppen)."""
|
||||
m = aehnlichkeit_matrix(texte)
|
||||
if _modell is None and fallback_floor is not None:
|
||||
floor = fallback_floor
|
||||
paare = [(i, j, m[i][j]) for i in range(len(texte)) for j in range(i + 1, len(texte))
|
||||
if m[i][j] >= floor]
|
||||
return sorted(paare, key=lambda p: -p[2])
|
||||
Reference in New Issue
Block a user