ContextReq commited on
Commit
c402a9b
·
verified ·
1 Parent(s): 2024c96

tokenizer+tokenized dataset

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. tinystories-ids.bin +3 -0
  3. tinystories-ids.txt +3 -0
  4. tokenizer.py +175 -0
.gitattributes CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  tinystories-cleaned.txt filter=lfs diff=lfs merge=lfs -text
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  tinystories-cleaned.txt filter=lfs diff=lfs merge=lfs -text
37
+ tinystories-ids.txt filter=lfs diff=lfs merge=lfs -text
tinystories-ids.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7135adda2a9a768730cf7d9b8882400df1c0f20cf1d82584f92db7c3a5525144
3
+ size 1895333009
tinystories-ids.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:104a0169ceba458c697947be41d65fad2216c77ac427f62bbc21a1e22fc83b18
3
+ size 1892606249
tokenizer.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """txt2ids / ids2txt — the runtime interface between text and the
2
+ binary token stream the model consumes.
3
+
4
+ - encode: text (or the tagged stream) -> u8 ids, longest-match, C-speed:
5
+ multi-byte tokens replaced by sentinel bytes, then a 256-entry translate.
6
+ - decode: u8 ids -> text, VERBATIM (tags stay literal strings).
7
+ - display: verbatim text -> human view (tags become newline/space/tab,
8
+ <bos>/<eos>/<pad>/<msk> dropped).
9
+
10
+ roundtrip: tokenize, decode, re-encode, assert byte-identical ids, and
11
+ assert the human views of source and decoded text match.
12
+ """
13
+ import multiprocessing as mp
14
+ import sys
15
+ import time
16
+ from pathlib import Path
17
+
18
+ WORKERS = 12
19
+ DATA = Path(__file__).resolve().parent.parent / "data"
20
+ SRC = Path(__file__).resolve().parent.parent / "src"
21
+
22
+ PIECES = None
23
+ TOKENS = None
24
+ TAG_ID = {}
25
+ CHAR_ID = {}
26
+ TABLE_ENC = None
27
+ SENT_ENC = []
28
+ TABLE_DEC = None
29
+ SENT_DEC = []
30
+
31
+
32
+ def load_vocab():
33
+ global TOKENS, TAG_ID, CHAR_ID, TABLE_ENC, SENT_ENC
34
+ raw = (DATA / "vocab.bin").read_bytes()
35
+ toks, p = [], 0
36
+ while p < len(raw):
37
+ n = raw[p]
38
+ toks.append(raw[p + 1:p + 1 + n])
39
+ p += 1 + n
40
+ txt = (SRC / "vocab.txt").read_bytes().split(b"\n")
41
+ if txt and txt[-1] == b"":
42
+ txt.pop()
43
+ assert toks == txt, "vocab.bin does not match vocab.txt"
44
+ TOKENS = toks
45
+ for i, t in enumerate(toks):
46
+ if t.startswith(b"<") and t.endswith(b">"):
47
+ TAG_ID[t] = i
48
+ else:
49
+ CHAR_ID[t] = i
50
+ # encoder tables
51
+ table = bytearray([TAG_ID[b"<unk>"]] * 256)
52
+ multi = [(t, i) for t, i in TAG_ID.items()] + \
53
+ [(t, i) for t, i in CHAR_ID.items() if len(t) > 1]
54
+ sent = 200
55
+ for t, i in multi:
56
+ table[sent] = i
57
+ SENT_ENC.append((t, bytes([sent])))
58
+ sent += 1
59
+ for t, i in CHAR_ID.items():
60
+ if len(t) == 1:
61
+ table[t[0]] = i
62
+ table[10] = TAG_ID[b"<nwl>"]
63
+ table[32] = TAG_ID[b"<spc>"]
64
+ table[9] = TAG_ID[b"<tab>"]
65
+ TABLE_ENC = bytes(table)
66
+ # decoder tables: single-byte tokens via translate, multi-byte via
67
+ # unique high sentinels replaced afterwards (never appear in the data)
68
+ global TABLE_DEC, SENT_DEC
69
+ dt = bytearray(b"?") * 256
70
+ sent = 0xF0
71
+ for i, t in enumerate(toks):
72
+ if len(t) == 1:
73
+ dt[i] = t[0]
74
+ else:
75
+ dt[i] = sent
76
+ SENT_DEC.append((bytes([sent]), t))
77
+ sent += 1
78
+ TABLE_DEC = bytes(dt)
79
+ print(f"vocab loaded: {len(toks)} tokens")
80
+
81
+
82
+ def encode_bytes(b):
83
+ for tok, sent in SENT_ENC:
84
+ b = b.replace(tok, sent)
85
+ return b.translate(TABLE_ENC)
86
+
87
+
88
+ def encode_piece_slice(lo_hi):
89
+ lo, hi = lo_hi
90
+ sep = bytes([TAG_ID[b"<eos>"], TAG_ID[b"<bos>"]])
91
+ return sep.join(encode_bytes(PIECES[i]) for i in range(lo, hi))
92
+
93
+
94
+ def decode_slice(lo_hi):
95
+ lo, hi = lo_hi
96
+ ids = PIECES[lo:hi]
97
+ out = ids.translate(TABLE_DEC)
98
+ for sent, tok in SENT_DEC:
99
+ out = out.replace(sent, tok)
100
+ return out
101
+
102
+
103
+ def run_parallel(fn, n, chunk):
104
+ bounds = [(i, min(i + chunk, n)) for i in range(0, n, chunk)]
105
+ with mp.Pool(WORKERS) as pool:
106
+ return pool.map(fn, bounds)
107
+
108
+
109
+ def text_to_ids(text):
110
+ assert text.startswith(b"<bos>")
111
+ global PIECES
112
+ PIECES = text[5:].split(b"<eos><bos>")
113
+ n = len(PIECES)
114
+ outs = run_parallel(encode_piece_slice, n, (n + WORKERS - 1) // WORKERS)
115
+ eos_id, bos_id = TAG_ID[b"<eos>"], TAG_ID[b"<bos>"]
116
+ sep = bytes([eos_id, bos_id])
117
+ return bytes([bos_id]) + sep.join(outs)
118
+
119
+
120
+ def ids_to_text(ids):
121
+ global PIECES
122
+ PIECES = ids
123
+ n = len(ids)
124
+ outs = run_parallel(decode_slice, n, (n + WORKERS - 1) // WORKERS)
125
+ return b"".join(outs)
126
+
127
+
128
+ def display(text):
129
+ for tag, sub in ((b"<nwl>", b"\n"), (b"<spc>", b" "), (b"<tab>", b"\t"),
130
+ (b"<bos>", b""), (b"<eos>", b""), (b"<pad>", b""),
131
+ (b"<msk>", b"")):
132
+ text = text.replace(tag, sub)
133
+ return text
134
+
135
+
136
+ def cmd_tokenize():
137
+ load_vocab()
138
+ t0 = time.time()
139
+ ids = text_to_ids((DATA / "tinystories-cleaned.bin").read_bytes())
140
+ (DATA / "tinystories-ids.bin").write_bytes(ids)
141
+ print(f"tokenized: {len(ids):,} ids in {time.time()-t0:.1f}s -> "
142
+ f"tinystories-ids.bin")
143
+
144
+
145
+ def cmd_detokenize():
146
+ load_vocab()
147
+ t0 = time.time()
148
+ text = ids_to_text((DATA / "tinystories-ids.bin").read_bytes())
149
+ print(f"detokenized: {len(text):,} bytes in {time.time()-t0:.1f}s")
150
+ disp = display(text)
151
+ (DATA / "tinystories-ids.txt").write_bytes(disp)
152
+ print(f"display text: {len(disp):,} bytes -> tinystories-ids.txt")
153
+ return text
154
+
155
+
156
+ def cmd_roundtrip():
157
+ cmd_tokenize()
158
+ text = cmd_detokenize()
159
+ orig_ids = (DATA / "tinystories-ids.bin").read_bytes()
160
+ t0 = time.time()
161
+ ids2 = text_to_ids(text)
162
+ print(f"re-encoded in {time.time()-t0:.1f}s")
163
+ print("roundtrip ids identical:", ids2 == orig_ids)
164
+ src = (DATA / "tinystories-cleaned.bin").read_bytes()
165
+ print("human view (display) identical:", display(text) == display(src))
166
+ if ids2 != orig_ids:
167
+ i = next((k for k, (a, b) in enumerate(zip(ids2, orig_ids)) if a != b), -1)
168
+ print(" first diff at id", i)
169
+ sys.exit(1)
170
+
171
+
172
+ if __name__ == "__main__":
173
+ cmd = sys.argv[1] if len(sys.argv) > 1 else "roundtrip"
174
+ {"tokenize": cmd_tokenize, "detokenize": cmd_detokenize,
175
+ "roundtrip": cmd_roundtrip}[cmd]()