phonebox 0.1.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.
- phonebox-0.1.0/LICENSE +21 -0
- phonebox-0.1.0/MANIFEST.in +19 -0
- phonebox-0.1.0/PKG-INFO +249 -0
- phonebox-0.1.0/README.md +206 -0
- phonebox-0.1.0/examples/library_usage.py +133 -0
- phonebox-0.1.0/examples/nbest_example.py +286 -0
- phonebox-0.1.0/examples/score_dictionary.py +342 -0
- phonebox-0.1.0/examples/score_pronunciation.py +82 -0
- phonebox-0.1.0/examples/simple_api.py +19 -0
- phonebox-0.1.0/examples/training_example.py +81 -0
- phonebox-0.1.0/phonebox/__init__.py +41 -0
- phonebox-0.1.0/phonebox/bundler.py +77 -0
- phonebox-0.1.0/phonebox/cart/__init__.py +11 -0
- phonebox-0.1.0/phonebox/cart/g2p_predict.py +315 -0
- phonebox-0.1.0/phonebox/cli/__init__.py +8 -0
- phonebox-0.1.0/phonebox/cli/commands/__init__.py +3 -0
- phonebox-0.1.0/phonebox/cli/commands/_common.py +61 -0
- phonebox-0.1.0/phonebox/cli/commands/align.py +73 -0
- phonebox-0.1.0/phonebox/cli/commands/bundle.py +71 -0
- phonebox-0.1.0/phonebox/cli/commands/check.py +218 -0
- phonebox-0.1.0/phonebox/cli/commands/compare.py +157 -0
- phonebox-0.1.0/phonebox/cli/commands/dict.py +120 -0
- phonebox-0.1.0/phonebox/cli/commands/model.py +353 -0
- phonebox-0.1.0/phonebox/cli/commands/normalize.py +79 -0
- phonebox-0.1.0/phonebox/cli/commands/pronounce.py +190 -0
- phonebox-0.1.0/phonebox/cli/commands/recipe.py +302 -0
- phonebox-0.1.0/phonebox/cli/commands/suggest_joins.py +291 -0
- phonebox-0.1.0/phonebox/cli/commands/train.py +182 -0
- phonebox-0.1.0/phonebox/cli/commands/train_multigram.py +124 -0
- phonebox-0.1.0/phonebox/cli/commands/vectorize.py +61 -0
- phonebox-0.1.0/phonebox/cli/main.py +159 -0
- phonebox-0.1.0/phonebox/config/__init__.py +1 -0
- phonebox-0.1.0/phonebox/config/locales/de_DE/config.json +25 -0
- phonebox-0.1.0/phonebox/config/locales/de_DE/defaults.json +13 -0
- phonebox-0.1.0/phonebox/config/locales/de_DE/g2p.xlit +13 -0
- phonebox-0.1.0/phonebox/config/locales/default/config.json +8 -0
- phonebox-0.1.0/phonebox/config/locales/default/defaults.json +8 -0
- phonebox-0.1.0/phonebox/config/locales/default/g2p.xlit +6 -0
- phonebox-0.1.0/phonebox/config/locales/en_IN/config.json +32 -0
- phonebox-0.1.0/phonebox/config/locales/en_IN/defaults.json +12 -0
- phonebox-0.1.0/phonebox/config/locales/en_US/config.json +64 -0
- phonebox-0.1.0/phonebox/config/locales/en_US/defaults.json +12 -0
- phonebox-0.1.0/phonebox/config/locales/en_US/g2p.xlit +6 -0
- phonebox-0.1.0/phonebox/config/locales/es_MX/config.json +12 -0
- phonebox-0.1.0/phonebox/config/locales/es_MX/defaults.json +10 -0
- phonebox-0.1.0/phonebox/config/locales/es_MX/g2p.xlit +30 -0
- phonebox-0.1.0/phonebox/config/locales/fr_FR/config.json +22 -0
- phonebox-0.1.0/phonebox/config/locales/fr_FR/defaults.json +12 -0
- phonebox-0.1.0/phonebox/config/locales/fr_FR/g2p.xlit +14 -0
- phonebox-0.1.0/phonebox/config/locales/it_IT/config.json +22 -0
- phonebox-0.1.0/phonebox/config/locales/it_IT/defaults.json +8 -0
- phonebox-0.1.0/phonebox/config/locales/it_IT/g2p.xlit +23 -0
- phonebox-0.1.0/phonebox/config/locales/norm.xlit +36 -0
- phonebox-0.1.0/phonebox/config/locales/pt_BR/config.json +14 -0
- phonebox-0.1.0/phonebox/config/locales/pt_BR/defaults.json +12 -0
- phonebox-0.1.0/phonebox/config/locales/pt_BR/g2p.xlit +12 -0
- phonebox-0.1.0/phonebox/config_builder.py +63 -0
- phonebox-0.1.0/phonebox/config_loader.py +206 -0
- phonebox-0.1.0/phonebox/configs/__init__.py +54 -0
- phonebox-0.1.0/phonebox/configs/accurate.yaml +25 -0
- phonebox-0.1.0/phonebox/configs/defaults.yaml +82 -0
- phonebox-0.1.0/phonebox/configs/fast.yaml +25 -0
- phonebox-0.1.0/phonebox/configs/pocketsphinx.yaml +28 -0
- phonebox-0.1.0/phonebox/configs/xsampa.yaml +27 -0
- phonebox-0.1.0/phonebox/constants.py +230 -0
- phonebox-0.1.0/phonebox/converter.py +366 -0
- phonebox-0.1.0/phonebox/core/__init__.py +11 -0
- phonebox-0.1.0/phonebox/core/decision_tree.py +8 -0
- phonebox-0.1.0/phonebox/core/em_align.py +514 -0
- phonebox-0.1.0/phonebox/core/g2p_model.py +960 -0
- phonebox-0.1.0/phonebox/core/joint_decode.py +111 -0
- phonebox-0.1.0/phonebox/core/multigram_align.py +703 -0
- phonebox-0.1.0/phonebox/core/multigram_g2p.py +286 -0
- phonebox-0.1.0/phonebox/core/multigram_lm.py +187 -0
- phonebox-0.1.0/phonebox/core/nbest.py +69 -0
- phonebox-0.1.0/phonebox/core/vectorizer.py +585 -0
- phonebox-0.1.0/phonebox/dictionary.py +428 -0
- phonebox-0.1.0/phonebox/eval/__init__.py +29 -0
- phonebox-0.1.0/phonebox/eval/g2p_compare.py +672 -0
- phonebox-0.1.0/phonebox/eval/g2p_compare_all.py +342 -0
- phonebox-0.1.0/phonebox/experiments/__init__.py +11 -0
- phonebox-0.1.0/phonebox/experiments/analysis.py +82 -0
- phonebox-0.1.0/phonebox/experiments/equiv.py +37 -0
- phonebox-0.1.0/phonebox/experiments/metrics.py +14 -0
- phonebox-0.1.0/phonebox/experiments/normalize.py +122 -0
- phonebox-0.1.0/phonebox/experiments/split.py +26 -0
- phonebox-0.1.0/phonebox/locales.py +84 -0
- phonebox-0.1.0/phonebox/normalize.py +62 -0
- phonebox-0.1.0/phonebox/py.typed +0 -0
- phonebox-0.1.0/phonebox/runner.py +245 -0
- phonebox-0.1.0/phonebox/tools/__init__.py +5 -0
- phonebox-0.1.0/phonebox/tools/count_diphones.py +53 -0
- phonebox-0.1.0/phonebox/utils/__init__.py +9 -0
- phonebox-0.1.0/phonebox/utils/icu_utils.py +60 -0
- phonebox-0.1.0/phonebox/utils/io.py +24 -0
- phonebox-0.1.0/phonebox/utils/logging_config.py +21 -0
- phonebox-0.1.0/phonebox.egg-info/PKG-INFO +249 -0
- phonebox-0.1.0/phonebox.egg-info/SOURCES.txt +123 -0
- phonebox-0.1.0/phonebox.egg-info/dependency_links.txt +1 -0
- phonebox-0.1.0/phonebox.egg-info/entry_points.txt +2 -0
- phonebox-0.1.0/phonebox.egg-info/requires.txt +21 -0
- phonebox-0.1.0/phonebox.egg-info/top_level.txt +1 -0
- phonebox-0.1.0/pyproject.toml +144 -0
- phonebox-0.1.0/requirements-dev.txt +30 -0
- phonebox-0.1.0/requirements.txt +5 -0
- phonebox-0.1.0/setup.cfg +4 -0
- phonebox-0.1.0/tests/__init__.py +1 -0
- phonebox-0.1.0/tests/test_cli.py +506 -0
- phonebox-0.1.0/tests/test_decision_tree.py +124 -0
- phonebox-0.1.0/tests/test_dict_processing.py +137 -0
- phonebox-0.1.0/tests/test_dictionary.py +121 -0
- phonebox-0.1.0/tests/test_edge_cases.py +591 -0
- phonebox-0.1.0/tests/test_experiment_normalize.py +69 -0
- phonebox-0.1.0/tests/test_g2p_api.py +204 -0
- phonebox-0.1.0/tests/test_integration.py +99 -0
- phonebox-0.1.0/tests/test_it_IT_locale.py +45 -0
- phonebox-0.1.0/tests/test_joint_decode.py +34 -0
- phonebox-0.1.0/tests/test_multigram_align.py +217 -0
- phonebox-0.1.0/tests/test_multigram_g2p.py +144 -0
- phonebox-0.1.0/tests/test_multigram_lm.py +17 -0
- phonebox-0.1.0/tests/test_nbest.py +446 -0
- phonebox-0.1.0/tests/test_no_config_joins.py +21 -0
- phonebox-0.1.0/tests/test_pocketsphinx.py +154 -0
- phonebox-0.1.0/tests/test_runner.py +165 -0
- phonebox-0.1.0/tests/test_vectorizer.py +101 -0
phonebox-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-2024 Kevin Lenzo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Include package data
|
|
2
|
+
include README.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
include requirements.txt
|
|
5
|
+
include requirements-dev.txt
|
|
6
|
+
|
|
7
|
+
# Include all configuration files
|
|
8
|
+
recursive-include g2p/config *.json *.xlit
|
|
9
|
+
|
|
10
|
+
# Include examples
|
|
11
|
+
recursive-include examples *.py
|
|
12
|
+
|
|
13
|
+
# Include tests
|
|
14
|
+
recursive-include tests *.py
|
|
15
|
+
|
|
16
|
+
# Exclude build artifacts
|
|
17
|
+
global-exclude __pycache__
|
|
18
|
+
global-exclude *.py[co]
|
|
19
|
+
global-exclude .DS_Store
|
phonebox-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phonebox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast, lightweight grapheme-to-phoneme conversion using decision trees
|
|
5
|
+
Author: Kevin Lenzo (lenzo-ka)
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lenzo-ka/phonebox
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/lenzo-ka/phonebox/issues
|
|
9
|
+
Project-URL: Source, https://github.com/lenzo-ka/phonebox
|
|
10
|
+
Keywords: g2p,grapheme-to-phoneme,pronunciation,phonetics,speech,nlp,phonebox
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: cartlet>=0.5.0
|
|
26
|
+
Requires-Dist: icukit>=0.1.2
|
|
27
|
+
Provides-Extra: icu
|
|
28
|
+
Requires-Dist: PyICU>=2.11; extra == "icu"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pre-commit>=3.0; extra == "dev"
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: sphinx>=5.0; extra == "docs"
|
|
37
|
+
Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
|
|
38
|
+
Requires-Dist: myst-parser>=0.18; extra == "docs"
|
|
39
|
+
Provides-Extra: viz
|
|
40
|
+
Requires-Dist: graphviz>=0.20; extra == "viz"
|
|
41
|
+
Requires-Dist: matplotlib>=3.5.0; extra == "viz"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# Phonebox: Grapheme-to-Phoneme Conversion
|
|
45
|
+
|
|
46
|
+
Fast, lightweight grapheme-to-phoneme (G2P) conversion using decision trees and
|
|
47
|
+
EM alignment. The package also includes **MultigramG2P** (n:m joint Viterbi) in
|
|
48
|
+
the library; 1:1 CLI commands use the decision tree path.
|
|
49
|
+
|
|
50
|
+
Decision-tree G2P is fast, compact, and interpretable; it trades some accuracy
|
|
51
|
+
for those properties, and neural G2P methods will generally score better.
|
|
52
|
+
CMUdict / PocketSphinx workflows are the main English use case; measured phone
|
|
53
|
+
error rates and IPA locale benchmarks are in
|
|
54
|
+
[`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- **Measured accuracy**: reported phone error rates on CMUdict (see docs/BENCHMARKS.md); neural G2P methods can be more accurate
|
|
59
|
+
- **Compact Models**: < 1MB model size for typical 1:1 trees
|
|
60
|
+
- **MultigramG2P**: n:m joint EM + Viterbi decode (`MultigramG2P` in Python API)
|
|
61
|
+
- **Zero Dependencies**: Bundled Python executable works standalone
|
|
62
|
+
- **CMUdict Support**: English pronunciation with PocketSphinx compatibility
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install phonebox
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or from source:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/lenzo-ka/phonebox.git
|
|
74
|
+
cd phonebox
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
### One Command
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Build G2P from CMUdict, bundle as Python executable
|
|
84
|
+
phonebox recipe cmudict pocketsphinx -o g2p.py
|
|
85
|
+
|
|
86
|
+
# Use it
|
|
87
|
+
python g2p.py "Hello, world!"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### TTS Preset (keeps stress)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
phonebox recipe cmudict tts -o g2p.py
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Using Bundled G2P
|
|
97
|
+
|
|
98
|
+
### Command Line
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
python g2p.py "Hello, world!"
|
|
102
|
+
# hello HH AH L OW
|
|
103
|
+
# world W ER L D
|
|
104
|
+
|
|
105
|
+
# Raw mode (no text normalization)
|
|
106
|
+
python g2p.py -r "Hello,"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Python Library
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from g2p import G2PPredictor
|
|
113
|
+
|
|
114
|
+
g2p = G2PPredictor.from_embedded()
|
|
115
|
+
phones = g2p.pronounce("hello") # ['HH', 'AH', 'L', 'OW']
|
|
116
|
+
|
|
117
|
+
# Process text (tokenizes automatically)
|
|
118
|
+
for word, phones in g2p.pronounce_text("Hello, world!"):
|
|
119
|
+
print(f"{word}: {' '.join(phones)}")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## CLI Commands
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Quick Start:
|
|
126
|
+
recipe Build complete G2P from dictionary (one command)
|
|
127
|
+
|
|
128
|
+
Using Models:
|
|
129
|
+
pronounce Get pronunciations for words
|
|
130
|
+
normalize Preview text normalization/tokenization
|
|
131
|
+
bundle Create standalone executable with embedded model
|
|
132
|
+
|
|
133
|
+
Building Models:
|
|
134
|
+
model Model operations (build, train, convert, benchmark)
|
|
135
|
+
dict Dictionary operations (fetch, export-vectors)
|
|
136
|
+
|
|
137
|
+
Low-Level:
|
|
138
|
+
align Align letters to phonemes (EM algorithm)
|
|
139
|
+
vectorize Convert alignments to feature vectors
|
|
140
|
+
|
|
141
|
+
Quality:
|
|
142
|
+
check Validate lexicon against phoneset
|
|
143
|
+
suggest-joins Discover join candidates (multigram EM)
|
|
144
|
+
compare 1:1 vs n:m eval (locale or all IPA locales)
|
|
145
|
+
train-multigram Train/export MultigramG2P
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### G2P evaluation (IPA locales)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
phonebox compare all # docs/G2P_COMPARE.md
|
|
152
|
+
phonebox compare locale --lexicon … --locale it_IT
|
|
153
|
+
phonebox train-multigram --locale it_IT --lexicon it_ipa.tsv -o model.g2p.gz
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Repo wrappers: `compare_g2p_all.py`, `compare_g2p_sweep.py`, `dump_units.py`.
|
|
157
|
+
See [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
|
|
158
|
+
|
|
159
|
+
### Examples
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Preview text normalization
|
|
163
|
+
phonebox normalize "Hello, world!"
|
|
164
|
+
|
|
165
|
+
# Pronounce with existing model
|
|
166
|
+
phonebox pronounce hello world -m model.g2p.gz
|
|
167
|
+
|
|
168
|
+
# Benchmark model
|
|
169
|
+
phonebox model benchmark model.g2p.gz
|
|
170
|
+
|
|
171
|
+
# Fetch dictionary manually
|
|
172
|
+
phonebox dict fetch cmudict
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Python API
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from phonebox import G2P, MultigramG2P
|
|
179
|
+
|
|
180
|
+
# 1:1 decision tree (CLI: phonebox pronounce)
|
|
181
|
+
g2p = G2P(model="model.g2p.gz")
|
|
182
|
+
phones = g2p.pronounce("hello")
|
|
183
|
+
print(phones) # ['HH', 'AH', 'L', 'OW']
|
|
184
|
+
|
|
185
|
+
# n:m multigram (train via CLI or library)
|
|
186
|
+
mg = MultigramG2P(max_letter_span=2, max_phone_span=2)
|
|
187
|
+
mg.train_from_dict("lexicon.tsv")
|
|
188
|
+
# phonebox train-multigram … ; phonebox pronounce -m model.g2p.gz (sidecar auto-detect)
|
|
189
|
+
|
|
190
|
+
# N-best alternatives
|
|
191
|
+
for pron, score in g2p.pronounce_nbest("read", n=3):
|
|
192
|
+
print(f"{pron} ({score:.3f})")
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Step-by-Step Training
|
|
196
|
+
|
|
197
|
+
For debugging or custom workflows:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# 1. Fetch dictionary
|
|
201
|
+
phonebox dict fetch cmudict
|
|
202
|
+
|
|
203
|
+
# 2. Align letters to phonemes
|
|
204
|
+
phonebox align data/cmudict/cmudict.dict -o alignments.txt --remove-stress
|
|
205
|
+
|
|
206
|
+
# 3. Vectorize alignments
|
|
207
|
+
phonebox vectorize alignments.txt -o vectors.txt
|
|
208
|
+
|
|
209
|
+
# 4. Train from vectors
|
|
210
|
+
phonebox model train en_US --vectors vectors.txt -o model.g2p.gz
|
|
211
|
+
|
|
212
|
+
# 5. Bundle
|
|
213
|
+
phonebox bundle model.g2p.gz -o g2p.py
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Standalone Deployment
|
|
217
|
+
|
|
218
|
+
Bundled files have zero dependencies beyond the Python standard library:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
phonebox bundle model.g2p.gz -o g2p.py
|
|
222
|
+
python g2p.py "test"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Algorithm
|
|
226
|
+
|
|
227
|
+
1. **EM Alignment**: Expectation-Maximization aligns letters to phonemes
|
|
228
|
+
2. **Feature Extraction**: 7-gram letter windows create feature vectors
|
|
229
|
+
3. **Decision Tree**: ID3-style tree trained on aligned data
|
|
230
|
+
4. **Prediction**: Tree traversal based on letter context
|
|
231
|
+
|
|
232
|
+
Based on research from CMU:
|
|
233
|
+
- [CMU G2P Research](http://www.cs.cmu.edu/afs/cs.cmu.edu/user/lenzo/html/areas/t2p/)
|
|
234
|
+
- [ICSLP 1998 Paper](https://www.isca-speech.org/archive/icslp_1998/i98_0561.html)
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT License - see LICENSE file.
|
|
239
|
+
|
|
240
|
+
## Author
|
|
241
|
+
|
|
242
|
+
Kevin Lenzo ([@lenzo-ka](https://github.com/lenzo-ka))
|
|
243
|
+
|
|
244
|
+
## Contributing
|
|
245
|
+
|
|
246
|
+
1. Fork the repository
|
|
247
|
+
2. Create a feature branch
|
|
248
|
+
3. Add tests for new functionality
|
|
249
|
+
4. Submit a pull request
|
phonebox-0.1.0/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Phonebox: Grapheme-to-Phoneme Conversion
|
|
2
|
+
|
|
3
|
+
Fast, lightweight grapheme-to-phoneme (G2P) conversion using decision trees and
|
|
4
|
+
EM alignment. The package also includes **MultigramG2P** (n:m joint Viterbi) in
|
|
5
|
+
the library; 1:1 CLI commands use the decision tree path.
|
|
6
|
+
|
|
7
|
+
Decision-tree G2P is fast, compact, and interpretable; it trades some accuracy
|
|
8
|
+
for those properties, and neural G2P methods will generally score better.
|
|
9
|
+
CMUdict / PocketSphinx workflows are the main English use case; measured phone
|
|
10
|
+
error rates and IPA locale benchmarks are in
|
|
11
|
+
[`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Measured accuracy**: reported phone error rates on CMUdict (see docs/BENCHMARKS.md); neural G2P methods can be more accurate
|
|
16
|
+
- **Compact Models**: < 1MB model size for typical 1:1 trees
|
|
17
|
+
- **MultigramG2P**: n:m joint EM + Viterbi decode (`MultigramG2P` in Python API)
|
|
18
|
+
- **Zero Dependencies**: Bundled Python executable works standalone
|
|
19
|
+
- **CMUdict Support**: English pronunciation with PocketSphinx compatibility
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install phonebox
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or from source:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/lenzo-ka/phonebox.git
|
|
31
|
+
cd phonebox
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### One Command
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Build G2P from CMUdict, bundle as Python executable
|
|
41
|
+
phonebox recipe cmudict pocketsphinx -o g2p.py
|
|
42
|
+
|
|
43
|
+
# Use it
|
|
44
|
+
python g2p.py "Hello, world!"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### TTS Preset (keeps stress)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
phonebox recipe cmudict tts -o g2p.py
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Using Bundled G2P
|
|
54
|
+
|
|
55
|
+
### Command Line
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python g2p.py "Hello, world!"
|
|
59
|
+
# hello HH AH L OW
|
|
60
|
+
# world W ER L D
|
|
61
|
+
|
|
62
|
+
# Raw mode (no text normalization)
|
|
63
|
+
python g2p.py -r "Hello,"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Python Library
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from g2p import G2PPredictor
|
|
70
|
+
|
|
71
|
+
g2p = G2PPredictor.from_embedded()
|
|
72
|
+
phones = g2p.pronounce("hello") # ['HH', 'AH', 'L', 'OW']
|
|
73
|
+
|
|
74
|
+
# Process text (tokenizes automatically)
|
|
75
|
+
for word, phones in g2p.pronounce_text("Hello, world!"):
|
|
76
|
+
print(f"{word}: {' '.join(phones)}")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## CLI Commands
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Quick Start:
|
|
83
|
+
recipe Build complete G2P from dictionary (one command)
|
|
84
|
+
|
|
85
|
+
Using Models:
|
|
86
|
+
pronounce Get pronunciations for words
|
|
87
|
+
normalize Preview text normalization/tokenization
|
|
88
|
+
bundle Create standalone executable with embedded model
|
|
89
|
+
|
|
90
|
+
Building Models:
|
|
91
|
+
model Model operations (build, train, convert, benchmark)
|
|
92
|
+
dict Dictionary operations (fetch, export-vectors)
|
|
93
|
+
|
|
94
|
+
Low-Level:
|
|
95
|
+
align Align letters to phonemes (EM algorithm)
|
|
96
|
+
vectorize Convert alignments to feature vectors
|
|
97
|
+
|
|
98
|
+
Quality:
|
|
99
|
+
check Validate lexicon against phoneset
|
|
100
|
+
suggest-joins Discover join candidates (multigram EM)
|
|
101
|
+
compare 1:1 vs n:m eval (locale or all IPA locales)
|
|
102
|
+
train-multigram Train/export MultigramG2P
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### G2P evaluation (IPA locales)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
phonebox compare all # docs/G2P_COMPARE.md
|
|
109
|
+
phonebox compare locale --lexicon … --locale it_IT
|
|
110
|
+
phonebox train-multigram --locale it_IT --lexicon it_ipa.tsv -o model.g2p.gz
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Repo wrappers: `compare_g2p_all.py`, `compare_g2p_sweep.py`, `dump_units.py`.
|
|
114
|
+
See [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
|
|
115
|
+
|
|
116
|
+
### Examples
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Preview text normalization
|
|
120
|
+
phonebox normalize "Hello, world!"
|
|
121
|
+
|
|
122
|
+
# Pronounce with existing model
|
|
123
|
+
phonebox pronounce hello world -m model.g2p.gz
|
|
124
|
+
|
|
125
|
+
# Benchmark model
|
|
126
|
+
phonebox model benchmark model.g2p.gz
|
|
127
|
+
|
|
128
|
+
# Fetch dictionary manually
|
|
129
|
+
phonebox dict fetch cmudict
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Python API
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from phonebox import G2P, MultigramG2P
|
|
136
|
+
|
|
137
|
+
# 1:1 decision tree (CLI: phonebox pronounce)
|
|
138
|
+
g2p = G2P(model="model.g2p.gz")
|
|
139
|
+
phones = g2p.pronounce("hello")
|
|
140
|
+
print(phones) # ['HH', 'AH', 'L', 'OW']
|
|
141
|
+
|
|
142
|
+
# n:m multigram (train via CLI or library)
|
|
143
|
+
mg = MultigramG2P(max_letter_span=2, max_phone_span=2)
|
|
144
|
+
mg.train_from_dict("lexicon.tsv")
|
|
145
|
+
# phonebox train-multigram … ; phonebox pronounce -m model.g2p.gz (sidecar auto-detect)
|
|
146
|
+
|
|
147
|
+
# N-best alternatives
|
|
148
|
+
for pron, score in g2p.pronounce_nbest("read", n=3):
|
|
149
|
+
print(f"{pron} ({score:.3f})")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Step-by-Step Training
|
|
153
|
+
|
|
154
|
+
For debugging or custom workflows:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# 1. Fetch dictionary
|
|
158
|
+
phonebox dict fetch cmudict
|
|
159
|
+
|
|
160
|
+
# 2. Align letters to phonemes
|
|
161
|
+
phonebox align data/cmudict/cmudict.dict -o alignments.txt --remove-stress
|
|
162
|
+
|
|
163
|
+
# 3. Vectorize alignments
|
|
164
|
+
phonebox vectorize alignments.txt -o vectors.txt
|
|
165
|
+
|
|
166
|
+
# 4. Train from vectors
|
|
167
|
+
phonebox model train en_US --vectors vectors.txt -o model.g2p.gz
|
|
168
|
+
|
|
169
|
+
# 5. Bundle
|
|
170
|
+
phonebox bundle model.g2p.gz -o g2p.py
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Standalone Deployment
|
|
174
|
+
|
|
175
|
+
Bundled files have zero dependencies beyond the Python standard library:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
phonebox bundle model.g2p.gz -o g2p.py
|
|
179
|
+
python g2p.py "test"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Algorithm
|
|
183
|
+
|
|
184
|
+
1. **EM Alignment**: Expectation-Maximization aligns letters to phonemes
|
|
185
|
+
2. **Feature Extraction**: 7-gram letter windows create feature vectors
|
|
186
|
+
3. **Decision Tree**: ID3-style tree trained on aligned data
|
|
187
|
+
4. **Prediction**: Tree traversal based on letter context
|
|
188
|
+
|
|
189
|
+
Based on research from CMU:
|
|
190
|
+
- [CMU G2P Research](http://www.cs.cmu.edu/afs/cs.cmu.edu/user/lenzo/html/areas/t2p/)
|
|
191
|
+
- [ICSLP 1998 Paper](https://www.isca-speech.org/archive/icslp_1998/i98_0561.html)
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT License - see LICENSE file.
|
|
196
|
+
|
|
197
|
+
## Author
|
|
198
|
+
|
|
199
|
+
Kevin Lenzo ([@lenzo-ka](https://github.com/lenzo-ka))
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
1. Fork the repository
|
|
204
|
+
2. Create a feature branch
|
|
205
|
+
3. Add tests for new functionality
|
|
206
|
+
4. Submit a pull request
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Examples of using phonebox as a Python library.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from phonebox import G2P, Dictionary
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def example_simple_usage():
|
|
10
|
+
"""Simplest way to use phonebox."""
|
|
11
|
+
print("=== Simple Usage ===")
|
|
12
|
+
|
|
13
|
+
# Load pre-trained model
|
|
14
|
+
g2p = G2P(model="models/en_US_nostress.g2p.gz")
|
|
15
|
+
|
|
16
|
+
# Pronounce a word
|
|
17
|
+
pronunciation = g2p.pronounce("hello")
|
|
18
|
+
print(f"hello: {pronunciation}")
|
|
19
|
+
|
|
20
|
+
# Or use as callable
|
|
21
|
+
print(f"world: {g2p('world')}")
|
|
22
|
+
|
|
23
|
+
# Batch pronunciation
|
|
24
|
+
words = ["python", "grapheme", "phoneme"]
|
|
25
|
+
for word, phones in g2p.pronounce_batch(words):
|
|
26
|
+
print(f"{word}: {' '.join(phones)}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def example_pocketsphinx_setup():
|
|
30
|
+
"""One-command PocketSphinx setup."""
|
|
31
|
+
print("\n=== PocketSphinx Setup ===")
|
|
32
|
+
|
|
33
|
+
# This fetches CMUdict, processes it, and trains a model
|
|
34
|
+
g2p = G2P.from_pocketsphinx()
|
|
35
|
+
|
|
36
|
+
# Use it
|
|
37
|
+
print(f"test: {g2p('test')}")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def example_dictionary_class():
|
|
41
|
+
"""Using the Dictionary class."""
|
|
42
|
+
print("\n=== Dictionary Class ===")
|
|
43
|
+
|
|
44
|
+
# Fetch a dictionary
|
|
45
|
+
dict = Dictionary.fetch("cmudict")
|
|
46
|
+
print(f"Dictionary: {dict}")
|
|
47
|
+
print(f"Size: {len(dict)} entries")
|
|
48
|
+
|
|
49
|
+
# Process it
|
|
50
|
+
processed = dict.process(remove_stress=True, output="processed.dict")
|
|
51
|
+
print(f"Processed: {processed}")
|
|
52
|
+
|
|
53
|
+
# Train a model
|
|
54
|
+
_ = processed.train_g2p_model(locale="en_US", output="my_model.g2p.gz")
|
|
55
|
+
print("Model trained")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def example_train_from_custom_dict():
|
|
59
|
+
"""Train from your own dictionary."""
|
|
60
|
+
print("\n=== Train from Custom Dictionary ===")
|
|
61
|
+
|
|
62
|
+
# Create a small test dictionary
|
|
63
|
+
test_dict = """
|
|
64
|
+
hello HH AH L OW
|
|
65
|
+
world W ER L D
|
|
66
|
+
python P AY TH AA N
|
|
67
|
+
""".strip()
|
|
68
|
+
|
|
69
|
+
with open("test_dict.txt", "w") as f:
|
|
70
|
+
f.write(test_dict)
|
|
71
|
+
|
|
72
|
+
# Train directly
|
|
73
|
+
g2p = G2P.train(dictionary="test_dict.txt", locale="en_US", remove_stress=False)
|
|
74
|
+
|
|
75
|
+
# Use it
|
|
76
|
+
print(f"hello: {g2p('hello')}")
|
|
77
|
+
|
|
78
|
+
# Clean up
|
|
79
|
+
import os
|
|
80
|
+
|
|
81
|
+
os.remove("test_dict.txt")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def example_method_chaining():
|
|
85
|
+
"""Fluent API with method chaining."""
|
|
86
|
+
print("\n=== Method Chaining ===")
|
|
87
|
+
|
|
88
|
+
# Fetch, process, and train in one line
|
|
89
|
+
_ = (
|
|
90
|
+
Dictionary.fetch("cmudict")
|
|
91
|
+
.process(remove_stress=True)
|
|
92
|
+
.train_g2p_model("en_US", output="model.g2p.gz")
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
print("Model trained via chaining")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def example_advanced_usage():
|
|
99
|
+
"""Advanced features."""
|
|
100
|
+
print("\n=== Advanced Usage ===")
|
|
101
|
+
|
|
102
|
+
# Direct DecisionTree access for full control
|
|
103
|
+
from phonebox import DecisionTree
|
|
104
|
+
|
|
105
|
+
dt = DecisionTree(
|
|
106
|
+
locale="en_US",
|
|
107
|
+
phoneset_name="cmu",
|
|
108
|
+
remove_stress=True,
|
|
109
|
+
max_iterations=50,
|
|
110
|
+
min_samples_split=5,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Load and train
|
|
114
|
+
with open("data/cmudict/cmudict_nostress.dict") as f:
|
|
115
|
+
dt.load_prondict(f)
|
|
116
|
+
dt.align()
|
|
117
|
+
dt.train()
|
|
118
|
+
|
|
119
|
+
# Use it
|
|
120
|
+
print(f"pronunciation: {dt.pronounce('pronunciation')}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
# Run only the simple example by default
|
|
125
|
+
# Uncomment others as needed
|
|
126
|
+
|
|
127
|
+
example_simple_usage()
|
|
128
|
+
|
|
129
|
+
# example_pocketsphinx_setup() # Requires internet
|
|
130
|
+
# example_dictionary_class() # Requires internet
|
|
131
|
+
# example_train_from_custom_dict()
|
|
132
|
+
# example_method_chaining() # Requires internet
|
|
133
|
+
# example_advanced_usage() # Requires data/
|