rework forward_step to use char-by-char state priming with SPACE token

Replace space ' ' with explicit SPACE='_' vocab token to disambiguate
padding from real spaces, and rewrite forward_step to take a single
character plus rolling state string instead of a reconstructed vc matrix,
producing predictions one character at a time per unit.
This commit is contained in:
2026-06-07 13:34:02 +02:00
parent 51274ee1ae
commit 2d3361d5ce
2 changed files with 33 additions and 27 deletions
+31 -26
View File
@@ -19,12 +19,12 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from rbm.entity import EntityParams, TrainingParams, Entity from rbm.entity import EntityParams, TrainingParams, Entity
from rbm.matrix import np, Mat from rbm.matrix import np, Mat
from model.model import Model from model.model import Model
from stack.rnn_helper import vocab_size, shift_left, clamp, concat, split, vec2idx, idx2ch, ch2idx, idx2vec from stack.rnn_helper import vocab_size, shift_left, clamp, concat, split, vec2idx, idx2ch, ch2idx, idx2vec, SPACE
from rbm.train import train from rbm.train import train
from rbm.status import Status from rbm.status import Status
# ── Hyper-parameters ────────────────────────────────────────────────────────── # ── Hyper-parameters ──────────────────────────────────────────────────────────
TEXT = "Call me Ishmael. Some years ago" # the character sequence to learn (matches diagram example) TEXT = "123456789_Call_me_Ishmael._Some_years_ago" # the character sequence to learn (matches diagram example)
WIN = 2 # sliding-window width (= N in the diagram) WIN = 2 # sliding-window width (= N in the diagram)
STRIDE = 1 # sliding-window step size STRIDE = 1 # sliding-window step size
UNITS = 5 UNITS = 5
@@ -55,7 +55,7 @@ def str2vec(ch_str: str) -> Mat:
def to_window(text: str) -> list[str]: def to_window(text: str) -> list[str]:
result = [] result = []
_win = [' ']*UNITS _win = [SPACE]*UNITS
for _i in range(WIN - 1): for _i in range(WIN - 1):
for ch in text: for ch in text:
_win = _win[1:WIN] + [ch] _win = _win[1:WIN] + [ch]
@@ -88,7 +88,8 @@ def batch_delay(_batch: Mat, delay=0):
return result return result
def vc2char(_vc: Mat): def vc2char(_vc: Mat):
_v, _ = split(_vc.reshape(1, WIN*vocab_size()+H_SIZE), H_SIZE, axis=1) __vc = _vc.reshape([1, WIN*vocab_size()+H_SIZE])
_v, _ = split(__vc, H_SIZE, axis=1)
return v2char(_v) return v2char(_v)
def v2char(_v: Mat): def v2char(_v: Mat):
@@ -112,25 +113,29 @@ class RnnModel(Model):
_batch = to_batch(_win_text, index) _batch = to_batch(_win_text, index)
_vc = concat(_batch, _c, axis=1) _vc = concat(_batch, _c, axis=1)
for i in range(_vc.shape[0]): for i in range(_vc.shape[0]):
print(f"train: {unit.name}:{vc2char(_vc[i,:])}") print(f"train[{index}:{i:03}]: {unit.name}:{vc2char(_vc[i,:])}")
train(unit, _vc, status) train(unit, _vc, status)
# For the next unit: Update context portion of vc # For the next unit: Update context portion of vc
_c = unit.forward(_vc) _c = unit.forward(_vc)
def forward_step(self, _vc: Mat): def forward_step(self, _ch: str, _state: str):
text = '' _state = _state[len(_ch):] + _ch
for unit in self.units: _c = np.zeros([len(_ch), H_SIZE])
print(f"forward_step in : {unit.name}:{vc2char(_vc)}") _ch_predict = '?'
for index, unit in enumerate(self.units):
_s = _state[index] + SPACE*(WIN-1)
print(f"forward_step in : {unit.name}:{_s}")
_v = str2vec(_s).reshape(1, WIN*vocab_size())
_vc = concat(_v, _c, axis=1)
_c = unit.forward(_vc) _c = unit.forward(_vc)
_vc = unit.reconstruct(_c) _vc = unit.reconstruct(_c)
print(f"forward_step out: {unit.name}:{vc2char(_vc)}") _r, _ = split(_vc, H_SIZE, axis=1)
text += vc2char(_vc)[WIN-1] _r_cl = clamp(_r.reshape([WIN, vocab_size()]), axis=1)
_v, _ = split(_vc, H_SIZE, axis=1)
_v_cl = clamp(_v.reshape([WIN, vocab_size()]), axis=1) _ch_predict = v2char(_r_cl)
_v = shift_left(_v_cl.reshape([1, WIN*vocab_size()]), vocab_size()) print(f"forward_step out: {unit.name}:{_ch_predict}")
_vc = concat(_v, _c, axis=1) return _state, _ch_predict[-1]
return _vc, text
def forward(self, x: Mat): def forward(self, x: Mat):
pass pass
@@ -163,14 +168,14 @@ if __name__ == "__main__":
model.save() model.save()
# test the model # test the model
seed_str = ' ' seed_str = '1234'
seed_padded = seed_str + ' '*(WIN-len(seed_str)) state = SPACE*UNITS
v_test = str2vec(seed_padded).reshape([1, WIN*vocab_size()])
c_test = np.zeros([1, H_SIZE]) # Prime
vc_test = concat(v_test, c_test, axis=1) for s in seed_str:
text = '' state, ch = model.forward_step(s, state)
for i in range(len(TEXT)): print(f'Predict: {ch}')
vc_test, text_frag = model.forward_step(vc_test)
text += text_frag state, ch = model.forward_step('_', state)
print(f'Predict: {ch}')
print(text)
+2 -1
View File
@@ -1,6 +1,7 @@
from rbm.matrix import Mat, np from rbm.matrix import Mat, np
VOCAB = ' -.!?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' SPACE='_'
VOCAB = SPACE + '-.!?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
_CH2IDX = {ch: i for i, ch in enumerate(VOCAB)} _CH2IDX = {ch: i for i, ch in enumerate(VOCAB)}
def clamp(vec: Mat, axis=0): def clamp(vec: Mat, axis=0):