Andro0s commited on
Commit
5d78646
·
verified ·
1 Parent(s): c3f2fc0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -304
app.py CHANGED
@@ -1,311 +1,63 @@
1
- import os
2
- import gradio as gr
3
- import threading
4
- import time
5
- from huggingface_hub import login
6
- from datasets import load_dataset
7
- from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling, pipeline
8
- from peft import get_peft_model, LoraConfig, TaskType, PeftModel
9
- import json
10
-
11
- # --- CONFIGURACIÓN DEL MODELO Y ENTRENAMIENTO ---
12
- BASE_MODEL = "bigcode/santacoder" # Modelo base de programación
13
- LORA_PATH = "./lora_output" # Ruta donde se guarda el modelo adaptado
14
- DATASET_FILE = "codesearchnet_lora_dataset.json"
15
- MAX_TOKEN_LENGTH = 256
16
- NUM_SAMPLES_TO_PROCESS = 1000
17
- DEFAULT_EPOCHS = 10
18
-
19
- # Configuración del ciclo AUTÓNOMO (Inicia reentrenamiento cada 5 interacciones)
20
- GENERATION_LIMIT_TO_TRAIN = 5
21
- AUTONOMOUS_EPOCHS = 3
22
-
23
- # Nueva configuración para manejo de modelos grandes
24
- TEMP_OFFLOAD_FOLDER = "./temp_offload"
25
-
26
- # --- ESTADO GLOBAL Y THREADING ---
27
- tokenizer = None
28
- lora_model = None
29
- tokenized_dataset = None
30
- lora_generator = None
31
-
32
- # Variables de estado
33
- version_number = 1.1 # Asumimos que el último entrenamiento llegó a V1.1
34
- is_trained = os.path.exists(LORA_PATH)
35
- generations_since_last_train = 0
36
- training_status_message = f"Modelo V{version_number:.1f} listo."
37
-
38
- # Lock para proteger las variables compartidas entre hilos (CRÍTICO para estabilidad)
39
- global_lock = threading.Lock()
40
-
41
- # --- LÓGICA DE PREPARACIÓN Y SETUP ---
42
-
43
- def prepare_codesearchnet():
44
- """Descarga y prepara el dataset inicial si no existe."""
45
- if os.path.exists(DATASET_FILE):
46
- return
47
  try:
