Andro0s commited on
Commit
578774c
·
verified ·
1 Parent(s): ceb558e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -124
app.py CHANGED
@@ -1,34 +1,51 @@
1
  import os
2
  import gradio as gr
 
 
3
  from huggingface_hub import login
4
  from datasets import load_dataset
5
  from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling, pipeline
6
  from peft import get_peft_model, LoraConfig, TaskType, PeftModel
7
  import json
8
 
9
- BASE_MODEL = "bigcode/santacoder"
10
- LORA_PATH = "./lora_output"
 
11
  DATASET_FILE = "codesearchnet_lora_dataset.json"
12
  MAX_TOKEN_LENGTH = 256
13
  NUM_SAMPLES_TO_PROCESS = 1000
14
  DEFAULT_EPOCHS = 10
15
 
 
 
 
 
 
16
  tokenizer = None
17
  lora_model = None
18
  tokenized_dataset = None
19
  lora_generator = None
20
 
 
 
 
 
 
 
 
 
 
 
 
21
  def prepare_codesearchnet():
 
22
  if os.path.exists(DATASET_FILE):
23
- print(f"Dataset '{DATASET_FILE}' ya existe.")
24
  return
25
-
26
- print(f"Descargando y procesando CodeSearchNet ({NUM_SAMPLES_TO_PROCESS} muestras)...")
27
-
28
  try:
29
  raw_csn = load_dataset('Nan-Do/code-search-net-python', split=f'train[:{NUM_SAMPLES_TO_PROCESS}]')
30
-
31
  def format_for_lora(example):
 
32
  prompt_text = (
33
  f"# Descripción: {example['docstring_summary']}\n"
34
  f"# Completa la siguiente función:\n"
@@ -37,21 +54,17 @@ def prepare_codesearchnet():
37
  completion_text = example['code']
38
  return {"prompt": prompt_text, "completion": completion_text}
39
 
40
- lora_dataset = raw_csn.map(
41
- format_for_lora,
42
- batched=False,
43
- remove_columns=raw_csn["train"].column_names,
44
- )
45
  lora_dataset.to_json(DATASET_FILE)
46
- print(f"Pre-procesamiento completado. {NUM_SAMPLES_TO_PROCESS} ejemplos guardados en '{DATASET_FILE}'.")
47
-
48
  except Exception as e:
49
- print(f"Error CRÍTICO al descargar/procesar CodeSearchNet. Error: {e}")
50
  minimal_dataset = [{"prompt": "# Error de carga. Intenta de nuevo.", "completion": "pass\n"}] * 10
51
  with open(DATASET_FILE, 'w') as f:
52
  json.dump(minimal_dataset, f)
53
 
 
54
  def setup_resources():
 
55
  global tokenizer, lora_model, tokenized_dataset
56
 
57
  prepare_codesearchnet()
@@ -60,7 +73,6 @@ def setup_resources():
60
  if hf_token:
61
  login(token=hf_token)
62
 
63
- print("\nCargando modelo base y tokenizer...")
64
  tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
65
  base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, device_map="auto")
66
 
@@ -68,17 +80,10 @@ def setup_resources():
68
  tokenizer.pad_token = tokenizer.eos_token
69
 
70
  peft_config = LoraConfig(
71
- task_type=TaskType.CAUSAL_LM,
72
- r=8,
73
- lora_alpha=32,
74
- lora_dropout=0.1,
75
- target_modules=["c_proj", "c_attn"],
76
  )
77
  lora_model = get_peft_model(base_model, peft_config)
78
 
79
- print(f"Modelo LoRA preparado. Parámetros entrenables listos.")
80
-
81
- print(f"Cargando y tokenizando dataset: {DATASET_FILE}...")
82
  try:
83
  raw_dataset = load_dataset("json", data_files=DATASET_FILE)
84
 
@@ -90,132 +95,188 @@ def setup_resources():
90
  max_length=MAX_TOKEN_LENGTH
91
  )
92
 
93
- tokenized_dataset = raw_dataset.map(
94
- tokenize_function,
95
- batched=True,
96
- remove_columns=raw_dataset["train"].column_names if "train" in raw_dataset else [],
97
- )
98
- print("Dataset tokenizado correctamente.")
99
- except Exception as e:
100
  tokenized_dataset = None
101
- print(f"Error al cargar o tokenizar el dataset. {e}")
102
 
103
- def train_lora(epochs, batch_size, learning_rate):
104
- global lora_model, tokenized_dataset, lora_generator
105
 
