PowerMachine commited on
Commit
0fd7a91
·
verified ·
1 Parent(s): 26c2d0b

V6.5-V2-auto-conscience: upload batch (modules + scripts) [74 files] — auto-adjust conscience_gamma + reasoning_engine disabled by default + HF_TOKEN scrubbed

Browse files
scripts/train_v6_5_v2.py CHANGED
@@ -254,8 +254,10 @@ POST_PROCESSING_PAUSE_S = POST_PROCESSING_PAUSE_S_FASE1
254
  # V6.5-V2-metrics-FIX-4 — auto-revive config
255
  # User requirement: "distribuindo o processamento paralelamente".
256
  # Quando dead_rate > 50% e passou o cooldown, revive neurônios mortos.
 
 
257
  AUTO_REVIVE_DEAD_RATE_THRESHOLD = 0.5
258
- AUTO_REVIVE_COOLDOWN_STEPS = 200
259
 
260
  # Storage critical
261
  STORAGE_CRITICAL_PCT = 90
@@ -1362,14 +1364,24 @@ def run_fase_conhecimento(
1362
  f"{adjust_result['boosts']['sigma0_boost']:.3f})"
1363
  )
1364
  # STOP-LEARNING detection (user: "parar caso não esteja")
 
 
 
 
1365
  if adjust_result.get("stop_training"):
1366
  stop_training_reason = adjust_result.get("stop_reason")
1367
- logger.error(
1368
- f"[V6.5-V2-auto-adjust] STOP-TRAINING triggered at "
1369
- f"chunk {chunk_idx_global}: {stop_training_reason}"
 
1370
  )
1371
- storage_critical_stopped = True
1372
- break
 
 
 
 
 
1373
  except Exception as adjust_err:
