Skip to content

Commit b0d9fbc

Browse files
committed
docs(kpi): say what P@1, MRR, r@6 and nDCG actually mean
The metrics table said what each number asks and why it is there, and never once expanded the abbreviation or gave the formula. Every table in the report is headed with them. So: Precision at 1, Mean Reciprocal Rank, recall at 6, normalized Discounted Cumulative Gain at 10 — each with what it computes per query, what the @k suffix means, and a worked table of what all four give for a target landing at #1, #2, #3, #4, #6, #7, #10, #11 and past #50. The table is generated from lib/harness.js rather than from the textbook, because in one respect they differ. That respect is worth stating plainly, since anyone who knows nDCG will assume otherwise: OURS IS NOT CUMULATIVE. Textbook DCG sums the discounted gains of every relevant hit inside the window; evaluate() takes the single best contribution (Math.max). It is deliberate — `also` lists ALTERNATIVES, so the ideal ranking puts one of them first rather than all of them, and summing would reward a ranker for returning three spellings of the same answer. The note existed before the README was rewritten and did not survive the rewrite; this puts it back next to the definition instead of three sections away. Also documents the two labels that appear in the tables with no explanation anywhere: `lost` (the target never returned, or returned past rank 50 — the deep and unreachable buckets summed) and micro versus macro. And names where each metric goes blind, which the numbers alone do not show: r@6 falls off a cliff between #6 and #7, nDCG@10 reaches zero after #10, and MRR@target stops counting at rank 50 because that is the window evaluate() scores.
1 parent e893aa4 commit b0d9fbc

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

‎scripts/search-kpi/README.md‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,89 @@ A floor is a tripwire, not a target. Moving one is a deliberate act that belongs
172172

173173
## The metrics, and which one to tune on
174174

175+
### What the names mean
176+
177+
Standard information-retrieval abbreviations. The `@k` suffix means "computed over the first *k*
178+
results only" — `P@1` looks at position 1, `recall@6` at the first six, `nDCG@10` at the first ten.
179+
180+
**`P@1` — Precision at 1.** Precision@k is normally "what fraction of the top *k* results are
181+
relevant". At k = 1 there is one result and one correct answer, so per query it is simply **1 if the
182+
`target` is the very first result, 0 otherwise**, and the reported figure is the percentage of queries
183+
that scored 1. `P@1 = 61.6%` means the page we judged best was literally first for 61.6% of queries.
184+
185+
An `also` page at #1 scores **zero** here. That is deliberate: `also` means "defensible, but not the
186+
best answer", and a site search has one job at position 1.
187+
188+
**`MRR` — Mean Reciprocal Rank.** The reciprocal rank of one query is `1 / rank`: first place is 1.0,
189+
second 0.5, third 0.333, tenth 0.1, and **0 if the page never appears**. MRR is the mean of that over
190+
every query, printed as a percentage. Ours is strictly **MRR@target** — the rank of the *named best*
191+
page, not of the first merely-acceptable one.
192+
193+
This is the number to tune on, because it moves continuously. Lifting a page from #4 to #2 takes it
194+
from 0.25 to 0.5 — a large move that `P@1` cannot see at all, since neither rank is 1.
195+
196+
**`r@6` — recall at 6.** Recall@k asks "did we find the answer within the first *k*", ignoring where
197+
inside those *k* it landed. Per query it is **1 if any acceptable page — the `target` or any `also` —
198+
is in the first six**, else 0.
199+
200+
Six, because `search_docs` returns six results and an agent reads all six. For that reader membership
201+
in the set is the whole question and rank inside it is noise, which is why this one is not
202+
position-decayed. `r@6 = 90.4%` means an acceptable answer was somewhere in the first six for 90.4% of
203+
queries.
204+
205+
**`nDCG@10` — normalized Discounted Cumulative Gain at 10.** The only metric here that grades
206+
part-credit rather than scoring pass/fail. Three ideas stacked up:
207+
208+
- **Gain** — each page carries a relevance grade: `target` = 3, `also` = 1, anything else 0.
209+
- **Discounted** — a hit at position *i* is divided by `log₂(i + 1)`, so later positions are worth
210+
less, on a curve much gentler than a linear penalty. Beyond rank 10 the gain is dropped entirely.
211+
- **normalized** — divided by the best achievable score, so 100% means "the `target`, at #1".
212+
213+
**One caveat, because anyone who knows nDCG will assume otherwise: ours is not cumulative.** Textbook
214+
DCG *sums* the discounted gains of every relevant hit in the window; this takes the single best
215+
contribution (`Math.max` in `lib/harness.js`). That is on purpose — `also` lists *alternatives*, so
216+
the ideal ranking puts **one** of them first, not all of them, and summing would reward a ranker for
217+
returning three spellings of the same answer. Read it as a graded, discounted "how good was the best
218+
hit and how high was it", not as a textbook nDCG.
219+
220+
### What each one gives for a single target at each rank
221+
222+
Computed from `lib/harness.js`, not from the textbook, so this is what the report really does:
223+
224+
| target lands at | P@1 | MRR@target | r@6 | nDCG@10 |
225+
|---|---|---|---|---|
226+
| **#1** | **100** | **100.0** | **100** | **100.0** |
227+
| #2 | 0 | 50.0 | 100 | 63.1 |
228+
| #3 | 0 | 33.3 | 100 | 50.0 |
229+
| #4 | 0 | 25.0 | 100 | 43.1 |
230+
| #6 | 0 | 16.7 | 100 | 35.6 |
231+
| #7 | 0 | 14.3 | **0** | 33.3 |
232+
| #10 | 0 | 10.0 | 0 | 28.9 |
233+
| #11 | 0 | 9.1 | 0 | **0.0** |
234+
| #51 or never returned | 0 | 0.0 | 0 | 0.0 |
235+
236+
Read the discontinuities, because they are where each metric goes blind: **r@6** falls off a cliff
237+
between #6 and #7 and says nothing about anything below; **nDCG@10** goes to zero after #10;
238+
**MRR@target** keeps counting to rank 50 and then stops, which is the window `evaluate()` scores. Only
239+
the four target-position buckets below see past 50, and that is why they exist.
240+
241+
An `also` page scores a third of the target's nDCG at the same rank — 33.3% at #1, 21.0% at #2 — which
242+
is the 3:1 gain ratio doing its job.
243+
244+
### Two more labels you will see in the tables
245+
246+
**`lost`** — the share of queries where the `target` is either **never returned at all, or returned
247+
but beyond rank 50**. It is the sum of the *deep* and *unreachable* buckets, i.e. the queries no amount
248+
of position-tweaking will rescue.
249+
250+
**micro and macro** — two ways to average. **micro** is the plain mean over every query, so it reports
251+
what a visitor gets given this query mix. **macro** is the mean over topic groups, each weighted
252+
equally, so one popular subject cannot carry the score. Macro is the headline; see
253+
[Macro is the headline](#macro-is-the-headline-over-pooled-groups-rather-than-raw-topics) for how the
254+
groups are pooled and why the two differ by nine points.
255+
256+
### Which one to tune on
257+
175258
| | what it asks | why |
176259
|---|---|---|
177260
| **P@1** | is the `target` at #1 | the headline. A site search has one job at position 1. An `also` page at #1 scores **zero** here, deliberately |

0 commit comments

Comments
 (0)