File size: 5,266 Bytes
a2f8159
 
7a8b44d
1e83509
 
7a8b44d
1e83509
7a8b44d
b8350d6
7a8b44d
 
 
 
 
 
1e83509
7a8b44d
 
1e83509
7a8b44d
 
 
a8a537c
 
 
 
 
 
 
7a8b44d
 
b8350d6
7a8b44d
 
 
 
 
 
 
 
 
1e83509
 
7a8b44d
 
 
 
 
 
 
 
 
1e83509
7a8b44d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18da99f
1e83509
 
 
b8350d6
 
 
1e83509
b8350d6
 
1e83509
 
 
 
 
 
 
a2f8159
b8350d6
a2f8159
b8350d6
1e83509
 
b8350d6
 
1e83509
 
 
 
 
a2f8159
1e83509
 
 
 
 
 
 
 
 
 
 
 
 
 
b8350d6
 
1e83509
 
 
 
 
 
 
 
 
 
 
 
 
b8350d6
 
1e83509
 
 
a8a537c
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
import os
from sentence_transformers import SentenceTransformer
import pandas as pd
import chromadb
from chromadb.utils import embedding_functions
from scipy.spatial.distance import cosine
import gradio as gr
from tqdm import tqdm
import re

# Crear directorio para almacenamiento persistente
os.makedirs('./data_embeddings', exist_ok=True)

# Cargar el modelo de embeddings
model = SentenceTransformer("jinaai/jina-embeddings-v2-base-es", trust_remote_code=True)

# Ruta del archivo CSV
file_path = './v12_db_turismo.csv'

# Cargar la base de datos
df = pd.read_csv(file_path, encoding='latin-1')

# Función para limpiar y normalizar texto
def limpiar_texto(texto):
    texto = str(texto)
    texto = re.sub(r'[^a-zA-ZáéíóúñÁÉÍÓÚÑ0-9\s]', '', texto)  # Eliminar caracteres especiales
    texto = texto.lower()  # Convertir a minúsculas
    return texto.strip()

# Crear una columna combinada para los embeddings
df['text'] = df.apply(
    lambda x: limpiar_texto(
        f"Estado: {x['estado']}, "
        f"Municipio: {x['municipio']}, "
        f"Área natural o aventura: {x['area_natural_aventura']}, "
        f"Resumen: {x['resumen']}, "
        f"Descripción: {x['descripcion']}, "
        f"Ubicación: {x['ubicacion']}, "
        f"Link: {x['link']}, "
        f"Latitud: {x['latitud']}, Longitud: {x['longitud']}"
    ), axis=1
)

# Generar embeddings en batches de 64 con barra de progreso
print("Generando embeddings en lotes...")
df['embeddings'] = list(
    model.encode(
        df['text'].tolist(),  # Convertir la columna 'text' a una lista
        batch_size=64,        # Tamaño del batch
        show_progress_bar=True  # Mostrar barra de progreso
    )
)

# Crear identificadores únicos
df['ids'] = df.index.astype(str)

# Configurar cliente de ChromaDB persistente
chroma_client = chromadb.PersistentClient(path='./data_embeddings')
embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="jinaai/jina-embeddings-v2-base-es")

db = chroma_client.get_or_create_collection(name='turismo_db', embedding_function=embedding_function)

# Agregar datos a la colección
db.add(
    ids=df['ids'].tolist(),
    embeddings=df['embeddings'].tolist(),
    documents=df['text'].tolist(),
    metadatas=df[['estado', 'municipio', 'area_natural_aventura', 'resumen',
                  'descripcion', 'ubicacion', 'link', 'latitud', 'longitud']].to_dict('records')
)

print(f"Registros en la colección: {db.count()}")

# Función para realizar consultas
def obtener_recomendaciones(preferencias, top_k=5):
    # Limpiar y generar embeddings de la consulta
    preferencias = limpiar_texto(preferencias)
    consulta_embedding = model.encode(preferencias, show_progress_bar=False)

    # Realizar consulta en ChromaDB
    results = db.query(query_embeddings=[consulta_embedding], n_results=top_k)

    if not results or not results["metadatas"] or not results["metadatas"][0]:
        return []

    recomendaciones = []
    for idx, metadata in enumerate(results["metadatas"][0]):
        dist = results["distances"][0][idx]
        similitud_coseno = 1 - cosine(consulta_embedding, df['embeddings'].iloc[idx])
        metadata['distancia_euclidiana'] = dist
        metadata['similitud_coseno'] = similitud_coseno
        recomendaciones.append(metadata)

    # Ordenar por similitud coseno y distancia euclidiana
    recomendaciones = sorted(recomendaciones, key=lambda x: (x['distancia_euclidiana'], -x['similitud_coseno']))
    return recomendaciones

# Función para procesar resultados y generar una tabla
def procesar_recomendaciones(preferencias):
    recomendaciones = obtener_recomendaciones(preferencias)

    if not recomendaciones:
        return pd.DataFrame([{"Mensaje": "No se encontraron resultados para las preferencias proporcionadas."}])

    resultados = []
    for idx, sitio in enumerate(recomendaciones, 1):
        resultados.append({
            "#": idx,
            "Estado": sitio.get('estado', 'Desconocido'),
            "Municipio": sitio.get('municipio', 'No disponible'),
            "Área Natural o Aventura": sitio.get('area_natural_aventura', 'No disponible'),
            "Resumen": sitio.get('resumen', 'No disponible'),
            "Descripción": sitio.get('descripcion', 'No disponible'),
            "Ubicación": sitio.get('ubicacion', 'No disponible'),
            "Link": sitio.get('link', 'No disponible'),
            "Distancia Euclidiana": sitio.get('distancia_euclidiana', 'N/A'),
            "Similitud Coseno": sitio.get('similitud_coseno', 'N/A'),
        })

    return pd.DataFrame(resultados)

# Interfaz de Gradio
def interfaz(preferencias):
    tabla_resultados = procesar_recomendaciones(preferencias)
    return tabla_resultados

ui = gr.Interface(
    fn=interfaz,
    inputs=gr.Textbox(label="Preferencias", placeholder="Escribe tus preferencias, e.g., cañones, cascadas, áreas naturales"),
    outputs=gr.Dataframe(label="Resultados de Búsqueda"),
    title="Buscador de Turismo con Búsqueda Semántica Mejorada",
    description="Introduce tus preferencias para obtener resultados relevantes basados en similitud semántica y ordenados por relevancia."
)

if __name__ == "__main__":
    ui.launch(server_name="0.0.0.0", server_port=7860)