Views
No views yet
⚠️ See Disclaimer below before using.
(query, passage) pair, it outputs a single relevance score for that pair. Unlike the
other rerankers in this collection, it's a seq2seq model, not a classification head, so it can't
run through ONNXEmbeddings -- it's deployed through Teradata's ONNXSeq2Seq instead, using a
small graph modification described in "How it works" below.relevance_token_id, a token id that decodes to <bucket_NNN> -- parse NNN and
divide by 999 for a [0, 1] relevance score (see Quickstart)1import getpass
2import teradataml as tdml
3from huggingface_hub import hf_hub_download
4
5repo_id = "martinhillebrandtd/monot5-small-msmarco-10k"
6model_id = "monot5-small-msmarco-10k"
7
8# 1. Download artifacts from this repo
9hf_hub_download(repo_id=repo_id, filename="onnx/model.onnx", local_dir="./")
10hf_hub_download(repo_id=repo_id, filename="tokenizer.json", local_dir="./")
11
12# 2. Connect to Teradata
13tdml.create_context(host=input("host: "), username=input("user: "), password=getpass.getpass("password: "))
14
15# 3. Load model + tokenizer into BYOM tables
16tdml.save_byom(model_id=model_id, model_file="onnx/model.onnx", table_name="seq2seq_models")
17tdml.save_byom(model_id=model_id, model_file="tokenizer.json", table_name="seq2seq_tokenizers")
18
19# 4. Score an existing chunks table against one fixed query. No Const_* generation parameters
20# are needed: this graph has no BeamSearch op and no extra inputs beyond
21# input_ids/attention_mask. The bucket number is parsed out of relevance_token_id in SQL,
22# with REGEXP_SUBSTR -- no client-side Python post-processing.
23chunks_table = "your_chunks_table" # columns: chunk_id, chunk_txt
24QUERY = "What causes rain?"
25
26query = f"""
27SELECT
28 chunk_id,
29 chunk_txt,
30 relevance_token_id,
31 CAST(REGEXP_SUBSTR(relevance_token_id, '[0-9]+') AS INTEGER) / 999.0 AS relevance_score
32FROM mldb.ONNXSeq2Seq(
33 ON (SELECT chunk_id, chunk_txt, 'Query: {QUERY} Document: ' || chunk_txt || ' Relevant:' AS txt
34 FROM {chunks_table}) AS InputTable
35 ON (SELECT model_id, model FROM seq2seq_models
36 WHERE model_id = '{model_id}') AS ModelTable DIMENSION
37 ON (SELECT model AS tokenizer FROM seq2seq_tokenizers
38 WHERE model_id = '{model_id}') AS TokenizerTable DIMENSION
39 USING
40 Accumulate('chunk_id', 'chunk_txt')
41 ModelOutputTensor('relevance_token_id')
42 SkipSpecialTokens('false')
43 OverwriteCachedModel('true')
44) AS t
45"""
46result = tdml.DataFrame.from_query(query).to_pandas().sort_values("relevance_score", ascending=False)QUERY = "What causes rain?" against three sample chunks:| chunk_id | chunk_txt | relevance_token_id | relevance_score |
|---|---|---|---|
| 0 | Rain forms when water vapor in clouds condenses into droplets heavy enough to fall. | <bucket_306> | 0.306 |
| 1 | Paris is the capital and most populous city of France. | <bucket_000> | 0.0 |
| 2 | Airplanes generate lift through the shape and angle of their wings. | <bucket_000> | 0.0 |
VectorDistance to get a shortlist of candidates by cosine distance, and rerank only that
shortlist with this model.1-- Stage 1: shortlist the 20 nearest chunks to the query embedding
2CREATE VOLATILE TABLE shortlist AS (
3 SELECT ref_id AS chunk_id, distance
4 FROM TD_VECTORDISTANCE(
5 ON query_embedding AS TargetTable
6 ON chunk_embeddings AS ReferenceTable DIMENSION
7 USING
8 TargetIDColumn('query_id')
9 TargetFeatureColumns('[emb_0:emb_383]')
10 RefIDColumn('chunk_id')
11 RefFeatureColumns('[emb_0:emb_383]')
12 DistanceMeasure('cosine')
13 TopK(20)
14 ) AS dt
15) WITH DATA ON COMMIT PRESERVE ROWS;
16
17-- Stage 2: rerank only those 20 shortlisted chunks (see Quickstart above, with
18-- your_chunks_table replaced by a join against `shortlist`)ONNXSeq2Seq is built around com.microsoft.BeamSearch and always returns
tokenizer-decoded text, never a raw number -- it's designed to reuse a text-generation model as
a generic "text in, next-token-as-text out" engine. The original model reads relevance as the
softmax probability of the token "true" at the first decode step -- a continuous score, not
free text. Run unmodified, it would only ever return the literal string "true" or "false",
discarding exactly the information a reranker needs.<bucket_000>...<bucket_999>, are added to the tokenizer
-- 3-decimal-digit precision, i.e. quantization error ≤ 0.0005. They're only ever used as an
output lookup, never fed through the model's own embedding layer or lm_head, so no model
embedding resizing is needed.softmax
over just the true/false logits to get the real probability, quantizes it to a bucket
index, and outputs that bucket token's id directly, as an int64 tensor named
relevance_token_id. The exported graph has zero BeamSearch nodes -- just a plain
feedforward computation.ONNXSeq2Seq's ModelOutputTensor argument validates against the model's actual declared
output tensors, not a hardcoded whitelist -- so relevance_token_id is accepted, and its
value is looked up in the tokenizer's vocabulary and returned as "<bucket_NNN>".onnxruntime) and against a live Vantage instance, versus the
true PyTorch score (T5ForConditionalGeneration, real softmax over the true/false logits):| mean |delta| | max |delta| | |
|---|---|---|
| Local ONNX (bucket-decoded) vs. true PyTorch | 0.00021 | 0.00041 |
Live Teradata ONNXSeq2Seq vs. true PyTorch | 0.00021 | 0.00041 |
convert.py is fully self-contained: no third-party conversion package is used. It (1) adds the
1000 bucket tokens to the tokenizer, (2) wraps T5ForConditionalGeneration's real forward pass
in a small torch.nn.Module that computes the softmax score and looks up the matching bucket
token id, and (3) exports via plain torch.onnx.export -- no merged/BeamSearch export machinery
needed. See convert.py for the full implementation, and
test_local.py for the ONNX-vs-PyTorch parity check above. The same technique
generalizes to other seq2seq "generative" rerankers that score by reading a token probability
rather than a classification logit -- swap in the target model's own true/false (or equivalent)
token ids and prompt template.