106
- if tokenized_dataset is None or "train" not in tokenized_dataset:
107
- return f"Error: El dataset no pudo cargarse o está vacío. No se puede entrenar."
 
108
 
109
  try:
110
- lora_generator = None
111
- data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
112
-
113
- training_args = TrainingArguments(
114
- output_dir=LORA_PATH,
115
- per_device_train_batch_size=int(batch_size),
116
- num_train_epochs=float(epochs),
117
- learning_rate=float(learning_rate),
118
- save_total_limit=1,
119
- logging_steps=10,
120
- push_to_hub=False,
121
- )
122
-
123
- trainer = Trainer(
124
- model=lora_model,
125
- args=training_args,
126
- train_dataset=tokenized_dataset["train"],
127
- data_collator=data_collator,
128
- )
 
 
 
 
 
 
 
 
129
 
130
- trainer.train()
131
- lora_model.save_pretrained(LORA_PATH)
132
- tokenizer.save_pretrained(LORA_PATH)
 
 
 
 
 
 
 
133
 
134
- return f"Entrenamiento completado. Adaptadores LoRA guardados en **{LORA_PATH}**"
135
  except Exception as e:
136
- return f"Error durante el entrenamiento: {e}"
 
 
 
137
 
138
  def generate_text(prompt_text):
139
- global lora_generator
 
140
 
141
- try:
142
- if lora_generator is None:
143
- base_model_gen = AutoModelForCausalLM.from_pretrained(BASE_MODEL, device_map="auto")
144
-
145
- if os.path.exists(LORA_PATH):
146
- print("Cargando adaptadores LoRA...")
147
- model_with_lora = PeftModel.from_pretrained(base_model_gen, LORA_PATH)
148
- final_model = model_with_lora.merge_and_unload()
149
- else:
150
- print("No se encontraron adaptadores LoRA. Usando modelo base.")
151
- final_model = base_model_gen
152
 
 
 
 
 
 
 
 
153
  final_model.eval()
154
  lora_generator = pipeline("text-generation", model=final_model, tokenizer=tokenizer)
155
- print("Modelo de inferencia listo.")
156
 
 
 
 
157
  prompt_with_indent = prompt_text.strip() + "\n "
158
-
159
- output = lora_generator(
160
- prompt_with_indent,
161
- max_new_tokens=150,
162
- temperature=0.7,
163
- top_p=0.9,
164
- clean_up_tokenization_spaces=True
165
- )
166
-
167
  full_output = output[0]["generated_text"]
168
 
 
169
  start_index = full_output.find(prompt_with_indent)
170
- if start_index != -1:
171
- completion = full_output[start_index + len(prompt_with_indent):]
172
- else:
173
- completion = full_output
174
-
175
- return completion
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  except Exception as e:
178
- return f"Error generando texto (Asegúrate de que el modelo base/LoRA esté cargado): {e}"
179
-
180
- with gr.Blocks(title="AmorCoderAI - LoRA") as demo:
181
- gr.Markdown("# 💙 AmorCoderAI - Entrenamiento y Pruebas LoRA")
182
- gr.Markdown(f"Modelo base: `{BASE_MODEL}`. Usando **{NUM_SAMPLES_TO_PROCESS}** ejemplos de CodeSearchNet (10 Épocas).")
183
-
184
- with gr.Tab("🧠 Entrenar (Manual)"):
185
- gr.Markdown(f"--- ¡CUIDADO! El auto-entrenamiento usará {DEFAULT_EPOCHS} épocas para aprender la sintaxis. ---")
186
- epochs = gr.Number(value=DEFAULT_EPOCHS, label="Épocas", precision=0)
187
- batch_size = gr.Number(value=2, label="Tamaño de lote (ajusta según tu VRAM)", precision=0)
188
- learning_rate = gr.Number(value=5e-5, label="Tasa de aprendizaje")
189
- train_button = gr.Button("🚀 Iniciar Entrenamiento Manual")
190
- train_output = gr.Textbox(label="Resultado del Entrenamiento Manual")
191
-
192
- train_button.click(
193
- train_lora,
194
- inputs=[epochs, batch_size, learning_rate],
195
- outputs=train_output
196
- )
 
 
197
 
