This commit is contained in:
team3
2026-07-10 15:43:11 +02:00
commit 0a41166cca
72 changed files with 7772 additions and 0 deletions

67
backend/embedding.py Normal file
View 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.30.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])