Files
pyRBM/CLAUDE.md
T
jensandClaude Sonnet 5 05dc6266fe Add CLAUDE.md with architecture overview and dev commands
Documents the RBM/Stack/Model composition patterns, the numpy/cupy
backend switch gotcha, and test/run commands for future Claude Code sessions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VSd9diGXwX5E9mAsw4Wp7R
2026-07-26 23:09:35 +02:00

9.1 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

pyRBM is a from-scratch, hand-derived implementation of Restricted Boltzmann Machines (RBMs) and Recurrent Temporal RBMs (RTRBMs) — no autodiff, no PyTorch training loop. Every gradient (Contrastive Divergence) is coded by hand in src/rbm/train.py. It's a research/educational codebase, not a competitive ML framework — see README.rbm.md for a full Q&A on the design rationale, why a Transformer can't be bolted on without adding autodiff, and how the src/compat/torch.py bridge relates to that.

Environment

  • Python >=3.12, project uses a .venv at the repo root (.venv/bin/python, .venv/bin/pytest).
  • Package is installed editable (pip install -e .), src-layout: src/{rbm,stack,model,label,image,compat} import as top-level packages (from rbm.entity import Entity, from stack.rnn import StackRnn, etc.) — there is no pyrbm. prefix.
  • GPU (CUDA) is used by default — see "CPU vs GPU" below.
  • No linter/formatter config exists (no ruff/black/flake8/mypy) and no pytest config file — match existing style (tabs for indentation in most src/rbm and src/stack files, spaces elsewhere; check the file you're editing).

Running tests

.venv/bin/pytest tests/                     # whole suite
.venv/bin/pytest tests/test_rnn.py -q       # single file
.venv/bin/pytest tests/test_rnn.py::test_rnn_shared -q      # single test

tests/README.md documents each test's purpose and is worth reading before adding new tests — it also has a fix-log at the bottom explaining several non-obvious bugs already found and fixed in train.py/entity.py (e.g. why L1/L2 regularization must NOT be scaled by 1/mini_batch_size, why CD-k>1 Gibbs loops must sample rather than use means). Some tests/*.py files are pytest suites (def test_...), others (test_faces_sub_image.py, test_norbs.py, test_rbm.py, etc.) are standalone scripts invoked directly (python tests/test_rbm.py <project_name>) that need external data/images not in the repo (see the table in tests/README.md).

CPU vs GPU (important gotcha)

src/rbm/matrix.py is the single backend switch for the entire codebase:

USE_CUDA = 1
if USE_CUDA:
    import cupy as np
else:
    import numpy as np

Every module imports the array module as np from here (from .matrix import np), so flipping USE_CUDA to 0 switches the whole codebase to CPU-only numpy with no other code changes. This machine has cupy-cuda12x + an RTX 4070 Ti, so GPU is the default/expected path. If you see cupy-related errors on a machine without a GPU, this flag is the first place to check. Note that cupy-cuda12x is a de-facto hard runtime dependency (nothing works with USE_CUDA=1 without it) but is not listed in pyproject.toml's dependencies — it's only present because it was installed manually into this .venv. torch is also installed but only backs src/compat/torch.py / notebook use, not core training.

Architecture

Core primitives (src/rbm/)

  • matrix.py — numpy/cupy backend switch (see above) + shared math helpers (sample, prob (sigmoid), gaussian, rms_error, read_armadillo for loading legacy Armadillo .dat files).
  • state.py (RbmState) — holds w_hv, b_v, b_h; pure linear algebra (v_to_h, h_to_v) plus .npz save/load.
  • entity.py (Entity) — one RBM unit. Entity.Type is one of BB_RBM/BG_RBM/GB_RBM/GG_RBM (binary/Gaussian visible × binary/Gaussian hidden), derived from EntityParams. Owns a RbmState (weights) and a grad (momentum accumulator). forward/reconstruct do (optionally multi-step) Gibbs sampling; h_given_v/v_given_h are the raw (unsampled) linear step. grad_compute / state_adjust apply momentum + L1/L2/weight-decay (regularization is applied at full strength, NOT scaled by mini-batch size — see the fix-log note above).
  • train.py — the four CD variants (cd_binary_binary, cd_gaussian_binary, cd_gaussian_gaussian, cd_binary_gaussian), each a positive-phase/negative-phase Gibbs pass mirroring the classic Hinton Matlab RBM code (comments in the file reference the original Matlab pseudocode). train() is the standard epoch/mini-batch loop for a single Entity, dispatching to the right CD function via entity.type and reporting through a Status.
  • status.py — pluggable progress reporting/checkpointing (Status, CheckpointStatus) driven by a callback (on_report) that can also halt training early.
  • layer.py (Layer) — thin wrapper: one Entity + name + save/load, sized from (numVisibleX, numVisibleY, numContext, numHidden).

Composition patterns — two different ways to build multi-entity models

  1. Stack family (src/stack/) — an ordered list[Layer] with save/load-by-index into {name}-{i}-state.npz files under work_dir.

    • StackDeep (deep.py) — classic layer-wise-pretrained deep belief net: train() trains layer 0, forwards its output as layer 1's input, etc. pass_up/pass_down/pass_down_up for encode/decode/round-trip through the stack.
    • StackRnn (rnn.py) — the RTRBM. Visible = [context h_{t-1} | sensory x_t]. Two modes based on how many layers you append():
      • Shared weights (1 layer): the same Entity is reused every time step; sequences can be any length T.
      • Unrolled (N layers): layers[t % N] handles time step t, each with its own weights; training requires sequences of length exactly N (uses "temporal-shift padding" — see the docstring in _train_unrolled). Use StackRnn.make_layer(...) / StackRnn.make_unrolled(...) to construct layers, .step(x) / .reconstruct(h) for inference, .train(sequences) for training.
    • factory.py (StackFactory) — builds a Stack from a JSON project dict/file (legacy .prj format from a prior C++ implementation this was ported from — field names like numVisibleX, doSampleBatch reflect that origin).
    • rnn_helper.py — one-hot vocab encode/decode (ch2idx/idx2ch/vec2idx/idx2vec) and visible-vector concat/split/shift_* helpers used by the RNN scripts/notebooks. VOCAB here is a small ad-hoc set (currently space + digits) — the top-level scripts/notebooks each define their own, larger vocab inline rather than importing this one.
  2. Model (src/model/model.py) — an alternate, reflection-based composition: subclass Model, assign Entity instances as plain attributes in __init__, implement forward(). Base class discovers entities via self.objects(Entity) (walks __dict__) for train/save/load/init, training each entity in sequence and feeding its output forward — used by test_model.py's DeepModel and by src/label/label.py's Label.Fitter.

Other modules

  • src/label/label.py (Label) — encodes discrete labels as binary or one-hot vectors, then fits a Model (an RBM) on the vectors so labels can be embedded/decoded through learned features.
  • src/image/sub_image.py — patch-based image tiling: SubImageExtract cuts overlapping patches (with stride) for training on image patches instead of whole images; SubImageCombine blends overlapping reconstructed patches back into a full image (pixel-averaged); also a small conv2d.
  • src/compat/torch.py — a thin PyTorch-optim-shaped wrapper (LossFunction, Optimizer with zero_grad()/step()) around the same hand-coded CD functions from train.py — it mimics the PyTorch API surface but is not autodiff; see README.rbm.md for why real backprop would require replacing the numpy/cupy backend with torch tensors throughout.

Top-level scripts and notebooks

These are the char-level RTRBM demos/experiments, each self-contained and runnable directly (python Rnn.py, no install needed — they sys.path.insert(0, "src") themselves):

  • Rnn.py — minimal unrolled-mode demo matching docs/Rnn.drawio.png (sliding window + context, small hard-coded text).
  • JayRnn.py / README_JayRnn.md — larger char-level language model, single shared-weight RBM, fixed vocabulary, checkpoint/resume via results/JayRnn-*-state.npz.
  • moby_rnn.ipynb / README_moby_rnn.md — the most fully worked example: trains on data/moby.txt, unrolled StackRnn with TEMPORAL_DEPTH positions, includes evaluation (teacher-forced reconstruction vs. honest next-step prediction), clamped-Gibbs text generation at multiple temperatures, and hidden-state visualization.
  • RNN-RBM.ipynb — the notebook the top-level README.md documents (context/sensory concat, UNROLL_DEPTH modes).
  • mnist_test*.ipynb, cifar_test*.ipynb, layer_test.ipynb — image-domain (non-recurrent) RBM/DBN experiments using data/MNIST and data/cifar-10-batches-py.

Checkpoints from all of the above land in results/{name}-{index}-state.npz, loaded automatically on the next run if present (RbmState.load silently no-ops on missing file/shape mismatch, so deleting a checkpoint is the way to force retraining from scratch).