198
- with gr.Tab("✨ Probar modelo"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  prompt = gr.Textbox(
200
- label="Escribe código (ej: # Descripción: Calcula el factorial de N. \n# Completa la siguiente función:\ndef factorial(n):)",
201
- lines=4
 
 
 
 
 
 
 
 
 
 
202
  )
203
- generate_button = gr.Button("💬 Generar código")
204
- output_box = gr.Textbox(label="Salida generada (SOLO CÓDIGO)", lines=10)
205
- generate_button.click(generate_text, inputs=prompt, outputs=output_box)
206
 
 
 
 
 
 
207
  if __name__ == "__main__":
208
  setup_resources()
209
 
210
- print("\n=============================================")
211
- print(f"🤖 INICIANDO AUTO-ENTRENAMIENTO ({DEFAULT_EPOCHS} Épocas, 2 Batch Size) usando {NUM_SAMPLES_TO_PROCESS} ejemplos")
212
- print("=============================================")
213
-
214
- auto_train_result = train_lora(epochs=DEFAULT_EPOCHS, batch_size=2, learning_rate=5e-5)
215
-
216
- print(f"\nFIN DEL AUTO-ENTRENAMIENTO: {auto_train_result}")
217
-
218
- print("\n=============================================")
219
- print("💻 LANZANDO INTERFAZ GRADIO")
220
- print("=============================================")
221
  demo.launch()
 
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
+ # --- ESTADO GLOBAL Y THREADING ---
24
  tokenizer = None
25
  lora_model = None
26
  tokenized_dataset = None
27
  lora_generator = None
28
 
29
+ # Variables de estado
30
+ version_number = 1.0
31
+ is_trained = os.path.exists(LORA_PATH)
32
+ generations_since_last_train = 0
33
+ training_status_message = "Esperando la inicialización V1.0..."
34
+
35
+ # Lock para proteger las variables compartidas entre hilos (CRÍTICO para estabilidad)
36
+ global_lock = threading.Lock()
37
+
38
+ # --- LÓGICA DE PREPARACIÓN Y SETUP ---
39
+
40
  def prepare_codesearchnet():
41
+ """Descarga y prepara el dataset inicial si no existe."""
42
  if os.path.exists(DATASET_FILE):
 
43
  return
 
 
 
44
  try:
45
  raw_csn = load_dataset('Nan-Do/code-search-net-python', split=f'train[:{NUM_SAMPLES_TO_PROCESS}]')
46
+
47
  def format_for_lora(example):
48
+ # Formato que entrena a la IA a enlazar descripción (español) con código (inglés)
49
  prompt_text = (
50
  f"# Descripción: {example['docstring_summary']}\n"
51
  f"# Completa la siguiente función:\n"
 
54
  completion_text = example['code']
55
  return {"prompt": prompt_text, "completion": completion_text}
56
 
57
+ lora_dataset = raw_csn.map(format_for_lora, batched=False, remove_columns=raw_csn["train"].column_names)
 
 
 
 
58
  lora_dataset.to_json(DATASET_FILE)
 
 
59
  except Exception as e:
60
+ print(f"Error al cargar dataset. Usando datos mínimos. Error: {e}")
61
  minimal_dataset = [{"prompt": "# Error de carga. Intenta de nuevo.", "completion": "pass\n"}] * 10
62
  with open(DATASET_FILE, 'w') as f:
63
  json.dump(minimal_dataset, f)
64
 
65
+
66
  def setup_resources():
67
+ """Configura el tokenizer, el modelo base y el adaptador LoRA."""
68
  global tokenizer, lora_model, tokenized_dataset
69
 
70
  prepare_codesearchnet()
 
73
  if hf_token:
74
  login(token=hf_token)
75
 
 
76
  tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
77
  base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, device_map="auto")
78
 
 
80
  tokenizer.pad_token = tokenizer.eos_token
81
 
82
  peft_config = LoraConfig(
83
+ task_type=TaskType.CAUSAL_LM, r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["c_proj", "c_attn"],
 
 
 
 
84
  )
85
  lora_model = get_peft_model(base_model, peft_config)
86
 
 
 
 
87
  try:
88
  raw_dataset = load_dataset("json", data_files=DATASET_FILE)
89
 
 
95
  max_length=MAX_TOKEN_LENGTH
96
  )
97
 
98
+ tokenized_dataset = raw_dataset.map(tokenize_function, batched=True, remove_columns=raw_dataset["train"].column_names if "train" in raw_dataset else [],)
99
+ except Exception:
 
 
 
 
 
100
  tokenized_dataset = None
 
101
 
102
+ # --- FUNCIÓN DE ENTRENAMIENTO (EJECUTADA EN HILO SEPARADO) ---
 
103
 
104
+ def autonomous_train_lora(epochs, batch_size, learning_rate):
105
+ """Ejecuta el entrenamiento en un hilo separado para la autonomía."""
106
+ global lora_model, tokenized_dataset, lora_generator, version_number, is_trained, training_status_message
107
 