48
- raw_csn = load_dataset('Nan-Do/code-search-net-python', split=f'train[:{NUM_SAMPLES_TO_PROCESS}]')
49
-
50
- def format_for_lora(example):
51
- # Formato que entrena a la IA a enlazar descripción (español) con código (inglés)
52
- prompt_text = (
53
- f"# Descripción: {example['docstring_summary']}\n"
54
- f"# Completa la siguiente función:\n"
55
- f"def {example['func_name']}("
56
- )
57
- completion_text = example['code']
58
- return {"prompt": prompt_text, "completion": completion_text}
59
-
60
- lora_dataset = raw_csn.map(format_for_lora, batched=False, remove_columns=raw_csn["train"].column_names)
61
- lora_dataset.to_json(DATASET_FILE)
62
- except Exception as e:
63
- print(f"Error al cargar dataset. Usando datos mínimos. Error: {e}")
64
- minimal_dataset = [{"prompt": "# Error de carga. Intenta de nuevo.", "completion": "pass\n"}] * 10
65
- with open(DATASET_FILE, 'w') as f:
66
- json.dump(minimal_dataset, f)
67
-
68
-
69
- def setup_resources():
70
- """Configura el tokenizer, el modelo base y el adaptador LoRA."""
71
- global tokenizer, lora_model, tokenized_dataset
72
-
73
- # Crear la carpeta de offload si no existe (CRÍTICO para el error actual)
74
- os.makedirs(TEMP_OFFLOAD_FOLDER, exist_ok=True)
75
-
76
- prepare_codesearchnet()
77
-
78
- hf_token = os.environ.get("HF_TOKEN")
79
- if hf_token:
80
- login(token=hf_token)
81
-
82
- tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
83
-
84
- # Añadir safetensors=True y offload_folder
85
- base_model = AutoModelForCausalLM.from_pretrained(
86
- BASE_MODEL,
87
- device_map="auto",
88
- offload_folder=TEMP_OFFLOAD_FOLDER, # SOLUCIÓN 1
89
- low_cpu_mem_usage=True,
90
- trust_remote_code=True,
91
- )
92
-
93
- if tokenizer.pad_token is None:
94
- tokenizer.pad_token = tokenizer.eos_token
95
-
96
- peft_config = LoraConfig(
97
- task_type=TaskType.CAUSAL_LM, r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["c_proj", "c_attn"],
98
- )
99
- lora_model = get_peft_model(base_model, peft_config)
100
-
101
- try:
102
- raw_dataset = load_dataset("json", data_files=DATASET_FILE)
103
-
104
- def tokenize_function(examples):
105
- return tokenizer(
106
- examples["prompt"] + examples["completion"],
107
- truncation=True,
108
- padding="max_length",
109
- max_length=MAX_TOKEN_LENGTH
110
- )
111
-
112
- tokenized_dataset = raw_dataset.map(tokenize_function, batched=True, remove_columns=raw_dataset["train"].column_names if "train" in raw_dataset else [],)
113
- except Exception:
114
- tokenized_dataset = None
115
-
116
- # --- FUNCIÓN DE ENTRENAMIENTO (EJECUTADA EN HILO SEPARADO) ---
117
-
118
- def autonomous_train_lora(epochs, batch_size, learning_rate):
119
- """Ejecuta el entrenamiento en un hilo separado para la autonomía."""
120
- global lora_model, tokenized_dataset, lora_generator, version_number, is_trained, training_status_message
121
-
122
- try:
123
- with global_lock:
124
- if tokenized_dataset is None or "train" not in tokenized_dataset:
125
- training_status_message = "ERROR: No se puede entrenar. Dataset no disponible."
126
- return
127
-
128
- # 1. ACTUALIZAR VERSIÓN (Pre-incremento)
129
- if is_trained:
130
- version_number += 0.1
131
- else:
132
- version_number = 1.0
133
-
134
- # 2. CONFIGURACIÓN E INICIO DEL ENTRENAMIENTO
135
- training_status_message = f"🧠 ENTRENANDO V{version_number:.1f} (Epochs: {epochs})...."
136
- print(f"\n[AUTÓNOMO] {training_status_message}")
137
-
138
- data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
139
- training_args = TrainingArguments(
140
- output_dir=LORA_PATH,
141
- per_device_train_batch_size=int(batch_size),
142
- num_train_epochs=float(epochs),
143
- learning_rate=float(learning_rate),
144
- save_total_limit=1,
145
- logging_steps=10,
146
- push_to_hub=False,
147
- disable_tqdm=True,
148
- report_to="none"
149
- )
150
-
151
- trainer = Trainer(model=lora_model, args=training_args, train_dataset=tokenized_dataset["train"], data_collator=data_collator)
152
-
153
- trainer.train()
154
- lora_model.save_pretrained(LORA_PATH)
155
- tokenizer.save_pretrained(LORA_PATH)
156
-
157
- # 3. Marcar como entrenado
158
- is_trained = True
159
- training_status_message = f"✅ ENTRENAMIENTO V{version_number:.1f} COMPLETADO. Modelo listo para Hot Swap."
160
- print(f"[AUTÓNOMO] {training_status_message}")
161
-
162
- except Exception as e:
163
- training_status_message = f"ERROR CRÍTICO durante el entrenamiento autónomo: {e}"
164
- print(f"[AUTÓNOMO] {training_status_message}")
165
-
166
- # --- FUNCIÓN DE GENERACIÓN (CORREGIDA PARA HOT SWAP ESTABLE) ---
167
-
168
- def generate_text(prompt_text):
169
- """Genera código y dispara el ciclo de reentrenamiento autónomo si es necesario."""
170
- global lora_generator, generations_since_last_train, is_trained, version_number, training_status_message
171
-
172
- if not is_trained:
173
- return "ERROR: El modelo LoRA no ha sido entrenado. Por favor, espere mientras la IA se inicializa con el entrenamiento V1.0.", update_status()
174
-
175
-
176
- # 1. HOT SWAP (Verifica si el modelo necesita recargarse con la nueva versión)
177
- if lora_generator is None:
178
- with global_lock:
179
- try:
180
- # SOLUCIÓN CLAVE: Reintroducir offload_folder y low_cpu_mem_usage aquí también.
181
- base_model_gen = AutoModelForCausalLM.from_pretrained(
182
- BASE_MODEL,
183
- device_map="auto",
184
- offload_folder=TEMP_OFFLOAD_FOLDER, # SOLUCIÓN 2
185
- low_cpu_mem_usage=True,
186
- trust_remote_code=True,
187
- )
188
- model_with_lora = PeftModel.from_pretrained(base_model_gen, LORA_PATH)
189
- final_model = model_with_lora.merge_and_unload()
190
- final_model.eval()
191
- lora_generator = pipeline("text-generation", model=final_model, tokenizer=tokenizer)
192
- print(f"[HOT SWAP] 🔄 Modelo de inferencia V{version_number:.1f} recargado y listo.")
193
- except Exception as e:
194
- # Si la recarga falla, retorna un error
195
- return f"Error al cargar el modelo V{version_number:.1f} para inferencia: {e}", update_status()
196
-
197
 
