PowerMachine commited on
Commit
b893208
·
verified ·
1 Parent(s): 17685dc

V6.5-7ds cleanup: remove legacy scripts/train_v6_1.py

Browse files
Files changed (1) hide show
  1. scripts/train_v6_1.py +0 -1200
scripts/train_v6_1.py DELETED
@@ -1,1200 +0,0 @@
1
- """train_v6_1.py — Treino V6.1 BiGRU_T_version.
2
-
3
- V6.1 — MELHORIAS PGVECTOR-LIKE + EWC MATH UPGRADE
4
- ═══════════════════════════════════════════════════
5
-
6
- Diferenças vs V6:
7
-
8
- 1. PGVector-like lookup no OrqCell: a cada step (treino e hipótese),
9
- consulta o mapa SOM e classifica o batch em:
10
- - COMPLETE : info presente e consolidada (neurônio filled + sim alta)
11
- - INCOMPLETE : info parcial (sim alta mas neurônio não-filled — faltam
12
- elementos, pesos EWC ainda não computados)
13
- - MISSING : info ausente (sem neurônio similar — usar EWC com
14
- pesos aleatórios iniciais)
15
-
16
- O usuário observou: "em uma busca PGVector posso encontrar informações
17
- incompletas (faltam elementos e no caso os pesos que devem ser
18
- calculados por EWC) e completas". Este treino captura a evolução dos
19
- três estados ao longo do treino.
20
-
21
- 2. EWC V6.1 com skip mask matemático: o EWC NÃO computa Fisher nem aplica
22
- penalidade para neurônios do SOM que já estão "filled". Isto evita a
23
- dupla penalização — o SOM já está consolidando esses pesos via regra
24
- de Kohonen (BMU + vizinhos puxados na direção do input, preservando
25
- topologia), então o EWC não precisa "ancorar" aquela região do mapa.
26
-
27
- O usuário pediu: "Elastic Weight Consolidation que deve ser
28
- matematicamente aprimorado para não usar pesos já existentes em SOM".
29
-
30
- 3. 5 datasets do streaming_datasets.py × 10 samples cada (50 total).
31
- O usuário pediu: "capturar métricas no re-treino de datasets 10 em 10
32
- samples diversos (5 datasets streaming_datasets.py)".
33
-
34
- 4. Xeon V6 sempre ativo (AVX512 + AMX_INT8 + IPEX + OneDNN + FP16).
35
-
36
- 5. Monitoramento 12/12 + Kohonen (sigma, alpha, BMU) + MTP (val loss,
37
- entropy reg) + EWC (penalty breakdown, n_masked_som_neurons) +
38
- PGVector (status, coverage_ratio, n_filled).
39
-
40
- 6. Apagar binários ao final (user requirement).
41
-
42
- 7. Hipótese (HypT) ativada em punição — usa pgvector_lookup para decidir
43
- se inicializa com pesos aleatórios (MISSING) ou reaproveita (COMPLETE).
44
- """
45
- from __future__ import annotations
46
-
47
- import os
48
- import sys
49
- import gc
50
- import json
51
- import time
52
- import psutil
53
- import math
54
- import shutil
55
- import glob
56
- import random
57
- from pathlib import Path
58
- from typing import Optional, Dict, List, Any, Tuple
59
-
60
- # ==============================================================================
61
- # 1. XEON RUNTIME V6 — ATIVAR ANTES DE QUALQUER IMPORT IA
62
- # ==============================================================================
63
- os.environ.setdefault("PYTHONUNBUFFERED", "1")
64
- os.environ.setdefault("TOKENIZERS_PARALLELISM", "true")
65
-
66
- sys.path.insert(0, "/home/z/my-project/BiGRU_T_version/src")
67
- sys.path.insert(0, "/home/z/my-project/scripts")
68
-
69
- from bigru_t.utils.xeon_runtime import ( # noqa: E402
70
- optimize_xeon_environment,
71
- get_xeon_status,
72
- fp16_autocast,
73
- benchmark_fp16_matmul,
74
- )
75
- # V6.1: sempre ativa o Xeon (user requirement: "ativar xeon_runtime.py")
76
- N_CORES = optimize_xeon_environment(verbose=True)
77
- XEON_STATUS = get_xeon_status()
78
- # V6.1: roda benchmark FP16 uma vez para registrar TFLOPS no report
79
- FP16_BENCH = benchmark_fp16_matmul(size=2000, iters=2)
80
-
81
- import torch # noqa: E402
82
- import torch.nn as nn # noqa: E402
83
- import torch.nn.functional as F # noqa: E402
84
- import numpy as np # noqa: E402
85
-
86
- # ==============================================================================
87
- # 2. IMPORTS DO PROJETO (V6 + V6.1)
88
- # ==============================================================================
89
- from bigru_t.model import UnifiedModel, UnifiedModelConfig # noqa: E402
90
- from bigru_t.model.orq_cell import OrqCell as OrqCellV6, Kohonen4DConfig # noqa: E402
91
- from bigru_t.tokenizer.bbpe_tokenizer import BBPETokenizer, PAD_ID # noqa: E402
92
- from bigru_t.training import ( # noqa: E402
93
- TripletPrototypeLoss,
94
- )
95
- from bigru_t.training.meta_configurator import MetaConfigurator # noqa: E402
96
- from bigru_t.training.ewc import EWCV6, EWCConfigV6 # noqa: E402
97
- from bigru_t.training.mtp import MTPHead, mtp_loss # noqa: E402
98
- from bigru_t.training.dynamic_repetition_penalty import DynamicRepetitionPenalty # noqa: E402
99
- from bigru_t.training.data_augmentation import DataAugmenter, AugmentationConfig # noqa: E402
100
- from bigru_t.training.rlhf_mapping import RLHFMapping # noqa: E402
101
- from bigru_t.attention.window_context import WindowContext, WindowContextConfig # noqa: E402
102
- from bigru_t.reasoning.thinking import ThinkingSystem, ThinkingConfig # noqa: E402
103
- from bigru_t.tools.tool_mutation import ToolMutator # noqa: E402
104
- from bigru_t.tools.tool_cache import ToolCache # noqa: E402
105
- from bigru_t.utils.memory_cleanup import aggressive_cleanup, get_rss_mb # noqa: E402
106
-
107
- # V6.1: streaming datasets (5 datasets × 10 samples)
108
- from bigru_t.data.streaming_datasets import ( # noqa: E402
109
- stream_dataset,
110
- DEFAULT_DATASETS,
111
- ProcessedSample,
112
- )
113
-
114
- import logging # noqa: E402
115
- logging.basicConfig(
116
- level=logging.INFO,
117
- format="%(asctime)s [%(levelname)s] %(message)s",
118
- datefmt="%H:%M:%S",
119
- )
120
- logger = logging.getLogger("train_v6_1")
121
-
122
- # ==============================================================================
123
- # 3. PARÂMETROS V6.1
124
- # ==============================================================================
125
- # 5 datasets × 10 samples = 50 total (user requirement)
126
- V61_DATASETS = [
127
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
128
- "Madras1/corpus-ptbr-v2",
129
- "TucanoBR/GigaVerbo",
130
- "nvidia/OpenMathInstruct-2",
131
- "dominguesm/restore-punctuation-ptbr-dataset",
132
- ]
133
- N_PER_DATASET = 10 # 10 samples por dataset (user requirement)
134
- FALLBACK_SYNTHETIC_PER_DS = 10 # fallback se streaming falhar
135
-
136
- EPOCHS = 2
137
- BATCH_SIZE = 4
138
- MAX_SEQ_LEN = 16
139
- NUM_LAYERS_HYP = 8
140
- TRIPLET_WEIGHT = 0.05
141
- META_INTERVAL = 3
142
- CLEANUP_INTERVAL = 1
143
- TRIPLET_EMA_ALPHA = 0.1
144
-
145
- # V6 features (mantidas)
146
- USE_MTP = True
147
- USE_EWC = True # V6.1: EWC ATIVADO (skip mask previne NaN)
148
- USE_KOHONEN_ORQ = True
149
- MTP_K = 4
150
- MTP_ENTROPY_BETA = 0.01
151
- EWC_LAMBDA = 0.0 # V6.1: classic Fisher desativado (causa NaN quando ativado)
152
- # A inovação V6.1 é a skip mask no Fisher Kohonen per-neuron
153
- # (quando todos os neurônios estão filled, EWC contribution = 0)
154
- EWC_LAMBDA_TOPO = 0.05
155
- MTP_LOSS_WEIGHT = 0.1
156
-
157
- # V6.1 — PGVector thresholds (orq_cell defaults, repeated here for report)
158
- PGV_COMPLETE_THR = 0.6
159
- PGV_MISSING_THR = 0.2
160
-
161
- REPORT_PATH = "/home/z/my-project/BiGRU_T_version/v6_1_report.json"
162
-
163
-
164
- # ==============================================================================
165
- # 4. MONITOR V6.1 — 12/12 + Kohonen + MTP + EWC + PGVector
166
- # ==============================================================================
167
- class MetricsMonitorV61:
168
- """Monitor V6.1: 12/12 + Kohonen SOM + MTP + EWC + PGVector lookup."""
169
-
170
- def __init__(self):
171
- self.history = {
172
- # 12/12 metrics
173
- "train_loss": [], "val_loss": [], "perplexity": [],
174
- "overfitting_gap": [], "eval_benchmark": [],
175
- "tokens_per_sec": [], "tflops_per_cpu": [], "mbu_gbps": [],
176
- "time_per_step_ms": [], "comm_overhead_ms": [],
177
- "grad_norm": [], "cpu_pct": [], "rss_mb": [],
178
- "alerts": [], "meta_history": [],
179
- # V6 Kohonen
180
- "sigma_t": [], "alpha_t": [], "bmu_diversity": [],
181
- "map_entropy": [], "neighborhood_spread": [],
182
- "bmu_id_mean": [], "update_magnitude": [], "bmu_drift": [],
183
- "n_filled": [], "filled_ratio": [],
184
- # V6 MTP
185
- "mtp_train_loss": [], "mtp_val_loss": [],
186
- "mtp_entropy_reg": [], "mtp_alphas": [],
187
- # V6 EWC
188
- "ewc_penalty": [], "ewc_classic_pen": [],
189
- "ewc_kohonen_pen": [], "ewc_topo_pen": [],
190
- "n_masked_som_neurons": [],
191
- # V6.1 PGVector
192
- "pgv_status": [], # str: COMPLETE | INCOMPLETE | MISSING
193
- "pgv_coverage": [], # float: fração de neurônios com sim ≥ missing_thr
194
- "pgv_best_sim": [], # float: similaridade máxima
195
- "pgv_n_filled": [], # int: neurônios filled no mapa
196
- "pgv_filled_ratio": [], # float
197
- # V6.1 dataset tracking
198
- "dataset_source": [], # str: nome do dataset da sample
199
- }
200
- self.prev_loss = None
201
- self.process = psutil.Process(os.getpid())
202
-
203
- def record_step(self, step, train_loss, n_tokens, elapsed_s, grad_norm,
204
- flops_estimate=0.0, kohonen_aux=None, mtp_metrics=None,
205
- ewc_metrics=None, pgv_metrics=None, dataset_name=None):
206
- tps = n_tokens / max(elapsed_s, 1e-6)
207
- tflops = flops_estimate / max(elapsed_s, 1e-6) / 1e12
208
- tps_ms = elapsed_s * 1000.0
209
- cpu_pct = self.process.cpu_percent(interval=None)
210
- rss_mb = get_rss_mb()
211
-
212
- spike_alert = False
213
- if self.prev_loss is not None and self.prev_loss > 0:
214
- delta_pct = abs(train_loss - self.prev_loss) / self.prev_loss
215
- if delta_pct > 0.5:
216
- spike_alert = True
217
- self.history["alerts"].append({"step": step, "type": "loss_spike", "delta_pct": delta_pct})
218
- self.prev_loss = train_loss
219
-
220
- grad_alert = None
221
- if grad_norm < 1e-7:
222
- grad_alert = "vanishing"
223
- elif grad_norm > 1e3:
224
- grad_alert = "exploding"
225
- if grad_alert:
226
- self.history["alerts"].append({"step": step, "type": f"grad_{grad_alert}"})
227
-
228
- if cpu_pct < 80.0 and step > 1:
229
- self.history["alerts"].append({"step": step, "type": "cpu_underutilization", "cpu_pct": cpu_pct})
230
-
231
- if len(self.history["alerts"]) > 50:
232
- self.history["alerts"] = self.history["alerts"][-50:]
233
-
234
- self.history["train_loss"].append(train_loss)
235
- self.history["tokens_per_sec"].append(tps)
236
- self.history["tflops_per_cpu"].append(tflops)
237
- self.history["mbu_gbps"].append(0.0)
238
- self.history["time_per_step_ms"].append(tps_ms)
239
- self.history["comm_overhead_ms"].append(0.0)
240
- self.history["grad_norm"].append(grad_norm)
241
- self.history["cpu_pct"].append(cpu_pct)
242
- self.history["rss_mb"].append(rss_mb)
243
-
244
- if kohonen_aux:
245
- for k in ["sigma_t", "alpha_t", "bmu_diversity", "map_entropy",
246
- "neighborhood_spread", "bmu_id_mean",
247
- "update_magnitude", "bmu_drift",
248
- "n_filled", "filled_ratio"]:
249
- self.history[k].append(kohonen_aux.get(k, 0))
250
- if mtp_metrics:
251
- self.history["mtp_train_loss"].append(mtp_metrics.get("total_loss", 0))
252
- self.history["mtp_entropy_reg"].append(mtp_metrics.get("entropy_reg", 0))
253
- self.history["mtp_alphas"].append(mtp_metrics.get("alphas", []))
254
- if ewc_metrics:
255
- self.history["ewc_penalty"].append(ewc_metrics.get("total_pen", 0))
256
- self.history["ewc_classic_pen"].append(ewc_metrics.get("classic_pen", 0))
257
- self.history["ewc_kohonen_pen"].append(ewc_metrics.get("kohonen_pen", 0))
258
- self.history["ewc_topo_pen"].append(ewc_metrics.get("topo_pen", 0))
259
- self.history["n_masked_som_neurons"].append(ewc_metrics.get("n_masked_som_neurons", 0))
260
- if pgv_metrics:
261
- self.history["pgv_status"].append(pgv_metrics.get("status", "UNKNOWN"))
262
- self.history["pgv_coverage"].append(pgv_metrics.get("coverage_ratio", 0))
263
- self.history["pgv_best_sim"].append(pgv_metrics.get("best_sim", 0))
264
- self.history["pgv_n_filled"].append(pgv_metrics.get("n_filled", 0))
265
- self.history["pgv_filled_ratio"].append(pgv_metrics.get("filled_ratio", 0))
266
- if dataset_name:
267
- self.history["dataset_source"].append(dataset_name)
268
-
269
- if step % 5 == 0 or step == 1 or spike_alert or grad_alert:
270
- kohonen_str = ""
271
- if kohonen_aux:
272
- kohonen_str = (f" σ={kohonen_aux.get('sigma_t', 0):.3f}"
273
- f" α={kohonen_aux.get('alpha_t', 0):.3f}"
274
- f" BMUdiv={kohonen_aux.get('bmu_diversity', 0)}"
275
- f" filled={kohonen_aux.get('n_filled', 0)}/8")
276
- pgv_str = ""
277
- if pgv_metrics:
278
- pgv_str = f" PGV={pgv_metrics.get('status', '?')[:4]} cov={pgv_metrics.get('coverage_ratio', 0):.2f}"
279
- logger.info(
280
- f" step {step:3d} | loss={train_loss:.4f} | tps={tps:.0f} | "
281
- f"step_ms={tps_ms:.0f} | ||g||={grad_norm:.2f} | cpu%={cpu_pct:.0f} | "
282
- f"rss={rss_mb:.0f}MB" + kohonen_str + pgv_str
283
- + (" ⚠️SPIKE" if spike_alert else "")
284
- + (f" ⚠️GRAD_{grad_alert}" if grad_alert else "")
285
- )
286
-
287
- def record_epoch(self, epoch, val_loss, train_loss_avg, eval_acc, mtp_val_loss=None):
288
- ppl = math.exp(min(val_loss, 20.0))
289
- gap = train_loss_avg - val_loss
290
- self.history["val_loss"].append(val_loss)
291
- self.history["perplexity"].append(ppl)
292
- self.history["overfitting_gap"].append(gap)
293
- self.history["eval_benchmark"].append(eval_acc)
294
- if mtp_val_loss is not None:
295
- self.history["mtp_val_loss"].append(mtp_val_loss)
296
- logger.info(
297
- f" EPOCH {epoch} | val_loss={val_loss:.4f} | train_avg={train_loss_avg:.4f} | "
298
- f"ppl={ppl:.2f} | gap={gap:+.4f} | eval_acc={eval_acc:.2%}"
299
- + (f" | mtp_val={mtp_val_loss:.4f}" if mtp_val_loss is not None else "")
300
- )
301
-
302
- def record_meta(self, meta_result):
303
- self.history["meta_history"].append({
304
- "T": meta_result.get("T_new", 0),
305
- "tau": meta_result.get("tau_new", 0),
306
- "lambda_ent": meta_result.get("lambda_ent_new", 0),
307
- "init_gate": meta_result.get("init_gate_new", 0),
308
- "meta_loss": meta_result.get("meta_loss", 0),
309
- "sharpness": meta_result.get("sharpness", 0),
310
- })
311
-
312
- def summary(self):
313
- h = self.history
314
- rss_trend = "stable"
315
- if len(h["rss_mb"]) >= 3:
316
- if h["rss_mb"][-1] > h["rss_mb"][0] * 1.5:
317
- rss_trend = "growing"
318
- elif h["rss_mb"][-1] < h["rss_mb"][0] * 0.8:
319
- rss_trend = "shrinking"
320
- # V6.1 — PGVector status distribution
321
- pgv_status_counts = {"COMPLETE": 0, "INCOMPLETE": 0, "MISSING": 0}
322
- for s in h["pgv_status"]:
323
- pgv_status_counts[s] = pgv_status_counts.get(s, 0) + 1
324
- # V6.1 — Dataset distribution
325
- ds_counts: Dict[str, int] = {}
326
- for d in h["dataset_source"]:
327
- ds_counts[d] = ds_counts.get(d, 0) + 1
328
- return {
329
- "n_steps": len(h["train_loss"]),
330
- "n_epochs": len(h["val_loss"]),
331
- "final_train_loss": h["train_loss"][-1] if h["train_loss"] else None,
332
- "final_val_loss": h["val_loss"][-1] if h["val_loss"] else None,
333
- "final_perplexity": h["perplexity"][-1] if h["perplexity"] else None,
334
- "final_overfitting_gap": h["overfitting_gap"][-1] if h["overfitting_gap"] else None,
335
- "final_eval_benchmark": h["eval_benchmark"][-1] if h["eval_benchmark"] else None,
336
- "avg_tokens_per_sec": sum(h["tokens_per_sec"]) / max(1, len(h["tokens_per_sec"])),
337
- "avg_tflops_per_cpu": sum(h["tflops_per_cpu"]) / max(1, len(h["tflops_per_cpu"])),
338
- "avg_time_per_step_ms": sum(h["time_per_step_ms"]) / max(1, len(h["time_per_step_ms"])),
339
- "avg_grad_norm": sum(h["grad_norm"]) / max(1, len(h["grad_norm"])),
340
- "avg_cpu_pct": sum(h["cpu_pct"]) / max(1, len(h["cpu_pct"])),
341
- "final_rss_mb": h["rss_mb"][-1] if h["rss_mb"] else None,
342
- "max_rss_mb": max(h["rss_mb"]) if h["rss_mb"] else None,
343
- "rss_trend": rss_trend,
344
- "n_alerts": len(h["alerts"]),
345
- "alerts": h["alerts"][:20],
346
- "alert_types": list({a["type"] for a in h["alerts"]}),
347
- "meta_history": h["meta_history"],
348
- "kohonen_evolution": {
349
- "sigma_t": h["sigma_t"],
350
- "alpha_t": h["alpha_t"],
351
- "bmu_diversity": h["bmu_diversity"],
352
- "map_entropy": h["map_entropy"],
353
- "neighborhood_spread": h["neighborhood_spread"],
354
- "update_magnitude": h["update_magnitude"],
355
- "bmu_drift": h["bmu_drift"],
356
- "n_filled": h["n_filled"],
357
- "filled_ratio": h["filled_ratio"],
358
- "final_sigma_t": h["sigma_t"][-1] if h["sigma_t"] else None,
359
- "final_alpha_t": h["alpha_t"][-1] if h["alpha_t"] else None,
360
- "sigma_decay": (h["sigma_t"][0] - h["sigma_t"][-1]) if len(h["sigma_t"]) >= 2 else 0,
361
- "alpha_decay": (h["alpha_t"][0] - h["alpha_t"][-1]) if len(h["alpha_t"]) >= 2 else 0,
362
- "final_n_filled": h["n_filled"][-1] if h["n_filled"] else 0,
363
- "final_filled_ratio": h["filled_ratio"][-1] if h["filled_ratio"] else 0,
364
- },
365
- "mtp_evolution": {
366
- "train_loss": h["mtp_train_loss"],
367
- "val_loss": h["mtp_val_loss"],
368
- "entropy_reg": h["mtp_entropy_reg"],
369
- "final_alphas": h["mtp_alphas"][-1] if h["mtp_alphas"] else None,
370
- },
371
- "ewc_evolution": {
372
- "total_penalty": h["ewc_penalty"],
373
- "classic_pen": h["ewc_classic_pen"],
374
- "kohonen_pen": h["ewc_kohonen_pen"],
375
- "topo_pen": h["ewc_topo_pen"],
376
- "n_masked_som_neurons": h["n_masked_som_neurons"],
377
- },
378
- # V6.1 — PGVector evolution
379
- "pgvector_evolution": {
380
- "status_per_step": h["pgv_status"],
381
- "status_counts": pgv_status_counts,
382
- "coverage_ratio": h["pgv_coverage"],
383
- "best_sim": h["pgv_best_sim"],
384
- "n_filled": h["pgv_n_filled"],
385
- "filled_ratio": h["pgv_filled_ratio"],
386
- "final_coverage": h["pgv_coverage"][-1] if h["pgv_coverage"] else None,
387
- "final_best_sim": h["pgv_best_sim"][-1] if h["pgv_best_sim"] else None,
388
- "final_n_filled": h["pgv_n_filled"][-1] if h["pgv_n_filled"] else 0,
389
- "final_filled_ratio": h["pgv_filled_ratio"][-1] if h["pgv_filled_ratio"] else 0,
390
- },
391
- # V6.1 — Dataset tracking
392
- "dataset_distribution": ds_counts,
393
- }
394
-
395
-
396
- # ==============================================================================
397
- # 5. LOAD 5 DATASETS × 10 SAMPLES (com fallback sintético)
398
- # ==============================================================================
399
- def load_v61_samples(tokenizer, max_seq_len, hf_token=None):
400
- """Carrega 5 datasets × 10 samples do streaming_datasets.py.
401
-
402
- Para cada dataset, tenta 10 samples reais via stream_dataset().
403
- Se o streaming falhar (rede, auth, etc.), usa 10 samples sintéticos
404
- da categoria equivalente (mantém o tracking de dataset_name).
405
- """
406
- samples = []
407
- rng = random.Random(42)
408
-
409
- # Fallback sintético por categoria (10 samples cada)
410
- synth_fallbacks = {
411
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1": [
412
- "Olá, como você está hoje?",
413
- "Bom dia! Posso ajudar com algo?",
414
- "Qual é a capital do Brasil?",
415
- "Me conte uma curiosidade sobre o espaço.",
416
- "Como funciona o aprendizado de máquina?",
417
- "Traduza 'hello world' para português.",
418
- "Escreva um poema curto sobre o mar.",
419
- "Quem foi Machado de Assis?",
420
- "Explique a teoria da relatividade.",
421
- "Liste três benefícios do exercício físico.",
422
- ],
423
- "Madras1/corpus-ptbr-v2": [
424
- "A literatura brasileira é riquíssima e diversa.",
425
- "O café é uma das bebidas mais consumidas no mundo.",
426
- "A Amazônia abriga milhões de espécies.",
427
- "São Paulo é a maior cidade do Brasil.",
428
- "O carnaval é uma festa tradicional brasileira.",
429
- "A música popular brasileira tem raízes africanas.",
430
- "O futebol é o esporte mais popular do país.",
431
- "A culinária regional varia muito entre estados.",
432
- "A língua portuguesa tem origem no latim vulgar.",
433
- "A história do Brasil começou em 1500.",
434
- ],
435
- "TucanoBR/GigaVerbo": [
436
- "O verbo é a palavra que indica ação.",
437
- "Conjugue o verbo 'falar' no presente.",
438
- "Os verbos irregulares merecem atenção especial.",
439
- "O infinitivo é a forma nominal do verbo.",
440
- "O gerúndio termina em -ndo em português.",
441
- "O particípio é usado nos tempos compostos.",
442
- "Verbos transitivos exigem objeto direto.",
443
- "Verbos intransitivos não exigem objeto.",
444
- "A voz passiva transforma o sujeito em paciente.",
445
- "O imperativo é usado para dar ordens.",
446
- ],
447
- "nvidia/OpenMathInstruct-2": [
448
- "Problema: calcule 7 × 8. Solução: 56.",
449
- "Problema: qual é a raiz de 144? Solução: 12.",
450
- "Problema: some 15 + 27. Solução: 42.",
451
- "Problema: divida 100 por 4. Solução: 25.",
452
- "Problema: calcule 3² + 4². Solução: 25.",
453
- "Problema: qual é o MDC de 12 e 18? Solução: 6.",
454
- "Problema: converta 1/2 para decimal. Solução: 0.5.",
455
- "Problema: calcule 20% de 80. Solução: 16.",
456
- "Problema: qual é o perímetro de um quadrado 5×5? Solução: 20.",
457
- "Problema: resolva 2x + 3 = 11. Solução: x = 4.",
458
- ],
459
- "dominguesm/restore-punctuation-ptbr-dataset": [
460
- "o gato subiu no telhado",
461
- "a chuva caiu a noite toda",
462
- "são paulo é uma cidade grande",
463
- "joão e maria foram passear",
464
- "o livro está sobre a mesa",
465
- "amanhã vou ao mercado comprar pão",
466
- "o sol nasce no leste",
467
- "ela canta muito bem",
468
- "o carro parou no sinal vermelho",
469
- "os alunos estudaram para a prova",
470
- ],
471
- }
472
-
473
- for ds_name in V61_DATASETS:
474
- logger.info(f" [STREAM] Loading {ds_name} (max {N_PER_DATASET} samples)...")
475
- ds_samples = []
476
- try:
477
- count = 0
478
- for sample in stream_dataset(
479
- ds_name,
480
- max_samples=N_PER_DATASET,
481
- hf_token=hf_token,
482
- ):
483
- if count >= N_PER_DATASET:
484
- break
485
- text = sample.raw_text[:200] if sample.raw_text else None
486
- if not text or len(text) < 5:
487
- continue
488
- ids = tokenizer.encode(text)
489
- if len(ids) > max_seq_len:
490
- ids = ids[:max_seq_len]
491
- else:
492
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
493
- # label = primeira palavra
494
- first_word = text.split()[0] if text.split() else "ok"
495
- label_ids = tokenizer.encode(first_word)
496
- if len(label_ids) > max_seq_len:
497
- label_ids = label_ids[:max_seq_len]
498
- else:
499
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
500
- ds_samples.append({
501
- "input_ids": torch.tensor(ids, dtype=torch.long),
502
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
503
- "label_text": first_word,
504
- "type": "streaming",
505
- "answer": "",
506
- "dataset": ds_name,
507
- })
508
- count += 1
509
- if count > 0:
510
- logger.info(f" ✓ {count} samples reais carregados de {ds_name}")
511
- samples.extend(ds_samples)
512
- continue
513
- else:
514
- logger.warning(f" ⚠ nenhuma sample de {ds_name}, usando fallback sintético")
515
- except Exception as e:
516
- logger.warning(f" ⚠ streaming falhou para {ds_name}: {type(e).__name__}: {str(e)[:80]}")
517
- logger.warning(f" usando fallback sintético")
518
-
519
- # Fallback sintético
520
- synth_texts = synth_fallbacks.get(ds_name, [f"sample {i}" for i in range(N_PER_DATASET)])
521
- for text in synth_texts[:N_PER_DATASET]:
522
- ids = tokenizer.encode(text)
523
- if len(ids) > max_seq_len:
524
- ids = ids[:max_seq_len]
525
- else:
526
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
527
- first_word = text.split()[0] if text.split() else "ok"
528
- label_ids = tokenizer.encode(first_word)
529
- if len(label_ids) > max_seq_len:
530
- label_ids = label_ids[:max_seq_len]
531
- else:
532
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
533
- samples.append({
534
- "input_ids": torch.tensor(ids, dtype=torch.long),
535
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
536
- "label_text": first_word,
537
- "type": "synthetic_fallback",
538
- "answer": "",
539
- "dataset": ds_name,
540
- })
541
-
542
- logger.info(f" Total samples: {len(samples)} ({len(V61_DATASETS)} datasets × até {N_PER_DATASET} samples)")
543
- return samples
544
-
545
-
546
- # ==============================================================================
547
- # 6. MCQ EVAL (igual V6)
548
- # ==============================================================================
549
- def make_mini_mcq(n_questions: int = 8):
550
- questions = []
551
- for i in range(n_questions):
552
- a, b = i + 1, (i * 2) % 10 + 1
553
- correct = a + b
554
- question = f"What is {a} + {b}?"
555
- options = [correct, correct + 1, correct - 1, correct + 2]
556
- rng = random.Random(i)
557
- rng.shuffle(options)
558
- correct_idx = options.index(correct)
559
- questions.append({
560
- "question": question,
561
- "options": [str(o) for o in options],
562
- "correct_idx": correct_idx,
563
- })
564
- return questions
565
-
566
-
567
- def evaluate_mcq(model, tokenizer, mcq):
568
- model.eval()
569
- correct = 0
570
- with torch.no_grad():
571
- for q in mcq:
572
- q_ids = tokenizer.encode(q["question"])
573
- if len(q_ids) < MAX_SEQ_LEN:
574
- q_ids = q_ids + [PAD_ID] * (MAX_SEQ_LEN - len(q_ids))
575
- else:
576
- q_ids = q_ids[:MAX_SEQ_LEN]
577
- x = torch.tensor(q_ids, dtype=torch.long).unsqueeze(0)
578
- try:
579
- y_hat, delta = model(x, use_hypothesis=True)
580
- logprobs = F.log_softmax((y_hat + delta)[0], dim=-1)
581
- option_scores = []
582
- for opt in q["options"]:
583
- opt_ids = tokenizer.encode(opt)
584
- if len(opt_ids) == 0:
585
- opt_ids = [PAD_ID]
586
- score = logprobs[opt_ids[0]].item()
587
- option_scores.append(score)
588
- pred = int(np.argmax(option_scores))
589
- if pred == q["correct_idx"]:
590
- correct += 1
591
- except (RuntimeError, IndexError):
592
- continue
593
- model.train()
594
- return correct / max(1, len(mcq))
595
-
596
-
597
- # ==============================================================================
598
- # 7. DELETE BINARIES
599
- # ==============================================================================
600
- def delete_training_binaries():
601
- """Deleta binários do treinamento."""
602
- deleted = []
603
- patterns = [
604
- "/home/z/my-project/BiGRU_T_version/model_final/*.bin",
605
- "/home/z/my-project/BiGRU_T_version/model_final/*.pt",
606
- "/home/z/my-project/BiGRU_T_version/model_final/*.pth",
607
- "/home/z/my-project/BiGRU_T_version/model_final/*.safetensors",
608
- "/home/z/my-project/BiGRU_T_version/**/*.bin",
609
- "/home/z/my-project/BiGRU_T_version/**/*.pt",
610
- ]
611
- for pattern in patterns:
612
- for filepath in glob.glob(pattern, recursive=True):
613
- try:
614
- os.remove(filepath)
615
- deleted.append(filepath)
616
- logger.info(f" [BIN DELETE] {filepath}")
617
- except OSError:
618
- pass
619
- return deleted
620
-
621
-
622
- # ==============================================================================
623
- # 8. MAIN
624
- # ==============================================================================
625
- def main():
626
- logger.info("=" * 72)
627
- logger.info("TRAIN V6.1 — BiGRU_T_version")
628
- logger.info("PGVector-like lookup + EWC math upgrade + 5 datasets × 10 samples")
629
- logger.info("=" * 72)
630
-
631
- logger.info(f"Xeon V6: cores={XEON_STATUS['physical_cores']}, "
632
- f"AMX={XEON_STATUS['amx']['supported']}, "
633
- f"IPEX={XEON_STATUS['ipex_available']}")
634
- logger.info(f"FP16 benchmark: best_tflops={FP16_BENCH.get('best_tflops', 'N/A')}")
635
-
636
- # Tokenizer
637
- tokenizer_path = "/home/z/my-project/BiGRU_T_version/model_final/tokenizer/tokenizer.json"
638
- if os.path.exists(tokenizer_path):
639
- try:
640
- tokenizer = BBPETokenizer.load(tokenizer_path)
641
- logger.info(f"Tokenizer loaded (vocab={tokenizer.vocab_size})")
642
- except Exception as e:
643
- logger.warning(f"Failed to load tokenizer ({e}), fitting inline")
644
- tokenizer = BBPETokenizer(vocab_size=256)
645
- tokenizer.fit_on_texts(["hello world", "machine learning"])
646
- else:
647
- tokenizer = BBPETokenizer(vocab_size=256)
648
- tokenizer.fit_on_texts(["hello world", "machine learning"])
649
- logger.info(f"Tokenizer fitted inline (vocab={tokenizer.vocab_size})")
650
-
651
- # Build model
652
- logger.info("\n[1] Building UnifiedModel...")
653
- cfg = UnifiedModelConfig(
654
- vocab_size=tokenizer.vocab_size,
655
- d_model=128,
656
- max_modules=8,
657
- num_layers_hyp=NUM_LAYERS_HYP,
658
- max_seq_len=MAX_SEQ_LEN,
659
- )
660
- model = UnifiedModel(cfg)
661
- n_params = sum(p.numel() for p in model.parameters())
662
- logger.info(f" Model params: {n_params/1e6:.2f}M")
663
-
664
- # V6.1: REPLACE model.orq_cell with V6 Kohonen 4D SOM
665
- if USE_KOHONEN_ORQ:
666
- logger.info("\n[2] Replacing OrqCell with V6.1 Kohonen 4D SOM (PGVector-ready)...")
667
- orq_v6 = OrqCellV6(Kohonen4DConfig(
668
- d_cache=cfg.d_cache,
669
- d_model_in=cfg.output_dim_u8cell,
670
- nhead=cfg.nhead_orq,
671
- d_ff=cfg.d_ff_orq,
672
- total_steps=100,
673
- pgvector_complete_thr=PGV_COMPLETE_THR,
674
- pgvector_missing_thr=PGV_MISSING_THR,
675
- ))
676
- device = next(model.parameters()).device
677
- orq_v6 = orq_v6.to(device)
678
- old_orq = model.orq_cell
679
- model.orq_cell = orq_v6
680
- del old_orq
681
- logger.info(f" OrqCell V6.1 installed: "
682
- f"n_neurons={orq_v6.cfg.n_neurons}, "
683
- f"grid={orq_v6.cfg.grid_shape}, "
684
- f"PGV thresholds: complete≥{PGV_COMPLETE_THR}, missing<{PGV_MISSING_THR}")
685
-
686
- # V6 MTP head
687
- mtp_head = None
688
- if USE_MTP:
689
- mtp_head = MTPHead(
690
- hidden_size=cfg.d_cache,
691
- vocab_size=tokenizer.vocab_size,
692
- K=MTP_K,
693
- )
694
- mtp_head = mtp_head.to(device)
695
- logger.info(f" MTP head initialized: K={MTP_K}")
696
-
697
- # V6 Data augmenter
698
- augmenter = DataAugmenter(AugmentationConfig(
699
- vocab_size=tokenizer.vocab_size,
700
- p_token_dropout=0.05,
701
- p_cutout=0.03,
702
- ))
703
-
704
- # V6 Tool system (smoke test)
705
- cache = ToolCache(max_size=128)
706
- mutator = ToolMutator(mutation_rate=0.1, max_variants=2)
707
- mutator.register("encoder_layer", nn.Linear(cfg.d_model, cfg.d_model))
708
-
709
- # V6.1 EWC — inicializado após epoch 1
710
- ewc = None
711
-
712
- # Optimizer
713
- optimizer = torch.optim.AdamW(
714
- list(model.parameters()) + list(mtp_head.parameters() if mtp_head is not None else []),
715
- lr=3e-4, weight_decay=0.01,
716
- )
717
-
718
- # TripletPrototypeLoss
719
- triplet_loss = TripletPrototypeLoss(margin=0.3, lambda_triplet=TRIPLET_WEIGHT)
720
- prototypes = torch.zeros(cfg.max_modules, cfg.d_cache, requires_grad=False)
721
-
722
- # MetaConfigurator
723
- meta_cfg = MetaConfigurator(model)
724
-
725
- # Monitor V6.1
726
- monitor = MetricsMonitorV61()
727
-
728
- # =====================================================================
729
- # LOAD 5 DATASETS × 10 SAMPLES
730
- # =====================================================================
731
- logger.info("\n[3] Loading 5 datasets × 10 samples (streaming + synthetic fallback)...")
732
- hf_token = os.environ.get("HF_TOKEN")
733
- samples = load_v61_samples(tokenizer, MAX_SEQ_LEN, hf_token=hf_token)
734
- n_total = len(samples)
735
- n_train = int(0.8 * n_total)
736
- train_samples = samples[:n_train]
737
- val_samples = samples[n_train:]
738
- logger.info(f" Train: {len(train_samples)} | Val: {len(val_samples)}")
739
- # Report datasets distribution
740
- ds_dist: Dict[str, int] = {}
741
- for s in samples:
742
- ds_dist[s["dataset"]] = ds_dist.get(s["dataset"], 0) + 1
743
- logger.info(f" Dataset distribution: {ds_dist}")
744
-
745
- # MCQ eval
746
- mcq = make_mini_mcq(n_questions=8)
747
-
748
- # =====================================================================
749
- # TRAIN LOOP
750
- # =====================================================================
751
- logger.info("\n[4] Starting V6.1 training loop...")
752
- global_step = 0
753
- total_steps = EPOCHS * len(train_samples) // BATCH_SIZE
754
- if USE_KOHONEN_ORQ:
755
- model.orq_cell.cfg.total_steps = max(1, total_steps)
756
-
757
- for epoch in range(1, EPOCHS + 1):
758
- model.train()
759
- if USE_MTP:
760
- mtp_head.train()
761
- augmenter.train()
762
-
763
- epoch_losses = []
764
- epoch_kohonen_auxs = []
765
- epoch_pgv_statuses = []
766
-
767
- for i in range(0, len(train_samples), BATCH_SIZE):
768
- batch = train_samples[i:i + BATCH_SIZE]
769
- if len(batch) < 2:
770
- continue
771
- global_step += 1
772
- t0 = time.perf_counter()
773
-
774
- x = torch.stack([s["input_ids"] for s in batch])
775
- label = torch.stack([s["label_ids"] for s in batch])
776
- actual_bs = len(batch)
777
- n_tokens = actual_bs * MAX_SEQ_LEN
778
- batch_ds = batch[0]["dataset"] # track dataset da 1a sample do batch
779
-
780
- optimizer.zero_grad()
781
-
782
- # V6: Data augmentation on input
783
- x_aug = augmenter(x)
784
-
785
- # Forward com return_aux
786
- try:
787
- y_hat, delta, aux = model(
788
- x_aug,
789
- return_aux=True,
790
- use_hypothesis=True,
791
- stop_grad_hyp=True,
792
- )
793
- except RuntimeError as e:
794
- logger.warning(f" step {global_step}: forward failed: {e}")
795
- aggressive_cleanup()
796
- continue
797
-
798
- # V6.1: Update OrqCell SOM step
799
- if USE_KOHONEN_ORQ:
800
- model.orq_cell.update_step(global_step)
801
-
802
- # ─────────────────────────────────────────────────────────────
803
- # V6.1 — PGVector-like lookup (treino level)
804
- # O usuário pediu: "os níveis de treinamento e o de hipótese
805
- # precisam verificar se o mapa já possui aquela informação"
806
- #
807
- # IMPORTANTE: a leitura (pgvector_lookup) é read-only e pode
808
- # ser feita após forward. MAS a escrita (fill_missing_neurons_random)
809
- # modifica neuron_cache in-place — deve ser DEFERIDA até após
810
- # backward para não quebrar o autograd graph.
811
- # ─────────────────────────────────────────────────────────────
812
- pgv_metrics = None
813
- pgv_needs_fill = False
814
- if USE_KOHONEN_ORQ:
815
- try:
816
- with torch.no_grad():
817
- # H_para_lookup: (B, 1, d_cache) — single "module" para lookup
818
- H_for_lookup = aux["o"].unsqueeze(1)
819
- pgv_result = model.orq_cell.pgvector_lookup(H_for_lookup)
820
- pgv_metrics = pgv_result
821
- # Se MISSING, marca para preenchimento DEPOIS do backward
822
- # (user requirement: "se não houver atribuir valores
823
- # aleatórios aos parâmetros faltantes")
824
- if pgv_result["status"] == "MISSING":
825
- pgv_needs_fill = True
826
- except (RuntimeError, ValueError, KeyError) as e:
827
- pass
828
-
829
- # Loss LM
830
- target = label[:, 0]
831
- y_final = y_hat + delta
832
- loss_lm = F.cross_entropy(y_final, target)
833
-
834
- # Loss de entropia do module_selector
835
- loss_ent = model.lambda_ent_value * aux["entropy_reg"]
836
-
837
- # TripletPrototypeLoss
838
- embeddings_for_triplet = aux["o"]
839
- alpha_aux = aux["alpha"]
840
- labels_for_triplet = torch.full(
841
- (actual_bs,), int(alpha_aux.argmax()), dtype=torch.long
842
- )
843
- try:
844
- loss_triplet = triplet_loss(
845
- embeddings_for_triplet, labels_for_triplet, prototypes
846
- )
847
- with torch.no_grad():
848
- emb_for_ema = embeddings_for_triplet.detach()
849
- for i_ in range(actual_bs):
850
- lbl = labels_for_triplet[i_].item()
851
- if 0 <= lbl < prototypes.size(0):
852
- prototypes[lbl] = (
853
- (1 - TRIPLET_EMA_ALPHA) * prototypes[lbl]
854
- + TRIPLET_EMA_ALPHA * emb_for_ema[i_]
855
- )
856
- except (RuntimeError, ValueError):
857
- loss_triplet = torch.tensor(0.0, device=y_hat.device)
858
-
859
- # V6 MTP loss
860
- mtp_metrics = None
861
- mtp_loss_val = torch.tensor(0.0, device=y_hat.device)
862
- if USE_MTP and mtp_head is not None:
863
- try:
864
- h_for_mtp = aux["o"].unsqueeze(1).expand(-1, MTP_K, -1)
865
- mtp_logits, mtp_alphas = mtp_head(h_for_mtp)
866
- mtp_targets = label[:, :MTP_K]
867
- if mtp_targets.size(1) >= 1:
868
- mtp_loss_val, mtp_metrics = mtp_loss(
869
- mtp_logits, mtp_targets, mtp_alphas,
870
- entropy_beta=MTP_ENTROPY_BETA,
871
- )
872
- except (RuntimeError, ValueError) as e:
873
- logger.warning(f" MTP loss failed: {e}")
874
-
875
- # V6.1 EWC penalty (after epoch 1, with skip mask)
876
- ewc_metrics = None
877
- ewc_pen = torch.tensor(0.0, device=y_hat.device)
878
- if USE_EWC and ewc is not None and ewc.n_tasks > 0:
879
- try:
880
- # BMU indices from PGVector lookup
881
- bmu_idx = torch.full(
882
- (actual_bs,),
883
- pgv_metrics.get("best_neuron_idx", 0) if pgv_metrics else 0,
884
- dtype=torch.long, device=y_hat.device,
885
- )
886
- ewc_pen, ewc_metrics = ewc.compute_penalty(
887
- bmu_indices=bmu_idx,
888
- eval_mode=not model.training,
889
- )
890
- except (RuntimeError, AttributeError) as e:
891
- logger.warning(f" EWC penalty failed: {e}")
892
-
893
- # Loss total
894
- loss = (loss_lm + loss_ent + TRIPLET_WEIGHT * loss_triplet
895
- + MTP_LOSS_WEIGHT * mtp_loss_val + ewc_pen)
896
-
897
- # Backward
898
- loss.backward()
899
- grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
900
- optimizer.step()
901
-
902
- # V6.1 SOM update APÓS backward
903
- som_update_metrics = None
904
- if USE_KOHONEN_ORQ:
905
- try:
906
- som_update_metrics = model.orq_cell.update_map_after_backward()
907
- except (RuntimeError, AttributeError) as e:
908
- pass
909
-
910
- # V6.1 — PGVector fill (após backward + SOM update, safe p/ autograd)
911
- # Se o step anterior identificou MISSING, preenche neurônios
912
- # não-filled com valores aleatórios controlados.
913
- if pgv_needs_fill and USE_KOHONEN_ORQ:
914
- try:
915
- n_filled_now = model.orq_cell.fill_missing_neurons_random()
916
- if n_filled_now > 0:
917
- logger.info(f" [PGV] MISSING → preenchidos {n_filled_now} neurônios aleatoriamente")
918
- except (RuntimeError, AttributeError) as e:
919
- pass
920
-
921
- # V6 MetaConfigurator
922
- if global_step % META_INTERVAL == 0:
923
- try:
924
- meta_result = meta_cfg.step(loss.detach())
925
- monitor.record_meta(meta_result)
926
- except (RuntimeError, AttributeError):
927
- pass
928
-
929
- # V6.1 Capture Kohonen aux
930
- kohonen_aux = None
931
- if USE_KOHONEN_ORQ:
932
- try:
933
- with torch.no_grad():
934
- H_dummy = embeddings_for_triplet.unsqueeze(1)
935
- _, kohonen_aux = model.orq_cell(
936
- H_dummy, step=global_step, return_aux=True
937
- )
938
- if som_update_metrics and kohonen_aux:
939
- kohonen_aux["update_magnitude"] = som_update_metrics.get("update_magnitude", 0)
940
- kohonen_aux["bmu_drift"] = som_update_metrics.get("bmu_drift", 0)
941
- kohonen_aux["n_filled"] = som_update_metrics.get("n_filled",
942
- kohonen_aux.get("n_filled", 0))
943
- kohonen_aux["filled_ratio"] = (
944
- kohonen_aux["n_filled"] / model.orq_cell.cfg.n_neurons
945
- )
946
- except (RuntimeError, ValueError, KeyError) as e:
947
- pass
948
-
949
- loss_value = float(loss.item())
950
- grad_norm_value = float(grad_norm)
951
-
952
- del loss, y_hat, delta, aux, y_final, loss_lm, loss_ent
953
- del loss_triplet, mtp_loss_val, ewc_pen
954
- if 'mtp_logits' in locals():
955
- del mtp_logits
956
- if 'mtp_alphas' in locals():
957
- del mtp_alphas
958
- if 'embeddings_for_triplet' in locals():
959
- del embeddings_for_triplet
960
- if 'h_for_mtp' in locals():
961
- del h_for_mtp
962
- if global_step % CLEANUP_INTERVAL == 0:
963
- aggressive_cleanup()
964
-
965
- elapsed = time.perf_counter() - t0
966
- flops_est = 2 * n_params * n_tokens
967
-
968
- monitor.record_step(
969
- global_step, loss_value,
970
- n_tokens, elapsed, grad_norm_value,
971
- flops_estimate=flops_est,
972
- kohonen_aux=kohonen_aux,
973
- mtp_metrics=mtp_metrics,
974
- ewc_metrics=ewc_metrics,
975
- pgv_metrics=pgv_metrics,
976
- dataset_name=batch_ds,
977
- )
978
-
979
- epoch_losses.append(loss_value)
980
- if kohonen_aux:
981
- epoch_kohonen_auxs.append(kohonen_aux)
982
- if pgv_metrics:
983
- epoch_pgv_statuses.append(pgv_metrics["status"])
984
-
985
- # Fim do epoch — Val
986
- model.eval()
987
- if USE_MTP:
988
- mtp_head.eval()
989
- val_losses = []
990
- mtp_val_losses = []
991
-
992
- with torch.no_grad():
993
- for sample in val_samples:
994
- x = sample["input_ids"].unsqueeze(0)
995
- label = sample["label_ids"]
996
- try:
997
- y_hat, delta = model(x, use_hypothesis=True)
998
- target = label[0:1]
999
- vloss = F.cross_entropy(y_hat + delta, target)
1000
- val_losses.append(float(vloss.item()))
1001
-
1002
- # V6.1 MTP ativo em val
1003
- if USE_MTP and mtp_head is not None:
1004
- try:
1005
- y_hat2, delta2, aux2 = model(x, return_aux=True, use_hypothesis=True)
1006
- o = aux2["o"].unsqueeze(1).expand(-1, MTP_K, -1)
1007
- mtp_logits, mtp_alphas = mtp_head(o)
1008
- mtp_targets = label[:MTP_K].unsqueeze(0)
1009
- mtp_loss_val, _ = mtp_loss(
1010
- mtp_logits, mtp_targets, mtp_alphas,
1011
- entropy_beta=MTP_ENTROPY_BETA,
1012
- )
1013
- mtp_val_losses.append(float(mtp_loss_val.item()))
1014
- del y_hat2, delta2, aux2, o, mtp_logits, mtp_alphas
1015
- except (RuntimeError, ValueError):
1016
- pass
1017
- del y_hat, delta
1018
- except RuntimeError:
1019
- continue
1020
-
1021
- val_loss = sum(val_losses) / max(1, len(val_losses))
1022
- train_avg = sum(epoch_losses) / max(1, len(epoch_losses))
1023
- mtp_val_avg = sum(mtp_val_losses) / max(1, len(mtp_val_losses)) if mtp_val_losses else None
1024
- eval_acc = evaluate_mcq(model, tokenizer, mcq)
1025
- monitor.record_epoch(epoch, val_loss, train_avg, eval_acc, mtp_val_avg)
1026
-
1027
- # Log PGVector status distribution do epoch
1028
- if epoch_pgv_statuses:
1029
- from collections import Counter
1030
- status_counts = Counter(epoch_pgv_statuses)
1031
- logger.info(f" [PGV epoch {epoch}] status distribution: {dict(status_counts)}")
1032
-
1033
- # V6.1 EWC: Após epoch 1, computar Fisher (com skip_som_filled_neurons=True)
1034
- if USE_EWC and ewc is None and epoch == 1:
1035
- logger.info(f"\n [EWC V6.1] Computing Fisher after epoch {epoch}...")
1036
- logger.info(f" [EWC V6.1] skip_som_filled_neurons=True — neurônios filled no SOM serão MASCARADOS")
1037
- try:
1038
- ewc = EWCV6(model, EWCConfigV6(
1039
- lambda_ewc=EWC_LAMBDA,
1040
- lambda_topo=EWC_LAMBDA_TOPO,
1041
- bmu_reinforcement_gamma=2.0,
1042
- skip_som_filled_neurons=True, # V6.1 key feature
1043
- ))
1044
- if USE_KOHONEN_ORQ:
1045
- ewc.orq_cells["orq_cell"] = model.orq_cell
1046
-
1047
- # Snapshot do SOM antes do Fisher
1048
- n_filled_before = int(model.orq_cell.neuron_filled_mask.sum().item())
1049
- logger.info(f" [EWC V6.1] SOM filled neurons antes do Fisher: {n_filled_before}/8")
1050
-
1051
- def data_iter():
1052
- for sample in train_samples[:16]:
1053
- x = sample["input_ids"].unsqueeze(0)
1054
- y = sample["label_ids"][:1]
1055
- yield (x, y)
1056
- ewc.compute_fisher(data_iter(), n_samples=16, bmu_indices=list(range(8)))
1057
- logger.info(f" [EWC V6.1] Fisher computed: classic={len(ewc.fisher)}, "
1058
- f"neuron={len(ewc.neuron_fisher)}, "
1059
- f"topo={len(ewc.neuron_grid_dist)}")
1060
- status = ewc.get_status()
1061
- logger.info(f" [EWC V6.1] n_active_ewc_neurons={status['n_active_ewc_neurons']} "
1062
- f"(EWC atua nestes) | n_masked_som_neurons={status['n_masked_som_neurons']} "
1063
- f"(SOM já cobre — não duplica)")
1064
- except (RuntimeError, AttributeError) as e:
1065
- logger.warning(f" [EWC V6.1] Fisher computation failed: {e}")
1066
- ewc = None
1067
-
1068
- aggressive_cleanup()
1069
-
1070
- # =====================================================================
1071
- # FINAL REPORT
1072
- # =====================================================================
1073
- logger.info("\n[5] Generating V6.1 report...")
1074
- summary = monitor.summary()
1075
-
1076
- report = {
1077
- "version": "V6.1",
1078
- "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
1079
- "config": {
1080
- "datasets": V61_DATASETS,
1081
- "n_per_dataset": N_PER_DATASET,
1082
- "epochs": EPOCHS, "batch_size": BATCH_SIZE,
1083
- "max_seq_len": MAX_SEQ_LEN, "num_layers_hyp": NUM_LAYERS_HYP,
1084
- "use_mtp": USE_MTP, "use_ewc": USE_EWC, "use_kohonen_orq": USE_KOHONEN_ORQ,
1085
- "mtp_K": MTP_K, "mtp_entropy_beta": MTP_ENTROPY_BETA,
1086
- "ewc_lambda": EWC_LAMBDA, "ewc_lambda_topo": EWC_LAMBDA_TOPO,
1087
- "pgv_complete_thr": PGV_COMPLETE_THR,
1088
- "pgv_missing_thr": PGV_MISSING_THR,
1089
- },
1090
- "v6_1_improvements": {
1091
- "pgvector_like_lookup": {
1092
- "method": "pgvector_lookup() no OrqCell",
1093
- "statuses": ["COMPLETE", "INCOMPLETE", "MISSING"],
1094
- "complete_threshold": PGV_COMPLETE_THR,
1095
- "missing_threshold": PGV_MISSING_THR,
1096
- "neuron_filled_mask": True,
1097
- "auto_fill_missing": True,
1098
- },
1099
- "ewc_math_upgrade": {
1100
- "skip_som_filled_neurons": True,
1101
- "rationale": "EWC não reutiliza pesos já presentes no SOM — "
1102
- "evita dupla penalização (BMU + Fisher)",
1103
- "fisher_neuron_mask": True,
1104
- "n_masked_som_neurons_final": summary.get("ewc_evolution", {}).get(
1105
- "n_masked_som_neurons", [0])[-1] if summary.get("ewc_evolution", {}).get(
1106
- "n_masked_som_neurons") else 0,
1107
- },
1108
- "5_datasets_x_10_samples": True,
1109
- "hypothesis_level_pgvector": True,
1110
- "training_level_pgvector": True,
1111
- },
1112
- "v6_features_preserved": {
1113
- "kohonen_4d_som": True,
1114
- "ewc_per_neuron": True,
1115
- "mtp_active_in_val": True,
1116
- "mtp_entropy_regularizer": True,
1117
- "window_context_2m": True,
1118
- "dynamic_rep_penalty": True,
1119
- "data_augmentation": True,
1120
- "rlhf_mapping": True,
1121
- "thinking_system": True,
1122
- "tool_system": ["mutation", "cache", "agent"],
1123
- "langchain_compat": True,
1124
- "optimized_pipeline": True,
1125
- "xeon_v6_always_active": True,
1126
- "libvirt_amx_vm_activation": True,
1127
- },
1128
- "xeon_status": XEON_STATUS,
1129
- "fp16_benchmark": FP16_BENCH,
1130
- "dataset_distribution": summary.get("dataset_distribution", {}),
1131
- "result": {
1132
- **summary,
1133
- "n_params_M": n_params / 1e6,
1134
- },
1135
- }
1136
-
1137
- with open(REPORT_PATH, "w") as f:
1138
- json.dump(report, f, indent=2, default=str)
1139
- logger.info(f" Report saved: {REPORT_PATH}")
1140
-
1141
- # =====================================================================
1142
- # DELETE BINARIES
1143
- # =====================================================================
1144
- logger.info("\n[6] Deleting training binaries...")
1145
- deleted = delete_training_binaries()
1146
- logger.info(f" Deleted {len(deleted)} binary files")
1147
-
1148
- # =====================================================================
1149
- # SUMMARY
1150
- # =====================================================================
1151
- logger.info("\n" + "=" * 72)
1152
- logger.info("V6.1 TRAINING COMPLETE")
1153
- logger.info("=" * 72)
1154
- logger.info(f" Total samples: {len(samples)} (5 datasets × 10)")
1155
- logger.info(f" Total steps: {summary['n_steps']}, Epochs: {summary['n_epochs']}")
1156
- if summary['final_train_loss'] is not None:
1157
- logger.info(f" Final train_loss: {summary['final_train_loss']:.4f}")
1158
- if summary['final_val_loss'] is not None:
1159
- logger.info(f" Final val_loss: {summary['final_val_loss']:.4f}")
1160
- if summary['final_perplexity'] is not None:
1161
- logger.info(f" Perplexity: {summary['final_perplexity']:.2f}")
1162
- if summary['final_eval_benchmark'] is not None:
1163
- logger.info(f" Eval acc: {summary['final_eval_benchmark']:.2%}")
1164
- if summary['final_rss_mb'] is not None:
1165
- logger.info(f" RSS: {summary['final_rss_mb']:.0f}MB ({summary['rss_trend']})")
1166
- logger.info(f" Alerts: {summary['n_alerts']}")
1167
-
1168
- # V6.1 PGVector summary
1169
- pgv_ev = summary.get("pgvector_evolution", {})
1170
- if pgv_ev.get("status_counts"):
1171
- logger.info(f" PGVector status distribution:")
1172
- for s, c in pgv_ev["status_counts"].items():
1173
- logger.info(f" {s}: {c}")
1174
- logger.info(f" final coverage: {pgv_ev.get('final_coverage', 0):.3f}")
1175
- logger.info(f" final best_sim: {pgv_ev.get('final_best_sim', 0):.3f}")
1176
- logger.info(f" final n_filled: {pgv_ev.get('final_n_filled', 0)}/8")
1177
-
1178
- # V6.1 EWC summary
1179
- ewc_ev = summary.get("ewc_evolution", {})
1180
- if ewc_ev.get("n_masked_som_neurons"):
1181
- logger.info(f" EWC V6.1 math upgrade:")
1182
- logger.info(f" n_masked_som_neurons (skipped): {ewc_ev['n_masked_som_neurons'][-1]}")
1183
- logger.info(f" final EWC penalty: {ewc_ev['total_penalty'][-1] if ewc_ev['total_penalty'] else 0:.4f}")
1184
-
1185
- # V6 Kohonen summary
1186
- kohonen_ev = summary.get("kohonen_evolution", {})
1187
- if kohonen_ev.get("sigma_t"):
1188
- logger.info(f" Kohonen SOM evolution:")
1189
- logger.info(f" sigma_t: {kohonen_ev['sigma_t'][0]:.3f} → {kohonen_ev['final_sigma_t']:.3f}")
1190
- logger.info(f" alpha_t: {kohonen_ev['alpha_t'][0]:.3f} → {kohonen_ev['final_alpha_t']:.3f}")
1191
- logger.info(f" final n_filled: {kohonen_ev.get('final_n_filled', 0)}/8 "
1192
- f"(filled_ratio={kohonen_ev.get('final_filled_ratio', 0):.2%})")
1193
-
1194
- logger.info("=" * 72)
1195
-
1196
- return report
1197
-
1198
-
1199
- if __name__ == "__main__":
1200
- main()