108
  try:
109
+ with global_lock:
110
+ if tokenized_dataset is None or "train" not in tokenized_dataset:
111
+ training_status_message = "ERROR: No se puede entrenar. Dataset no disponible."
112
+ return
113
+
114
+ # 1. ACTUALIZAR VERSIÓN (Pre-incremento)
115
+ if is_trained:
116
+ version_number += 0.1
117
+ else:
118
+ version_number = 1.0
119
+
120
+ # 2. CONFIGURACIÓN E INICIO DEL ENTRENAMIENTO
121
+ training_status_message = f"🧠 ENTRENANDO V{version_number:.1f} (Epochs: {epochs})...."
122
+ print(f"\n[AUTÓNOMO] {training_status_message}")
123
+
124
+ data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
125
+ training_args = TrainingArguments(
126
+ output_dir=LORA_PATH,
127
+ per_device_train_batch_size=int(batch_size),
128
+ num_train_epochs=float(epochs),
129
+ learning_rate=float(learning_rate),
130
+ save_total_limit=1,
131
+ logging_steps=10,
132
+ push_to_hub=False,
133
+ disable_tqdm=True,
134
+ report_to="none"
135
+ )
136
 
137
+ trainer = Trainer(model=lora_model, args=training_args, train_dataset=tokenized_dataset["train"], data_collator=data_collator)
138
+
139
+ trainer.train()
140
+ lora_model.save_pretrained(LORA_PATH)
141
+ tokenizer.save_pretrained(LORA_PATH)
142
+
143
+ # 3. Marcar como entrenado
144
+ is_trained = True
145
+ training_status_message = f"✅ ENTRENAMIENTO V{version_number:.1f} COMPLETADO. Modelo listo para Hot Swap."
146
+ print(f"[AUTÓNOMO] {training_status_message}")
147
 
 
148
  except Exception as e:
149
+ training_status_message = f"ERROR CRÍTICO durante el entrenamiento autónomo: {e}"
150
+ print(f"[AUTÓNOMO] {training_status_message}")
151
+
152
+ # --- GENERACIÓN DE CÓDIGO (HOT SWAP IMPLEMENTADO) ---
153
 
154
  def generate_text(prompt_text):
155
+ """Genera código y dispara el ciclo de reentrenamiento autónomo si es necesario."""
156
+ global lora_generator, generations_since_last_train, is_trained, version_number
157
 
158
+ if not is_trained:
159
+ return "ERROR: El modelo LoRA no ha sido entrenado. Por favor, espere mientras la IA se inicializa con el entrenamiento V1.0."
 
 
 
 
 
 
 
 
 
160
 
161
+ # 1. HOT SWAP (Verifica si el modelo necesita recargarse con la nueva versión)
162
+ if lora_generator is None:
163
+ with global_lock:
164
+ # Recarga el modelo solo si está vacío
165
+ base_model_gen = AutoModelForCausalLM.from_pretrained(BASE_MODEL, device_map="auto")
166
+ model_with_lora = PeftModel.from_pretrained(base_model_gen, LORA_PATH)
167
+ final_model = model_with_lora.merge_and_unload()
168
  final_model.eval()
169
  lora_generator = pipeline("text-generation", model=final_model, tokenizer=tokenizer)
170
+ print(f"[HOT SWAP] 🔄 Modelo de inferencia V{version_number:.1f} recargado y listo.")
171
 
172
+ # 2. Generación de texto (Lógica de inferencia)
173
+ try:
174
+ # Prepara el prompt para guiar la generación del código
175
  prompt_with_indent = prompt_text.strip() + "\n "
176
+ output = lora_generator(prompt_with_indent, max_new_tokens=150, temperature=0.7, top_p=0.9, clean_up_tokenization_spaces=True)
 
 
 
 
 
 
 
 
177
  full_output = output[0]["generated_text"]
178
 
179
+ # Extrae solo la parte de la compleción (el código generado)
180
  start_index = full_output.find(prompt_with_indent)
181
+ completion = full_output[start_index + len(prompt_with_indent):] if start_index != -1 else full_output
 
 
 
 
 
182
 
