V6.7: upload src/bigru_t/model/kohonen_learning_system.py (BBPE serial mode + OomGuard V7)
Browse files
src/bigru_t/model/kohonen_learning_system.py
CHANGED
|
@@ -443,6 +443,15 @@ class KohonenSOM4D:
|
|
| 443 |
self.fisher_accum = torch.zeros(self.I, self.J, self.K, self.L)
|
| 444 |
self.fisher_count = torch.zeros(self.I, self.J, self.K, self.L)
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
# V6.5-V2-metrics-FIX-4 — Conscience mechanism (DeSieno 1988)
|
| 447 |
# Inicializa p_i = 1/N (uniforme) — sem viés inicial.
|
| 448 |
# b_i = γ · (1/N - p_i) começa em 0 (sem bias).
|
|
@@ -1392,10 +1401,19 @@ class KohonenSOM4D:
|
|
| 1392 |
NaN não deve contaminar permanentemente os pesos).
|
| 1393 |
3. Clamp dos pesos após update (evita explosão numérica).
|
| 1394 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1395 |
Args:
|
| 1396 |
x: tensor [4] — amostra 4D.
|
| 1397 |
bmu_idx: (i, j, k, l) — índice do BMU.
|
| 1398 |
-
accumulate_fisher: se True
|
|
|
|
| 1399 |
"""
|
| 1400 |
# V6.5-V2-metrics-FIX-3 — sanitiza x: substitui NaN/Inf por 0
|
| 1401 |
# (uma amostra degenerada não deve contaminar todo o SOM).
|
|
@@ -1427,13 +1445,16 @@ class KohonenSOM4D:
|
|
| 1427 |
if torch.isnan(self.weights).any() or torch.isinf(self.weights).any():
|
| 1428 |
self.weights = torch.nan_to_num(self.weights, nan=0.0, posinf=100.0, neginf=-100.0)
|
| 1429 |
|
| 1430 |
-
|
|
|
|
|
|
|
|
|
|
| 1431 |
# Acumula Fisher apenas em neurônios próximos ao BMU (Λ > 0.1)
|
| 1432 |
mask = h > 0.1
|
| 1433 |
if mask.any():
|
| 1434 |
diff_sq = (x[3] - self.weights[mask][..., 3]) ** 2
|
| 1435 |
-
self.fisher_accum[mask]
|
| 1436 |
-
self.fisher_count[mask]
|
| 1437 |
|
| 1438 |
self.t += 1
|
| 1439 |
|
|
@@ -1458,16 +1479,41 @@ class KohonenSOM4D:
|
|
| 1458 |
pass
|
| 1459 |
|
| 1460 |
def finalize_fisher(self):
|
| 1461 |
-
"""Fisher = mean((x_w - W_w)²) sobre samples acumuladas.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1462 |
cnt = self.fisher_count.clamp(min=1e-8)
|
| 1463 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1464 |
|
| 1465 |
def set_ewc_reference(self):
|
| 1466 |
-
"""Consolida W_w como referência EWC e finaliza Fisher.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1467 |
self.old_weights_w = self.weights[..., 3].clone()
|
| 1468 |
self.finalize_fisher()
|
|
|
|
| 1469 |
self.fisher_accum.zero_()
|
| 1470 |
self.fisher_count.zero_()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1471 |
|
| 1472 |
# ------------------------------------------------------------------
|
| 1473 |
# Métricas para monitoramento (V6.4)
|
|
|
|
| 443 |
self.fisher_accum = torch.zeros(self.I, self.J, self.K, self.L)
|
| 444 |
self.fisher_count = torch.zeros(self.I, self.J, self.K, self.L)
|
| 445 |
|
| 446 |
+
# V6.6 — Fisher accumulation CONTÍNUA (online EWC pattern)
|
| 447 |
+
# Quando True, Fisher é acumulado em TODOS os updates (não só
|
| 448 |
+
# antes da primeira consolidação). Após set_ewc_reference(),
|
| 449 |
+
# Fisher é resetado e começa a acumular novamente para a próxima
|
| 450 |
+
# tarefa. A consolidada é preservada via EMA em finalize_fisher().
|
| 451 |
+
self.fisher_continuous: bool = True
|
| 452 |
+
# Histórico de Fisher consolidations (uma entrada por set_ewc_reference)
|
| 453 |
+
self.fisher_consolidation_count: int = 0
|
| 454 |
+
|
| 455 |
# V6.5-V2-metrics-FIX-4 — Conscience mechanism (DeSieno 1988)
|
| 456 |
# Inicializa p_i = 1/N (uniforme) — sem viés inicial.
|
| 457 |
# b_i = γ · (1/N - p_i) começa em 0 (sem bias).
|
|
|
|
| 1401 |
NaN não deve contaminar permanentemente os pesos).
|
| 1402 |
3. Clamp dos pesos após update (evita explosão numérica).
|
| 1403 |
|
| 1404 |
+
V6.6 — Fisher accumulation CONTÍNUA (online EWC pattern):
|
| 1405 |
+
Quando self.fisher_continuous=True (default), acumula Fisher em
|
| 1406 |
+
TODOS os updates, não apenas quando accumulate_fisher=True.
|
| 1407 |
+
Após set_ewc_reference(), Fisher é resetado e começa a acumular
|
| 1408 |
+
novamente para a próxima tarefa. Isto suporta online EWC
|
| 1409 |
+
(Schwarz et al. 2018) onde múltiplas tarefas são consolidadas
|
| 1410 |
+
sequencialmente sem catastrophic forgetting.
|
| 1411 |
+
|
| 1412 |
Args:
|
| 1413 |
x: tensor [4] — amostra 4D.
|
| 1414 |
bmu_idx: (i, j, k, l) — índice do BMU.
|
| 1415 |
+
accumulate_fisher: se True (ou fisher_continuous=True), acumula
|
| 1416 |
+
(x_w - W_w)² nos Fisher accumulators.
|
| 1417 |
"""
|
| 1418 |
# V6.5-V2-metrics-FIX-3 — sanitiza x: substitui NaN/Inf por 0
|
| 1419 |
# (uma amostra degenerada não deve contaminar todo o SOM).
|
|
|
|
| 1445 |
if torch.isnan(self.weights).any() or torch.isinf(self.weights).any():
|
| 1446 |
self.weights = torch.nan_to_num(self.weights, nan=0.0, posinf=100.0, neginf=-100.0)
|
| 1447 |
|
| 1448 |
+
# V6.6 — Fisher accumulation contínua (online EWC)
|
| 1449 |
+
# Acumula SEMPRE que: accumulate_fisher=True OU fisher_continuous=True
|
| 1450 |
+
should_accumulate = accumulate_fisher or getattr(self, 'fisher_continuous', False)
|
| 1451 |
+
if should_accumulate:
|
| 1452 |
# Acumula Fisher apenas em neurônios próximos ao BMU (Λ > 0.1)
|
| 1453 |
mask = h > 0.1
|
| 1454 |
if mask.any():
|
| 1455 |
diff_sq = (x[3] - self.weights[mask][..., 3]) ** 2
|
| 1456 |
+
self.fisher_accum[mask] = self.fisher_accum[mask] + diff_sq
|
| 1457 |
+
self.fisher_count[mask] = self.fisher_count[mask] + 1
|
| 1458 |
|
| 1459 |
self.t += 1
|
| 1460 |
|
|
|
|
| 1479 |
pass
|
| 1480 |
|
| 1481 |
def finalize_fisher(self):
|
| 1482 |
+
"""Fisher = mean((x_w - W_w)²) sobre samples acumuladas.
|
| 1483 |
+
|
| 1484 |
+
V6.6 — se fisher_continuous=True E já existe fisher_w (consolidação
|
| 1485 |
+
anterior), faz EMA (exponential moving average) com a Fisher anterior
|
| 1486 |
+
em vez de substituir — isto preserva informação de tarefas passadas
|
| 1487 |
+
(online EWC pattern, cf. Schwarz et al. 2018).
|
| 1488 |
+
"""
|
| 1489 |
cnt = self.fisher_count.clamp(min=1e-8)
|
| 1490 |
+
new_fisher = self.fisher_accum / cnt
|
| 1491 |
+
if (getattr(self, 'fisher_continuous', False)
|
| 1492 |
+
and self.fisher_w is not None
|
| 1493 |
+
and getattr(self, 'fisher_consolidation_count', 0) > 0):
|
| 1494 |
+
# EMA: F_t = α·F_t_new + (1-α)·F_{t-1}
|
| 1495 |
+
# α=0.5 dá peso igual a tarefa atual e à memória das anteriores
|
| 1496 |
+
ema_alpha = 0.5
|
| 1497 |
+
self.fisher_w = ema_alpha * new_fisher + (1 - ema_alpha) * self.fisher_w
|
| 1498 |
+
else:
|
| 1499 |
+
self.fisher_w = new_fisher
|
| 1500 |
|
| 1501 |
def set_ewc_reference(self):
|
| 1502 |
+
"""Consolida W_w como referência EWC e finaliza Fisher.
|
| 1503 |
+
|
| 1504 |
+
V6.6 — Após consolidação, Fisher é RESETADO para acumular informação
|
| 1505 |
+
da PRÓXIMA tarefa (online EWC pattern). A Fisher consolidada é
|
| 1506 |
+
preservada em self.fisher_w via EMA em finalize_fisher().
|
| 1507 |
+
"""
|
| 1508 |
self.old_weights_w = self.weights[..., 3].clone()
|
| 1509 |
self.finalize_fisher()
|
| 1510 |
+
# Reset dos accumuladores para a próxima tarefa
|
| 1511 |
self.fisher_accum.zero_()
|
| 1512 |
self.fisher_count.zero_()
|
| 1513 |
+
# V6.6 — Track consolidation count for EMA logic
|
| 1514 |
+
if not hasattr(self, 'fisher_consolidation_count'):
|
| 1515 |
+
self.fisher_consolidation_count = 0
|
| 1516 |
+
self.fisher_consolidation_count += 1
|
| 1517 |
|
| 1518 |
# ------------------------------------------------------------------
|
| 1519 |
# Métricas para monitoramento (V6.4)
|