Applied ML systems
LambdaMART
LambdaMART combines boosted trees with pairwise gradients scaled by how much a swap would change NDCG. It is the workhorse of listwise ranking.
LambdaMART is the listwise ranker most often used in industry. It is built from two parts. MART is gradient-boosted regression trees, which add many small trees one at a time. LambdaRank is the training signal: a pairwise gradient scaled by how much swapping that pair would change NDCG.
The combined idea is simple. If swapping two documents barely changes NDCG, the model should spend little effort on that pair. If swapping would move a highly relevant document from position 10 to position 1, the model should push hard.
Walking through a swap
Take two documents for one query. One has grade 2 and the other grade 0.
If the grade-0 document is ranked first, the DCG is 0 + 3 / log2(3) = 1.893. The ideal DCG is 3 / log2(2) = 3. So NDCG is 1.893 / 3 = 0.631.
Swapping them gives DCG 3, and NDCG 1. The swap changes NDCG by 0.369, so the model pushes hard.
Now take two grade-0 documents. Swapping them changes nothing, so the change in NDCG is 0, and LambdaRank gives no gradient for that pair.
A swap near the top moves more NDCG than the same swap lower down, because the discount log2(i + 1) is smallest at the top.
Training it
In practice you use XGBoost with rank:ndcg or LightGBM with lambdarank. Each query's rows must stay together, which is what the qid argument does.
import numpy as np
import xgboost as xgb
rng = np.random.default_rng(0)
n_queries, docs_per_query = 50, 10
X = rng.normal(size=(n_queries * docs_per_query, 3))
qid = np.repeat(np.arange(n_queries), docs_per_query) # rows grouped by query
y = np.clip(np.round(X[:, 0] + 0.5 * rng.normal(size=len(X)) + 1), 0, 3).astype(int) # grades 0-3
ranker = xgb.XGBRanker(
objective="rank:ndcg",
tree_method="hist",
n_estimators=100,
learning_rate=0.05,
)
ranker.fit(X, y, qid=qid)
scores = ranker.predict(X[:docs_per_query]) # score the first query's documents
order = np.argsort(-scores)
print("grades in predicted order:", y[:docs_per_query][order])Output:
grades in predicted order: [3 2 2 2 1 2 0 0 0 0]The data here is synthetic. The grades are high at the top and zero at the bottom, so the ranker learned the order. The code needs xgboost and scikit-learn installed.
Neural rankers exist, but boosted trees still win on many tabular feature sets with graded labels.
Trade-off
A pointwise model gives you calibrated probabilities, which ads auctions need. LambdaMART gives you a better top-k order when the product cares about the list and you have graded labels per query. Many teams train a pointwise model for calibrated scores and keep a listwise model for ordering.
Common mistakes
- Splitting a query's rows. The pairwise gradients then compare documents from different queries, and the signal is meaningless.
- Expecting probabilities. LambdaMART scores only order documents.
- Optimizing NDCG and reporting AUC. Report the metric you optimize, at the cutoff the UI shows.
Check yourself
- Why does a swap at rank 1 matter more than the same swap at rank 10? Answer: the discount
log2(i + 1)is smaller at the top, so the same gain counts for more there. - What does
qiddo? Answer: it marks which rows belong to the same query, so pairs are only compared within a query.
Key takeaways
- LambdaMART is boosted trees trained with pairwise gradients scaled by the NDCG change of each swap.
- Pairs that do not change NDCG get no gradient. Swaps near the top get the most.
- Keep each query's rows together with
qidor the equivalent. - Scores order the list. They are not calibrated probabilities.
Further learning
Next, see how click-through models estimate click probabilities for ads and feeds in CTR prediction.
Related
Learning to rank basics
Learning to rank trains a model to order a list. Pointwise, pairwise and listwise methods differ in what the loss compares, not in how you serve the sorted list.
Ranking metrics
NDCG, MRR, MAP and recall at k measure how good an order is. Work one NDCG example by hand and the formula stops being abstract.
Trees and gradient boosting
Decision trees, random forests and XGBoost/LightGBM: still the best default for tabular data.