Token Classification
Transformers
ONNX
Safetensors
Transformers.js
modernbert
punctuation
punctuation-restoration
truecasing
capitalization
asr
speech-recognition
transcription
mmbert
Instructions to use Horizon-Labs/multilingual-punctuation-truecase-base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Horizon-Labs/multilingual-punctuation-truecase-base with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="Horizon-Labs/multilingual-punctuation-truecase-base")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("Horizon-Labs/multilingual-punctuation-truecase-base") model = AutoModelForTokenClassification.from_pretrained("Horizon-Labs/multilingual-punctuation-truecase-base", device_map="auto") - Transformers.js
How to use Horizon-Labs/multilingual-punctuation-truecase-base with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('token-classification', 'Horizon-Labs/multilingual-punctuation-truecase-base'); - Notebooks
- Google Colab
- Kaggle
File size: 6,646 Bytes
b11a6e7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | """Train a joint punctuation + truecasing token classifier (truecase/common_tc.py labels '<mark>|<case>', 18 classes) on the punctuation
training windows (punct/build_data.py + spoken data). Inputs are ALWAYS lowercased; the case label of each word comes from the original
text. Each word's label sits on its last token. Checkpoint selection: mean of punctuation F1 and casing F1 (Cap/UPPER) on validation.
python truecase/train_tc.py --base jhu-clsp/mmBERT-small --data DATA --out DIR"""
import argparse, json, os, random, sys, time
import numpy as np, pandas as pd, torch
from transformers import AutoModelForTokenClassification, AutoTokenizer, get_cosine_schedule_with_warmup
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "truecase"))
from common_tc import LABELS, L2I, CASES, unit_cases, lower_keep_len
from common import NOSPACE
ap = argparse.ArgumentParser()
ap.add_argument("--base", default="jhu-clsp/mmBERT-small")
ap.add_argument("--data", required=True)
ap.add_argument("--out", required=True)
ap.add_argument("--max_len", type=int, default=512)
ap.add_argument("--tokens_per_batch", type=int, default=65536)
ap.add_argument("--lr", type=float, default=5e-5)
ap.add_argument("--epochs", type=float, default=1)
ap.add_argument("--warmup", type=float, default=0.05)
ap.add_argument("--max_train", type=int, default=0)
ap.add_argument("--max_val", type=int, default=6000)
ap.add_argument("--evals", type=int, default=8, help="validation checks over the run")
ap.add_argument("--seed", type=int, default=0)
args = ap.parse_args()
random.seed(args.seed); np.random.seed(args.seed); torch.manual_seed(args.seed)
os.makedirs(args.out, exist_ok=True)
tr = pd.read_parquet(f"{args.data}/train.parquet")
va = pd.read_parquet(f"{args.data}/val.parquet").sample(frac=1.0, random_state=0).head(args.max_val)
if args.max_train:
tr = tr.sample(min(len(tr), args.max_train), random_state=args.seed)
tok = AutoTokenizer.from_pretrained(args.base)
model = AutoModelForTokenClassification.from_pretrained(args.base, num_labels=len(LABELS), id2label=dict(enumerate(LABELS)),
label2id=L2I).cuda()
def encode(df):
out = []
for s0 in range(0, len(df), 20000):
part = df.iloc[s0:s0 + 20000]
texts = [lower_keep_len(t) for t in part.text]
enc = tok(texts, truncation=True, max_length=args.max_len, return_offsets_mapping=True)
for ids, offs, text, ends, labs, lang in zip(enc["input_ids"], enc["offset_mapping"], part.text, part.ends, part.labs, part.lang):
ends, labs = ends.tolist(), labs.tolist(); cs = unit_cases(text, ends, lang in NOSPACE)
at = {e: l * len(CASES) + c for e, l, c in zip(ends, labs, cs)}
out.append((ids, [at[e] if e > b and e in at else -100 for b, e in offs]))
return out
t0 = time.time()
rng = np.random.RandomState(args.seed)
tr_enc = encode(tr)
va_l = encode(va)
print(f"train {len(tr_enc)} val {len(va_l)} encoded in {time.time() - t0:.0f}s", flush=True)
def batches(data, shuffle):
idx = list(range(len(data)))
if shuffle:
random.shuffle(idx)
out = []
for s in range(0, len(idx), 8192):
part = sorted(idx[s:s + 8192], key=lambda i: len(data[i][0])); cur, mx = [], 0
for i in part:
L = len(data[i][0])
if cur and max(mx, L) * (len(cur) + 1) > args.tokens_per_batch:
out.append(cur); cur, mx = [], 0
cur.append(i); mx = max(mx, L)
if cur:
out.append(cur)
if shuffle:
random.shuffle(out)
return out
def collate(data, b):
L = max(len(data[i][0]) for i in b)
ids = torch.full((len(b), L), tok.pad_token_id, dtype=torch.long); att = torch.zeros((len(b), L), dtype=torch.long)
lab = torch.full((len(b), L), -100, dtype=torch.long)
for j, i in enumerate(b):
n = len(data[i][0]); ids[j, :n] = torch.tensor(data[i][0]); att[j, :n] = 1; lab[j, :n] = torch.tensor(data[i][1])
return ids.cuda(), att.cuda(), lab.cuda()
@torch.no_grad()
def evaluate(data):
model.eval(); conf = np.zeros((len(LABELS), len(LABELS)), np.int64)
for b in batches(data, False):
ids, att, lab = collate(data, b)
with torch.autocast("cuda", dtype=torch.bfloat16):
pr = model(input_ids=ids, attention_mask=att).logits.argmax(-1)
m = lab >= 0
np.add.at(conf, (lab[m].cpu().numpy(), pr[m].cpu().numpy()), 1)
model.train()
P = conf.reshape(6, 3, 6, 3).sum(axis=(1, 3)); C = conf.reshape(6, 3, 6, 3).sum(axis=(0, 2))
tp = sum(P[i, i] for i in range(1, 6)); fp = P[:, 1:].sum() - tp; fn = P[1:, :].sum() - tp
ctp = C[1, 1] + C[2, 2]; cfp = C[:, 1:].sum() - ctp; cfn = C[1:, :].sum() - ctp
return dict(punct_f1=round(2 * tp / max(1, 2 * tp + fp + fn), 4), case_f1=round(2 * ctp / max(1, 2 * ctp + cfp + cfn), 4),
case_acc=round(float(np.trace(C) / max(1, C.sum())), 4))
nb = len(batches(tr_enc, True)); total = int(nb * args.epochs)
opt = torch.optim.AdamW(model.parameters(), lr=args.lr, betas=(0.9, 0.98), eps=1e-6, weight_decay=0.01, fused=True)
sch = get_cosine_schedule_with_warmup(opt, int(args.warmup * total), total)
every = max(1, total // args.evals)
print(f"steps/epoch {nb} total {total} eval every {every}", flush=True)
step, best, log, t0 = 0, -1, [], time.time()
model.train()
while step < total:
for b in batches(tr_enc, True):
ids, att, lab = collate(tr_enc, b)
with torch.autocast("cuda", dtype=torch.bfloat16):
loss = torch.nn.functional.cross_entropy(model(input_ids=ids, attention_mask=att).logits.float().view(-1, len(LABELS)),
lab.view(-1), ignore_index=-100)
loss.backward(); torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0); opt.step(); sch.step(); opt.zero_grad(set_to_none=True)
step += 1
if step % 200 == 0:
print(f"step {step}/{total} loss {loss.item():.4f} {time.time() - t0:.0f}s", flush=True)
if step % every == 0 or step == total:
l = evaluate(va_l); score = (l["punct_f1"] + l["case_f1"]) / 2
log.append(dict(step=step, lower=l, score=score)); print(json.dumps(log[-1]), flush=True)
if score > best:
best = score; model.save_pretrained(args.out); tok.save_pretrained(args.out)
if step >= total:
break
json.dump(dict(args=vars(args), log=log, best=best), open(f"{args.out}/train_log.json", "w"), indent=1)
print("best", best, flush=True)
|