198
- # 2. Generación de texto (Lógica de inferencia)
199
- try:
200
- # Prepara el prompt para guiar la generación del código
201
- prompt_with_indent = prompt_text.strip() + "\n "
202
- output = lora_generator(prompt_with_indent, max_new_tokens=150, temperature=0.7, top_p=0.9, clean_up_tokenization_spaces=True)
203
- full_output = output[0]["generated_text"]
204
 
205
- # Extrae solo la parte de la compleción (el código generado)
206
- start_index = full_output.find(prompt_with_indent)
207
- completion = full_output[start_index + len(prompt_with_indent):] if start_index != -1 else full_output
208
-
209
- # 3. Aumentar contador de autonomía
210
- with global_lock:
211
- generations_since_last_train += 1
212
- current_count = generations_since_last_train
213
- current_version = version_number
214
-
215
- # 4. Verificar si se requiere reentrenamiento (y dispararlo en un nuevo hilo)
216
- notification = ""
217
- if current_count >= GENERATION_LIMIT_TO_TRAIN:
218
- # Verifica que no haya otro hilo de entrenamiento ya corriendo
219
- if not any(isinstance(t, threading.Thread) and t.name == 'AutonomousTrainer' for t in threading.enumerate()):
220
- print(f"[AUTONOMÍA] Generación #{current_count} alcanzada. Disparando reentrenamiento autónomo en segundo plano...")
221
-
222
- # Reiniciar el contador de generaciones y forzar Hot Swap en la próxima interacción
223
- with global_lock:
224
- generations_since_last_train = 0
225
- lora_generator = None
226
-
227
- trainer_thread = threading.Thread(
228
- target=autonomous_train_lora,
229
- args=(AUTONOMOUS_EPOCHS, 2, 5e-5),
230
- name='AutonomousTrainer'
231
- )
232
- trainer_thread.daemon = True
233
- trainer_thread.start()
234
-
235
- 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. ---"
236
-
237
- # Retorna el código Y el estado actualizado
238
- return completion + notification, update_status()
239
 
240
  except Exception as e:
241
- # Si falla la generación, retorna el mensaje de error y el estado actual
242
- return f"Error generando texto: {e}", update_status()
243
-
244
- # --- FUNCIÓN PARA INICIALIZACIÓN Y ENTRENAMIENTO V1.0 (Obligatorio) ---
245
-
246
- def initialize_and_train_v1():
247
- """Ejecuta el entrenamiento inicial V1.0 de forma autónoma al iniciar."""
248
- global version_number, is_trained, training_status_message
249
- if not is_trained:
250
- autonomous_train_lora(epochs=DEFAULT_EPOCHS, batch_size=2, learning_rate=5e-5)
251
- else:
252
- # Si ya está entrenado, actualiza la versión y el mensaje
253
- training_status_message = f"✅ Modelo V{version_number:.1f} ya entrenado. Listo."
254
- print(f"[INICIALIZACIÓN] {training_status_message}")
255
-
256
- # --- FUNCIÓN PARA ACTUALIZAR EL ESTADO EN LA UI ---
257
 
258
- def update_status():
259
- """Actualiza la versión y el estado del entrenamiento en la interfaz de Gradio."""
260
- global training_status_message, version_number
261
- # Retorna un texto en Markdown que se actualiza constantemente
262
- return f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}"
263
-
264
-
265
- # --- INTERFAZ GRADIO ---
266
- with gr.Blocks(title="AmorCoderAI - Aprendizaje Continuo") as demo:
267
- gr.Markdown("# 💙 AmorCoderAI - Asistente de Código con Aprendizaje Continuo")
268
-
269
- # Muestra la versión y el estado.
270
- version_and_status = gr.Markdown(
271
- f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}",
272
- elem_id="status_display"
273
- )
274
-
275
- 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.")
276
-
277
- with gr.Tab("✨ Generación de Código"):
278
- gr.Markdown("## Escribe tu idea en palabras (¡Usa español fluido!)")
279
-
280
- gr.Markdown("Recomendación inicial: Usa el siguiente formato para obtener el mejor código mientras la IA aprende tu idioma:")
281
-
282
- prompt = gr.Textbox(
283
- label="Instrucción de Programación:",
284
- lines=4,
285
- 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):"
286
- )
287
- generate_button = gr.Button("💬 Generar código y disparar Aprendizaje")
288
- output_box = gr.Textbox(label="Código generado", lines=10)
289
-
290
- # Conexión del botón con la función principal
291
- generate_button.click(
292
- generate_text,
293
- inputs=prompt,
294
- outputs=[output_box, version_and_status],
295
- )
296
-
297
- # El estado se actualiza solo al cargar la página.
298
- demo.load(update_status, None, version_and_status)
299
-
300
-
301
- # --- INICIO DE LA APLICACIÓN ---
302
  if __name__ == "__main__":
