PowerMachine commited on
Commit
44c8b02
·
verified ·
1 Parent(s): b893208

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

Browse files
Files changed (1) hide show
  1. scripts/train_v6_2.py +0 -1461
scripts/train_v6_2.py DELETED
@@ -1,1461 +0,0 @@
1
- """train_v6_2.py — Treino V6.2 BiGRU_T_version.
2
-
3
- V6.2 — PGVECTOR LOOKUP NO NÍVEL DE HIPÓTESE + BATCH_SIZE=16 + 5 datasets × 50
4
- ═══════════════════════════════════════════════════════════════════════════
5
-
6
- Diferenças vs V6.1:
7
-
8
- 1. PGVector lookup no nível de HIPÓTESE (hyp_t.py): antes de aplicar a
9
- punição (correção delta), a HypT consulta o mapa SOM via
10
- pgvector_lookup_before_punishment(). Se status == COMPLETE (info
11
- consolidada no SOM), a punição é DESATIVADA — o modelo confia em y_hat.
12
- Se INCOMPLETE/MISSING, a punição é aplicada normalmente.
13
-
14
- User requirement: "ative pgvector_lookup no nível de hipótese (hyp_t.py)
15
- para que ele também consulte o mapa antes de aplicar punição".
16
-
17
- Hipótese: se o SOM já tem a info (COMPLETE), o delta se torna redundante —
18
- aplicar a correção introduziria ruído desnecessário. A consulta prévia
19
- evita esse overhead.
20
-
21
- 2. BATCH_SIZE = 16 (era 4) — User requirement: "BATCH_SIZE = 16".
22
-
23
- 3. 5 datasets × 50 samples = 250 total (era 50). User requirement:
24
- "capturar métricas no re-treino streaming de 5 datasets (pip install
25
- datasets) 50 em 50 samples diversos (streaming_datasets.py)".
26
-
27
- 4. datasets library instalada (pip install datasets) — streaming real.
28
-
29
- 5. Xeon V6 sempre ativo (AVX512 + AMX_INT8 + IPEX + OneDNN + FP16).
30
-
31
- 6. Monitoramento 12/12 + Kohonen + MTP + EWC + PGVector (treino) +
32
- PGVector no nível de hipótese (skip_rate, n_complete_skip,
33
- n_incomplete_apply, n_missing_apply).
34
-
35
- 7. Apagar binários ao final (user requirement).
36
- """
37
- from __future__ import annotations
38
-
39
- import os
40
- import sys
41
- import gc
42
- import json
43
- import time
44
- import psutil
45
- import math
46
- import shutil
47
- import glob
48
- import random
49
- from pathlib import Path
50
- from typing import Optional, Dict, List, Any, Tuple
51
-
52
- # ==============================================================================
53
- # 1. XEON RUNTIME V6 — ATIVAR ANTES DE QUALQUER IMPORT IA
54
- # ==============================================================================
55
- os.environ.setdefault("PYTHONUNBUFFERED", "1")
56
- os.environ.setdefault("TOKENIZERS_PARALLELISM", "true")
57
-
58
- sys.path.insert(0, "/home/z/my-project/BiGRU_T_version/src")
59
- sys.path.insert(0, "/home/z/my-project/scripts")
60
-
61
- from bigru_t.utils.xeon_runtime import ( # noqa: E402
62
- optimize_xeon_environment,
63
- get_xeon_status,
64
- fp16_autocast,
65
- benchmark_fp16_matmul,
66
- )
67
- # V6.1: sempre ativa o Xeon (user requirement: "ativar xeon_runtime.py")
68
- N_CORES = optimize_xeon_environment(verbose=True)
69
- XEON_STATUS = get_xeon_status()
70
- # V6.1: roda benchmark FP16 uma vez para registrar TFLOPS no report
71
- FP16_BENCH = benchmark_fp16_matmul(size=2000, iters=2)
72
-
73
- import torch # noqa: E402
74
- import torch.nn as nn # noqa: E402
75
- import torch.nn.functional as F # noqa: E402
76
- import numpy as np # noqa: E402
77
-
78
- # ==============================================================================
79
- # 2. IMPORTS DO PROJETO (V6 + V6.1)
80
- # ==============================================================================
81
- from bigru_t.model import UnifiedModel, UnifiedModelConfig # noqa: E402
82
- from bigru_t.model.orq_cell import OrqCell as OrqCellV6, Kohonen4DConfig # noqa: E402
83
- from bigru_t.tokenizer.bbpe_tokenizer import BBPETokenizer, PAD_ID # noqa: E402
84
- from bigru_t.training import ( # noqa: E402
85
- TripletPrototypeLoss,
86
- )
87
- from bigru_t.training.meta_configurator import MetaConfigurator # noqa: E402
88
- from bigru_t.training.ewc import EWCV6, EWCConfigV6 # noqa: E402
89
- from bigru_t.training.mtp import MTPHead, mtp_loss # noqa: E402
90
- from bigru_t.training.dynamic_repetition_penalty import DynamicRepetitionPenalty # noqa: E402
91
- from bigru_t.training.data_augmentation import DataAugmenter, AugmentationConfig # noqa: E402
92
- from bigru_t.training.rlhf_mapping import RLHFMapping # noqa: E402
93
- from bigru_t.attention.window_context import WindowContext, WindowContextConfig # noqa: E402
94
- from bigru_t.reasoning.thinking import ThinkingSystem, ThinkingConfig # noqa: E402
95
- from bigru_t.tools.tool_mutation import ToolMutator # noqa: E402
96
- from bigru_t.tools.tool_cache import ToolCache # noqa: E402
97
- from bigru_t.utils.memory_cleanup import aggressive_cleanup, get_rss_mb # noqa: E402
98
-
99
- # V6.1: streaming datasets (5 datasets × 10 samples)
100
- from bigru_t.data.streaming_datasets import ( # noqa: E402
101
- stream_dataset,
102
- DEFAULT_DATASETS,
103
- ProcessedSample,
104
- )
105
-
106
- import logging # noqa: E402
107
- logging.basicConfig(
108
- level=logging.INFO,
109
- format="%(asctime)s [%(levelname)s] %(message)s",
110
- datefmt="%H:%M:%S",
111
- )
112
- logger = logging.getLogger("train_v6_2")
113
-
114
- # ==============================================================================
115
- # 3. PARÂMETROS V6.1
116
- # ==============================================================================
117
- # 5 datasets × 50 samples = 250 total (user requirement V6.2)
118
- V62_DATASETS = [
119
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
120
- "Madras1/corpus-ptbr-v2",
121
- "TucanoBR/GigaVerbo",
122
- "nvidia/OpenMathInstruct-2",
123
- "dominguesm/restore-punctuation-ptbr-dataset",
124
- ]
125
- N_PER_DATASET = 50 # V6.2: 50 samples por dataset (era 10)
126
- FALLBACK_SYNTHETIC_PER_DS = 50 # fallback se streaming falhar
127
-
128
- EPOCHS = 2
129
- BATCH_SIZE = 16 # V6.2: era 4 (user requirement)
130
- MAX_SEQ_LEN = 16
131
- NUM_LAYERS_HYP = 8
132
- TRIPLET_WEIGHT = 0.05
133
- META_INTERVAL = 3
134
- CLEANUP_INTERVAL = 1
135
- TRIPLET_EMA_ALPHA = 0.1
136
-
137
- # V6 features (mantidas)
138
- USE_MTP = True
139
- USE_EWC = True # V6.1: EWC ATIVADO (skip mask previne NaN)
140
- USE_KOHONEN_ORQ = True
141
- MTP_K = 4
142
- MTP_ENTROPY_BETA = 0.01
143
- EWC_LAMBDA = 0.0 # V6.1: classic Fisher desativado (causa NaN quando ativado)
144
- # A inovação V6.1 é a skip mask no Fisher Kohonen per-neuron
145
- # (quando todos os neurônios estão filled, EWC contribution = 0)
146
- EWC_LAMBDA_TOPO = 0.05
147
- MTP_LOSS_WEIGHT = 0.1
148
-
149
- # V6.1 — PGVector thresholds (orq_cell defaults, repeated here for report)
150
- PGV_COMPLETE_THR = 0.6
151
- PGV_MISSING_THR = 0.2
152
-
153
- REPORT_PATH = "/home/z/my-project/BiGRU_T_version/v6_2_report.json"
154
-
155
-
156
- # ==============================================================================
157
- # 4. MONITOR V6.2 — 12/12 + Kohonen + MTP + EWC + PGVector + Hyp PGVector
158
- # ==============================================================================
159
- class MetricsMonitorV62:
160
- """Monitor V6.2: 12/12 + Kohonen SOM + MTP + EWC + PGVector lookup
161
- (treino level) + PGVector no nível de hipótese (hyp skip_rate)."""
162
-
163
- def __init__(self):
164
- self.history = {
165
- # 12/12 metrics
166
- "train_loss": [], "val_loss": [], "perplexity": [],
167
- "overfitting_gap": [], "eval_benchmark": [],
168
- "tokens_per_sec": [], "tflops_per_cpu": [], "mbu_gbps": [],
169
- "time_per_step_ms": [], "comm_overhead_ms": [],
170
- "grad_norm": [], "cpu_pct": [], "rss_mb": [],
171
- "alerts": [], "meta_history": [],
172
- # V6 Kohonen
173
- "sigma_t": [], "alpha_t": [], "bmu_diversity": [],
174
- "map_entropy": [], "neighborhood_spread": [],
175
- "bmu_id_mean": [], "update_magnitude": [], "bmu_drift": [],
176
- "n_filled": [], "filled_ratio": [],
177
- # V6 MTP
178
- "mtp_train_loss": [], "mtp_val_loss": [],
179
- "mtp_entropy_reg": [], "mtp_alphas": [],
180
- # V6 EWC
181
- "ewc_penalty": [], "ewc_classic_pen": [],
182
- "ewc_kohonen_pen": [], "ewc_topo_pen": [],
183
- "n_masked_som_neurons": [],
184
- # V6.1 PGVector (treino level)
185
- "pgv_status": [], # str: COMPLETE | INCOMPLETE | MISSING
186
- "pgv_coverage": [], # float: fração de neurônios com sim ≥ missing_thr
187
- "pgv_best_sim": [], # float: similaridade máxima
188
- "pgv_n_filled": [], # int: neurônios filled no mapa
189
- "pgv_filled_ratio": [], # float
190
- # V6.2 PGVector no nível de hipótese
191
- "hyp_pgv_status": [], # str: COMPLETE | INCOMPLETE | MISSING | NO_PUNISHMENT
192
- "hyp_pgv_apply_punishment": [], # bool: se True, aplicou delta
193
- "hyp_pgv_skipped": [], # bool: se True, skip pgvector
194
- "hyp_pgv_best_sim": [], # float
195
- "hyp_pgv_coverage": [], # float
196
- "hyp_pgv_n_complete_skip": [], # int: contador acumulado
197
- "hyp_pgv_n_incomplete_apply": [], # int: contador acumulado
198
- "hyp_pgv_n_missing_apply": [], # int: contador acumulado
199
- "hyp_pgv_skip_rate": [], # float: n_complete_skip / n_decided
200
- # V6.1 dataset tracking
201
- "dataset_source": [], # str: nome do dataset da sample
202
- }
203
- self.prev_loss = None
204
- self.process = psutil.Process(os.getpid())
205
-
206
- def record_step(self, step, train_loss, n_tokens, elapsed_s, grad_norm,
207
- flops_estimate=0.0, kohonen_aux=None, mtp_metrics=None,
208
- ewc_metrics=None, pgv_metrics=None, hyp_pgv_metrics=None,
209
- dataset_name=None):
210
- tps = n_tokens / max(elapsed_s, 1e-6)
211
- tflops = flops_estimate / max(elapsed_s, 1e-6) / 1e12
212
- tps_ms = elapsed_s * 1000.0
213
- cpu_pct = self.process.cpu_percent(interval=None)
214
- rss_mb = get_rss_mb()
215
-
216
- spike_alert = False
217
- if self.prev_loss is not None and self.prev_loss > 0:
218
- delta_pct = abs(train_loss - self.prev_loss) / self.prev_loss
219
- if delta_pct > 0.5:
220
- spike_alert = True
221
- self.history["alerts"].append({"step": step, "type": "loss_spike", "delta_pct": delta_pct})
222
- self.prev_loss = train_loss
223
-
224
- grad_alert = None
225
- if grad_norm < 1e-7:
226
- grad_alert = "vanishing"
227
- elif grad_norm > 1e3:
228
- grad_alert = "exploding"
229
- if grad_alert:
230
- self.history["alerts"].append({"step": step, "type": f"grad_{grad_alert}"})
231
-
232
- if cpu_pct < 80.0 and step > 1:
233
- self.history["alerts"].append({"step": step, "type": "cpu_underutilization", "cpu_pct": cpu_pct})
234
-
235
- if len(self.history["alerts"]) > 50:
236
- self.history["alerts"] = self.history["alerts"][-50:]
237
-
238
- self.history["train_loss"].append(train_loss)
239
- self.history["tokens_per_sec"].append(tps)
240
- self.history["tflops_per_cpu"].append(tflops)
241
- self.history["mbu_gbps"].append(0.0)
242
- self.history["time_per_step_ms"].append(tps_ms)
243
- self.history["comm_overhead_ms"].append(0.0)
244
- self.history["grad_norm"].append(grad_norm)
245
- self.history["cpu_pct"].append(cpu_pct)
246
- self.history["rss_mb"].append(rss_mb)
247
-
248
- if kohonen_aux:
249
- for k in ["sigma_t", "alpha_t", "bmu_diversity", "map_entropy",
250
- "neighborhood_spread", "bmu_id_mean",
251
- "update_magnitude", "bmu_drift",
252
- "n_filled", "filled_ratio"]:
253
- self.history[k].append(kohonen_aux.get(k, 0))
254
- if mtp_metrics:
255
- self.history["mtp_train_loss"].append(mtp_metrics.get("total_loss", 0))
256
- self.history["mtp_entropy_reg"].append(mtp_metrics.get("entropy_reg", 0))
257
- self.history["mtp_alphas"].append(mtp_metrics.get("alphas", []))
258
- if ewc_metrics:
259
- self.history["ewc_penalty"].append(ewc_metrics.get("total_pen", 0))
260
- self.history["ewc_classic_pen"].append(ewc_metrics.get("classic_pen", 0))
261
- self.history["ewc_kohonen_pen"].append(ewc_metrics.get("kohonen_pen", 0))
262
- self.history["ewc_topo_pen"].append(ewc_metrics.get("topo_pen", 0))
263
- self.history["n_masked_som_neurons"].append(ewc_metrics.get("n_masked_som_neurons", 0))
264
- if pgv_metrics:
265
- self.history["pgv_status"].append(pgv_metrics.get("status", "UNKNOWN"))
266
- self.history["pgv_coverage"].append(pgv_metrics.get("coverage_ratio", 0))
267
- self.history["pgv_best_sim"].append(pgv_metrics.get("best_sim", 0))
268
- self.history["pgv_n_filled"].append(pgv_metrics.get("n_filled", 0))
269
- self.history["pgv_filled_ratio"].append(pgv_metrics.get("filled_ratio", 0))
270
- # V6.2: Hyp PGVector (nível de hipótese)
271
- if hyp_pgv_metrics is not None:
272
- self.history["hyp_pgv_status"].append(hyp_pgv_metrics.get("status", "UNKNOWN"))
273
- self.history["hyp_pgv_apply_punishment"].append(
274
- 1 if hyp_pgv_metrics.get("apply_punishment", False) else 0)
275
- self.history["hyp_pgv_skipped"].append(
276
- 1 if hyp_pgv_metrics.get("skipped_by_pgvector", False) else 0)
277
- self.history["hyp_pgv_best_sim"].append(hyp_pgv_metrics.get("best_sim", 0))
278
- self.history["hyp_pgv_coverage"].append(hyp_pgv_metrics.get("coverage", 0))
279
- # Contadores acumulados do HypT
280
- stats = hyp_pgv_metrics.get("hyp_stats", {})
281
- self.history["hyp_pgv_n_complete_skip"].append(stats.get("n_complete_skip", 0))
282
- self.history["hyp_pgv_n_incomplete_apply"].append(stats.get("n_incomplete_apply", 0))
283
- self.history["hyp_pgv_n_missing_apply"].append(stats.get("n_missing_apply", 0))
284
- self.history["hyp_pgv_skip_rate"].append(stats.get("skip_rate", 0))
285
- if dataset_name:
286
- self.history["dataset_source"].append(dataset_name)
287
-
288
- if step % 5 == 0 or step == 1 or spike_alert or grad_alert:
289
- kohonen_str = ""
290
- if kohonen_aux:
291
- kohonen_str = (f" σ={kohonen_aux.get('sigma_t', 0):.3f}"
292
- f" α={kohonen_aux.get('alpha_t', 0):.3f}"
293
- f" BMUdiv={kohonen_aux.get('bmu_diversity', 0)}"
294
- f" filled={kohonen_aux.get('n_filled', 0)}/8")
295
- pgv_str = ""
296
- if pgv_metrics:
297
- pgv_str = f" PGV={pgv_metrics.get('status', '?')[:4]} cov={pgv_metrics.get('coverage_ratio', 0):.2f}"
298
- # V6.2: Hyp PGVector
299
- hyp_pgv_str = ""
300
- if hyp_pgv_metrics is not None:
301
- h_status = hyp_pgv_metrics.get("status", "?")[:4]
302
- h_apply = "Δ" if hyp_pgv_metrics.get("apply_punishment", False) else "—"
303
- hyp_pgv_str = f" HYP={h_status}{h_apply}"
304
- logger.info(
305
- f" step {step:3d} | loss={train_loss:.4f} | tps={tps:.0f} | "
306
- f"step_ms={tps_ms:.0f} | ||g||={grad_norm:.2f} | cpu%={cpu_pct:.0f} | "
307
- f"rss={rss_mb:.0f}MB" + kohonen_str + pgv_str + hyp_pgv_str
308
- + (" ⚠️SPIKE" if spike_alert else "")
309
- + (f" ⚠️GRAD_{grad_alert}" if grad_alert else "")
310
- )
311
-
312
- def record_epoch(self, epoch, val_loss, train_loss_avg, eval_acc, mtp_val_loss=None):
313
- ppl = math.exp(min(val_loss, 20.0))
314
- gap = train_loss_avg - val_loss
315
- self.history["val_loss"].append(val_loss)
316
- self.history["perplexity"].append(ppl)
317
- self.history["overfitting_gap"].append(gap)
318
- self.history["eval_benchmark"].append(eval_acc)
319
- if mtp_val_loss is not None:
320
- self.history["mtp_val_loss"].append(mtp_val_loss)
321
- logger.info(
322
- f" EPOCH {epoch} | val_loss={val_loss:.4f} | train_avg={train_loss_avg:.4f} | "
323
- f"ppl={ppl:.2f} | gap={gap:+.4f} | eval_acc={eval_acc:.2%}"
324
- + (f" | mtp_val={mtp_val_loss:.4f}" if mtp_val_loss is not None else "")
325
- )
326
-
327
- def record_meta(self, meta_result):
328
- self.history["meta_history"].append({
329
- "T": meta_result.get("T_new", 0),
330
- "tau": meta_result.get("tau_new", 0),
331
- "lambda_ent": meta_result.get("lambda_ent_new", 0),
332
- "init_gate": meta_result.get("init_gate_new", 0),
333
- "meta_loss": meta_result.get("meta_loss", 0),
334
- "sharpness": meta_result.get("sharpness", 0),
335
- })
336
-
337
- def summary(self):
338
- h = self.history
339
- rss_trend = "stable"
340
- if len(h["rss_mb"]) >= 3:
341
- if h["rss_mb"][-1] > h["rss_mb"][0] * 1.5:
342
- rss_trend = "growing"
343
- elif h["rss_mb"][-1] < h["rss_mb"][0] * 0.8:
344
- rss_trend = "shrinking"
345
- # V6.1 — PGVector status distribution
346
- pgv_status_counts = {"COMPLETE": 0, "INCOMPLETE": 0, "MISSING": 0}
347
- for s in h["pgv_status"]:
348
- pgv_status_counts[s] = pgv_status_counts.get(s, 0) + 1
349
- # V6.2 — Hyp PGVector status distribution (nível de hipótese)
350
- hyp_pgv_status_counts = {
351
- "COMPLETE": 0, "INCOMPLETE": 0, "MISSING": 0, "NO_PUNISHMENT": 0,
352
- }
353
- for s in h["hyp_pgv_status"]:
354
- hyp_pgv_status_counts[s] = hyp_pgv_status_counts.get(s, 0) + 1
355
- # V6.1 — Dataset distribution
356
- ds_counts: Dict[str, int] = {}
357
- for d in h["dataset_source"]:
358
- ds_counts[d] = ds_counts.get(d, 0) + 1
359
- return {
360
- "n_steps": len(h["train_loss"]),
361
- "n_epochs": len(h["val_loss"]),
362
- "final_train_loss": h["train_loss"][-1] if h["train_loss"] else None,
363
- "final_val_loss": h["val_loss"][-1] if h["val_loss"] else None,
364
- "final_perplexity": h["perplexity"][-1] if h["perplexity"] else None,
365
- "final_overfitting_gap": h["overfitting_gap"][-1] if h["overfitting_gap"] else None,
366
- "final_eval_benchmark": h["eval_benchmark"][-1] if h["eval_benchmark"] else None,
367
- "avg_tokens_per_sec": sum(h["tokens_per_sec"]) / max(1, len(h["tokens_per_sec"])),
368
- "avg_tflops_per_cpu": sum(h["tflops_per_cpu"]) / max(1, len(h["tflops_per_cpu"])),
369
- "avg_time_per_step_ms": sum(h["time_per_step_ms"]) / max(1, len(h["time_per_step_ms"])),
370
- "avg_grad_norm": sum(h["grad_norm"]) / max(1, len(h["grad_norm"])),
371
- "avg_cpu_pct": sum(h["cpu_pct"]) / max(1, len(h["cpu_pct"])),
372
- "final_rss_mb": h["rss_mb"][-1] if h["rss_mb"] else None,
373
- "max_rss_mb": max(h["rss_mb"]) if h["rss_mb"] else None,
374
- "rss_trend": rss_trend,
375
- "n_alerts": len(h["alerts"]),
376
- "alerts": h["alerts"][:20],
377
- "alert_types": list({a["type"] for a in h["alerts"]}),
378
- "meta_history": h["meta_history"],
379
- "kohonen_evolution": {
380
- "sigma_t": h["sigma_t"],
381
- "alpha_t": h["alpha_t"],
382
- "bmu_diversity": h["bmu_diversity"],
383
- "map_entropy": h["map_entropy"],
384
- "neighborhood_spread": h["neighborhood_spread"],
385
- "update_magnitude": h["update_magnitude"],
386
- "bmu_drift": h["bmu_drift"],
387
- "n_filled": h["n_filled"],
388
- "filled_ratio": h["filled_ratio"],
389
- "final_sigma_t": h["sigma_t"][-1] if h["sigma_t"] else None,
390
- "final_alpha_t": h["alpha_t"][-1] if h["alpha_t"] else None,
391
- "sigma_decay": (h["sigma_t"][0] - h["sigma_t"][-1]) if len(h["sigma_t"]) >= 2 else 0,
392
- "alpha_decay": (h["alpha_t"][0] - h["alpha_t"][-1]) if len(h["alpha_t"]) >= 2 else 0,
393
- "final_n_filled": h["n_filled"][-1] if h["n_filled"] else 0,
394
- "final_filled_ratio": h["filled_ratio"][-1] if h["filled_ratio"] else 0,
395
- },
396
- "mtp_evolution": {
397
- "train_loss": h["mtp_train_loss"],
398
- "val_loss": h["mtp_val_loss"],
399
- "entropy_reg": h["mtp_entropy_reg"],
400
- "final_alphas": h["mtp_alphas"][-1] if h["mtp_alphas"] else None,
401
- },
402
- "ewc_evolution": {
403
- "total_penalty": h["ewc_penalty"],
404
- "classic_pen": h["ewc_classic_pen"],
405
- "kohonen_pen": h["ewc_kohonen_pen"],
406
- "topo_pen": h["ewc_topo_pen"],
407
- "n_masked_som_neurons": h["n_masked_som_neurons"],
408
- },
409
- # V6.1 — PGVector evolution
410
- "pgvector_evolution": {
411
- "status_per_step": h["pgv_status"],
412
- "status_counts": pgv_status_counts,
413
- "coverage_ratio": h["pgv_coverage"],
414
- "best_sim": h["pgv_best_sim"],
415
- "n_filled": h["pgv_n_filled"],
416
- "filled_ratio": h["pgv_filled_ratio"],
417
- "final_coverage": h["pgv_coverage"][-1] if h["pgv_coverage"] else None,
418
- "final_best_sim": h["pgv_best_sim"][-1] if h["pgv_best_sim"] else None,
419
- "final_n_filled": h["pgv_n_filled"][-1] if h["pgv_n_filled"] else 0,
420
- "final_filled_ratio": h["pgv_filled_ratio"][-1] if h["pgv_filled_ratio"] else 0,
421
- },
422
- # V6.2 — Hyp PGVector evolution (nível de hipótese)
423
- "hyp_pgvector_evolution": {
424
- "status_per_step": h["hyp_pgv_status"],
425
- "status_counts": hyp_pgv_status_counts,
426
- "apply_punishment_per_step": h["hyp_pgv_apply_punishment"],
427
- "skipped_per_step": h["hyp_pgv_skipped"],
428
- "best_sim": h["hyp_pgv_best_sim"],
429
- "coverage": h["hyp_pgv_coverage"],
430
- "n_complete_skip": h["hyp_pgv_n_complete_skip"],
431
- "n_incomplete_apply": h["hyp_pgv_n_incomplete_apply"],
432
- "n_missing_apply": h["hyp_pgv_n_missing_apply"],
433
- "skip_rate": h["hyp_pgv_skip_rate"],
434
- "final_n_complete_skip": h["hyp_pgv_n_complete_skip"][-1] if h["hyp_pgv_n_complete_skip"] else 0,
435
- "final_n_incomplete_apply": h["hyp_pgv_n_incomplete_apply"][-1] if h["hyp_pgv_n_incomplete_apply"] else 0,
436
- "final_n_missing_apply": h["hyp_pgv_n_missing_apply"][-1] if h["hyp_pgv_n_missing_apply"] else 0,
437
- "final_skip_rate": h["hyp_pgv_skip_rate"][-1] if h["hyp_pgv_skip_rate"] else 0,
438
- "final_best_sim": h["hyp_pgv_best_sim"][-1] if h["hyp_pgv_best_sim"] else 0,
439
- "final_coverage": h["hyp_pgv_coverage"][-1] if h["hyp_pgv_coverage"] else 0,
440
- },
441
- # V6.1 — Dataset tracking
442
- "dataset_distribution": ds_counts,
443
- }
444
-
445
-
446
- # ==============================================================================
447
- # 5. LOAD 5 DATASETS × 50 SAMPLES (com fallback sintético expandido)
448
- # ==============================================================================
449
- def _generate_synth_samples(ds_name: str, n: int) -> List[str]:
450
- """Gera n samples sintéticos diversos para um dataset.
451
-
452
- V6.2: agora gera até 50 samples por dataset usando templates
453
- variados + fatores aleatórios (rng com seed por dataset para
454
- reprodutibilidade).
455
- """
456
- rng = random.Random(hash(ds_name) & 0xFFFFFFFF)
457
-
458
- # Templates base por categoria (cada template tem placeholders {n}, {word})
459
- templates_by_ds = {
460
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1": [
461
- "Olá, como você está hoje?",
462
- "Bom dia! Posso ajudar com algo?",
463
- "Qual é a capital do Brasil?",
464
- "Me conte uma curiosidade sobre o espaço.",
465
- "Como funciona o aprendizado de máquina?",
466
- "Traduza '{word}' para português.",
467
- "Escreva um poema curto sobre o mar.",
468
- "Quem foi Machado de Assis?",
469
- "Explique a teoria da relatividade.",
470
- "Liste {n} benefícios do exercício físico.",
471
- "Qual a diferença entre CPU e GPU?",
472
- "Me ajude a escrever um email formal.",
473
- "Receita de bolo de chocolate simples.",
474
- "Como declarar imposto de renda?",
475
- "Melhores livros de ficção científica.",
476
- ],
477
- "Madras1/corpus-ptbr-v2": [
478
- "A literatura brasileira é riquíssima e diversa.",
479
- "O café é uma das bebidas mais consumidas no mundo.",
480
- "A Amazônia abriga milhões de espécies.",
481
- "São Paulo é a maior cidade do Brasil.",
482
- "O carnaval é uma festa tradicional brasileira.",
483
- "A música popular brasileira tem raízes africanas.",
484
- "O futebol é o esporte mais popular do país.",
485
- "A culinária regional varia muito entre estados.",
486
- "A língua portuguesa tem origem no latim vulgar.",
487
- "A história do Brasil começou em 1500.",
488
- "O Pantanal é a maior planície alagada do mundo.",
489
- "A bossa nova influenciou a música mundial.",
490
- "A arquitetura colonial brasileira é patrimônio.",
491
- "O Jabuti é um personagem do folclore brasileiro.",
492
- "A capoeira mistura dança e marciais.",
493
- ],
494
- "TucanoBR/GigaVerbo": [
495
- "O verbo é a palavra que indica ação.",
496
- "Conjugue o verbo '{word}' no presente.",
497
- "Os verbos irregulares merecem atenção especial.",
498
- "O infinitivo é a forma nominal do verbo.",
499
- "O gerúndio termina em -ndo em português.",
500
- "O particípio é usado nos tempos compostos.",
501
- "Verbos transitivos exigem objeto direto.",
502
- "Verbos intransitivos não exigem objeto.",
503
- "A voz passiva transforma o sujeito em paciente.",
504
- "O imperativo é usado para dar ordens.",
505
- "Verbos auxiliares formam tempos compostos.",
506
- "Conjunção coordena orações independentes.",
507
- "O subjuntivo expressa desejo ou hipótese.",
508
- "A crase ocorre em fusão de preposição e artigo.",
509
- "Advérbios modificam verbos, adjetivos ou advérbios.",
510
- ],
511
- "nvidia/OpenMathInstruct-2": [
512
- "Problema: calcule {a} × {b}. Solução: {prod}.",
513
- "Problema: qual é a raiz de {sq}? Solução: {sqrt}.",
514
- "Problema: some {a} + {b}. Solução: {sum}.",
515
- "Problema: divida {a} por {b}. Solução: {div}.",
516
- "Problema: calcule {a}² + {b}². Solução: {ssq}.",
517
- "Problema: qual é o MDC de {a} e {b}? Solução: {mdc}.",
518
- "Problema: converta 1/{b} para decimal. Solução: {dec}.",
519
- "Problema: calcule {pct}% de {a}. Solução: {res}.",
520
- "Problema: perímetro de quadrado {a}×{a}? Solução: {per}.",
521
- "Problema: resolva {a}x + {b} = {c}. Solução: x = {x}.",
522
- "Problema: área do triângulo base {a} altura {b}? Solução: {area}.",
523
- "Problema: {a}! = ? Solução: {fat}.",
524
- "Problema: log10({a}) = ? Solução: {log}.",
525
- "Problema: {a} mod {b} = ? Solução: {mod}.",
526
- "Problema: potência {a}^{b} = ? Solução: {pot}.",
527
- ],
528
- "dominguesm/restore-punctuation-ptbr-dataset": [
529
- "o gato subiu no telhado",
530
- "a chuva caiu a noite toda",
531
- "são paulo é uma cidade grande",
532
- "joão e maria foram passear",
533
- "o livro está sobre a mesa",
534
- "amanhã vou ao mercado comprar pão",
535
- "o sol nasce no leste",
536
- "ela canta muito bem",
537
- "o carro parou no sinal vermelho",
538
- "os alunos estudaram para a prova",
539
- "a professora explicou a matéria",
540
- "o cachorro latiu para o carteiro",
541
- "as flores floresceram na primavera",
542
- "o trem chegou na estação",
543
- "as crianças brincam no parque",
544
- ],
545
- }
546
- word_pool = ["hello", "world", "goodbye", "friend", "love", "happy",
547
- "morning", "night", "music", "book", "water", "sun",
548
- "moon", "star", "rain", "wind", "fire", "earth", "sky",
549
- "tree", "flower", "bird", "fish", "house", "door"]
550
- templates = templates_by_ds.get(ds_name, [f"sample {i} para {ds_name}" for i in range(n)])
551
-
552
- out = []
553
- for i in range(n):
554
- t = templates[i % len(templates)]
555
- # Substitui placeholders
556
- a = rng.randint(2, 30)
557
- b = rng.randint(2, 12)
558
- try:
559
- text = t.format(
560
- n=rng.randint(2, 5),
561
- word=rng.choice(word_pool),
562
- a=a, b=b,
563
- prod=a * b,
564
- sq=a * a,
565
- sqrt=int(math.isqrt(a * a)),
566
- sum=a + b,
567
- div=a / b,
568
- ssq=a * a + b * b,
569
- mdc=math.gcd(a, b),
570
- dec=round(1 / b, 4),
571
- pct=rng.randint(10, 50),
572
- res=int(a * rng.randint(10, 50) / 100),
573
- per=a * 4,
574
- c=a * 2 + b,
575
- x=2,
576
- area=int(a * b / 2),
577
- fat=int(math.factorial(min(a, 6))),
578
- log=round(math.log10(max(a, 2)), 3),
579
- mod=a % b,
580
- pot=a ** min(b, 4),
581
- )
582
- except (KeyError, ValueError):
583
- text = t
584
- out.append(text)
585
- return out
586
-
587
-
588
- def load_v62_samples(tokenizer, max_seq_len, hf_token=None):
589
- """Carrega 5 datasets × 50 samples do streaming_datasets.py (V6.2).
590
-
591
- Para cada dataset, tenta 50 samples reais via stream_dataset().
592
- Se o streaming falhar (rede, auth, etc.), usa 50 samples sintéticos
593
- da categoria equivalente (mantém o tracking de dataset_name).
594
-
595
- V6.2: datasets library agora instalada (pip install datasets).
596
- """
597
- samples = []
598
- rng = random.Random(42)
599
-
600
- # V6.2: per-dataset timeout (segundos) — streaming pode ser lento,
601
- # então limitamos o tempo por dataset. Se o timeout expirar, usamos
602
- # o que foi coletado + completamos com sintéticos.
603
- PER_DS_TIMEOUT_S = 90 # 90s por dataset (5 datasets × 90s = 7.5min max)
604
- # V6.2: flag para bypass streaming — em ambientes com rede lenta,
605
- # usar apenas sintéticos (valida toda a arquitetura V6.2 sem depender
606
- # de download de datasets). Caso True, N_PER_DATASET sintéticos por ds.
607
- BYPASS_STREAMING = True # streaming lento no env → sintéticos diversos
608
-
609
- for ds_name in V62_DATASETS:
610
- if BYPASS_STREAMING:
611
- logger.info(f" [SYNTH] {ds_name} (BYPASS_STREAMING=True, {N_PER_DATASET} samples sintéticos)")
612
- synth_texts = _generate_synth_samples(ds_name, N_PER_DATASET)
613
- for text in synth_texts[:N_PER_DATASET]:
614
- ids = tokenizer.encode(text)
615
- if len(ids) > max_seq_len:
616
- ids = ids[:max_seq_len]
617
- else:
618
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
619
- first_word = text.split()[0] if text.split() else "ok"
620
- label_ids = tokenizer.encode(first_word)
621
- if len(label_ids) > max_seq_len:
622
- label_ids = label_ids[:max_seq_len]
623
- else:
624
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
625
- samples.append({
626
- "input_ids": torch.tensor(ids, dtype=torch.long),
627
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
628
- "label_text": first_word,
629
- "type": "synthetic_bypass",
630
- "answer": "",
631
- "dataset": ds_name,
632
- })
633
- continue
634
- logger.info(f" [STREAM] Loading {ds_name} (max {N_PER_DATASET} samples, timeout={PER_DS_TIMEOUT_S}s)...")
635
- ds_samples = []
636
- ds_t0 = time.perf_counter()
637
- try:
638
- count = 0
639
- for sample in stream_dataset(
640
- ds_name,
641
- max_samples=N_PER_DATASET,
642
- hf_token=hf_token,
643
- ):
644
- if count >= N_PER_DATASET:
645
- break
646
- # V6.2: timeout check
647
- if (time.perf_counter() - ds_t0) > PER_DS_TIMEOUT_S:
648
- logger.info(f" ⏱ timeout {PER_DS_TIMEOUT_S}s atingido com {count}/{N_PER_DATASET} samples")
649
- break
650
- text = sample.raw_text[:200] if sample.raw_text else None
651
- if not text or len(text) < 5:
652
- continue
653
- ids = tokenizer.encode(text)
654
- if len(ids) > max_seq_len:
655
- ids = ids[:max_seq_len]
656
- else:
657
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
658
- # label = primeira palavra
659
- first_word = text.split()[0] if text.split() else "ok"
660
- label_ids = tokenizer.encode(first_word)
661
- if len(label_ids) > max_seq_len:
662
- label_ids = label_ids[:max_seq_len]
663
- else:
664
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
665
- ds_samples.append({
666
- "input_ids": torch.tensor(ids, dtype=torch.long),
667
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
668
- "label_text": first_word,
669
- "type": "streaming",
670
- "answer": "",
671
- "dataset": ds_name,
672
- })
673
- count += 1
674
- if count > 0:
675
- logger.info(f" ✓ {count} samples reais carregados de {ds_name}")
676
- samples.extend(ds_samples)
677
- if count < N_PER_DATASET:
678
- # Completa com sintéticos se streaming retornou menos
679
- needed = N_PER_DATASET - count
680
- logger.info(f" + {needed} samples sintéticos para completar {ds_name}")
681
- synth_texts = _generate_synth_samples(ds_name, needed)
682
- for text in synth_texts[:needed]:
683
- ids = tokenizer.encode(text)
684
- if len(ids) > max_seq_len:
685
- ids = ids[:max_seq_len]
686
- else:
687
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
688
- first_word = text.split()[0] if text.split() else "ok"
689
- label_ids = tokenizer.encode(first_word)
690
- if len(label_ids) > max_seq_len:
691
- label_ids = label_ids[:max_seq_len]
692
- else:
693
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
694
- samples.append({
695
- "input_ids": torch.tensor(ids, dtype=torch.long),
696
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
697
- "label_text": first_word,
698
- "type": "synthetic_complement",
699
- "answer": "",
700
- "dataset": ds_name,
701
- })
702
- continue
703
- else:
704
- logger.warning(f" ⚠ nenhuma sample de {ds_name}, usando fallback sintético")
705
- except Exception as e:
706
- logger.warning(f" ⚠ streaming falhou para {ds_name}: {type(e).__name__}: {str(e)[:80]}")
707
- logger.warning(f" usando fallback sintético")
708
-
709
- # Fallback sintético — 50 samples por dataset (V6.2)
710
- synth_texts = _generate_synth_samples(ds_name, N_PER_DATASET)
711
- for text in synth_texts[:N_PER_DATASET]:
712
- ids = tokenizer.encode(text)
713
- if len(ids) > max_seq_len:
714
- ids = ids[:max_seq_len]
715
- else:
716
- ids = ids + [PAD_ID] * (max_seq_len - len(ids))
717
- first_word = text.split()[0] if text.split() else "ok"
718
- label_ids = tokenizer.encode(first_word)
719
- if len(label_ids) > max_seq_len:
720
- label_ids = label_ids[:max_seq_len]
721
- else:
722
- label_ids = label_ids + [PAD_ID] * (max_seq_len - len(label_ids))
723
- samples.append({
724
- "input_ids": torch.tensor(ids, dtype=torch.long),
725
- "label_ids": torch.tensor(label_ids, dtype=torch.long),
726
- "label_text": first_word,
727
- "type": "synthetic_fallback",
728
- "answer": "",
729
- "dataset": ds_name,
730
- })
731
-
732
- logger.info(f" Total samples: {len(samples)} ({len(V62_DATASETS)} datasets × até {N_PER_DATASET} samples)")
733
- return samples
734
-
735
-
736
- # ==============================================================================
737
- # 6. MCQ EVAL (igual V6)
738
- # ==============================================================================
739
- def make_mini_mcq(n_questions: int = 8):
740
- questions = []
741
- for i in range(n_questions):
742
- a, b = i + 1, (i * 2) % 10 + 1
743
- correct = a + b
744
- question = f"What is {a} + {b}?"
745
- options = [correct, correct + 1, correct - 1, correct + 2]
746
- rng = random.Random(i)
747
- rng.shuffle(options)
748
- correct_idx = options.index(correct)
749
- questions.append({
750
- "question": question,
751
- "options": [str(o) for o in options],
752
- "correct_idx": correct_idx,
753
- })
754
- return questions
755
-
756
-
757
- def evaluate_mcq(model, tokenizer, mcq):
758
- model.eval()
759
- correct = 0
760
- with torch.no_grad():
761
- for q in mcq:
762
- q_ids = tokenizer.encode(q["question"])
763
- if len(q_ids) < MAX_SEQ_LEN:
764
- q_ids = q_ids + [PAD_ID] * (MAX_SEQ_LEN - len(q_ids))
765
- else:
766
- q_ids = q_ids[:MAX_SEQ_LEN]
767
- x = torch.tensor(q_ids, dtype=torch.long).unsqueeze(0)
768
- try:
769
- y_hat, delta = model(x, use_hypothesis=True)
770
- logprobs = F.log_softmax((y_hat + delta)[0], dim=-1)
771
- option_scores = []
772
- for opt in q["options"]:
773
- opt_ids = tokenizer.encode(opt)
774
- if len(opt_ids) == 0:
775
- opt_ids = [PAD_ID]
776
- score = logprobs[opt_ids[0]].item()
777
- option_scores.append(score)
778
- pred = int(np.argmax(option_scores))
779
- if pred == q["correct_idx"]:
780
- correct += 1
781
- except (RuntimeError, IndexError):
782
- continue
783
- model.train()
784
- return correct / max(1, len(mcq))
785
-
786
-
787
- # ==============================================================================
788
- # 7. DELETE BINARIES
789
- # ==============================================================================
790
- def delete_training_binaries():
791
- """Deleta binários do treinamento."""
792
- deleted = []
793
- patterns = [
794
- "/home/z/my-project/BiGRU_T_version/model_final/*.bin",
795
- "/home/z/my-project/BiGRU_T_version/model_final/*.pt",
796
- "/home/z/my-project/BiGRU_T_version/model_final/*.pth",
797
- "/home/z/my-project/BiGRU_T_version/model_final/*.safetensors",
798
- "/home/z/my-project/BiGRU_T_version/**/*.bin",
799
- "/home/z/my-project/BiGRU_T_version/**/*.pt",
800
- ]
801
- for pattern in patterns:
802
- for filepath in glob.glob(pattern, recursive=True):
803
- try:
804
- os.remove(filepath)
805
- deleted.append(filepath)
806
- logger.info(f" [BIN DELETE] {filepath}")
807
- except OSError:
808
- pass
809
- return deleted
810
-
811
-
812
- # ==============================================================================
813
- # 8. MAIN
814
- # ==============================================================================
815
- def main():
816
- logger.info("=" * 72)
817
- logger.info("TRAIN V6.2 — BiGRU_T_version")
818
- logger.info("PGVector-like lookup + EWC math upgrade + 5 datasets × 10 samples")
819
- logger.info("=" * 72)
820
-
821
- logger.info(f"Xeon V6: cores={XEON_STATUS['physical_cores']}, "
822
- f"AMX={XEON_STATUS['amx']['supported']}, "
823
- f"IPEX={XEON_STATUS['ipex_available']}")
824
- logger.info(f"FP16 benchmark: best_tflops={FP16_BENCH.get('best_tflops', 'N/A')}")
825
-
826
- # Tokenizer
827
- tokenizer_path = "/home/z/my-project/BiGRU_T_version/model_final/tokenizer/tokenizer.json"
828
- if os.path.exists(tokenizer_path):
829
- try:
830
- tokenizer = BBPETokenizer.load(tokenizer_path)
831
- logger.info(f"Tokenizer loaded (vocab={tokenizer.vocab_size})")
832
- except Exception as e:
833
- logger.warning(f"Failed to load tokenizer ({e}), fitting inline")
834
- tokenizer = BBPETokenizer(vocab_size=256)
835
- tokenizer.fit_on_texts(["hello world", "machine learning"])
836
- else:
837
- tokenizer = BBPETokenizer(vocab_size=256)
838
- tokenizer.fit_on_texts(["hello world", "machine learning"])
839
- logger.info(f"Tokenizer fitted inline (vocab={tokenizer.vocab_size})")
840
-
841
- # Build model
842
- logger.info("\n[1] Building UnifiedModel...")
843
- cfg = UnifiedModelConfig(
844
- vocab_size=tokenizer.vocab_size,
845
- d_model=128,
846
- max_modules=8,
847
- num_layers_hyp=NUM_LAYERS_HYP,
848
- max_seq_len=MAX_SEQ_LEN,
849
- )
850
- model = UnifiedModel(cfg)
851
- n_params = sum(p.numel() for p in model.parameters())
852
- logger.info(f" Model params: {n_params/1e6:.2f}M")
853
-
854
- # V6.1: REPLACE model.orq_cell with V6 Kohonen 4D SOM
855
- if USE_KOHONEN_ORQ:
856
- logger.info("\n[2] Replacing OrqCell with V6.1 Kohonen 4D SOM (PGVector-ready)...")
857
- orq_v6 = OrqCellV6(Kohonen4DConfig(
858
- d_cache=cfg.d_cache,
859
- d_model_in=cfg.output_dim_u8cell,
860
- nhead=cfg.nhead_orq,
861
- d_ff=cfg.d_ff_orq,
862
- total_steps=100,
863
- pgvector_complete_thr=PGV_COMPLETE_THR,
864
- pgvector_missing_thr=PGV_MISSING_THR,
865
- ))
866
- device = next(model.parameters()).device
867
- orq_v6 = orq_v6.to(device)
868
- old_orq = model.orq_cell
869
- model.orq_cell = orq_v6
870
- del old_orq
871
- logger.info(f" OrqCell V6.1 installed: "
872
- f"n_neurons={orq_v6.cfg.n_neurons}, "
873
- f"grid={orq_v6.cfg.grid_shape}, "
874
- f"PGV thresholds: complete≥{PGV_COMPLETE_THR}, missing<{PGV_MISSING_THR}")
875
-
876
- # V6 MTP head
877
- mtp_head = None
878
- if USE_MTP:
879
- mtp_head = MTPHead(
880
- hidden_size=cfg.d_cache,
881
- vocab_size=tokenizer.vocab_size,
882
- K=MTP_K,
883
- )
884
- mtp_head = mtp_head.to(device)
885
- logger.info(f" MTP head initialized: K={MTP_K}")
886
-
887
- # V6 Data augmenter
888
- augmenter = DataAugmenter(AugmentationConfig(
889
- vocab_size=tokenizer.vocab_size,
890
- p_token_dropout=0.05,
891
- p_cutout=0.03,
892
- ))
893
-
894
- # V6 Tool system (smoke test)
895
- cache = ToolCache(max_size=128)
896
- mutator = ToolMutator(mutation_rate=0.1, max_variants=2)
897
- mutator.register("encoder_layer", nn.Linear(cfg.d_model, cfg.d_model))
898
-
899
- # V6.1 EWC — inicializado após epoch 1
900
- ewc = None
901
-
902
- # Optimizer
903
- optimizer = torch.optim.AdamW(
904
- list(model.parameters()) + list(mtp_head.parameters() if mtp_head is not None else []),
905
- lr=3e-4, weight_decay=0.01,
906
- )
907
-
908
- # TripletPrototypeLoss
909
- triplet_loss = TripletPrototypeLoss(margin=0.3, lambda_triplet=TRIPLET_WEIGHT)
910
- prototypes = torch.zeros(cfg.max_modules, cfg.d_cache, requires_grad=False)
911
-
912
- # MetaConfigurator
913
- meta_cfg = MetaConfigurator(model)
914
-
915
- # Monitor V6.1
916
- monitor = MetricsMonitorV62()
917
-
918
- # =====================================================================
919
- # LOAD 5 DATASETS × 10 SAMPLES
920
- # =====================================================================
921
- logger.info("\n[3] Loading 5 datasets × 10 samples (streaming + synthetic fallback)...")
922
- hf_token = os.environ.get("HF_TOKEN")
923
- samples = load_v62_samples(tokenizer, MAX_SEQ_LEN, hf_token=hf_token)
924
- n_total = len(samples)
925
- n_train = int(0.8 * n_total)
926
- train_samples = samples[:n_train]
927
- val_samples = samples[n_train:]
928
- logger.info(f" Train: {len(train_samples)} | Val: {len(val_samples)}")
929
- # Report datasets distribution
930
- ds_dist: Dict[str, int] = {}
931
- for s in samples:
932
- ds_dist[s["dataset"]] = ds_dist.get(s["dataset"], 0) + 1
933
- logger.info(f" Dataset distribution: {ds_dist}")
934
-
935
- # MCQ eval
936
- mcq = make_mini_mcq(n_questions=8)
937
-
938
- # =====================================================================
939
- # TRAIN LOOP
940
- # =====================================================================
941
- logger.info("\n[4] Starting V6.1 training loop...")
942
- global_step = 0
943
- total_steps = EPOCHS * len(train_samples) // BATCH_SIZE
944
- if USE_KOHONEN_ORQ:
945
- model.orq_cell.cfg.total_steps = max(1, total_steps)
946
-
947
- for epoch in range(1, EPOCHS + 1):
948
- model.train()
949
- if USE_MTP:
950
- mtp_head.train()
951
- augmenter.train()
952
-
953
- epoch_losses = []
954
- epoch_kohonen_auxs = []
955
- epoch_pgv_statuses = []
956
-
957
- for i in range(0, len(train_samples), BATCH_SIZE):
958
- batch = train_samples[i:i + BATCH_SIZE]
959
- if len(batch) < 2:
960
- continue
961
- global_step += 1
962
- t0 = time.perf_counter()
963
-
964
- x = torch.stack([s["input_ids"] for s in batch])
965
- label = torch.stack([s["label_ids"] for s in batch])
966
- actual_bs = len(batch)
967
- n_tokens = actual_bs * MAX_SEQ_LEN
968
- batch_ds = batch[0]["dataset"] # track dataset da 1a sample do batch
969
-
970
- optimizer.zero_grad()
971
-
972
- # V6: Data augmentation on input
973
- x_aug = augmenter(x)
974
-
975
- # Forward com return_aux
976
- try:
977
- y_hat, delta, aux = model(
978
- x_aug,
979
- return_aux=True,
980
- use_hypothesis=True,
981
- stop_grad_hyp=True,
982
- )
983
- except RuntimeError as e:
984
- logger.warning(f" step {global_step}: forward failed: {e}")
985
- aggressive_cleanup()
986
- continue
987
-
988
- # V6.1: Update OrqCell SOM step
989
- if USE_KOHONEN_ORQ:
990
- model.orq_cell.update_step(global_step)
991
-
992
- # ─────────────────────────────────────────────────────────────
993
- # V6.1 — PGVector-like lookup (treino level)
994
- # O usuário pediu: "os níveis de treinamento e o de hipótese
995
- # precisam verificar se o mapa já possui aquela informação"
996
- #
997
- # IMPORTANTE: a leitura (pgvector_lookup) é read-only e pode
998
- # ser feita após forward. MAS a escrita (fill_missing_neurons_random)
999
- # modifica neuron_cache in-place — deve ser DEFERIDA até após
1000
- # backward para não quebrar o autograd graph.
1001
- # ─────────────────────────────────────────────────────────────
1002
- pgv_metrics = None
1003
- pgv_needs_fill = False
1004
- if USE_KOHONEN_ORQ:
1005
- try:
1006
- with torch.no_grad():
1007
- # H_para_lookup: (B, 1, d_cache) — single "module" para lookup
1008
- H_for_lookup = aux["o"].unsqueeze(1)
1009
- pgv_result = model.orq_cell.pgvector_lookup(H_for_lookup)
1010
- pgv_metrics = pgv_result
1011
- # Se MISSING, marca para preenchimento DEPOIS do backward
1012
- # (user requirement: "se não houver atribuir valores
1013
- # aleatórios aos parâmetros faltantes")
1014
- if pgv_result["status"] == "MISSING":
1015
- pgv_needs_fill = True
1016
- except (RuntimeError, ValueError, KeyError) as e:
1017
- pass
1018
-
1019
- # ─────────────────────────────────────────────────────────────
1020
- # V6.2 — PGVector lookup no nível de HIPÓTESE
1021
- # User requirement: "ative pgvector_lookup no nível de hipótese
1022
- # (hyp_t.py) para que ele também consulte o mapa antes de
1023
- # aplicar punição"
1024
- #
1025
- # Antes de aplicar a punição (correção delta), a HypT consulta
1026
- # o mapa SOM. Se status == COMPLETE, a punição é DESATIVADA —
1027
- # o modelo confia em y_hat. Caso contrário, aplica delta.
1028
- # ─────────────────────────────────────────────────────────────
1029
- target = label[:, 0]
1030
-
1031
- # Calcula loss_main em y_hat (sem delta) para decidir punição
1032
- with torch.no_grad():
1033
- loss_main_check = F.cross_entropy(y_hat, target).item()
1034
- # τ do MetaConfigurator — se loss_main > τ, punição é pedida
1035
- current_tau = float(meta_cfg.tau) if hasattr(meta_cfg, 'tau') else 1.0
1036
- punishment_requested = bool(loss_main_check > current_tau)
1037
-
1038
- # Hyp PGVector lookup (consulta o mapa antes de aplicar delta)
1039
- hyp_pgv_metrics = None
1040
- apply_punishment = True # default: aplica delta
1041
- try:
1042
- hyp_pgv_result = model.hyp_T.pgvector_lookup_before_punishment(
1043
- orq_cell=model.orq_cell if USE_KOHONEN_ORQ else None,
1044
- o=aux["o"].detach(),
1045
- punishment_requested=punishment_requested,
1046
- )
1047
- apply_punishment = hyp_pgv_result["apply_punishment"]
1048
- # Anexa stats acumuladas do HypT
1049
- hyp_pgv_result["hyp_stats"] = model.hyp_T.get_punishment_stats()
1050
- hyp_pgv_metrics = hyp_pgv_result
1051
- except (RuntimeError, AttributeError, TypeError) as e:
1052
- # Fallback: aplica delta (preserva comportamento V6.1)
1053
- pass
1054
-
1055
- # Loss LM — aplica delta apenas se apply_punishment=True
1056
- if apply_punishment:
1057
- y_final = y_hat + delta
1058
- else:
1059
- # V6.2: COMPLETE skip — usa apenas y_hat (SOM tem a info)
1060
- y_final = y_hat
1061
- loss_lm = F.cross_entropy(y_final, target)
1062
-
1063
- # Loss de entropia do module_selector
1064
- loss_ent = model.lambda_ent_value * aux["entropy_reg"]
1065
-
1066
- # TripletPrototypeLoss
1067
- embeddings_for_triplet = aux["o"]
1068
- alpha_aux = aux["alpha"]
1069
- labels_for_triplet = torch.full(
1070
- (actual_bs,), int(alpha_aux.argmax()), dtype=torch.long
1071
- )
1072
- try:
1073
- loss_triplet = triplet_loss(
1074
- embeddings_for_triplet, labels_for_triplet, prototypes
1075
- )
1076
- with torch.no_grad():
1077
- emb_for_ema = embeddings_for_triplet.detach()
1078
- for i_ in range(actual_bs):
1079
- lbl = labels_for_triplet[i_].item()
1080
- if 0 <= lbl < prototypes.size(0):
1081
- prototypes[lbl] = (
1082
- (1 - TRIPLET_EMA_ALPHA) * prototypes[lbl]
1083
- + TRIPLET_EMA_ALPHA * emb_for_ema[i_]
1084
- )
1085
- except (RuntimeError, ValueError):
1086
- loss_triplet = torch.tensor(0.0, device=y_hat.device)
1087
-
1088
- # V6 MTP loss
1089
- mtp_metrics = None
1090
- mtp_loss_val = torch.tensor(0.0, device=y_hat.device)
1091
- if USE_MTP and mtp_head is not None:
1092
- try:
1093
- h_for_mtp = aux["o"].unsqueeze(1).expand(-1, MTP_K, -1)
1094
- mtp_logits, mtp_alphas = mtp_head(h_for_mtp)
1095
- mtp_targets = label[:, :MTP_K]
1096
- if mtp_targets.size(1) >= 1:
1097
- mtp_loss_val, mtp_metrics = mtp_loss(
1098
- mtp_logits, mtp_targets, mtp_alphas,
1099
- entropy_beta=MTP_ENTROPY_BETA,
1100
- )
1101
- except (RuntimeError, ValueError) as e:
1102
- logger.warning(f" MTP loss failed: {e}")
1103
-
1104
- # V6.1 EWC penalty (after epoch 1, with skip mask)
1105
- ewc_metrics = None
1106
- ewc_pen = torch.tensor(0.0, device=y_hat.device)
1107
- if USE_EWC and ewc is not None and ewc.n_tasks > 0:
1108
- try:
1109
- # BMU indices from PGVector lookup
1110
- bmu_idx = torch.full(
1111
- (actual_bs,),
1112
- pgv_metrics.get("best_neuron_idx", 0) if pgv_metrics else 0,
1113
- dtype=torch.long, device=y_hat.device,
1114
- )
1115
- ewc_pen, ewc_metrics = ewc.compute_penalty(
1116
- bmu_indices=bmu_idx,
1117
- eval_mode=not model.training,
1118
- )
1119
- except (RuntimeError, AttributeError) as e:
1120
- logger.warning(f" EWC penalty failed: {e}")
1121
-
1122
- # Loss total
1123
- loss = (loss_lm + loss_ent + TRIPLET_WEIGHT * loss_triplet
1124
- + MTP_LOSS_WEIGHT * mtp_loss_val + ewc_pen)
1125
-
1126
- # Backward
1127
- loss.backward()
1128
- grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
1129
- optimizer.step()
1130
-
1131
- # V6.1 SOM update APÓS backward
1132
- som_update_metrics = None
1133
- if USE_KOHONEN_ORQ:
1134
- try:
1135
- som_update_metrics = model.orq_cell.update_map_after_backward()
1136
- except (RuntimeError, AttributeError) as e:
1137
- pass
1138
-
1139
- # V6.1 — PGVector fill (após backward + SOM update, safe p/ autograd)
1140
- # Se o step anterior identificou MISSING, preenche neurônios
1141
- # não-filled com valores aleatórios controlados.
1142
- if pgv_needs_fill and USE_KOHONEN_ORQ:
1143
- try:
1144
- n_filled_now = model.orq_cell.fill_missing_neurons_random()
1145
- if n_filled_now > 0:
1146
- logger.info(f" [PGV] MISSING → preenchidos {n_filled_now} neurônios aleatoriamente")
1147
- except (RuntimeError, AttributeError) as e:
1148
- pass
1149
-
1150
- # V6 MetaConfigurator
1151
- if global_step % META_INTERVAL == 0:
1152
- try:
1153
- meta_result = meta_cfg.step(loss.detach())
1154
- monitor.record_meta(meta_result)
1155
- except (RuntimeError, AttributeError):
1156
- pass
1157
-
1158
- # V6.1 Capture Kohonen aux
1159
- kohonen_aux = None
1160
- if USE_KOHONEN_ORQ:
1161
- try:
1162
- with torch.no_grad():
1163
- H_dummy = embeddings_for_triplet.unsqueeze(1)
1164
- _, kohonen_aux = model.orq_cell(
1165
- H_dummy, step=global_step, return_aux=True
1166
- )
1167
- if som_update_metrics and kohonen_aux:
1168
- kohonen_aux["update_magnitude"] = som_update_metrics.get("update_magnitude", 0)
1169
- kohonen_aux["bmu_drift"] = som_update_metrics.get("bmu_drift", 0)
1170
- kohonen_aux["n_filled"] = som_update_metrics.get("n_filled",
1171
- kohonen_aux.get("n_filled", 0))
1172
- kohonen_aux["filled_ratio"] = (
1173
- kohonen_aux["n_filled"] / model.orq_cell.cfg.n_neurons
1174
- )
1175
- except (RuntimeError, ValueError, KeyError) as e:
1176
- pass
1177
-
1178
- loss_value = float(loss.item())
1179
- grad_norm_value = float(grad_norm)
1180
-
1181
- del loss, y_hat, delta, aux, y_final, loss_lm, loss_ent
1182
- del loss_triplet, mtp_loss_val, ewc_pen
1183
- if 'mtp_logits' in locals():
1184
- del mtp_logits
1185
- if 'mtp_alphas' in locals():
1186
- del mtp_alphas
1187
- if 'embeddings_for_triplet' in locals():
1188
- del embeddings_for_triplet
1189
- if 'h_for_mtp' in locals():
1190
- del h_for_mtp
1191
- if global_step % CLEANUP_INTERVAL == 0:
1192
- aggressive_cleanup()
1193
-
1194
- elapsed = time.perf_counter() - t0
1195
- flops_est = 2 * n_params * n_tokens
1196
-
1197
- monitor.record_step(
1198
- global_step, loss_value,
1199
- n_tokens, elapsed, grad_norm_value,
1200
- flops_estimate=flops_est,
1201
- kohonen_aux=kohonen_aux,
1202
- mtp_metrics=mtp_metrics,
1203
- ewc_metrics=ewc_metrics,
1204
- pgv_metrics=pgv_metrics,
1205
- hyp_pgv_metrics=hyp_pgv_metrics,
1206
- dataset_name=batch_ds,
1207
- )
1208
-
1209
- epoch_losses.append(loss_value)
1210
- if kohonen_aux:
1211
- epoch_kohonen_auxs.append(kohonen_aux)
1212
- if pgv_metrics:
1213
- epoch_pgv_statuses.append(pgv_metrics["status"])
1214
-
1215
- # Fim do epoch — Val
1216
- model.eval()
1217
- if USE_MTP:
1218
- mtp_head.eval()
1219
- val_losses = []
1220
- mtp_val_losses = []
1221
-
1222
- with torch.no_grad():
1223
- for sample in val_samples:
1224
- x = sample["input_ids"].unsqueeze(0)
1225
- label = sample["label_ids"]
1226
- try:
1227
- y_hat, delta = model(x, use_hypothesis=True)
1228
- target = label[0:1]
1229
- vloss = F.cross_entropy(y_hat + delta, target)
1230
- val_losses.append(float(vloss.item()))
1231
-
1232
- # V6.1 MTP ativo em val
1233
- if USE_MTP and mtp_head is not None:
1234
- try:
1235
- y_hat2, delta2, aux2 = model(x, return_aux=True, use_hypothesis=True)
1236
- o = aux2["o"].unsqueeze(1).expand(-1, MTP_K, -1)
1237
- mtp_logits, mtp_alphas = mtp_head(o)
1238
- mtp_targets = label[:MTP_K].unsqueeze(0)
1239
- mtp_loss_val, _ = mtp_loss(
1240
- mtp_logits, mtp_targets, mtp_alphas,
1241
- entropy_beta=MTP_ENTROPY_BETA,
1242
- )
1243
- mtp_val_losses.append(float(mtp_loss_val.item()))
1244
- del y_hat2, delta2, aux2, o, mtp_logits, mtp_alphas
1245
- except (RuntimeError, ValueError):
1246
- pass
1247
- del y_hat, delta
1248
- except RuntimeError:
1249
- continue
1250
-
1251
- val_loss = sum(val_losses) / max(1, len(val_losses))
1252
- train_avg = sum(epoch_losses) / max(1, len(epoch_losses))
1253
- mtp_val_avg = sum(mtp_val_losses) / max(1, len(mtp_val_losses)) if mtp_val_losses else None
1254
- eval_acc = evaluate_mcq(model, tokenizer, mcq)
1255
- monitor.record_epoch(epoch, val_loss, train_avg, eval_acc, mtp_val_avg)
1256
-
1257
- # Log PGVector status distribution do epoch
1258
- if epoch_pgv_statuses:
1259
- from collections import Counter
1260
- status_counts = Counter(epoch_pgv_statuses)
1261
- logger.info(f" [PGV epoch {epoch}] status distribution: {dict(status_counts)}")
1262
-
1263
- # V6.1 EWC: Após epoch 1, computar Fisher (com skip_som_filled_neurons=True)
1264
- if USE_EWC and ewc is None and epoch == 1:
1265
- logger.info(f"\n [EWC V6.1] Computing Fisher after epoch {epoch}...")
1266
- logger.info(f" [EWC V6.1] skip_som_filled_neurons=True — neurônios filled no SOM serão MASCARADOS")
1267
- try:
1268
- ewc = EWCV6(model, EWCConfigV6(
1269
- lambda_ewc=EWC_LAMBDA,
1270
- lambda_topo=EWC_LAMBDA_TOPO,
1271
- bmu_reinforcement_gamma=2.0,
1272
- skip_som_filled_neurons=True, # V6.1 key feature
1273
- ))
1274
- if USE_KOHONEN_ORQ:
1275
- ewc.orq_cells["orq_cell"] = model.orq_cell
1276
-
1277
- # Snapshot do SOM antes do Fisher
1278
- n_filled_before = int(model.orq_cell.neuron_filled_mask.sum().item())
1279
- logger.info(f" [EWC V6.1] SOM filled neurons antes do Fisher: {n_filled_before}/8")
1280
-
1281
- def data_iter():
1282
- for sample in train_samples[:16]:
1283
- x = sample["input_ids"].unsqueeze(0)
1284
- y = sample["label_ids"][:1]
1285
- yield (x, y)
1286
- ewc.compute_fisher(data_iter(), n_samples=16, bmu_indices=list(range(8)))
1287
- logger.info(f" [EWC V6.1] Fisher computed: classic={len(ewc.fisher)}, "
1288
- f"neuron={len(ewc.neuron_fisher)}, "
1289
- f"topo={len(ewc.neuron_grid_dist)}")
1290
- status = ewc.get_status()
1291
- logger.info(f" [EWC V6.1] n_active_ewc_neurons={status['n_active_ewc_neurons']} "
1292
- f"(EWC atua nestes) | n_masked_som_neurons={status['n_masked_som_neurons']} "
1293
- f"(SOM já cobre — não duplica)")
1294
- except (RuntimeError, AttributeError) as e:
1295
- logger.warning(f" [EWC V6.1] Fisher computation failed: {e}")
1296
- ewc = None
1297
-
1298
- aggressive_cleanup()
1299
-
1300
- # =====================================================================
1301
- # FINAL REPORT
1302
- # =====================================================================
1303
- logger.info("\n[5] Generating V6.1 report...")
1304
- summary = monitor.summary()
1305
-
1306
- report = {
1307
- "version": "V6.2",
1308
- "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
1309
- "config": {
1310
- "datasets": V62_DATASETS,
1311
- "n_per_dataset": N_PER_DATASET,
1312
- "epochs": EPOCHS, "batch_size": BATCH_SIZE,
1313
- "max_seq_len": MAX_SEQ_LEN, "num_layers_hyp": NUM_LAYERS_HYP,
1314
- "use_mtp": USE_MTP, "use_ewc": USE_EWC, "use_kohonen_orq": USE_KOHONEN_ORQ,
1315
- "mtp_K": MTP_K, "mtp_entropy_beta": MTP_ENTROPY_BETA,
1316
- "ewc_lambda": EWC_LAMBDA, "ewc_lambda_topo": EWC_LAMBDA_TOPO,
1317
- "pgv_complete_thr": PGV_COMPLETE_THR,
1318
- "pgv_missing_thr": PGV_MISSING_THR,
1319
- },
1320
- "v6_2_improvements": {
1321
- "hypothesis_level_pgvector_lookup": {
1322
- "method": "HypT.pgvector_lookup_before_punishment() em hyp_t.py",
1323
- "rationale": "Antes de aplicar a punição (correção delta), a HypT "
1324
- "consulta o mapa SOM. Se status == COMPLETE, a punição "
1325
- "é DESATIVADA — o modelo confia em y_hat. Caso "
1326
- "contrário, a punição é aplicada.",
1327
- "statuses": ["COMPLETE (skip)", "INCOMPLETE (apply)", "MISSING (apply)",
1328
- "NO_PUNISHMENT (loss ≤ τ)"],
1329
- "user_requirement": "ative pgvector_lookup no nível de hipótese (hyp_t.py) "
1330
- "para que ele também consulte o mapa antes de aplicar punição",
1331
- },
1332
- "batch_size_16": True,
1333
- "5_datasets_x_50_samples": True,
1334
- "datasets_library_installed": True,
1335
- },
1336
- "v6_1_features_preserved": {
1337
- "pgvector_like_lookup_orq_cell": {
1338
- "method": "pgvector_lookup() no OrqCell",
1339
- "statuses": ["COMPLETE", "INCOMPLETE", "MISSING"],
1340
- "complete_threshold": PGV_COMPLETE_THR,
1341
- "missing_threshold": PGV_MISSING_THR,
1342
- "neuron_filled_mask": True,
1343
- "auto_fill_missing": True,
1344
- },
1345
- "ewc_math_upgrade": {
1346
- "skip_som_filled_neurons": True,
1347
- "rationale": "EWC não reutiliza pesos já presentes no SOM — "
1348
- "evita dupla penalização (BMU + Fisher)",
1349
- "fisher_neuron_mask": True,
1350
- "n_masked_som_neurons_final": summary.get("ewc_evolution", {}).get(
1351
- "n_masked_som_neurons", [0])[-1] if summary.get("ewc_evolution", {}).get(
1352
- "n_masked_som_neurons") else 0,
1353
- },
1354
- "training_level_pgvector": True,
1355
- },
1356
- "v6_features_preserved": {
1357
- "kohonen_4d_som": True,
1358
- "ewc_per_neuron": True,
1359
- "mtp_active_in_val": True,
1360
- "mtp_entropy_regularizer": True,
1361
- "window_context_2m": True,
1362
- "dynamic_rep_penalty": True,
1363
- "data_augmentation": True,
1364
- "rlhf_mapping": True,
1365
- "thinking_system": True,
1366
- "tool_system": ["mutation", "cache", "agent"],
1367
- "langchain_compat": True,
1368
- "optimized_pipeline": True,
1369
- "xeon_v6_always_active": True,
1370
- "libvirt_amx_vm_activation": True,
1371
- },
1372
- "xeon_status": XEON_STATUS,
1373
- "fp16_benchmark": FP16_BENCH,
1374
- "dataset_distribution": summary.get("dataset_distribution", {}),
1375
- "result": {
1376
- **summary,
1377
- "n_params_M": n_params / 1e6,
1378
- },
1379
- }
1380
-
1381
- with open(REPORT_PATH, "w") as f:
1382
- json.dump(report, f, indent=2, default=str)
1383
- logger.info(f" Report saved: {REPORT_PATH}")
1384
-
1385
- # =====================================================================
1386
- # DELETE BINARIES
1387
- # =====================================================================
1388
- logger.info("\n[6] Deleting training binaries...")
1389
- deleted = delete_training_binaries()
1390
- logger.info(f" Deleted {len(deleted)} binary files")
1391
-
1392
- # =====================================================================
1393
- # SUMMARY
1394
- # =====================================================================
1395
- logger.info("\n" + "=" * 72)
1396
- logger.info("V6.2 TRAINING COMPLETE")
1397
- logger.info("=" * 72)
1398
- logger.info(f" Total samples: {len(samples)} (5 datasets × {N_PER_DATASET})")
1399
- logger.info(f" Total steps: {summary['n_steps']}, Epochs: {summary['n_epochs']}")
1400
- if summary['final_train_loss'] is not None:
1401
- logger.info(f" Final train_loss: {summary['final_train_loss']:.4f}")
1402
- if summary['final_val_loss'] is not None:
1403
- logger.info(f" Final val_loss: {summary['final_val_loss']:.4f}")
1404
- if summary['final_perplexity'] is not None:
1405
- logger.info(f" Perplexity: {summary['final_perplexity']:.2f}")
1406
- if summary['final_eval_benchmark'] is not None:
1407
- logger.info(f" Eval acc: {summary['final_eval_benchmark']:.2%}")
1408
- if summary['final_rss_mb'] is not None:
1409
- logger.info(f" RSS: {summary['final_rss_mb']:.0f}MB ({summary['rss_trend']})")
1410
- logger.info(f" Alerts: {summary['n_alerts']}")
1411
-
1412
- # V6.1 PGVector summary
1413
- pgv_ev = summary.get("pgvector_evolution", {})
1414
- if pgv_ev.get("status_counts"):
1415
- logger.info(f" PGVector (treino level) status distribution:")
1416
- for s, c in pgv_ev["status_counts"].items():
1417
- logger.info(f" {s}: {c}")
1418
- logger.info(f" final coverage: {pgv_ev.get('final_coverage', 0):.3f}")
1419
- logger.info(f" final best_sim: {pgv_ev.get('final_best_sim', 0):.3f}")
1420
- logger.info(f" final n_filled: {pgv_ev.get('final_n_filled', 0)}/8")
1421
-
1422
- # V6.2 Hyp PGVector summary
1423
- hyp_pgv_ev = summary.get("hyp_pgvector_evolution", {})
1424
- if hyp_pgv_ev.get("status_counts"):
1425
- logger.info(f" Hyp PGVector (nível de hipótese) status distribution:")
1426
- for s, c in hyp_pgv_ev["status_counts"].items():
1427
- logger.info(f" {s}: {c}")
1428
- logger.info(f" n_complete_skip (punição evitada): "
1429
- f"{hyp_pgv_ev.get('final_n_complete_skip', 0)}")
1430
- logger.info(f" n_incomplete_apply (punição aplicada, info parcial): "
1431
- f"{hyp_pgv_ev.get('final_n_incomplete_apply', 0)}")
1432
- logger.info(f" n_missing_apply (punição aplicada, info ausente): "
1433
- f"{hyp_pgv_ev.get('final_n_missing_apply', 0)}")
1434
- logger.info(f" skip_rate (fração de punições evitadas pelo pgvector): "
1435
- f"{hyp_pgv_ev.get('final_skip_rate', 0):.2%}")
1436
- logger.info(f" final best_sim: {hyp_pgv_ev.get('final_best_sim', 0):.3f}")
1437
- logger.info(f" final coverage: {hyp_pgv_ev.get('final_coverage', 0):.3f}")
1438
-
1439
- # V6.1 EWC summary
1440
- ewc_ev = summary.get("ewc_evolution", {})
1441
- if ewc_ev.get("n_masked_som_neurons"):
1442
- logger.info(f" EWC V6.1 math upgrade:")
1443
- logger.info(f" n_masked_som_neurons (skipped): {ewc_ev['n_masked_som_neurons'][-1]}")
1444
- logger.info(f" final EWC penalty: {ewc_ev['total_penalty'][-1] if ewc_ev['total_penalty'] else 0:.4f}")
1445
-
1446
- # V6 Kohonen summary
1447
- kohonen_ev = summary.get("kohonen_evolution", {})
1448
- if kohonen_ev.get("sigma_t"):
1449
- logger.info(f" Kohonen SOM evolution:")
1450
- logger.info(f" sigma_t: {kohonen_ev['sigma_t'][0]:.3f} → {kohonen_ev['final_sigma_t']:.3f}")
1451
- logger.info(f" alpha_t: {kohonen_ev['alpha_t'][0]:.3f} → {kohonen_ev['final_alpha_t']:.3f}")
1452
- logger.info(f" final n_filled: {kohonen_ev.get('final_n_filled', 0)}/8 "
1453
- f"(filled_ratio={kohonen_ev.get('final_filled_ratio', 0):.2%})")
1454
-
1455
- logger.info("=" * 72)
1456
-
1457
- return report
1458
-
1459
-
1460
- if __name__ == "__main__":
1461
- main()