expon_backend / src /expon /ml /text_embed.py
SantosPatazca
Add IAM, Feedback, and Multimodal modules with updated backend services
a162c90
Raw
History Blame Contribute Delete
2.92 kB
# src/expon/ml/text_embed.py
from __future__ import annotations
import re
import numpy as np
class TextEncoder:
"""
Encoder de texto super liviano basado en keywords.
Produce una distribuci贸n de probabilidad sobre las clases.
"""
# Orden de clases (隆mantener igual en todo el proyecto!)
labels = ["entusiasta", "neutra", "confiada", "ansiosa", "motivada", "nerviosa"]
# Palabras/claves simples por clase (ajustables)
_lexicon = {
"entusiasta": [
r"emocionad[oa]s?", r"entusias[mt]o?", r"content[oa]s?",
r"alegr[ia]", r"feliz", r"genial", r"fant[a谩]stic[oa]", r"encantad[oa]"
],
"neutra": [
r"present(a|are)mos", r"metodolog[i铆]a", r"resultados?", r"conclusi[o贸]n",
r"validaci[o贸]n", r"an[a谩]lisis", r"datos?", r"grupo?s?"
],
"confiada": [
r"segur[oa]", r"claro", r"convencid[oa]", r"garantiz[ao]?", r"confirm(a|an|amos)",
r"demostr(a|amos|ado)", r"solidez", r"certeza"
],
"ansiosa": [
r"preocupad[oa]s?", r"ansios[oa]s?", r"inquiet[oa]s?", r"dud[ao]?",
r"nervios?", r"temor", r"incertidumbre", r"estresad[oa]"
],
"motivada": [
r"motivad[oa]s?", r"impuls[oa]?", r"seguir trabajando", r"inspirad[oa]",
r"proactividad", r"superaci[o贸]n", r"lograr", r"objetiv[oa]s?"
],
"nerviosa": [
r"nervios[oa]s?", r"tensi[o贸]n", r"temblor", r"titube[oa]?", r"dificultad para",
r"insegur[oa]", r"me puse", r"me siento mal"
],
}
def __init__(self) -> None:
# Precompilar regex
self._compiled = {
k: [re.compile(pat, flags=re.IGNORECASE) for pat in arr]
for k, arr in self._lexicon.items()
}
def _score_text(self, text: str) -> np.ndarray:
"""
Suma match de keywords por clase. Devuelve vector de scores raw (no normalizados).
"""
text = text or ""
scores = []
for lab in self.labels:
pats = self._compiled.get(lab, [])
s = 0.0
for rgx in pats:
for m in rgx.finditer(text):
# peso por coincidencia (m谩s largo, un poco m谩s de score)
span = m.end() - m.start()
s += 1.0 + min(span / 10.0, 1.0)
scores.append(s)
return np.asarray(scores, dtype=float)
def probs(self, text: str) -> np.ndarray:
"""
Devuelve una distribuci贸n [L] en el mismo orden que `labels`.
Usa suavizado para evitar vectores nulos.
"""
raw = self._score_text(text)
# Suavizado: prior uniforme peque帽o
eps = 1e-3
raw = raw + eps
s = raw.sum()
if s <= 0:
return np.full(len(self.labels), 1.0 / len(self.labels), dtype=float)
return raw / s