---
id: wiki-2026-0508-ranking-algorithms
title: Ranking Algorithms
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Learning to Rank, LTR, Search Ranking, Recommendation Ranking]
duplicate_of: none
source_trust_level: A
confidence_score: 0.92
verification_status: applied
tags: [ranking, ir, ml, ltr, search]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: python
  framework: lightgbm/xgboost/lightning
---

# Ranking Algorithms

## 매 한 줄
> **"매 query 별 candidate 의 ordered list 의 produce"**. BM25 의 classical → LambdaMART 의 LTR → 2024-2026 의 dual-encoder + cross-encoder reranker (E5, BGE-M3, Cohere Rerank 3) 의 modern stack — 매 search / RAG / recommendation 의 backbone.

## 매 핵심

### 매 paradigms
- **Pointwise**: 매 single (q, doc) 의 score regression / classification.
- **Pairwise**: 매 (q, doc⁺, doc⁻) margin 의 prefer.
- **Listwise**: 매 entire ranking 의 metric (NDCG) 의 optimize.

### 매 metrics
- **MRR**: 1 / rank of first relevant.
- **MAP**: mean of average precisions.
- **NDCG@k**: 매 graded relevance + log discount.
- **Precision@k / Recall@k**.

### 매 modern stack (2026)
1. **First-stage retrieval**: BM25 + dense (E5-large-v2, BGE-M3, GTE-Qwen2).
2. **Reranking**: cross-encoder (BGE-Reranker-v2-m3, Cohere Rerank 3, Voyage Rerank).
3. **Final blend**: RRF / weighted fusion.

### 매 응용
1. Web search.
2. RAG document selection.
3. E-commerce product ranking.
4. Feed ranking (social).
5. Recommendation reordering.

## 💻 패턴

### BM25 (rank_bm25)
```python
from rank_bm25 import BM25Okapi
import re
tok = lambda s: re.findall(r"\w+", s.lower())
corpus = [tok(d) for d in docs]
bm25 = BM25Okapi(corpus)
scores = bm25.get_scores(tok(query))
top = sorted(zip(scores, docs), reverse=True)[:10]
```

### Dense retrieval (sentence-transformers)
```python
from sentence_transformers import SentenceTransformer, util
m = SentenceTransformer("intfloat/e5-large-v2")
doc_emb = m.encode([f"passage: {d}" for d in docs], normalize_embeddings=True)
q_emb   = m.encode([f"query: {q}" for q in queries], normalize_embeddings=True)
hits    = util.semantic_search(q_emb, doc_emb, top_k=50)
```

### Cross-encoder rerank
```python
from sentence_transformers import CrossEncoder
ce = CrossEncoder("BAAI/bge-reranker-v2-m3")
pairs = [(q, docs[h["corpus_id"]]) for h in hits[0]]
scores = ce.predict(pairs)
reranked = [d for _, d in sorted(zip(scores, [docs[h["corpus_id"]] for h in hits[0]]),
                                 reverse=True)][:10]
```

### LambdaMART with LightGBM
```python
import lightgbm as lgb, numpy as np
train = lgb.Dataset(X_train, y_train, group=group_train)
params = dict(objective="lambdarank", metric="ndcg", ndcg_eval_at=[5,10],
              learning_rate=0.05, num_leaves=63)
model = lgb.train(params, train, num_boost_round=500)
preds = model.predict(X_test)         # 매 score per (q, doc); 매 group 별 sort.
```

### Reciprocal Rank Fusion (combine bm25 + dense)
```python
def rrf(rankings, k=60):
    score = {}
    for ranks in rankings:
        for rank, docid in enumerate(ranks):
            score[docid] = score.get(docid, 0) + 1.0/(k + rank + 1)
    return sorted(score, key=score.get, reverse=True)

fused = rrf([bm25_ranking, dense_ranking])
```

### NDCG implementation
```python
import numpy as np
def dcg(rels): return ((2**np.array(rels)-1)/np.log2(np.arange(2, len(rels)+2))).sum()
def ndcg(rels, k=10):
    rels = rels[:k]; ideal = sorted(rels, reverse=True)
    return dcg(rels)/dcg(ideal) if dcg(ideal) else 0.0
```

### Pairwise loss (PyTorch)
```python
import torch, torch.nn.functional as F
def pairwise_logistic(s_pos, s_neg):
    return F.softplus(-(s_pos - s_neg)).mean()
```

### ListNet (listwise softmax-CE)
```python
def listnet_loss(scores, rels):
    # scores, rels: (B, list_len)
    P_y = torch.softmax(rels.float(), dim=-1)
    log_P_z = torch.log_softmax(scores, dim=-1)
    return -(P_y * log_P_z).sum(dim=-1).mean()
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| 매 small corpus, no labels | BM25 alone |
| 매 semantic queries / paraphrase | Dense (E5/BGE) + BM25 RRF |
| 매 RAG top-k 정확도 priority | + cross-encoder rerank |
| 매 graded relevance labels | LambdaMART (LightGBM) |
| 매 deep features + click logs | Pairwise/listwise neural LTR |
| 매 personalization | Two-tower + reranker |

**기본값**: 매 BM25 + dense (E5-large-v2) + RRF → cross-encoder rerank top 50.

## 🔗 Graph
- 부모: [[Information Retrieval (IR)]] · [[Information Retrieval Evaluation Metrics]] · [[Keyword Search]]
- 변형: [[Locality-Sensitive-Hashing (LSH)]] · [[Approximate Nearest Neighbor]]
- 응용: [[Knowledge Graph]] · [[Bloom-Filters in Search]] · [[Structured Data]]
- Adjacent: [[Relevance-Feedback]] · [[Similarity-Metrics]] · [[Optimization-Algorithms]]

## 🤖 LLM 활용
**언제**: 매 RAG 의 top-k passage 의 quality 의 dominant impact — 매 reranker 의 ROI 의 highest.
**언제 X**: 매 corpus 의 < 1k docs — 매 just embed-and-cosine 의 simpler.

## ❌ 안티패턴
- **Position bias 무시**: 매 click data 의 raw 의 label 으로 사용 → 매 IPS / debiasing 의 필요.
- **Train/test query overlap**: 매 leakage — 매 query-level split.
- **NDCG 의 graded 없는 binary 적용**: 매 useful 하지만 정보 손실.
- **Single-stage neural everything**: 매 first-stage retrieval 없이 cross-encoder 전체 corpus 의 score → 매 수십 thousand 배 cost.
- **Same scoring model for retrieve + rerank**: 매 inefficient — 매 dual-encoder + cross-encoder 의 separate.

## 🧪 검증 / 중복
- Verified (Liu *Learning to Rank for IR* 2009; MS MARCO / BEIR benchmarks 2024-2026; LambdaMART paper).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — modern BM25+dense+rerank stack with LambdaMART and listwise patterns |
