This is a small side project where I play with a classic HMM + Viterbi decoder to label phoneme / jamo sequences with syllable roles:
- English: Onset / Nucleus / Coda (ONC)
- Korean: 초성 / 중성 / 종성 (mapped to O / N / C)
The goal is not to build a production syllabifier, but to have a readable, linguistics-oriented demo that connects:
- very simple phonotactic assumptions,
- a tiny HMM implementation, and
- visualisations of the Viterbi lattice.
- Hand-written HMM with three states:
O(Onset),N(Nucleus),C(Coda) - Emission preferences:
O/Cprefer consonantsNprefers vowels
- Transition preferences:
- syllables tend to follow
O → N → C C → Oopens up a new syllable- some onset/coda clusters are allowed
- syllables tend to follow
Typical usage:
from src.english.syllabifier_en import build_english_onc_hmm, syllabify_phones, STATES
from src.common.plotting import plot_viterbi_heatmap
phones = ["S", "T", "R", "AE", "N", "D"] # e.g. "strand"
hmm = build_english_onc_hmm(phoneme_inventory=phones)
best_path, dp = hmm.viterbi(phones)
print("Phones:", phones)
print("ONC states:", best_path)
plot_viterbi_heatmap(
dp,
states=STATES,
obs_seq=phones,
best_path=best_path,
title="English ONC decoding",
)This prints an ONC sequence such as:
Phones: ['S', 'T', 'R', 'AE', 'N', 'D']
ONC states: ['O', 'O', 'O', 'N', 'C', 'C']
and shows a heatmap of the Viterbi DP table with the best path overlaid.
You can also plug in a real lexicon:
src/english/cmudict_loader.pycontains a small helper to read a CMUdict-style pronunciation dictionary.- For a given word:
- look up its phoneme sequence,
- run it through
syllabify_phones, - inspect the ONC pattern and the lattice.
Korean is a nice contrast case because syllable structure is visible in the writing system.
src/korean/hangul_utils.pyimplements Unicode-based decomposition of each Hangul syllable into 초성/중성/종성.src/korean/syllabifier_ko.pydefines a very small HMM over jamo:OandCprefer consonant-type jamo,Nprefers vowel-type jamo,- transitions favour
(C)V(C)patterns.
Example:
from src.korean.syllabifier_ko import build_korean_hmm, STATES
from src.korean.hangul_utils import decompose_word
from src.common.plotting import plot_viterbi_heatmap
word = "한국말"
jamo_seq = decompose_word(word)
hmm = build_korean_hmm(jamo_seq)
best_path, dp = hmm.viterbi(jamo_seq)
print("Word:", word)
print("Jamo:", jamo_seq)
print("States:", best_path)
plot_viterbi_heatmap(
dp,
states=STATES,
obs_seq=jamo_seq,
best_path=best_path,
title="Korean ONC decoding",
)Again, this prints the jamo sequence and a corresponding sequence of O/N/C labels, and visualises the Viterbi lattice.
viterbi-syllabifier/
├── README.md
├── requirements.txt
├── src/
│ ├── common/
│ │ ├── viterbi.py # generic ViterbiHMM implementation
│ │ └── plotting.py # heatmap visualisation
│ ├── english/
│ │ ├── phonology_en.py # tiny consonant/vowel inventory helpers
│ │ ├── syllabifier_en.py # English ONC HMM
│ │ └── cmudict_loader.py # CMUdict-style lexicon loader
│ └── korean/
│ ├── hangul_utils.py # Unicode Hangul decomposition (초/중/종)
│ └── syllabifier_ko.py # Korean ONC HMM over jamo
└── examples/
├── demo_english.py
└── demo_korean.py
src/common/viterbi.pyis a small, language-independent HMM + Viterbi decoder.- Language-specific files just define:
- the state set,
- transition log-probabilities, and
- emission log-probabilities.
git clone https://github.com/<your-username>/viterbi-syllabifier.git
cd viterbi-syllabifier
python3 -m venv viterbi
source viterbi/bin/activate # Windows: viterbi\Scripts\activate
pip install -r requirements.txtMinimal requirements.txt:
matplotlib
numpy
(Jupyter or VS Code are convenient but not required.)
From the project root:
# English demo
python examples/demo_english.py
# Korean demo
python examples/demo_korean.pyEach script prints the decoded state sequence and pops up a matplotlib window with a lattice heatmap.
This project is deliberately small:
- It uses hand-crafted transition and emission scores, based on simple phonological intuitions:
- onsets and codas like consonants,
- nuclei like vowels,
- syllables roughly follow O → N → C.
- It is not tuned on real labelled syllabification data.
- The output should be interpreted as:
- “what this particular toy HMM thinks is the most plausible ONC pattern”
rather than - “ground-truth syllabification”.
- “what this particular toy HMM thinks is the most plausible ONC pattern”
There is plenty of room to extend it, for example:
- refine the state inventory (e.g. split
OintoO1,O2, or treat /s/-clusters separately), - estimate probabilities from an ONC-annotated corpus,
- compare rule-based and probabilistic syllabifiers on the same lexicon,
- treat the Viterbi path score as a simple phonotactic “well-formedness” score.
This repository contains my own code, but it relies on some standard resources and ideas:
-
HMM + Viterbi
The model and algorithm are standard textbook material in speech processing / NLP.
The implementation here is a minimal version written for this project. -
CMU Pronouncing Dictionary (optional data)
If you add the CMUdict file underdata/english/, please make sure to:- download it from the official CMUdict distribution, and
- keep its original licence and copyright notice alongside the file.
The helpercmudict_loader.pyis written to work with the plain-text format.
-
Hangul decomposition
The Hangul decomposition logic follows the algorithm described in the Unicode Standard (precomposed syllables mapped to (choseong, jungseong, jongseong) indices via simple arithmetic). -
Python ecosystem
This project uses standard libraries such asnumpyandmatplotlib.
Their licences are provided by the respective projects.
You are free to reuse or modify the code in this repository under the terms of the licence below.
MIT License (or another licence you prefer — update this section if you change it).