AbstractPhil commited on
Commit
18bbb49
·
verified ·
1 Parent(s): 2ad0453

mirror geolip 0.8.8: model/relay.py (RelayPatchwork + RelayEMA functional form; additive, unimported by modeling path)

Browse files
Files changed (1) hide show
  1. relay.py +171 -0
relay.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """RelayPatchwork + RelayEMA — the relay organ and its causal-EMA memory taps.
2
+
3
+ The organ is the certified residual patchwork (amoe-lora's arm geometry,
4
+ brought here as a native model component):
5
+
6
+ slots_t = proj(x_t) (B, n, n_slots, D) orthogonal, no bias
7
+ f_t = m_hat(slots_t) flattened the reconstructive signed read
8
+ m_hat(x) = sum_k sinh(u_k) a_hat_k / sum_j cosh(u_j)
9
+ = signed(x) @ A_hat (composed from AlephAddress)
10
+ y_t = x_t + sigmoid(gate) * consume(f_t)
11
+
12
+ consume = Linear(nD -> hidden), ReLU^2, LayerNorm, Linear(hidden -> d) with the
13
+ output layer zero-initialized (weight AND bias — the strict C6 null path: a
14
+ fresh organ is bit-inert, y == x exactly). Gate initialized -3. Reconstructive,
15
+ never comparative: no softmax-over-choices, no argmax, no top-k.
16
+
17
+ RelayEMA adds ONE mechanism: two fixed-decay causal EMAs of the organ's own
18
+ read, fed to the head through input columns that are ZERO at birth —
19
+
20
+ F1_t = (1 - r1) F1_{t-1} + r1 f_t r1 = 1/16
21
+ F2_t = (1 - r2) F2_{t-1} + r2 f_t r2 = 1/64
22
+ y_t = x_t + sigmoid(gate) * consume(cat(f_t, F1_t, F2_t))
23
+
24
+ so at birth RelayEMA's forward EQUALS the memory-free organ. Training uses the
25
+ chunked closed-form scan (ema_chunked, renormalized so q^-t never overflows);
26
+ decode carries (F1, F2) state and updates one step at a time — exact, because
27
+ the head is position-wise.
28
+
29
+ The record (canon btx_e003, E-G2 2026-09-14, on frozen mini-beatrix-2s):
30
+ the memory-free organ form, trained answer-only with the rule-order shortcut
31
+ blocked in the data, internalized a 5-step rule chain — .527 as-is / .553
32
+ rules-shuffled on out-of-vocabulary words (order-invariant; heuristic ceiling
33
+ ~.2), 1.000 in-vocabulary. The EMA taps added nothing measurable on that task
34
+ (.527/.587). Packaged with the taps because they are free at birth and the
35
+ accumulator is the certified form. Default geometry below is the validated
36
+ wide arm (n_slots 32, K 64, D 4, hidden 256 at d 1024: 428k params/site).
37
+ """
38
+ from __future__ import annotations
39
+
40
+ from dataclasses import dataclass
41
+
42
+ import torch
43
+ import torch.nn as nn
44
+ import torch.nn.functional as F
45
+
46
+ from .address import AlephAddress
47
+
48
+
49
+ @dataclass
50
+ class RelaySpec:
51
+ n_slots: int = 32
52
+ K: int = 64
53
+ D: int = 4
54
+ tau: float = 0.1
55
+ hidden: int = 256
56
+ rho1: float = 1.0 / 16.0
57
+ rho2: float = 1.0 / 64.0
58
+ gate_init: float = -3.0
59
+ zero_init_head: bool = True
60
+
61
+
62
+ class SquaredReLU(nn.Module):
63
+ def forward(self, x):
64
+ return F.relu(x) ** 2
65
+
66
+
67
+ def ema_chunked(f: torch.Tensor, rho: float, s0: torch.Tensor, chunk: int = 256):
68
+ """Causal EMA over dim 1, closed form per chunk (renormalized so q^-t
69
+ never overflows; autograd-safe). F_t = q^t (s0 + rho * sum_{s<=t} q^-s f_s),
70
+ q = 1 - rho; state carried between chunks. -> (F (B,T,C), F_last (B,C))."""
71
+ B, T, C = f.shape
72
+ q = 1.0 - rho
73
+ outs = []
74
+ s = s0
75
+ for a in range(0, T, chunk):
76
+ fb = f[:, a:a + chunk]
77
+ t = fb.shape[1]
78
+ idx = torch.arange(1, t + 1, device=f.device, dtype=f.dtype)
79
+ acc = torch.cumsum(fb * torch.pow(q, -idx).view(1, t, 1), dim=1)
80
+ Fb = torch.pow(q, idx).view(1, t, 1) * (s.unsqueeze(1) + rho * acc)
81
+ s = Fb[:, -1]
82
+ outs.append(Fb)
83
+ return torch.cat(outs, dim=1), s
84
+
85
+
86
+ def _feats(m, x: torch.Tensor) -> torch.Tensor:
87
+ """The reconstructive read, flattened: m_hat = signed(slots) @ A_hat."""
88
+ B, n, _ = x.shape
89
+ slots = m.proj(x).view(B, n, m.n_slots, m.spec.D)
90
+ A = F.normalize(m.addr.codebook, dim=-1)
91
+ return (m.addr.signed(slots) @ A).reshape(B, n, -1)
92
+
93
+
94
+ class RelayPatchwork(nn.Module):
95
+ """The memory-free relay organ: proj -> reconstructive aleph read ->
96
+ zero-born squared-ReLU patch head, residual write behind a sigmoid gate."""
97
+
98
+ def __init__(self, d: int, spec: RelaySpec | None = None):
99
+ super().__init__()
100
+ s = spec or RelaySpec()
101
+ self.spec = s
102
+ self.n_slots = s.n_slots
103
+ self.nD = s.n_slots * s.D
104
+ self.proj = nn.Linear(d, self.nD, bias=False)
105
+ nn.init.orthogonal_(self.proj.weight)
106
+ self.addr = AlephAddress(s.K, s.D, s.tau)
107
+ self.consume = nn.Sequential(
108
+ nn.Linear(self.nD, s.hidden), SquaredReLU(),
109
+ nn.LayerNorm(s.hidden), nn.Linear(s.hidden, d))
110
+ if s.zero_init_head:
111
+ nn.init.zeros_(self.consume[-1].weight)
112
+ nn.init.zeros_(self.consume[-1].bias)
113
+ self.gate = nn.Parameter(torch.tensor(float(s.gate_init)))
114
+
115
+ def feats(self, x: torch.Tensor) -> torch.Tensor:
116
+ return _feats(self, x)
117
+
118
+ def forward(self, x):
119
+ return x + torch.sigmoid(self.gate) * self.consume(self.feats(x))
120
+
121
+
122
+ class RelayEMA(nn.Module):
123
+ """The organ plus zero-born EMA memory taps. Built by widening an organ's
124
+ head to 3nD input columns, the added columns zeroed — at birth the forward
125
+ equals the organ it came from (shared weights)."""
126
+
127
+ def __init__(self, d: int, spec: RelaySpec | None = None):
128
+ super().__init__()
129
+ self._widen(RelayPatchwork(d, spec))
130
+
131
+ @classmethod
132
+ def from_organ(cls, organ: RelayPatchwork) -> "RelayEMA":
133
+ """Upgrade a (possibly trained) organ in place: shares proj/addr/gate
134
+ and the head tail, widens the head's first layer with zeroed columns."""
135
+ self = cls.__new__(cls)
136
+ nn.Module.__init__(self)
137
+ self._widen(organ)
138
+ return self
139
+
140
+ def _widen(self, organ: RelayPatchwork):
141
+ self.spec = organ.spec
142
+ self.nD = organ.nD
143
+ self.proj, self.addr, self.gate = organ.proj, organ.addr, organ.gate
144
+ self.n_slots = organ.n_slots
145
+ wide = nn.Linear(3 * self.nD, organ.spec.hidden)
146
+ with torch.no_grad():
147
+ wide.weight[:, :self.nD] = organ.consume[0].weight
148
+ wide.weight[:, self.nD:] = 0.0
149
+ wide.bias.copy_(organ.consume[0].bias)
150
+ self.consume = nn.Sequential(
151
+ wide, organ.consume[1], organ.consume[2], organ.consume[3])
152
+
153
+ def feats(self, x: torch.Tensor) -> torch.Tensor:
154
+ return _feats(self, x)
155
+
156
+ def run(self, x, state=None):
157
+ """-> (y = x + write, (F1_last, F2_last)). state None starts both EMAs
158
+ at zero (fresh context). Decode: call on the single new position with
159
+ the carried state — exact, the head is position-wise."""
160
+ f = self.feats(x)
161
+ B = f.shape[0]
162
+ s1 = state[0] if state is not None else f.new_zeros(B, self.nD)
163
+ s2 = state[1] if state is not None else f.new_zeros(B, self.nD)
164
+ F1, s1 = ema_chunked(f, self.spec.rho1, s1)
165
+ F2, s2 = ema_chunked(f, self.spec.rho2, s2)
166
+ y = x + torch.sigmoid(self.gate) * self.consume(
167
+ torch.cat([f, F1, F2], dim=-1))
168
+ return y, (s1, s2)
169
+
170
+ def forward(self, x, state=None):
171
+ return self.run(x, state)[0]