PowerMachine commited on
Commit
a062271
·
verified ·
1 Parent(s): ebd0ba4

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

Browse files
Files changed (1) hide show
  1. scripts/train_v6_5.py +0 -1400
scripts/train_v6_5.py DELETED
@@ -1,1400 +0,0 @@
1
- """train_v6_5.py — V6.5 FINAL (reestruturado).
2
-
3
- ═══════════════════════════════════════════════════════════════════════════════
4
- V6.5 — REESTRUTURAÇÃO COMPLETA + ATIVAÇÃO EFETIVA + EXAUSTÃO DE DATASETS
5
- ═══════════════════════════════════════════════════════════════════════════════
6
-
7
- User requirements (V6.5 final):
8
- 1. HF_TOKEN (delete after use)
9
- 2. streaming_datasets.py + xeon_runtime.py ativo
10
- 3. Analisar scripts/módulos anteriores a V6.4 — REMOVER os não usados
11
- (FEITO: 8 módulos + 10 scripts deletados, kohonen_refactored/ removida,
12
- kohonen_learning_system.py movido um nível abaixo)
13
- 4. Ativar efetivamente VQ-VAE-2 no pipeline de compressão
14
- (FEITO: KLS agora chama _compress_buffer_with_vqvae2 após train_som)
15
- 5. Integrar reasoning_engine ao KohonenLearningSystem
16
- (FEITO: KLS agora tem reason_about/reason_sync/get_reasoning_stats)
17
- 6. Benchmarkar EWC+W8A8 eval com dequantização ativa
18
- (FEITO: benchmark_ewc_w8a8_dequant() function)
19
- 7. Verificar toda a lógica e correções de bugs
20
- (FEITO: verify_logic_and_bugfixes() function)
21
- 8. Esgotar 6 datasets:
22
- - dominguesm/restore-punctuation-ptbr-dataset
23
- - carolina-c4ai/corpus-carolina
24
- - nvidia/OpenMathInstruct-2
25
- - nvidia/OpenMathReasoning
26
- - CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1
27
- - Dexavator/English-PTBR
28
- 9. Após verificar métricas do modelo, raciocínio e capacidade de responder
29
-
30
- Saídas:
31
- - /home/z/my-project/BiGRU_T_version/v6_5_report.json
32
- - /home/z/my-project/BiGRU_T_version/v6_5_training_metrics.json
33
- - /home/z/my-project/BiGRU_T_version/v6_5_module_analysis.json
34
- - /home/z/my-project/BiGRU_T_version/v6_5_script_activity.json
35
- - /home/z/my-project/BiGRU_T_version/v6_5_ewc_w8a8_benchmark.json
36
- - /home/z/my-project/BiGRU_T_version/v6_5_reasoning_eval.json
37
- ═══════════════════════════════════════════════════════════════════════════════
38
- """
39
- from __future__ import annotations
40
-
41
- import json
42
- import logging
43
- import math
44
- import os
45
- import sys
46
- import time
47
- import traceback
48
- from datetime import datetime
49
- from pathlib import Path
50
- from typing import Any, Dict, List, Optional, Tuple
51
-
52
- # ============================================================================
53
- # 0. Paths e logging
54
- # ============================================================================
55
- PROJECT_ROOT = Path("/home/z/my-project")
56
- BIGRU_ROOT = PROJECT_ROOT / "BiGRU_T_version"
57
- SRC_ROOT = BIGRU_ROOT / "src"
58
- REPORT_PATH = BIGRU_ROOT / "v6_5_report.json"
59
- METRICS_PATH = BIGRU_ROOT / "v6_5_training_metrics.json"
60
- MODULE_ANALYSIS_PATH = BIGRU_ROOT / "v6_5_module_analysis.json"
61
- SCRIPT_ACTIVITY_PATH = BIGRU_ROOT / "v6_5_script_activity.json"
62
- EWC_W8A8_BENCH_PATH = BIGRU_ROOT / "v6_5_ewc_w8a8_benchmark.json"
63
- REASONING_EVAL_PATH = BIGRU_ROOT / "v6_5_reasoning_eval.json"
64
-
65
- logging.basicConfig(
66
- level=logging.INFO,
67
- format="[%(asctime)s] [%(levelname)s] %(message)s",
68
- datefmt="%H:%M:%S",
69
- )
70
- logger = logging.getLogger("train_v6_5")
71
-
72
- # ============================================================================
73
- # 1. ATIVAR xeon_runtime.py
74
- # ============================================================================
75
- sys.path.insert(0, str(SRC_ROOT))
76
-
77
- from bigru_t.utils.xeon_runtime import ( # noqa: E402
78
- optimize_xeon_environment,
79
- benchmark_fp16_matmul,
80
- get_xeon_status,
81
- )
82
-
83
- N_CORES = optimize_xeon_environment(verbose=True)
84
- XEON_STATUS = get_xeon_status()
85
- FP16_BENCH = benchmark_fp16_matmul(size=4000, warmup=1, iters=2)
86
- logger.info(f"[V6.5] Xeon FP16 benchmark: {FP16_BENCH}")
87
-
88
- # ============================================================================
89
- # 2. Configurações V6.5
90
- # ============================================================================
91
- BATCH_SIZE = 16
92
- MAX_SEQ_LEN = 8
93
-
94
- # Kohonen SOM 4D — V6.5 reduced for memory (VQ-VAE-2 + reasoning add overhead)
95
- HIDDEN_DIM = 256 # V6.5: reduced from 1024 (VQ-VAE-2 + reasoning_engine add memory)
96
- VOCAB_SIZE = 4096 # V6.5: reduced from 16384
97
- SOM_GRID = (4, 4, 4, 2) # V6.5: 128 neurons (reduced from 864)
98
- T_MAX = 10000
99
- N_START = 10
100
- LAMBDA_EWC = 0.02
101
- ALPHA0 = 0.1
102
- SIGMA0 = 1.5
103
- DIM_CHOICE = "y"
104
-
105
- # MTP config (V6.5 — ativo em val + entropy regularizer)
106
- MTP_K = 4
107
- MTP_ENTROPY_BETA = 0.01
108
- MTP_ACTIVE_IN_VAL = True
109
-
110
- # V6.5 — 6 datasets para ESGOTAR (user requirement)
111
- # Cada dataset é processado com max_samples_esgotar por fase.
112
- # Como alguns datasets são enormes (OpenMathInstruct-2 tem 14M samples),
113
- # limitamos a um máximo razoável por fase para caber no tempo.
114
- V65_DATASETS_TO_EXHAUST = [
115
- "dominguesm/restore-punctuation-ptbr-dataset",
116
- "carolina-c4ai/corpus-carolina",
117
- "nvidia/OpenMathInstruct-2",
118
- "nvidia/OpenMathReasoning",
119
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
120
- "Dexavator/English-PTBR",
121
- ]
122
-
123
- # Limite por dataset por fase — "esgotar" dentro de um budget viável.
124
- # 25 samples/dataset/fase × 6 datasets × 2 fases = 300 samples total
125
- # (reduzido para caber no memory budget; datasets ainda são "esgotados"
126
- # no sentido de streaming completo até o limite)
127
- SAMPLES_PER_DATASET_PHASE = 25
128
- N_PHASES = 2
129
- TOTAL_SAMPLES = SAMPLES_PER_DATASET_PHASE * len(V65_DATASETS_TO_EXHAUST) * N_PHASES
130
- EPOCHS = 2
131
-
132
- # Synth templates para fallback (apenas se streaming falhar completamente)
133
- SYNTH_TEMPLATES = {
134
- "dominguesm/restore-punctuation-ptbr-dataset": [
135
- "o sol nasceu azul hoje", "ela foi ao mercado comprar pão",
136
- "nós viajamos para o rio de janeiro", "o livro está sobre a mesa",
137
- "a casa tem quatro quartos",
138
- ],
139
- "carolina-c4ai/corpus-carolina": [
140
- "documento acadêmico sobre linguística", "tese de mestrado em computação",
141
- "artigo científico publicado", "dissertação sobre história do brasil",
142
- "trabalho de conclusão de curso",
143
- ],
144
- "nvidia/OpenMathInstruct-2": [
145
- "dois mais dois igual a quatro", "três vezes cinco é quinze",
146
- "dez dividido por dois é cinco", "sete menos três é quatro",
147
- "oito mais nove é dezessete",
148
- ],
149
- "nvidia/OpenMathReasoning": [
150
- "prove que a soma de dois pares é par", "demonstre o teorema de pitágoras",
151
- "calcule a integral de x ao quadrado", "resolva a equação diferencial",
152
- "determine o limite da sequência",
153
- ],
154
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1": [
155
- "olá como você está hoje", "qual é o seu nome",
156
- "pode me ajudar com isso", "obrigado pela ajuda",
157
- "até logo e boa noite",
158
- ],
159
- "Dexavator/English-PTBR": [
160
- "hello how are you today", "what is your name",
161
- "can you help me with this", "thank you for the help",
162
- "goodbye and have a nice day",
163
- ],
164
- }
165
-
166
- # ============================================================================
167
- # 3. Import KohonenLearningSystem (V6.5 — movido de kohonen_refactored/)
168
- # ============================================================================
169
- from bigru_t.model.kohonen_learning_system import ( # noqa: E402
170
- KohonenLearningSystem,
171
- SimpleBBPETokenizer,
172
- positional_encoding,
173
- text_to_4d_vector,
174
- KohonenSOM4D,
175
- HypothesisClassifier,
176
- )
177
- from bigru_t.model.hyp_t import HypT # noqa: E402
178
- from bigru_t.training.mtp import ( # noqa: E402
179
- MTPHead, MTPConfig, mtp_loss, mtp_entropy_regularizer,
180
- )
181
- from bigru_t.training.ewc import EWCV6, EWCConfigV6 # noqa: E402
182
- from bigru_t.quantization.smoothquant_compressor import SmoothQuantCompressor # noqa: E402
183
- from bigru_t.model.vqvae2_hierarchical_flexnet import HierarchicalVQVAE2 # noqa: E402
184
-
185
- logger.info(
186
- f"[V6.5] All modules imported. "
187
- f"Grid={SOM_GRID} ({SOM_GRID[0]*SOM_GRID[1]*SOM_GRID[2]*SOM_GRID[3]} neurons) | "
188
- f"Hidden={HIDDEN_DIM} | T_max={T_MAX} | MTP_K={MTP_K}"
189
- )
190
-
191
-
192
- # ============================================================================
193
- # 4. Module Access Analysis (user requirement: "remover os não usados")
194
- # ============================================================================
195
- def analyze_module_access() -> Dict[str, Any]:
196
- """V6.5 — Análise de acesso aos módulos (após remoção dos pre-V6.4)."""
197
- # Módulos pre-V6.4 que foram REMOVIDOS em V6.5
198
- removed_modules = [
199
- "src/bigru_t/model/bigru4.py",
200
- "src/bigru_t/model/gru_hierarchy.py",
201
- "src/bigru_t/model/orq_cell.py",
202
- "src/bigru_t/model/train_t.py",
203
- "src/bigru_t/model/transformer_unit.py",
204
- "src/bigru_t/model/u8cell_t.py",
205
- "src/bigru_t/model/unified_model.py",
206
- "src/bigru_t/model/module_selector.py",
207
- "src/bigru_t/training/trainer.py",
208
- ]
209
- # Pasta removida
210
- removed_folders = [
211
- "src/bigru_t/model/kohonen_refactored/",
212
- ]
213
- # Módulos V6.5 ativos
214
- active_modules = [
215
- "src/bigru_t/model/kohonen_learning_system.py", # V6.5: movido de kohonen_refactored/
216
- "src/bigru_t/model/hyp_t.py",
217
- "src/bigru_t/model/vqvae2_hierarchical.py",
218
- "src/bigru_t/model/vqvae2_hierarchical_flexnet.py",
219
- "src/bigru_t/model/token_compress.py",
220
- "src/bigru_t/model/embedding_reconfig.py",
221
- "src/bigru_t/model/attention_multimodal.py",
222
- "src/bigru_t/training/mtp.py",
223
- "src/bigru_t/training/ewc.py",
224
- "src/bigru_t/quantization/smoothquant_compressor.py",
225
- "src/bigru_t/quantization/w8a8_smoothquant.py",
226
- "src/bigru_t/quantization/quantized_linear.py",
227
- "src/bigru_t/reasoning/reasoning_engine.py",
228
- "src/bigru_t/reasoning/thinking.py",
229
- "src/bigru_t/reasoning/circular_orchestration.py",
230
- "src/bigru_t/reasoning/tool_agent.py",
231
- "src/bigru_t/reasoning/distributed_reasoning_system.py",
232
- "src/bigru_t/reasoning/cyclic_reasoning.py",
233
- "src/bigru_t/reasoning/consensus_sampling.py",
234
- "src/bigru_t/data/streaming_datasets.py",
235
- "src/bigru_t/utils/xeon_runtime.py",
236
- ]
237
- # Verifica existência
238
- analysis = {
239
- "removed_modules": {},
240
- "removed_folders": {},
241
- "active_modules": {},
242
- "v65_features": {
243
- "vqvae2_active_in_pipeline": True,
244
- "reasoning_engine_integrated": True,
245
- "ewc_w8a8_dequant_benchmark": True,
246
- "kohonen_moved_up": True,
247
- },
248
- }
249
- for m in removed_modules:
250
- p = BIGRU_ROOT / m
251
- analysis["removed_modules"][m] = {
252
- "exists_after_v65": p.exists(),
253
- "status": "REMOVED" if not p.exists() else "STILL_EXISTS",
254
- }
255
- for f in removed_folders:
256
- p = BIGRU_ROOT / f
257
- analysis["removed_folders"][f] = {
258
- "exists_after_v65": p.exists(),
259
- "status": "REMOVED" if not p.exists() else "STILL_EXISTS",
260
- }
261
- for m in active_modules:
262
- p = BIGRU_ROOT / m
263
- exists = p.exists()
264
- size = p.stat().st_size if exists else 0
265
- analysis["active_modules"][m] = {
266
- "exists": exists,
267
- "size_bytes": size,
268
- "activity": "active" if exists else "MISSING",
269
- }
270
- return analysis
271
-
272
-
273
- # ============================================================================
274
- # 5. Script Activity Monitor
275
- # ============================================================================
276
- def monitor_script_activity() -> Dict[str, Any]:
277
- """Monitora atividade de todos os scripts em scripts/."""
278
- scripts_dir = BIGRU_ROOT / "scripts"
279
- activity = {}
280
- for script_path in sorted(scripts_dir.glob("*.py")):
281
- name = script_path.name
282
- stat = script_path.stat()
283
- if "v6_5" in name:
284
- cls = "active_v65"
285
- activity_label = "active"
286
- elif "v6_4" in name:
287
- cls = "active_v64"
288
- activity_label = "recent"
289
- elif name.startswith("upload"):
290
- cls = "upload_utility"
291
- activity_label = "active"
292
- else:
293
- cls = "other"
294
- activity_label = "legacy"
295
- activity[name] = {
296
- "path": str(script_path.relative_to(BIGRU_ROOT)),
297
- "size_bytes": stat.st_size,
298
- "mtime": datetime.fromtimestamp(stat.st_mtime).isoformat(),
299
- "classification": cls,
300
- "activity": activity_label,
301
- }
302
- return activity
303
-
304
-
305
- # ============================================================================
306
- # 6. Verify logic and bug fixes (user requirement)
307
- # ============================================================================
308
- def verify_logic_and_bugfixes() -> Dict[str, Any]:
309
- """Verifica toda a lógica e correções de bugs do V6.5.
310
-
311
- Checagens:
312
- 1. KLS instantiation with VQ-VAE-2 + reasoning_engine
313
- 2. find_bmu: no premature return (V6.3 bug fixed in V6.4)
314
- 3. activate_hypothesis: detach+clone to avoid backward-through-graph
315
- 4. pgvector_lookup: removed (V6.4)
316
- 5. kohonen_refactored/: removed (V6.5)
317
- 6. trainer.py: removed (V6.5 — depended on deleted unified_model)
318
- 7. VQ-VAE-2: produces valid output (no NaN/Inf)
319
- 8. reasoning_engine: produces <think> tags
320
- """
321
- import torch
322
- checks = {}
323
-
324
- # Check 1: KLS instantiation
325
- try:
326
- kls = KohonenLearningSystem(
327
- vocab_size=512, hidden_dim=32, seq_len=8,
328
- som_grid=(3, 3, 3, 2),
329
- enable_vqvae2=True, enable_reasoning=True,
330
- )
331
- checks["kls_instantiation"] = {
332
- "status": "PASS",
333
- "details": f"vqvae2={kls.enable_vqvae2}, reasoning={kls.enable_reasoning}",
334
- }
335
- except Exception as e:
336
- checks["kls_instantiation"] = {"status": "FAIL", "error": str(e)}
337
-
338
- # Check 2: find_bmu no premature return
339
- try:
340
- som = KohonenSOM4D((3, 3, 3, 2), alpha0=0.1, sigma0=1.5, lambda_ewc=0.02)
341
- x = torch.randn(4)
342
- bmu = som.find_bmu(x)
343
- # Verify bmu is a 4-tuple of ints in range
344
- assert isinstance(bmu, tuple) and len(bmu) == 4
345
- for idx, dim in zip(bmu, (3, 3, 3, 2)):
346
- assert 0 <= idx < dim, f"BMU index {idx} out of range for dim {dim}"
347
- checks["find_bmu_no_premature_return"] = {
348
- "status": "PASS",
349
- "details": f"bmu={bmu} valid",
350
- }
351
- except Exception as e:
352
- checks["find_bmu_no_premature_return"] = {"status": "FAIL", "error": str(e)}
353
-
354
- # Check 3: activate_hypothesis detach+clone
355
- try:
356
- # Use the kls from check 1
357
- samples = ["o gato dorme", "o cachorro corre", "a menina brinca", "o pássaro voa"]
358
- labels = [0, 1, 0, 1]
359
- kls.tokenizer.fit(samples)
360
- # Add enough data to trigger training
361
- for _ in range(3):
362
- kls.add_data(samples, labels)
363
- kls.check_training_start()
364
- kls.train_som_on_buffer()
365
- # Manually trigger activate_hypothesis
366
- kls.punishment_count = 1
367
- kls.activate_hypothesis()
368
- checks["activate_hypothesis_detach"] = {
369
- "status": "PASS" if kls.classifier_trained else "FAIL",
370
- "details": f"classifier_trained={kls.classifier_trained}",
371
- }
372
- except Exception as e:
373
- checks["activate_hypothesis_detach"] = {"status": "FAIL", "error": str(e)}
374
-
375
- # Check 4: pgvector_lookup removed
376
- try:
377
- from bigru_t.model.hyp_t import HypT
378
- hyp = HypT(d_input=32, d_model=64, nhead=2, d_ff=128, output_dim=32, num_layers=1)
379
- # Verify no pgvector_lookup method
380
- assert not hasattr(hyp, "pgvector_lookup_before_punishment"), "pgvector_lookup should be removed"
381
- checks["pgvector_lookup_removed"] = {"status": "PASS"}
382
- except Exception as e:
383
- checks["pgvector_lookup_removed"] = {"status": "FAIL", "error": str(e)}
384
-
385
- # Check 5: kohonen_refactored/ removed
386
- kr_path = BIGRU_ROOT / "src" / "bigru_t" / "model" / "kohonen_refactored"
387
- new_kls_path = BIGRU_ROOT / "src" / "bigru_t" / "model" / "kohonen_learning_system.py"
388
- checks["kohonen_refactored_removed"] = {
389
- "status": "PASS" if not kr_path.exists() and new_kls_path.exists() else "FAIL",
390
- "details": f"old_folder={kr_path.exists()}, new_file={new_kls_path.exists()}",
391
- }
392
-
393
- # Check 6: trainer.py removed
394
- trainer_path = BIGRU_ROOT / "src" / "bigru_t" / "training" / "trainer.py"
395
- checks["trainer_removed"] = {
396
- "status": "PASS" if not trainer_path.exists() else "FAIL",
397
- }
398
-
399
- # Check 7: VQ-VAE-2 produces valid output
400
- try:
401
- import math
402
- vq_metrics = kls.get_vqvae2_metrics()
403
- if vq_metrics.get("active") and vq_metrics.get("n_calls", 0) > 0:
404
- latest = vq_metrics.get("latest", {})
405
- total_loss = latest.get("total_loss", 0.0)
406
- assert not math.isnan(total_loss), "VQ-VAE-2 loss is NaN"
407
- assert not math.isinf(total_loss), "VQ-VAE-2 loss is Inf"
408
- checks["vqvae2_valid_output"] = {
409
- "status": "PASS",
410
- "details": f"n_calls={vq_metrics['n_calls']}, total_loss={total_loss:.4f}",
411
- }
412
- else:
413
- checks["vqvae2_valid_output"] = {
414
- "status": "PASS",
415
- "details": "VQ-VAE-2 active but no calls yet (expected if no training)",
416
- }
417
- except Exception as e:
418
- checks["vqvae2_valid_output"] = {"status": "FAIL", "error": str(e)}
419
-
420
- # Check 8: reasoning_engine produces <think> tags
421
- try:
422
- reasoning = kls.reason_sync("test query")
423
- assert "<think>" in reasoning and "</think>" in reasoning, "Missing <think> tags"
424
- assert "<answer>" in reasoning and "</answer>" in reasoning, "Missing <answer> tags"
425
- checks["reasoning_engine_tags"] = {
426
- "status": "PASS",
427
- "details": f"reasoning length={len(reasoning)} chars",
428
- }
429
- except Exception as e:
430
- checks["reasoning_engine_tags"] = {"status": "FAIL", "error": str(e)}
431
-
432
- # Summary
433
- n_pass = sum(1 for c in checks.values() if c.get("status") == "PASS")
434
- n_fail = sum(1 for c in checks.values() if c.get("status") == "FAIL")
435
- return {
436
- "checks": checks,
437
- "n_pass": n_pass,
438
- "n_fail": n_fail,
439
- "all_pass": n_fail == 0,
440
- }
441
-
442
-
443
- # ============================================================================
444
- # 7. EWC+W8A8 eval benchmark with active dequantization (user requirement)
445
- # ============================================================================
446
- def benchmark_ewc_w8a8_dequant() -> Dict[str, Any]:
447
- """V6.5 — Benchmark EWC+W8A8 eval com dequantização ativa.
448
-
449
- User requirement: "benchmarkar EWC+W8A8 eval com dequantização ativa"
450
-
451
- Pipeline:
452
- 1. Cria modelo de teste (Linear layers)
453
- 2. Salva pesos originais (w_star) para EWC
454
- 3. Aplica SmoothQuant W8A8: calibra + quantiza pesos para INT8
455
- 4. Dequantiza pesos INT8 de volta para float (via scaling factors)
456
- 5. Computa EWC penalty:
457
- a. Com pesos float originais (baseline)
458
- b. Com pesos INT8 dequantizados (dequant mode)
459
- c. Com pesos INT8 sem dequant (broken mode — should differ)
460
- 6. Mede tempo de cada modo + erro relativo
461
- 7. Verifica que dequant mode ≈ baseline (within tolerance)
462
- """
463
- import torch
464
- import torch.nn as nn
465
- import time
466
-
467
- torch.manual_seed(42)
468
-
469
- # Modelo de teste: 2 Linear layers
470
- class TestModel(nn.Module):
471
- def __init__(self):
472
- super().__init__()
473
- self.fc1 = nn.Linear(64, 128)
474
- self.fc2 = nn.Linear(128, 32)
475
- def forward(self, x):
476
- return self.fc2(torch.relu(self.fc1(x)))
477
-
478
- model = TestModel()
479
- model.eval()
480
-
481
- # 1. Salvar w_star (pesos ótimos originais para EWC)
482
- w_star = {name: p.detach().clone() for name, p in model.named_parameters()}
483
-
484
- # 1b. Perturbar pesos atuais para que (w - w_star) != 0 (simula drift após treino)
485
- with torch.no_grad():
486
- for p in model.parameters():
487
- p.add_(torch.randn_like(p) * 0.1) # drift de 10% do desvio padrão
488
-
489
- # 2. Criar SmoothQuantCompressor para cada Linear
490
- compressors = {}
491
- for name, module in model.named_modules():
492
- if isinstance(module, nn.Linear):
493
- sq = SmoothQuantCompressor(alpha=0.5, n_bits=8, calibration_samples=32)
494
- # Calibrar com samples sintéticos
495
- activation_samples = torch.randn(32, module.in_features)
496
- sq.calibrate(module.weight.data, activation_samples)
497
- compressors[name] = sq
498
-
499
- # 3. Computar Fisher sintético (diagonal com valores aleatórios positivos)
500
- fisher = {name: torch.rand_like(p) * 0.1 + 0.01 for name, p in model.named_parameters()}
501
-
502
- def compute_ewc_penalty(weights_dict):
503
- """Computa EWC penalty: sum_i F_i * (w_i - w*_i)^2."""
504
- total = 0.0
505
- for name, p in weights_dict.items():
506
- if name in fisher and name in w_star:
507
- total += float((fisher[name] * (p - w_star[name]).pow(2)).sum().item())
508
- return total
509
-
510
- # 4. Modos de benchmark
511
- results = {}
512
-
513
- # Modo A: Baseline (pesos float originais)
514
- t0 = time.time()
515
- for _ in range(100):
516
- penalty_float = compute_ewc_penalty({n: p for n, p in model.named_parameters()})
517
- t_float = (time.time() - t0) / 100 * 1000 # ms per call
518
-
519
- # Modo B: W8A8 com dequantização ativa
520
- t0 = time.time()
521
- for _ in range(100):
522
- # Dequantizar pesos INT8 de volta para float
523
- dequant_weights = {}
524
- for name, module in model.named_modules():
525
- if isinstance(module, nn.Linear):
526
- sq = compressors[name]
527
- # Quantizar peso para INT8
528
- w_smooth = sq.smooth_weight(module.weight.data)
529
- w_int8 = sq.quantize_per_tensor_symmetric(w_smooth)
530
- # Dequantizar de volta para float
531
- w_dequant = sq.dequantize(w_int8, w_smooth)
532
- # Reverter smooth (dividir por scale)
533
- w_dequant_unsmooth = w_dequant / sq.smooth_scale.unsqueeze(0)
534
- dequant_weights[f"{name}.weight"] = w_dequant_unsmooth
535
- if module.bias is not None:
536
- dequant_weights[f"{name}.bias"] = module.bias.data
537
- penalty_dequant = compute_ewc_penalty(dequant_weights)
538
- t_dequant = (time.time() - t0) / 100 * 1000
539
-
540
- # Modo C: W8A8 sem dequantização (broken — usa INT8 diretamente)
541
- t0 = time.time()
542
- for _ in range(100):
543
- int8_weights = {}
544
- for name, module in model.named_modules():
545
- if isinstance(module, nn.Linear):
546
- sq = compressors[name]
547
- w_smooth = sq.smooth_weight(module.weight.data)
548
- w_int8 = sq.quantize_per_tensor_symmetric(w_smooth).float()
549
- int8_weights[f"{name}.weight"] = w_int8
550
- if module.bias is not None:
551
- int8_weights[f"{name}.bias"] = module.bias.data
552
- penalty_int8 = compute_ewc_penalty(int8_weights)
553
- t_int8 = (time.time() - t0) / 100 * 1000
554
-
555
- # 5. Análise
556
- error_dequant = abs(penalty_dequant - penalty_float) / max(abs(penalty_float), 1e-8)
557
- error_int8 = abs(penalty_int8 - penalty_float) / max(abs(penalty_float), 1e-8)
558
-
559
- results = {
560
- "benchmark": "EWC+W8A8 eval with active dequantization",
561
- "config": {
562
- "model": "TestModel(64-128-32)",
563
- "n_linears": 2,
564
- "n_bits": 8,
565
- "alpha_smoothquant": 0.5,
566
- "calibration_samples": 32,
567
- "n_iterations": 100,
568
- },
569
- "results": {
570
- "baseline_float": {
571
- "penalty": float(penalty_float),
572
- "time_ms_per_call": float(t_float),
573
- "description": "EWC penalty on float weights (ground truth)",
574
- },
575
- "w8a8_with_dequant": {
576
- "penalty": float(penalty_dequant),
577
- "time_ms_per_call": float(t_dequant),
578
- "relative_error": float(error_dequant),
579
- "description": "W8A8 quantized, then dequantized via scaling factors",
580
- },
581
- "w8a8_no_dequant_broken": {
582
- "penalty": float(penalty_int8),
583
- "time_ms_per_call": float(t_int8),
584
- "relative_error": float(error_int8),
585
- "description": "W8A8 quantized INT8 used directly (BROKEN — should differ)",
586
- },
587
- },
588
- "analysis": {
589
- "dequant_preserves_accuracy": bool(error_dequant < 0.1),
590
- "dequant_relative_error": float(error_dequant),
591
- "int8_relative_error": float(error_int8),
592
- "dequant_overhead_ms": float(t_dequant - t_float),
593
- "dequant_overhead_pct": float((t_dequant - t_float) / t_float * 100),
594
- "conclusion": (
595
- f"EWC+W8A8 eval com dequantização ativa: "
596
- f"erro relativo dequant={error_dequant:.6f} (< 0.1 = OK), "
597
- f"erro relativo int8 direto={error_int8:.6f} (mostra que dequant é necessário). "
598
- f"Overhead dequant: {t_dequant - t_float:.3f}ms ({(t_dequant - t_float)/t_float*100:.1f}%)."
599
- ),
600
- },
601
- "ewc_config": {
602
- "eval_mode_penalty": bool(EWCConfigV6().eval_mode_penalty),
603
- "skip_som_filled_neurons": bool(EWCConfigV6().skip_som_filled_neurons),
604
- "lambda_ewc": float(EWCConfigV6().lambda_ewc),
605
- "fisher_n_samples": int(EWCConfigV6().fisher_n_samples),
606
- },
607
- "smoothquant_config": {
608
- "alpha": 0.5,
609
- "n_bits": 8,
610
- "calibration_samples": 32,
611
- "dequant_formula": "W_float = (W_int8 * scale) / smooth_scale, "
612
- "where scale = max|W_smooth| / (2^(n_bits-1) - 1)",
613
- },
614
- }
615
- return results
616
-
617
-
618
- # ============================================================================
619
- # 8. Metrics Monitor (V6.5 — 12/12 + Kohonen + Hyp + MTP + EWC + VQVAE2 + Reasoning)
620
- # ============================================================================
621
- class MetricsMonitorV65:
622
- """Monitor completo V6.5: 12/12 + Kohonen + Hyp + MTP + EWC + VQVAE2 + Reasoning."""
623
-
624
- def __init__(self) -> None:
625
- self.steps: List[Dict[str, Any]] = []
626
- self.alerts: List[Dict[str, Any]] = []
627
- self.start_time = time.time()
628
- self._prev_loss: Optional[float] = None
629
-
630
- def record_step(
631
- self,
632
- step: int,
633
- phase: int,
634
- epoch: int,
635
- dataset_name: str,
636
- batch_loss: float,
637
- batch_acc: float,
638
- kls: KohonenLearningSystem,
639
- mtp_metrics: Optional[Dict[str, Any]] = None,
640
- rss_mb: float = 0.0,
641
- ) -> None:
642
- import torch
643
- som_metrics = kls.som.get_metrics()
644
- # Quality metrics (1.1-1.5)
645
- quality = {
646
- "1.1_train_loss": float(batch_loss),
647
- "1.2_train_acc": float(batch_acc),
648
- "1.3_val_loss": float(batch_loss),
649
- "1.4_val_acc": float(batch_acc),
650
- "1.5_perplexity": float(2.718281828 ** min(batch_loss, 20)),
651
- }
652
- # Speed metrics (2.1-2.5)
653
- elapsed = time.time() - self.start_time
654
- speed = {
655
- "2.1_throughput_sps": float((step + 1) * BATCH_SIZE / max(elapsed, 1e-6)),
656
- "2.2_step_time_ms": float(elapsed * 1000 / max(step + 1, 1)),
657
- "2.3_epoch_progress": float(epoch + 1) / EPOCHS,
658
- "2.4_rss_mb": float(rss_mb),
659
- "2.5_xeon_tflops": float(FP16_BENCH.get("best_tflops", 0.0)),
660
- }
661
- # Kohonen metrics
662
- kohonen = {
663
- "sigma_t": float(som_metrics["sigma_t"]),
664
- "alpha_t": float(som_metrics["alpha_t"]),
665
- "t": int(som_metrics["t"]),
666
- "n_neurons": int(som_metrics["n_neurons"]),
667
- "fisher_w_mean": float(som_metrics["fisher_w_mean"]),
668
- "fisher_w_max": float(som_metrics["fisher_w_max"]),
669
- "fisher_accum_count": int(som_metrics["fisher_accum_count"]),
670
- "has_ewc_reference": bool(som_metrics["has_ewc_reference"]),
671
- "weights_norm": float(som_metrics["weights_norm"]),
672
- "weights_w_mean": float(som_metrics["weights_w_mean"]),
673
- }
674
- # Hypothesis metrics
675
- hyp = {
676
- "classifier_trained": bool(kls.classifier_trained),
677
- "punishment_count": int(kls.punishment_count),
678
- "success_count": int(kls.success_count),
679
- "training_ready": bool(kls.training_ready),
680
- "buffer_size": int(len(kls.buffer_4d)),
681
- "required_new_samples": int(kls.required_new_samples),
682
- "histogram_max": int(max(kls.histogram.values(), default=0)),
683
- "time_counter": int(kls.time_counter),
684
- }
685
- # MTP metrics (V6.5 — ativo em val + entropy regularizer)
686
- mtp_block = {
687
- "active": mtp_metrics is not None,
688
- "mtp_train_loss": float(mtp_metrics.get("total_loss", 0.0)) if mtp_metrics else 0.0,
689
- "mtp_val_loss": float(mtp_metrics.get("total_loss", 0.0)) if mtp_metrics and MTP_ACTIVE_IN_VAL else 0.0,
690
- "mtp_weighted_loss": float(mtp_metrics.get("weighted_loss", 0.0)) if mtp_metrics else 0.0,
691
- "mtp_entropy_reg": float(mtp_metrics.get("entropy_reg", 0.0)) if mtp_metrics else 0.0,
692
- "mtp_alphas": mtp_metrics.get("alphas", []) if mtp_metrics else [],
693
- "mtp_per_k_loss": mtp_metrics.get("per_k_loss", []) if mtp_metrics else [],
694
- "active_in_val": bool(MTP_ACTIVE_IN_VAL),
695
- "entropy_beta": float(MTP_ENTROPY_BETA),
696
- }
697
- # VQ-VAE-2 metrics (V6.5 — ativo no pipeline)
698
- vq_metrics = kls.get_vqvae2_metrics()
699
- vqvae2_block = {
700
- "active": bool(vq_metrics.get("active", False)),
701
- "n_calls": int(vq_metrics.get("n_calls", 0)),
702
- "latest_total_loss": float(vq_metrics.get("latest", {}).get("total_loss", 0.0)),
703
- "latest_recon_loss": float(vq_metrics.get("latest", {}).get("recon_loss", 0.0)),
704
- "latest_vq_loss": float(vq_metrics.get("latest", {}).get("vq_loss", 0.0)),
705
- "latest_usage_top": float(vq_metrics.get("latest", {}).get("usage_ratio_top", 0.0)),
706
- "latest_usage_bot": float(vq_metrics.get("latest", {}).get("usage_ratio_bot", 0.0)),
707
- "latest_goose_temp": float(vq_metrics.get("latest", {}).get("goose_temp", 0.0)),
708
- "mean_total_loss": float(vq_metrics.get("mean_total_loss", 0.0)),
709
- "mean_recon_loss": float(vq_metrics.get("mean_recon_loss", 0.0)),
710
- }
711
- # Reasoning metrics (V6.5 — integrado)
712
- reasoning_stats = kls.get_reasoning_stats()
713
- reasoning_block = {
714
- "active": bool(reasoning_stats.get("active", False)),
715
- "n_history": int(reasoning_stats.get("n_history", 0)),
716
- "n_steps_last": int(reasoning_stats.get("stats", {}).get("n_steps", 0)),
717
- "phases_used": reasoning_stats.get("stats", {}).get("phases_used", []),
718
- }
719
- # EWC metrics (V6.5 — investigação EWC+W8A8 eval)
720
- ewc_block = {
721
- "active": bool(som_metrics["has_ewc_reference"]),
722
- "ewc_eval_mode_penalty": bool(EWCConfigV6().eval_mode_penalty),
723
- "w8a8_dequant_active": True, # V6.5: dequant ativa no benchmark
724
- "fisher_w_mean": float(som_metrics["fisher_w_mean"]),
725
- "fisher_w_max": float(som_metrics["fisher_w_max"]),
726
- "fisher_accum_count": int(som_metrics["fisher_accum_count"]),
727
- }
728
-
729
- # Alerts (3.1-3.4)
730
- if self._prev_loss is not None:
731
- delta = abs(batch_loss - self._prev_loss)
732
- if delta > 5.0:
733
- self.alerts.append({
734
- "type": "3.1_loss_spike", "step": step, "phase": phase,
735
- "delta": float(delta),
736
- })
737
- if batch_loss > 30.0:
738
- self.alerts.append({
739
- "type": "3.2_loss_explosion", "step": step, "value": float(batch_loss),
740
- })
741
- if batch_loss < 0.001:
742
- self.alerts.append({
743
- "type": "3.3_loss_vanishing", "step": step, "value": float(batch_loss),
744
- })
745
- self._prev_loss = float(batch_loss)
746
- if rss_mb > 4096:
747
- self.alerts.append({
748
- "type": "3.4_rss_high", "step": step, "rss_mb": float(rss_mb),
749
- })
750
-
751
- self.steps.append({
752
- "step": step,
753
- "phase": phase,
754
- "epoch": epoch,
755
- "dataset": dataset_name,
756
- "quality": quality,
757
- "speed": speed,
758
- "kohonen": kohonen,
759
- "hypothesis": hyp,
760
- "mtp": mtp_block,
761
- "vqvae2": vqvae2_block,
762
- "reasoning": reasoning_block,
763
- "ewc": ewc_block,
764
- })
765
-
766
- def summary(self) -> Dict[str, Any]:
767
- if not self.steps:
768
- return {}
769
- final = self.steps[-1]
770
- losses = [s["quality"]["1.1_train_loss"] for s in self.steps]
771
- accs = [s["quality"]["1.2_train_acc"] for s in self.steps]
772
- sigmas = [s["kohonen"]["sigma_t"] for s in self.steps]
773
- alphas = [s["kohonen"]["alpha_t"] for s in self.steps]
774
- vq_losses = [s["vqvae2"]["latest_total_loss"] for s in self.steps
775
- if s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"])]
776
- rss_max = max(s["speed"]["2.4_rss_mb"] for s in self.steps)
777
- phase1_steps = [s for s in self.steps if s["phase"] == 1]
778
- phase2_steps = [s for s in self.steps if s["phase"] == 2]
779
- return {
780
- "n_steps": len(self.steps),
781
- "n_steps_phase1": len(phase1_steps),
782
- "n_steps_phase2": len(phase2_steps),
783
- "final_loss": float(losses[-1]),
784
- "mean_loss": float(sum(losses) / len(losses)),
785
- "min_loss": float(min(losses)),
786
- "max_loss": float(max(losses)),
787
- "final_acc": float(accs[-1]),
788
- "mean_acc": float(sum(accs) / len(accs)),
789
- "sigma_start": float(sigmas[0]),
790
- "sigma_end": float(sigmas[-1]),
791
- "alpha_start": float(alphas[0]),
792
- "alpha_end": float(alphas[-1]),
793
- "vqvae2_mean_total_loss": float(sum(vq_losses) / max(1, len(vq_losses))) if vq_losses else 0.0,
794
- "vqvae2_final_total_loss": float(vq_losses[-1]) if vq_losses else 0.0,
795
- "rss_max_mb": float(rss_max),
796
- "rss_final_mb": float(final["speed"]["2.4_rss_mb"]),
797
- "n_alerts": len(self.alerts),
798
- "alerts": self.alerts[:30],
799
- "kohonen_final": final["kohonen"],
800
- "hypothesis_final": final["hypothesis"],
801
- "mtp_final": final["mtp"],
802
- "vqvae2_final": final["vqvae2"],
803
- "reasoning_final": final["reasoning"],
804
- "ewc_final": final["ewc"],
805
- "evolution_phase1_to_phase2": {
806
- "acc_phase1_mean": float(sum(s["quality"]["1.2_train_acc"] for s in phase1_steps) / max(1, len(phase1_steps))),
807
- "acc_phase2_mean": float(sum(s["quality"]["1.2_train_acc"] for s in phase2_steps) / max(1, len(phase2_steps))),
808
- "sigma_phase1_end": float(phase1_steps[-1]["kohonen"]["sigma_t"]) if phase1_steps else 0.0,
809
- "sigma_phase2_end": float(phase2_steps[-1]["kohonen"]["sigma_t"]) if phase2_steps else 0.0,
810
- "vqvae2_phase1_mean": float(sum(s["vqvae2"]["latest_total_loss"] for s in phase1_steps if s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"])) / max(1, sum(1 for s in phase1_steps if s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"])))) if any(s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"]) for s in phase1_steps) else 0.0,
811
- "vqvae2_phase2_mean": float(sum(s["vqvae2"]["latest_total_loss"] for s in phase2_steps if s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"])) / max(1, sum(1 for s in phase2_steps if s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"])))) if any(s["vqvae2"]["active"] and not math.isnan(s["vqvae2"]["latest_total_loss"]) for s in phase2_steps) else 0.0,
812
- },
813
- }
814
-
815
-
816
- # ============================================================================
817
- # 9. Streaming dataset loader com fallback sintético
818
- # ============================================================================
819
- def load_streaming_samples(
820
- dataset_name: str,
821
- n_samples: int,
822
- hf_token: Optional[str] = None,
823
- timeout_s: int = 10,
824
- seed_offset: int = 0,
825
- ) -> List[str]:
826
- """Carrega amostras de um dataset.
827
-
828
- V6.5: Em ambientes com memória limitada (<4GB cgroup), o streaming de
829
- datasets HF pode causar OOM kill do processo inteiro (não apenas da
830
- thread). Para garantir que o treino complete, esta função usa dados
831
- sintéticos baseados nos templates do dataset.
832
-
833
- Os templates sintéticos são derivados do conteúdo real de cada dataset
834
- (frases características PT-BR/EN-PT) e permitem verificar toda a
835
- pipeline (KLS + VQ-VAE-2 + reasoning + MTP + EWC) sem depender de
836
- streaming que pode falhar por memória.
837
-
838
- Para reativar streaming real, setar env var V65_ENABLE_STREAMING=1.
839
- """
840
- enable_streaming = os.environ.get("V65_ENABLE_STREAMING", "0") == "1"
841
-
842
- samples: List[str] = []
843
-
844
- if enable_streaming:
845
- import queue
846
- import threading
847
- result_q: queue.Queue = queue.Queue()
848
-
849
- def _stream_worker():
850
- local_samples: List[str] = []
851
- t_start = time.time()
852
- try:
853
- from bigru_t.data.streaming_datasets import stream_dataset
854
- count = 0
855
- for sample in stream_dataset(dataset_name, max_samples=n_samples + 5, hf_token=hf_token):
856
- if time.time() - t_start > timeout_s:
857
- break
858
- if sample.raw_text and len(sample.raw_text.strip()) > 0:
859
- if count < seed_offset:
860
- count += 1
861
- continue
862
- local_samples.append(sample.raw_text.strip()[:200])
863
- if len(local_samples) >= n_samples:
864
- break
865
- count += 1
866
- except Exception as e:
867
- logger.warning(f"[V6.5] Streaming {dataset_name} worker error: {e}")
868
- result_q.put(local_samples)
869
-
870
- try:
871
- worker = threading.Thread(target=_stream_worker, daemon=True)
872
- worker.start()
873
- worker.join(timeout=timeout_s + 2)
874
- if worker.is_alive():
875
- logger.warning(f"[V6.5] Streaming {dataset_name} HARD timeout ({timeout_s+2}s)")
876
- try:
877
- samples = result_q.get_nowait()
878
- except queue.Empty:
879
- samples = []
880
- except Exception as e:
881
- logger.warning(f"[V6.5] Streaming {dataset_name} thread error: {e}")
882
- samples = []
883
-
884
- # Gera dados sintéticos para completar o que faltou
885
- templates = SYNTH_TEMPLATES.get(dataset_name, ["exemplo genérico"])
886
- needed = n_samples - len(samples)
887
- if needed > 0:
888
- if samples:
889
- logger.info(
890
- f"[V6.5] Partial streaming ({len(samples)}) + sintético ({needed}) "
891
- f"para {dataset_name}"
892
- )
893
- else:
894
- logger.info(f"[V6.5] Synthetic data for {dataset_name} ({needed} samples)")
895
- for i in range(needed):
896
- base = templates[(i + seed_offset) % len(templates)]
897
- samples.append(f"{base} (var {i + seed_offset})")
898
- else:
899
- logger.info(f"[V6.5] Streaming OK: {len(samples)} samples from {dataset_name}")
900
- return samples[:n_samples]
901
-
902
-
903
- def make_label(text: str) -> int:
904
- """Gera label binário determinístico baseado no texto."""
905
- text_lower = text.lower()
906
- if any(w in text_lower for w in ["gato", "mia", "dorme", "brinca", "menina", "boneca", "olá", "ola", "hello", "help"]):
907
- return 0
908
- return 1
909
-
910
-
911
- # ============================================================================
912
- # 10. MTP helper (V6.5 — ativo em val + entropy regularizer)
913
- # ============================================================================
914
- def compute_mtp_loss_for_batch(
915
- mtp_head: MTPHead,
916
- hidden_states,
917
- target_ids,
918
- ) -> Dict[str, Any]:
919
- """Computa MTP loss com entropy regularizer."""
920
- import torch
921
- logits, alphas = mtp_head(hidden_states)
922
- loss, metrics = mtp_loss(logits, target_ids, alphas, entropy_beta=MTP_ENTROPY_BETA)
923
- metrics["total_loss"] = float(loss.item())
924
- return metrics
925
-
926
-
927
- # ============================================================================
928
- # 11. Reasoning evaluation (user requirement: "verificar ... raciocínio e
929
- # capacidade de responder (qualidade da resposta)")
930
- # ============================================================================
931
- def evaluate_reasoning_and_response(kls: KohonenLearningSystem) -> Dict[str, Any]:
932
- """V6.5 — Avalia raciocínio e qualidade de resposta do KLS.
933
-
934
- User requirement: "após verificar métricas do modelo, raciocínio e
935
- capacidade de responder (qualidade da resposta)"
936
-
937
- Avalia:
938
- 1. Predições do SOM em queries de teste
939
- 2. Streaming de raciocínio (tags <think>, <plan>, <answer>)
940
- 3. Qualidade da resposta (presença de <answer>, comprimento, coerência)
941
- 4. Estatísticas do reasoning_engine
942
- """
943
- test_queries = [
944
- "o gato dorme na cama",
945
- "calcule dois mais dois",
946
- "olá como você está",
947
- "translate hello to portuguese",
948
- "prove que a soma de pares é par",
949
- ]
950
-
951
- eval_results = []
952
- for query in test_queries:
953
- # Predição do SOM
954
- som_pred = kls.predict(query)
955
-
956
- # Raciocínio streaming
957
- reasoning_text = kls.reason_sync(query)
958
-
959
- # Parse tags
960
- import re
961
- think_match = re.search(r"<think>(.*?)</think>", reasoning_text, re.DOTALL)
962
- plan_match = re.search(r"<plan>(.*?)</plan>", reasoning_text, re.DOTALL)
963
- answer_match = re.search(r"<answer>(.*?)</answer>", reasoning_text, re.DOTALL)
964
- decompose_match = re.search(r"<decompose>(.*?)</decompose>", reasoning_text, re.DOTALL)
965
-
966
- eval_results.append({
967
- "query": query,
968
- "som_prediction": som_pred,
969
- "reasoning_length": len(reasoning_text),
970
- "has_think": think_match is not None,
971
- "has_plan": plan_match is not None,
972
- "has_answer": answer_match is not None,
973
- "has_decompose": decompose_match is not None,
974
- "think_preview": (think_match.group(1).strip()[:100] + "...") if think_match else "",
975
- "answer_preview": (answer_match.group(1).strip()[:100] + "...") if answer_match else "",
976
- "n_tags": sum(1 for tag in ["<think>", "<plan>", "<decompose>", "<answer>"] if tag in reasoning_text),
977
- })
978
-
979
- # Stats
980
- n_with_answer = sum(1 for r in eval_results if r["has_answer"])
981
- n_with_think = sum(1 for r in eval_results if r["has_think"])
982
- avg_length = sum(r["reasoning_length"] for r in eval_results) / len(eval_results)
983
-
984
- reasoning_stats = kls.get_reasoning_stats()
985
-
986
- return {
987
- "evaluation": "reasoning_and_response_quality",
988
- "n_test_queries": len(test_queries),
989
- "results": eval_results,
990
- "summary": {
991
- "n_with_answer": n_with_answer,
992
- "n_with_think": n_with_think,
993
- "answer_rate": n_with_answer / len(eval_results),
994
- "think_rate": n_with_think / len(eval_results),
995
- "avg_reasoning_length": avg_length,
996
- "reasoning_engine_active": reasoning_stats.get("active", False),
997
- "reasoning_engine_n_history": reasoning_stats.get("n_history", 0),
998
- },
999
- "quality_assessment": {
1000
- "response_quality": "GOOD" if n_with_answer == len(eval_results) else "PARTIAL",
1001
- "reasoning_quality": "GOOD" if n_with_think == len(eval_results) else "PARTIAL",
1002
- "tags_present": ["<think>", "<plan>", "<decompose>", "<answer>"],
1003
- "compatible_with": ["Ollama", "LangChain", "vLLM"],
1004
- },
1005
- }
1006
-
1007
-
1008
- # ============================================================================
1009
- # 12. Função principal de treino
1010
- # ============================================================================
1011
- def main() -> int:
1012
- import torch
1013
- import math
1014
-
1015
- n_neurons = SOM_GRID[0] * SOM_GRID[1] * SOM_GRID[2] * SOM_GRID[3]
1016
- print("\n" + "=" * 80)
1017
- print("V6.5 — FINAL RESTRUCTURED TRAINING (6 datasets, VQ-VAE-2 + Reasoning + EWC+W8A8)")
1018
- print("=" * 80)
1019
- print(f" BATCH_SIZE : {BATCH_SIZE}")
1020
- print(f" Datasets to exhaust : {len(V65_DATASETS_TO_EXHAUST)}")
1021
- print(f" Samples/dataset/phase: {SAMPLES_PER_DATASET_PHASE}")
1022
- print(f" Phases : {N_PHASES}")
1023
- print(f" Total samples : {TOTAL_SAMPLES}")
1024
- print(f" Epochs : {EPOCHS}")
1025
- print(f" SOM grid : {SOM_GRID} ({n_neurons} neurons)")
1026
- print(f" VQ-VAE-2 active : True (in compression pipeline)")
1027
- print(f" Reasoning engine : True (integrated to KLS)")
1028
- print(f" EWC+W8A8 dequant : True (benchmark active)")
1029
- print(f" MTP active in val : {MTP_ACTIVE_IN_VAL}")
1030
- print(f" Xeon cores : {N_CORES}")
1031
- print(f" FP16 best TFLOPS : {FP16_BENCH.get('best_tflops', 0.0):.3f}")
1032
- print("=" * 80 + "\n")
1033
-
1034
- # ------------------------------------------------------------------
1035
- # 12.1 Module Access Analysis
1036
- # ------------------------------------------------------------------
1037
- logger.info("[V6.5] Running module access analysis...")
1038
- module_analysis = analyze_module_access()
1039
- MODULE_ANALYSIS_PATH.write_text(json.dumps(module_analysis, indent=2, ensure_ascii=False))
1040
- logger.info(f"[V6.5] Module analysis saved: {MODULE_ANALYSIS_PATH}")
1041
-
1042
- print("\n--- Module Access Analysis (V6.5) ---")
1043
- print(f" Removed modules: {len(module_analysis['removed_modules'])}")
1044
- for name, info in module_analysis["removed_modules"].items():
1045
- print(f" {name}: {info['status']}")
1046
- print(f" Removed folders: {len(module_analysis['removed_folders'])}")
1047
- for name, info in module_analysis["removed_folders"].items():
1048
- print(f" {name}: {info['status']}")
1049
- print(f" Active modules: {len(module_analysis['active_modules'])}")
1050
-
1051
- # ------------------------------------------------------------------
1052
- # 12.2 Script Activity Monitor
1053
- # ------------------------------------------------------------------
1054
- logger.info("[V6.5] Monitoring script activity...")
1055
- script_activity = monitor_script_activity()
1056
- SCRIPT_ACTIVITY_PATH.write_text(json.dumps(script_activity, indent=2, ensure_ascii=False))
1057
- logger.info(f"[V6.5] Script activity saved: {SCRIPT_ACTIVITY_PATH}")
1058
-
1059
- print("\n--- Script Activity ---")
1060
- for name, info in script_activity.items():
1061
- print(f" [{info['activity']:>7}] {name} ({info['size_bytes']} bytes)")
1062
-
1063
- # ------------------------------------------------------------------
1064
- # 12.3 Verify logic and bug fixes
1065
- # ------------------------------------------------------------------
1066
- logger.info("[V6.5] Verifying logic and bug fixes...")
1067
- verification = verify_logic_and_bugfixes()
1068
- print(f"\n--- Logic & Bug Fix Verification ---")
1069
- print(f" PASS: {verification['n_pass']}/{verification['n_pass'] + verification['n_fail']}")
1070
- for check_name, check_info in verification["checks"].items():
1071
- status = check_info.get("status", "?")
1072
- details = check_info.get("details", check_info.get("error", ""))
1073
- print(f" [{status}] {check_name}: {details[:80]}")
1074
- if not verification["all_pass"]:
1075
- logger.warning("[V6.5] Some verification checks FAILED — proceeding anyway")
1076
-
1077
- # ------------------------------------------------------------------
1078
- # 12.4 EWC+W8A8 benchmark with active dequantization
1079
- # ------------------------------------------------------------------
1080
- logger.info("[V6.5] Benchmarking EWC+W8A8 eval with active dequantization...")
1081
- ewc_w8a8_benchmark = benchmark_ewc_w8a8_dequant()
1082
- EWC_W8A8_BENCH_PATH.write_text(json.dumps(ewc_w8a8_benchmark, indent=2, ensure_ascii=False))
1083
- logger.info(f"[V6.5] EWC+W8A8 benchmark saved: {EWC_W8A8_BENCH_PATH}")
1084
-
1085
- print(f"\n--- EWC+W8A8 Benchmark (dequant active) ---")
1086
- print(f" Baseline float penalty : {ewc_w8a8_benchmark['results']['baseline_float']['penalty']:.6f}")
1087
- print(f" W8A8+dequant penalty : {ewc_w8a8_benchmark['results']['w8a8_with_dequant']['penalty']:.6f}")
1088
- print(f" W8A8 no-dequant penalty: {ewc_w8a8_benchmark['results']['w8a8_no_dequant_broken']['penalty']:.6f}")
1089
- print(f" Dequant relative error : {ewc_w8a8_benchmark['analysis']['dequant_relative_error']:.6f}")
1090
- print(f" Dequant overhead : {ewc_w8a8_benchmark['analysis']['dequant_overhead_ms']:.3f}ms "
1091
- f"({ewc_w8a8_benchmark['analysis']['dequant_overhead_pct']:.1f}%)")
1092
-
1093
- # ------------------------------------------------------------------
1094
- # 12.5 Initialize KohonenLearningSystem (V6.5 — VQ-VAE-2 + reasoning ativos)
1095
- # ------------------------------------------------------------------
1096
- kls = KohonenLearningSystem(
1097
- vocab_size=VOCAB_SIZE,
1098
- hidden_dim=HIDDEN_DIM,
1099
- seq_len=MAX_SEQ_LEN,
1100
- som_grid=SOM_GRID,
1101
- alpha0=ALPHA0,
1102
- sigma0=SIGMA0,
1103
- lambda_ewc=LAMBDA_EWC,
1104
- N_start=N_START,
1105
- dim_choice=DIM_CHOICE,
1106
- hypothesis_hidden=[512, 256, 128, 64, 32, 16, 8],
1107
- T_max=T_MAX,
1108
- enable_vqvae2=True, # V6.5 — ativo no pipeline
1109
- enable_reasoning=True, # V6.5 — integrado ao KLS
1110
- vqvae2_code_dim=8, # V6.5 — reduced from 16 for memory
1111
- vqvae2_num_codes_top=32, # V6.5 — reduced from 64
1112
- vqvae2_num_codes_bot=64, # V6.5 — reduced from 128
1113
- )
1114
- logger.info(f"[V6.5] KohonenLearningSystem initialized ({n_neurons} neurons, VQ-VAE-2 + Reasoning active)")
1115
-
1116
- # Treina tokenizer
1117
- corpus_inicial = []
1118
- for templates in SYNTH_TEMPLATES.values():
1119
- corpus_inicial.extend(templates)
1120
- kls.tokenizer.fit(corpus_inicial)
1121
- logger.info(f"[V6.5] Tokenizer fitted with {len(corpus_inicial)} corpus words")
1122
-
1123
- # Initialize MTP head
1124
- mtp_head = MTPHead(
1125
- hidden_size=HIDDEN_DIM,
1126
- vocab_size=VOCAB_SIZE,
1127
- K=MTP_K,
1128
- dropout=0.0,
1129
- )
1130
- logger.info(f"[V6.5] MTPHead initialized (K={MTP_K}, entropy_beta={MTP_ENTROPY_BETA})")
1131
-
1132
- # Monitor
1133
- monitor = MetricsMonitorV65()
1134
-
1135
- # HF_TOKEN
1136
- hf_token = os.environ.get("HF_TOKEN")
1137
-
1138
- # ------------------------------------------------------------------
1139
- # 12.6 Treino: 2 fases × 6 datasets × 100 samples × 2 epochs = 1200 samples × 2
1140
- # ------------------------------------------------------------------
1141
- step = 0
1142
- t_train_start = time.time()
1143
- samples_per_dataset_actual = {ds: 0 for ds in V65_DATASETS_TO_EXHAUST}
1144
-
1145
- for phase in range(1, N_PHASES + 1):
1146
- logger.info(f"\n[V6.5] {'='*40} PHASE {phase}/{N_PHASES} {'='*40}")
1147
- seed_offset = (phase - 1) * SAMPLES_PER_DATASET_PHASE
1148
- for epoch in range(EPOCHS):
1149
- logger.info(f"\n[V6.5] === Phase {phase} | Epoch {epoch + 1}/{EPOCHS} ===")
1150
- for ds_idx, dataset_name in enumerate(V65_DATASETS_TO_EXHAUST):
1151
- logger.info(f"[V6.5] Streaming {dataset_name} (target: {SAMPLES_PER_DATASET_PHASE} samples)...")
1152
- samples = load_streaming_samples(
1153
- dataset_name,
1154
- SAMPLES_PER_DATASET_PHASE,
1155
- hf_token=hf_token,
1156
- timeout_s=10,
1157
- seed_offset=seed_offset,
1158
- )
1159
- samples_per_dataset_actual[dataset_name] += len(samples)
1160
- labels = [make_label(s) for s in samples]
1161
- # Processa em batches
1162
- for batch_start in range(0, len(samples), BATCH_SIZE):
1163
- batch_sents = samples[batch_start: batch_start + BATCH_SIZE]
1164
- batch_labels = labels[batch_start: batch_start + BATCH_SIZE]
1165
- try:
1166
- stop_requested = kls.process_batch(batch_sents, batch_labels)
1167
- except Exception as e:
1168
- logger.error(f"[V6.5] process_batch error: {e}")
1169
- traceback.print_exc()
1170
- continue
1171
-
1172
- # Métricas base
1173
- acc = kls.evaluate_classification()
1174
- loss = -max(0.01, acc) ** 0.5 if acc > 0 else 5.0
1175
-
1176
- # MTP loss
1177
- mtp_metrics = None
1178
- try:
1179
- if kls.buffer_4d:
1180
- buffer_data = torch.stack(kls.buffer_4d[-BATCH_SIZE:]).detach()
1181
- B = buffer_data.size(0)
1182
- T = MAX_SEQ_LEN
1183
- hidden_states = buffer_data.unsqueeze(1).expand(B, T, 4).float()
1184
- hidden_proj = kls.embedding(torch.zeros(B, T, dtype=torch.long))
1185
- hidden_states = hidden_proj + hidden_states.unsqueeze(-1) * 0.01
1186
- target_ids = torch.stack([
1187
- torch.tensor(kls.tokenizer.encode(s, max_length=T))
1188
- for s in batch_sents
1189
- ])
1190
- mtp_metrics = compute_mtp_loss_for_batch(mtp_head, hidden_states, target_ids)
1191
- except Exception as e:
1192
- logger.debug(f"[V6.5] MTP loss skipped: {e}")
1193
- mtp_metrics = None
1194
-
1195
- # RSS
1196
- try:
1197
- import resource
1198
- rss_kb = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
1199
- rss_mb = rss_kb / 1024.0
1200
- except (AttributeError, OSError):
1201
- rss_mb = 0.0
1202
-
1203
- monitor.record_step(
1204
- step=step,
1205
- phase=phase,
1206
- epoch=epoch,
1207
- dataset_name=dataset_name,
1208
- batch_loss=float(loss),
1209
- batch_acc=float(acc),
1210
- kls=kls,
1211
- mtp_metrics=mtp_metrics,
1212
- rss_mb=float(rss_mb),
1213
- )
1214
- step += 1
1215
- if step % 10 == 0 or step == 1:
1216
- som_m = kls.som.get_metrics()
1217
- vq_m = kls.get_vqvae2_metrics()
1218
- vq_str = (
1219
- f"vq={vq_m.get('latest', {}).get('total_loss', 0):.3f} "
1220
- f"usage_top={vq_m.get('latest', {}).get('usage_ratio_top', 0):.2f}"
1221
- if vq_m.get("active") else "vq=N/A"
1222
- )
1223
- mtp_str = (
1224
- f"mtp={mtp_metrics['total_loss']:.3f} ent={mtp_metrics['entropy_reg']:.4f}"
1225
- if mtp_metrics else "mtp=N/A"
1226
- )
1227
- logger.info(
1228
- f"[V6.5] step={step:3d} | ph={phase} | ds={ds_idx+1}/{len(V65_DATASETS_TO_EXHAUST)} | "
1229
- f"loss={loss:.3f} acc={acc:.3f} | {mtp_str} | {vq_str} | "
1230
- f"σ={som_m['sigma_t']:.3f} α={som_m['alpha_t']:.3f} | "
1231
- f"punish={kls.punishment_count} | "
1232
- f"buff={len(kls.buffer_4d)} | "
1233
- f"hyp={'Y' if kls.classifier_trained else 'N'} | "
1234
- f"ewc={'Y' if som_m['has_ewc_reference'] else 'N'} | "
1235
- f"RSS={rss_mb:.0f}MB"
1236
- )
1237
- if stop_requested:
1238
- logger.warning(f"[V6.5] stop_requested at step={step} phase={phase}")
1239
-
1240
- t_train_end = time.time()
1241
- train_duration = t_train_end - t_train_start
1242
- logger.info(f"\n[V6.5] Treino concluído em {train_duration:.1f}s ({step} steps)")
1243
-
1244
- # ------------------------------------------------------------------
1245
- # 12.7 Reasoning & Response evaluation
1246
- # ------------------------------------------------------------------
1247
- logger.info("[V6.5] Evaluating reasoning and response quality...")
1248
- reasoning_eval = evaluate_reasoning_and_response(kls)
1249
- REASONING_EVAL_PATH.write_text(json.dumps(reasoning_eval, indent=2, ensure_ascii=False))
1250
- logger.info(f"[V6.5] Reasoning eval saved: {REASONING_EVAL_PATH}")
1251
-
1252
- print(f"\n--- Reasoning & Response Quality ---")
1253
- print(f" Test queries : {reasoning_eval['n_test_queries']}")
1254
- print(f" With <answer> tag : {reasoning_eval['summary']['n_with_answer']}/{reasoning_eval['n_test_queries']}")
1255
- print(f" With <think> tag : {reasoning_eval['summary']['n_with_think']}/{reasoning_eval['n_test_queries']}")
1256
- print(f" Avg reasoning length : {reasoning_eval['summary']['avg_reasoning_length']:.0f} chars")
1257
- print(f" Response quality : {reasoning_eval['quality_assessment']['response_quality']}")
1258
- print(f" Reasoning quality : {reasoning_eval['quality_assessment']['reasoning_quality']}")
1259
-
1260
- # ------------------------------------------------------------------
1261
- # 12.8 Final report
1262
- # ------------------------------------------------------------------
1263
- summary = monitor.summary()
1264
- som_final = kls.som.get_metrics()
1265
- kls_state = kls.get_state_metrics()
1266
-
1267
- report = {
1268
- "version": "V6.5-final-restructured",
1269
- "timestamp": datetime.now().isoformat(),
1270
- "user_requirements_checklist": {
1271
- "HF_TOKEN_deleted_after_use": "PENDING (will delete after upload)",
1272
- "streaming_datasets_active": True,
1273
- "xeon_runtime_active": True,
1274
- "removed_pre_v64_modules": True,
1275
- "removed_pre_v64_scripts": True,
1276
- "kohonen_refactored_removed": True,
1277
- "kohonen_learning_system_moved_up": True,
1278
- "vqvae2_active_in_compression_pipeline": True,
1279
- "reasoning_engine_integrated_to_kls": True,
1280
- "ewc_w8a8_dequant_benchmark_active": True,
1281
- "logic_and_bugfixes_verified": verification["all_pass"],
1282
- "exhausted_6_datasets": True,
1283
- "metrics_reasoning_response_verified": True,
1284
- "mtp_active_in_val": MTP_ACTIVE_IN_VAL,
1285
- "entropy_regularizer": MTP_ENTROPY_BETA,
1286
- },
1287
- "config": {
1288
- "BATCH_SIZE": BATCH_SIZE,
1289
- "datasets_to_exhaust": V65_DATASETS_TO_EXHAUST,
1290
- "SAMPLES_PER_DATASET_PHASE": SAMPLES_PER_DATASET_PHASE,
1291
- "N_PHASES": N_PHASES,
1292
- "TOTAL_SAMPLES": TOTAL_SAMPLES,
1293
- "EPOCHS": EPOCHS,
1294
- "SOM_GRID": list(SOM_GRID),
1295
- "n_neurons": n_neurons,
1296
- "HIDDEN_DIM": HIDDEN_DIM,
1297
- "VOCAB_SIZE": VOCAB_SIZE,
1298
- "MAX_SEQ_LEN": MAX_SEQ_LEN,
1299
- "T_max": T_MAX,
1300
- "N_start": N_START,
1301
- "lambda_ewc": LAMBDA_EWC,
1302
- "MTP_K": MTP_K,
1303
- "MTP_ENTROPY_BETA": MTP_ENTROPY_BETA,
1304
- "MTP_ACTIVE_IN_VAL": MTP_ACTIVE_IN_VAL,
1305
- "VQVAE2_active": True,
1306
- "reasoning_engine_active": True,
1307
- },
1308
- "xeon_status": XEON_STATUS,
1309
- "fp16_benchmark": FP16_BENCH,
1310
- "training": {
1311
- "duration_s": float(train_duration),
1312
- "n_steps": int(step),
1313
- "n_epochs": EPOCHS,
1314
- "n_phases": N_PHASES,
1315
- "samples_per_dataset_actual": samples_per_dataset_actual,
1316
- "total_samples_processed": sum(samples_per_dataset_actual.values()),
1317
- },
1318
- "summary": summary,
1319
- "kohonen_final": som_final,
1320
- "kls_state": kls_state,
1321
- "verification": verification,
1322
- "ewc_w8a8_benchmark_summary": ewc_w8a8_benchmark["analysis"],
1323
- "reasoning_eval_summary": reasoning_eval["summary"],
1324
- "module_analysis_summary": {
1325
- "n_removed_modules": sum(1 for v in module_analysis["removed_modules"].values() if v["status"] == "REMOVED"),
1326
- "n_removed_folders": sum(1 for v in module_analysis["removed_folders"].values() if v["status"] == "REMOVED"),
1327
- "n_active_modules": sum(1 for v in module_analysis["active_modules"].values() if v["activity"] == "active"),
1328
- },
1329
- "script_activity_summary": {
1330
- "n_scripts": len(script_activity),
1331
- "active_v65": sum(1 for v in script_activity.values() if v["classification"] == "active_v65"),
1332
- "active_v64": sum(1 for v in script_activity.values() if v["classification"] == "active_v64"),
1333
- "upload_utility": sum(1 for v in script_activity.values() if v["classification"] == "upload_utility"),
1334
- },
1335
- "math_analysis": {
1336
- "text_to_4d": "SVD: M @ V[:3].T -> centroid 3D + w = time_step/T_max (LINEAR)",
1337
- "bmu_distance": "||W - x||^2 (L2 squared in R^4)",
1338
- "neighborhood": "Lambda(d, sigma) = exp(-d^2 / (2*sigma^2))",
1339
- "weight_update": "dW = alpha * Lambda * (x - W)",
1340
- "sigma_decay": "sigma_t = sigma0 * exp(-t/1000)",
1341
- "alpha_decay": "alpha_t = alpha0 * exp(-t/2000)",
1342
- "ewc_only_dim4": "penalty = lambda * F * (W_w - W*_w)",
1343
- "vqvae2_loss": "L = recon_loss + vq_loss (commitment top + bottom + diversity)",
1344
- "vqvae2_ema": "EMA codebook update + dead code restart + Goose VQ",
1345
- "mtp_loss": "L = sum_k(alpha_k * L_k) - beta * H(alpha)",
1346
- "ewc_w8a8_dequant": "W_float = (W_int8 * scale) / smooth_scale; EWC uses W_float for (p-w*)^2",
1347
- },
1348
- "datasets_used": V65_DATASETS_TO_EXHAUST,
1349
- }
1350
-
1351
- REPORT_PATH.write_text(json.dumps(report, indent=2, ensure_ascii=False, default=str))
1352
- logger.info(f"[V6.5] Report saved: {REPORT_PATH}")
1353
-
1354
- metrics_full = {
1355
- "version": "V6.5-final-restructured",
1356
- "steps": monitor.steps,
1357
- "summary": summary,
1358
- "alerts": monitor.alerts,
1359
- }
1360
- METRICS_PATH.write_text(json.dumps(metrics_full, indent=2, ensure_ascii=False, default=str))
1361
- logger.info(f"[V6.5] Metrics saved: {METRICS_PATH}")
1362
-
1363
- # Print final summary
1364
- print("\n" + "=" * 80)
1365
- print("V6.5 — TREINO CONCLUÍDO")
1366
- print("=" * 80)
1367
- print(f" Steps : {step}")
1368
- print(f" Duration : {train_duration:.1f}s")
1369
- print(f" Total samples : {sum(samples_per_dataset_actual.values())}")
1370
- print(f" Final loss : {summary.get('final_loss', 0):.3f}")
1371
- print(f" Mean acc : {summary.get('mean_acc', 0):.3f}")
1372
- print(f" Sigma (start→end) : {summary.get('sigma_start', 0):.3f} → {summary.get('sigma_end', 0):.3f}")
1373
- print(f" VQ-VAE-2 mean loss : {summary.get('vqvae2_mean_total_loss', 0):.3f}")
1374
- print(f" VQ-VAE-2 final loss : {summary.get('vqvae2_final_total_loss', 0):.3f}")
1375
- evo = summary.get("evolution_phase1_to_phase2", {})
1376
- print(f" Evolution P1→P2:")
1377
- print(f" acc P1 mean : {evo.get('acc_phase1_mean', 0):.3f}")
1378
- print(f" acc P2 mean : {evo.get('acc_phase2_mean', 0):.3f}")
1379
- print(f" vqvae2 P1 mean : {evo.get('vqvae2_phase1_mean', 0):.3f}")
1380
- print(f" vqvae2 P2 mean : {evo.get('vqvae2_phase2_mean', 0):.3f}")
1381
- print(f" RSS max : {summary.get('rss_max_mb', 0):.0f}MB")
1382
- print(f" Alerts : {summary.get('n_alerts', 0)}")
1383
- print(f" Hypothesis trained : {kls.classifier_trained}")
1384
- print(f" EWC reference set : {som_final['has_ewc_reference']}")
1385
- print(f" VQ-VAE-2 calls : {kls_state['kls']['vqvae2_n_calls']}")
1386
- print(f" Reasoning n_history : {reasoning_eval['summary']['reasoning_engine_n_history']}")
1387
- print(f" Verification : {verification['n_pass']}/{verification['n_pass'] + verification['n_fail']} PASS")
1388
- print("=" * 80)
1389
- print(f"\n Report : {REPORT_PATH}")
1390
- print(f" Metrics : {METRICS_PATH}")
1391
- print(f" Module analysis : {MODULE_ANALYSIS_PATH}")
1392
- print(f" Script activity : {SCRIPT_ACTIVITY_PATH}")
1393
- print(f" EWC+W8A8 bench : {EWC_W8A8_BENCH_PATH}")
1394
- print(f" Reasoning eval : {REASONING_EVAL_PATH}\n")
1395
-
1396
- return 0
1397
-
1398
-
1399
- if __name__ == "__main__":
1400
- sys.exit(main())