PowerMachine commited on
Commit
ea330dc
·
verified ·
1 Parent(s): a95c6be

Delete tests/test_50_samples.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. tests/test_50_samples.py +0 -832
tests/test_50_samples.py DELETED
@@ -1,832 +0,0 @@
1
- """test_50_samples.py — Teste de 50 amostras para erros lógicos ou falhas.
2
-
3
- Executa o pipeline completo:
4
- 1. Inicializa xeon_runtime + memory_optimizer
5
- 2. Treina BBPE tokenizer em corpus sintético
6
- 3. Cria dataset streaming multimodal (50 amostras)
7
- 4. Instancia modelo multimodal + generator + verifier + anti-hallucination
8
- 5. Executa treinamento cooperativo com:
9
- - Synergy search (N tentativas)
10
- - Hypothesis controller (ativa em punições)
11
- - Auto-learner (ajuste dinâmico de LR + spectral norm)
12
- 6. Executa inferência com sampling
13
- 7. Avalia perplexidade
14
- 8. Reporta erros lógicos/falhas encontradas
15
-
16
- Usage:
17
- python -m cnn_bigru.tests.test_50_samples
18
- """
19
- from __future__ import annotations
20
-
21
- import logging
22
- import os
23
- import sys
24
- import time
25
- import traceback
26
- from pathlib import Path
27
- from typing import Dict, List
28
-
29
- # Setup paths (must come before torch import for xeon_runtime)
30
- PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
31
- sys.path.insert(0, str(PROJECT_ROOT))
32
-
33
- # Ativa Xeon runtime ANTES de importar torch
34
- from cnn_bigru.utils.xeon_runtime import optimize_xeon_environment
35
- N_CORES = optimize_xeon_environment()
36
-
37
- import numpy as np
38
- import torch
39
- from torch.utils.data import DataLoader
40
-
41
- from cnn_bigru.tokenizer.bbpe_tokenizer import BBPETokenizer
42
- from cnn_bigru.data.streaming_dataset import (
43
- MultimodalStreamingDataset,
44
- collate_multimodal,
45
- )
46
- from cnn_bigru.models.multimodal_model import MultimodalCNNBiGRU
47
- from cnn_bigru.models.generator_verifier import (
48
- GeneratorCNNBiGRU,
49
- VerifierCNNBiGRU,
50
- AntiHallucinationLayer,
51
- )
52
- from cnn_bigru.models.rope import RotaryPositionEmbedding
53
- from cnn_bigru.models.transformer_block import (
54
- TransformerBlockConfig,
55
- CausalSelfAttention,
56
- TransformerBlock,
57
- TransformerDecoderStack,
58
- )
59
- from cnn_bigru.models.context_window import (
60
- ContextWindowConfig,
61
- ContextWindowManager,
62
- KVCache,
63
- )
64
- from cnn_bigru.utils.ewc import EWCConfig, EWCState
65
- from cnn_bigru.losses.losses import LossConfig, MultiLoss
66
- from cnn_bigru.training.trainer import TrainerConfig, CooperativeTrainer
67
- from cnn_bigru.training.auto_learner import (
68
- AutoLearnConfig,
69
- orthogonal_init_model,
70
- )
71
- from cnn_bigru.training.hypothesis_controller import (
72
- HypothesisConfig,
73
- HypothesisController,
74
- )
75
- from cnn_bigru.inference.inference import (
76
- generate_with_sampling,
77
- evaluate_perplexity,
78
- make_default_context_window,
79
- )
80
- from cnn_bigru.utils.memory_optimizer import MemoryOptimizer
81
-
82
- logging.basicConfig(
83
- level=logging.INFO,
84
- format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
85
- datefmt="%H:%M:%S",
86
- )
87
- logger = logging.getLogger("test_50")
88
-
89
-
90
- # ============================================================================
91
- # Corpus para treino do tokenizer
92
- # ============================================================================
93
-
94
- CORPUS_TEXTS = [
95
- "o modelo cooperativo cnn-bigru combina fluxos paralelos",
96
- "a atencao cruzada troca informacoes entre redes a e b",
97
- "gru bidirecional captura dependencias temporais em ambas direcoes",
98
- "a porta de atenuacao controla o fluxo de informacao entre celulas",
99
- "a camada anti-alucinacao usa logica fuzzy de lukasiewicz",
100
- "o verificador classifica passos com sigmoid binaria",
101
- "penalidades linear e exponencial reduzem o erro grave",
102
- "ajuste dinamico de lr estabiliza o treinamento cooperativo",
103
- "normalizacao espectral limita a norma dos pesos",
104
- "inicializacao ortogonal estabiliza matrizes recorrentes",
105
- "o gerador decodificador usa atencao bahdanau sobre o encoder",
106
- "a fusao multimodal combina texto imagem e audio",
107
- "hipoteses sao ativadas quando o verificador pune o passo",
108
- "synergy search tenta n configuracoes e escolhe a melhor",
109
- "a perplexidade mede a confusao do modelo na previsao",
110
- "top-k e top-p filtram a distribuicao de probabilidade",
111
- "temperature ajusta a entropia das previsoes do modelo",
112
- "presence penalty pune tokens ja aparecidos na geracao",
113
- "frequency penalty pune proporcionalmente a frequencia do token",
114
- "streaming dataset carrega amostras sem materializacao completa",
115
- "byte bpe tokeniza qualquer string utf-8 sem unk",
116
- "o otimizador adamw combina momentum e weight decay",
117
- "gradient clipping previne explosao de gradiente",
118
- "amp reduz vram com precisao mista bfloat16",
119
- "memory optimizer limpa cache entre batches",
120
- ] * 4 # ~100 amostras para treino BBPE
121
-
122
-
123
- def step_01_init_runtime() -> Dict:
124
- """Inicializa runtime e reporta configurações."""
125
- logger.info("=" * 70)
126
- logger.info("PASSO 1: Inicialização do runtime Xeon + memory optimizer")
127
- logger.info("=" * 70)
128
- from cnn_bigru.utils.xeon_runtime import get_runtime_info
129
- info = get_runtime_info()
130
- for k, v in info.items():
131
- logger.info(" %s: %s", k, v)
132
- mem = MemoryOptimizer(enable_amp=False)
133
- mem.configure()
134
- mem_info = mem.get_memory_mb()
135
- logger.info(" Memória: %s", mem_info)
136
- return {"runtime": info, "memory": mem_info}
137
-
138
-
139
- def step_02_train_tokenizer() -> BBPETokenizer:
140
- """Treina BBPE tokenizer em corpus sintético."""
141
- logger.info("=" * 70)
142
- logger.info("PASSO 2: Treino do BBPE tokenizer")
143
- logger.info("=" * 70)
144
- t0 = time.time()
145
- tok = BBPETokenizer.train_from_texts(
146
- CORPUS_TEXTS,
147
- vocab_size=2000,
148
- min_frequency=1,
149
- )
150
- elapsed = time.time() - t0
151
- logger.info(" Vocab size: %d", tok.vocab_size)
152
- logger.info(" BOS/PAD/EOS IDs: %d/%d/%d", tok.bos_id, tok.pad_id, tok.eos_id)
153
- logger.info(" Tempo: %.2fs", elapsed)
154
-
155
- # Validação roundtrip
156
- test_strs = CORPUS_TEXTS[:5]
157
- roundtrip = tok.validate_roundtrip(test_strs)
158
- logger.info(" Roundtrip accuracy: %.2f%%", roundtrip * 100)
159
-
160
- if roundtrip < 0.8:
161
- logger.warning(" Roundtrip baixo — possível problema no tokenizer")
162
- return tok, roundtrip
163
-
164
-
165
- def step_03_create_dataset(tokenizer: BBPETokenizer, n_samples: int = 50) -> DataLoader:
166
- """Cria dataset streaming multimodal com N amostras."""
167
- logger.info("=" * 70)
168
- logger.info("PASSO 3: Criação do dataset streaming multimodal (%d amostras)", n_samples)
169
- logger.info("=" * 70)
170
-
171
- # Para teste determinístico, usamos fallback sintético
172
- dataset = MultimodalStreamingDataset(
173
- n_samples=n_samples,
174
- hf_datasets=[], # skip HF forçando sintético
175
- use_synthetic_fallback=True,
176
- seed=42,
177
- image_size=(28, 28, 1),
178
- audio_shape=(32, 40),
179
- )
180
-
181
- # Coleta todas as amostras em uma lista (para DataLoader iterável)
182
- samples = list(dataset)
183
- logger.info(" Amostras coletadas: %d", len(samples))
184
- assert len(samples) == n_samples, f"Esperado {n_samples}, obtido {len(samples)}"
185
-
186
- # Cria DataLoader com collate_fn
187
- from torch.utils.data import DataLoader
188
- loader = DataLoader(
189
- samples,
190
- batch_size=8,
191
- shuffle=False,
192
- collate_fn=lambda b: collate_multimodal(b, tokenizer, max_len=32),
193
- )
194
- logger.info(" DataLoader criado: batch_size=8, max_len=32")
195
- return loader
196
-
197
-
198
- def step_04_init_model(tokenizer: BBPETokenizer, device: str = "cpu") -> Dict:
199
- """Instancia todos os componentes do modelo."""
200
- logger.info("=" * 70)
201
- logger.info("PASSO 4: Instanciação dos modelos")
202
- logger.info("=" * 70)
203
- V = tokenizer.vocab_size
204
-
205
- # Modelo multimodal principal
206
- model = MultimodalCNNBiGRU(
207
- vocab_size=V,
208
- num_classes=3,
209
- embedding_dim=32,
210
- cnn_filters=32,
211
- gru_hidden=32,
212
- n_heads=4,
213
- dropout=0.1,
214
- pad_idx=tokenizer.pad_id,
215
- img_channels=1,
216
- img_hidden=16,
217
- img_out_dim=32,
218
- audio_freq=40,
219
- audio_hidden=16,
220
- audio_out_dim=32,
221
- fusion_dim=64,
222
- use_spectral_norm=False,
223
- )
224
-
225
- # Aplica inicialização ortogonal
226
- n_init = orthogonal_init_model(model)
227
- logger.info(" Modelo multimodal: %d params, %d camadas ortogonalizadas",
228
- sum(p.numel() for p in model.parameters()), n_init)
229
-
230
- # Generator (encoder + decoder)
231
- generator = GeneratorCNNBiGRU(
232
- vocab_size=V,
233
- embedding_dim=32,
234
- cnn_filters=32,
235
- gru_hidden=32,
236
- n_heads=4,
237
- dropout=0.1,
238
- pad_idx=tokenizer.pad_id,
239
- max_proof_len=16,
240
- )
241
- orthogonal_init_model(generator)
242
-
243
- # Verifier
244
- verifier = VerifierCNNBiGRU(
245
- vocab_size=V,
246
- embedding_dim=32,
247
- cnn_filters=32,
248
- gru_hidden=32,
249
- n_heads=4,
250
- dropout=0.1,
251
- pad_idx=tokenizer.pad_id,
252
- )
253
- orthogonal_init_model(verifier)
254
-
255
- # Anti-hallucination
256
- anti_hall = AntiHallucinationLayer(vocab_size=V, embed_dim=16)
257
-
258
- logger.info(" Generator params: %d", sum(p.numel() for p in generator.parameters()))
259
- logger.info(" Verifier params: %d", sum(p.numel() for p in verifier.parameters()))
260
- logger.info(" Anti-hall params: %d", sum(p.numel() for p in anti_hall.parameters()))
261
-
262
- return {
263
- "model": model,
264
- "generator": generator,
265
- "verifier": verifier,
266
- "anti_hall": anti_hall,
267
- }
268
-
269
-
270
- def step_05_train(
271
- models: Dict,
272
- tokenizer: BBPETokenizer,
273
- dataloader: DataLoader,
274
- device: str = "cpu",
275
- ewc_state: EWCState = None,
276
- ) -> Dict:
277
- """Executa treinamento cooperativo.
278
-
279
- NOVO v2.0: aceita ewc_state opcional para testar EWC.
280
- """
281
- logger.info("=" * 70)
282
- logger.info("PASSO 5: Treinamento cooperativo (synergy + hipóteses + auto-learn)")
283
- logger.info("=" * 70)
284
-
285
- cfg = TrainerConfig(
286
- num_epochs=2,
287
- max_batches_per_epoch=6,
288
- batch_size=8,
289
- n_synergy_attempts=3,
290
- use_synergy_search=True,
291
- use_hypotheses=True,
292
- n_hypotheses=4,
293
- loss_config=LossConfig(
294
- alpha=1.0, beta=0.5, gamma_loss=0.3, delta=0.01,
295
- lambda_penal=0.1, mu_exp_penal=0.05,
296
- gamma_exp=1.0, threshold_err=0.5,
297
- l2_reg=1e-5, use_curvature=True, curvature_eps=1e-3,
298
- ),
299
- auto_config=AutoLearnConfig(
300
- kappa_curv=0.01, grad_clip=1.0, spectral_radius=1.0,
301
- lr_min=1e-6, lr_max=1e-2,
302
- initial_lr_G=1e-3, initial_lr_V=1e-3,
303
- use_spectral_norm=True, apply_after_step=True,
304
- l2_reg=1e-5,
305
- ),
306
- ewc_config=ewc_state.config if ewc_state else None,
307
- device=device,
308
- log_every=1,
309
- use_verifier_real=True, # NOVO v2.0: usar verificador real
310
- use_generator=True,
311
- use_hypothesis_output=True,
312
- )
313
-
314
- trainer = CooperativeTrainer(
315
- model=models["model"],
316
- tokenizer=tokenizer,
317
- config=cfg,
318
- generator=models["generator"],
319
- verifier=models["verifier"],
320
- anti_hallucination=models["anti_hall"],
321
- ewc_state=ewc_state,
322
- )
323
-
324
- try:
325
- result = trainer.train(dataloader)
326
- logger.info(" Treinamento OK | final_loss=%.4f | final_ppl=%.2f | elapsed=%.1fs",
327
- result["final_loss"], result["final_ppl"], result["elapsed_s"])
328
- return result
329
- except Exception as e:
330
- logger.error(" Treinamento FALHOU: %s", e)
331
- logger.error(traceback.format_exc())
332
- raise
333
-
334
-
335
- def step_05b_test_new_modules(
336
- models: Dict,
337
- tokenizer: BBPETokenizer,
338
- device: str = "cpu",
339
- ) -> Dict:
340
- """Testa os novos módulos v2.0: EWC, Context Window, RoPE, TransformerBlock.
341
-
342
- NOVO v2.0: esta função exercita isoladamente cada novo módulo para garantir
343
- que estão funcionando e integráveis ao pipeline.
344
- """
345
- logger.info("=" * 70)
346
- logger.info("PASSO 5b: Teste dos novos módulos v2.0 (EWC, ContextWindow, RoPE, TransformerBlock)")
347
- logger.info("=" * 70)
348
-
349
- results = {"ewc": None, "context_window": None, "rope": None, "transformer": None}
350
-
351
- # ---------- EWC ----------
352
- try:
353
- logger.info(" [EWC] Testando EWCState...")
354
- ewc_cfg = EWCConfig(
355
- enabled=True,
356
- lambda_ewc=100.0,
357
- n_samples_fisher=5, # poucas amostras para teste rápido
358
- online_gamma=0.9,
359
- device=device,
360
- )
361
- ewc_state = EWCState(ewc_cfg)
362
- # Antes de consolidar: penalty deve ser 0
363
- pen0 = ewc_state.penalty(models["model"])
364
- logger.info(" Penalty antes de consolidar: %.6f", float(pen0))
365
- assert pen0.item() == 0.0, "EWC penalty deveria ser 0 antes de consolidar"
366
-
367
- # Simular forward_fn para cálculo de Fisher
368
- # IMPORTANTE: NÃO usar torch.no_grad() aqui — a EWC precisa de gradientes
369
- # para calcular a matriz de Fisher (grad^2 da log-verossimilhança)
370
- V = tokenizer.vocab_size
371
- B = 2
372
- T = 8
373
- ids_a = torch.randint(0, V, (B, T), device=device)
374
- ids_b = torch.randint(0, V, (B, T), device=device)
375
-
376
- def forward_fn(_idx=None):
377
- # Sem torch.no_grad() — a EWC interna ativa enable_grad()
378
- out = models["model"](ids_a, ids_b, mode="classify")
379
- return out["logits"]
380
-
381
- # Consolidar (calcula Fisher e armazena theta_star)
382
- ewc_state.consolidate(models["model"], forward_fn=forward_fn)
383
- assert ewc_state.num_tasks() == 1, f"Esperado 1 tarefa, obtido {ewc_state.num_tasks()}"
384
-
385
- # Penalty deve ser > 0 agora (parâmetros não mudaram desde consolidate, mas
386
- # a penalidade ainda assim é computada e deve ser >= 0)
387
- pen1 = ewc_state.penalty(models["model"])
388
- logger.info(" Penalty após consolidar: %.6f", float(pen1.detach()))
389
- assert pen1.item() >= 0.0, "EWC penalty deve ser >= 0"
390
-
391
- results["ewc"] = {
392
- "ok": True,
393
- "penalty_before": float(pen0),
394
- "penalty_after": float(pen1),
395
- "num_tasks": ewc_state.num_tasks(),
396
- }
397
- logger.info(" [EWC] ✓ OK")
398
-
399
- # Guardar para uso posterior no trainer
400
- results["ewc_state"] = ewc_state
401
- except Exception as e:
402
- logger.error(" [EWC] ✗ FALHOU: %s", e)
403
- logger.error(traceback.format_exc())
404
- results["ewc"] = {"ok": False, "error": str(e)}
405
-
406
- # ---------- Context Window ----------
407
- try:
408
- logger.info(" [ContextWindow] Testando ContextWindowManager + KVCache...")
409
- cw = make_default_context_window(
410
- max_window=32,
411
- n_sink=2,
412
- embed_dim=64,
413
- n_heads=4,
414
- n_layers=2,
415
- device=device,
416
- )
417
- # Inicializar cache
418
- cache = cw.init_cache(batch_size=1, device=torch.device(device))
419
- assert cache is not None
420
- assert cache.get_seq_len() == 0
421
-
422
- # Append tokens
423
- tokens1 = torch.tensor([[1, 2, 3, 4, 5]], dtype=torch.long, device=device)
424
- window = cw.append_tokens(tokens1)
425
- assert window.size(1) == 5
426
- assert cw.kv_cache.get_seq_len() == 0 # cache só é populado quando chamamos KVCache.update
427
-
428
- # Simular update do cache KV
429
- dummy_k = torch.randn(1, 4, 5, 16, device=device) # [B, n_heads, T, head_dim]
430
- dummy_v = torch.randn(1, 4, 5, 16, device=device)
431
- new_k, new_v = cache.update(0, dummy_k, dummy_v)
432
- assert cache.get_seq_len() == 5
433
-
434
- # Adicionar mais tokens para forçar eviction
435
- tokens2 = torch.tensor([[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]],
436
- dtype=torch.long, device=device)
437
- cw.append_tokens(tokens2)
438
- # Total histórico: 5 + 15 = 20, mas max_window=32, então sem eviction ainda
439
- assert cw.token_history and sum(t.size(1) for t in cw.token_history) == 20
440
-
441
- # Forçar eviction adicionando mais tokens
442
- tokens3 = torch.tensor([[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]],
443
- dtype=torch.long, device=device)
444
- cw.append_tokens(tokens3) # total = 34 > 32 = max_window
445
- cw.evict_cache()
446
-
447
- info = cw.get_info()
448
- logger.info(" Context window info: %s", info)
449
- results["context_window"] = {"ok": True, "info": info}
450
- logger.info(" [ContextWindow] ✓ OK")
451
- except Exception as e:
452
- logger.error(" [ContextWindow] ✗ FALHOU: %s", e)
453
- logger.error(traceback.format_exc())
454
- results["context_window"] = {"ok": False, "error": str(e)}
455
-
456
- # ---------- RoPE ----------
457
- try:
458
- logger.info(" [RoPE] Testando RotaryPositionEmbedding...")
459
- head_dim = 32
460
- max_seq = 16
461
- rope = RotaryPositionEmbedding(head_dim=head_dim, max_seq_len=max_seq).to(device)
462
- # x: [B, n_heads, T, head_dim]
463
- x = torch.randn(2, 4, 8, head_dim, device=device)
464
- x_rot = rope(x)
465
- assert x_rot.shape == x.shape, f"Shape mismatch: {x_rot.shape} vs {x.shape}"
466
- # Verificar que RoPE preserva a norma (é uma rotação)
467
- norm_before = x.norm(dim=-1)
468
- norm_after = x_rot.norm(dim=-1)
469
- diff = (norm_before - norm_after).abs().max().item()
470
- logger.info(" Norma preservada (diff=%.6f)", diff)
471
- assert diff < 1e-4, f"RoPE não preservou norma (diff={diff})"
472
-
473
- # Testar com offset (para geração autoregressiva)
474
- x_rot2 = rope(x, offset=10)
475
- assert x_rot2.shape == x.shape
476
-
477
- results["rope"] = {"ok": True, "norm_diff": diff}
478
- logger.info(" [RoPE] ✓ OK")
479
- except Exception as e:
480
- logger.error(" [RoPE] ✗ FALHOU: %s", e)
481
- logger.error(traceback.format_exc())
482
- results["rope"] = {"ok": False, "error": str(e)}
483
-
484
- # ---------- TransformerBlock ----------
485
- try:
486
- logger.info(" [TransformerBlock] Testando CausalSelfAttention + TransformerBlock...")
487
- embed_dim = 64
488
- n_heads = 4
489
- n_layers = 2
490
- V = tokenizer.vocab_size
491
-
492
- block_config = TransformerBlockConfig(
493
- embed_dim=embed_dim,
494
- n_heads=n_heads,
495
- ff_dim=4 * embed_dim,
496
- dropout=0.1,
497
- use_rope=True,
498
- max_seq_len=32,
499
- )
500
- block = TransformerBlock(block_config).to(device)
501
-
502
- # Forward sem cache
503
- x = torch.randn(2, 8, embed_dim, device=device)
504
- out, k, v = block(x)
505
- assert out.shape == x.shape, f"Output shape: {out.shape} vs {x.shape}"
506
- assert k.shape == (2, n_heads, 8, embed_dim // n_heads)
507
- assert v.shape == (2, n_heads, 8, embed_dim // n_heads)
508
-
509
- # Forward com padding mask
510
- mask = torch.tensor([[1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 0]],
511
- dtype=torch.float, device=device)
512
- out2, k2, v2 = block(x, padding_mask=mask)
513
- assert out2.shape == x.shape
514
-
515
- # Forward com cache KV
516
- cache_k = None
517
- cache_v = None
518
- for t in range(3):
519
- x_t = torch.randn(2, 1, embed_dim, device=device)
520
- out_t, cache_k, cache_v = block(
521
- x_t, kv_cache_k=cache_k, kv_cache_v=cache_v, position_offset=t
522
- )
523
- assert out_t.shape == (2, 1, embed_dim)
524
- assert cache_k.size(2) == t + 1, f"Cache len: {cache_k.size(2)}, esperado {t+1}"
525
- logger.info(" Cache KV após 3 steps: %d tokens", cache_k.size(2))
526
-
527
- # Testar TransformerDecoderStack completo
528
- decoder = TransformerDecoderStack(
529
- vocab_size=V,
530
- embed_dim=embed_dim,
531
- n_heads=n_heads,
532
- n_layers=n_layers,
533
- max_seq_len=32,
534
- use_rope=True,
535
- pad_id=tokenizer.pad_id,
536
- weight_tying=True,
537
- ).to(device)
538
-
539
- idx = torch.randint(0, V, (2, 8), device=device)
540
- logits, new_caches = decoder(idx)
541
- assert logits.shape == (2, 8, V), f"Logits shape: {logits.shape}"
542
- assert len(new_caches) == n_layers
543
-
544
- # Verificar weight tying
545
- assert decoder.lm_head.weight is decoder.token_embedding.weight
546
-
547
- results["transformer"] = {
548
- "ok": True,
549
- "block_output_shape": list(out.shape),
550
- "decoder_logits_shape": list(logits.shape),
551
- "weight_tying": True,
552
- }
553
- logger.info(" [TransformerBlock] ✓ OK")
554
- except Exception as e:
555
- logger.error(" [TransformerBlock] ✗ FALHOU: %s", e)
556
- logger.error(traceback.format_exc())
557
- results["transformer"] = {"ok": False, "error": str(e)}
558
-
559
- return results
560
-
561
-
562
- def step_06_inference(
563
- model: MultimodalCNNBiGRU,
564
- tokenizer: BBPETokenizer,
565
- device: str = "cpu",
566
- generator: GeneratorCNNBiGRU = None,
567
- use_context_window: bool = True,
568
- ) -> Dict:
569
- """Executa inferência com sampling.
570
-
571
- NOVO v2.0: usa generator (se disponível) para geração autoregressiva REAL,
572
- e integra context_window para suporte a sequências longas.
573
- """
574
- logger.info("=" * 70)
575
- logger.info("PASSO 6: Inferência com Temperatura + Top-K + Top-P + Penalidades")
576
- if generator is not None:
577
- logger.info(" (usando GeneratorCNNBiGRU para geração autoregressiva REAL)")
578
- else:
579
- logger.info(" (sem generator — usando fallback single-step)")
580
- logger.info("=" * 70)
581
-
582
- prompts = [
583
- ("o modelo coopera entre", "fluxos paralelos"),
584
- ("atencao cruzada troca", "informacoes entre redes"),
585
- ("gru bidirecional captura", "dependencias temporais"),
586
- ]
587
-
588
- # Inicializar context window se solicitado
589
- cw = None
590
- if use_context_window:
591
- try:
592
- cw = make_default_context_window(
593
- max_window=64,
594
- n_sink=2,
595
- embed_dim=32,
596
- n_heads=4,
597
- n_layers=2,
598
- device=device,
599
- )
600
- logger.info(" Context window ativo (max_window=64, sink=2)")
601
- except Exception as e:
602
- logger.warning(" Context window falhou ao inicializar: %s — desativando", e)
603
- cw = None
604
-
605
- results = []
606
- for prompt_a, prompt_b in prompts:
607
- try:
608
- result = generate_with_sampling(
609
- model=model,
610
- tokenizer=tokenizer,
611
- prompt_a=prompt_a,
612
- prompt_b=prompt_b,
613
- max_new_tokens=16,
614
- temperature=0.7,
615
- top_k=20,
616
- top_p=0.9,
617
- presence_penalty=0.3,
618
- frequency_penalty=0.3,
619
- device=device,
620
- generator=generator,
621
- context_window=cw,
622
- )
623
- logger.info(" Prompt A: %s", prompt_a)
624
- logger.info(" Prompt B: %s", prompt_b)
625
- logger.info(" Gerado: %s", result["text"][:100])
626
- logger.info(" Metrics: %s", result["metrics"])
627
- results.append({"prompt_a": prompt_a, "prompt_b": prompt_b, **result})
628
- except Exception as e:
629
- logger.error(" Inferência falhou para (%s, %s): %s", prompt_a, prompt_b, e)
630
- logger.error(traceback.format_exc())
631
- results.append({"prompt_a": prompt_a, "prompt_b": prompt_b, "error": str(e)})
632
-
633
- return {"results": results}
634
-
635
-
636
- def step_07_eval_ppl(
637
- model: MultimodalCNNBiGRU,
638
- tokenizer: BBPETokenizer,
639
- dataloader: DataLoader,
640
- device: str = "cpu",
641
- generator: GeneratorCNNBiGRU = None,
642
- ) -> Dict:
643
- """Avalia perplexidade.
644
-
645
- NOVO v2.0: usa generator (se disponível) para PPL real baseado em
646
- geração autoregressiva com teacher forcing.
647
- """
648
- logger.info("=" * 70)
649
- logger.info("PASSO 7: Avaliação de Perplexidade (PPL)")
650
- if generator is not None:
651
- logger.info(" (usando GeneratorCNNBiGRU com teacher forcing)")
652
- else:
653
- logger.info(" (sem generator — usando fallback single-step)")
654
- logger.info("=" * 70)
655
- try:
656
- result = evaluate_perplexity(
657
- model=model,
658
- dataloader=dataloader,
659
- tokenizer=tokenizer,
660
- device=device,
661
- max_batches=5,
662
- generator=generator,
663
- )
664
- logger.info(" Loss: %.4f | PPL: %.2f | batches: %d | used_generator: %s",
665
- result["loss"], result["ppl"], result["n_batches"],
666
- result.get("used_generator", False))
667
- return result
668
- except Exception as e:
669
- logger.error(" PPL falhou: %s", e)
670
- logger.error(traceback.format_exc())
671
- return {"error": str(e)}
672
-
673
-
674
- def step_08_report_errors(errors: List[str], warnings: List[str]) -> None:
675
- """Reporta erros lógicos ou falhas encontradas."""
676
- logger.info("=" * 70)
677
- logger.info("PASSO 8: Relatório de erros lógicos ou falhas")
678
- logger.info("=" * 70)
679
-
680
- if not errors:
681
- logger.info(" ✓ NENHUM ERRO CRÍTICO encontrado")
682
- else:
683
- logger.error(" ✗ %d ERRO(S) CRÍTICO(S) encontrado(s):", len(errors))
684
- for e in errors:
685
- logger.error(" - %s", e)
686
-
687
- if not warnings:
688
- logger.info(" ✓ Nenhum warning relevante")
689
- else:
690
- logger.warning(" ⚠ %d warning(s):", len(warnings))
691
- for w in warnings:
692
- logger.warning(" - %s", w)
693
-
694
- logger.info("=" * 70)
695
-
696
-
697
- def main():
698
- """Executa o teste completo de 50 amostras."""
699
- logger.info("INICIANDO TESTE DE 50 AMOSTRAS — CNN-BiGRU MULTIMODAL COOPERATIVO")
700
- logger.info("Project root: %s", PROJECT_ROOT)
701
- logger.info("Device: %s", "cpu")
702
- logger.info("Cores: %d", N_CORES)
703
-
704
- errors: List[str] = []
705
- warnings: List[str] = []
706
-
707
- try:
708
- # Step 1
709
- step_01_init_runtime()
710
-
711
- # Step 2
712
- tokenizer, roundtrip = step_02_train_tokenizer()
713
- if roundtrip < 0.8:
714
- warnings.append(f"BBPE roundtrip accuracy = {roundtrip:.2%} (esperado >= 80%)")
715
-
716
- # Step 3
717
- dataloader = step_03_create_dataset(tokenizer, n_samples=50)
718
-
719
- # Step 4
720
- models = step_04_init_model(tokenizer, device="cpu")
721
-
722
- # Verificação: forward pass simples antes do treino
723
- try:
724
- batch = next(iter(dataloader))
725
- with torch.no_grad():
726
- out = models["model"](
727
- batch["input_ids_a"], batch["input_ids_b"],
728
- images=batch["images"], audios=batch["audios"],
729
- mode="classify",
730
- )
731
- logger.info(" Forward pass OK | logits shape: %s", out["logits"].shape)
732
- assert out["logits"].shape == (batch["input_ids_a"].size(0), 3), \
733
- f"Shape inesperado: {out['logits'].shape}"
734
- except Exception as e:
735
- errors.append(f"Forward pass inicial falhou: {e}")
736
- logger.error(traceback.format_exc())
737
-
738
- # Step 5b: Testar novos módulos v2.0 (EWC, Context Window, RoPE, TransformerBlock)
739
- ewc_state = None
740
- if not errors:
741
- try:
742
- new_modules_result = step_05b_test_new_modules(models, tokenizer, device="cpu")
743
- # Verificar se algum módulo falhou
744
- for mod_name, mod_res in new_modules_result.items():
745
- if mod_name == "ewc_state":
746
- continue
747
- if isinstance(mod_res, dict) and not mod_res.get("ok", True):
748
- errors.append(f"Módulo {mod_name} falhou: {mod_res.get('error', 'unknown')}")
749
- # Guardar EWC state para usar no treino
750
- ewc_state = new_modules_result.get("ewc_state")
751
- if ewc_state is None:
752
- warnings.append("EWC state não foi criado — EWC não será testado no treino")
753
- except Exception as e:
754
- errors.append(f"Teste de novos módulos falhou: {e}")
755
- logger.error(traceback.format_exc())
756
-
757
- # Step 5: Treinamento (com EWC se disponível)
758
- if not errors:
759
- try:
760
- train_result = step_05_train(models, tokenizer, dataloader, device="cpu",
761
- ewc_state=ewc_state)
762
- except Exception as e:
763
- errors.append(f"Treinamento falhou: {e}")
764
- train_result = None
765
- else:
766
- train_result = None
767
-
768
- # Step 6: Inferência com generator + context window
769
- if not errors:
770
- try:
771
- step_06_inference(
772
- models["model"], tokenizer, device="cpu",
773
- generator=models.get("generator"),
774
- use_context_window=True,
775
- )
776
- except Exception as e:
777
- errors.append(f"Inferência falhou: {e}")
778
- logger.error(traceback.format_exc())
779
-
780
- # Step 7: PPL com generator
781
- if not errors:
782
- try:
783
- step_07_eval_ppl(
784
- models["model"], tokenizer, dataloader, device="cpu",
785
- generator=models.get("generator"),
786
- )
787
- except Exception as e:
788
- warnings.append(f"PPL falhou (não crítico): {e}")
789
-
790
- # Step 8
791
- step_08_report_errors(errors, warnings)
792
-
793
- # Resumo final
794
- logger.info("=" * 70)
795
- logger.info("RESUMO FINAL DO TESTE DE 50 AMOSTRAS")
796
- logger.info("=" * 70)
797
- logger.info(" Amostras processadas: 50")
798
- logger.info(" Erros críticos: %d", len(errors))
799
- logger.info(" Warnings: %d", len(warnings))
800
- if train_result:
801
- logger.info(" Loss final: %.4f", train_result["final_loss"])
802
- logger.info(" PPL final: %.2f", train_result["final_ppl"])
803
- logger.info(" Tempo: %.1fs", train_result["elapsed_s"])
804
- logger.info(" Synergy attempts: %d", len(train_result.get("synergy_history", [])))
805
- logger.info(" Hypothesis activations: %d",
806
- sum(h.get("hypothesis_activations", 0) for h in train_result.get("history", [])))
807
-
808
- if errors:
809
- logger.error(" STATUS: FALHA — %d erro(s)", len(errors))
810
- return 1
811
- else:
812
- logger.info(" STATUS: SUCESSO")
813
- return 0
814
-
815
- except Exception as e:
816
- logger.error("ERRO FATAL: %s", e)
817
- logger.error(traceback.format_exc())
818
- return 2
819
-
820
-
821
- if __name__ == "__main__":
822
- try:
823
- rc = main()
824
- except SystemExit:
825
- raise
826
- except Exception as e:
827
- logger.error("Unhandled exception: %s", e)
828
- rc = 2
829
- # Evita o "Fatal Python error: PyGILState_Release" no shutdown causado
830
- # por threads do tokenizers/datasets que ainda estão ativas.
831
- import os
832
- os._exit(rc)