vukrosic commited on
Commit
c2633b0
·
verified ·
1 Parent(s): b876e8e

Upload data_spell.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. data_spell.py +148 -0
data_spell.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """spell — a code-generated supervised task: a misspelled word -> the correct word.
2
+
3
+ The honest nano-task test: correcting a typo needs a learned lexicon AND a sense of
4
+ which real word a misspelling most likely meant. The realistic script baseline is
5
+ "nearest dictionary word by edit distance" — but it has no frequency prior, so when a
6
+ typo lands as close to a wrong word as to the right one (`bcak` -> back? buck? beck?)
7
+ it guesses, and it breaks on transpositions and double-letters. The model packs a
8
+ frequency-weighted vocabulary into ~1M params and ranks the correction.
9
+
10
+ Each example is one line::
11
+
12
+ recieve => receive
13
+ teh => the
14
+ definately => definitely
15
+
16
+ Generation is answer-first and correct by construction: sample a real word from a
17
+ fixed, frequency-ordered vocabulary (Zipf-weighted so common words dominate), then
18
+ apply 1-2 realistic typo edits (delete, insert, substitute a keyboard neighbour,
19
+ transpose adjacent, double a letter). Because we start from the real word, the label
20
+ is ground truth. ~15% of examples are the identity (already-correct word) so the
21
+ model learns to leave correct words alone. The prompt is everything up to and
22
+ including ``" => "``; the target is the correct word. Byte-level vocab (256).
23
+ ``spell_pairs`` is imported by both the dataset and the eval so train and test share
24
+ one data path. The vocabulary is the same frequency-ordered ~480-word list used
25
+ across the nano-* lexicon models.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import numpy as np
31
+
32
+ _VOWELS = frozenset("aeiou")
33
+
34
+ # A fixed, roughly frequency-ordered vocabulary of common English words (most
35
+ # common first). This list IS the lexicon prior the model learns; earlier words
36
+ # are sampled more often (Zipf), so corrections resolve toward the common word.
37
+ _RAW = """
38
+ the be to of and a in that have it for not on with he as you do at this but his by
39
+ from they we say her she or an will my one all would there their what so up out if
40
+ about who get which go me when make can like time no just him know take people into
41
+ year your good some could them see other than then now look only come its over think
42
+ also back after use two how our work first well way even new want because any these
43
+ give day most us man find here thing tell very big small great little own under last
44
+ right move place such again off play move where turn need start show point try kind
45
+ hand high old life feel three world school still state never become between high
46
+ really something most family leave word both water side without head house seem
47
+ end ask group never word turn problem child city week company system program question
48
+ during number course company point case fact night area money story result job book
49
+ word eye door health person art war history party result change morning reason
50
+ research girl guy moment air teacher force education foot boy age policy music
51
+ market sense nation plan college interest death course experience effort water late
52
+ example heart paper space ground form event official matter center couple site
53
+ project activity star table court produce stock paint deep base camp signal critic
54
+ gold storm phone north union deal author cover plant flag worth wide class
55
+ green field add light cross hard early hold listen body keep watch human seven send
56
+ build stay fall reach kill remain suggest raise pass sell require report decide
57
+ pull return explain hope develop carry break receive agree support hit produce
58
+ eat cover catch draw choose cause point talk lead serve die remember love
59
+ consider appear buy wait serve die send expect build stay fall cut reach
60
+ read spend grow open walk win offer remember love consider appear wear
61
+ green blue red black white brown gray pink quick brave clean clear close
62
+ deep dry fair fine flat fresh full glad hot huge late loud nice plain proud
63
+ rare rich ripe rough safe sharp short slow soft sour sweet tall thick thin tight
64
+ warm weak wet wild wise young above across along among around behind below beneath
65
+ beside beyond inside outside toward within against during except toward
66
+ animal apple bird boat bread brother bridge button camera candle carpet castle
67
+ cattle circle cloud coast color corner cotton cousin danger dinner doctor dollar
68
+ dragon dream eagle earth engine farmer feather finger flower forest friend garden
69
+ ghost glass grass guard heaven honey island jacket jungle ladder leather letter
70
+ lion magic market mirror monkey mother mountain needle nephew object ocean office
71
+ orange palace parent pencil pepper picture pillow planet pocket potato prince queen
72
+ rabbit rather record river rocket sailor school season silver singer sister snake
73
+ spider spring statue summer sunset symbol table teacher temple thread throat thunder
74
+ ticket tiger toast tower travel turtle uncle valley village voice wagon weather
75
+ window winter wizard wonder yellow zebra ocean orange forest planet flower garden
76
+ """.split()
77
+
78
+ # rough QWERTY left/right neighbours for substitution typos
79
+ _NEIGHBORS = {
80
+ "a": "sq", "b": "vn", "c": "xv", "d": "sf", "e": "wr", "f": "dg", "g": "fh",
81
+ "h": "gj", "i": "uo", "j": "hk", "k": "jl", "l": "k", "m": "n", "n": "bm",
82
+ "o": "ip", "p": "o", "q": "wa", "r": "et", "s": "ad", "t": "ry", "u": "yi",
83
+ "v": "cb", "w": "qe", "x": "zc", "y": "tu", "z": "x",
84
+ }
85
+
86
+
87
+ def _build_vocab() -> list[str]:
88
+ """De-duplicate `_RAW` keeping first (most-frequent) occurrence, drop words with
89
+ no consonant skeleton (matches the shared nano-* lexicon build exactly, so the
90
+ Zipf indices line up with the training data). Order = rough frequency rank."""
91
+ seen: set[str] = set()
92
+ vocab: list[str] = []
93
+ for w in _RAW:
94
+ if w in seen:
95
+ continue
96
+ seen.add(w)
97
+ if len("".join(c for c in w if c not in _VOWELS)) >= 1: # need a consonant
98
+ vocab.append(w)
99
+ return vocab
100
+
101
+
102
+ _WORDS = _build_vocab()
103
+ _RANKS = np.arange(1, len(_WORDS) + 1, dtype=np.float64)
104
+ _WEIGHTS = 1.0 / (_RANKS + 5.0)
105
+ _WEIGHTS /= _WEIGHTS.sum()
106
+
107
+
108
+ def _typo(rng, w: str) -> str:
109
+ """Apply one realistic single-character typo to `w`."""
110
+ if len(w) < 2:
111
+ return w
112
+ kind = rng.integers(5)
113
+ i = int(rng.integers(len(w)))
114
+ if kind == 0: # delete
115
+ return w[:i] + w[i + 1:]
116
+ if kind == 1: # insert a random letter
117
+ c = chr(int(rng.integers(26)) + 97)
118
+ return w[:i] + c + w[i:]
119
+ if kind == 2: # substitute a keyboard neighbour
120
+ opts = _NEIGHBORS.get(w[i], "")
121
+ if not opts:
122
+ return w[:i] + chr(int(rng.integers(26)) + 97) + w[i + 1:]
123
+ return w[:i] + opts[int(rng.integers(len(opts)))] + w[i + 1:]
124
+ if kind == 3 and i < len(w) - 1: # transpose adjacent
125
+ return w[:i] + w[i + 1] + w[i] + w[i + 2:]
126
+ # double a letter
127
+ return w[:i] + w[i] + w[i:]
128
+
129
+
130
+ def spell_pairs(seed: int, n: int) -> list[tuple[str, str]]:
131
+ """`n` deterministic (prompt, correct-word) pairs from `seed`.
132
+
133
+ The prompt is ``<misspelling> => `` and the target is the correct word. ~15% are
134
+ the identity (already-correct) so the model learns to leave good words alone.
135
+ """
136
+ rng = np.random.default_rng(seed)
137
+ idx = rng.choice(len(_WORDS), size=n, p=_WEIGHTS)
138
+ out: list[tuple[str, str]] = []
139
+ for i in idx:
140
+ word = _WORDS[int(i)]
141
+ if rng.random() < 0.15:
142
+ typo = word # already correct
143
+ else:
144
+ typo = _typo(rng, word)
145
+ if rng.random() < 0.3: # sometimes a second edit
146
+ typo = _typo(rng, typo)
147
+ out.append((f"{typo} => ", word))
148
+ return out