Skip to content

Commit e607e33

Browse files
committed
transformerless_lm: phonics layer -- word_spacing + pronounceability
Two new substrate primitives addressing concatenation + impossible lettering ('naygrumio', 'thouA', 'drinesa', 'mensFDoroyali'): - word_spacing: after word-token (rank >= n_chars), boost space token by phi. Encourages word-boundary spacing. - pronounceability: precomputed mask. Flags tokens with max consonant cluster > F(4)=3 same-letter triple (F(3)=2 reps) vowel ratio < 1/phi^2 ~ 0.382 At sampling, flagged tokens multiplied by 1/phi^pi ~ 0.221. Pure substrate: char-class arithmetic + Fibonacci-tier thresholds.
1 parent dcaf6a6 commit e607e33

1 file changed

Lines changed: 120 additions & 15 deletions

File tree

experiments/transformerless_lm/train_self_recursive.py

Lines changed: 120 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,89 @@ def build_vowel_start_mask(vocab: list) -> torch.Tensor:
667667
return mask
668668

669669

670+
def build_unpronounceable_mask(vocab: list) -> torch.Tensor:
671+
"""Mask = 1 for tokens with impossible-shape lettering:
672+
- max consonant cluster > F(4)=3
673+
- same letter triple (F(3)=2 repetitions of same char in a row)
674+
- vowel ratio < 1/phi^2 ~ 0.382 (too few vowels to be syllabic)
675+
Char tokens (len=1) are exempt. Pure substrate (char-class
676+
arithmetic + Fibonacci-tier thresholds).
677+
"""
678+
V = len(vocab)
679+
mask = torch.zeros(V)
680+
F = _FIB_NUMS_FOR_BIGRAM
681+
inv_phi2 = 1.0 / (_PHI_FOR_SAMPLING ** 2)
682+
for i, tok in enumerate(vocab):
683+
if not tok or len(tok) <= 1:
684+
continue
685+
# Skip if not all alphabetic (punctuation/contractions).
686+
if not all(c.isalpha() for c in tok):
687+
continue
688+
# Max consonant cluster.
689+
max_cluster = 0
690+
cur = 0
691+
for ch in tok:
692+
if ch in _IAMBIC_VOWELS:
693+
cur = 0
694+
else:
695+
cur += 1
696+
if cur > max_cluster:
697+
max_cluster = cur
698+
# Same-letter triple.
699+
triple = False
700+
for j in range(len(tok) - 2):
701+
if tok[j] == tok[j + 1] == tok[j + 2]:
702+
triple = True
703+
break
704+
# Vowel ratio.
705+
n_vowel = sum(1 for c in tok if c in _IAMBIC_VOWELS)
706+
vowel_ratio = n_vowel / len(tok)
707+
if max_cluster > F[4] or triple or vowel_ratio < inv_phi2:
708+
mask[i] = 1.0
709+
return mask
710+
711+
712+
def substrate_word_spacing(prev_tid: int, probs: torch.Tensor,
713+
vocab: list, n_chars: int = 65) -> torch.Tensor:
714+
"""After a word-token (rank >= n_chars), boost the space token to
715+
encourage word-boundary spacing. Prevents adjacent-word
716+
concatenation ("naygrumio", "thouA"). Pure substrate (rank tier +
717+
char-class identification of space).
718+
719+
Boost = phi (substrate-canonical). Penalty for other chars stays
720+
flat (no suppression to avoid breaking punctuation flow).
721+
"""
722+
if prev_tid < n_chars or not vocab:
723+
return probs
724+
space_idx = None
725+
for i in range(min(n_chars, len(vocab))):
726+
if vocab[i] == ' ':
727+
space_idx = i
728+
break
729+
if space_idx is None:
730+
return probs
731+
out = probs.clone()
732+
out[space_idx] = out[space_idx] * _PHI_FOR_SAMPLING
733+
return out / (out.sum() + 1e-8)
734+
735+
736+
def substrate_pronounceability(probs: torch.Tensor,
737+
unpronounceable_mask: torch.Tensor
738+
) -> torch.Tensor:
739+
"""Suppress tokens flagged as un-pronounceable (impossible shape).
740+
Multiplicative penalty by 1/phi^pi ~ 0.221.
741+
Pure substrate (precomputed shape filter).
742+
"""
743+
if unpronounceable_mask is None:
744+
return probs
745+
upm = unpronounceable_mask.to(probs.device).to(probs.dtype)
746+
penalty = 1.0 / (_PHI_FOR_SAMPLING ** math.pi)
747+
multiplier = 1.0 - upm * (1.0 - penalty)
748+
out = probs * multiplier
749+
return out / (out.sum() + 1e-8)
750+
return mask
751+
752+
670753
def substrate_phonotactics(cluster_len: int, probs: torch.Tensor,
671754
vowel_start_mask: torch.Tensor) -> torch.Tensor:
672755
"""When recent consonant cluster >= 2, boost vowel-starting tokens.
@@ -1124,7 +1207,8 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
11241207
vowel_start_mask: torch.Tensor = None,
11251208
end_vowels: list = None,
11261209
punct_mask: torch.Tensor = None,
1127-
newline_mask: torch.Tensor = None):
1210+
newline_mask: torch.Tensor = None,
1211+
unpronounceable_mask: torch.Tensor = None):
11281212
"""Sample n_new tokens autoregressively with substrate sampling AND
11291213
a substrate-canonical recency penalty.
11301214
@@ -1206,6 +1290,15 @@ def autoregressive_generate(model, prompt: torch.Tensor, n_new: int,
12061290
recent_list = seq[0, -13:].tolist()
12071291
probs[0] = substrate_rhyme_resonance(
12081292
recent_list, end_vowels, probs[0])
1293+
# Word spacing (post-word space pressure).
1294+
if vocab is not None and seq.shape[1] >= 1:
1295+
probs[0] = substrate_word_spacing(
1296+
int(seq[0, -1]), probs[0], vocab,
1297+
n_chars=n_chars_local)
1298+
# Pronounceability filter (suppress impossible shapes).
1299+
if unpronounceable_mask is not None:
1300+
probs[0] = substrate_pronounceability(
1301+
probs[0], unpronounceable_mask)
12091302
# Theme momentum (subject-matter coherence).
12101303
if token_signatures is not None and seq.shape[1] >= 1:
12111304
recent_list = seq[0, -13:].tolist()
@@ -1369,11 +1462,20 @@ def _single_stage_refine(model, draft, vocab_size, scorer, mode: str,
13691462
if vowel_start_mask is not None and cl_len >= 2:
13701463
pos_probs = substrate_phonotactics(
13711464
cl_len, pos_probs, vowel_start_mask)
1465+
# Word spacing (post-word space pressure).
1466+
pos_probs = substrate_word_spacing(
1467+
int(new[0, t_draft - 1]), pos_probs, vocab,
1468+
n_chars=n_chars_r)
1469+
# Pronounceability filter.
1470+
if unpronounceable_mask is not None:
1471+
pos_probs = substrate_pronounceability(
1472+
pos_probs, unpronounceable_mask)
1473+
# Rhyme resonance.
13721474
if end_vowels is not None:
1373-
recent_start = max(0, t_draft - 13)
1374-
recent_list = new[0, recent_start:t_draft].tolist()
1475+
recent_start_ev = max(0, t_draft - 13)
1476+
recent_list_ev = new[0, recent_start_ev:t_draft].tolist()
13751477
pos_probs = substrate_rhyme_resonance(
1376-
recent_list, end_vowels, pos_probs)
1478+
recent_list_ev, end_vowels, pos_probs)
13771479
# Theme momentum (subject-matter coherence).
13781480
if token_signatures is not None and t_draft >= 1:
13791481
recent_start = max(0, t_draft - 13)
@@ -1454,7 +1556,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
14541556
with torch.no_grad():
14551557
draft = autoregressive_generate(model, prompt, n_new=n_new,
14561558
vocab_size=vocab_size,
1457-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
1559+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
14581560
stages_out = {}
14591561
stages_out["initial"] = {"seq": draft.clone(),
14601562
"harmony": harmony_scorer(draft),
@@ -1467,7 +1569,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
14671569
n_iters=n_iters_per_stage,
14681570
resample_frac=resample_frac,
14691571
prompt_len=prompt_len,
1470-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
1572+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
14711573
stages_out["after_harmony"] = {"seq": draft.clone(),
14721574
"trajectory": h_traj,
14731575
"harmony": harmony_scorer(draft),
@@ -1480,7 +1582,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
14801582
n_iters=n_iters_per_stage,
14811583
resample_frac=resample_frac,
14821584
prompt_len=prompt_len,
1483-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
1585+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
14841586
stages_out["after_quality"] = {"seq": draft.clone(),
14851587
"trajectory": q_traj,
14861588
"harmony": harmony_scorer(draft),
@@ -1494,7 +1596,7 @@ def staged_refine(model, prompt, n_new, vocab_size,
14941596
n_iters=n_iters_per_stage,
14951597
resample_frac=resample_frac,
14961598
prompt_len=prompt_len,
1497-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
1599+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
14981600
stages_out["after_creativity"] = {"seq": draft.clone(),
14991601
"trajectory": c_traj,
15001602
"harmony": harmony_scorer(draft),
@@ -1528,7 +1630,7 @@ def iterative_refine(model, prompt, n_new, vocab_size,
15281630
# Step 1: initial draft.
15291631
draft = autoregressive_generate(model, prompt, n_new=n_new,
15301632
vocab_size=vocab_size,
1531-
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
1633+
temperature=temperature, bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
15321634
history = []
15331635
h0 = harmony_scorer(draft) if harmony_scorer is not None else None
15341636
q0 = quality_scorer(draft) if quality_scorer is not None else None
@@ -1936,13 +2038,15 @@ def quality_fn(seq_tokens):
19362038
end_vowels = build_end_vowel_per_token(vocab_for_bigram)
19372039
punct_mask = build_punct_mask(vocab_for_bigram)
19382040
newline_mask = build_newline_mask(vocab_for_bigram)
2041+
unpronounceable_mask = build_unpronounceable_mask(vocab_for_bigram)
19392042
print(f" symbol classes: {n_classes} | "
19402043
f"pronoun cand: {int(pronoun_mask.sum().item())} | "
19412044
f"vowel-start: {int(vowel_start_mask.sum().item())} | "
19422045
f"punct: {int(punct_mask.sum().item())} | "
19432046
f"newline: {int(newline_mask.sum().item())} | "
1944-
f"end-vowel toks: "
1945-
f"{sum(1 for v in end_vowels if v)}")
2047+
f"unpronounceable: "
2048+
f"{int(unpronounceable_mask.sum().item())} | "
2049+
f"end-vowel: {sum(1 for v in end_vowels if v)}")
19462050
else:
19472051
class_id_tensor = None
19482052
n_classes = 0
@@ -1951,6 +2055,7 @@ def quality_fn(seq_tokens):
19512055
end_vowels = None
19522056
punct_mask = None
19532057
newline_mask = None
2058+
unpronounceable_mask = None
19542059

19552060
# Active training base: starts as tiny_seed, GROWS by appending each
19562061
# cycle's best refined output -- only if (a) creativity > corpus
@@ -2018,14 +2123,14 @@ def quality_fn(seq_tokens):
20182123
draft = autoregressive_generate(
20192124
model, prompt_s, n_new=growth_n_new,
20202125
vocab_size=vocab_size, temperature=0.8,
2021-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
2126+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
20222127
refined_s, _ = staged_refine(
20232128
model, prompt_s, n_new=growth_n_new, vocab_size=vocab_size,
20242129
harmony_scorer=harmony_fn, quality_scorer=quality_fn,
20252130
creativity_scorer=creativity_fn,
20262131
n_iters_per_stage=30, resample_frac=0.35,
20272132
prompt_len=16, temperature=0.5,
2028-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
2133+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
20292134
samples.append((refined_s.squeeze(0).clone(),
20302135
creativity_fn(refined_s)))
20312136
# Sort by creativity desc, keep top K.
@@ -2095,14 +2200,14 @@ def quality_fn(seq_tokens):
20952200
final_gen = autoregressive_generate(model, prompt, n_new=n_new,
20962201
vocab_size=vocab_size,
20972202
temperature=0.8,
2098-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
2203+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
20992204
final_refined, _ = staged_refine(
21002205
model, prompt, n_new=n_new, vocab_size=vocab_size,
21012206
harmony_scorer=harmony_fn, quality_scorer=quality_fn,
21022207
creativity_scorer=creativity_fn,
21032208
n_iters_per_stage=200, resample_frac=0.35,
21042209
prompt_len=16, temperature=0.5,
2105-
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask)
2210+
bigram_prior=bigram_prior, vocab=vocab, token_signatures=token_signatures, active_vocab_size=active_vocab_size, class_id_tensor=class_id_tensor, n_classes=n_classes, pronoun_mask=pronoun_mask, vowel_start_mask=vowel_start_mask, end_vowels=end_vowels, punct_mask=punct_mask, newline_mask=newline_mask, unpronounceable_mask=unpronounceable_mask)
21062211

21072212
return {"name": name, "mode": "self_distillation",
21082213
"n_params": n_params,

0 commit comments

Comments
 (0)