Commit Graph
182 Commits
Author SHA1 Message Date
jens bb6680c931 getting Rnn to work 2026-06-04 10:28:19 +02:00
jens 851bad28d3 cleaned up 2026-06-03 23:01:10 +02:00
jens 3a41014d50 improved context update for JayRnn 2026-06-03 22:58:18 +02:00
jens 2701daf1c4 added JayRnn 2026-06-03 22:14:40 +02:00
jens 6a75e0b33b added more Rnn helber 2026-06-03 22:14:34 +02:00
jens 5c3734a2a4 extend model member detection by list[Entity] 2026-06-03 22:13:57 +02:00
jens 27460c503d fixed type alias 2026-06-03 22:13:26 +02:00
jensandClaude Sonnet 4.6 c62605de51 [update] README.rbm.md: add Q&A on image/video and code generation architectures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 11:15:10 +02:00
jensandClaude Sonnet 4.6 ae81e3e340 [add] README.rbm.md: Q&A on RBMs, VAEs, Transformers, and autodiff
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 11:05:48 +02:00
jensandClaude Sonnet 4.6 df4a9b2ac2 [add] Rnn.py: unrolled character-level RNN-RBM matching docs/Rnn.drawio.png
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 10:49:46 +02:00
jensandClaude Sonnet 4.6 3edc124a5c [refactor] move tests to tests/, add pytest functions and main() entrypoints
- Moved src/tests/ → tests/
- Added test_* functions with assertions to script-style test files
- Added main() to each so IDEs offer it as a separate run target from pytest
- Fixed cupy_test.py: remove spurious x_gpu += x_cpu, fix duplicate xlabel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:32:58 +02:00
jensandClaude Sonnet 4.6 829547d271 [notebook] update cifar notebook outputs and add N_HID constant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:06:41 +02:00
jensandClaude Sonnet 4.6 0b4794de4e [fix] use np.asnumpy in SubImageCombine to support CuPy arrays
_np.asarray() raises TypeError on CuPy arrays; np.asnumpy() handles both.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 20:57:58 +02:00
jensandClaude Sonnet 4.6 1954064a54 [refactor] move rbm.model → model.model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:55:16 +02:00
jensandClaude Sonnet 4.6 b41274c7a6 [refactor] move rbm.torch → compat.torch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:36:21 +02:00
jensandClaude Sonnet 4.6 3d235b4214 [refactor] move rbm.label → label.label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:30:26 +02:00
jensandClaude Sonnet 4.6 807de19fa7 [fix] restore SubImageCombine name in cifar notebook (clobbered by bulk rename)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:23:08 +02:00
jensandClaude Sonnet 4.6 a07698497d [refactor] SubImage → SubImageExtract, add SubImageCombine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:20:38 +02:00
jensandClaude Sonnet 4.6 2da2262368 [refactor] move rbm.stack* + rbm.rnn_helper → stack/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:10:03 +02:00
jensandClaude Sonnet 4.6 b0d370a56b [refactor] move rbm.image → image.sub_image
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 08:01:51 +02:00
jensandClaude Sonnet 4.6 599ba4f6d5 [cifar_sub_image] run with STRIDE=6 (25 patches/image, ~33% overlap)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 20:07:10 +02:00
jensandClaude Sonnet 4.6 dddb937909 [cifar_sub_image] add full 32x32 reconstruction from sub-images with overlap blending
- Add PATCH/STRIDE params; STRIDE=4 for 50% overlap (49 patches per image)
- Reconstruct 32x32 by averaging overlapping patch contributions (assemble_overlapping pattern from test_faces_sub_image)
- All cells updated to use PATCH/STRIDE throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 19:59:30 +02:00
jensandClaude Sonnet 4.6 00993e29f1 [rnn_helper] add shift functions, ch2idx/idx2ch, and tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 16:43:33 +02:00
jensandClaude Sonnet 4.6 cce3943b27 [RNN-RBM] rename to RNN-RBM, add unrolled depth-2 mode, update README
- Rename context33.ipynb → RNN-RBM.ipynb
- Add UNROLL_DEPTH parameter (1 = shared weights, N = alternating entities)
- Training loop uses t % n_layers instead of hardcoded t % 2
- README: RNN-RBM section with signal-flow ASCII art, modes table, parameter table, papers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:22:00 +02:00
jensandClaude Sonnet 4.6 73ff6c2bb4 [context33] - momentum 0.9, epochs 500
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:47:27 +02:00
jensandClaude Sonnet 4.6 4a915f165a [context33] - increase momentum 0.5→0.7; run outputs 99% recon, 88% next-step
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:06:20 +02:00
jensandClaude Sonnet 4.6 586803856f [context33] - add batch tiling (N_REPEAT=500) for GPU utilisation
Tile the single sentence 500× to form a (500, T, 40) batch so matrix ops
are (500, 168) instead of (1, 168). Note: with identical rows the gradient
is mathematically equivalent to batch=1; real speedup requires diverse data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:03:03 +02:00
jensandClaude Sonnet 4.6 bbc03d37e1 [context33] - 200-char sentence, auto-uppercase, adaptive tick spacing
SENTENCE now accepts mixed case and strips non-vocab chars automatically.
Extended to ~200 chars using the Moby Dick opening passage.
Visualisation cells use adaptive tick spacing (max 40 labels) to handle
longer sequences without crowding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:15:01 +02:00
jensandClaude Sonnet 4.6 0beb7a5c1b [context33] - replace armadillo data with 20-char sentence, moby encoding
Remove context33.training.dat loading (and read_armadillo import).
Training data is now a hardcoded 20-character sentence encoded with the
same 40-char vocabulary as moby_rnn.ipynb (space .!? A-Z 0-9).

  SENTENCE = "MOBY DICK IS A WHALE"

