Eric Le

case study

diction

A pronunciation trainer that scores each sound against what a native speaker actually sounds like, and reports how often it is wrong.

Local and offline. Nothing leaves the machine it runs on.

problem

The drill told native speakers they were wrong. It played a word, listened to the reading, and flagged the sound when the acoustic model's confidence fell below -5.0. One number, applied to every sound in English.

On real speech it failed in both directions at once. A native th came back flagged. A swapped vowel passed, so ship read for sheep and wok for walk cleared the bar. A single cutoff cannot tell a good sound from a bad one, and the reason is the whole finding.

data

speechocean762 supplies 5,000 English clips from non-native speakers, each phoneme scored 0 to 2 by five human experts. That per-sound score is the ground truth a threshold can be fit against, and the corpus is Apache-2.0, so it ships.

The acoustic model ran over 2,500 of those clips to read off its confidence in each sound, a measure called Goodness of Pronunciation. Transcription was skipped: the words being read are known in advance, so only the acoustic model has to run, which cut an hours-long sweep to minutes.

Mean confidence per phoneme for clean and clearly-wrong readings, with the old global threshold marked far to the left of nearly every sound
every sound sits somewhere different

A native s sits near perfect confidence. A native th averages -2.6, because the model is less certain about it even when a native speaker says it correctly. The clean and wrong readings separate for every sound, and each pair sits at its own place on the axis.

The old -5.0 line cuts none of it cleanly. It falls so far left that a mangled th still scores above it and passes, while it clips the low end of confident vowels and flags them. Different sounds do not share a baseline, so one vertical line was never going to work.

fix

The rule became per-sound. For each phoneme, take the mean and spread of its native confidence, then flag a reading only where it falls more than 1.6 standard deviations below that sound's own mean. The cut travels with the sound.

Separation measured as AUC for all 27 sounds, every bar clearing the 0.75 reliability gate
separation per sound, against the reliability gate

All 27 sounds separate, from 0.83 to 0.98. A reliability gate backs the rule up: a sound whose confidence failed to separate clean from wrong would be held out of the verdict rather than guessed at. Every sound clears it today, and the gate is there for the next model.

Confidence distributions for long-e, th and w, each calibrated threshold falling between the clean and the clearly-wrong readings
calibrated thresholds against the readings they separate

One earlier read had written the long-e vowel off as hopeless, its clean and wrong clouds apparently on top of each other. That came from comparing clean speech against borderline speech. Set against clearly-wrong readings, long-e separates at 0.98, as cleanly as any consonant. What a measurement is compared against decides what it concludes.

holdout

A rule fit to its own data proves nothing, so the thresholds were fit on one half of the corpus and measured on the other half, held out from start to finish.

On unseen speech it false-flags native readings 7% of the time and catches 66% of genuinely wrong sounds. That operating point is deliberate. Nagging a correct speaker erodes trust faster than missing an error does, so the cut keeps false alarms low and accepts that a third of subtle errors slip past.

It also fixes the two failures that started this. A native th sits at its own baseline and passes. A substituted vowel drops below its own line and flags.

beyond

Calibrating one score set the pattern for every other score the tool presents.

score what it was what it is now
Minimal-pair verdict The target sound's own native band The competing sound scored at the target's aligned frames
Fluency 100 * (1 - pause ratio) Four timing features fit against human fluency labels
Interview gaze A 15-degree head-turn gate A 7-degree gate, sitting in the measured gap between takes
Verdict errors before and after the competitor check, false passes dropping from 15 to 2 while false retries rise from 5 to 6
minimal-pair verdict errors, before and after

The minimal-pair drill asks a different question from the one the phoneme flag was fit for: whether a reading was θ rather than f, not whether it was an acceptable θ. Scoring the competitor at the target's own frames cut wrong words accepted from 15 to 2 across 32 pairs, and moved right words rejected from 5 to 6.

Predicted against human fluency on the held-out split, beside the long-pause-ratio distribution that collapses to zero on a read-aloud corpus
fluency against human judgment, and the feature that cannot be fit

Fluency correlates with human judgment at 0.39 on held-out clips, matching its in-sample fit. That is real and modest, so the score reads as directional rather than as a grade. Two of its four features carry the result and the two pause features cannot be fit at all here, because a read-aloud corpus almost never hesitates. Those two stay reasoned rather than fitted, and the whole score is labeled for what it is.

Per-frame yaw for a lens-look against a screen-glance, beside the threshold sweep where separation falls to zero at the old 15-degree gate and reaches 51 points at the new 7-degree one
gaze yaw per frame, and the gate sweep behind the new threshold

The interview gaze metric first read a lens-look and a screen-glance as identical, since both keep the head inside 15 degrees. The raw yaw separates them cleanly, at a median of +1.2 degrees against -7.3. Tightening the gate to 7 degrees recovers a 51-point separation. Two clips from one speaker is a separation check rather than a fit, and the threshold is a directional placeholder until more recordings arrive.

The flag is trustworthy now. The composite accent and prosody scores are still labeled directional, and they stay that way until real recorded pairs validate them. Calibrating a score presented as truth is the hard part, and that part is done.

The whole experiment lives beside the case study in the repository. measure.py runs the sweep, analyze.py fits the thresholds, validate.py checks them on held-out speech, and plots.py draws these figures. The models and the dataset cache on first download, so a re-run takes minutes.