Skip to content

Measuring Quality with Metrics

This section shows how to evaluate NLP model output using grapheme-aware metrics — the core reason grapheme-kit exists.

Why Standard Metrics Fall Short

Character-level metrics assume that one code point is one visually-perceived character. For English, that's true, so nothing here changes the numbers you already get. For scripts like Tamil and Sinhala, where several code points can render as a single visual grapheme, that assumption breaks down.

Consider the Tamil word for "Shri" written as ஸ்ரீ. A standard code-point CER calculation treats it as 4 separate characters. If a model produces the slightly wrong form ஸ்ரி, a standard metric counts only 1 error out of 4 characters (25% error rate). But visually, the word is entirely wrong — it should be 1 error out of 1 grapheme (100% error rate).

grapheme-kit corrects this by evaluating all metrics at the grapheme boundary — for any language you throw at it. This is not a replacement for chrF, chrF++, or CER; it's the same metrics, computed on the unit a reader actually perceives. Where that unit already matches a code point, as it does in English or German, the score doesn't change at all.

Character Error Rate (CER)

from grapheme_kit.metric import CER

# English: code point and grapheme are the same thing here, so this
# matches a standard character-level CER exactly.
print(CER("anwer", "answer"))
# Output: 0.16666666666666666

# Sinhala: one dropped vowel sign changes the whole visual unit.
print(CER("කනවා", "කනව"))
# Output: 0.3333333333333333

GraphemeCHRF (chrF and chrF++)

from grapheme_kit.metric import GraphemeCHRF

metric = GraphemeCHRF()

# German -- a small word-order slip
print(metric.corpus_score(["Das Wetter ist heute sehr schoen"], [["Heute ist das Wetter sehr schoen"]]).score)
# Output: 62.561731210281934

# Tamil -- partial match
print(metric.corpus_score(["நல்ல"], [["நல்ல மாணவன்"]]).score)
# Output: 37.10506980161646

CharBLEU

from grapheme_kit.metric import charbleu

# note argument order: (reference, hypothesis)
print(charbleu("Good morning everyone", "Good morning everybody"))
# Output: 0.8151678595510183

Next: Segmenting Text