Skip to content

Commit 2663c7f

Browse files
committed
transformerless_lm: vocab curriculum (F-tier expansion)
Active sampling vocab expands through Fibonacci tiers per cycle: F(8)=21 -> F(9)=34 -> ... -> F(12)=144 -> full V. Counter-balances K-shrink: as substrate capacity tightens, lexicon widens. Cycle 1 constrains to functional/common tokens; later cycles unlock content words and proper nouns. Pure substrate (Fibonacci-tier walk).
1 parent a2944ff commit 2663c7f

1 file changed

Lines changed: 51 additions & 13 deletions

File tree

experiments/transformerless_lm/train_self_recursive.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,24 @@ def substrate_theme_momentum(recent_tokens: list,
478478
return out / (out.sum() + 1e-8)
479479

480480

481+
def substrate_vocab_curriculum(probs: torch.Tensor,
482+
active_vocab_size: int) -> torch.Tensor:
483+
"""Vocabulary expansion curriculum: restrict sampling to the first
484+
`active_vocab_size` tokens. As substrate K shrinks across cycles,
485+
vocab EXPANDS through Fibonacci tiers (F(8)=21 -> F(12)=144 -> full).
486+
Functional/common tokens always available; content/proper nouns
487+
unlock progressively. Pure substrate (Fibonacci-tier walk).
488+
"""
489+
if active_vocab_size <= 0 or active_vocab_size >= probs.shape[0]:
490+
return probs
491+
out = probs.clone()
492+
out[active_vocab_size:] = 0.0
493+
s = out.sum()
494+
if s < 1e-8:
495+
return probs
496+
return out / s
497+
498+
481499
def substrate_golden_phase(t_pos: int, probs: torch.Tensor,
482500
vocab_size: int) -> torch.Tensor:
483501
"""Golden-angle phase: functional/content rhythm primitive.
@@ -743,7 +761,8 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
743761
recency_penalty: bool = True,
744762
bigram_prior: torch.Tensor = None,
745763
vocab: list = None,
746-
token_signatures: torch.Tensor = None):
764+
token_signatures: torch.Tensor = None,
765+
active_vocab_size: int = None):
747766
"""Sample n_new tokens autoregressively with substrate sampling AND
748767
a substrate-canonical recency penalty.
749768
@@ -796,6 +815,10 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
796815
history_aw = seq[0, -21:]
797816
probs[0] = substrate_anti_stagnation(history_aw, probs[0],
798817
vocab_size)
818+
# Vocab curriculum (applied last; hard mask high-rank).
819+
if active_vocab_size is not None:
820+
probs[0] = substrate_vocab_curriculum(
821+
probs[0], active_vocab_size)
799822
next_tok = torch.multinomial(probs, num_samples=1)
800823
seq = torch.cat([seq, next_tok], dim=1)
801824
model.train()
@@ -808,7 +831,8 @@ def _single_stage_refine(model, draft, vocab_size, scorer, mode: str,
808831
patience: int = 5,
809832
bigram_prior: torch.Tensor = None,
810833
vocab: list = None,
811-
token_signatures: torch.Tensor = None):
834+
token_signatures: torch.Tensor = None,
835+
active_vocab_size: int = None):
812836
"""One refinement stage: optimize a single score until plateau.
813837
814838
mode: 'min' (harmony, quality) or 'max' (creativity).
@@ -883,6 +907,10 @@ def _single_stage_refine(model, draft, vocab_size, scorer, mode: str,
883907
history_aw = new[0, aw_start:t_draft]
884908
pos_probs = substrate_anti_stagnation(
885909
history_aw, pos_probs, vocab_size_local)
910+
# Vocab curriculum (hard mask high-rank).
911+
if active_vocab_size is not None:
912+
pos_probs = substrate_vocab_curriculum(
913+
pos_probs, active_vocab_size)
886914
new[0, t_draft] = torch.multinomial(
887915
pos_probs, num_samples=1).item()
888916

@@ -914,7 +942,8 @@ def staged_refine(model, prompt, n_new, vocab_size,
914942
temperature: float = 0.5,
915943
bigram_prior: torch.Tensor = None,
916944
vocab: list = None,
917-
token_signatures: torch.Tensor = None):
945+
token_signatures: torch.Tensor = None,
946+
active_vocab_size: int = None):
918947
"""Staircase refinement: hit one score, then the next, then the next.
919948
920949
Stage 1: substrate alignment (minimize harmony) -- match the shape.
@@ -930,7 +959,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
930959
with torch.no_grad():
931960
draft = autoregressive_generate(model, prompt, n_new=n_new,
932961
vocab_size=vocab_size,
933-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
962+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
934963
stages_out = {}
935964
stages_out["initial"] = {"seq": draft.clone(),
936965
"harmony": harmony_scorer(draft),
@@ -943,7 +972,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
943972
n_iters=n_iters_per_stage,
944973
resample_frac=resample_frac,
945974
prompt_len=prompt_len,
946-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
975+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
947976
stages_out["after_harmony"] = {"seq": draft.clone(),
948977
"trajectory": h_traj,
949978
"harmony": harmony_scorer(draft),
@@ -956,7 +985,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
956985
n_iters=n_iters_per_stage,
957986
resample_frac=resample_frac,
958987
prompt_len=prompt_len,
959-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
988+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
960989
stages_out["after_quality"] = {"seq": draft.clone(),
961990
"trajectory": q_traj,
962991
"harmony": harmony_scorer(draft),
@@ -970,7 +999,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
970999
n_iters=n_iters_per_stage,
9711000
resample_frac=resample_frac,
9721001
prompt_len=prompt_len,
973-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1002+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
9741003
stages_out["after_creativity"] = {"seq": draft.clone(),
9751004
"trajectory": c_traj,
9761005
"harmony": harmony_scorer(draft),
@@ -1004,7 +1033,7 @@ def iterative_refine(model, prompt, n_new, vocab_size,
10041033
# Step 1: initial draft.
10051034
draft = autoregressive_generate(model, prompt, n_new=n_new,
10061035
vocab_size=vocab_size,
1007-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1036+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
10081037
history = []
10091038
h0 = harmony_scorer(draft) if harmony_scorer is not None else None
10101039
q0 = quality_scorer(draft) if quality_scorer is not None else None
@@ -1425,9 +1454,18 @@ def quality_fn(seq_tokens):
14251454
global_step = 0
14261455
prompt = train_seed[:16].unsqueeze(0)
14271456

1457+
# Substrate vocab curriculum: tier-walked Fibonacci expansion.
1458+
# Cycle 1: F(8)=21 word slots; cycle 6: full vocab.
1459+
_VOCAB_TIERS = [21, 34, 55, 89, 144] # F(8)..F(12)
1460+
n_chars = sum(1 for t in (vocab or []) if len(t) == 1) if vocab else 65
14281461
for cycle in range(n_cycles):
1462+
if cycle < len(_VOCAB_TIERS):
1463+
active_vocab_size = n_chars + _VOCAB_TIERS[cycle]
1464+
else:
1465+
active_vocab_size = None # full vocab unlocked
14291466
print(f"\n --- Cycle {cycle+1}/{n_cycles} "
1430-
f"active_base_size={active_base.numel()} chars "
1467+
f"active_base_size={active_base.numel()} chars "
1468+
f"active_vocab={active_vocab_size or vocab_size} "
14311469
f"best_creativity={best_creativity:.4f} ---", flush=True)
14321470
for s in range(steps_per_cycle):
14331471
new_K = sched(global_step, args.steps)
@@ -1471,14 +1509,14 @@ def quality_fn(seq_tokens):
14711509
draft = autoregressive_generate(
14721510
model, prompt_s, n_new=growth_n_new,
14731511
vocab_size=vocab_size, temperature=0.8,
1474-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1512+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
14751513
refined_s, _ = staged_refine(
14761514
model, prompt_s, n_new=growth_n_new, vocab_size=vocab_size,
14771515
harmony_scorer=harmony_fn, quality_scorer=quality_fn,
14781516
creativity_scorer=creativity_fn,
14791517
n_iters_per_stage=30, resample_frac=0.35,
14801518
prompt_len=16, temperature=0.5,
1481-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1519+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
14821520
samples.append((refined_s.squeeze(0).clone(),
14831521
creativity_fn(refined_s)))
14841522
# Sort by creativity desc, keep top K.
@@ -1548,14 +1586,14 @@ def quality_fn(seq_tokens):
15481586
final_gen = autoregressive_generate(model, prompt, n_new=n_new,
15491587
vocab_size=vocab_size,
15501588
temperature=0.8,
1551-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1589+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
15521590
final_refined, _ = staged_refine(
15531591
model, prompt, n_new=n_new, vocab_size=vocab_size,
15541592
harmony_scorer=harmony_fn, quality_scorer=quality_fn,
15551593
creativity_scorer=creativity_fn,
15561594
n_iters_per_stage=200, resample_frac=0.35,
15571595
prompt_len=16, temperature=0.5,
1558-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures)
1596+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size)
15591597

15601598
return {"name": name, "mode": "self_distillation",
15611599
"n_params": n_params,

0 commit comments

Comments
 (0)