An Inverse Text Normalization (ITN) transformer model fine-tuned to convert unformatted, raw Latin Automatic Speech Recognition (ASR) outputs into fully formatted, classical Latin text. It simultaneously restores capitalization and trailing punctuation using a 14-class composite sequence labeling schema.
This model is intended to be used directly downstream of the acoustic model
njand/wav2vec2-xls-r-latin.
Because raw ASR models emit stream-of-consciousness text (lowercased, space-separated, and unpunctuated), the text must pass through an input normalization pipeline before being fed into this model for casing and punctuation restoration.
1+-----------------------+ +-------------------------------+ +-------------------------------+
2| Raw Audio Waveform | --> | njand/wav2vec2-xls-r-latin | --> | Preprocessing & Normalization |
3+-----------------------+ +-------------------------------+ +-------------------------------+
4 |
5 v
6+-----------------------+ +-------------------------------+ +-------------------------------+
7| Formatted Text Output | <-- | Latin ASR Post-Processor | <-- | Custom CLTK Tokenization |
8+-----------------------+ +-------------------------------+ +-------------------------------+
To prepare raw transcript outputs for inference, apply the following sequence of transformations:
Below is a complete Python script demonstrating how to prepare raw ASR output and run inference using the post-processing pipeline.
1from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
2
3PUNCT_MAP = {
4 "NONE": "",
5 "COMMA": ",",
6 "PERIOD": ".",
7 "SEMICOLON": ";",
8 "COLON": ":",
9 "QUESTION": "?",
10 "EXCLAMATION": "!",
11}
12
13def format_token(word: str, tag: str) -> str:
14 """Applies composite ITN tag (e.g., 'TITLE_COMMA') to a word token."""
15 parts = tag.split("_")
16 if len(parts) != 2:
17 return word
18
19 casing, punct = parts[0], parts[1]
20
21 if casing == "TITLE":
22 word = word.capitalize()
23 elif casing == "LOWER":
24 word = word.lower()
25
26 return f"{word}{PUNCT_MAP.get(punct, '')}"
27
28def restore_latin_text(pipe, raw_text: str) -> str:
29 """Runs inference and reconstructs formatted Latin text."""
30 predictions = pipe(raw_text, aggregation_strategy="first")
31 formatted_words = [
32 format_token(pred["word"].strip(" "), pred["entity_group"])
33 for pred in predictions
34 ]
35 return " ".join(formatted_words)
36
37# 1. Load pipeline
38model_id = "njand/latin-asr-postprocessor"
39tokenizer = AutoTokenizer.from_pretrained(model_id)
40model = AutoModelForTokenClassification.from_pretrained(model_id)
41
42itn_pipe = pipeline("token-classification", model=model, tokenizer=tokenizer)
43
44# 2. Test reconstruction with preprocessed ASR output
45raw_asr_input = "gallia est omnis divisa in partes tres quarum unam incolunt belgae"
46print(restore_latin_text(itn_pipe, raw_asr_input))
47# Output: "Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae."
48
To facilitate production deployment on CPU-based infrastructure, this repository provides the model in three formats:
Evaluated on a 95/5 train/holdout split across diverse Classical Latin literary and historical corpora.
The model was trained over 9 epochs fine-tuning
latincy/latin-bert. Model weights from
Epoch 7 were selected based on optimal overall F1.