Results: 95% reconstruction, 95% next-step prediction (one miss each
on the first character where context is zero).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:35:59 +02:00
jensandClaude Sonnet 4.6 5506fef0fa [context33] - fix row order; sequence now reads 1 5 6 9 … R U X
The training.dat file stores samples LIFO (last added in the C++ GUI = row 0).
Reversing the rows restores chronological order, matching the C++ sequence.

Next-step prediction remains 100%: model predicts x_{t+1} from h_t correctly
in the C++ insertion order 1 5 6 9 B F I L Q R U X.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:22:43 +02:00
jensandClaude Sonnet 4.6 2a6b00db88 [context33] - pyRBM port of context33.prj; predict next char from current context
Notebook reconstructs the C++ context33.prj configuration exactly:
  - shared-weights StackRnn (1 entity, reused every time step)
  - visible = [context(128) | x_t(40)] = 168 units, hidden = 128
  - all hyperparams from .prj: lr=0.05, momentum=0.5, epochs=1000,
    mini_batch=100, gibbs=3, rao_blackwell=True, weight_decay=0

Training data loaded directly from context33.training.dat (Armadillo format,
12 × 168): sensory one-hot chars decoded as 'XURQLIFB9651'.

Key design: training pairs are (h_t, x_{t+1}) not (h_{t-1}, x_t) —
context is first advanced by seeing x_t, then the model is trained to
predict the next character x_{t+1} from that context. Evaluation using
clamped Gibbs on h_t achieves 100% next-step prediction accuracy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 15:12:14 +02:00
jensandClaude Sonnet 4.6 952045b9f1 [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>
2026-05-31 14:37:08 +02:00
jensandClaude Sonnet 4.6 3698c85ed4 [StackRnn] - add unrolled (own-weights) mode; each position gets its own RBM
Previously a single shared-weight Entity processed every time step.  Now:
- Shared mode  (1 layer via make_layer):  original behaviour unchanged
- Unrolled mode (N layers via make_unrolled): layers[t] owns W_t, b_v_t, b_h_t

New API:
  make_unrolled(T, sensory_size, h_size, ...)  → list[Layer]
  next_entity()   → Entity for the upcoming step() call
  current_entity() → Entity from the most recent step() call
  is_shared        → bool

moby_rnn.ipynb: switch Build model cell to make_unrolled(T=100); update
  predict_next() to use rnn.next_entity() for position-correct Gibbs sampling
README_moby_rnn.md: redraw temporal-unrolling ASCII art showing per-position
  weights W_t; update parameter count and checkpoint file listing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 11:52:01 +02:00
jensandClaude Sonnet 4.6 0b302a9c12 [moby_rnn] - add README with architecture ASCII art; consolidate constants
- README_moby_rnn.md: single-step and temporally-unrolled ASCII diagrams,
  configuration table, notebook cell guide, generation API docs, references
- moby_rnn.ipynb: move all constants (T, NUM_SEQ, EVAL_CHARS, PRED_CHARS,
  N_GIBBS, SEED, SEQ_IDX) into the Build model cell under a Configuration
  header; rename H_SIZE → CONTEXT_SIZE throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 11:42:55 +02:00
jensandClaude Sonnet 4.6 eb1f96f4ec [StackRnn] - add recurrent RBM stack with character-level LM notebook
Implements StackRnn: a cascaded/recurrent RBM where the visible layer at
each time step is the concatenation of the previous hidden state (context)
and the current sensory input — V[t] = [context | x_t].  Weights are
shared across time steps (RTRBM-style concatenation variant).

- stack_rnn.py: StackRnn with step(), reconstruct(), reset(), train(),
  make_layer() factory; supports greedy layer-wise training over sequences
  of shape (num_seq, T, sensory_size)
- test_rnn.py: single-layer, two-layer, and save/load tests
- moby_rnn.ipynb: character-level language model on Moby Dick; one-hot
  encoding, clamped-Gibbs next-char prediction, free text generation,
  hidden-state trace and character-distribution visualisations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 11:27:07 +02:00
jensandClaude Sonnet 4.6 17429ddce8 [tests] - fix CuPy display in test_learn_encoded_labels; enable model.load in test_linear
test_learn_encoded_labels: add .get() for cupy→numpy conversion before imshow;
reduce num_epochs 10000→1000.
test_linear: uncomment model.load() to resume from saved weights.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 23:45:42 +02:00
jensandClaude Sonnet 4.6 37c6e33948 [CheckpointStatus] - add checkpoint status handler; Model.train() accepts status param
status.py: add CheckpointStatus(save_fn, update_interval) — prints progress
and calls save_fn at every report interval to persist model state mid-training.
model.py: Model.train() now accepts an optional Status instance; defaults to
plain Status() if none provided.
test_faces_sub_image.py: use CheckpointStatus(model.save) during training.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 22:42:01 +02:00
jensandClaude Sonnet 4.6 2f9fe1b66b [bugfix] - support large CPU-side datasets; fix numpy/CuPy interop
train.py: add _to_gpu() helper; convert mini-batches from CPU numpy to GPU
CuPy on the fly so large datasets can stay in RAM. Use numpy permutation for
numpy batches during shuffle. Limit status err_rms to first 5000 samples to
avoid GPU OOM on status checks.

model.py: replace np.copy() with np.array() — cupy.copy() does not convert
numpy arrays to CuPy; cupy.array() does. Fixes entity.forward() crash after
training when batch was passed as numpy.

test_faces_sub_image.py: load_patches normalizes on CPU and returns numpy,
avoiding multi-copy GPU OOM for large datasets (500 images ≈ 1.4 GB).
show_reconstructions converts numpy patches to CuPy before inference.
Add --n_images CLI arg.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 22:26:06 +02:00
jensandClaude Sonnet 4.6 8c10bda002 [README] - update faces_sub_image with training results and CLI args
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 21:15:02 +02:00
jensandClaude Sonnet 4.6 68b376657c [faces_sub_image] - add --mini_batch_size CLI arg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 21:13:25 +02:00
jensandClaude Sonnet 4.6 9911caae06 [README] - document cd_jens removal, dbv simplification, Gibbs sampling fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:55:00 +02:00
jensandClaude Sonnet 4.6 b5104ebc97 [bugfix] - remove dead cd_jens; fix cd_gaussian_binary dbv; fix cd_binary_gaussian Gibbs sampling
- Remove cd_jens: dead code never called, contained multiple bugs.
- cd_gaussian_binary: dbv formula simplified — b_v terms cancelled between
  positive and negative phases, making the subtraction redundant.
- cd_binary_gaussian: Gibbs loop (CD-k > 1) now properly samples both visible
  (sample(prob(...))) and hidden (+ sample_gaussian) states when
  do_rao_blackwell=False. Previously used means throughout, giving biased
  gradient estimates for k > 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:54:09 +02:00
jensandClaude Sonnet 4.6 a6bfdf8eaf [README] - document train() shuffling and elif fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:44:13 +02:00
jensandClaude Sonnet 4.6 003152518a [bugfix] - shuffle data each epoch; replace if chains with elif/else in train()
Shuffling eliminates systematic gradient bias from fixed mini-batch ordering.
elif/else raises ValueError for unrecognised entity types instead of silently
calling None and crashing with a cryptic TypeError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:43:42 +02:00
jensandClaude Sonnet 4.6 96f271f160 [faces_sub_image] - add --num_epochs CLI arg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:38:33 +02:00
jensandClaude Sonnet 4.6 edeeb1773b [README] - update faces_sub_image entry; bump N_HID to 256
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 20:18:46 +02:00
jensandClaude Sonnet 4.6 1d18983192 [README] - document L1/L2 batch-size scaling fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 19:59:34 +02:00
jensandClaude Sonnet 4.6 d4d3d33931 [bugfix] - fix L1/L2 scaling: move regularisation out of grad_compute into state_adjust
Regularisation terms (L1, L2, weight_decay) were being divided by mini_batch_size
in state_adjust along with the CD gradient. The CD gradient is a batch sum so
1/N normalisation is correct; regularisation penalties are per-weight and
batch-size independent. Separating them makes l1_lambda/l2_lambda directly
interpretable regardless of batch size.

Also adds --l1_lambda CLI arg to test_faces_sub_image.py and sets num_epochs=1000.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 19:58:44 +02:00
jensandClaude Sonnet 4.6 95b114003e [README] - document entity.py L1/L2 and image.py normalize fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:28:45 +02:00
jensandClaude Sonnet 4.6 abee870ada [bugfix] - fix L1 and L2 regularisation gradients in grad_compute
Previous code computed scalar norms and multiplied by W, giving gradients
that scaled with total weight magnitude rather than the correct per-element
derivatives:
  L1: gradient is λ·sign(W), not λ·‖W‖₁·W
  L2: gradient is 2λ·W, not λ·‖W‖₂²·W
Also removed the combined (l1+l2)*W term which incorrectly mixed the two.
weight_decay (correct L2 form λW) is kept as a separate term.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:26:06 +02:00