summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVoid Agent <void@jayrup.hermes>2026-08-02 15:31:53 +0100
committerVoid Agent <void@jayrup.hermes>2026-08-02 15:31:53 +0100
commit3bd33fbf685c6b16747d6ec4c1026d4fa6d04966 (patch)
tree62a3e6eca548365d03b3a5becfbe1f89e144d052
parentf65bfa0fc338769b36b9091ac10241de878549fa (diff)
Fix in-word metric: strict (both-sides, 58% random / 0% clean) vs touches (95%); blog/results caveat corrected to the strict discriminator
-rw-r--r--docs/blog-jlens-frequency.md13
-rw-r--r--results.md11
-rw-r--r--src/synthetic_pair.py10
3 files changed, 20 insertions, 14 deletions
diff --git a/docs/blog-jlens-frequency.md b/docs/blog-jlens-frequency.md
index ceb97d5..4b2b429 100644
--- a/docs/blog-jlens-frequency.md
+++ b/docs/blog-jlens-frequency.md
@@ -242,12 +242,13 @@ predictability* (what "verbalizable" should mean) depends on a control that is
still running — see the caveat below.
One caveat, found by a reviewer: the noise token '#' was inserted at random
-character positions, which slices *inside* words ~95% of the time (th#e,
-ki#ng), while '@' always sits at a clean word boundary after "the ". That
-means predictability is not perfectly isolated from n-gram corruption. We are
-running a clean-boundary control (noise token inserted after random word
-boundaries — still unpredictable, no word-slicing) to rule it out; the numbers
-above should be read with that caveat until the control lands.
+character positions, which slices through the middle of a word 58% of the time
+(th#e, ki#ng — letter on both sides), while '@' always sits at a clean word
+boundary after "the ". Predictability is therefore not perfectly isolated from
+n-gram corruption. We are running a clean-boundary control (noise token
+inserted after random word boundaries — 0% word-slicing, still unpredictable)
+to rule it out; the numbers above should be read with that caveat until the
+control lands.
## 8. The causal test: what actually happened
diff --git a/results.md b/results.md
index afb130e..88834c0 100644
--- a/results.md
+++ b/results.md
@@ -113,11 +113,12 @@ Middle-layer ratio across seeds: 1.47 +/- 0.09 (SD), bootstrap 95% CI
genuine conditional-predictability signal.
CAVEAT (from adversarial review): '#' was inserted at uniform random character
-positions, which slices inside words ~95% of the time (th#e, ki#ng); '@'
-always sits at a clean word boundary after "the ". Predictability is therefore
-not perfectly isolated from n-gram corruption. A clean-boundary control (noise
-token after random word boundaries) is planned; the numbers above should be
-read with that caveat until it lands.
+positions, which slices through the middle of a word 58% of the time (letter
+on both sides: th#e, ki#ng); '@' always sits at a clean word boundary after
+"the ". Predictability is therefore not perfectly isolated from n-gram
+corruption. A clean-boundary control (noise token after random word
+boundaries, 0% word-slicing) is running; the numbers above should be read with
+that caveat until it lands.
## 3. Loss-reweighting causal test (`src/loss_reweight.py`)
diff --git a/src/synthetic_pair.py b/src/synthetic_pair.py
index 4c64379..bfe5b8b 100644
--- a/src/synthetic_pair.py
+++ b/src/synthetic_pair.py
@@ -72,8 +72,11 @@ def prep(clean=False):
noise_pos = sorted(rng.choice(avail, size=n_target, replace=False).tolist())
# sanity: fraction of noise insertions that slice inside a word
- in_word = sum(1 for p in noise_pos
- if 0 < p < len(text) and (text[p - 1].isalnum() and text[p].isalnum()))
+ # strict: letter on BOTH sides (th#e, ki#ng); touches: letter on either side
+ strict = sum(1 for p in noise_pos
+ if 0 < p < len(text) and (text[p - 1].isalnum() and text[p].isalnum()))
+ touches = sum(1 for p in noise_pos
+ if 0 < p < len(text) and (text[p - 1].isalnum() or text[p].isalnum()))
# insert with offset (both sets sorted -> single merge pass)
insertions = [(p, T_STRUCT) for p in struct_pos] + [(p, T_NOISE) for p in noise_pos]
@@ -91,7 +94,8 @@ def prep(clean=False):
print(f"prep({mode}): '{T_STRUCT}' x{n_target} after '{TRIGGER.strip()}', "
f"'{T_NOISE}' x{n_target} {mode}, "
f"freq each = {n_target/len(modified):.4%}, "
- f"in-word '#' = {in_word}/{n_target} ({in_word/n_target:.1%})")
+ f"in-word '#' = {strict}/{n_target} ({strict/n_target:.1%}), "
+ f"touches word = {touches}/{n_target} ({touches/n_target:.1%})")
# build vocab (existing chars + the two synthetic)
chars = sorted(set(text)) + [T_STRUCT, T_NOISE]