Part III · Ranking · chapter 10 of 19
Learning to rank
The candidates are selected — now they have to be ordered. The question «which loss to take» looks like a choice from a catalogue, but in fact it is a choice of what exactly you declare to be the truth: relevance itself, the observed order, or the structure of the shown list. We work through three families, derive BPR from likelihood and look at what each choice takes away.
- The three families differ by the set on which a term of the loss is computed. One item, a pair, the whole slate. Everything else follows.
- Calibration and order are different things, and the second does not give you the first. A monotone transformation of the score changes neither AUC nor NDCG, but loses 67% of the revenue in arithmetic of the form \(P \cdot \text{price}\).
- LambdaRank is a weighted BPR. A swap at positions (1,2) weighs 31 times more than one at (9,10); BPR counts both pairs the same.
- The main limitation is shared by all three. They optimise a proxy under a fixed impression policy and know nothing about the items that policy never showed.
1. Three families
The classification of ranking losses is simple, and it should be held in exactly this formulation: the losses differ by the set of items over which a single term is computed.
The rest of the chapter is about what each widening of the context gives and what it takes away in exchange.
2. Pointwise: relevance directly
The most straightforward approach: ignore the fact that items were shown together and predict a pointwise target.
Here \(y_{ui} \in \{0,1\}\) is the label of the pair, \(f(u,i)\) the model's logit and \(\sigma\) the sigmoid. The factor \(y_{ui}\) kills the second term and \((1-y_{ui})\) the first, so exactly one remains for each pair.
| \(f(u,i)\) | \(\sigma(f)\) | penalty at \(y=1\) | penalty at \(y=0\) |
|---|---|---|---|
| −3 | 0.047 | 3.049 | 0.049 |
| 0 | 0.500 | 0.693 | 0.693 |
| +3 | 0.953 | 0.049 | 3.049 |
A confident mistake costs sixty times more than a confident hit. That is the entire mechanism.
In ordinary classification the label is given: an email is either spam or it is not. Here \(y_{ui}\) is the result of your decision, and it is the hardest part of the setup, entirely hidden behind an innocuous formula.
Suppose \(y = 1\) means «added to cart» and \(y = 0\) means «shown but not added». Note that a zero here means «shown and rejected», not «there was no interaction». The difference is fundamental.
- Items that were never shown to the user have no label at all. They are not zeros — they are simply absent from the sum.
- If you add them as zeros you get a different problem, and the question of where to get negatives and how to correct their bias arises at once. That is exactly the story of sampling negatives and the LogQ correction.
So behind the formula hide two decisions that are invisible in it: what counts as a one (a click? a cart? a purchase? a full view?) and which pairs enter the sum at all. The first determines what the model optimises; the second determines what a zero means. Both cost more than the choice of architecture.
Binarity is not mandatory: \(y_{ui}\) can be fractional. With graded targets cross-entropy works if the values are brought into \([0,1]\) — that is cross-entropy with soft labels, and it is the same mechanism as distillation.
What pointwise cannot do
- It does not see the structure of the list, and order in particular. The model is punished identically for a mistake at position 1 and at position 50, though the two cost completely different amounts.
- It is not directly aligned with either AUC or NDCG. Optimising log loss, you optimise the quality of the probabilities, not the order.
And yet pointwise heads live in production even where the final ranking is done by an entirely different loss. There is one reason, and it is a weighty one.
Take five products with a true purchase probability and a price. The second model is the same thing with the logit multiplied by two: a monotone transformation that does not change the order by score at all.
| Item | true \(p\) | score of the second model | price |
|---|---|---|---|
| A | 0.30 | 0.1552 | 100 |
| B | 0.20 | 0.0588 | 200 |
| C | 0.10 | 0.0122 | 900 |
| D | 0.05 | 0.0028 | 400 |
| E | 0.02 | 0.0004 | 300 |
The order by score for both models is ABCDE. So their AUC and NDCG coincide exactly: any ranking metric depends only on the order.
Now let us rank by expected revenue \(p \cdot \text{price}\):
- by calibrated probabilities: C (90) > B (40) > A (30) > D (20) > E (6);
- by the scores of the second model: A (16) > B (12) > C (11) > D (1) > E (0).
A rises to the top with an expected revenue of 30 instead of C with an expected revenue of 90 — a loss of 67% at the top position. The average log loss honestly worsens by 27% meanwhile, from 0.3466 to 0.4394: log loss sees the difference, ranking metrics do not.
The numbers are reproduced by the script _tools/ltr_demo.py in this repository.
Hence a rule worth remembering word for word: a calibrated score is needed whenever the score takes part in arithmetic — \(P(\text{buy})\cdot\text{price}\), \(pCTR \cdot bid\) in an ad auction, thresholds for business rules. A ranking loss does not give such a score and by construction cannot.
Calibration is convenient to check with the calibration widget in the trainer: it draws how the predicted probability relates to the observed frequency.
3. Pairwise: BPR
The next step is to stop telling the model «this is a one and that is a zero» and start telling it «this one is above that one».
We postulate that the probability «the positive is ranked above the negative» is modelled by a sigmoid of the difference of scores:
$$ P\bigl(i^{+} \succ i^{-} \mid u\bigr) = \sigma\bigl(f(u,i^{+}) - f(u,i^{-})\bigr) $$The likelihood of all observed preferences is the product of these probabilities. Take the logarithm, flip the sign:
$$ \mathcal{L}_{\text{BPR}} = -\sum_{i^{+} \in P}\ \sum_{i^{-} \in N} \log \sigma\bigl(f(u,i^{+}) - f(u,i^{-})\bigr) $$Here \(P\) and \(N\) are the user's sets of positives and negatives, usually from one slate, and the double sum runs over all pairs of «every positive against every negative».
Note what is not in the formula: the label \(y\). The absolute values of the scores take no part at all — only the difference \(\Delta = f(u,i^{+}) - f(u,i^{-})\).
First: invariance to a shift. Add any constant to all of one user's scores and the loss does not change by a hair. The model is free in its absolute values, and it lacks calibration not «by accident» but by construction.
Second: alignment with AUC. AUC is the share of correctly ordered «positive–negative» pairs, that is, \(\mathbb{E}\,\mathbb{1}[\Delta > 0]\). The indicator is not differentiable, while \(\log\sigma(\Delta)\) is its smooth upper bound. BPR literally minimises a smoothed \(1 - \text{AUC}\).
Differentiate with respect to the positive's score:
$$ \frac{\partial \mathcal{L}}{\partial f(u,i^{+})} = -\sigma(-\Delta) $$The weight of a pair in the gradient is \(\sigma(-\Delta)\), that is, a function of the margin already achieved:
| \(\Delta\) | weight | what kind of pair this is |
|---|---|---|
| −3 | 0.9526 | inverted, and badly |
| −1 | 0.7311 | inverted |
| 0 | 0.5000 | on the boundary |
| +1 | 0.2689 | ordered |
| +3 | 0.0474 | confidently ordered |
| +5 | 0.0067 | practically switched off |
An inverted pair weighs 20 times more than one already separated. No explicit hard-negative mining has to be written — it is built into the shape of the loss. This is the same property as the softmax in chapter 7, and for the same reason: the derivative of the logistic function is small at saturation.
The numbers are reproduced by the script _tools/ltr_demo.py.
It does not give calibration — see the invariance to a shift. Substituting the output of BPR into \(P(\text{buy})\cdot\text{price}\) is meaningless: it is not a probability but a number with an arbitrary zero.
It does not distinguish positions. The pair «first place against second» and the pair «five-hundredth against five-hundred-and-first» enter the sum with the same weight, though the first decides everything and the second decides nothing. That is exactly the hole the next section closes.
Most pairwise losses are extensions of BPR: reweightings and cleverer sampling of pairs. Conceptually BPR coincides with RankNet from the world of search engines.
4. LambdaRank: a bridge from pairs to the metric
The idea: since the metric cannot be optimised directly, choose the loss so that the gradients are proportional to the change of the metric from swapping that pair.
The notation: \(f_i\) and \(f_j\) are the scores of two items of the same query; \(s_{ij} \in \{+1,-1\}\) is the sign of the correct order, needed so that one formula works for a pair in either order; \(\Delta\mathrm{Metric}_{ij}\) is how much the metric changes if these two items are swapped (usually \(|\Delta\mathrm{NDCG}|\)).
The second factor is exactly the logistic loss on the margin, that is, the same BPR. The entire difference hides in the first factor.
A list of ten documents with exactly one relevant, \(\mathrm{IDCG} = 1\). We compute what happens to NDCG when the relevant document slides down one position:
| Swap | NDCG before | NDCG after | \(\Delta\mathrm{NDCG}\) |
|---|---|---|---|
| 1 ↔ 2 | 1.0000 | 0.6309 | 0.3691 |
| 2 ↔ 3 | 0.6309 | 0.5000 | 0.1309 |
| 5 ↔ 6 | 0.3869 | 0.3562 | 0.0306 |
| 9 ↔ 10 | 0.3010 | 0.2891 | 0.0120 |
The pair (1,2) weighs 31 times more than the pair (9,10) — while for BPR the two pairs are identical, since it sees only the sign of the difference. That is the whole essence of LambdaRank in one number.
The numbers are reproduced by the script _tools/ltr_demo.py.
A good interview question, and the right answer is not obvious. Formally the loss is pairwise — the sum runs over pairs. But the weight \(\Delta\mathrm{NDCG}_{ij}\) is computed over the whole slate: to know how much the metric will change from swapping a pair you need the positions of all the other documents and the IDCG of the query.
So a loss that is pairwise in form carries information about the structure of the entire list. The boundary between the families does not run where it seems to: what matters is not how many items are in one term but how many items you have to know to compute that term.
5. YetiRank: what if the labels lie
A method from Yandex that won the transfer learning track of the Yahoo! Learning to Rank Challenge and is available today in CatBoost. It is convenient to take apart right here, because its loss is the same pairwise one:
$$ \mathcal{L} \;=\; -\sum_{(i,j)} w_{ij} \log \frac{e^{x_i}}{e^{x_i} + e^{x_j}} $$This is literally BPR, where \(x_i\) is the model's score. All the peculiarity hides in the weights, and they factor into two independent multipliers:
$$ w_{ij} \;=\; N_{ij} \cdot c(l_i, l_j) $$The first answers the question «does this pair matter at all», the second «is \(i\) really better than \(j\)». Both are interesting, in different ways.
The factor \(N_{ij}\): which pairs have a chance of meeting near the top
The intuition: not all pairs matter, only those that can end up next to each other and high. The pair «first place against five-hundredth» decides nothing — it is already ordered correctly and will never be inverted.
How this is worked out: the scores are perturbed many times and re-ranked.
$$ \hat{x}_i \;=\; x_i + \log \frac{r_i}{1 - r_i}, \qquad r_i \sim U[0, 1] $$The addition \(\log \frac{r}{1-r}\) is a random variable with a logistic distribution, that is, the score «trembles». After each perturbation the list is re-sorted, and then the main point: weight is added only to the pairs that turned out adjacent in the new order, and the addition equals \(1/R\), where \(R\) is the position of that adjacency.
Hence three consequences, and all three are worth being able to name.
- Pairs that never turned out adjacent get exactly zero. Not a small weight but zero: they drop out of training entirely.
- The position discount is built in through \(1/R\). Adjacency at the first and second places contributes 1, at the hundredth and hundred-and-first 0.01.
- The weights depend on the current model and are recomputed at every boosting iteration. Pairs the model has already separated confidently stop being adjacent under perturbation and gradually drop out.
In a slate of \(n\) documents there are \(\binom{n}{2}\) pairs, and that grows quadratically: 45 at \(n=10\), 4950 at \(n=100\), 499 500 at \(n=1000\).
In YetiRank, one perturbation gives exactly \(n-1\) adjacent pairs, that is, 99. With the default ten perturbations, no more than 990 pairs — 20% of 4950 — get a non-zero weight, and that estimate does not depend on the arrangement of the scores: the growth is linear in \(n\) rather than quadratic.
The simulation also shows a second, subtler property:
| Spread of scores | Pairs with weight | Mean distance in rank |
|---|---|---|
| 3 (scores nearly indistinguishable) | 899 (18.2%) | 30.3 |
| 30 (scores separated) | 714 (14.4%) | 7.1 |
The more confidently the model has separated the documents, the closer in rank the remaining pairs are — training pulls itself towards the places in the list where there is still real uncertainty.
The numbers are reproduced by the script _tools/ltr_demo.py.
The difference is subtler than it looks at first glance, and it is about the attitude to one's own uncertainty.
- LambdaRank weighs a pair by how much the metric will change if it is swapped. And \(\Delta\mathrm{NDCG}\) is computed at the current deterministic order — that is, the method proceeds from the assumption that the model is already right.
- YetiRank weighs a pair by how likely it is to end up adjacent and high at all. Perturbing the scores is an explicit admission that close scores are nearly indistinguishable and that the slightest noise will flip the order between them.
The factor \(c(l_i, l_j)\): a model of labelling errors
The usual pairwise approach takes \(c(l_i, l_j) = l_i - l_j\) and keeps the pairs with a positive difference: the labels are declared to be the truth. YetiRank makes its central assumption and rejects that.
Here \(p(u \mid l)\) is the matrix of labelling errors: the probability that a true label \(u\) was recorded by an assessor as \(l\). The whole sum is the probability that document \(i\) is actually better than document \(j\), given that both labels could have been assigned inaccurately.
Take a pair with labels 3 and 2 on a five-point scale. The classical approach will say: the difference is 1, the pair is valid, learn from it. YetiRank will say: assessors confuse adjacent grades all the time, so the probability of a real superiority is small and the weight of the pair has to be cut. And a pair with labels 4 and 0 will keep a weight of about one: confusing the extreme grades is hard.
The effect: training stops being spent on pairs that differ only by labelling noise. The original work shows that modelling exactly this uncertainty gave the main gain over LambdaRank.
In CatBoost, YetiRank has a parameter decay with a default of 0.85 — the probability that the user will look at the next position. This is a geometric model of examination from the pFound metric, which Yandex measured search with. The same technique as the \(1/\log_2(i+1)\) discount in NDCG, but the model of the user is different:
| Position | \(1/\log_2(1+i)\) — NDCG | \(0.85^{\,i-1}\) — pFound |
|---|---|---|
| 1 | 1.0000 | 1.0000 |
| 2 | 0.6309 | 0.8500 |
| 3 | 0.5000 | 0.7225 |
| 5 | 0.3869 | 0.5220 |
| 10 | 0.2891 | 0.2316 |
| 20 | 0.2277 | 0.0456 |
At the twentieth position the models diverge 5-fold: NDCG keeps 0.228 of the weight of the first position there, the geometric one 0.046. The share of weight in the top 3 out of twenty: 30.3% against 40.1%.
The numbers are reproduced by the script _tools/ltr_demo.py.
These are different claims about the user, not two ways of writing the same thing. The logarithmic discount says «attention decays slowly, the tail is still worth something»; the geometric one says «a quarter of the audience leaves at every step». By choosing a metric you choose a model of the user.
- There is no graded relevance. The whole point of \(c(l_i,l_j)\) is that there are several labels and adjacent ones get confused. On binary «clicked / did not click» the factor degenerates and what remains is a weighted BPR.
- The labels come from logs rather than assessors. A click is not an expert's opinion, it is not «confused», it is biased in a different way: by position, by impression, by clickbait. A matrix of labelling errors does not describe that noise; here bias corrections work, not a model of assessor error. This is the most common mistake in applying it.
- Calibration is needed. Like any pairwise loss, YetiRank depends only on differences of scores.
6. Listwise: softmax-over-slate
The last family learns to arrange the whole slate correctly at once. The simplest and therefore the most popular variant is a softmax over the slate.
Here \(S\) is the slate, that is, the set of items shown together at one time; the sum in the denominator runs over it. \(y_i\) is the label of an item inside the slate: in the simplest case a one for the chosen item and zeros for the rest, and then the loss reduces to \(-\log P(\text{chosen} \mid S)\).
This is not a click probability. The softmax is normalised inside the slate, and the sum over the slate is always one — whatever the slate happens to contain.
A slate of four items with scores 2.0, 1.5, 1.0, 0.5:
| Slate | A | B | C | D | E |
|---|---|---|---|---|---|
| of four | 0.4551 | 0.2760 | 0.1674 | 0.1015 | — |
| added E with a score of 3.0 | 0.2034 | 0.1234 | 0.0748 | 0.0454 | 0.5530 |
The score of item A did not change by a hair — only its surroundings did. And its «probability» fell by 55%, from 0.4551 to 0.2034.
The numbers are reproduced by the script _tools/ltr_demo.py.
Hence a consequence that sounds paradoxical but is true: a listwise output is even less calibrated than a pairwise one. With BPR the score at least does not depend on what lies next to it; here it depends directly.
The formula is literally the same as in sampled softmax from chapter 7. There is one difference and it is fundamental: where the set in the denominator came from.
- In candidate generation the denominator is sampled negatives, and since we sampled them we know their distribution and can correct the bias (LogQ).
- In ranking the denominator is the shown slate, that is, the result of the logging policy. Its distribution is not under our control, and correcting it takes entirely different means.
This is the same distinction as between negatives from impressions and negatives from the catalogue, only now it shows up in the denominator of a softmax.
7. What of this goes to production
After going through the three families, an interviewer naturally asks: «so which one do you choose?» The right answer is that production usually runs several losses at once, and here is why.
| Family | Models | Gives | Does not give |
|---|---|---|---|
| Pointwise | relevance itself | calibrated probabilities | order, the structure of the slate |
| Pairwise | the observed order | alignment with AUC, hard-negative mining for free | calibration, distinguishing positions |
| Listwise | the structure of the slate | closeness to NDCG | calibration; more expensive and more capricious to train |
The typical construction: a multi-head model where pointwise heads give calibrated probabilities of individual events (click, purchase, full view), and the final order is assembled from them by a weighted combination or by a separate ranking loss on top. Calibration is needed not for beauty — the auction, the business rules and any arithmetic with money rest on it.
And the most important thing to say at the end of the answer. All three families optimise a proxy under a fixed impression policy.
No loss will tell the model about the items the policy never showed: they are in neither the pointwise sum, nor the pairs, nor the denominator of the softmax. This circle cannot be broken by the choice of loss — you have to affect the policy itself, and that is already the story of exploration and bandits.
Interview questions
How do pointwise, pairwise and listwise differ in meaning?
By the set over which a single term of the loss is computed, and everything else follows from that.
Pointwise models relevance itself (BCE on 0/1) — it gives calibrated probabilities but knows nothing about order or the structure of the slate: a mistake at position 1 and at position 50 are punished identically.
Pairwise models the observed order — it is aligned with AUC, because AUC is the share of correctly ordered pairs, but it loses calibration by construction.
Listwise models the structure of the slate — it is closest to NDCG, more expensive to train and even worse calibrated.
Derive BPR.
We postulate \(P(i^+ \succ i^- \mid u) = \sigma(f(u,i^+) - f(u,i^-))\). The likelihood of all observed preferences is a product of such probabilities; take the logarithm and flip the sign:
\(\mathcal{L}_{\mathrm{BPR}} = -\sum_{i^+ \in P}\sum_{i^- \in N} \log \sigma\bigl(f(u,i^+)-f(u,i^-)\bigr)\)
Two properties have to be named at once. The loss depends only on the difference of scores — so it is invariant to a shift and gives no calibration. And the gradient with respect to the positive's score equals \(-\sigma(-\Delta)\): a pair inverted by 3 weighs 0.95, one already separated by 3 weighs 0.047, that is, 20 times less. Mining of hard pairs is built into the shape of the loss.
Why do AUC and NDCG not see calibration, and when does that hurt?
Both metrics depend only on the order, and any monotone transformation of the score preserves the order. So a calibrated model and its own score passed through a monotone function are indistinguishable by AUC and NDCG.
It starts to hurt exactly when the score takes part in arithmetic. An example: five products, the ranking by score is the same for both models, but the ranking by expected revenue \(p \cdot \text{price}\) is different; the product that rises to the top has an expected revenue of 30 instead of 90, a loss of 67% at the top position. Log loss meanwhile worsens by 27% — it does see the difference.
Hence the practice: \(pCTR \cdot bid\) in an auction, \(P(\text{buy})\cdot\text{price}\), thresholds for business rules — everywhere a pointwise output is needed, even if the final order is produced by a different loss.
What is LambdaRank and why is it considered a listwise loss?
It is a pairwise loss in which every pair is weighted by the change of the metric from swapping it: \(\mathcal{L} = \sum_{(i,j)} \Delta\mathrm{NDCG}_{ij}\log(1+\exp(-s_{ij}(f_i-f_j)))\). The second factor is the same BPR; all the peculiarity is in the first.
Why: NDCG is piecewise constant and not differentiable, so it cannot be optimised directly, but the gradients can be weighted by a pair's contribution to the metric. The spread is enormous — a swap at positions (1,2) gives a ΔNDCG of 0.3691, at (9,10) 0.0120, that is, 31 times less.
And it is listwise because ΔNDCG is computed over the whole slate: you need the positions of all the other documents and the IDCG of the query. A formally pairwise loss carries information about the structure of the entire list.
How is YetiRank built and how does it differ from LambdaRank?
The loss itself is pairwise, the same BPR. Everything is in the weights \(w_{ij} = N_{ij}\cdot c(l_i,l_j)\).
\(N_{ij}\): the scores are perturbed many times with logistic noise and re-sorted; only the pairs that turn out adjacent get weight, with an addition of \(1/R\) by the position of the adjacency. Hence the position discount is built in, the weight matrix is sparse (no more than 990 pairs out of 4950 at 100 documents and 10 perturbations, that is, linear in n rather than quadratic), and the weights are recomputed at every boosting iteration.
\(c(l_i,l_j)\): the probability that \(i\) really is better than \(j\), given a matrix of labelling errors. The pair «3 against 2» gets a cut weight, the pair «4 against 0» keeps nearly one.
The difference from LambdaRank: the latter weighs a pair by how much the metric changes when it is swapped, proceeding from the assumption that the current order is right. YetiRank weighs it by how likely the pair is to end up adjacent and high at all — that is, it models its own uncertainty explicitly.
When should YetiRank not be applied?
Three cases. First, binary labels: the factor \(c(l_i,l_j)\) degenerates and a weighted BPR is all that is left. Second, labels from logs rather than assessors: a click is not «confused», it is biased by position and by impression, and a matrix of labelling errors does not describe that noise; bias corrections are what is needed. Third, when calibration is needed: like any pairwise loss it depends only on differences of scores.
The second case is the most common mistake in applying it: the method is taken into recommendations by analogy with search, where the labels really are from assessors.
How does a listwise softmax differ from sampled softmax in candidate generation?
The formula is one and the same. The difference is where the set in the denominator came from, and it is fundamental.
In candidate generation the denominator is sampled negatives: we set the distribution ourselves, so we know it and can correct the bias with LogQ. In ranking the denominator is the shown slate, that is, the result of the logging policy; its distribution is not under our control.
Plus a listwise output is particularly badly calibrated: add one strong item to a slate of four and the «probability» of the first falls from 0.4551 to 0.2034 — by 55%, though the item itself did not change.
What limitation do all three families share?
All of them optimise a proxy under a fixed impression policy. The items the policy never showed are in neither the pointwise sum, nor the pairs, nor the denominator of the softmax — no loss will tell the model about them.
That is the feedback loop: the model learns from what the previous model showed. The circle cannot be broken by the choice of loss — you have to affect the impression policy itself, that is, do exploration.
One-screen cheat sheet
The classification
They differ by the set a term is computed on: an item, a pair, a slate.
Pointwise
BCE. Calibration yes, order no. A zero means «shown and rejected», not «there was none».
BPR
\(-\sum\log\sigma(\Delta)\). From maximum likelihood, aligned with AUC, invariant to a shift → no calibration.
The BPR gradient
The weight of a pair is \(\sigma(-\Delta)\): 0.95 at −3 against 0.047 at +3. Hard negatives for free.
LambdaRank
BPR weighted by ΔNDCG. The pair (1,2) weighs 31 times more than (9,10). Formally pairwise, in essence listwise.
YetiRank
\(w_{ij}=N_{ij}c(l_i,l_j)\): perturbations give sparsity and a discount, the error matrix gives distrust of the labels.
Listwise
A softmax over the slate. Normalised inside — add a strong item and the «probability» of A falls by 55%.
The shared limitation
All three are proxies under a fixed policy. About what was not shown, none of them knows.
Primary sources
- S. Rendle, C. Freudenthaler, Z. Gantner, L. Schmidt-Thieme. BPR: Bayesian Personalized Ranking from Implicit Feedback, UAI 2009.
- C. Burges. From RankNet to LambdaRank to LambdaMART: An Overview, MSR-TR-2010-82 — the whole line of pairwise losses from one of the authors.
- A. Gulin, I. Kuralenok, D. Pavlov. Winning the Transfer Learning Track of Yahoo!'s Learning to Rank Challenge with YetiRank, JMLR W&CP 14, 2011.
- Z. Cao, T. Qin, T.-Y. Liu et al. Learning to Rank: From Pairwise Approach to Listwise Approach, ICML 2007 — ListNet, where the listwise family began.
- S. Bruch, X. Wang, M. Bendersky, M. Najork. An Analysis of the Softmax Cross Entropy Loss for Learning-to-Rank with Binary Relevance, ICTIR 2019 — why the softmax loss is connected with NDCG.
- CatBoost documentation on ranking loss functions — the parameters
mode,decay,permutations,top. - The numbers in this chapter:
_tools/ltr_demo.pyin this repository.