183
+ # 3. Aumentar contador de autonomía
184
+ with global_lock:
185
+ generations_since_last_train += 1
186
+ current_count = generations_since_last_train
187
+ current_version = version_number
188
+
189
+ # 4. Verificar si se requiere reentrenamiento (y dispararlo en un nuevo hilo)
190
+ notification = ""
191
+ if current_count >= GENERATION_LIMIT_TO_TRAIN:
192
+ # Verifica que no haya otro hilo de entrenamiento ya corriendo
193
+ if not any(isinstance(t, threading.Thread) and t.name == 'AutonomousTrainer' for t in threading.enumerate()):
194
+ print(f"[AUTONOMÍA] Generación #{current_count} alcanzada. Disparando reentrenamiento autónomo en segundo plano...")
195
+
196
+ # Reiniciar el contador de generaciones y forzar Hot Swap en la próxima interacción
197
+ with global_lock:
198
+ generations_since_last_train = 0
199
+ lora_generator = None
200
+
201
+ trainer_thread = threading.Thread(
202
+ target=autonomous_train_lora,
203
+ args=(AUTONOMOUS_EPOCHS, 2, 5e-5),
204
+ name='AutonomousTrainer'
205
+ )
206
+ trainer_thread.daemon = True
207
+ trainer_thread.start()
208
+
209
+ 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. ---"
210
+
211
+ return completion + notification
212
+
213
  except Exception as e:
214
+ return f"Error generando texto: {e}"
215
+
216
+ # --- FUNCIÓN PARA INICIALIZACIÓN Y ENTRENAMIENTO V1.0 (Obligatorio) ---
217
+
218
+ def initialize_and_train_v1():
219
+ """Ejecuta el entrenamiento inicial V1.0 de forma autónoma al iniciar."""
220
+ if not is_trained:
221
+ autonomous_train_lora(epochs=DEFAULT_EPOCHS, batch_size=2, learning_rate=5e-5)
222
+ else:
223
+ global training_status_message
224
+ training_status_message = f" Modelo V{version_number:.1f} ya entrenado. Listo."
225
+ print(f"[INICIALIZACIÓN] {training_status_message}")
226
+
227
+ # --- FUNCIÓN PARA ACTUALIZAR EL ESTADO EN LA UI ---
228
+
229
+ def update_status():
230
+ """Actualiza la versión y el estado del entrenamiento en la interfaz de Gradio."""
231
+ global training_status_message, version_number
232
+ # Retorna un texto en Markdown que se actualiza constantemente
233
+ return f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}"
234
+
235
 
236
+ # --- INTERFAZ GRADIO ---
237
+ with gr.Blocks(title="AmorCoderAI - Aprendizaje Continuo") as demo:
238
+ gr.Markdown("# 💙 AmorCoderAI - Asistente de Código con Aprendizaje Continuo")
239
+
240
+ # Muestra la versión y el estado, se actualiza cada segundo (every=1)
241
+ version_and_status = gr.Markdown(
242
+ f"**Versión de Comprensión:** V{version_number:.1f} | **Estado del Entrenador:** {training_status_message}",
243
+ elem_id="status_display"
244
+ )
245
+
246
+ 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.")
247
+
248
+ with gr.Tab("✨ Generación de Código"):
249
+ gr.Markdown("## Escribe tu idea en palabras (¡Usa español fluido!)")
250
+
251
+ gr.Markdown("Recomendación inicial: Usa el siguiente formato para obtener el mejor código mientras la IA aprende tu idioma:")
252
+
253
  prompt = gr.Textbox(
254
+ label="Instrucción de Programación:",
255
+ lines=4,
256
+ 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):"
257
+ )
258
+ generate_button = gr.Button("💬 Generar código y disparar Aprendizaje")
259
+ output_box = gr.Textbox(label="Código generado", lines=10)
260
+
261
+ # Conexión del botón con la función principal
262
+ generate_button.click(
263
+ generate_text,
264
+ inputs=prompt,
265
+ outputs=output_box,
266
  )
 
 
 
267
 
268
+ # Inicia la actualización continua del estado en la UI (el corazón del entrenamiento continuo)
269
+ demo.load(update_status, None, version_and_status, every=1)
270
+
271
+
272
+ # --- INICIO DE LA APLICACIÓN ---
273
  if __name__ == "__main__":
274
  setup_resources()
275
 
276
+ # Lanza el entrenamiento V1.0 inicial en un hilo para que no congele la UI
277
+ initialization_thread = threading.Thread(target=initialize_and_train_v1, name='InitializationTrainer')
278
+ initialization_thread.daemon = True
279
+ initialization_thread.start()
280
+
281
+ print(f"\n💻 LANZANDO INTERFAZ GRADIO (El entrenamiento V1.0 se ejecuta en segundo plano)")
 
 
 
 
 
282
  demo.launch()