README.md: dependencies, build instructions for all three targets (verified all six PRJ x CONFIG combinations build cleanly from a fresh make clean, plus make clean's CONFIG scoping, default invocation, incremental no-op rebuilds, and the install target), how to run each binary, GPU acceleration usage, and a project-files overview. CLAUDE.md's "Entry points" section still described main.cpp's old CREATE_TEST/TRAIN_TEST behavior -- missed this on the earlier pass that fixed the "Build" section's description of the same file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016K8Gu7Qejd11JbdiHZqYAs
119 lines
7.2 KiB
Markdown
119 lines
7.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Overview
|
|
|
|
C++11 library and set of executables implementing Restricted Boltzmann Machines (RBMs), stacked into
|
|
deep belief networks (`DeepStack`) or recurrent structures (`RnnStack`) for character-level text generation
|
|
("poet"). There are three build targets sharing the same core sources: a JUCE-based desktop GUI (`GUI`), a
|
|
console test harness (`TEST`), and a text-generation CLI (`POET`).
|
|
|
|
## Build
|
|
|
|
Build system is a hand-written `Makefile` (no CMake). Select the target with `PRJ` and the build flavor with
|
|
`CONFIG`:
|
|
|
|
```sh
|
|
make PRJ=POET CONFIG=release # builds build/release/poet.elf (default PRJ=POET, CONFIG=release)
|
|
make PRJ=TEST CONFIG=debug # builds build/debug/test.elf
|
|
make PRJ=GUI # builds build/release/rbm.elf, also builds the JUCE static lib first
|
|
make clean # removes build/${CONFIG}
|
|
```
|
|
|
|
The `GUI` target additionally depends on `juce/build/${CONFIG}/libjuce.a`, built via `make -C juce` (uses
|
|
`juce/Makefile`, `juce/pkg.mk`, `juce/JUCE-3.1.1.mk` against the vendored `juce/JUCE-3.1.1` source tree).
|
|
`install` copies `build/release/rbm.elf` to `${HOME}/bin`.
|
|
|
|
Object/dependency files land in `build/${CONFIG}/`; `.d` files are auto-included for header dependency
|
|
tracking. There is no separate lint command. `TEST_CXX_SRCS` builds `main.cpp` into `test.elf`, a
|
|
minimal dependency-free test suite: for each named project under `prj/<name>/`, it loads the project,
|
|
loads weights and training batch, runs a full up-pass/down-pass reconstruction (`DeepStack::upDownPass`),
|
|
and checks the reconstruction error is finite and beats a trivial per-feature-mean baseline. Prints
|
|
PASS/FAIL (or SKIP for known issues) per project and a summary line; exit code reflects only genuine
|
|
failures.
|
|
|
|
```sh
|
|
make PRJ=TEST CONFIG=debug && ./build/debug/test.elf
|
|
```
|
|
|
|
### GPU acceleration (optional)
|
|
|
|
`run_gpu.sh` runs any built binary with NVBLAS (`nvblas.conf`), which transparently intercepts Armadillo's
|
|
large matrix-multiply BLAS calls (`v_to_h`, `h_to_v`, CD gradients) and offloads them to an NVIDIA GPU via
|
|
cuBLAS, falling back to the system's OpenBLAS otherwise. No source changes — it's a runtime `LD_PRELOAD`
|
|
shim, opt-in only:
|
|
|
|
```sh
|
|
./run_gpu.sh ./build/release/poet.elf t some_text.txt
|
|
```
|
|
|
|
Requires an NVIDIA GPU + the CUDA runtime's `libnvblas.so` (already present on this machine, alongside
|
|
pyRBM's cupy setup). Confirmed via nvidia-smi utilization/memory during a real `poet` training run — no
|
|
behavior change, same output, just optionally GPU-accelerated matrix multiplies.
|
|
|
|
### External dependencies (system-installed, not vendored)
|
|
|
|
- **Armadillo** (`<armadillo>`, linked `-larmadillo`) — all matrix/vector math goes through `arma::mat`.
|
|
- **JsonCpp** (`<jsoncpp/json/json.h>`, linked `-ljsoncpp`) — project files (`*.prj`) and layer weight
|
|
metadata serialize to/from `Json::Value`.
|
|
- JUCE (vendored under `juce/`, only needed for the `GUI` target) plus its Linux deps: freetype, X11,
|
|
Xinerama, Xext, GL, pthread, dl, rt.
|
|
|
|
## Architecture
|
|
|
|
### Core RBM hierarchy
|
|
|
|
- **`Rbm`** (`Rbm.hpp/cpp`) — a single restricted Boltzmann machine: visible/hidden units, weights (`m_whv`),
|
|
biases (`m_bh`, `m_bv`), `Params` (learning rate, momentum, Gibbs sampling options, etc.), and the
|
|
contrastive-divergence training step (`cd`, `cd_hinton`, `cd_jens`, ...). Params and trained state
|
|
round-trip through `toJson()`/`fromJson()`.
|
|
- **`Layer`** (`Layer.hpp/cpp`) — wraps an `Rbm` with 2D visible geometry (`numVisibleX/Y`, for image-like
|
|
data) plus an optional context block (`numContext`, used by the RNN stack to feed in previous state).
|
|
Layers form an intrusive doubly-linked list via raw `next`/`prev` pointers (not `std::vector`); `root()`
|
|
walks to the first layer. Per-layer weights persist as separate `<prj>.Layer.<id>.{w,bh,bv}.dat` files
|
|
(`weightsLoad`/`weightsSave`), independent from the JSON project file.
|
|
- **`AStack`** (`AStack.hpp/cpp`) — abstract base owning the `Layer` linked list for a named project
|
|
("stack"). Handles layer add/remove, weight init/load/save for the whole stack, and the training-batch
|
|
matrix (`m_trainingBatch`, loaded from `*.training.dat`). Declares `train()` as pure virtual — subclasses
|
|
decide how data flows between layers.
|
|
- **`DeepStack`** — classic layer-wise DBN: `upPass`/`downPass`/`upDownPass` propagate a sample through
|
|
the full stack; `train()` greedily trains each layer on the representation produced by the layers below.
|
|
- **`RnnStack`** — treats the stack as a recurrent cell operating on one-hot character codes
|
|
(`NUM_CODES = 37`: space + a-z + 0-9). `step_forward` advances the recurrent state one timestep;
|
|
`v_to_vc`/`vc_to_v` pack/unpack the visible+context vector. Used by `poet.cpp` for text generation.
|
|
- **`StackCreator`** (`StackCreator.hpp/cpp`) — serializes/deserializes an `AStack` (and its `Layer`s) to/from
|
|
a `<name>.prj` JSON file. Reads `stack.type` to decide whether to instantiate a `DeepStack` or `RnnStack`.
|
|
Accepts an optional `LayerConstructor` callback so callers (e.g. the GUI) can substitute their own
|
|
`Layer` subclass (see `RbmComponent`) when building layers from JSON.
|
|
- **`RnnTextHelper`** / **`Matutils`** (`matutils.hpp`) — character <-> one-hot vector encoding
|
|
(`char2vec`/`vec2char`, `ch2idx`/`idx2ch`), training-batch construction from text files, and small
|
|
Armadillo helpers (`sample`, `prob`, `normalize`, `uniform`) used by the CD training routines.
|
|
|
|
### Entry points
|
|
|
|
- **`source/main.cpp`** — `TEST` target; the project test suite (see "Build" above) — loads each named
|
|
project under `prj/<name>/` and checks its reconstruction error.
|
|
- **`source/poet.cpp`** — `POET` target; loads an `RnnStack` project (default `poet_2v_5s`) and dispatches
|
|
on `argv[1]`: `c`(reate)/`r`(eset weights)/`t`(rain, optionally with a training-text path in `argv[2]`)/
|
|
`f`(orward-generate text, optional seed string in `argv[2]`). Training periodically calls back into
|
|
`forward()` to sample generated text as a progress check.
|
|
- **`source/gui.cpp`** + **`MainComponent`** — JUCE app entry point; `MainComponent` implements
|
|
`LayerConstructor` to build `RbmComponent` layers (interactive `Layer` subclass with sliders/toggles bound
|
|
to `Rbm::Params`) and drives training via the `Rbm::IListener` progress callback, mirroring what
|
|
`RbmListener` does in the CLI tools.
|
|
|
|
### Project files on disk
|
|
|
|
Each named project `<name>` (e.g. `mnist`, `poet_2v_5s`, `context99` — see the ~40 project folders under
|
|
`prj/`, mostly saved experiment artifacts) lives under `prj/<name>/` and consists of:
|
|
- `<name>.prj` — JSON describing the stack type and layer geometry (via `StackCreator`).
|
|
- `<name>.training.dat` — the training batch matrix.
|
|
- `<name>.Layer.<id>.w.dat` / `.bh.dat` / `.bv.dat` — per-layer weights/biases (Armadillo binary/text format).
|
|
|
|
`StackCreator`/`AStack` resolve these paths themselves (`{dir}/prj/{name}/{name}.*`) from whatever base
|
|
`dir` the caller passes (`poet.cpp`/the GUI's startup auto-load both pass `"."`, the repo root) — callers
|
|
never construct the `prj/<name>/` part themselves. These are experiment data and generated artifacts, not
|
|
something to edit by hand; treat them as fixtures unless a task specifically concerns training data or
|
|
saved models.
|