Spaces:
Runtime error
Runtime error
File size: 13,763 Bytes
03b3c9a 578774c 03b3c9a 578774c 7c68366 ceb558e 03b3c9a 578774c 5f7cb64 578774c 03b3c9a 578774c 5f7cb64 578774c 5f7cb64 578774c 03b3c9a 578774c 7c68366 578774c 7c68366 578774c 7c68366 ceb558e 7c68366 578774c 7c68366 578774c 7c68366 03b3c9a 578774c 03b3c9a 578774c 7c68366 5f7cb64 7c68366 5f7cb64 7c68366 578774c 7c68366 578774c 7c68366 03b3c9a 578774c 7c68366 578774c 7c68366 578774c 7c68366 578774c 7c68366 578774c 5f7cb64 03b3c9a 578774c 17d5260 7c68366 578774c 17d5260 7c68366 578774c 17d5260 5f7cb64 17d5260 5f7cb64 17d5260 7c68366 578774c ceb558e 578774c ceb558e 578774c ceb558e 578774c 7c68366 578774c 5f7cb64 17d5260 578774c 7c68366 17d5260 578774c 5f7cb64 578774c 5f7cb64 578774c 7c68366 578774c 17d5260 578774c ceb558e 578774c 17d5260 ceb558e 03b3c9a 17d5260 4509f5e 578774c 03b3c9a 7c68366 578774c 5f7cb64 7c68366 |
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 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
import os
import gradio as gr
import threading
import time
from huggingface_hub import login
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling, pipeline
from peft import get_peft_model, LoraConfig, TaskType, PeftModel
import json
# --- CONFIGURACIÓN DEL MODELO Y ENTRENAMIENTO ---
BASE_MODEL = "bigcode/santacoder" # Modelo base de programación
LORA_PATH = "./lora_output" # Ruta donde se guarda el modelo adaptado
DATASET_FILE = "codesearchnet_lora_dataset.json"
MAX_TOKEN_LENGTH = 256
NUM_SAMPLES_TO_PROCESS = 1000
DEFAULT_EPOCHS = 10
# Configuración del ciclo AUTÓNOMO (Inicia reentrenamiento cada 5 interacciones)
GENERATION_LIMIT_TO_TRAIN = 5
AUTONOMOUS_EPOCHS = 3
# Nueva configuración para manejo de modelos grandes
TEMP_OFFLOAD_FOLDER = "./temp_offload"
# --- ESTADO GLOBAL Y THREADING ---
tokenizer = None
lora_model = None
tokenized_dataset = None
lora_generator = None
# Variables de estado
version_number = 1.1 # Asumimos que el último entrenamiento llegó a V1.1
is_trained = os.path.exists(LORA_PATH)
generations_since_last_train = 0
training_status_message = f"Modelo V{version_number:.1f} listo."
# Lock para proteger las variables compartidas entre hilos (CRÍTICO para estabilidad)
global_lock = threading.Lock()
# --- LÓGICA DE PREPARACIÓN Y SETUP ---
def prepare_codesearchnet():
"""Descarga y prepara el dataset inicial si no existe."""
if os.path.exists(DATASET_FILE):
return
try:
raw_csn = load_dataset('Nan-Do/code-search-net-python', split=f'train[:{NUM_SAMPLES_TO_PROCESS}]')
def format_for_lora(example):
# Formato que entrena a la IA a enlazar descripción (español) con código (inglés)
prompt_text = (
f"# Descripción: {example['docstring_summary']}\n"
f"# Completa la siguiente función:\n"
f"def {example['func_name']}("
)
completion_text = example['code']
return {"prompt": prompt_text, "completion": completion_text}
lora_dataset = raw_csn.map(format_for_lora, batched=False, remove_columns=raw_csn["train"].column_names)
lora_dataset.to_json(DATASET_FILE)
except Exception as e:
print(f"Error al cargar dataset. Usando datos mínimos. Error: {e}")
minimal_dataset = [{"prompt": "# Error de carga. Intenta de nuevo.", "completion": "pass\n"}] * 10
with open(DATASET_FILE, 'w') as f:
json.dump(minimal_dataset, f)
def setup_resources():
"""Configura el tokenizer, el modelo base y el adaptador LoRA."""
global tokenizer, lora_model, tokenized_dataset
# Crear la carpeta de offload si no existe (CRÍTICO para el error actual)
os.makedirs(TEMP_OFFLOAD_FOLDER, exist_ok=True)
prepare_codesearchnet()
hf_token = os.environ.get("HF_TOKEN")
if hf_token:
login(token=hf_token)
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
# Añadir safetensors=True y offload_folder
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
device_map="auto",
offload_folder=TEMP_OFFLOAD_FOLDER, # SOLUCIÓN 1
low_cpu_mem_usage=True,
trust_remote_code=True,
)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["c_proj", "c_attn"],
)
lora_model = get_peft_model(base_model, peft_config)
try:
raw_dataset = load_dataset("json", data_files=DATASET_FILE)
def tokenize_function(examples):
return tokenizer(
examples["prompt"] + examples["completion"],
truncation=True,
padding="max_length",
max_length=MAX_TOKEN_LENGTH
)
tokenized_dataset = raw_dataset.map(tokenize_function, batched=True, remove_columns=raw_dataset["train"].column_names if "train" in raw_dataset else [],)
except Exception:
tokenized_dataset = None
# --- FUNCIÓN DE ENTRENAMIENTO (EJECUTADA EN HILO SEPARADO) ---
def autonomous_train_lora(epochs, batch_size, learning_rate):
"""Ejecuta el entrenamiento en un hilo separado para la autonomía."""
global lora_model, tokenized_dataset, lora_generator, version_number, is_trained, training_status_message
try:
with global_lock:
if tokenized_dataset is None or "train" not in tokenized_dataset:
training_status_message = "ERROR: No se puede entrenar. Dataset no disponible."
return
# 1. ACTUALIZAR VERSIÓN (Pre-incremento)
if is_trained:
version_number += 0.1
else:
version_number = 1.0
# 2. CONFIGURACIÓN E INICIO DEL ENTRENAMIENTO
training_status_message = f"🧠 ENTRENANDO V{version_number:.1f} (Epochs: {epochs})...."
print(f"\n[AUTÓNOMO] {training_status_message}")
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
training_args = TrainingArguments(
output_dir=LORA_PATH,
per_device_train_batch_size=int(batch_size),
num_train_epochs=float(epochs),
learning_rate=float(learning_rate),
save_total_limit=1,
logging_steps=10,
push_to_hub=False,
disable_tqdm=True,
report_to="none"
)
trainer = Trainer(model=lora_model, args=training_args, train_dataset=tokenized_dataset["train"], data_collator=data_collator)
trainer.train()
lora_model.save_pretrained(LORA_PATH)
tokenizer.save_pretrained(LORA_PATH)
# 3. Marcar como entrenado
is_trained = True
training_status_message = f"✅ ENTRENAMIENTO V{version_number:.1f} COMPLETADO. Modelo listo para Hot Swap."
print(f"[AUTÓNOMO] {training_status_message}")
except Exception as e:
training_status_message = f"ERROR CRÍTICO durante el entrenamiento autónomo: {e}"
print(f"[AUTÓNOMO] {training_status_message}")
# --- FUNCIÓN DE GENERACIÓN (CORREGIDA PARA HOT SWAP ESTABLE) ---
def generate_text(prompt_text):
"""Genera código y dispara el ciclo de reentrenamiento autónomo si es necesario."""
global lora_generator, generations_since_last_train, is_trained, version_number, training_status_message
if not is_trained:
return "ERROR: El modelo LoRA no ha sido entrenado. Por favor, espere mientras la IA se inicializa con el entrenamiento V1.0.", update_status()
# 1. HOT SWAP (Verifica si el modelo necesita recargarse con la nueva versión)
if lora_generator is None:
with global_lock:
try:
# SOLUCIÓN CLAVE: Reintroducir offload_folder y low_cpu_mem_usage aquí también.
base_model_gen = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
device_map="auto",
offload_folder=TEMP_OFFLOAD_FOLDER, # SOLUCIÓN 2
low_cpu_mem_usage=True,
trust_remote_code=True,
)
model_with_lora = PeftModel.from_pretrained(base_model_gen, LORA_PATH)
final_model = model_with_lora.merge_and_unload()
final_model.eval()
lora_generator = pipeline("text-generation", model=final_model, tokenizer=tokenizer)
print(f"[HOT SWAP] 🔄 Modelo de inferencia V{version_number:.1f} recargado y listo.")
except Exception as e:
# Si la recarga falla, retorna un error
return f"Error al cargar el modelo V{version_number:.1f} para inferencia: {e}", update_status()
# 2. Generación de texto (Lógica de inferencia)
try:
# Prepara el prompt para guiar la generación del código
prompt_with_indent = prompt_text.strip() + "\n "
output = lora_generator(prompt_with_indent, max_new_tokens=150, temperature=0.7, top_p=0.9, clean_up_tokenization_spaces=True)
full_output = output[0]["generated_text"]
# Extrae solo la parte de la compleción (el código generado)
start_index = full_output.find(prompt_with_indent)
completion = full_output[start_index + len(prompt_with_indent):] if start_index != -1 else full_output
# 3. Aumentar contador de autonomía
with global_lock:
generations_since_last_train += 1
current_count = generations_since_last_train
current_version = version_number
# 4. Verificar si se requiere reentrenamiento (y dispararlo en un nuevo hilo)
notification = ""
if current_count >= GENERATION_LIMIT_TO_TRAIN:
# Verifica que no haya otro hilo de entrenamiento ya corriendo
if not any(isinstance(t, threading.Thread) and t.name == 'AutonomousTrainer' for t in threading.enumerate()):
print(f"[AUTONOMÍA] Generación #{current_count} alcanzada. Disparando reentrenamiento autónomo en segundo plano...")
# Reiniciar el contador de generaciones y forzar Hot Swap en la próxima interacción
with global_lock:
generations_since_last_train = 0
lora_generator = None
trainer_thread = threading.Thread(
target=autonomous_train_lora,
args=(AUTONOMOUS_EPOCHS, 2, 5e-5),
name='AutonomousTrainer'
)
trainer_thread.daemon = True
trainer_thread.start()
notification = f"\n\n--- [AUTONOMÍA] La IA ha iniciado el reentrenamiento V{current_version+0.1:.1f} para mejorar la traducción de tu diálogo. La próxima generación cargará la nueva versión. ---"
# Retorna el código Y el estado actualizado
return completion + notification, update_status()
except Exception as e:
# Si falla la generación, retorna el mensaje de error y el estado actual
return f"Error generando texto: {e}", update_status()
# --- FUNCIÓN PARA INICIALIZACIÓN Y ENTRENAMIENTO V1.0 (Obligatorio) ---
def initialize_and_train_v1():
"""Ejecuta el entrenamiento inicial V1.0 de forma autónoma al iniciar."""
global version_number, is_trained, training_status_message
if not is_trained:
autonomous_train_lora(epochs=DEFAULT_EPOCHS, batch_size=2, learning_rate=5e-5)
else:
# Si ya está entrenado, actualiza la versión y el mensaje
training_status_message = f"✅ Modelo V{version_number:.1f} ya entrenado. Listo."
print(f"[INICIALIZACIÓN] {training_status_message}")
# --- FUNCIÓN PARA ACTUALIZAR EL ESTADO EN LA UI ---
def update_status():
"""Actualiza la versión y el estado del entrenamiento en la interfaz de Gradio."""
global training_status_message, version_number
# Retorna un texto en Markdown que se actualiza constantemente
return f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}"
# --- INTERFAZ GRADIO ---
with gr.Blocks(title="AmorCoderAI - Aprendizaje Continuo") as demo:
gr.Markdown("# 💙 AmorCoderAI - Asistente de Código con Aprendizaje Continuo")
# Muestra la versión y el estado.
version_and_status = gr.Markdown(
f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}",
elem_id="status_display"
)
gr.Markdown(f"**Modo Autónomo:** La IA se reentrena automáticamente cada **{GENERATION_LIMIT_TO_TRAIN}** códigos generados. Esto mejora su capacidad para traducir tu español conversacional a código.")
with gr.Tab("✨ Generación de Código"):
gr.Markdown("## Escribe tu idea en palabras (¡Usa español fluido!)")
gr.Markdown("Recomendación inicial: Usa el siguiente formato para obtener el mejor código mientras la IA aprende tu idioma:")
prompt = gr.Textbox(
label="Instrucción de Programación:",
lines=4,
placeholder="# Descripción: Quiero que me hagas un código similar a Google Gemini.\n# Completa la siguiente función:\ndef generar_contenido(prompt, modelo):"
)
generate_button = gr.Button("💬 Generar código y disparar Aprendizaje")
output_box = gr.Textbox(label="Código generado", lines=10)
# Conexión del botón con la función principal
generate_button.click(
generate_text,
inputs=prompt,
outputs=[output_box, version_and_status],
)
# El estado se actualiza solo al cargar la página.
demo.load(update_status, None, version_and_status)
# --- INICIO DE LA APLICACIÓN ---
if __name__ == "__main__":
setup_resources()
# Lanza el entrenamiento V1.0 inicial en un hilo para que no congele la UI
initialization_thread = threading.Thread(target=initialize_and_train_v1, name='InitializationTrainer')
initialization_thread.daemon = True
initialization_thread.start()
print(f"\n💻 LANZANDO INTERFAZ GRADIO (El entrenamiento V1.0/V1.1 se ejecuta en segundo plano)")
demo.launch()
|