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 +54 -14
- src/bigru_t/model/kohonen_learning_system.py +126 -36
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 =
|
| 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.
|
| 1368 |
-
f"[V6.5-V2-auto-
|
| 1369 |
-
f"chunk {chunk_idx_global}: {stop_training_reason}"
|
|
|
|
| 1370 |
)
|
| 1371 |
-
|
| 1372 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 2099 |
-
f"[V6.5-V2-auto-
|
| 2100 |
-
f"
|
|
|
|
| 2101 |
)
|
| 2102 |
-
|
| 2103 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 —
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
neurônios
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 762 |
|
| 763 |
-
Comportamento esperado
|
| 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
|
| 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 |
-
#
|
| 800 |
-
|
|
|
|
|
|
|
| 801 |
gamma_old = float(self.conscience_gamma)
|
| 802 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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(
|
|
|
|
| 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):
|