Arquitecturas y Algoritmos para el Cálculo de Similitud de Texto

En el ámbito del procesamiento del lenguaje natural (NLP), una tarea recurrente es la estructuración de contenido no etiquetado. Tras diversos prototipos, se determina que este problema puede redefinirse como un sistema de clasificación y ordenamiento basado en búsquedas por similitud textual. Existen dos vertientes principales para abordar esta métrica: métodos estadísticos basados en la superficie del texto y enfoques semánticos mediante redes neuronales profundas.

  1. Métodos Estocásticos Basados en Superficie

Estas técnicas evalúan la proximidad entre cadenas de caracteres o conjuntos de palabras sin considerar necesariamente el contexto profundo.

1.1 Métrica de Coseno sobre Vectores de Palabras

La similitud del coseno mide el ángulo entre dos vectores numéricos. En texto, esto implica convertir las frases en representaciones vectoriales de frecuencia de términos (bag-of-words).

def evaluar_proximidad_lexica(texto_a, texto_b): # Convertir entrada a conteo de tokens conteo_x = Counter(texto_a) conteo_y = Counter(texto_b)

<span style="color: #d0d0f0;"># Unificar vocabulario único presente en ambas muestras</span>
conjunto_tokens = <span style="color: #d0d0f0;">set</span>(list(conteo_x.keys()) + list(conteo_y.keys()))

vector_x = []
vector_y = []

<span style="color: #d0d0f0;"># Construir vectores densos alineados al mismo vocabulario</span>
<span style="color: #d0d0f0;">for</span> token <span style="color: #d0d0f0;">in</span> conjunto_tokens:
    vector_x.append(conteo_x.get(token, 0))
    vector_y.append(conteo_y.get(token, 0))
    
arr_x = np.array(vector_x, dtype=<span style="color: #d0d0f0;">float</span>)
arr_y = np.array(vector_y, dtype=<span style="color: #d0d0f0;">float</span>)

<span style="color: #d0d0f0;"># Fórmula del coseno: dot product / (norm_a * norm_b)</span>
numerador = np.dot(arr_x, arr_y)
denominador = np.sqrt(np.dot(arr_x, arr_x)) * np.sqrt(np.dot(arr_y, arr_y))

<span style="color: #6a9955;">if</span> denominador == 0:
    <span style="color: #6a9955;">return</span> 0.0
    
<span style="color: #6a9955;">return</span> numerador / denominador

# Ejecución de prueba resultado = evaluar_proximidad_lexica(["feliz", "en", "ciudad", "sobre"], ["feliz", "en", "pueblo", "sobre"]) print(f"Puntuación de similitud: {resultado}")


</div>### 1.2 Comparación de Secuencias Diferenciales

Bibliotecas estándar ofrecen algoritmos para detectar patrones comunes en secuencias de caracteres o edición de distancia, útiles cuando no se dispone de datos entrenados.

