Sujit-Tokenizer 1.0.0__tar.gz
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.
- sujit_tokenizer-1.0.0/PKG-INFO +233 -0
- sujit_tokenizer-1.0.0/README.md +205 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer/__init__.py +3 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer/tokenizer.py +374 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer.egg-info/PKG-INFO +233 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer.egg-info/SOURCES.txt +9 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer.egg-info/dependency_links.txt +1 -0
- sujit_tokenizer-1.0.0/Sujit_Tokenizer.egg-info/top_level.txt +1 -0
- sujit_tokenizer-1.0.0/pyproject.toml +45 -0
- sujit_tokenizer-1.0.0/setup.cfg +4 -0
- sujit_tokenizer-1.0.0/setup.py +41 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Sujit-Tokenizer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Custom Byte-Level BPE Tokenizer built from scratch in Python
|
|
5
|
+
Home-page: https://github.com/iamsspm07/Sujit-Tokenizer
|
|
6
|
+
Author: Sujit Maity
|
|
7
|
+
Author-email: Sujit Maity <sujitmaity.in@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/iamsspm07/Sujit-Tokenizer
|
|
10
|
+
Project-URL: Repository, https://github.com/iamsspm07/Sujit-Tokenizer
|
|
11
|
+
Project-URL: Issues, https://github.com/iamsspm07/Sujit-Tokenizer/issues
|
|
12
|
+
Keywords: tokenizer,bpe,byte-pair-encoding,nlp,llm,machine-learning,artificial-intelligence
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Text Processing
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
|
|
29
|
+
# Sujit-Tokenizer
|
|
30
|
+
|
|
31
|
+
A custom Byte-Level BPE (Byte Pair Encoding) Tokenizer implemented from scratch in Python.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
* Byte-level tokenization
|
|
36
|
+
* Custom BPE training
|
|
37
|
+
* Text encoding and decoding
|
|
38
|
+
* Save and load tokenizer models
|
|
39
|
+
* UTF-8 support
|
|
40
|
+
* Lightweight and dependency-free
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### Clone Repository
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/your-username/Sujit-Tokenizer.git
|
|
48
|
+
cd Sujit-Tokenizer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Install Package
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Project Structure
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
Sujit-Tokenizer/
|
|
61
|
+
│
|
|
62
|
+
├── Sujit_Tokenizer/
|
|
63
|
+
│ ├── __init__.py
|
|
64
|
+
│ └── tokenizer.py
|
|
65
|
+
│
|
|
66
|
+
├── corpus.txt
|
|
67
|
+
├── train_and_test.py
|
|
68
|
+
│
|
|
69
|
+
├── README.md
|
|
70
|
+
├── setup.py
|
|
71
|
+
├── pyproject.toml
|
|
72
|
+
└── LICENSE
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
### Import Tokenizer
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Train Tokenizer
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
corpus = [
|
|
87
|
+
"Transformers are amazing.",
|
|
88
|
+
"Machine Learning is powerful.",
|
|
89
|
+
"Python is widely used in AI."
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
93
|
+
vocab_size=1000
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
tokenizer.train(corpus)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Save Model
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
tokenizer.save_model(
|
|
103
|
+
"tokenizer.model"
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Load Model
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
tokenizer = CustomByteLevelBPETokenizer()
|
|
111
|
+
|
|
112
|
+
tokenizer.load_model(
|
|
113
|
+
"tokenizer.model"
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Encode Text
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
encoded = tokenizer.encode(
|
|
121
|
+
"Transformers use attention."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
print(encoded)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Example Output:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
[2, 451, 723, 812, 3]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Decode Text
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
decoded = tokenizer.decode(
|
|
137
|
+
encoded
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
print(decoded)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Output:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
Transformers use attention.
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Training Workflow
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
Corpus
|
|
153
|
+
↓
|
|
154
|
+
Byte Conversion
|
|
155
|
+
↓
|
|
156
|
+
Pair Frequency Counting
|
|
157
|
+
↓
|
|
158
|
+
BPE Merging
|
|
159
|
+
↓
|
|
160
|
+
Vocabulary Construction
|
|
161
|
+
↓
|
|
162
|
+
Tokenizer Model
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Special Tokens
|
|
166
|
+
|
|
167
|
+
| Token | ID |
|
|
168
|
+
| ----- | -- |
|
|
169
|
+
| <PAD> | 0 |
|
|
170
|
+
| <UNK> | 1 |
|
|
171
|
+
| <BOS> | 2 |
|
|
172
|
+
| <EOS> | 3 |
|
|
173
|
+
|
|
174
|
+
## Example
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
178
|
+
|
|
179
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
180
|
+
vocab_size=1000
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
corpus = [
|
|
184
|
+
"Artificial Intelligence",
|
|
185
|
+
"Machine Learning",
|
|
186
|
+
"Deep Learning"
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
tokenizer.train(corpus)
|
|
190
|
+
|
|
191
|
+
tokenizer.save_model(
|
|
192
|
+
"tokenizer.model"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
tokenizer.load_model(
|
|
196
|
+
"tokenizer.model"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
text = "Machine Learning"
|
|
200
|
+
|
|
201
|
+
encoded = tokenizer.encode(text)
|
|
202
|
+
print(encoded)
|
|
203
|
+
|
|
204
|
+
decoded = tokenizer.decode(encoded)
|
|
205
|
+
print(decoded)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Use Cases
|
|
209
|
+
|
|
210
|
+
* NLP experiments
|
|
211
|
+
* Tokenization research
|
|
212
|
+
* Educational projects
|
|
213
|
+
* Understanding BPE internals
|
|
214
|
+
* Building custom language models
|
|
215
|
+
* Learning tokenizer architecture
|
|
216
|
+
|
|
217
|
+
## Future Enhancements
|
|
218
|
+
|
|
219
|
+
* Faster BPE training
|
|
220
|
+
* WordPiece tokenizer
|
|
221
|
+
* SentencePiece tokenizer
|
|
222
|
+
* Parallel processing
|
|
223
|
+
* Vocabulary statistics
|
|
224
|
+
* Token frequency analysis
|
|
225
|
+
* Hugging Face compatibility
|
|
226
|
+
|
|
227
|
+
## Author
|
|
228
|
+
|
|
229
|
+
Sujit Maity
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT License
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Sujit-Tokenizer
|
|
2
|
+
|
|
3
|
+
A custom Byte-Level BPE (Byte Pair Encoding) Tokenizer implemented from scratch in Python.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* Byte-level tokenization
|
|
8
|
+
* Custom BPE training
|
|
9
|
+
* Text encoding and decoding
|
|
10
|
+
* Save and load tokenizer models
|
|
11
|
+
* UTF-8 support
|
|
12
|
+
* Lightweight and dependency-free
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Clone Repository
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/your-username/Sujit-Tokenizer.git
|
|
20
|
+
cd Sujit-Tokenizer
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Install Package
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Project Structure
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Sujit-Tokenizer/
|
|
33
|
+
│
|
|
34
|
+
├── Sujit_Tokenizer/
|
|
35
|
+
│ ├── __init__.py
|
|
36
|
+
│ └── tokenizer.py
|
|
37
|
+
│
|
|
38
|
+
├── corpus.txt
|
|
39
|
+
├── train_and_test.py
|
|
40
|
+
│
|
|
41
|
+
├── README.md
|
|
42
|
+
├── setup.py
|
|
43
|
+
├── pyproject.toml
|
|
44
|
+
└── LICENSE
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### Import Tokenizer
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Train Tokenizer
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
corpus = [
|
|
59
|
+
"Transformers are amazing.",
|
|
60
|
+
"Machine Learning is powerful.",
|
|
61
|
+
"Python is widely used in AI."
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
65
|
+
vocab_size=1000
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
tokenizer.train(corpus)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Save Model
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
tokenizer.save_model(
|
|
75
|
+
"tokenizer.model"
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Load Model
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
tokenizer = CustomByteLevelBPETokenizer()
|
|
83
|
+
|
|
84
|
+
tokenizer.load_model(
|
|
85
|
+
"tokenizer.model"
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Encode Text
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
encoded = tokenizer.encode(
|
|
93
|
+
"Transformers use attention."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
print(encoded)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Example Output:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
[2, 451, 723, 812, 3]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Decode Text
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
decoded = tokenizer.decode(
|
|
109
|
+
encoded
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
print(decoded)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Output:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
Transformers use attention.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Training Workflow
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
Corpus
|
|
125
|
+
↓
|
|
126
|
+
Byte Conversion
|
|
127
|
+
↓
|
|
128
|
+
Pair Frequency Counting
|
|
129
|
+
↓
|
|
130
|
+
BPE Merging
|
|
131
|
+
↓
|
|
132
|
+
Vocabulary Construction
|
|
133
|
+
↓
|
|
134
|
+
Tokenizer Model
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Special Tokens
|
|
138
|
+
|
|
139
|
+
| Token | ID |
|
|
140
|
+
| ----- | -- |
|
|
141
|
+
| <PAD> | 0 |
|
|
142
|
+
| <UNK> | 1 |
|
|
143
|
+
| <BOS> | 2 |
|
|
144
|
+
| <EOS> | 3 |
|
|
145
|
+
|
|
146
|
+
## Example
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
150
|
+
|
|
151
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
152
|
+
vocab_size=1000
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
corpus = [
|
|
156
|
+
"Artificial Intelligence",
|
|
157
|
+
"Machine Learning",
|
|
158
|
+
"Deep Learning"
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
tokenizer.train(corpus)
|
|
162
|
+
|
|
163
|
+
tokenizer.save_model(
|
|
164
|
+
"tokenizer.model"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
tokenizer.load_model(
|
|
168
|
+
"tokenizer.model"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
text = "Machine Learning"
|
|
172
|
+
|
|
173
|
+
encoded = tokenizer.encode(text)
|
|
174
|
+
print(encoded)
|
|
175
|
+
|
|
176
|
+
decoded = tokenizer.decode(encoded)
|
|
177
|
+
print(decoded)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Use Cases
|
|
181
|
+
|
|
182
|
+
* NLP experiments
|
|
183
|
+
* Tokenization research
|
|
184
|
+
* Educational projects
|
|
185
|
+
* Understanding BPE internals
|
|
186
|
+
* Building custom language models
|
|
187
|
+
* Learning tokenizer architecture
|
|
188
|
+
|
|
189
|
+
## Future Enhancements
|
|
190
|
+
|
|
191
|
+
* Faster BPE training
|
|
192
|
+
* WordPiece tokenizer
|
|
193
|
+
* SentencePiece tokenizer
|
|
194
|
+
* Parallel processing
|
|
195
|
+
* Vocabulary statistics
|
|
196
|
+
* Token frequency analysis
|
|
197
|
+
* Hugging Face compatibility
|
|
198
|
+
|
|
199
|
+
## Author
|
|
200
|
+
|
|
201
|
+
Sujit Maity
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT License
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pickle
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import Dict, List, Tuple
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomByteLevelBPETokenizer:
|
|
9
|
+
"""
|
|
10
|
+
Custom Byte-Level BPE Tokenizer.
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Byte-level tokenization
|
|
14
|
+
- BPE training
|
|
15
|
+
- Encoding / Decoding
|
|
16
|
+
- Save / Load model
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, vocab_size: int = 1000):
|
|
20
|
+
|
|
21
|
+
if vocab_size < 260:
|
|
22
|
+
raise ValueError(
|
|
23
|
+
"vocab_size must be >= 260 "
|
|
24
|
+
"(256 bytes + 4 special tokens)"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
self.vocab_size = vocab_size
|
|
28
|
+
|
|
29
|
+
self.special_tokens = {
|
|
30
|
+
"<PAD>": 0,
|
|
31
|
+
"<UNK>": 1,
|
|
32
|
+
"<BOS>": 2,
|
|
33
|
+
"<EOS>": 3,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
self.vocab: Dict[bytes, int] = {}
|
|
37
|
+
|
|
38
|
+
# Initialize byte vocabulary
|
|
39
|
+
for i in range(256):
|
|
40
|
+
self.vocab[bytes([i])] = (
|
|
41
|
+
i + len(self.special_tokens)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Add special tokens
|
|
45
|
+
for token, token_id in self.special_tokens.items():
|
|
46
|
+
self.vocab[token.encode()] = token_id
|
|
47
|
+
|
|
48
|
+
self.inverse_vocab: Dict[int, bytes] = {
|
|
49
|
+
idx: token
|
|
50
|
+
for token, idx in self.vocab.items()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
self.merges: List[
|
|
54
|
+
Tuple[bytes, bytes]
|
|
55
|
+
] = []
|
|
56
|
+
|
|
57
|
+
def text_to_bytes(
|
|
58
|
+
self,
|
|
59
|
+
text: str
|
|
60
|
+
) -> List[int]:
|
|
61
|
+
|
|
62
|
+
if not isinstance(text, str):
|
|
63
|
+
raise TypeError(
|
|
64
|
+
"text must be a string"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return list(text.encode("utf-8"))
|
|
68
|
+
|
|
69
|
+
def get_pair_frequencies(
|
|
70
|
+
self,
|
|
71
|
+
corpus: List[List[bytes]]
|
|
72
|
+
) -> Dict[Tuple[bytes, bytes], int]:
|
|
73
|
+
|
|
74
|
+
pair_freq = defaultdict(int)
|
|
75
|
+
|
|
76
|
+
for tokens in corpus:
|
|
77
|
+
for i in range(len(tokens) - 1):
|
|
78
|
+
pair = (
|
|
79
|
+
tokens[i],
|
|
80
|
+
tokens[i + 1]
|
|
81
|
+
)
|
|
82
|
+
pair_freq[pair] += 1
|
|
83
|
+
|
|
84
|
+
return pair_freq
|
|
85
|
+
|
|
86
|
+
def merge_pair(
|
|
87
|
+
self,
|
|
88
|
+
pair: Tuple[bytes, bytes],
|
|
89
|
+
corpus: List[List[bytes]]
|
|
90
|
+
) -> List[List[bytes]]:
|
|
91
|
+
|
|
92
|
+
updated_corpus = []
|
|
93
|
+
|
|
94
|
+
for tokens in corpus:
|
|
95
|
+
|
|
96
|
+
merged_tokens = []
|
|
97
|
+
i = 0
|
|
98
|
+
|
|
99
|
+
while i < len(tokens):
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
i < len(tokens) - 1
|
|
103
|
+
and tokens[i] == pair[0]
|
|
104
|
+
and tokens[i + 1] == pair[1]
|
|
105
|
+
):
|
|
106
|
+
merged_tokens.append(
|
|
107
|
+
tokens[i] + tokens[i + 1]
|
|
108
|
+
)
|
|
109
|
+
i += 2
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
merged_tokens.append(tokens[i])
|
|
113
|
+
i += 1
|
|
114
|
+
|
|
115
|
+
updated_corpus.append(
|
|
116
|
+
merged_tokens
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return updated_corpus
|
|
120
|
+
|
|
121
|
+
def train(
|
|
122
|
+
self,
|
|
123
|
+
corpus: List[str]
|
|
124
|
+
) -> None:
|
|
125
|
+
|
|
126
|
+
if not corpus:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
"Corpus cannot be empty"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
tokenized_corpus = []
|
|
132
|
+
|
|
133
|
+
for text in corpus:
|
|
134
|
+
tokens = [
|
|
135
|
+
bytes([b])
|
|
136
|
+
for b in self.text_to_bytes(text)
|
|
137
|
+
]
|
|
138
|
+
tokenized_corpus.append(tokens)
|
|
139
|
+
|
|
140
|
+
num_merges = (
|
|
141
|
+
self.vocab_size -
|
|
142
|
+
len(self.vocab)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
print("=" * 50)
|
|
146
|
+
print("Training Byte-Level BPE")
|
|
147
|
+
print("=" * 50)
|
|
148
|
+
|
|
149
|
+
for step in range(
|
|
150
|
+
max(0, num_merges)
|
|
151
|
+
):
|
|
152
|
+
|
|
153
|
+
pair_freq = (
|
|
154
|
+
self.get_pair_frequencies(
|
|
155
|
+
tokenized_corpus
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if not pair_freq:
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
best_pair = max(
|
|
163
|
+
pair_freq,
|
|
164
|
+
key=pair_freq.get
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
merged_token = (
|
|
168
|
+
best_pair[0]
|
|
169
|
+
+ best_pair[1]
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self.merges.append(
|
|
173
|
+
best_pair
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
merged_token
|
|
178
|
+
not in self.vocab
|
|
179
|
+
):
|
|
180
|
+
token_id = len(
|
|
181
|
+
self.vocab
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.vocab[
|
|
185
|
+
merged_token
|
|
186
|
+
] = token_id
|
|
187
|
+
|
|
188
|
+
self.inverse_vocab[
|
|
189
|
+
token_id
|
|
190
|
+
] = merged_token
|
|
191
|
+
|
|
192
|
+
tokenized_corpus = (
|
|
193
|
+
self.merge_pair(
|
|
194
|
+
best_pair,
|
|
195
|
+
tokenized_corpus
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
step + 1
|
|
201
|
+
) % 100 == 0:
|
|
202
|
+
print(
|
|
203
|
+
f"Completed "
|
|
204
|
+
f"{step + 1} merges"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
print(
|
|
208
|
+
f"Training Complete. "
|
|
209
|
+
f"Vocabulary Size: "
|
|
210
|
+
f"{len(self.vocab)}"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def apply_bpe(
|
|
214
|
+
self,
|
|
215
|
+
byte_tokens: List[int]
|
|
216
|
+
) -> List[bytes]:
|
|
217
|
+
|
|
218
|
+
tokens = [
|
|
219
|
+
bytes([b])
|
|
220
|
+
for b in byte_tokens
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
for pair in self.merges:
|
|
224
|
+
|
|
225
|
+
merged_tokens = []
|
|
226
|
+
i = 0
|
|
227
|
+
|
|
228
|
+
while i < len(tokens):
|
|
229
|
+
|
|
230
|
+
if (
|
|
231
|
+
i < len(tokens) - 1
|
|
232
|
+
and tokens[i] == pair[0]
|
|
233
|
+
and tokens[i + 1] == pair[1]
|
|
234
|
+
):
|
|
235
|
+
merged_tokens.append(
|
|
236
|
+
tokens[i]
|
|
237
|
+
+ tokens[i + 1]
|
|
238
|
+
)
|
|
239
|
+
i += 2
|
|
240
|
+
|
|
241
|
+
else:
|
|
242
|
+
merged_tokens.append(
|
|
243
|
+
tokens[i]
|
|
244
|
+
)
|
|
245
|
+
i += 1
|
|
246
|
+
|
|
247
|
+
tokens = merged_tokens
|
|
248
|
+
|
|
249
|
+
return tokens
|
|
250
|
+
|
|
251
|
+
def encode(
|
|
252
|
+
self,
|
|
253
|
+
text: str
|
|
254
|
+
) -> List[int]:
|
|
255
|
+
|
|
256
|
+
byte_tokens = (
|
|
257
|
+
self.text_to_bytes(text)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
bpe_tokens = self.apply_bpe(
|
|
261
|
+
byte_tokens
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
token_ids = [
|
|
265
|
+
self.special_tokens[
|
|
266
|
+
"<BOS>"
|
|
267
|
+
]
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
for token in bpe_tokens:
|
|
271
|
+
|
|
272
|
+
token_ids.append(
|
|
273
|
+
self.vocab.get(
|
|
274
|
+
token,
|
|
275
|
+
self.special_tokens[
|
|
276
|
+
"<UNK>"
|
|
277
|
+
]
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
token_ids.append(
|
|
282
|
+
self.special_tokens[
|
|
283
|
+
"<EOS>"
|
|
284
|
+
]
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return token_ids
|
|
288
|
+
|
|
289
|
+
def decode(
|
|
290
|
+
self,
|
|
291
|
+
token_ids: List[int]
|
|
292
|
+
) -> str:
|
|
293
|
+
|
|
294
|
+
byte_stream = b""
|
|
295
|
+
|
|
296
|
+
for token_id in token_ids:
|
|
297
|
+
|
|
298
|
+
token = (
|
|
299
|
+
self.inverse_vocab.get(
|
|
300
|
+
token_id
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
if token is None:
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
if token in {
|
|
308
|
+
b"<PAD>",
|
|
309
|
+
b"<UNK>",
|
|
310
|
+
b"<BOS>",
|
|
311
|
+
b"<EOS>",
|
|
312
|
+
}:
|
|
313
|
+
continue
|
|
314
|
+
|
|
315
|
+
byte_stream += token
|
|
316
|
+
|
|
317
|
+
return byte_stream.decode(
|
|
318
|
+
"utf-8",
|
|
319
|
+
errors="replace"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def save_model(
|
|
323
|
+
self,
|
|
324
|
+
path: str = "tokenizer.model"
|
|
325
|
+
) -> None:
|
|
326
|
+
|
|
327
|
+
model_data = {
|
|
328
|
+
"vocab": self.vocab,
|
|
329
|
+
"inverse_vocab":
|
|
330
|
+
self.inverse_vocab,
|
|
331
|
+
"merges": self.merges,
|
|
332
|
+
"special_tokens":
|
|
333
|
+
self.special_tokens,
|
|
334
|
+
"vocab_size":
|
|
335
|
+
self.vocab_size,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
with open(path, "wb") as f:
|
|
339
|
+
pickle.dump(
|
|
340
|
+
model_data,
|
|
341
|
+
f
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
print(
|
|
345
|
+
f"Tokenizer saved to "
|
|
346
|
+
f"{path}"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def load_model(
|
|
350
|
+
self,
|
|
351
|
+
path: str = "tokenizer.model"
|
|
352
|
+
) -> None:
|
|
353
|
+
|
|
354
|
+
with open(path, "rb") as f:
|
|
355
|
+
model_data = pickle.load(f)
|
|
356
|
+
|
|
357
|
+
self.vocab = model_data["vocab"]
|
|
358
|
+
self.inverse_vocab = (
|
|
359
|
+
model_data["inverse_vocab"]
|
|
360
|
+
)
|
|
361
|
+
self.merges = model_data["merges"]
|
|
362
|
+
self.special_tokens = (
|
|
363
|
+
model_data[
|
|
364
|
+
"special_tokens"
|
|
365
|
+
]
|
|
366
|
+
)
|
|
367
|
+
self.vocab_size = (
|
|
368
|
+
model_data["vocab_size"]
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
print(
|
|
372
|
+
f"Tokenizer loaded from "
|
|
373
|
+
f"{path}"
|
|
374
|
+
)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Sujit-Tokenizer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Custom Byte-Level BPE Tokenizer built from scratch in Python
|
|
5
|
+
Home-page: https://github.com/iamsspm07/Sujit-Tokenizer
|
|
6
|
+
Author: Sujit Maity
|
|
7
|
+
Author-email: Sujit Maity <sujitmaity.in@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/iamsspm07/Sujit-Tokenizer
|
|
10
|
+
Project-URL: Repository, https://github.com/iamsspm07/Sujit-Tokenizer
|
|
11
|
+
Project-URL: Issues, https://github.com/iamsspm07/Sujit-Tokenizer/issues
|
|
12
|
+
Keywords: tokenizer,bpe,byte-pair-encoding,nlp,llm,machine-learning,artificial-intelligence
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Text Processing
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
|
|
29
|
+
# Sujit-Tokenizer
|
|
30
|
+
|
|
31
|
+
A custom Byte-Level BPE (Byte Pair Encoding) Tokenizer implemented from scratch in Python.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
* Byte-level tokenization
|
|
36
|
+
* Custom BPE training
|
|
37
|
+
* Text encoding and decoding
|
|
38
|
+
* Save and load tokenizer models
|
|
39
|
+
* UTF-8 support
|
|
40
|
+
* Lightweight and dependency-free
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### Clone Repository
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/your-username/Sujit-Tokenizer.git
|
|
48
|
+
cd Sujit-Tokenizer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Install Package
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Project Structure
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
Sujit-Tokenizer/
|
|
61
|
+
│
|
|
62
|
+
├── Sujit_Tokenizer/
|
|
63
|
+
│ ├── __init__.py
|
|
64
|
+
│ └── tokenizer.py
|
|
65
|
+
│
|
|
66
|
+
├── corpus.txt
|
|
67
|
+
├── train_and_test.py
|
|
68
|
+
│
|
|
69
|
+
├── README.md
|
|
70
|
+
├── setup.py
|
|
71
|
+
├── pyproject.toml
|
|
72
|
+
└── LICENSE
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
### Import Tokenizer
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Train Tokenizer
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
corpus = [
|
|
87
|
+
"Transformers are amazing.",
|
|
88
|
+
"Machine Learning is powerful.",
|
|
89
|
+
"Python is widely used in AI."
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
93
|
+
vocab_size=1000
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
tokenizer.train(corpus)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Save Model
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
tokenizer.save_model(
|
|
103
|
+
"tokenizer.model"
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Load Model
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
tokenizer = CustomByteLevelBPETokenizer()
|
|
111
|
+
|
|
112
|
+
tokenizer.load_model(
|
|
113
|
+
"tokenizer.model"
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Encode Text
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
encoded = tokenizer.encode(
|
|
121
|
+
"Transformers use attention."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
print(encoded)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Example Output:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
[2, 451, 723, 812, 3]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Decode Text
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
decoded = tokenizer.decode(
|
|
137
|
+
encoded
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
print(decoded)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Output:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
Transformers use attention.
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Training Workflow
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
Corpus
|
|
153
|
+
↓
|
|
154
|
+
Byte Conversion
|
|
155
|
+
↓
|
|
156
|
+
Pair Frequency Counting
|
|
157
|
+
↓
|
|
158
|
+
BPE Merging
|
|
159
|
+
↓
|
|
160
|
+
Vocabulary Construction
|
|
161
|
+
↓
|
|
162
|
+
Tokenizer Model
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Special Tokens
|
|
166
|
+
|
|
167
|
+
| Token | ID |
|
|
168
|
+
| ----- | -- |
|
|
169
|
+
| <PAD> | 0 |
|
|
170
|
+
| <UNK> | 1 |
|
|
171
|
+
| <BOS> | 2 |
|
|
172
|
+
| <EOS> | 3 |
|
|
173
|
+
|
|
174
|
+
## Example
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from Sujit_Tokenizer import CustomByteLevelBPETokenizer
|
|
178
|
+
|
|
179
|
+
tokenizer = CustomByteLevelBPETokenizer(
|
|
180
|
+
vocab_size=1000
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
corpus = [
|
|
184
|
+
"Artificial Intelligence",
|
|
185
|
+
"Machine Learning",
|
|
186
|
+
"Deep Learning"
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
tokenizer.train(corpus)
|
|
190
|
+
|
|
191
|
+
tokenizer.save_model(
|
|
192
|
+
"tokenizer.model"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
tokenizer.load_model(
|
|
196
|
+
"tokenizer.model"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
text = "Machine Learning"
|
|
200
|
+
|
|
201
|
+
encoded = tokenizer.encode(text)
|
|
202
|
+
print(encoded)
|
|
203
|
+
|
|
204
|
+
decoded = tokenizer.decode(encoded)
|
|
205
|
+
print(decoded)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Use Cases
|
|
209
|
+
|
|
210
|
+
* NLP experiments
|
|
211
|
+
* Tokenization research
|
|
212
|
+
* Educational projects
|
|
213
|
+
* Understanding BPE internals
|
|
214
|
+
* Building custom language models
|
|
215
|
+
* Learning tokenizer architecture
|
|
216
|
+
|
|
217
|
+
## Future Enhancements
|
|
218
|
+
|
|
219
|
+
* Faster BPE training
|
|
220
|
+
* WordPiece tokenizer
|
|
221
|
+
* SentencePiece tokenizer
|
|
222
|
+
* Parallel processing
|
|
223
|
+
* Vocabulary statistics
|
|
224
|
+
* Token frequency analysis
|
|
225
|
+
* Hugging Face compatibility
|
|
226
|
+
|
|
227
|
+
## Author
|
|
228
|
+
|
|
229
|
+
Sujit Maity
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Sujit_Tokenizer
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "Sujit-Tokenizer"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Custom Byte-Level BPE Tokenizer built from scratch in Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "Sujit Maity", email = "sujitmaity.in@gmail.com"}
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
keywords = [
|
|
18
|
+
"tokenizer",
|
|
19
|
+
"bpe",
|
|
20
|
+
"byte-pair-encoding",
|
|
21
|
+
"nlp",
|
|
22
|
+
"llm",
|
|
23
|
+
"machine-learning",
|
|
24
|
+
"artificial-intelligence"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 4 - Beta",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Intended Audience :: Education",
|
|
31
|
+
"License :: OSI Approved :: MIT License",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.9",
|
|
34
|
+
"Programming Language :: Python :: 3.10",
|
|
35
|
+
"Programming Language :: Python :: 3.11",
|
|
36
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
37
|
+
"Topic :: Text Processing"
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
dependencies = []
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/iamsspm07/Sujit-Tokenizer"
|
|
44
|
+
Repository = "https://github.com/iamsspm07/Sujit-Tokenizer"
|
|
45
|
+
Issues = "https://github.com/iamsspm07/Sujit-Tokenizer/issues"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="Sujit-Tokenizer",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
author="Sujit Maity",
|
|
7
|
+
author_email="[sujitmaity.in@gmail.com](mailto:sujitmaity.in@gmail.com)",
|
|
8
|
+
description="Custom Byte-Level BPE Tokenizer built from scratch in Python",
|
|
9
|
+
long_description=open(
|
|
10
|
+
"README.md",
|
|
11
|
+
encoding="utf-8"
|
|
12
|
+
).read(),
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://github.com/iamsspm07/Sujit-Tokenizer",
|
|
15
|
+
packages=find_packages(),
|
|
16
|
+
include_package_data=True,
|
|
17
|
+
python_requires=">=3.9",
|
|
18
|
+
install_requires=[],
|
|
19
|
+
classifiers=[
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Education",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
30
|
+
"Topic :: Text Processing",
|
|
31
|
+
],
|
|
32
|
+
keywords=[
|
|
33
|
+
"tokenizer",
|
|
34
|
+
"bpe",
|
|
35
|
+
"byte-pair-encoding",
|
|
36
|
+
"nlp",
|
|
37
|
+
"llm",
|
|
38
|
+
"machine-learning",
|
|
39
|
+
"artificial-intelligence"
|
|
40
|
+
],
|
|
41
|
+
)
|