303
- setup_resources()
304
-
305
- # Lanza el entrenamiento V1.0 inicial en un hilo para que no congele la UI
306
- initialization_thread = threading.Thread(target=initialize_and_train_v1, name='InitializationTrainer')
307
- initialization_thread.daemon = True
308
- initialization_thread.start()
309
-
310
- print(f"\n💻 LANZANDO INTERFAZ GRADIO (El entrenamiento V1.0/V1.1 se ejecuta en segundo plano)")
311
- demo.launch()
 
1
+ # Importamos las clases necesarias de la librería Hugging Face Transformers.
2
+ # Usamos GPT2LMHeadModel porque incluye el "Language Model Head"
3
+ # necesario para tareas de generación de texto.
4
+ from transformers import GPT2LMHeadModel, GPT2Tokenizer
5
+ import torch
6
+ import sys
7
+
8
+ # --- CONFIGURACIÓN ESTÁNDAR ---
9
+ # MODEL_ID: Usamos 'gpt2', el identificador oficial y compatible.
10
+ # ESTA ES LA CORRECCIÓN CLAVE para evitar el error de compatibilidad.
11
+ MODEL_ID = 'gpt2'
12
+ PROMPT = "La arquitectura de la red neuronal Transformer se basa en un mecanismo de"
13
+ MAX_LENGTH = 70
14
+ TEMPERATURE = 0.8
15
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
16
+
17
+ print(f"Iniciando carga del modelo GPT-2: {MODEL_ID} en dispositivo: {DEVICE}")
18
+
19
+ def main():
20
+ """Función principal para cargar el modelo y generar texto."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  try:
22
+ # 1. Cargar el Tokenizer
23
+ tokenizer = GPT2Tokenizer.from_pretrained(MODEL_ID)
24
+
25
+ # 2. Cargar el Modelo con el 'Language Modeling Head'
26
+ model = GPT2LMHeadModel.from_pretrained(MODEL_ID).to(DEVICE)
27
+
28
+ # Configuramos el token de padding (necesario para la generación)
29
+ if tokenizer.pad_token is None:
30
+ tokenizer.pad_token = tokenizer.eos_token
31
+ model.config.pad_token_id = tokenizer.eos_token_id
32
+
33
+ # 3. Codificar el texto de inicio (prompt)
34
+ input_ids = tokenizer.encode(PROMPT, return_tensors='pt').to(DEVICE)
35
+
36
+ print("\n--- Generando Texto ---")
37
+
38
+ # 4. Generar la secuencia de texto
39
+ output = model.generate(
40
+ input_ids,
41
+ max_length=MAX_LENGTH,
42
+ num_return_sequences=1,
43
+ do_sample=True, # Permite sampling (generación más creativa)
44
+ top_k=50, # Limita las opciones a las 50 más probables
45
+ temperature=TEMPERATURE, # Controla la creatividad (más alto = más aleatorio)
46
+ pad_token_id=tokenizer.eos_token_id
47
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # 5. Decodificar y mostrar el resultado
50
+ generated_text = tokenizer.decode(output[0], skip_special_tokens=True)
 
 
 
 
51
 
52
+ print(f"\nPrompt Original: {PROMPT}")
53
+ print("-" * (len(PROMPT) + 15))
54
+ print(f"Texto Generado Completo: {generated_text}")
55
+ print("\n¡Generación completada con éxito!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  except Exception as e:
58
+ print(f"\n[ERROR CRÍTICO] Fallo al cargar el modelo o generar texto.", file=sys.stderr)
59
+ print(f"Asegúrate de tener instalada la librería 'transformers' y 'torch' (pip install transformers torch).", file=sys.stderr)
60
+ print(f"Detalle del error: {e}", file=sys.stderr)
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  if __name__ == "__main__":
63
+ main()