<div>```
<span style="color: #6a9955;">import</span> difflib
<span style="color: #6a9955;">from</span> fuzzywuzzy <span style="color: #6a9955;">import</span> fuzz

<span style="color: #2b91af;">def</span> <span style="color: #2b91af;">analizador_ratio_secuencia</span>(cadena_origen, cadena_destino):
    matcher = difflib.SequenceMatcher(a=cadena_origen, b=cadena_destino)
    <span style="color: #6a9955;">return</span> matcher.ratio()

<span style="color: #6a9955;">print</span>(analizador_ratio_secuencia(<span style="color: #ce9178;">"El clima cambia"</span>, <span style="color: #ce9178;">"El tiempo varía"</span>))

<span style="color: #2b91af;">def</span> <span style="color: #2b91af;">obtener_fidelidad_edicion</span>(txt1, txt2):
    <span style="color: #6a9955;">return</span> fuzz.ratio(txt1, txt2)

<span style="color: #6a9955;">print</span>(obtener_fidelidad_edicion(<span style="color: #ce9178;">"Analizando texto"</span>, <span style="color: #ce9178;">"Estando texto"</span>))

Para capturar el significado real más allá de la coincidencia de palabras, se emplean modelos preentrenados. La arquitectura Sentence-BERT (SBERT) representa la evolución natural para tareas de comparación por pares.

A diferencia del BERT tradicional, que requiere procesar pares de oraciones concatenadas (método interactivo), SBERT utiliza una red gemela (Siamese Network). Cada oración se pasa independientemente por una codificación BERT compartida para generar un embedding denso. Posteriormente, se calcula la distancia euclidiana o coseno entre estos vectores.

Ventajas Computacionales

  • Complejidad: Si tenemos textos de longitud M y N, el método interactivo tiene costo O((M+N)2). El enfoque de representación descompone esto en O(M2 + N2).
  • Caché de Embeddings: Se pueden calcular los vectores de la base de conocimiento offline. Durante la consulta on line, solo se necesita incrustar el nuevo query y buscar en un índice vecino aproximado (ANN), reduciendo drásticamente la latencia.
  • Escalabiliadd: Permite usar motores de búsqueda vectorial como FAISS para filtrar millones de documentos en milisegundos.

Implementación del Entrenamiento

A continuación se presenta un flujo completo utilizando la librería especializada sentence-transformers.

def cargar_conjunto_datos(ruta_archivo): df = pd.read_csv(ruta_archivo, sep=",") return df.sample(frac=1) # Mezclar filas aleatoriamente

def construir_objetivos_entreno(df_entreno): ejemplos = [] col_s1, col_s2, col_label = "phr1", "phr2", "score"

iterator = zip(df_entreno[col_s1], df_entreno[col_s2], df_entreno[col_label])
<span style="color: #6a9955;">for</span> p1, p2, valor in tqdm(iterator):
    ejemplos.append(InputExample(texts=[p1, p2], label=<span style="color: #d0d0f0;">float</span>(valor)))
<span style="color: #6a9955;">return</span> ejemplos

# Inicialización del modelo base multilingüe modelo_base = SentenceTransformer('distiluse-base-multilingual-cased')

datos_tren = cargar_conjunto_datos("./datos/fase_entrenamiento.csv") dataset_objetivo = construir_objetivos_entreno(datos_tren)

# Preparación del DataLoader dataloader_tren = DataLoader(SentencesDataset(dataset_objetivo, modelo_base), batch_size=32, shuffle=True) funcion_perdida = losses.CosineSimilarityLoss(modelo_base)

# Configuración de evaluación datos_val = cargar_conjunto_datos("./datos/fase_validacion.csv") # (Lógica simplificada para extracción de tuplas de validación omitida por brevedad) validador = evaluation.BinaryClassificationEvaluator(...)

# Ejecución del ciclo de ajuste de hiperparámetros modelo_base.fit( train_objectives=[(dataloader_tren, funcion_perdida)], evaluator=validador, epochs=5, warmup_steps=150, evaluation_steps=250, output_path='./modelo_semiotico_final' )


</div>### Inferencia y Predicción

Una vez guardado el modelo ajustado, se puede reucperar su estado persistente para comparar nuevos inputs de manera eficiente.

<div>```
<span style="color: #6a9955;">from</span> sentence_transformers <span style="color: #6a9955;">import</span> util

<span style="color: #d0d0f0;"># Recuperar el modelo fine-tuned localmente</span>
motor_semantico = SentenceTransformer(<span style="color: #ce9178;">'./modelo_semiotico_final'</span>)

<span style="color: #d0d0f0;"># Definir queries de interés</span>
query_candidate_1 = motor_semantico.encode(<span style="color: #ce9178;">"Candidato miembro"</span>)
query_doc_long = motor_semantico.encode(<span style="color: #ce9178;">"IV.\tDiseño de Infraestructura Cloud Regional"</span>)
query_candidate_2 = motor_semantico.encode(<span style="color: #ce9178;">"Pago mensual cuota"</span>)

<span style="color: #d0d0f0;"># Cálculo de puntuaciones de afinidad</span>
puntaje_1 = util.pytorch_cos_sim(query_candidate_1, query_doc_long)[0]
puntaje_2 = util.pytorch_cos_sim(query_candidate_2, query_doc_long)[0]

<span style="color: #6a9955;">print</span>(<span style="color: #ce9178;">f"Afinidad A: {puntaje_1.item():.4f}"</span>)
<span style="color: #6a9955;">print</span>(<span style="color: #ce9178;">f"Afinidad B: {puntaje_2.item():.4f}"</span>)

Etiquetas: Python nlp sentence-bert machine-learning text-similarity

Publicado el 10-1 08:46