One vector per text, or a score for the pair
TOY: the pair scorer is a hand-written stand-in for a cross-encoder (its word-relation table is written out in the code). A real one learns such relations from data.
An embedding squeezes each passage into one vector before it ever sees the question. A cross-encoder reads the question and one passage together and scores the pair: much sharper, far too slow for the whole index, so it re-orders only a shortlist.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 5 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write rerank_ms(n, pair_ms) that returns the milliseconds to score n pairs sequentially. Print it for n = 30 and n = 1000 with pair_ms = 8.
Write order_by_pair(question, candidates, scorer) that returns the candidates sorted by scorer(question, candidate), best first, keeping the original order for ties. Print the result for candidates ["a", "bb", "cc"] with a scorer that returns the length of the candidate.
Explain it without notes
How does a cross-encoder differ from a bi-encoder, and why is it more accurate?
Why is a cross-encoder too slow to run over the whole index?
What happens to a relevant passage that the first stage did not return?
Why shouldn't you trust the toy scorer's results as evidence about real re-rankers?
Expected output
Q: What is the age limit for helmets?
one vector per text, top 3: D1 D2 D3
pair scorer, top 3: D4=0.87 D11=0.27 D1=0.00
D4 is at: vector 4, pair scorer 1
Q: What happens if someone steals the bike?
one vector per text, top 3: D7 D11 D10
pair scorer, top 3: D12=0.90 D5=0.50 D6=0.50
D12 is at: vector 9, pair scorer 1