[refactor] move rbm.stack* + rbm.rnn_helper → stack/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 08:10:03 +02:00
co-authored by Claude Sonnet 4.6
parent b0d370a56b
commit 2da2262368
12 changed files with 56 additions and 58 deletions
+17 -17
View File
@@ -18,7 +18,7 @@
"source": [
"import numpy as np_cpu\n",
"import matplotlib.pyplot as plt\n",
"from rbm.stack_rnn import StackRnn\n",
"from stack.rnn import StackRnn\n",
"from rbm.matrix import np, convert\n",
"from rbm.entity import EntityParams, TrainingParams\n",
"from rbm.status import CheckpointStatus"
@@ -42,7 +42,7 @@
}
},
"source": [
"# ── Configuration ─────────────────────────────────────────────────────────\n",
"# \u2500\u2500 Configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"TEMPORAL_DEPTH = 16 # characters per training sequence (= number of RBMs)\n",
"NUM_SEQ = 200 # training sequences (covers first 200k chars)\n",
"CONTEXT_SIZE = 128 # recurrent hidden / context state size\n",
@@ -73,12 +73,12 @@
}
},
"source": [
"# ── Load text and build character vocabulary ───────────────────────────────\n",
"# \u2500\u2500 Load text and build character vocabulary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"with open(\"data/moby.txt\", \"r\", encoding=\"utf-8\") as f:\n",
" raw = f.read()\n",
"\n",
"# Reduce vocabulary: uppercase letters, digits, and separating punctuation.\n",
"# Lowercase uppercase; everything else space; collapse runs of spaces.\n",
"# Lowercase \u2192 uppercase; everything else \u2192 space; collapse runs of spaces.\n",
"import re\n",
"ALLOWED = set(' .!?ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')\n",
"text = raw.upper()\n",
@@ -131,7 +131,7 @@
}
},
"source": [
"# ── Encode text as one-hot sequences ──────────────────────────────────────\n",
"# \u2500\u2500 Encode text as one-hot sequences \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# Each training sequence is TEMPORAL_DEPTH consecutive characters encoded as\n",
"# one-hot vectors. Non-overlapping; constants defined in Configuration.\n",
"encoded = np_cpu.array([char_to_idx[c] for c in text], dtype=np_cpu.int32)\n",
@@ -143,7 +143,7 @@
"\n",
"print(f\"sequences shape : {sequences.shape}\")\n",
"print(f\"memory : {sequences.nbytes / 1e6:.1f} MB\")\n",
"print(f\"covers chars : 0 {NUM_SEQ * TEMPORAL_DEPTH - 1:,}\")"
"print(f\"covers chars : 0 \u2013 {NUM_SEQ * TEMPORAL_DEPTH - 1:,}\")"
],
"outputs": [
{
@@ -152,7 +152,7 @@
"text": [
"sequences shape : (200, 16, 40)\n",
"memory : 1.0 MB\n",
"covers chars : 0 3,199\n"
"covers chars : 0 \u2013 3,199\n"
]
}
],
@@ -174,7 +174,7 @@
}
},
"source": [
"# ── Build model ────────────────────────────────────────────────────────────\n",
"# \u2500\u2500 Build model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# Unrolled mode: TEMPORAL_DEPTH separate RBMs, one per position in the sequence.\n",
"# Each RBM_t has its own W_t, b_v_t, b_h_t.\n",
"# visible_t = [context_{t-1} (CONTEXT_SIZE) | x_t (vocab_size)]\n",
@@ -234,7 +234,7 @@
}
},
"source": [
"# ── Train ──────────────────────────────────────────────────────────────────\n",
"# \u2500\u2500 Train \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# CheckpointStatus saves weights at every progress report.\n",
"# Re-run this cell to continue training from the last checkpoint.\n",
"status = CheckpointStatus(rnn.state_save, update_interval=5)\n",
@@ -352,7 +352,7 @@
}
},
"source": [
"# ── Generation helpers ─────────────────────────────────────────────────────\n",
"# \u2500\u2500 Generation helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"\n",
"def predict_next(rnn, context, n_gibbs=10, temperature=1.0):\n",
" \"\"\"Return a character probability distribution conditioned on context.\n",
@@ -435,7 +435,7 @@
}
},
"source": [
"# ── Reconstruction accuracy (teacher-forced) ───────────────────────────────\n",
"# \u2500\u2500 Reconstruction accuracy (teacher-forced) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# At each step the model sees the true x_t, updates h_t, then reconstructs\n",
"# x_t from h_t. This measures how well h_t retains the input, not prediction.\n",
"EVAL_START = NUM_SEQ * TEMPORAL_DEPTH\n",
@@ -482,7 +482,7 @@
}
},
"source": [
"# ── Next-step prediction accuracy ─────────────────────────────────────────\n",
"# \u2500\u2500 Next-step prediction accuracy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# Given context (= h_{t-1}), predict x_t before observing it.\n",
"rnn.reset(batch_size=1)\n",
"h = np.zeros((1, CONTEXT_SIZE))\n",
@@ -534,7 +534,7 @@
}
},
"source": [
"# ── Free text generation ───────────────────────────────────────────────────\n",
"# \u2500\u2500 Free text generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"for temp in [0.5, 1.0, 1.5]:\n",
" print(f\"\\n{'='*60}\")\n",
" print(f\"Temperature = {temp}\")\n",
@@ -582,7 +582,7 @@
}
},
"source": [
"# ── Hidden state trace ─────────────────────────────────────────────────────\n",
"# \u2500\u2500 Hidden state trace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# Plot the hidden unit activations over one sequence to see what the RNN\n",
"# has learned to track.\n",
"rnn.reset(batch_size=1)\n",
@@ -605,7 +605,7 @@
"plt.colorbar(label='h activation')\n",
"plt.xlabel('Time step (character)')\n",
"plt.ylabel('Hidden unit (first 64)')\n",
"plt.title(f'Hidden state trace \"{seq_str[:50]}\"')\n",
"plt.title(f'Hidden state trace \u2014 \"{seq_str[:50]}\u2026\"')\n",
"tick_pos = range(0, TEMPORAL_DEPTH, 5)\n",
"plt.xticks(tick_pos, [seq_str[i] for i in tick_pos], fontsize=8)\n",
"plt.tight_layout()\n",
@@ -655,7 +655,7 @@
}
},
"source": [
"# ── Character frequency: data vs model predictions ─────────────────────────\n",
"# \u2500\u2500 Character frequency: data vs model predictions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
"# Compare the character distribution in the training data to the distribution\n",
"# the model assigns when conditioning on a fixed context.\n",
"true_counts = np_cpu.bincount(eval_enc[:500].astype(int), minlength=vocab_size).astype(float)\n",
@@ -730,4 +730,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}