1text_a =f"{bm25_score} [SEP] {query}"# score BEFORE the query2text_b = passage
3# tokenises to: [CLS] score [SEP] query [SEP] passage [SEP]
⚠️ This ordering differs from the paper's Eq. 3, which places the score between query and passage.
This model follows the authors' released training code, which puts it before the query. A cross-encoder
fed a layout it never saw in training returns confident nonsense rather than an error, so the ordering
matters.
The score must be normalised the same way
bm25_score = int(((raw_bm25 - 0) / (50 - 0)) * 100) # Min-Max, GLOBAL setting, then x100 as an integer
Min-Max normalisation in the global setting — fixed constants min=0, max=50, not per-query
statistics — then scaled by 100 and truncated to an integer, per the paper's §3.3. Raw or per-query
normalised scores put the feature on a distribution the model has never seen.
Training scores came from pyserini / Lucene BM25Similarity(k1=0.82, b=0.68) (the Anserini-tuned MS
MARCO settings). Other BM25 implementations — Terrier's defaults are k1=1.2, b=0.75 — produce a
different score distribution, which shifts the injected integers.
Usage
python
1from sentence_transformers import CrossEncoder
23model = CrossEncoder("Amdestya/bm25cat-minilm-l12", max_length=256)45defnorm(raw):6returnint((raw /50)*100)78query ="what is a cat"9passage ="A cat is a small domesticated carnivorous mammal."10raw_bm25 =36.51112score = model.predict([[f"{norm(raw_bm25)} [SEP] {query}", passage]])
Verify the tokenisation rather than assuming the literal [SEP] resolves as intended:
This is a knowledge-distillation setup, following the authors' released notebook, rather than the
cross-entropy objective described in the paper's §4. Worth noting when interpreting results: the teacher
ensemble's scores are computed without any BM25 input, so the training objective does not directly reward
the model for using the injected feature.
Intended use
Built as a reference artifact for a dissertation study on reproducing IR papers with LLMs.