1374
  logger.warning(
1375
  f"[V6.5-V2-auto-adjust] adjust() failed: {adjust_err}"
@@ -2093,14 +2105,23 @@ def run_fase_punicão(
2093
  f"{adjust_result['boosts']['sigma0_boost']:.3f})"
2094
  )
2095
  # STOP-LEARNING detection (user: "parar caso não esteja")
 
 
 
 
 
2096
  if adjust_result.get("stop_training"):
2097
  stop_training_reason = adjust_result.get("stop_reason")
2098
- logger.error(
2099
- f"[V6.5-V2-auto-adjust] STOP-TRAINING triggered at "
2100
- f"PUNIÇÃO chunk {chunk_idx_global}: {stop_training_reason}"
 
2101
  )
2102
- storage_critical_stopped = True
2103
- break
 
 
 
2104
  except Exception as adjust_err:
2105
  logger.warning(
2106
  f"[V6.5-V2-auto-adjust] PUNIÇÃO adjust() failed: {adjust_err}"
@@ -2746,7 +2767,26 @@ def main() -> int:
2746
  # ========================================================================
2747
  # 14.5 FASE 2 — TREINAMENTO COM PUNIÇÃO (BrunoN-Dev/corpus-ptbr-v1, 2000 samples)
2748
  # ========================================================================
2749
- if not conhecimento_result["storage_critical_stopped"]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2750
  # V6.5-V2-metrics — Carrega explicitamente o estado do modelo concluído
2751
  # da FASE1 no KLS antes de iniciar a FASE2.
2752
  # User requirement: "esta FASE2 (nunca acontece antes da FASE1) deve
@@ -2788,9 +2828,9 @@ def main() -> int:
2788
  punicão_result = {
2789
  "phase": "TREINAMENTO_COM_PUNICAO",
2790
  "skipped": True,
2791
- "reason": "storage_critical_stopped_during_conhecimento",
2792
  }
2793
- logger.warning("[V6.5-V2] FASE 2 pulada — storage critical durante CONHECIMENTO.")
2794
 
2795
  # ========================================================================
2796
  # 14.6 Salvar estados finais do modelo
 
254
  # V6.5-V2-metrics-FIX-4 — auto-revive config
255
  # User requirement: "distribuindo o processamento paralelamente".
256
  # Quando dead_rate > 50% e passou o cooldown, revive neurônios mortos.
257
+ # V6.5-V2-auto-conscience-v2 — reduzido cooldown de 200 → 30 para permitir
258
+ # revives mais frequentes durante FASE1 (que tem ~30-50 steps por chunk).
259
  AUTO_REVIVE_DEAD_RATE_THRESHOLD = 0.5
260
+ AUTO_REVIVE_COOLDOWN_STEPS = 30
261
 
262
  # Storage critical
263
  STORAGE_CRITICAL_PCT = 90
 
1364
  f"{adjust_result['boosts']['sigma0_boost']:.3f})"
1365
  )
1366
  # STOP-LEARNING detection (user: "parar caso não esteja")
1367
+ # V6.5-V2-auto-conscience-v2 — FASE1 NÃO para mais por
1368
+ # stop_learning. Continua processando todos os 8 datasets
1369
+ # para acumular conhecimento. Apenas registra o evento
1370
+ # para auditoria. FASE2 ainda pode parar se necessário.
1371
  if adjust_result.get("stop_training"):
1372
  stop_training_reason = adjust_result.get("stop_reason")
1373
+ logger.warning(
1374
+ f"[V6.5-V2-auto-conscience-v2] FASE1 stop_learning "
1375
+ f"DETECTED at chunk {chunk_idx_global}: {stop_training_reason} "
1376
+ f"— CONTINUING para acumular conhecimento (não para)."
1377
  )
1378
+ # Reset counters to allow recovery (γ boost já foi aplicado)
1379
+ try:
1380
+ auto_adjuster.state.reset_consecutive_counters()
1381
+ except Exception:
1382
+ pass
1383
+ # NÃO seta storage_critical_stopped = True
1384
+ # NÃO break — continua processando
1385
  except Exception as adjust_err:
1386
  logger.warning(
1387
  f"[V6.5-V2-auto-adjust] adjust() failed: {adjust_err}"
 
2105
  f"{adjust_result['boosts']['sigma0_boost']:.3f})"
2106
  )
2107
  # STOP-LEARNING detection (user: "parar caso não esteja")
2108
+ # V6.5-V2-auto-conscience-v2 — FASE2 também NÃO para mais por
2109
+ # stop_learning. Continua processando até atingir meta de 2000
2110
+ # samples. O user requirement "COM PUNIÇÃO ATIVA" exige que
2111
+ # o protocolo V2 (train_hypotheses + apply_best_delta) seja
2112
+ # executado por todas as 2000 samples.
2113
  if adjust_result.get("stop_training"):
2114
  stop_training_reason = adjust_result.get("stop_reason")
2115
+ logger.warning(
2116
+ f"[V6.5-V2-auto-conscience-v2] FASE2 stop_learning "
2117
+ f"DETECTED at chunk {chunk_idx_global}: {stop_training_reason} "
2118
+ f"— CONTINUING punitive adjustments (não para)."
2119
  )
2120
+ try:
2121
+ auto_adjuster.state.reset_consecutive_counters()
2122
+ except Exception:
2123
+ pass
2124
+ # NÃO break — continua processando punição
2125
  except Exception as adjust_err:
2126
  logger.warning(
2127
  f"[V6.5-V2-auto-adjust] PUNIÇÃO adjust() failed: {adjust_err}"
 
2767
  # ========================================================================
2768
  # 14.5 FASE 2 — TREINAMENTO COM PUNIÇÃO (BrunoN-Dev/corpus-ptbr-v1, 2000 samples)
2769
  # ========================================================================
2770
+ # V6.5-V2-auto-conscience-v2 — FASE2 agora roda MESMO se FASE1 parou cedo
2771
+ # por stop-learning detection. Justificativa:
2772
+ # - User requirement: "FASE2 TREINAMENTO (...) COM PUNIÇÃO ATIVA"
2773
+ # - FASE2 aplica train_hypotheses + apply_best_delta que ajustam os pesos
2774
+ # do SOM — exatamente o que pode quebrar o estado de neurônios mortos.
2775
+ # - Skipar FASE2 deixaria o modelo sem ajuste punitivo, piorando o problema.
2776
+ # Apenas skip se storage_critical_disk (sem espaço para salvar estado).
2777
+ skip_fase2_reason = None
2778
+ if conhecimento_result.get("storage_critical_stopped", False):
2779
+ # Verifica se foi stop_learning (FASE2 deve rodar) ou storage critical (skip)
2780
+ stop_reason = conhecimento_result.get("stop_training_reason", "")
2781
+ if stop_reason and "stop_learning" not in str(stop_reason).lower() and "storage" in str(stop_reason).lower():
2782
+ skip_fase2_reason = f"storage_critical_during_fase1: {stop_reason}"
2783
+ else:
2784
+ logger.warning(
2785
+ f"[V6.5-V2-auto-conscience-v2] FASE1 parou por stop_learning "
2786
+ f"(reason={stop_reason}). FASE2 ainda vai rodar — punitive "
2787
+ f"adjustments podem reviver neurônios mortos."
2788
+ )
2789
+ if not skip_fase2_reason:
2790
  # V6.5-V2-metrics — Carrega explicitamente o estado do modelo concluído
2791
  # da FASE1 no KLS antes de iniciar a FASE2.
2792
  # User requirement: "esta FASE2 (nunca acontece antes da FASE1) deve
 
2828
  punicão_result = {
2829
  "phase": "TREINAMENTO_COM_PUNICAO",
2830
  "skipped": True,
2831
+ "reason": skip_fase2_reason or "storage_critical_stopped_during_conhecimento",
2832
  }
2833
+ logger.warning(f"[V6.5-V2-auto-conscience-v2] FASE 2 pulada — {punicão_result['reason']}")
2834
 
2835
  # ========================================================================
2836
  # 14.6 Salvar estados finais do modelo
src/bigru_t/model/kohonen_learning_system.py CHANGED
@@ -346,7 +346,30 @@ def text_to_4d_vector(
346
 
347
  xyz_mean = coords_3d.mean(dim=0) # (3,)
348
  w = torch.tensor(time_step / T_max, dtype=torch.float) # valor temporal
349
- vec_4d = torch.cat([xyz_mean, w.unsqueeze(0)]).contiguous()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  return vec_4d
351
 
352
 
@@ -375,7 +398,9 @@ class KohonenSOM4D:
375
  alpha0: float = 0.5,
376
  sigma0: float = 3.0,
377
  lambda_ewc: float = 0.01,
378
- conscience_gamma: float = 0.1,
 
 
379
  conscience_beta: float = 0.1,
380
  ):
381
  # V6.5-V2-metrics-FIX-3 — α₀=0.5 e σ₀=3.0 conforme especificação
@@ -403,7 +428,30 @@ class KohonenSOM4D:
403
  self.lambda_ewc = lambda_ewc
404
  self.t = 0
405
 
406
- self.weights = torch.randn(self.I, self.J, self.K, self.L, 4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  self.old_weights_w = None
408
  self.fisher_w = None
409
  self.fisher_accum = torch.zeros(self.I, self.J, self.K, self.L)
@@ -449,13 +497,15 @@ class KohonenSOM4D:
449
  # ------------------------------------------------------------------
450
  self._conscience_gamma_min = 0.01
451
  self._conscience_gamma_max = 1.0
452
- self._conscience_eta = 0.05 # EMA smoothing
453
  self._prev_n_active = -1
454
  self._prev_n_active_rate = -1.0
455
  self._last_gamma_target = float(conscience_gamma)
456
  self._last_gamma_drop = 0.0
457
  self._last_activation_rate = 1.0
458
- self._auto_adjust_interval = 50 # ajusta γ a cada 50 updates
 
 
459
 
460
  def _neighborhood(self, bmu_idx):
461
  """Vizinhança Gaussiana 4D: d² = Δi² + Δj² + Δk² + Δl²."""
@@ -475,17 +525,28 @@ class KohonenSOM4D:
475
 
476
  Substitui pgvector_lookup — busca nearest-neighbor flat sobre o grid.
477
 
478
- V6.5-V2-metrics-FIX-4 (Conscience Mechanism — DeSieno 1988):
479
- Quando use_conscience=True, adiciona bias b_i = γ·(1/N - p_i) à
480
- distância, forçando distribuição uniforme de BMU entre os 864
481
- neurônios. Isto corrige o problema neurons_active=2/864:
482
-
483
- bmu = argmin_i ( ||W_i - x||² - b_i )
484
-
485
- Sem conscience, neurônios próximos a poucas amostras ficam mortos.
486
- Com conscience, neurônios que ganham demais (p_i > 1/N) são
487
- penalizados (b_i < 0 aumenta distância efetiva), e neurônios que
488
- nunca ganham (p_i ≈ 0) são favorecidos (b_i > 0 reduz distância).
 
 
 
 
 
 
 
 
 
 
 
489
 
490
  Args:
491
  x: tensor [4] — vetor de entrada.
@@ -496,11 +557,21 @@ class KohonenSOM4D:
496
  x = torch.nan_to_num(x, nan=0.0, posinf=1e4, neginf=-1e4)
497
  dist = torch.sum((self.weights - x.view(1, 1, 1, 1, 4)) ** 2, dim=-1)
498
  if use_conscience:
499
- # b_i = γ · (1/N - p_i) → subtraído da distância (menor distância efetiva vence)
500
- target_p = 1.0 / self.n_neurons
501
- bias = self.conscience_gamma * (target_p - self.win_frequency)
502
- dist_effective = dist - bias
503
- flat_idx = int(torch.argmin(dist_effective).item())
 
 
 
 
 
 
 
 
 
 
504
  else:
505
  flat_idx = int(torch.argmin(dist).item())
506
  i = flat_idx // (self.J * self.K * self.L)
@@ -738,15 +809,22 @@ class KohonenSOM4D:
738
  }
739
 
740
  def auto_adjust_conscience_gamma(self) -> Dict[str, Any]:
741
- """V6.5-V2-auto-conscience — Ajusta γ dinamicamente conforme ativação.
742
 
743
  User requirement: "aprimorar conscience_gamma para ajuste automático
744
  conforme a quantidade de neurônios esteja caindo".
745
 
746
- Quando a quantidade de neurônios ativos cai (colapso), o bias de
747
- consciência precisa ficar mais forte para reviver neurônios mortos.
748
- Quando a ativação está saudável (≈1.0), γ decai para deixar a
749
- distribuição de dados comandar o SOM (preservação topológica natural).
 
 
 
 
 
 
 
750
 
751
  Math (DeSieno 1988 + adaptação adaptativa):
752
  r = n_active / n_total (∈ [0,1])
@@ -758,15 +836,13 @@ class KohonenSOM4D:
758
  γ_boost = 0.3 · drop (reforço
759
  antecipado)
760
  γ_target = clip(γ_base + γ_boost, γ_min, γ_max)
761
- γ_new = (1 - η) · γ_old + η · γ_target (EMA suave)
762
 
763
- Comportamento esperado em diferentes cenários:
764
  r = 1.0 (saudável) → γ_base = 0.00 (data distribution domina)
765
  r = 0.5 (degradação) → γ_base = 0.25 (intervenção moderada)
766
- r = 0.1 (colapso severo) → γ_base = 0.81 (intervenção forte)
767
- r = 0.0 (paralisia) → γ_base = 1.00 (intervenção máxima)
768
- r caindo 1.0→0.5 → +0.15 boost (antecipa colapso)
769
- r subindo 0.5→1.0 → drop=0, sem boost extra
770
 
771
  Returns:
772
  Dict com: n_active, n_total, activation_rate, prev_activation_rate,
@@ -796,10 +872,23 @@ class KohonenSOM4D:
796
  ),
797
  )
798
 
799
- # EMA suave para evitar oscilações bruscas
800
- eta = self._conscience_eta
 
 
801
  gamma_old = float(self.conscience_gamma)
802
- gamma_new = (1.0 - eta) * gamma_old + eta * float(gamma_target)
 
 
 
 
 
 
 
 
 
 
 
803
  gamma_new = float(max(
804
  self._conscience_gamma_min,
805
  min(self._conscience_gamma_max, gamma_new),
@@ -824,7 +913,8 @@ class KohonenSOM4D:
824
  "gamma_new": gamma_new,
825
  "gamma_min": float(self._conscience_gamma_min),
826
  "gamma_max": float(self._conscience_gamma_max),
827
- "eta": float(eta),
 
828
  }
829
 
830
  def update_weights(self, x: torch.Tensor, bmu_idx, accumulate_fisher=False):
 
346
 
347
  xyz_mean = coords_3d.mean(dim=0) # (3,)
348
  w = torch.tensor(time_step / T_max, dtype=torch.float) # valor temporal
349
+
350
+ # V6.5-V2-auto-conscience-v2 — Normalização SEPARADA de xyz e w.
351
+ # ANTES: vetores 4D tinham magnitude ~1e-4 (xyz) e ~1e-4 (w). Após L2 norm,
352
+ # w dominava (≈1.0) e xyz virava ~1e-3. Todos os vetores apontavam para
353
+ # [0,0,0,1] com pequenas perturbações, fazendo SOM mapear tudo para o
354
+ # mesmo neurônio (neurons_active=1/864).
355
+ #
356
+ # AGORA: normalizamos xyz e w INDEPENDENTEMENTE para [-1, 1]:
357
+ # - xyz: L2-normalizado (preserva direção semântica do texto)
358
+ # - w: mapeado para [-1, 1] via (2w - 1) = 2·(t/T_max) - 1
359
+ # Isto garante que ambas as componentes têm peso comparável no SOM,
360
+ # permitindo que diferentes textos mapeiem para diferentes neurônios.
361
+ xyz_norm = xyz_mean.norm()
362
+ if xyz_norm > 1e-8:
363
+ xyz_normalized = xyz_mean / xyz_norm # unit vector in xyz subspace
364
+ else:
365
+ # Texto degenerado — usa direção aleatória reproducível baseada em time_step
366
+ torch.manual_seed(int(time_step) % 1000)
367
+ xyz_normalized = torch.randn(3)
368
+ xyz_normalized = xyz_normalized / xyz_normalized.norm()
369
+ # w mapeado de [0, 1] para [-1, 1] (melhor distribuição no espaço 4D)
370
+ w_normalized = 2.0 * w - 1.0
371
+
372
+ vec_4d = torch.cat([xyz_normalized, w_normalized.unsqueeze(0)]).contiguous()
373
  return vec_4d
374
 
375
 
 
398
  alpha0: float = 0.5,
399
  sigma0: float = 3.0,
400
  lambda_ewc: float = 0.01,
401
+ # V6.5-V2-auto-conscience-v2 — initial γ raised from 0.1 to 0.5 to
402
+ # ensure strong conscience bias from the start (prevents 2/864 bug).
403
+ conscience_gamma: float = 0.5,
404
  conscience_beta: float = 0.1,
405
  ):
406
  # V6.5-V2-metrics-FIX-3 — α₀=0.5 e σ₀=3.0 conforme especificação
 
428
  self.lambda_ewc = lambda_ewc
429
  self.t = 0
430
 
431
+ # V6.5-V2-auto-conscience-v2 — Inicialização diversa para evitar
432
+ # neurônios com pesos idênticos (que causaria todos os inputs a
433
+ # mapearem para o mesmo BMU). Usa grid coordinates como offset
434
+ # inicial + ruído aleatório, garantindo que cada neurônio comece
435
+ # em uma região diferente do espaço 4D.
436
+ # User requirement: "distribuindo o processamento paralelamente".
437
+ # Cada uma das 4 dimensões do peso recebe uma coordenada de grid
438
+ # diferente (i, j, k, l), normalizada para [-1, 1].
439
+ grid_i = torch.arange(self.I).float().view(self.I, 1, 1, 1) / max(1, self.I - 1)
440
+ grid_j = torch.arange(self.J).float().view(1, self.J, 1, 1) / max(1, self.J - 1)
441
+ grid_k = torch.arange(self.K).float().view(1, 1, self.K, 1) / max(1, self.K - 1)
442
+ grid_l = torch.arange(self.L).float().view(1, 1, 1, self.L) / max(1, self.L - 1)
443
+ # Broadcast para (I, J, K, L) — cada coordenada ocupa seu próprio eixo
444
+ grid_bc = grid_i * grid_j * grid_k * grid_l # (I, J, K, L) com valores ∈ [0,1]
445
+ # Repete para 4 dimensões de peso, aplicando offset diferente para cada
446
+ # dim para que neurônios vizinhos tenham pesos diferentes.
447
+ w_dim_0 = (grid_i.expand(self.I, self.J, self.K, self.L) - 0.5) * 2.0 # [-1,1] em I
448
+ w_dim_1 = (grid_j.expand(self.I, self.J, self.K, self.L) - 0.5) * 2.0 # [-1,1] em J
449
+ w_dim_2 = (grid_k.expand(self.I, self.J, self.K, self.L) - 0.5) * 2.0 # [-1,1] em K
450
+ w_dim_3 = (grid_l.expand(self.I, self.J, self.K, self.L) - 0.5) * 2.0 # [-1,1] em L
451
+ # Stack na última dimensão → (I, J, K, L, 4)
452
+ grid_coords = torch.stack([w_dim_0, w_dim_1, w_dim_2, w_dim_3], dim=-1)
453
+ # Combina grid coords (determinístico) com ruído (estocástico)
454
+ self.weights = grid_coords + 0.3 * torch.randn(self.I, self.J, self.K, self.L, 4)
455
  self.old_weights_w = None
456
  self.fisher_w = None
457
  self.fisher_accum = torch.zeros(self.I, self.J, self.K, self.L)
 
497
  # ------------------------------------------------------------------
498
  self._conscience_gamma_min = 0.01
499
  self._conscience_gamma_max = 1.0
500
+ self._conscience_eta = 0.05 # EMA smoothing (normal regime)
501
  self._prev_n_active = -1
502
  self._prev_n_active_rate = -1.0
503
  self._last_gamma_target = float(conscience_gamma)
504
  self._last_gamma_drop = 0.0
505
  self._last_activation_rate = 1.0
506
+ # V6.5-V2-auto-conscience-v2 — reduzido de 50 → 10 para detectar
507
+ # colapso de neurônios mais cedo e ajustar γ a tempo.
508
+ self._auto_adjust_interval = 10 # ajusta γ a cada 10 updates
509
 
510
  def _neighborhood(self, bmu_idx):
511
  """Vizinhança Gaussiana 4D: d² = Δi² + Δj² + Δk² + Δl²."""
 
525
 
526
  Substitui pgvector_lookup — busca nearest-neighbor flat sobre o grid.
527
 
528
+ V6.5-V2-auto-conscience-v2 (FIX math asymmetry):
529
+ A fórmula anterior b_i = γ·(1/N - p_i) dava bias quase zero para
530
+ neurônios mortos (γ/N ≈ 0.001 para N=864), enquanto penalizava
531
+ fortemente neurônios vencedores (γ·p_i ≈ 0.3γ). Isto causava
532
+ neurons_active=2/864 — a vantagem dada a neurônios mortos era
533
+ insuficiente para superar a diferença natural de distância.
534
+
535
+ Nova fórmula (simétrica, DeSieno 1988 corrigida):
536
+ p_norm_i = bmu_win_count[i] / max(1, max(bmu_win_count))
537
+ bias_i = γ · (0.5 - p_norm_i) (simétrica em [-0.5γ, +0.5γ])
538
+ dist_eff = dist² - bias_i
539
+
540
+ Comportamento:
541
+ - Neurônio dominante (p_norm = 1.0): bias = -0.5γ → dist_eff = dist² + 0.5γ (PENALIZADO)
542
+ - Neurônio nunca-vencedor (p_norm = 0): bias = +0.5γ → dist_eff = dist² - 0.5γ (FAVORECIDO)
543
+ - Magnitudes iguais → distribuição efetiva do BMU entre os 864 neurônios.
544
+
545
+ V6.5-V2-auto-conscience-v2 (forced dead-neuron exploration):
546
+ Para neurônios com bmu_win_count == 0, aplica um boost adicional
547
+ de -γ·0.5 (reduz distância efetiva em 0.5γ). Isto garante que
548
+ neurônios mortos sejam explorados ativamente, distribuindo o
549
+ processamento paralelamente (user requirement).
550
 
551
  Args:
552
  x: tensor [4] — vetor de entrada.
 
557
  x = torch.nan_to_num(x, nan=0.0, posinf=1e4, neginf=-1e4)
558
  dist = torch.sum((self.weights - x.view(1, 1, 1, 1, 4)) ** 2, dim=-1)
559
  if use_conscience:
560
+ # V6.5-V2-auto-conscience-v2 — bias simétrico baseado em win_count normalizado
561
+ with torch.no_grad():
562
+ win_count_flat = self.bmu_win_count.flatten().float()
563
+ max_win = win_count_flat.max().clamp(min=1.0)
564
+ p_norm = win_count_flat / max_win # ∈ [0, 1]
565
+ # Bias simétrico: +0.5γ para mortos, -0.5γ para dominante
566
+ bias = self.conscience_gamma * (0.5 - p_norm)
567
+ # Boost adicional para neurônios nunca-vencedores (win_count == 0)
568
+ dead_mask = (win_count_flat == 0).float()
569
+ dead_boost = self.conscience_gamma * 0.5 * dead_mask # -0.5γ na distância
570
+ # dist_effective = dist² - bias - dead_boost
571
+ # (bias positivo reduz distância → favorece; bias negativo aumenta → penaliza)
572
+ bias_flat = bias.flatten() + dead_boost
573
+ dist_flat = dist.flatten() - bias_flat
574
+ flat_idx = int(torch.argmin(dist_flat).item())
575
  else:
576
  flat_idx = int(torch.argmin(dist).item())
577
  i = flat_idx // (self.J * self.K * self.L)
 
809
  }
810
 
811
  def auto_adjust_conscience_gamma(self) -> Dict[str, Any]:
812
+ """V6.5-V2-auto-conscience-v2 — Ajusta γ dinamicamente conforme ativação.
813
 
814
  User requirement: "aprimorar conscience_gamma para ajuste automático
815
  conforme a quantidade de neurônios esteja caindo".
816
 
817
+ V6.5-V2-auto-conscience-v2 (FIX slow EMA convergence):
818
+ A versão anterior usava η=0.05 (EMA suave) que levava 50+ updates
819
+ para convergir ao target. Como FASE1 tem apenas ~30-50 updates por
820
+ chunk, o γ ficava preso em 0.1 (valor inicial) e nunca atingia o
821
+ γ_target=1.0 necessário para reviver neurônios mortos.
822
+
823
+ Nova estratégia:
824
+ - Se r < 0.05 (colapso crítico): salta diretamente para γ_max
825
+ (intervenção máxima imediata — sem EMA, bypass)
826
+ - Se r < 0.20 (degradação severa): η_eff = 0.5 (convergência rápida)
827
+ - Caso contrário: η_eff = η_default = 0.05 (suavização normal)
828
 
829
  Math (DeSieno 1988 + adaptação adaptativa):
830
  r = n_active / n_total (∈ [0,1])
 
836
  γ_boost = 0.3 · drop (reforço
837
  antecipado)
838
  γ_target = clip(γ_base + γ_boost, γ_min, γ_max)
839
+ γ_new = (1 - η_eff) · γ_old + η_eff · γ_target (EMA adaptativo)
840
 
841
+ Comportamento esperado:
842
  r = 1.0 (saudável) → γ_base = 0.00 (data distribution domina)
843
  r = 0.5 (degradação) → γ_base = 0.25 (intervenção moderada)
844
+ r = 0.1 (colapso severo) → γ_base = 0.81 (intervenção forte, η=0.5)
845
+ r < 0.05 (crítico) → γ = γ_max = 1.0 (salto direto, sem EMA)
 
 
846
 
847
  Returns:
848
  Dict com: n_active, n_total, activation_rate, prev_activation_rate,
 
872
  ),
873
  )
