diff options
| author | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-22 03:35:41 -0500 |
|---|---|---|
| committer | YurenHao0426 <Blackhao0426@gmail.com> | 2026-07-22 03:35:41 -0500 |
| commit | a065428141f68858ea4ae4e56472718a8024042e (patch) | |
| tree | 34734ec106fb7bc47fb87bd41caa5535c1d24e64 /sdil | |
| parent | dcadaff8622e6c8b3450f12f45447f605c68556a (diff) | |
fix: bound state-conditioned feedback features
Diffstat (limited to 'sdil')
| -rw-r--r-- | sdil/core.py | 17 |
1 files changed, 9 insertions, 8 deletions
diff --git a/sdil/core.py b/sdil/core.py index e66dc39..64c7c79 100644 --- a/sdil/core.py +++ b/sdil/core.py @@ -265,10 +265,11 @@ class SDILNet: def vectorizer(self, l, c, h_l=None, context=None): """Return the learned instructional component of apical activity. - ``soma_gated`` adds ``h_i G_i c`` independently in every cell. - ``context_gated`` adds a linear readout of ``c outer context``. Both - vanish when c=0, so neutral-period predictor identification remains - unchanged. + ``soma_gated`` adds ``tanh(h_i) G_i c`` independently in every cell. + ``context_gated`` adds a linear readout of ``c outer tanh(context)``. + Bounding the state feature prevents a bilinear positive-feedback loop; + both terms vanish when c=0, so neutral-period predictor identification + remains unchanged. """ value = c @ self.A[l].t() if self.vectorizer_mode == "linear": @@ -276,10 +277,10 @@ class SDILNet: if self.vectorizer_mode == "soma_gated": if h_l is None: raise ValueError("soma-gated vectorizer requires somatic activity") - return value + h_l * (c @ self.A_gate[l].t()) + return value + torch.tanh(h_l) * (c @ self.A_gate[l].t()) if context is None: raise ValueError("context-gated vectorizer requires a context state") - features = (c.unsqueeze(2) * context.unsqueeze(1)).flatten(1) + features = (c.unsqueeze(2) * torch.tanh(context).unsqueeze(1)).flatten(1) return value + features @ self.A_gate[l].t() def apical(self, l, c, h_l=None, context=None): @@ -551,10 +552,10 @@ def sdil_step(net, x, y, y_onehot, cfg, step, prev_error=None): dA = calibration_error.t() @ c / B # (n_l, n_classes) net.A[l] += cfg.eta_A * dA if net.vectorizer_mode == "soma_gated": - dgate = (calibration_error * h[l + 1]).t() @ c / B + dgate = (calibration_error * torch.tanh(h[l + 1])).t() @ c / B net.A_gate[l] += cfg.eta_A * dgate elif net.vectorizer_mode == "context_gated": - features = (c.unsqueeze(2) * context.unsqueeze(1)).flatten(1) + features = (c.unsqueeze(2) * torch.tanh(context).unsqueeze(1)).flatten(1) dgate = calibration_error.t() @ features / B net.A_gate[l] += cfg.eta_A * dgate |
