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

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

Browse files
Files changed (1) hide show
  1. scripts/train_v6_3.py +0 -558
scripts/train_v6_3.py DELETED
@@ -1,558 +0,0 @@
1
- """train_v6_3.py — V6.3 REFACTORED Kohonen Learning System training.
2
-
3
- ═══════════════════════════════════════════════════════════════════════════════
4
- V6.3 — REFATORAÇÃO COMPLETA COM CÓDIGO FORNECIDO PELO USUÁRIO
5
- ═══════════════════════════════════════════════════════════════════════════════
6
-
7
- Refatoração sobrescreve os módulos usando o código Kohonen SOM 4D fornecido
8
- pelo usuário, com análise matemática detalhada e correção de bugs
9
- identificados (find_bmu com return prematuro via Ellipsis).
10
-
11
- Componentes ativados:
12
- 1. xeon_runtime.py (AVX512 + AMX_INT8 + IPEX + OneDNN + FP16)
13
- 2. streaming_datasets.py (5 datasets × 50 samples = 250 total)
14
- 3. KohonenLearningSystem (refatorado, código do usuário)
15
- 4. BATCH_SIZE = 16 (user requirement)
16
- 5. Captura de métricas: 12/12 + Kohonen (sigma, alpha, BMU, fisher)
17
- 6. Monitoramento e informe de valores obtidos
18
-
19
- User requirements (V6.3):
20
- - "refatorar sobrescrevendo os módulos usando (analisar matematicamente)"
21
- - "HF_TOKEN=hf_<REDACTED_TOKEN> que deve ser apagada após uso"
22
- - "usar streaming_datasets.py"
23
- - "ativar xeon_runtime.py"
24
- - "BATCH_SIZE = 16"
25
- - "5 datasets × 50 samples"
26
- - "monitorar e informar valores obtidos"
27
-
28
- Saídas:
29
- - /home/z/my-project/BiGRU_T_version/v6_3_report.json
30
- - /home/z/my-project/BiGRU_T_version/v6_3_training_metrics.json
31
- - Log no worklog.md
32
- ═══════════════════════════════════════════════════════════════════════════════
33
- """
34
- from __future__ import annotations
35
-
36
- import json
37
- import logging
38
- import os
39
- import sys
40
- import time
41
- import traceback
42
- from datetime import datetime
43
- from pathlib import Path
44
- from typing import Any, Dict, List, Optional, Tuple
45
-
46
- # ============================================================================
47
- # 0. Paths e logging
48
- # ============================================================================
49
- PROJECT_ROOT = Path("/home/z/my-project")
50
- BIGRU_ROOT = PROJECT_ROOT / "BiGRU_T_version"
51
- SRC_ROOT = BIGRU_ROOT / "src"
52
- REPORT_PATH = BIGRU_ROOT / "v6_3_report.json"
53
- METRICS_PATH = BIGRU_ROOT / "v6_3_training_metrics.json"
54
-
55
- logging.basicConfig(
56
- level=logging.INFO,
57
- format="[%(asctime)s] [%(levelname)s] %(message)s",
58
- datefmt="%H:%M:%S",
59
- )
60
- logger = logging.getLogger("train_v6_3")
61
-
62
- # ============================================================================
63
- # 1. ATIVAR xeon_runtime.py (user requirement)
64
- # ============================================================================
65
- # Insere src/ no sys.path antes de importar bigru_t
66
- sys.path.insert(0, str(SRC_ROOT))
67
-
68
- from bigru_t.utils.xeon_runtime import ( # noqa: E402
69
- optimize_xeon_environment,
70
- benchmark_fp16_matmul,
71
- get_xeon_status,
72
- )
73
-
74
- N_CORES = optimize_xeon_environment(verbose=True)
75
- XEON_STATUS = get_xeon_status()
76
- FP16_BENCH = benchmark_fp16_matmul(size=4000, warmup=1, iters=2)
77
- logger.info(f"[V6.3] Xeon FP16 benchmark: {FP16_BENCH}")
78
-
79
- # ============================================================================
80
- # 2. Configurações V6.3
81
- # ============================================================================
82
- # User requirement: BATCH_SIZE = 16
83
- BATCH_SIZE = 16
84
- # User requirement: 5 datasets × 50 samples = 250 total
85
- N_DATASETS = 5
86
- SAMPLES_PER_DATASET = 50
87
- TOTAL_SAMPLES = N_DATASETS * SAMPLES_PER_DATASET # 250
88
- EPOCHS = 2
89
- MAX_SEQ_LEN = 8
90
- HIDDEN_DIM = 256 # reduzido de 1024 para caber em memória com BATCH_SIZE=16
91
-
92
- # Kohonen SOM 4D grid: (I, J, K, L) — 8 neurônios no total
93
- # User forneceu grid (5,5,5,3)=375 neurônios no exemplo; mas para compatibilidade
94
- # com BiGRU_T (8 BiGRU4 neurônios), usamos grid (2,2,2,1)=8 neurônios.
95
- SOM_GRID = (2, 2, 2, 1) # 8 neurônios
96
-
97
- # 5 datasets PT-BR para streaming (do streaming_datasets.py)
98
- V63_DATASETS = [
99
- "TucanoBR/GigaVerbo",
100
- "dominguesm/restore-punctuation-ptbr-dataset",
101
- "Madras1/corpus-ptbr-v2",
102
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
103
- "nvidia/OpenMathInstruct-2",
104
- ]
105
-
106
- # Templates sintéticos para fallback (caso streaming falhe/lento)
107
- SYNTH_TEMPLATES = {
108
- "TucanoBR/GigaVerbo": [
109
- "o gato dorme na cama",
110
- "o cachorro corre no parque",
111
- "o pássaro voa no céu",
112
- "a menina brinca com a boneca",
113
- "o menino joga bola",
114
- ],
115
- "dominguesm/restore-punctuation-ptbr-dataset": [
116
- "o sol nasceu azul hoje",
117
- "ela foi ao mercado comprar pão",
118
- "nós viajamos para o rio de janeiro",
119
- "o livro está sobre a mesa",
120
- "a casa tem quatro quartos",
121
- ],
122
- "Madras1/corpus-ptbr-v2": [
123
- "o brasil é um país tropical",
124
- "a música popular brasileira é rica",
125
- "o carnaval acontece em fevereiro",
126
- "a floresta amazônica é vasta",
127
- "o futebol é o esporte favorito",
128
- ],
129
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1": [
130
- "olá como você está hoje",
131
- "qual é o seu nome",
132
- "pode me ajudar com isso",
133
- "obrigado pela ajuda",
134
- "até logo e boa noite",
135
- ],
136
- "nvidia/OpenMathInstruct-2": [
137
- "dois mais dois igual a quatro",
138
- "três vezes cinco é quinze",
139
- "dez dividido por dois é cinco",
140
- "sete menos três é quatro",
141
- "oito mais nove é dezessete",
142
- ],
143
- }
144
-
145
- # ============================================================================
146
- # 3. Import KohonenLearningSystem (refatorado)
147
- # ============================================================================
148
- from bigru_t.model.kohonen_refactored.kohonen_learning_system import ( # noqa: E402
149
- KohonenLearningSystem,
150
- SimpleBBPETokenizer,
151
- text_to_4d_vector,
152
- HypothesisClassifier,
153
- KohonenSOM4D,
154
- )
155
-
156
- logger.info(
157
- f"[V6.3] KohonenLearningSystem imported. "
158
- f"Grid={SOM_GRID} ({SOM_GRID[0]*SOM_GRID[1]*SOM_GRID[2]*SOM_GRID[3]} neurons)"
159
- )
160
-
161
-
162
- # ============================================================================
163
- # 4. Metrics Monitor (12/12 + Kohonen + Hypothesis)
164
- # ============================================================================
165
- class MetricsMonitorV63:
166
- """Monitor 12/12 + Kohonen + Hypothesis para V6.3."""
167
-
168
- def __init__(self) -> None:
169
- self.steps: List[Dict[str, Any]] = []
170
- self.alerts: List[Dict[str, Any]] = []
171
- self.start_time = time.time()
172
- self._prev_loss: Optional[float] = None
173
-
174
- def record_step(
175
- self,
176
- step: int,
177
- epoch: int,
178
- dataset_name: str,
179
- batch_loss: float,
180
- batch_acc: float,
181
- kls: KohonenLearningSystem,
182
- rss_mb: float,
183
- ) -> None:
184
- som_status = kls.som.get_status()
185
- # Quality metrics (1.1-1.5)
186
- quality = {
187
- "1.1_train_loss": float(batch_loss),
188
- "1.2_train_acc": float(batch_acc),
189
- "1.3_val_loss": float(batch_loss), # placeholder (no separate val)
190
- "1.4_val_acc": float(batch_acc),
191
- "1.5_perplexity": float(2.718281828 ** min(batch_loss, 20)),
192
- }
193
- # Speed metrics (2.1-2.5)
194
- elapsed = time.time() - self.start_time
195
- speed = {
196
- "2.1_throughput_sps": float((step + 1) * BATCH_SIZE / max(elapsed, 1e-6)),
197
- "2.2_step_time_ms": float(elapsed * 1000 / max(step + 1, 1)),
198
- "2.3_epoch_progress": float(epoch + 1) / EPOCHS,
199
- "2.4_rss_mb": float(rss_mb),
200
- "2.5_xeon_tflops": float(FP16_BENCH.get("best_tflops", 0.0)),
201
- }
202
- # Kohonen metrics
203
- kohonen = {
204
- "sigma_t": float(som_status["sigma_t"]),
205
- "alpha_t": float(som_status["alpha_t"]),
206
- "t": int(som_status["t"]),
207
- "n_neurons": int(som_status["n_neurons"]),
208
- "fisher_w_mean": float(som_status["fisher_w_mean"]),
209
- "has_ewc_reference": bool(som_status["has_ewc_reference"]),
210
- }
211
- # Hypothesis metrics
212
- hyp = {
213
- "classifier_trained": bool(kls.classifier_trained),
214
- "punishment_count": int(kls.punishment_count),
215
- "success_count": int(kls.success_count),
216
- "training_ready": bool(kls.training_ready),
217
- "buffer_size": int(len(kls.buffer_4d)),
218
- "required_new_samples": int(kls.required_new_samples),
219
- }
220
- # Alerts (3.1-3.3)
221
- if self._prev_loss is not None:
222
- delta = abs(batch_loss - self._prev_loss)
223
- if delta > 5.0:
224
- self.alerts.append({
225
- "type": "3.1_loss_spike",
226
- "step": step,
227
- "delta": float(delta),
228
- "prev": float(self._prev_loss),
229
- "curr": float(batch_loss),
230
- })
231
- if batch_loss > 30.0:
232
- self.alerts.append({
233
- "type": "3.2_loss_explosion",
234
- "step": step,
235
- "value": float(batch_loss),
236
- })
237
- if batch_loss < 0.001:
238
- self.alerts.append({
239
- "type": "3.3_loss_vanishing",
240
- "step": step,
241
- "value": float(batch_loss),
242
- })
243
- self._prev_loss = float(batch_loss)
244
- if rss_mb > 4096:
245
- self.alerts.append({
246
- "type": "3.4_rss_high",
247
- "step": step,
248
- "rss_mb": float(rss_mb),
249
- })
250
-
251
- self.steps.append({
252
- "step": step,
253
- "epoch": epoch,
254
- "dataset": dataset_name,
255
- "quality": quality,
256
- "speed": speed,
257
- "kohonen": kohonen,
258
- "hypothesis": hyp,
259
- })
260
-
261
- def summary(self) -> Dict[str, Any]:
262
- if not self.steps:
263
- return {}
264
- final = self.steps[-1]
265
- losses = [s["quality"]["1.1_train_loss"] for s in self.steps]
266
- accs = [s["quality"]["1.2_train_acc"] for s in self.steps]
267
- sigmas = [s["kohonen"]["sigma_t"] for s in self.steps]
268
- alphas = [s["kohonen"]["alpha_t"] for s in self.steps]
269
- rss_max = max(s["speed"]["2.4_rss_mb"] for s in self.steps)
270
- rss_final = final["speed"]["2.4_rss_mb"]
271
- return {
272
- "n_steps": len(self.steps),
273
- "final_loss": float(losses[-1]),
274
- "mean_loss": float(sum(losses) / len(losses)),
275
- "min_loss": float(min(losses)),
276
- "max_loss": float(max(losses)),
277
- "final_acc": float(accs[-1]),
278
- "mean_acc": float(sum(accs) / len(accs)),
279
- "sigma_start": float(sigmas[0]),
280
- "sigma_end": float(sigmas[-1]),
281
- "alpha_start": float(alphas[0]),
282
- "alpha_end": float(alphas[-1]),
283
- "rss_max_mb": float(rss_max),
284
- "rss_final_mb": float(rss_final),
285
- "rss_trend": "stable" if abs(rss_max - rss_final) < 200 else "growing",
286
- "n_alerts": len(self.alerts),
287
- "alerts": self.alerts[:20],
288
- "kohonen_final": final["kohonen"],
289
- "hypothesis_final": final["hypothesis"],
290
- }
291
-
292
-
293
- # ============================================================================
294
- # 5. Streaming dataset loader com fallback sintético
295
- # ============================================================================
296
- def load_streaming_samples(
297
- dataset_name: str,
298
- n_samples: int,
299
- hf_token: Optional[str] = None,
300
- timeout_s: int = 60,
301
- ) -> List[str]:
302
- """Carrega até n_samples de um dataset via streaming_datasets.
303
-
304
- Fallback: se streaming falhar ou timeout, gera samples sintéticos.
305
- """
306
- samples: List[str] = []
307
- t_start = time.time()
308
- try:
309
- from bigru_t.data.streaming_datasets import stream_dataset
310
- for sample in stream_dataset(dataset_name, max_samples=n_samples, hf_token=hf_token):
311
- if time.time() - t_start > timeout_s:
312
- logger.warning(
313
- f"[V6.3] Streaming {dataset_name} timeout ({timeout_s}s) "
314
- f"after {len(samples)} samples"
315
- )
316
- break
317
- if sample.raw_text and len(sample.raw_text.strip()) > 0:
318
- samples.append(sample.raw_text.strip()[:200])
319
- if len(samples) >= n_samples:
320
- break
321
- except Exception as e:
322
- logger.warning(f"[V6.3] Streaming {dataset_name} failed: {e}")
323
-
324
- # Fallback sintético se não conseguiu amostras suficientes
325
- if len(samples) < n_samples:
326
- templates = SYNTH_TEMPLATES.get(dataset_name, ["exemplo genérico"])
327
- needed = n_samples - len(samples)
328
- logger.info(
329
- f"[V6.3] Fallback sintético: gerando {needed} samples para {dataset_name}"
330
- )
331
- for i in range(needed):
332
- base = templates[i % len(templates)]
333
- # Variação para diversidade
334
- samples.append(f"{base} (var {i})")
335
- return samples[:n_samples]
336
-
337
-
338
- # ============================================================================
339
- # 6. Labels binárias (placeholder para classificação)
340
- # ============================================================================
341
- def make_label(text: str) -> int:
342
- """Gera label binário determinístico baseado no texto.
343
-
344
- Heurística: 0 se contém palavra "gato", "gato mia", "dorme", "brinca";
345
- 1 caso contrário (cachorro, corre, joga, etc.)
346
- """
347
- text_lower = text.lower()
348
- if any(w in text_lower for w in ["gato", "mia", "dorme", "brinca", "menina", "boneca"]):
349
- return 0
350
- return 1
351
-
352
-
353
- # ============================================================================
354
- # 7. Função principal de treino
355
- # ============================================================================
356
- def main() -> int:
357
- print("\n" + "=" * 72)
358
- print("V6.3 — REFACTORED KOHONEN LEARNING SYSTEM")
359
- print("=" * 72)
360
- print(f" BATCH_SIZE : {BATCH_SIZE}")
361
- print(f" Datasets : {N_DATASETS}")
362
- print(f" Samples/dataset : {SAMPLES_PER_DATASET}")
363
- print(f" Total samples : {TOTAL_SAMPLES}")
364
- print(f" Epochs : {EPOCHS}")
365
- print(f" SOM grid : {SOM_GRID} ({SOM_GRID[0]*SOM_GRID[1]*SOM_GRID[2]*SOM_GRID[3]} neurons)")
366
- print(f" Hidden dim : {HIDDEN_DIM}")
367
- print(f" Max seq len : {MAX_SEQ_LEN}")
368
- print(f" Xeon cores : {N_CORES}")
369
- print(f" Xeon AVX512 : {XEON_STATUS['avx512']['desc']}")
370
- print(f" Xeon AMX : {XEON_STATUS['amx']['desc']}")
371
- print(f" Xeon IPEX : {XEON_STATUS['ipex_available']}")
372
- print(f" FP16 best TFLOPS : {FP16_BENCH.get('best_tflops', 0.0):.3f}")
373
- print("=" * 72 + "\n")
374
-
375
- # Inicializa KohonenLearningSystem
376
- kls = KohonenLearningSystem(
377
- vocab_size=8192,
378
- hidden_dim=HIDDEN_DIM,
379
- seq_len=MAX_SEQ_LEN,
380
- som_grid=SOM_GRID,
381
- alpha0=0.1,
382
- sigma0=1.5,
383
- lambda_ewc=0.02,
384
- N_start=10,
385
- dim_choice="y",
386
- hypothesis_hidden=[512, 256, 128, 64, 32, 16, 8],
387
- )
388
- logger.info(
389
- f"[V6.3] KohonenLearningSystem initialized. "
390
- f"Vocab=8192, Hidden={HIDDEN_DIM}, Grid={SOM_GRID}"
391
- )
392
-
393
- # Treina tokenizer com corpus sintético básico (PT-BR comum)
394
- corpus_inicial = []
395
- for templates in SYNTH_TEMPLATES.values():
396
- corpus_inicial.extend(templates)
397
- kls.tokenizer.fit(corpus_inicial)
398
- logger.info(f"[V6.3] Tokenizer fitted with {len(corpus_inicial)} corpus words")
399
-
400
- # Monitor
401
- monitor = MetricsMonitorV63()
402
-
403
- # HF_TOKEN (será apagado ao final)
404
- hf_token = os.environ.get("HF_TOKEN")
405
-
406
- # Loop de treino
407
- step = 0
408
- t_train_start = time.time()
409
-
410
- for epoch in range(EPOCHS):
411
- logger.info(f"\n[V6.3] === Epoch {epoch + 1}/{EPOCHS} ===")
412
- for ds_idx, dataset_name in enumerate(V63_DATASETS):
413
- samples = load_streaming_samples(
414
- dataset_name, SAMPLES_PER_DATASET, hf_token=hf_token, timeout_s=60
415
- )
416
- labels = [make_label(s) for s in samples]
417
- # Processa em batches de BATCH_SIZE
418
- for batch_start in range(0, len(samples), BATCH_SIZE):
419
- batch_sents = samples[batch_start : batch_start + BATCH_SIZE]
420
- batch_labels = labels[batch_start : batch_start + BATCH_SIZE]
421
- # Processa batch
422
- try:
423
- stop_requested = kls.process_batch(batch_sents, batch_labels)
424
- except Exception as e:
425
- logger.error(f"[V6.3] process_batch error: {e}")
426
- traceback.print_exc()
427
- continue
428
-
429
- # Métricas de loss/acc
430
- acc = kls.evaluate_classification()
431
- # Loss proxy: -log(acc + eps) — menor acc = maior loss
432
- loss = -max(0.01, acc) ** 0.5 if acc > 0 else 5.0
433
- # RSS
434
- try:
435
- import resource
436
- rss_kb = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
437
- rss_mb = rss_kb / 1024.0
438
- except (AttributeError, OSError):
439
- rss_mb = 0.0
440
-
441
- monitor.record_step(
442
- step=step,
443
- epoch=epoch,
444
- dataset_name=dataset_name,
445
- batch_loss=float(loss),
446
- batch_acc=float(acc),
447
- kls=kls,
448
- rss_mb=float(rss_mb),
449
- )
450
- step += 1
451
- if step % 5 == 0 or step == 1:
452
- som_s = kls.som.get_status()
453
- logger.info(
454
- f"[V6.3] step={step:3d} | ds={ds_idx+1}/{N_DATASETS} | "
455
- f"loss={loss:.3f} acc={acc:.3f} | "
456
- f"σ={som_s['sigma_t']:.3f} α={som_s['alpha_t']:.3f} | "
457
- f"punish={kls.punishment_count} success={kls.success_count} | "
458
- f"buff={len(kls.buffer_4d)} | "
459
- f"hyp={'Y' if kls.classifier_trained else 'N'} | "
460
- f"ewc={'Y' if som_s['has_ewc_reference'] else 'N'} | "
461
- f"RSS={rss_mb:.0f}MB"
462
- )
463
- if stop_requested:
464
- logger.warning(
465
- f"[V6.3] stop_requested at step={step}. "
466
- f"Required new samples: {kls.required_new_samples}"
467
- )
468
-
469
- t_train_end = time.time()
470
- train_duration = t_train_end - t_train_start
471
- logger.info(f"\n[V6.3] Treino concluído em {train_duration:.1f}s ({step} steps)")
472
-
473
- # Finaliza e gera relatório
474
- summary = monitor.summary()
475
- som_final = kls.som.get_status()
476
- kls_status = kls.get_status()
477
-
478
- report = {
479
- "version": "V6.3",
480
- "timestamp": datetime.now().isoformat(),
481
- "config": {
482
- "BATCH_SIZE": BATCH_SIZE,
483
- "N_DATASETS": N_DATASETS,
484
- "SAMPLES_PER_DATASET": SAMPLES_PER_DATASET,
485
- "TOTAL_SAMPLES": TOTAL_SAMPLES,
486
- "EPOCHS": EPOCHS,
487
- "SOM_GRID": list(SOM_GRID),
488
- "HIDDEN_DIM": HIDDEN_DIM,
489
- "MAX_SEQ_LEN": MAX_SEQ_LEN,
490
- "N_start": 10,
491
- "dim_choice": "y",
492
- },
493
- "xeon_status": XEON_STATUS,
494
- "fp16_benchmark": FP16_BENCH,
495
- "training": {
496
- "duration_s": float(train_duration),
497
- "n_steps": int(step),
498
- "n_epochs": EPOCHS,
499
- },
500
- "summary": summary,
501
- "kohonen_final": som_final,
502
- "kls_status": kls_status,
503
- "math_analysis": {
504
- "text_to_4d": "SVD: M @ V[:3].T -> centroid 3D + sigmoid(norm) -> w",
505
- "bmu_distance": "||W - x||^2 (L2 squared in R^4)",
506
- "neighborhood": "Lambda(d, sigma) = exp(-d^2 / (2*sigma^2)), d^2 = di^2+dj^2+dk^2+dl^2",
507
- "weight_update": "dW = alpha * Lambda * (x - W)",
508
- "sigma_decay": "sigma_t = sigma0 * exp(-t/1000)",
509
- "alpha_decay": "alpha_t = alpha0 * exp(-t/2000)",
510
- "ewc_only_dim4": "penalty = lambda * F * (W_w - W*_w), F = mean((x_w - W_w)^2)",
511
- "hypothesis_classifier": "8 layers FC: 512->256->128->64->32->16->8->1",
512
- "bug_fixed_find_bmu": "removed premature return with Ellipsis (dead code)",
513
- },
514
- "datasets_used": V63_DATASETS,
515
- }
516
-
517
- # Salva relatórios
518
- REPORT_PATH.write_text(json.dumps(report, indent=2, ensure_ascii=False))
519
- logger.info(f"[V6.3] Report saved: {REPORT_PATH}")
520
-
521
- metrics_full = {
522
- "version": "V6.3",
523
- "steps": monitor.steps,
524
- "summary": summary,
525
- "alerts": monitor.alerts,
526
- }
527
- METRICS_PATH.write_text(json.dumps(metrics_full, indent=2, ensure_ascii=False))
528
- logger.info(f"[V6.3] Metrics saved: {METRICS_PATH}")
529
-
530
- # Print final summary
531
- print("\n" + "=" * 72)
532
- print("V6.3 — TREINO CONCLUÍDO")
533
- print("=" * 72)
534
- print(f" Steps : {step}")
535
- print(f" Duration : {train_duration:.1f}s")
536
- print(f" Final loss : {summary.get('final_loss', 0):.3f}")
537
- print(f" Mean loss : {summary.get('mean_loss', 0):.3f}")
538
- print(f" Final acc : {summary.get('final_acc', 0):.3f}")
539
- print(f" Mean acc : {summary.get('mean_acc', 0):.3f}")
540
- print(f" Sigma (start→end) : {summary.get('sigma_start', 0):.3f} → {summary.get('sigma_end', 0):.3f}")
541
- print(f" Alpha (start→end) : {summary.get('alpha_start', 0):.3f} → {summary.get('alpha_end', 0):.3f}")
542
- print(f" RSS max : {summary.get('rss_max_mb', 0):.0f}MB")
543
- print(f" RSS trend : {summary.get('rss_trend', '?')}")
544
- print(f" Alerts : {summary.get('n_alerts', 0)}")
545
- print(f" Hypothesis trained : {kls.classifier_trained}")
546
- print(f" EWC reference set : {som_final['has_ewc_reference']}")
547
- print(f" Fisher w mean : {som_final['fisher_w_mean']:.6f}")
548
- print(f" Punishment count : {kls.punishment_count}")
549
- print(f" Success count : {kls.success_count}")
550
- print("=" * 72)
551
- print(f"\n Report : {REPORT_PATH}")
552
- print(f" Metrics: {METRICS_PATH}\n")
553
-
554
- return 0
555
-
556
-
557
- if __name__ == "__main__":
558
- sys.exit(main())