Views
No views yet
overall output.audio (16 kHz mono) → WavLM-Large → audio_frames (B, T_a, 1024)
expected phone IDs (≤16 phones) → phone_emb (50 → 256 → 1024) → ph_q
ph_q × audio_frames (cross-attention) → per-phone repr (B, T_p, 1024)
per-phone repr → per-phone score head (regression, 0..100)
per-phone repr + audio attention pool → word score head (regression, 0..100)L = 1.0·L_word + 0.8·L_phone.| Metric | V9 baseline | V13c2 |
|---|---|---|
| MAE (lower better) | 37.98 | 12.63 |
| Pearson corr | 0.665 | 0.819 |
| P(|err|<5) | 6.1% | 29.2% |
| P(|err|<10) | — | 51.6% |
| GT band | n | MAE | P<5 |
|---|---|---|---|
| 0–20 | 19,879 | 15.74 | 15.7% |
| 20–40 | 20,095 | 12.41 | 32.4% |
| 40–60 | 19,724 | 13.23 | 21.9% |
| 60–80 | 19,926 | 11.58 | 30.2% |
| 80–100 | 20,233 | 10.25 | 45.2% |
wavlm_word_v13c2.pt — model checkpoint (state_dict + phone_vocab + training args + metrics)train_v13_multitask.py — training script with model class definition1import torch
2import sys, os
3# Make sure train_v13_multitask.py is on the path so the model class can be imported
4sys.path.insert(0, "/path/to/checkpoint/directory")
5from train_v13_multitask import V13Model
6
7ckpt = torch.load("wavlm_word_v13c2.pt", map_location="cpu", weights_only=False)
8phone_vocab = ckpt["phone_vocab"]
9phone_to_id = {p: i for i, p in enumerate(phone_vocab)}
10
11model = V13Model(n_phones=len(phone_vocab), unfreeze_top_n=12).eval().cuda()
12model.load_state_dict(ckpt["model_state"])
13
14# Prepare a single example:
15import soundfile as sf, numpy as np, torch.nn.functional as F
16wav, sr = sf.read("user_word.mp3", dtype="float32")
17if wav.ndim > 1: wav = wav.mean(axis=1)
18if sr != 16000:
19 idxs = np.linspace(0, len(wav)-1, int(len(wav)*16000/sr)).astype(np.int64)
20 wav = wav[idxs]
21wav = wav[:40000] # truncate to 2.5s
22wav = np.pad(wav, 4800, mode="constant") # 0.3s silence each side
23wav = (wav - wav.mean()) / (wav.std() + 1e-7)
24mask = np.ones_like(wav, dtype=np.float32)
25
26# Expected phones for "street" (use your phone tokenizer; vocab listed in ckpt['phone_vocab'])
27phones = ["s", "tr", "iy", "t"]
28phone_ids = [phone_to_id.get(p, 1) for p in phones]
29# Pad to MAX_PHONES=16
30phone_ids += [0] * (16 - len(phone_ids))
31ph_mask = [1.0] * len(phones) + [0.0] * (16 - len(phones))
32
33with torch.no_grad():
34 word_pred, phone_pred = model(
35 torch.FloatTensor(wav).unsqueeze(0).cuda(),
36 torch.FloatTensor(mask).unsqueeze(0).cuda(),
37 torch.LongTensor([phone_ids]).cuda(),
38 torch.FloatTensor([ph_mask]).cuda(),
39 )
40print(f"word score: {float(word_pred):.1f}")
41print(f"per-phone scores: {phone_pred[0][:len(phones)].tolist()}")<pad>, <unk>); full list is ckpt['phone_vocab'].overall 0–100, per-phone scores)1@misc{she2026wavlmwordv13c2,
2 title = {WavLM Word V13c2: WavLM-Large + phone cross-attention for English pronunciation scoring},
3 author = {She, Jianshu},
4 year = {2026},
5 url = {https://huggingface.co/Jianshu001/wavlm-word-v13c2},
6}