import gradio as gr import re import random from collections import defaultdict from typing import Dict, List, Tuple class NgramModel: def __init__(self): # Diccionarios para almacenar n-gramas y sus frecuencias self.ngram_dicts = { 1: defaultdict(int), 2: defaultdict(int), 3: defaultdict(int), 4: defaultdict(int), 5: defaultdict(int) } self.is_trained = False def preprocess_text(self, text: str) -> List[str]: """Preprocesa el texto para tokenizarlo en palabras.""" # Convertir a minúsculas y eliminar caracteres especiales text = text.lower() text = re.sub(r'[^\w\s]', ' ', text) # Dividir en palabras y eliminar espacios extra words = [word.strip() for word in text.split() if word.strip()] return words def train(self, text: str): """Entrena el modelo creando diccionarios de n-gramas.""" # Reiniciar los diccionarios self.ngram_dicts = { 1: defaultdict(int), 2: defaultdict(int), 3: defaultdict(int), 4: defaultdict(int), 5: defaultdict(int) } words = self.preprocess_text(text) # Crear n-gramas para n=1 hasta n=5 for n in range(1, 6): for i in range(len(words) - n + 1): ngram = tuple(words[i:i + n]) self.ngram_dicts[n][ngram] += 1 self.is_trained = True def get_random_unigram(self) -> Tuple[str, str]: """Selecciona una palabra aleatoria del diccionario de 1-gramas.""" # Obtener todas las palabras únicas de los 1-gramas unigrams = [(ngram[0], freq) for ngram, freq in self.ngram_dicts[1].items()] if not unigrams: return "", "No hay palabras disponibles en el modelo." # Seleccionar una palabra aleatoria y su frecuencia word, freq = random.choice(unigrams) explanation = "No se encontraron coincidencias para el contexto proporcionado.\n" explanation += "Seleccionando palabra aleatoria del vocabulario:\n\n" explanation += "Candidatos disponibles:\n" # Mostrar todas las palabras disponibles ordenadas por frecuencia sorted_unigrams = sorted(unigrams, key=lambda x: x[1], reverse=True) for w, f in sorted_unigrams: explanation += f"- '{w}' (frecuencia: {f})" if w == word: explanation += " ← SELECCIONADA ALEATORIAMENTE" explanation += "\n" return word, explanation def predict_next_word(self, text: str) -> Tuple[str, str]: """Predice la siguiente palabra basada en el contexto.""" if not self.is_trained: return "", "El modelo no ha sido entrenado aún." words = self.preprocess_text(text) if not words: return self.get_random_unigram() # Buscar coincidencias empezando por el n-grama más largo for n in range(min(5, len(words) + 1), 1, -1): # Note: ahora empezamos en n>1 context = tuple(words[-(n-1):]) # Tomamos n-1 palabras como contexto # Recoger coincidencias exactas candidates = {} for ngram, freq in self.ngram_dicts[n].items(): if ngram[:-1] == context: # Solo coincidencias exactas del contexto candidates[ngram[-1]] = freq if candidates: # Si encontramos coincidencias exactas # Encontrar la frecuencia máxima max_freq = max(candidates.values()) # Obtener todas las palabras con la frecuencia máxima max_candidates = [word for word, freq in candidates.items() if freq == max_freq] # Seleccionar aleatoriamente entre los candidatos con frecuencia máxima next_word = random.choice(max_candidates) # Crear explicación detallada explanation = f"Predicción basada en {n}-grama\n\n" explanation += f"Contexto utilizado: {' '.join(context)}\n\n" explanation += "Candidatos encontrados:\n" # Ordenar candidatos por frecuencia sorted_candidates = sorted(candidates.items(), key=lambda x: x[1], reverse=True) for word, freq in sorted_candidates: explanation += f"- '{word}' (frecuencia: {freq})" if freq == max_freq: explanation += " ← CANDIDATO MÁXIMA FRECUENCIA" if word == next_word: explanation += " ← SELECCIONADA" explanation += "\n" if len(max_candidates) > 1: explanation += "\nNota: Se seleccionó aleatoriamente entre las palabras con máxima frecuencia." return next_word, explanation # Si no se encontraron coincidencias en ningún n-grama, usar selección aleatoria return self.get_random_unigram() # Crear interfaz con Gradio def create_interface(): model = NgramModel() def train_model(text_input, file_input): """Entrena el modelo con texto o archivo.""" if file_input is not None: training_text = file_input elif text_input.strip(): training_text = text_input else: return "Por favor, proporcione texto de entrenamiento." model.train(training_text) return "Modelo entrenado exitosamente." def predict(input_text): """Realiza la predicción y actualiza el texto de entrada.""" if not model.is_trained: return input_text, "", "El modelo no ha sido entrenado aún." next_word, explanation = model.predict_next_word(input_text) new_text = input_text.strip() + " " + next_word if input_text.strip() else next_word return new_text, next_word, explanation # Crear interfaz con Gradio def create_interface(): model = NgramModel() def train_model(text_input, file_input): """Entrena el modelo con texto o archivo.""" if file_input is not None: training_text = file_input elif text_input.strip(): training_text = text_input else: return "Por favor, proporcione texto de entrenamiento." model.train(training_text) return "Modelo entrenado exitosamente." def predict(input_text): """Realiza la predicción y actualiza el texto de entrada.""" if not model.is_trained: return input_text, "", "El modelo no ha sido entrenado aún." next_word, explanation = model.predict_next_word(input_text) new_text = input_text.strip() + " " + next_word if input_text.strip() else next_word return new_text, next_word, explanation # Crear la interfaz con estilo mejorado with gr.Blocks(css=""" .logo { display: flex; justify-content: center; margin-bottom: 1rem; } .container { max-width: 900px; margin: auto; } .centered-title { text-align: center !important; } .centered-title h1 { text-align: center !important; margin-bottom: 1.5rem; } .centered-title h2 { text-align: center !important; margin-bottom: 1rem; } """) as interface: with gr.Column(elem_classes="container"): # Logo centrado #gr.HTML(""" # #""") #with gr.Column(elem_classes="centered-title"): # gr.Markdown("# ¡Construye tu propio miniGPT!") # gr.Markdown("### En la pestaña Entrenamiento puedes copiar o subir un texto base y entrenar tu miniGPT") # gr.Markdown("### En la pestaña Predicción puedes comenzar a escribir un texto y usar tu miniGPT para continuarlo automáticamente") with gr.Tab("Entrenamiento"): text_input = gr.Textbox( label="Texto de entrenamiento", lines=5, placeholder="Introduce aquí el texto para entrenar el modelo..." ) file_input = gr.File( label="O sube un archivo de texto", file_types=[".txt"] ) train_button = gr.Button( "Entrenar modelo", variant="primary" ) train_output = gr.Textbox( label="Estado del entrenamiento", interactive=False ) train_button.click( fn=train_model, inputs=[text_input, file_input], outputs=train_output ) with gr.Tab("Predicción"): input_text = gr.Textbox( label="Introduce texto", placeholder="Escribe aquí el texto para predecir la siguiente palabra..." ) predict_button = gr.Button( "Predecir siguiente palabra", variant="primary" ) predicted_word = gr.Textbox( label="Palabra predicha", interactive=False ) explanation = gr.Textbox( label="Explicación", lines=10, interactive=False ) predict_button.click( fn=predict, inputs=input_text, outputs=[input_text, predicted_word, explanation] ) return interface # Crear y lanzar la interfaz interface = create_interface() interface.launch()