Semplifica T5 Temporal Normalizer is a fine-tuned version of Google's
ByT5-Small specifically designed to solve a complex NLP problem:
normalizing noisy, slang, relative, and incomplete temporal expressions into standard ISO formats (
YYYY-MM-DD or
HH:MM).
By operating at the character level (UTF-8 bytes), ByT5 is intrinsically immune to typos, dirty OCR outputs, and Out-Of-Vocabulary (OOV) tokens, making it exceptionally reliable for real-world, messy documents.
1from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
2
3model_id = "SemplificaAI/t5-temporal-normalizer"
4# Important: always load the tokenizer from the base model to avoid a known
5# ByT5 tokenizer serialization bug in transformers >= 5.x
6tokenizer = AutoTokenizer.from_pretrained("google/byt5-small")
7model = AutoModelForSeq2SeqLM.from_pretrained(model_id)
8
9# Format: YYYY-MM-DD | lang (optional) | text
10input_text = "2024-01-01 | en | 3 days post admission"
11inputs = tokenizer(input_text, return_tensors="pt")
12
13outputs = model.generate(**inputs, max_length=16)
14# Use skip_special_tokens=False + manual cleanup to avoid a deadlock bug
15# in transformers >= 5.x with skip_special_tokens=True
16result = tokenizer.decode(outputs[0], skip_special_tokens=False)
17result = result.replace("<pad>", "").replace("</s>", "").strip()
18
19print(result)
20# Output: 2024-01-04
1import onnxruntime as ort
2import numpy as np
3from transformers import AutoTokenizer
4
5tokenizer = AutoTokenizer.from_pretrained("google/byt5-small")
6opts = ort.SessionOptions()
7enc_sess = ort.InferenceSession("byt5_encoder_int8.onnx", sess_opts=opts, providers=["CPUExecutionProvider"])
8dec_sess = ort.InferenceSession("byt5_decoder_int8.onnx", sess_opts=opts, providers=["CPUExecutionProvider"])
9
10input_text = "2024-01-01 | en | 3 days post admission"
11enc = tokenizer(input_text, return_tensors="np", max_length=64, padding="max_length", truncation=True)
12
13# 1. Encoder forward pass
14enc_hs = enc_sess.run(None, {
15 "input_ids": enc["input_ids"],
16 "attention_mask": enc["attention_mask"],
17})[0]
18
19# 2. Autoregressive greedy decode loop
20MAX_OUT_LEN = 16
21PAD_ID = 0
22EOS_ID = 1
23
24cur_ids = np.zeros((1, MAX_OUT_LEN), dtype=np.int64)
25cur_mask = np.zeros((1, MAX_OUT_LEN), dtype=np.int64)
26cur_ids[0, 0] = PAD_ID
27cur_mask[0, 0] = 1
28
29generated = []
30
31for step in range(MAX_OUT_LEN - 1):
32 logits = dec_sess.run(None, {
33 "decoder_input_ids": cur_ids,
34 "decoder_attention_mask": cur_mask,
35 "encoder_hidden_states": enc_hs,
36 "encoder_attention_mask": enc["attention_mask"],
37 })[0]
38
39 next_tok = int(np.argmax(logits[0, step]))
40 if next_tok == EOS_ID:
41 break
42 generated.append(next_tok)
43
44 cur_ids[0, step + 1] = next_tok
45 cur_mask[0, step + 1] = 1
46
47output_text = bytes([t - 3 for t in generated if t >= 3]).decode("utf-8", errors="ignore")
48print("Prediction:", output_text)
1package main
2
3import (
4"fmt"
5ort "github.com/yalue/onnxruntime_go"
6)
7
8func main() {
9ort.SetSharedLibraryPath("libonnxruntime.so")
10ort.InitializeEnvironment()
11defer ort.DestroyEnvironment()
12
13// Load separated ONNX models
14encSess, _ := ort.NewAdvancedSession("byt5_encoder_fp32.onnx", /* ... */)
15decSess, _ := ort.NewAdvancedSession("byt5_decoder_fp32.onnx", /* ... */)
16
17// 1. Encoder pass
18_ = encSess.Run()
19
20// 2. Decoder autoregressive loop with fixed mask
21for step := 0; step < 15; step++ {
22_ = decSess.Run()
23// Get step logits, argmax, and update input buffer
24}
25}
For production environments, use the
ort crate. Since T5 is an encoder-decoder architecture, generation requires an autoregressive loop.
1# Cargo.toml
2[dependencies]
3ort = "2.0"
1use ort::{GraphOptimizationLevel, Session};
2
3fn main() -> ort::Result<()> {
4 let session = Session::builder()?
5 .with_optimization_level(GraphOptimizationLevel::Level3)?
6 .with_intra_threads(4)?
7 .commit_from_file("byt5_encoder_fp32.onnx")?;
8
9 // ByT5 tokenization: each UTF-8 byte maps to token_id = byte + 3
10 // (0=pad, 1=eos, 2=unk, then 3..258 = bytes 0..255)
11 // Load both encoder and decoder sessions, then run autoregressive loop with fixed size padding
12
13 Ok(())
14}