File size: 5,641 Bytes
493ed61 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
from typing import Dict, List, Optional, Union
import spacy
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
import re
from patterns import (
PATRONES_AMBIGUEDAD_LEXICA,
PATRONES_AMBIGUEDAD_SINTACTICA,
SUGERENCIAS_MEJORA
)
class SemanticAnalyzer:
"""
Analizador semántico que utiliza embeddings para comparar textos.
"""
def __init__(self, model_name: str = "PlanTL-GOB-ES/roberta-base-bne"):
"""
Inicializa el analizador semántico.
Args:
model_name (str): Nombre del modelo de HuggingFace a utilizar
"""
try:
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModel.from_pretrained(model_name)
except Exception as e:
raise RuntimeError(f"Error cargando el modelo {model_name}: {str(e)}")
def get_embedding(self, texto: str) -> np.ndarray:
"""
Obtiene el embedding de un texto usando el modelo de transformers.
Args:
texto (str): Texto a procesar
Returns:
np.ndarray: Vector de embedding
"""
inputs = self.tokenizer(texto, return_tensors="pt", padding=True, truncation=True)
with torch.no_grad():
outputs = self.model(**inputs)
return outputs.last_hidden_state.mean(dim=1).numpy()[0]
def calcular_similitud(self, texto1: str, texto2: str) -> float:
"""
Compara la similitud semántica entre dos textos.
Args:
texto1 (str): Primer texto
texto2 (str): Segundo texto
Returns:
float: Score de similitud entre 0 y 1
"""
emb1 = self.get_embedding(texto1)
emb2 = self.get_embedding(texto2)
similarity = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2))
return float(similarity)
class AmbiguityClassifier:
"""
Clasificador de ambigüedades en historias de usuario.
Detecta ambigüedades léxicas y sintácticas, y proporciona sugerencias de mejora.
"""
def __init__(self, model_name: str = "PlanTL-GOB-ES/roberta-base-bne"):
"""
Inicializa el clasificador de ambigüedades.
Args:
model_name (str): Nombre del modelo de HuggingFace a utilizar
"""
try:
self.nlp = spacy.load("es_core_news_sm")
except OSError:
raise RuntimeError("Es necesario instalar el modelo es_core_news_sm. Ejecute: python -m spacy download es_core_news_sm")
self.semantic_analyzer = SemanticAnalyzer(model_name)
def __call__(self, texto: str) -> Dict[str, Union[bool, List[str], float]]:
"""
Analiza una historia de usuario en busca de ambigüedades.
Args:
texto (str): Historia de usuario a analizar
Returns:
Dict: Resultado del análisis con tipos de ambigüedad y sugerencias
"""
if not texto or not isinstance(texto, str):
return {
"tiene_ambiguedad": False,
"ambiguedad_lexica": [],
"ambiguedad_sintactica": [],
"sugerencias": ["El texto está vacío o no es válido"],
"score_ambiguedad": 0.0
}
# Procesar el texto con spaCy
doc = self.nlp(texto.strip())
# Detectar ambigüedades léxicas
ambiguedades_lexicas = []
for patron in PATRONES_AMBIGUEDAD_LEXICA:
if re.search(patron["patron"], texto, re.IGNORECASE):
ambiguedades_lexicas.append({
"tipo": patron["tipo"],
"descripcion": patron["descripcion"]
})
# Detectar ambigüedades sintácticas
ambiguedades_sintacticas = []
for patron in PATRONES_AMBIGUEDAD_SINTACTICA:
if re.search(patron["patron"], texto, re.IGNORECASE):
ambiguedades_sintacticas.append({
"tipo": patron["tipo"],
"descripcion": patron["descripcion"]
})
# Generar sugerencias de mejora
sugerencias = []
if ambiguedades_lexicas or ambiguedades_sintacticas:
for ambiguedad in ambiguedades_lexicas + ambiguedades_sintacticas:
tipo = ambiguedad["tipo"]
if tipo in SUGERENCIAS_MEJORA:
sugerencias.extend(SUGERENCIAS_MEJORA[tipo])
# Calcular score de ambigüedad
score = len(ambiguedades_lexicas) * 0.4 + len(ambiguedades_sintacticas) * 0.6
score_normalizado = min(1.0, score / 5.0) # Normalizar a un rango de 0 a 1
return {
"tiene_ambiguedad": bool(ambiguedades_lexicas or ambiguedades_sintacticas),
"ambiguedad_lexica": [amb["descripcion"] for amb in ambiguedades_lexicas],
"ambiguedad_sintactica": [amb["descripcion"] for amb in ambiguedades_sintacticas],
"sugerencias": sugerencias if sugerencias else ["No se encontraron ambigüedades"],
"score_ambiguedad": round(score_normalizado, 2)
}
def analizar_similitud_semantica(self, texto1: str, texto2: str) -> float:
"""
Compara la similitud semántica entre dos textos.
Args:
texto1 (str): Primer texto
texto2 (str): Segundo texto
Returns:
float: Score de similitud entre 0 y 1
"""
return self.semantic_analyzer.calcular_similitud(texto1, texto2) |