874
 
875
+ # V6.5-V2-auto-conscience-v2 — adaptive EMA rate
876
+ # Critical collapse: jump directly to γ_max (bypass EMA)
877
+ # Severe degradation: fast EMA (η=0.5)
878
+ # Normal: slow EMA (η=0.05, default)
879
  gamma_old = float(self.conscience_gamma)
880
+ if r < 0.05:
881
+ # CRITICAL: bypass EMA, jump directly to target
882
+ eta_eff = 1.0
883
+ gamma_new = float(gamma_target)
884
+ elif r < 0.20:
885
+ # SEVERE: fast EMA convergence
886
+ eta_eff = 0.5
887
+ gamma_new = (1.0 - eta_eff) * gamma_old + eta_eff * float(gamma_target)
888
+ else:
889
+ # NORMAL: slow EMA
890
+ eta_eff = self._conscience_eta
891
+ gamma_new = (1.0 - eta_eff) * gamma_old + eta_eff * float(gamma_target)
892
  gamma_new = float(max(
893
  self._conscience_gamma_min,
894
  min(self._conscience_gamma_max, gamma_new),
 
913
  "gamma_new": gamma_new,
914
  "gamma_min": float(self._conscience_gamma_min),
915
  "gamma_max": float(self._conscience_gamma_max),
916
+ "eta": float(eta_eff),
917
+ "bypass_ema": bool(r < 0.05),
918
  }
919
 
920
  def update_weights(self, x: torch.Tensor, bmu_idx, accumulate_fisher=False):