morphbpe-pl 0.3.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.
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: morphbpe-pl
3
+ Version: 0.3.0
4
+ Summary: MorphBPE: Morphologically-aware BPE tokenizer for Polish
5
+ Author: Piotr Styła
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
9
+ Classifier: Natural Language :: Polish
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: numpy>=1.24
13
+ Requires-Dist: tqdm>=4.60
14
+ Provides-Extra: morfeusz
15
+ Requires-Dist: morfeusz2; extra == "morfeusz"
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=7.0; extra == "dev"
18
+
19
+ # MorphBPE
20
+
21
+ **Morphologically-aware BPE tokenizer for Polish.**
22
+
23
+ MorphBPE extends Byte-Pair Encoding with morphological boundary penalties.
24
+ Merges that cross morpheme boundaries receive a frequency penalty controlled by `alpha`,
25
+ preserving suffix (or prefix) structure during tokenization.
26
+
27
+ Based on: Styła (2026), *MorphBPE: morfologicznie informowany BPE dla polszczyzny*.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install morphbpe
33
+
34
+ # With Morfeusz2 support (recommended):
35
+ pip install morphbpe[morfeusz]
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Python API
41
+
42
+ ```python
43
+ from morphbpe import MorphBPETokenizer
44
+
45
+ # Train
46
+ tok = MorphBPETokenizer(vocab_size=32000, alpha=2.0, constraint="suffix")
47
+ tok.train(["list of", "training texts", ...])
48
+
49
+ # Encode / decode
50
+ ids = tok.encode("prezydent podpisał ustawę")
51
+ text = tok.decode(ids)
52
+
53
+ # Subword tokens
54
+ tokens = tok.encode_word("ustawodawstwo")
55
+ # ['ustaw', 'odaw', 'stwo'] (example)
56
+
57
+ # Save / load
58
+ tok.save("my-tokenizer")
59
+ tok = MorphBPETokenizer.load("my-tokenizer")
60
+
61
+ # Metrics
62
+ print(tok.fertility(test_texts))
63
+ print(tok.morph_edit_distance(test_texts))
64
+ mean, lo, hi = tok.morph_edit_distance_ci(test_texts)
65
+ ```
66
+
67
+ ### CLI
68
+
69
+ ```bash
70
+ # Train
71
+ morphbpe train corpus.txt --vocab-size 32000 --alpha 2.0 --constraint suffix -o my-model
72
+
73
+ # Encode
74
+ echo "prezydent podpisał ustawę" | morphbpe encode my-model
75
+
76
+ # Info
77
+ morphbpe info my-model
78
+ ```
79
+
80
+ ## Key Parameters
81
+
82
+ | Parameter | Default | Description |
83
+ |-----------|---------|-------------|
84
+ | `vocab_size` | 1000 | Target vocabulary size |
85
+ | `alpha` | 1.0 | Penalty strength: 0=BPE, 1=block, >1=over-penalization |
86
+ | `constraint` | `"suffix"` | Boundary type: `suffix`, `prefix`, or `both` |
87
+
88
+ **Recommended for Polish:** `alpha=2.0, constraint="suffix"` (see paper, §Penalty Sweep).
89
+
90
+ ## How It Works
91
+
92
+ During BPE training, each candidate merge is scored by its corpus frequency.
93
+ MorphBPE multiplies the frequency of boundary-crossing merges by `(1 - alpha)`:
94
+
95
+ - `alpha=0.0` → no penalty → standard BPE
96
+ - `alpha=0.5` → 50% frequency reduction for crossing merges
97
+ - `alpha=1.0` → full block (frequency = 0)
98
+ - `alpha=2.0` → negative score → crossing merges actively deprioritized
99
+
100
+ Boundaries are detected using [Morfeusz2](http://morfeusz.sgjp.pl/) (Polish morphological analyzer)
101
+ or a heuristic fallback for environments without Morfeusz2.
102
+
103
+ ## Results
104
+
105
+ ### Intrinsic Evaluation (from paper, v=1000)
106
+
107
+ | Metric | BPE | MorphBPE α=1.0 | Δ |
108
+ |--------|-----|----------------|---|
109
+ | Fertility (v=1000) | 1.935 | 1.935 | +0.1% |
110
+ | MorphED (v=1000) | 1.838 | 1.712 | **−6.8%** |
111
+ | CE LSTM (v=1000) | 3.744 | 3.646 | **−2.6%** |
112
+ | CE Transformer (v=1000) | 3.525 | 3.431 | **−2.7%** |
113
+
114
+ Over-penalization (`alpha=2.0`) reduces CE by an additional 1.6–3.1%.
115
+
116
+ ### Extrinsic Evaluation (vocab 8k–32k, fine-grained alpha sweep)
117
+
118
+ Corpus: Sejm speeches (8000 train / 500 test), seq_len=32, 3 epochs. See `plots/` for visualizations.
119
+
120
+ | Vocab | α | Model | CE | PPL |
121
+ |-------|---|-------|----|-----|
122
+ | 8k | 0.0 (BPE) | LSTM | 4.1073 | 60.78 |
123
+ | 8k | 0.0 (BPE) | Transformer | 3.9140 | 50.10 |
124
+ | 8k | 0.5 | LSTM | 4.0433 | 57.01 |
125
+ | 8k | 0.5 | Transformer | 3.8552 | 47.24 |
126
+ | 8k | 1.0 | LSTM | 3.9243 | 50.62 |
127
+ | 8k | 1.0 | Transformer | 3.7743 | 43.57 |
128
+ | 8k | 1.5 | LSTM | 3.8849 | 48.66 |
129
+ | 8k | 1.5 | Transformer | 3.7112 | 40.90 |
130
+ | 8k | 2.0 | LSTM | 3.7758 | 43.63 |
131
+ | 8k | 2.0 | Transformer | 3.6319 | 37.79 |
132
+ | 8k | 2.5 | LSTM | 3.7871 | 44.13 |
133
+ | 8k | 2.5 | Transformer | **3.6210** | **37.37** |
134
+ | 16k | 0.0 (BPE) | LSTM | 4.1494 | 63.40 |
135
+ | 16k | 0.0 (BPE) | Transformer | 3.9473 | 51.80 |
136
+ | 16k | 0.5 | LSTM | 4.1132 | 61.14 |
137
+ | 16k | 0.5 | Transformer | 3.8966 | 49.24 |
138
+ | 16k | 1.0 | LSTM | 3.9870 | 53.89 |
139
+ | 16k | 1.0 | Transformer | 3.7789 | 43.77 |
140
+ | 16k | 1.5 | LSTM | 3.9287 | 50.84 |
141
+ | 16k | 1.5 | Transformer | 3.7188 | 41.21 |
142
+ | 16k | 2.0 | LSTM | 3.7984 | 44.63 |
143
+ | 16k | 2.0 | Transformer | 3.6272 | 37.61 |
144
+ | 16k | 2.5 | LSTM | 3.8305 | 46.09 |
145
+ | 16k | 2.5 | Transformer | 3.6276 | 37.62 |
146
+ | 32k | 0.0 (BPE) | LSTM | 4.2140 | 67.62 |
147
+ | 32k | 0.0 (BPE) | Transformer | 3.9605 | 52.49 |
148
+ | 32k | 0.5 | LSTM | 4.1380 | 62.68 |
149
+ | 32k | 0.5 | Transformer | 3.9135 | 50.07 |
150
+ | 32k | 1.0 | LSTM | 4.0106 | 55.18 |
151
+ | 32k | 1.0 | Transformer | 3.7803 | 43.83 |
152
+ | 32k | 1.5 | LSTM | 3.9687 | 52.92 |
153
+ | 32k | 1.5 | Transformer | 3.7210 | 41.31 |
154
+ | 32k | 2.0 | LSTM | 3.8427 | 46.65 |
155
+ | 32k | 2.0 | Transformer | 3.6347 | 37.89 |
156
+ | 32k | 2.5 | LSTM | 3.8777 | 48.31 |
157
+ | 32k | 2.5 | Transformer | 3.6403 | 38.10 |
158
+
159
+ **Key findings:**
160
+
161
+ - **Monotonic improvement up to α=2.0**: CE decreases monotonically with α up to 2.0 for all three vocab sizes (8k, 16k, 32k) across both models
162
+ - **α=2.0 is the sweet spot**: α=2.5 shows diminishing returns — improvements plateau or reverse (especially at 16k and 32k), confirming α=2.0 as the optimal penalty strength
163
+ - **Best overall**: 8k α=2.5 Transformer (CE=3.6210, PPL=37.37) edges out 8k α=2.0 (CE=3.6319), but 16k and 32k α=2.5 are worse than α=2.0
164
+ - **α=1.5 sweet spot for efficiency**: 8k α=1.5 Transformer (CE=3.7112, PPL=40.90) beats 32k BPE Transformer (CE=3.9605, PPL=52.49) — 4x smaller vocab, 22% lower PPL. *Caveat: CE/PPL across different vocab sizes are not strictly comparable without bits-per-character normalization (see Methodological caveats below).*
165
+ - **CE reduction vs BPE**: α=0.5 → 0.9–1.8%; α=1.0 → 3.6–4.8%; α=1.5 → 5.2–6.0%; α=2.0 → 7.2–8.8%; α=2.5 → 7.2–8.1% (plateau)
166
+ - **Transformer > LSTM**: 3.8–6.2% CE reduction across all configs
167
+ - **Vocab size**: 8k and 16k perform similarly at α=2.0; 32k slightly worse due to sparse coverage on the small pilot corpus
168
+
169
+ ### Cost-Benefit Analysis
170
+
171
+ Comparing MorphBPE 8k α=2.0 against a 4× larger BPE 32k tokenizer reveals a **trade-off**, not uniform dominance:
172
+
173
+ | Metric | BPE 32k | MorphBPE 8k α=2.0 | Δ | Winner |
174
+ |--------|---------|---------------------|---|--------|
175
+ | Vocab size | 32,000 | 8,000 | **4× smaller** | MorphBPE |
176
+ | Fertility (↓ better) | 1.020 | 1.149 | **+12.6%** | BPE 32k |
177
+ | MorphED (↓ better) | 0.389 | 0.456 | **+17.2%** | BPE 32k |
178
+ | CE Transformer (↓ better) | 3.9605 | 3.632 | **−8.3%** | MorphBPE |
179
+ | PPL Transformer (↓ better) | 52.49 | 37.79 | **−28.0%** | MorphBPE |
180
+
181
+ **Interpretation**: MorphBPE 8k α=2.0 wins decisively on language-model quality (CE/PPL) and vocabulary size, but at a cost — its fertility is 12.6% higher and its MorphED is 17.2% higher (worse) than BPE 32k. This is **not Pareto dominance**: the two configs trade off intrinsic segmentation quality (where the larger BPE 32k vocab wins on MorphED/fertility) against downstream LM performance and memory footprint (where MorphBPE 8k wins).
182
+
183
+ The higher MorphED and fertility for MorphBPE 8k are partly expected: it has 4× fewer tokens, so it must split words into more (and less morphologically-clean) pieces than a 32k vocab. The interesting result is that despite worse intrinsic metrics, the morphologically-informed 8k tokenizer still yields **lower LM cross-entropy** than 32k BPE.
184
+
185
+ > **Methodological caveats** (this is a small-corpus pilot — single runs, no seeds):
186
+ > 1. **CE/PPL are not directly comparable across vocab sizes.** Different tokenization granularity (fertility) affects per-token entropy independently of segmentation quality. A rigorous "8k beats 32k" claim requires bits-per-character, not raw CE/PPL. *(Planned: add bits-per-character.)*
187
+ > 2. **Small corpus (8k sentences) favors smaller vocabularies** regardless of morphology — 32k is under-trained with rare tokens on this little data, which may partly explain MorphBPE 8k's edge over BPE 32k.
188
+ > 3. **No repeats/seeds** — differences on the order of thousandths of CE (e.g. 8k vs 16k at α=2.0) have no estimated variance.
189
+ > 4. Conclusions about transfer to production-scale models are premature with an 8k-sentence corpus and seq_len=32.
190
+
191
+ See `plots/` for visualizations: `pareto_ce_vs_vocab.png`, `fertility_vs_morphed.png`, `efficiency_ratio.png`.
192
+
193
+ ## License
194
+
195
+ MIT
@@ -0,0 +1,177 @@
1
+ # MorphBPE
2
+
3
+ **Morphologically-aware BPE tokenizer for Polish.**
4
+
5
+ MorphBPE extends Byte-Pair Encoding with morphological boundary penalties.
6
+ Merges that cross morpheme boundaries receive a frequency penalty controlled by `alpha`,
7
+ preserving suffix (or prefix) structure during tokenization.
8
+
9
+ Based on: Styła (2026), *MorphBPE: morfologicznie informowany BPE dla polszczyzny*.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install morphbpe
15
+
16
+ # With Morfeusz2 support (recommended):
17
+ pip install morphbpe[morfeusz]
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### Python API
23
+
24
+ ```python
25
+ from morphbpe import MorphBPETokenizer
26
+
27
+ # Train
28
+ tok = MorphBPETokenizer(vocab_size=32000, alpha=2.0, constraint="suffix")
29
+ tok.train(["list of", "training texts", ...])
30
+
31
+ # Encode / decode
32
+ ids = tok.encode("prezydent podpisał ustawę")
33
+ text = tok.decode(ids)
34
+
35
+ # Subword tokens
36
+ tokens = tok.encode_word("ustawodawstwo")
37
+ # ['ustaw', 'odaw', 'stwo'] (example)
38
+
39
+ # Save / load
40
+ tok.save("my-tokenizer")
41
+ tok = MorphBPETokenizer.load("my-tokenizer")
42
+
43
+ # Metrics
44
+ print(tok.fertility(test_texts))
45
+ print(tok.morph_edit_distance(test_texts))
46
+ mean, lo, hi = tok.morph_edit_distance_ci(test_texts)
47
+ ```
48
+
49
+ ### CLI
50
+
51
+ ```bash
52
+ # Train
53
+ morphbpe train corpus.txt --vocab-size 32000 --alpha 2.0 --constraint suffix -o my-model
54
+
55
+ # Encode
56
+ echo "prezydent podpisał ustawę" | morphbpe encode my-model
57
+
58
+ # Info
59
+ morphbpe info my-model
60
+ ```
61
+
62
+ ## Key Parameters
63
+
64
+ | Parameter | Default | Description |
65
+ |-----------|---------|-------------|
66
+ | `vocab_size` | 1000 | Target vocabulary size |
67
+ | `alpha` | 1.0 | Penalty strength: 0=BPE, 1=block, >1=over-penalization |
68
+ | `constraint` | `"suffix"` | Boundary type: `suffix`, `prefix`, or `both` |
69
+
70
+ **Recommended for Polish:** `alpha=2.0, constraint="suffix"` (see paper, §Penalty Sweep).
71
+
72
+ ## How It Works
73
+
74
+ During BPE training, each candidate merge is scored by its corpus frequency.
75
+ MorphBPE multiplies the frequency of boundary-crossing merges by `(1 - alpha)`:
76
+
77
+ - `alpha=0.0` → no penalty → standard BPE
78
+ - `alpha=0.5` → 50% frequency reduction for crossing merges
79
+ - `alpha=1.0` → full block (frequency = 0)
80
+ - `alpha=2.0` → negative score → crossing merges actively deprioritized
81
+
82
+ Boundaries are detected using [Morfeusz2](http://morfeusz.sgjp.pl/) (Polish morphological analyzer)
83
+ or a heuristic fallback for environments without Morfeusz2.
84
+
85
+ ## Results
86
+
87
+ ### Intrinsic Evaluation (from paper, v=1000)
88
+
89
+ | Metric | BPE | MorphBPE α=1.0 | Δ |
90
+ |--------|-----|----------------|---|
91
+ | Fertility (v=1000) | 1.935 | 1.935 | +0.1% |
92
+ | MorphED (v=1000) | 1.838 | 1.712 | **−6.8%** |
93
+ | CE LSTM (v=1000) | 3.744 | 3.646 | **−2.6%** |
94
+ | CE Transformer (v=1000) | 3.525 | 3.431 | **−2.7%** |
95
+
96
+ Over-penalization (`alpha=2.0`) reduces CE by an additional 1.6–3.1%.
97
+
98
+ ### Extrinsic Evaluation (vocab 8k–32k, fine-grained alpha sweep)
99
+
100
+ Corpus: Sejm speeches (8000 train / 500 test), seq_len=32, 3 epochs. See `plots/` for visualizations.
101
+
102
+ | Vocab | α | Model | CE | PPL |
103
+ |-------|---|-------|----|-----|
104
+ | 8k | 0.0 (BPE) | LSTM | 4.1073 | 60.78 |
105
+ | 8k | 0.0 (BPE) | Transformer | 3.9140 | 50.10 |
106
+ | 8k | 0.5 | LSTM | 4.0433 | 57.01 |
107
+ | 8k | 0.5 | Transformer | 3.8552 | 47.24 |
108
+ | 8k | 1.0 | LSTM | 3.9243 | 50.62 |
109
+ | 8k | 1.0 | Transformer | 3.7743 | 43.57 |
110
+ | 8k | 1.5 | LSTM | 3.8849 | 48.66 |
111
+ | 8k | 1.5 | Transformer | 3.7112 | 40.90 |
112
+ | 8k | 2.0 | LSTM | 3.7758 | 43.63 |
113
+ | 8k | 2.0 | Transformer | 3.6319 | 37.79 |
114
+ | 8k | 2.5 | LSTM | 3.7871 | 44.13 |
115
+ | 8k | 2.5 | Transformer | **3.6210** | **37.37** |
116
+ | 16k | 0.0 (BPE) | LSTM | 4.1494 | 63.40 |
117
+ | 16k | 0.0 (BPE) | Transformer | 3.9473 | 51.80 |
118
+ | 16k | 0.5 | LSTM | 4.1132 | 61.14 |
119
+ | 16k | 0.5 | Transformer | 3.8966 | 49.24 |
120
+ | 16k | 1.0 | LSTM | 3.9870 | 53.89 |
121
+ | 16k | 1.0 | Transformer | 3.7789 | 43.77 |
122
+ | 16k | 1.5 | LSTM | 3.9287 | 50.84 |
123
+ | 16k | 1.5 | Transformer | 3.7188 | 41.21 |
124
+ | 16k | 2.0 | LSTM | 3.7984 | 44.63 |
125
+ | 16k | 2.0 | Transformer | 3.6272 | 37.61 |
126
+ | 16k | 2.5 | LSTM | 3.8305 | 46.09 |
127
+ | 16k | 2.5 | Transformer | 3.6276 | 37.62 |
128
+ | 32k | 0.0 (BPE) | LSTM | 4.2140 | 67.62 |
129
+ | 32k | 0.0 (BPE) | Transformer | 3.9605 | 52.49 |
130
+ | 32k | 0.5 | LSTM | 4.1380 | 62.68 |
131
+ | 32k | 0.5 | Transformer | 3.9135 | 50.07 |
132
+ | 32k | 1.0 | LSTM | 4.0106 | 55.18 |
133
+ | 32k | 1.0 | Transformer | 3.7803 | 43.83 |
134
+ | 32k | 1.5 | LSTM | 3.9687 | 52.92 |
135
+ | 32k | 1.5 | Transformer | 3.7210 | 41.31 |
136
+ | 32k | 2.0 | LSTM | 3.8427 | 46.65 |
137
+ | 32k | 2.0 | Transformer | 3.6347 | 37.89 |
138
+ | 32k | 2.5 | LSTM | 3.8777 | 48.31 |
139
+ | 32k | 2.5 | Transformer | 3.6403 | 38.10 |
140
+
141
+ **Key findings:**
142
+
143
+ - **Monotonic improvement up to α=2.0**: CE decreases monotonically with α up to 2.0 for all three vocab sizes (8k, 16k, 32k) across both models
144
+ - **α=2.0 is the sweet spot**: α=2.5 shows diminishing returns — improvements plateau or reverse (especially at 16k and 32k), confirming α=2.0 as the optimal penalty strength
145
+ - **Best overall**: 8k α=2.5 Transformer (CE=3.6210, PPL=37.37) edges out 8k α=2.0 (CE=3.6319), but 16k and 32k α=2.5 are worse than α=2.0
146
+ - **α=1.5 sweet spot for efficiency**: 8k α=1.5 Transformer (CE=3.7112, PPL=40.90) beats 32k BPE Transformer (CE=3.9605, PPL=52.49) — 4x smaller vocab, 22% lower PPL. *Caveat: CE/PPL across different vocab sizes are not strictly comparable without bits-per-character normalization (see Methodological caveats below).*
147
+ - **CE reduction vs BPE**: α=0.5 → 0.9–1.8%; α=1.0 → 3.6–4.8%; α=1.5 → 5.2–6.0%; α=2.0 → 7.2–8.8%; α=2.5 → 7.2–8.1% (plateau)
148
+ - **Transformer > LSTM**: 3.8–6.2% CE reduction across all configs
149
+ - **Vocab size**: 8k and 16k perform similarly at α=2.0; 32k slightly worse due to sparse coverage on the small pilot corpus
150
+
151
+ ### Cost-Benefit Analysis
152
+
153
+ Comparing MorphBPE 8k α=2.0 against a 4× larger BPE 32k tokenizer reveals a **trade-off**, not uniform dominance:
154
+
155
+ | Metric | BPE 32k | MorphBPE 8k α=2.0 | Δ | Winner |
156
+ |--------|---------|---------------------|---|--------|
157
+ | Vocab size | 32,000 | 8,000 | **4× smaller** | MorphBPE |
158
+ | Fertility (↓ better) | 1.020 | 1.149 | **+12.6%** | BPE 32k |
159
+ | MorphED (↓ better) | 0.389 | 0.456 | **+17.2%** | BPE 32k |
160
+ | CE Transformer (↓ better) | 3.9605 | 3.632 | **−8.3%** | MorphBPE |
161
+ | PPL Transformer (↓ better) | 52.49 | 37.79 | **−28.0%** | MorphBPE |
162
+
163
+ **Interpretation**: MorphBPE 8k α=2.0 wins decisively on language-model quality (CE/PPL) and vocabulary size, but at a cost — its fertility is 12.6% higher and its MorphED is 17.2% higher (worse) than BPE 32k. This is **not Pareto dominance**: the two configs trade off intrinsic segmentation quality (where the larger BPE 32k vocab wins on MorphED/fertility) against downstream LM performance and memory footprint (where MorphBPE 8k wins).
164
+
165
+ The higher MorphED and fertility for MorphBPE 8k are partly expected: it has 4× fewer tokens, so it must split words into more (and less morphologically-clean) pieces than a 32k vocab. The interesting result is that despite worse intrinsic metrics, the morphologically-informed 8k tokenizer still yields **lower LM cross-entropy** than 32k BPE.
166
+
167
+ > **Methodological caveats** (this is a small-corpus pilot — single runs, no seeds):
168
+ > 1. **CE/PPL are not directly comparable across vocab sizes.** Different tokenization granularity (fertility) affects per-token entropy independently of segmentation quality. A rigorous "8k beats 32k" claim requires bits-per-character, not raw CE/PPL. *(Planned: add bits-per-character.)*
169
+ > 2. **Small corpus (8k sentences) favors smaller vocabularies** regardless of morphology — 32k is under-trained with rare tokens on this little data, which may partly explain MorphBPE 8k's edge over BPE 32k.
170
+ > 3. **No repeats/seeds** — differences on the order of thousandths of CE (e.g. 8k vs 16k at α=2.0) have no estimated variance.
171
+ > 4. Conclusions about transfer to production-scale models are premature with an 8k-sentence corpus and seq_len=32.
172
+
173
+ See `plots/` for visualizations: `pareto_ce_vs_vocab.png`, `fertility_vs_morphed.png`, `efficiency_ratio.png`.
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,22 @@
1
+ """MorphBPE: Morphologically-aware BPE tokenizer for Polish."""
2
+
3
+ __version__ = "0.3.0"
4
+
5
+ from morphbpe.tokenizer import (
6
+ MorphBPETokenizer,
7
+ pre_tokenize,
8
+ SPECIAL_TOKENS,
9
+ BYTE_TOKENS,
10
+ BYTE_TOKENS_INV,
11
+ )
12
+ from morphbpe.morph_metrics import evaluate_all, evaluate_lexeme
13
+
14
+ __all__ = [
15
+ "MorphBPETokenizer",
16
+ "pre_tokenize",
17
+ "SPECIAL_TOKENS",
18
+ "BYTE_TOKENS",
19
+ "BYTE_TOKENS_INV",
20
+ "evaluate_all",
21
+ "evaluate_lexeme",
22
+ ]
@@ -0,0 +1,84 @@
1
+ """Command-line interface for MorphBPE."""
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ from morphbpe.tokenizer import MorphBPETokenizer
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(
11
+ prog="morphbpe",
12
+ description="MorphBPE: Morphologically-aware BPE tokenizer for Polish",
13
+ )
14
+ sub = parser.add_subparsers(dest="command", required=True)
15
+
16
+ # ── train ────────────────────────────────────────────────────────
17
+ p_train = sub.add_parser("train", help="Train a tokenizer from a text file")
18
+ p_train.add_argument("corpus", help="Path to plain-text corpus (one doc per line)")
19
+ p_train.add_argument("-v", "--vocab-size", type=int, default=1000)
20
+ p_train.add_argument("-a", "--alpha", type=float, default=2.0)
21
+ p_train.add_argument(
22
+ "-c", "--constraint", choices=["suffix", "prefix", "both"], default="suffix"
23
+ )
24
+ p_train.add_argument(
25
+ "-o", "--output", default="morphbpe-model", help="Output directory"
26
+ )
27
+
28
+ # ── encode ───────────────────────────────────────────────────────
29
+ p_enc = sub.add_parser("encode", help="Encode text using a trained tokenizer")
30
+ p_enc.add_argument("model", help="Tokenizer directory")
31
+ p_enc.add_argument(
32
+ "text", nargs="?", default=None, help="Text to encode (or stdin)"
33
+ )
34
+
35
+ # ── info ─────────────────────────────────────────────────────────
36
+ p_info = sub.add_parser("info", help="Show tokenizer info")
37
+ p_info.add_argument("model", help="Tokenizer directory")
38
+
39
+ args = parser.parse_args()
40
+
41
+ if args.command == "train":
42
+ _cmd_train(args)
43
+ elif args.command == "encode":
44
+ _cmd_encode(args)
45
+ elif args.command == "info":
46
+ _cmd_info(args)
47
+
48
+
49
+ def _cmd_train(args):
50
+ tok = MorphBPETokenizer(
51
+ vocab_size=args.vocab_size,
52
+ alpha=args.alpha,
53
+ constraint=args.constraint,
54
+ )
55
+ tok.train_from_file(args.corpus)
56
+ tok.save(args.output)
57
+ print(f"Saved to {args.output}/")
58
+
59
+
60
+ def _cmd_encode(args):
61
+ tok = MorphBPETokenizer.load(args.model)
62
+ if args.text:
63
+ lines = [args.text]
64
+ else:
65
+ lines = sys.stdin.read().splitlines()
66
+
67
+ for line in lines:
68
+ tokens = []
69
+ for word in line.split():
70
+ tokens.extend(tok.encode_word(word))
71
+ print(" ".join(tokens))
72
+
73
+
74
+ def _cmd_info(args):
75
+ tok = MorphBPETokenizer.load(args.model)
76
+ print(f"Vocab size: {tok.get_vocab_size()}")
77
+ print(f"Merges: {len(tok.merges)}")
78
+ print(f"Alpha: {tok.alpha}")
79
+ print(f"Constraint: {tok.constraint}")
80
+ print(f"Morfeusz: {tok._backend.has_morfeusz}")
81
+
82
+
83
+ if __name__ == "__main__":
84
+ main()