Spaces:
Running
Running
File size: 11,685 Bytes
20acd0c 632ef52 c4b1914 632ef52 c4b1914 5df7fe4 c4b1914 fed3071 c4b1914 20acd0c c4b1914 20acd0c c4b1914 20acd0c c4b1914 20acd0c c4b1914 20acd0c c4b1914 fed3071 c4b1914 fed3071 c4b1914 fed3071 c4b1914 20acd0c 540937a 20acd0c 632ef52 c4b1914 fed3071 c4b1914 632ef52 540937a fed3071 540937a fed3071 632ef52 fed3071 c4b1914 632ef52 c4b1914 540937a c4b1914 81fbbf6 c4b1914 29210fc c4b1914 81fbbf6 c4b1914 81fbbf6 540937a 81fbbf6 540937a 81fbbf6 540937a f74c751 540937a d76f986 fed3071 d76f986 fed3071 d76f986 540937a d76f986 fed3071 d76f986 c4b1914 540937a fed3071 540937a 4c4ec3c c4b1914 fed3071 c4b1914 4c4ec3c d76f986 4c4ec3c 81fbbf6 c4b1914 fed3071 c4b1914 81fbbf6 c4b1914 2cf4e9b 632ef52 c4b1914 632ef52 c4b1914 81fbbf6 c4b1914 9eaaf51 c4b1914 |
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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
from datasets import load_dataset, concatenate_datasets, Dataset
from sentence_transformers import SentenceTransformer
import numpy as np
import pandas as pd
import gradio as gr
import kagglehub
from kagglehub import KaggleDatasetAdapter
import faiss
import os
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import unicodedata
import random
# ==================================================
# Cargar datasets
# ==================================================
# Dataset 1: MyDramaList (5000 dramas)
mydramalist = kagglehub.load_dataset(
KaggleDatasetAdapter.PANDAS,
"anittasaju/complete-5000-dramas-from-mydramalist-review-info",
"Top_5000_popular_drama_details_from_mydramalist.csv"
)
mydramalist = Dataset.from_pandas(mydramalist)
# Dataset 2: Netflix Movies and Shows
netflix_movies_shows = load_dataset("harshi321/netflix-movies_shows")["train"]
# ==================================================
# Preprocesamiento de datos
# ==================================================
# Renombrar columnas para unificar los datasets
mydramalist = mydramalist.rename_column("name", "title").rename_column("content", "description")
# Filtrar por país (Corea del Sur)
def filter_kdramas(dataset):
if 'country' in dataset.features:
return dataset.filter(lambda x: x['country'] == "South Korea" if x['country'] else False)
return dataset
# Aplicar filtro a los datasets
kdramas1 = filter_kdramas(netflix_movies_shows)
kdramas2 = filter_kdramas(mydramalist)
# Eliminar columnas innecesarias (incluyendo 'rating')
columns_to_remove = ["Unnamed: 0", "no_of_reviews", "aka_names", "screenwriter", "director",
"no_of_viewers", "end_date", "start_date", "year", "duration", "no_of_rating",
"rank", "popularity", "content_rating", "where_to_watch", "main_role",
"support_role", "no_of_extracted_reviews", "Total_sentences",
"POSITIVE_people_sentiment", "POSITIVE_sentences", "NEGATIVE_people_sentiment",
"NEGATIVE_sentences", "rating"] # Eliminar 'rating'
for dataset in [kdramas1, kdramas2]:
dataset = dataset.remove_columns([col for col in columns_to_remove if col in dataset.features])
# Asegurar que todos los datasets tengan la columna 'type'
def add_type_column(dataset, default_type="TV Show"):
if 'type' not in dataset.features:
dataset = dataset.map(lambda x: {"type": default_type})
return dataset
kdramas1 = add_type_column(kdramas1, "TV Show")
kdramas2 = add_type_column(kdramas2, "TV Show")
# Asegurar que todos los datasets tengan la columna 'genres'
def add_genres_column(dataset, default_genres="Unknown"):
if 'genres' not in dataset.features:
dataset = dataset.map(lambda x: {"genres": default_genres})
return dataset
kdramas1 = add_genres_column(kdramas1, "Unknown")
kdramas2 = add_genres_column(kdramas2, "Unknown")
# Asegurar que todos los datasets tengan las mismas columnas y tipos
def align_datasets(dataset1, dataset2):
# Obtener las columnas comunes
common_columns = set(dataset1.features.keys()).intersection(set(dataset2.features.keys()))
# Mantener solo las columnas comunes
dataset1 = dataset1.select_columns(list(common_columns))
dataset2 = dataset2.select_columns(list(common_columns))
return dataset1, dataset2
# Alinear los datasets
kdramas1, kdramas2 = align_datasets(kdramas1, kdramas2)
# Eliminar columnas adicionales que no se usan
kdramas1 = kdramas1.remove_columns(['rating'])
kdramas2 = kdramas2.remove_columns(['rating'])
# Combinar todos los datasets
kdramas = concatenate_datasets([kdramas1, kdramas2])
# Eliminar duplicados basados en el título
kdramas_df = kdramas.to_pandas().drop_duplicates(subset=['title'])
kdramas = Dataset.from_pandas(kdramas_df)
# ==================================================
# Modelo de embeddings y recomendación
# ==================================================
# Cargar el modelo de embeddings
model = SentenceTransformer('sentence-transformers/paraphrase-MiniLM-L6-v2')
# Calcular o cargar embeddings
# Verificar si el archivo de embeddings existe
if os.path.exists("kdrama_embeddings.npy"):
# Cargar embeddings precalculados
embeddings_np = np.load("kdrama_embeddings.npy")
else:
# Calcular embeddings y guardarlos
descriptions = kdramas["description"]
embeddings = model.encode(descriptions, convert_to_tensor=True)
embeddings_np = embeddings.cpu().numpy()
np.save("kdrama_embeddings.npy", embeddings_np)
print("¡Embeddings listos! Cada descripción ahora es un vector numérico.")
# Crear un índice FAISS para búsqueda eficiente
dimension = embeddings_np.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(embeddings_np)
# Función para recomendar K-Dramas similares
def recommend_kdramas(title, k=5):
title_indices = [i for i, t in enumerate(kdramas['title']) if title.lower() in t.lower()]
if not title_indices:
return f"No se encontraron títulos similares a '{title}'."
query_embedding = embeddings_np[title_indices[0]].reshape(1, -1)
distances, similar_indices = index.search(query_embedding, k + 10) # Ampliar el rango de búsqueda
# Seleccionar aleatoriamente k índices de los 10 más similares
selected_indices = random.sample(list(similar_indices[0][1:]), k)
recommendations = []
for i in selected_indices:
recommended_title = kdramas["title"][i]
recommended_type = kdramas["type"][i] if "type" in kdramas.features else "Unknown"
recommended_genres = kdramas["genres"][i] if "genres" in kdramas.features else "Unknown"
recommendations.append(
f"### {recommended_title}\n"
f"- **Tipo**: {recommended_type}\n"
f"- **Géneros**: {recommended_genres}\n"
)
return "\n".join(recommendations)
# ==================================================
# Chatbot
# ==================================================
# Cargar el modelo de chat
tokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-medium")
model_chat = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-medium")
# Mapeo de géneros (español sin tildes → inglés)
mapeo_generos = {
# Géneros (sin tildes)
"romantico": "romantic",
"accion": "action",
"misterio": "mystery",
"comedia": "comedy",
"drama": "drama",
"crimen": "crime",
"fantasia": "fantasy",
"thriller": "thriller",
"romance": "romance",
# Palabras clave adicionales (sin tildes)
"aventura": "adventure",
"historico": "historical",
# Agrega más según sea necesario
}
# Función para normalizar texto
def normalizar_texto(texto):
# Eliminar tildes y convertir a minúsculas
texto_normalizado = ''.join(
letra for letra in unicodedata.normalize('NFD', texto)
if unicodedata.category(letra) != 'Mn'
)
return texto_normalizado.lower()
# Función para traducir preferencias
def traducir_preferencia(entrada_usuario):
entrada_normalizada = normalizar_texto(entrada_usuario)
for espanol, ingles in mapeo_generos.items():
if espanol in entrada_normalizada:
return ingles
return None # Si no se encuentra una traducción
# Función para buscar K-Dramas por género
def buscar_por_genero(genero, k=5):
# Filtrar K-Dramas que contengan el género especificado
genre_embedding = model.encode(genero, convert_to_tensor=True).cpu().numpy()
genre_embedding = genre_embedding.reshape(1, -1)
# Buscar en el índice FAISS
D, I = index.search(genre_embedding, k * 2) # Ampliar el rango de búsqueda
# Seleccionar aleatoriamente k índices de los resultados
selected_indices = random.sample(list(I[0]), k)
# Formatear las recomendaciones
recommendations = []
for i in selected_indices:
recommended_title = kdramas["title"][i]
recommended_type = kdramas["type"][i] if "type" in kdramas.features else "Unknown"
recommended_genres = kdramas["genres"][i] if "genres" in kdramas.features else "Unknown"
recommendations.append(
f"{recommended_title}\n"
f"- **Tipo**: {recommended_type}\n"
f"- **Géneros**: {recommended_genres}\n"
)
return "\n".join(recommendations) if recommendations else f"No se encontraron K-Dramas del género '{genero}'."
# Función para recomendar K-Dramas basado en preferencias
def recomendar_kdramas_chat(entrada_usuario):
# Traducir preferencia del usuario
preferencia_traducida = traducir_preferencia(entrada_usuario)
if preferencia_traducida:
return buscar_por_genero(preferencia_traducida, k=5)
else:
return None # No se encontró una preferencia válida
# Función para generar respuestas del chatbot
def generar_respuesta(entrada_usuario, historial_chat=""):
inputs = tokenizer.encode(entrada_usuario + historial_chat, return_tensors="pt")
respuesta_ids = model_chat.generate(inputs, max_length=1000, pad_token_id=tokenizer.eos_token_id)
respuesta = tokenizer.decode(respuesta_ids[:, inputs.shape[-1]:][0], skip_special_tokens=True)
return respuesta
# Función para manejar la interacción del chatbot
def chat(entrada_usuario, historial_chat=""):
# Agregar el mensaje del usuario al historial
historial_chat += f"Usuario: {entrada_usuario}\n"
# Generar una respuesta natural del chatbot
if not historial_chat.strip(): # Si es la primera interacción
respuesta = "¡Hola! Soy tu asistente para recomendar K-Dramas. ¿Qué tipo de K-Drama te gustaría ver hoy?"
else:
respuesta = generar_respuesta(entrada_usuario, historial_chat)
# Verificar si el usuario mencionó un género
recomendacion = recomendar_kdramas_chat(entrada_usuario)
if recomendacion:
respuesta = f"¡Genial! Aquí tienes algunas recomendaciones de K-Dramas que podrían gustarte:\n{recomendacion}"
else:
respuesta = "¿Qué tipo de K-Drama te gustaría ver? Puedo recomendarte dramas de acción, romance, misterio y más."
# Agregar la respuesta del chatbot al historial
historial_chat += f"Chatbot: {respuesta}\n"
# Devolver la respuesta y el historial actualizado
return respuesta, historial_chat, "" # Limpiar el cuadro de texto
# ==================================================
# Interfaz de Gradio
# ==================================================
# Interfaz para el chatbot
interfaz_chatbot = gr.Interface(
fn=chat,
inputs=[gr.Textbox(label="Escribe tu mensaje"), gr.Textbox(label="Historial", visible=False)],
outputs=[gr.Textbox(label="Respuesta del chatbot"), gr.Textbox(label="Mensaje", visible=False)],
title="Chatbot Recomendador de K-Dramas",
description="Habla con el chatbot para obtener recomendaciones personalizadas de K-Dramas.",
allow_flagging="never"
)
# Interfaz para el recomendador tradicional
interfaz_recomendador = gr.Interface(
theme=gr.themes.Citrus(),
fn=recommend_kdramas,
inputs=[
gr.Textbox(label="Ingresa el título de un K-Drama"),
],
outputs=gr.Markdown(label="K-Dramas recomendados"),
title="Recomendador de K-Dramas",
description="Ingresa el título de un K-Drama y recibe recomendaciones similares, indicando si son series o películas y sus géneros.",
examples=[["Vincenzo", 5], ["Crash Landing on You", 3], ["Itaewon Class", 4]],
allow_flagging="never",
)
# Lanzar ambas interfaces
gr.TabbedInterface(
[interfaz_recomendador, interfaz_chatbot], # Chatbot en la segunda pestaña
["Recomendador", "Chatbot"]
).launch() |