miniGPTngram / app.py
jemole's picture
Quito logo y títulos para mejorar incrustado en Programamos website
c7190e0 verified
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("""
# <div class="logo">
# <img src="https://programamos.es/web/wp-content/uploads/2014/09/ProgramamosLogo.png"
# alt="Programamos Logo"
# style="height: 100px; object-fit: contain;">
# </div>
#""")
#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()