deepakdsoni's picture
Antahkarana-Net — Vedic cognitive AI POC
f9abec3 verified
Raw
History Blame Contribute Delete
5.33 kB
"""
Synthetic continual-learning benchmark — fully offline (no downloads).
Conflicting-teacher stream (the setting where forgetting is strong and real):
- Inputs x ~ N(0, I) in R^dim, shared across all tasks.
- Each task t has its OWN random teacher hyperplane w_t: label y = 1[ w_t·x > 0 ].
- A SINGLE shared head is used for every task.
Because each task demands a DIFFERENT decision boundary, learning task t+1
genuinely overwrites the weights that solved task t → catastrophic forgetting.
Consolidation (EWC / saṃskāra) protects the weights that mattered for earlier
teachers, trading some new-task fit for retained old-task accuracy.
Deliberately small — the whole study runs on CPU in well under a minute.
"""
from __future__ import annotations
from typing import List, Tuple
import torch
from torch.utils.data import TensorDataset, DataLoader
def make_tasks(
n_tasks: int = 6,
dim: int = 32,
n_train: int = 1000,
n_test: int = 400,
margin: float = 0.15, # drop points too close to the boundary (cleaner labels)
seed: int = 0,
) -> List[Tuple[TensorDataset, TensorDataset]]:
g = torch.Generator().manual_seed(seed)
tasks = []
for _ in range(n_tasks):
w = torch.randn(dim, generator=g)
w = w / w.norm()
train = _teacher(w, n_train, margin, dim, g)
test = _teacher(w, n_test, margin, dim, g)
tasks.append((train, test))
return tasks
def _teacher(w, n, margin, dim, g) -> TensorDataset:
xs, ys = [], []
while sum(len(y) for y in ys) < n:
x = torch.randn(n, dim, generator=g)
s = x @ w
keep = s.abs() > margin # enforce a margin → clean labels
xs.append(x[keep]); ys.append((s[keep] > 0).long())
x = torch.cat(xs)[:n]; y = torch.cat(ys)[:n]
return TensorDataset(x, y)
def loaders(tasks, batch_size: int = 64):
out = []
for train, test in tasks:
out.append((
DataLoader(train, batch_size=batch_size, shuffle=True),
DataLoader(test, batch_size=256, shuffle=False),
))
return out
# ---------------------------------------------------------------------------
# Capacity-headroom stream: tasks share a common nonlinear feature map φ (one
# fixed random teacher MLP), and differ only in a per-task linear readout. A
# backbone that learns φ can solve ALL tasks jointly (so avg accuracy can be
# high — there is real headroom), but training a new task can overwrite the
# parts of φ the old tasks relied on (forgetting). This is the setting where
# consolidation/replay/decay/tapas can actually differentiate.
# Use with a MULTI-HEAD model: evaluate task k with head k.
# ---------------------------------------------------------------------------
def make_shared_feature_tasks(
n_tasks: int = 8,
dim: int = 24,
teacher_hidden: int = 32,
n_train: int = 800,
n_test: int = 400,
margin: float = 0.2,
seed: int = 0,
):
g = torch.Generator().manual_seed(seed)
# one shared, fixed nonlinear teacher φ: R^dim -> R^teacher_hidden
W1 = torch.randn(dim, teacher_hidden, generator=g) / (dim ** 0.5)
b1 = torch.randn(teacher_hidden, generator=g) * 0.1
def phi(x):
return torch.tanh(x @ W1 + b1)
tasks = []
for _ in range(n_tasks):
u = torch.randn(teacher_hidden, generator=g) # per-task readout
u = u / u.norm()
train = _shared(phi, u, n_train, dim, margin, g)
test = _shared(phi, u, n_test, dim, margin, g)
tasks.append((train, test))
return tasks, dim
def make_difficulty_tasks(difficulty: float, n_tasks: int = 5, dim: int = 24,
n_train: int = 400, n_test: int = 200, margin: float = 0.1,
seed: int = 0):
"""Single-head continual stream with a tunable FORGETTING knob.
Each task's teacher is w_t = normalize((1-d)·w0 + d·r_t):
d=0 → every task identical (no forgetting; protection only hurts)
d=1 → every task a fresh random boundary (severe forgetting; protection helps)
The controller must read the resulting forgetting and adapt. Returns task list
+ dim; use with a SINGLE-head model."""
g = torch.Generator().manual_seed(seed)
w0 = torch.randn(dim, generator=g); w0 = w0 / w0.norm()
tasks = []
for _ in range(n_tasks):
r = torch.randn(dim, generator=g)
w = (1 - difficulty) * w0 + difficulty * r
w = w / w.norm()
tr = _hyper(w, n_train, dim, margin, g)
te = _hyper(w, n_test, dim, margin, g)
tasks.append((tr, te))
return tasks, dim
def _hyper(w, n, dim, margin, g) -> TensorDataset:
xs, ys = [], []
while sum(len(y) for y in ys) < n:
x = torch.randn(n, dim, generator=g)
s = x @ w
keep = s.abs() > margin
xs.append(x[keep]); ys.append((s[keep] > 0).long())
return TensorDataset(torch.cat(xs)[:n], torch.cat(ys)[:n])
def _shared(phi, u, n, dim, margin, g) -> TensorDataset:
xs, ys = [], []
while sum(len(y) for y in ys) < n:
x = torch.randn(n, dim, generator=g)
s = phi(x) @ u
keep = s.abs() > margin
xs.append(x[keep]); ys.append((s[keep] > 0).long())
x = torch.cat(xs)[:n]; y = torch.cat(ys)[:n]
return TensorDataset(x, y)