[StackRnn] - temporal-shift padding, joint training, vocab reduction, TEMPORAL_DEPTH=64
stack_rnn.py:
- _train_unrolled: C++-style temporal-shift padding — flatten (num_seq, T, s)
→ (N, s), append T-1 zero rows, layer t trains on batch[t:N+t]; joint
training replaces greedy sequential (all layers update each epoch)
- Reverted sequential/greedy training path (poor next-step prediction)
moby_rnn.ipynb:
- Vocabulary reduced 85 → 40 chars (A-Z, 0-9, space, .!?)
- T renamed to TEMPORAL_DEPTH throughout
- TEMPORAL_DEPTH increased 5 → 64; CONTEXT_SIZE 128
- SEED "Call me Ishmael." now falls within training data coverage
state.py:
- Remove per-file load/save print messages (too noisy with 64-layer checkpoints)
README_moby_rnn.md:
- Update vocab, constants, parameter count, checkpoint listing
- Add temporal-shift padding and joint training sections
- Clarify reconstruction accuracy vs next-step prediction as honest metric
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+30
-12
@@ -209,13 +209,30 @@ class StackRnn(Stack):
|
||||
})
|
||||
|
||||
def _train_unrolled(self, seqs: Mat, num_seq: int, T: int, status: Status):
|
||||
"""N entities, one per time step — each has its own W, b_v, b_h."""
|
||||
"""N entities, one per time step — each has its own W, b_v, b_h.
|
||||
|
||||
Uses temporal-shift padding (matching C++ RnnStack):
|
||||
Flatten (num_seq, T, sensory_size) → (N, sensory_size), append T-1 zero
|
||||
rows, then layer t trains on batch_padded[t : N+t] — a one-step delay.
|
||||
|
||||
Joint training: all layers are updated together each epoch.
|
||||
Context c from layer t feeds layer t+1 within the same epoch pass,
|
||||
so gradients propagate through the full temporal chain.
|
||||
"""
|
||||
params = self.from_index(0).entity.training_params
|
||||
h_sz = self.from_index(0).entity.shape[1]
|
||||
s_sz = seqs.shape[2]
|
||||
d_progress = 100.0 / params.num_epochs
|
||||
progress = 0.0
|
||||
keep_running = True
|
||||
|
||||
# Flatten to (N, s_sz) keeping sequence-major order, on CPU for slicing
|
||||
flat = _np_cpu.asarray(convert(seqs) if hasattr(seqs, 'get') else seqs)
|
||||
flat = flat.reshape(-1, s_sz)
|
||||
N = flat.shape[0]
|
||||
pad = _np_cpu.zeros((T - 1, s_sz), dtype=flat.dtype)
|
||||
batch_pad = _np_cpu.concatenate([flat, pad], axis=0) # (N+T-1, s_sz)
|
||||
|
||||
for layer in self.layers:
|
||||
layer.entity.grad_zero()
|
||||
status.on_change(self.from_index(0).entity)
|
||||
@@ -224,22 +241,22 @@ class StackRnn(Stack):
|
||||
if not keep_running:
|
||||
break
|
||||
|
||||
h = np.zeros((num_seq, h_sz))
|
||||
c = np.zeros((N, h_sz))
|
||||
err_total = 0.0
|
||||
|
||||
for t, layer in enumerate(self.layers):
|
||||
entity = layer.entity
|
||||
cd_func = _CD_FUNC[entity.type]
|
||||
|
||||
x_t = _to_gpu(seqs[:, t, :])
|
||||
visible = np.concatenate([h, x_t], axis=1)
|
||||
x_t = _to_gpu(batch_pad[t : N + t]) # shifted slice, (N, s_sz)
|
||||
visible = np.concatenate([c, x_t], axis=1)
|
||||
|
||||
dwhv, dbv, dbh = cd_func(entity, visible)
|
||||
grad = entity.grad_compute(dbv, dbh, dwhv)
|
||||
entity.state_adjust(grad, 1.0 / num_seq)
|
||||
entity.state_adjust(grad, 1.0 / N)
|
||||
|
||||
h = entity.forward(visible)
|
||||
err_total += rms_error_accu(visible - entity.reconstruct(h))
|
||||
c = entity.forward(visible)
|
||||
err_total += rms_error_accu(visible - entity.reconstruct(c))
|
||||
|
||||
progress += d_progress
|
||||
if status.want_report(round(progress)):
|
||||
@@ -250,14 +267,15 @@ class StackRnn(Stack):
|
||||
keep_running = False
|
||||
break
|
||||
|
||||
h = np.zeros((num_seq, h_sz))
|
||||
# Final report pass
|
||||
c = np.zeros((N, h_sz))
|
||||
err_total = 0.0
|
||||
for t, layer in enumerate(self.layers):
|
||||
entity = layer.entity
|
||||
x_t = _to_gpu(seqs[:, t, :])
|
||||
visible = np.concatenate([h, x_t], axis=1)
|
||||
h = entity.forward(visible)
|
||||
err_total += rms_error_accu(visible - entity.reconstruct(h))
|
||||
x_t = _to_gpu(batch_pad[t : N + t])
|
||||
visible = np.concatenate([c, x_t], axis=1)
|
||||
c = entity.forward(visible)
|
||||
err_total += rms_error_accu(visible - entity.reconstruct(c))
|
||||
status.on_change(self.from_index(0).entity, {
|
||||
"progress": {"value": 100, "unit": "%"},
|
||||
"err_rms_total": {"value": err_total / T, "unit": ""},
|
||||
|
||||
@@ -22,7 +22,6 @@ class RbmState:
|
||||
with np.load(filename) as X:
|
||||
w_hv, b_v, b_h = [X[i] for i in ('whv', 'bv', 'bh')]
|
||||
obj = cls(w_hv, b_v, b_h)
|
||||
print(f"{filename} loaded successfully!")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except KeyError:
|
||||
@@ -44,7 +43,6 @@ class RbmState:
|
||||
self.w_hv = w_hv
|
||||
self.b_v = b_v
|
||||
self.b_h = b_h
|
||||
print(f"{filename} loaded successfully!")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except KeyError:
|
||||
@@ -53,7 +51,6 @@ class RbmState:
|
||||
|
||||
def save(self, filename: str):
|
||||
np.savez(filename, whv=self.w_hv, bv=self.b_v, bh=self.b_h)
|
||||
print(f"{filename} saved successfully!")
|
||||
|
||||
def init(self, mu: float = 0.0, std: float = 1.0):
|
||||
self.w_hv = uniform(self.w_hv.shape, mu, std)
|
||||
|
||||
Reference in New Issue
Block a user