Part IV · Sequences and the slate · chapter 15 of 19
Re-ranking and diversity
The ranker has produced the scores; all that is left is to take the top \(k\). That could be the end of it — if an item's value did not depend on what stands next to it. But it does, and that is why there is a separate layer that assembles not a list of the best items but the best list. These are different problems, and the second is the harder one.
- The best slate is not the top by score. A less relevant item from another topic gives an output 48% better than a more relevant duplicate.
- Diversity is bought cheaply, but only at first. The first few percent cost 7.6% of relevance and cover every topic; the further 0.07 of diversity costs another 13 percentage points.
- Greedy selection is not a hack here. On a submodular function it guarantees no worse than 63.2% of the optimum, while enumerating a slate of 10 from 500 candidates is 2.5 · 1020 options.
- An offline metric always votes for \(\lambda = 1\). It cannot count the value of dissimilarity, so only an A/B test decides.
1. Why the top by score is not the best output
A ranking model scores items one at a time: \(f(u, i)\) does not know what else will end up in the output. And the user sees the list as a whole, with the items in it interacting.
Two items, each with a click probability of 0.20. If they are independent, the probability of at least one click is 0.36. And if they are near-duplicates, the second adds nothing: 0.20.
Now take a third item — from another topic, with a lower probability of 0.12:
| Slate | Probability of a click |
|---|---|
| original + duplicate (scores 0.20 and 0.20) | 0.2000 |
| original + another topic (scores 0.20 and 0.12) | 0.2960 |
The numbers are reproduced by the script _tools/rerank_demo.py in this repository.
A less relevant item gives a slate 48% better. A ranker that honestly sorted by score would have put the duplicate there — and would have been formally right about every item taken separately.
Hence the existence of a separate layer: the problem «choose the best \(k\) items» and the problem «assemble the best list of \(k\) items» are different, and the second does not reduce to the first by sorting.
These two are easy to confuse, and in an interview the confusion shows immediately. The difference is in the goal, not in the mechanism.
- Exploration: we change the logging policy for the sake of changing the policy — to obtain data about what we have not been showing. That is the story of the feedback loop, and the next chapter is about it.
- Diversity: we change the output for product reasons — because a list of ten nearly identical products is bad for the user here and now, regardless of what data we will collect.
The mechanism is similar — in both cases we deviate from the greedy top. But exploration is paid for with the future quality of the model, and diversity with the user's present satisfaction. Those are different budgets and different metrics of success.
2. How diversity is measured
Before optimising it you have to be able to measure it. Three levels, and it is useful to keep them apart.
| Level | Metric | What it catches |
|---|---|---|
| Inside one output | intra-list diversity — the average dissimilarity of pairs in the slate; the entropy of categories | «ten identical products in a row» |
| Per user over a period | the share of categories new to them, serendipity | «they have been locked into one topic» |
| Across the whole system | coverage and Gini over impressions | «the catalogue is not turning over, the tail is not being shown» |
A slate of 10 positions, different arrangements by category:
| Arrangement | Categories | Entropy | Maximum |
|---|---|---|---|
| one category | 1 | 0.0000 | 0.0000 |
| 8 + 1 + 1 | 3 | 0.6390 | 1.0986 |
| 5 + 3 + 2 | 3 | 1.0297 | 1.0986 |
| 4 + 3 + 3 | 3 | 1.0889 | 1.0986 |
| 2 in each of five categories | 5 | 1.6094 | 1.6094 |
The numbers are reproduced by the script _tools/rerank_demo.py.
Compare the second and fourth rows: there are three categories in both, and the entropy differs twofold. A simple count of categories would say those slates are equal — while «eight plus one plus one» is an output from a single topic with two random specks in it.
The metric penalises not only a single topic but also a skew. That is exactly what is wanted.
3. Business rules: the most common option
Before talking about MMR and DPP it is worth saying honestly that neither is what usually runs in production. The most widespread and cheapest approach is greedy re-ranking under an explicit rule.
- «no more than two products from the same category in a row»;
- «on every prefix the distribution of categories must be as close to uniform as possible»;
- «do not show what was already seen yesterday»;
- quotas: «no more than one ad position in the first five».
Candidates with scores and categories: 0.95A, 0.93A, 0.91A, 0.90A, 0.88A, 0.86B, 0.84A, 0.80C, 0.78B, 0.70C. We take six.
| without the rule | 0.95A 0.93A 0.91A 0.90A 0.88A 0.86B | sum 5.43 | 2 categories |
| with the rule | 0.95A 0.93A 0.86B 0.91A 0.90A 0.80C | sum 5.35 | 3 categories |
The loss is 1.47% of the sum of scores.
The numbers are reproduced by the script _tools/rerank_demo.py.
And here is why it works out so cheaply — a thought worth saying out loud in an interview. The scores of neighbouring candidates by rank are almost identical. Swapping one item for another a few positions down, we lose hundredths and gain a structural change in the output.
The flip side: if the scores diverge sharply, the same swap becomes expensive. The cost of a rule is not a constant of the product but a property of the distribution of scores, and it has to be measured rather than assumed.
The upsides of the approach: simple, predictable, easy to explain to product and easy to roll back. There is one downside, but a substantial one — a rule knows nothing about substantive similarity. Two products from different categories can be practically identical, and the rule will be formally satisfied.
4. MMR
We build the slate greedily. At step \(k\) we choose the item:
$$ i_k = \arg\max_{i \notin S}\Bigl[\lambda \cdot \mathrm{rel}(u,i) - (1-\lambda)\cdot \max_{j \in S}\mathrm{sim}(i,j)\Bigr] $$where \(S\) is the set of elements already chosen, similarity is usually computed over a content embedding, and \(\lambda\) is a hyperparameter. It reads literally: «take the most relevant thing among what is not too similar to what has already been taken».
Note the \(\max_{j \in S}\): the penalty runs over the nearest item already chosen, not over the average. That matters — one duplicate in the slate poisons a candidate entirely, even if it resembles nothing else there.
18 items in three topical clusters, a slate of five. The relevance is concentrated in one cluster — as it tends to be, since similar items get similar scores.
| \(\lambda\) | Clusters covered | Diversity | Sum of relevance | Loss |
|---|---|---|---|---|
| 1.0 | 1 | 0.3173 | 4.3745 | 0.0% |
| 0.9 | 1 | 0.3173 | 4.3745 | 0.0% |
| 0.8 | 2 | 0.6187 | 4.2280 | 3.3% |
| 0.7 | 3 | 0.8159 | 4.0425 | 7.6% |
| 0.6 | 3 | 0.8159 | 4.0425 | 7.6% |
| 0.3 | 3 | 0.8691 | 3.6950 | 15.5% |
| 0.0 | 3 | 0.8874 | 3.4642 | 20.8% |
The numbers are reproduced by the script _tools/rerank_demo.py; the same script independently repeats the widget's computation.
This table has to be read bottom-up and top-down at the same time.
- At \(\lambda = 1\) this is the ordinary top, and the whole output has slid into one cluster — 1 of 3 covered. The reason is exactly the one described above: similar items get similar scores, so a top by score is topically homogeneous by construction.
- The first few percent of diversity are almost free. Moving to \(\lambda = 0.7\) covers all three clusters and raises diversity from 0.32 to 0.82 — for 7.6% of relevance.
- After that the price rises sharply. From \(\lambda = 0.7\) to \(\lambda = 0\) diversity adds only 0.07 while the loss grows from 7.6% to 20.8%.
The shape of the curve is typical and is itself the practical advice: work at the knee, not at the ends.
Note the first row of the table: at \(\lambda = 1\) the sum of relevance is maximal by definition. And NDCG will be maximal, and Recall@k, and any other offline ranking metric.
Offline metrics cannot count the value of dissimilarity. They evaluate an output as a set of independent items — the very assumption because of which this layer was needed in the first place.
The practical consequence is hard: \(\lambda\) cannot be chosen offline. Only an A/B test, and only by product metrics — return rate, session length, the share of users interacting with more than one topic.
- Set \(\lambda = 1\) and look at the picture: the selected points huddle in one cluster.
- Lower \(\lambda\) and watch two numbers at once — diversity and the sum of relevance. Find the knee.
- Switch to DPP mode and compare: there is no manual \(\lambda\) there, the trade-off is built into the construction.
What to say in an interview: «MMR is greedy selection with a penalty for similarity to what is already chosen. Offline it always looks like a loss, because offline metrics cannot count the value of dissimilarity; only an A/B test decides».
5. DPP: diversity through volume
We define a distribution over subsets in which the probability is proportional to the determinant of the corresponding submatrix of a similarity matrix, and sample from it:
$$ P(S) \;\propto\; \det\bigl(L_S\bigr), \qquad L = \operatorname{diag}(q)\, S\, \operatorname{diag}(q) $$Here \(q\) are the relevances and \(S\) is the similarity matrix. The intuition is geometric and worth remembering: the determinant of a Gram matrix equals the squared volume of the parallelepiped spanned by the vectors.
For two items of unit length \(\det = 1 - \cos^2\theta = \sin^2\theta\):
| Angle | 0° | 15° | 30° | 60° | 90° |
|---|---|---|---|---|---|
| \(\det\) | 0.0000 | 0.0670 | 0.2500 | 0.7500 | 1.0000 |
Now let us fix the angle at 60° and move the relevance of the first item:
| \(q_1\) | 0.5 | 1.0 | 2.0 |
|---|---|---|---|
| \(\det\) | 0.1875 | 0.7500 | 3.0000 |
The numbers are reproduced by the script _tools/rerank_demo.py.
Collinear vectors give \(\det = 0\) — such a subset is not sampled at all, not «more rarely» but never. And the determinant grows as the square of relevance and as the square of the sine of the angle.
Hence the main advantage of DPP over MMR: one construction accounts for both relevance (the lengths) and diversity (the angles), with no manual \(\lambda\) balancing two terms of different natures.
Building such a distribution explicitly is very hard. Algorithms for approximate sampling in reasonable time exist, but in real production DPP is rarely used.
The reason is engineering: even approximate algorithms usually require the similarity matrix to be computed explicitly and decomposed. For hundreds of candidates per request and a budget of a few milliseconds that is expensive — while MMR requires exactly \(k \cdot |C|\) similarity computations and always fits.
DPP is worth knowing because people ask about it and because the geometric intuition is useful in itself. Putting it into production — probably not.
6. Why greedy selection is a legitimate algorithm
MMR, business rankers and almost everything else in this layer are greedy. A fair question arises: is that not a botch? The answer is no, and it has a proof.
The «usefulness of a slate» function is usually monotone (adding an item does not hurt) and submodular: every next item adds no more than it would have added had it been taken earlier. This is a formalisation of saturation — the second product of the same category is less useful than the first.
$$ f(S \cup \{i\}) - f(S) \;\ge\; f(T \cup \{i\}) - f(T) \quad \text{for } S \subseteq T $$For such functions there is a classical result: the greedy algorithm gives no worse than \(1 - 1/e \approx 0.6321\) of the optimum.
The alternative to greediness is enumerating subsets:
| a slate of 5 from 18 (as in the widget) | 8 568 options | enumerable |
| a slate of 10 from 500 (as in production) | 2.46e+20 options | impossible |
The numbers are reproduced by the script _tools/rerank_demo.py.
At the real sizes of the problem, greediness is the only option that has any proven guarantee at all. That is not a compromise on quality, it is the one point on the map where quality is bounded from below by anything.
7. An autoregressive slate
There is also a fundamentally different route that removes the problem at once. If ranking is solved in the sequential formulation — predicting the next positive item — then the slate can be built autoregressively: every next item is chosen given those already chosen.
Elegant: the model learns for itself that after a pair of trainers it should not show four more pairs of trainers — because in the training data trainers are usually not followed by the same thing. Diversity comes out as a side effect of the right formulation rather than as a separate layer with a manual \(\lambda\). This is the same technique as generative retrieval: replace an external constraint with the structure of the problem.
Not free: it needs \(k\) sequential passes instead of one batch (position \(k\) cannot be computed without having chosen \(k-1\)), and that is exactly what the budget cannot bear. Plus the model inherits its diversity from the logs — and the logs were collected by a previous policy that was not distinguished by diversity.
Interview questions
Why is a separate re-ranking layer needed if the ranker has already sorted everything?
Because the ranker scores items one at a time while the user sees the list as a whole, with the items interacting. Two items with a click probability of 0.20 give 0.36 if they are independent and only 0.20 if they are duplicates. And the original plus a less relevant item from another topic (0.12) gives 0.296 — a slate 48% better.
So «choose the best k items» and «assemble the best list of k items» are different problems, and the second does not reduce to the first by sorting.
How does diversity differ from exploration?
By the goal, not the mechanism. In exploration we deviate from the greedy top in order to obtain data about what we have not been showing — we pay with present quality for the future quality of the model. In working with diversity we deviate for product reasons: a list of ten nearly identical products is bad here and now, regardless of what data we will collect.
The mechanism is similar, but the budgets and the metrics of success are different.
How do you measure diversity?
At three levels. Inside the output — intra-list diversity (the average dissimilarity of pairs) and the entropy of categories. Per user over a period — the share of categories new to them, serendipity. Across the system — coverage and Gini over impressions.
Entropy is more convenient than a simple count of categories because it also penalises a skew: the slates «8 + 1 + 1» and «4 + 3 + 3» both contain three categories, but the entropies are 0.639 against 1.089 — nearly double. And «8 + 1 + 1» is an output from one topic with two random specks.
What is MMR?
Greedy selection with a penalty for similarity to what is already chosen: at every step we take \(\arg\max [\lambda\,\mathrm{rel}(u,i) - (1-\lambda)\max_{j\in S}\mathrm{sim}(i,j)]\). The penalty runs over the nearest item already chosen rather than the average: one duplicate in the slate poisons a candidate entirely.
The shape of the trade-off curve is typical: the first few percent of diversity are almost free (all three clusters are covered for 7.6% of relevance, and diversity grows from 0.32 to 0.82), after which the price rises sharply — the remaining 0.07 of diversity costs another 13 percentage points. Work at the knee.
How do you choose λ?
Only by A/B. Offline metrics always vote for λ = 1: at that value the sum of relevance is maximal by definition, and so are NDCG and Recall@k. They evaluate an output as a set of independent items — the very assumption because of which the re-ranking layer was needed at all.
What has to be measured is product metrics: return rate, session length, the share of users interacting with more than one topic.
What is a DPP and why does a determinant mean diversity?
We define a distribution over subsets with \(P(S) \propto \det(L_S)\), where \(L = \mathrm{diag}(q)\,S\,\mathrm{diag}(q)\), and sample from it. The determinant of a Gram matrix is the squared volume of the parallelepiped on those vectors.
Collinear vectors (nearly identical items) give a volume of zero, and such a subset is never sampled at all. Orthogonal ones give the maximum. For two unit vectors det = sin²θ: 0 at 0°, 0.75 at 60°, 1 at 90°. And the lengths are responsible for relevance — the determinant grows as its square.
The advantage over MMR: one construction accounts for both relevance and diversity, with no manual λ balancing two terms of different natures. But it is rarely used in production — even approximate algorithms require the similarity matrix to be computed explicitly and decomposed, which does not fit the budget.
Why is a greedy algorithm acceptable here?
Because the usefulness of a slate is usually monotone and submodular: every next item adds no more than it would have added earlier — a formalisation of saturation. For such functions the greedy algorithm gives no worse than 1 − 1/e ≈ 0.632 of the optimum.
And that is not a consolation prize: enumerating a slate of 10 from 500 candidates is 2.5·10²⁰ options. At the real sizes of the problem, greediness is the only option that has any lower bound at all.
Can you do without a separate diversity layer?
Yes, if the slate is built autoregressively: solving the problem as predicting the next item given those already chosen. Then the model learns for itself that after a pair of trainers it should not show four more — diversity comes out as a side effect of the formulation, with no manual λ.
The price: k sequential passes instead of one batch, because position k cannot be computed without choosing the previous ones — and that is exactly what the ranking budget cannot bear. Plus the model inherits its diversity from logs collected by a previous policy.
One-screen cheat sheet
Why the layer
The score is per item, the output is shared. A duplicate: 0.20 against 0.296 for a less relevant item from another topic.
Do not confuse
Exploration is for data. Diversity is for the user now. Different budgets.
Metrics
ILD and entropy in the slate, novelty per user, coverage and Gini across the system.
Rules
The most common option. «No more than two in a row» costs 1.47% — neighbouring scores are nearly equal.
MMR
\(\lambda\,\mathrm{rel} - (1-\lambda)\max_j \mathrm{sim}\). The penalty by the nearest. The knee of the curve is 7.6%.
λ only online
Offline is always for \(\lambda=1\): the metrics cannot count the value of dissimilarity.
DPP
\(\det\) = a squared volume: lengths give relevance, angles diversity. Rarely in production.
Greediness
Submodularity gives \(1-1/e = 0.632\). Enumerating 10 from 500 is 2.5e20 options.
Primary sources
- J. Carbonell, J. Goldstein. The Use of MMR, Diversity-Based Reranking for Reordering Documents and Producing Summaries, SIGIR 1998 — the original work on MMR.
- A. Kulesza, B. Taskar. Determinantal Point Processes for Machine Learning, 2012 — the exhaustive survey of DPPs.
- L. Chen, G. Zhang, H. Zhou. Fast Greedy MAP Inference for Determinantal Point Process to Improve Recommendation Diversity, NeurIPS 2018 — approximate DPP in recommendations.
- G. Nemhauser, L. Wolsey, M. Fisher. An analysis of approximations for maximizing submodular set functions, Mathematical Programming 1978 — that \(1 - 1/e\) guarantee.
- C.-N. Ziegler et al. Improving Recommendation Lists Through Topic Diversification, WWW 2005 — metrics of intra-list diversity.
- The numbers in this chapter:
_tools/rerank_demo.pyin this repository.