跳转至

475_信息检索与排序


一句话说明

信息检索(IR)是从大规模文档库中找到与用户查询最相关文档的技术,搜索引擎的核心;排序学习(Learning to Rank)用机器学习优化搜索结果排序。


核心知识点

  • 两阶段检索:召回(快速粗筛)→ 排序(精细重排)
  • 倒排索引:关键词→文档列表的映射,BM25搜索的基础
  • 向量检索:把文档和查询编码为向量,通过近似最近邻(ANN)检索
  • Learning to Rank(LTR)
  • Pointwise:对单文档评分
  • Pairwise:比较文档对(RankNet、LambdaMART)
  • Listwise:直接优化列表级指标(LambdaRank)
  • 评估指标:NDCG(归一化折扣累计增益)、MAP(均值平均精度)

核心技术对比

技术类型优势局限
BM25关键词快、无需训练无语义理解
DPR(密集检索)语义语义匹配需要训练数据
ColBERT晚交互高精度+效率存储开销大
LambdaMARTLTR工业级可靠需点击/标注数据
BERT Reranker精排最高精度慢,不适合召回

代码示例

# ---- 1. BM25 关键词检索 ----
# pip install rank-bm25 jieba
from rank_bm25 import BM25Okapi
import jieba

# 文档库(实际场景可能有百万文档)
corpus = [
    "深度学习在图像识别领域取得突破性进展",
    "自然语言处理BERT模型的预训练方法",
    "推荐系统协同过滤算法原理",
    "机器学习随机森林模型解析",
    "图神经网络在知识图谱中的应用",
]

# 中文分词(BM25需要分词后的token列表)
tokenized_corpus = [list(jieba.cut(doc)) for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)

# 查询
query = "深度学习图像"
tokenized_query = list(jieba.cut(query))
scores = bm25.get_scores(tokenized_query)
top_n = bm25.get_top_n(tokenized_query, corpus, n=3)

print("BM25 Top-3结果:")
for i, doc in enumerate(top_n):
    print(f"  {i+1}. {doc}")

# ---- 2. 密集向量检索(DPR风格)----
from sentence_transformers import SentenceTransformer
import numpy as np
import faiss  # pip install faiss-cpu

# 用Sentence-BERT编码文档库
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
doc_embeddings = model.encode(corpus, show_progress_bar=True)
# doc_embeddings.shape: (5, 384)

# 构建FAISS索引(近似最近邻检索,百万级文档秒级返回)
dimension = doc_embeddings.shape[1]  # 384
index = faiss.IndexFlatIP(dimension)  # IP=内积(等效于L2归一化后的余弦相似度)

# L2归一化,使内积等于余弦相似度
faiss.normalize_L2(doc_embeddings)
index.add(doc_embeddings.astype(np.float32))  # 添加向量到索引

# 查询
query_emb = model.encode([query])
faiss.normalize_L2(query_emb)
distances, indices = index.search(query_emb.astype(np.float32), k=3)

print("\nFAISS 向量检索 Top-3:")
for i, (dist, idx) in enumerate(zip(distances[0], indices[0])):
    print(f"  {i+1}. (相似度={dist:.4f}) {corpus[idx]}")

# ---- 3. 混合检索(BM25 + 向量,取分数融合)----
def hybrid_search(query, corpus, bm25, model, alpha=0.5, top_k=3):
    """
    alpha: BM25权重;(1-alpha): 向量检索权重
    """
    # BM25分数
    tokenized_q = list(jieba.cut(query))
    bm25_scores = np.array(bm25.get_scores(tokenized_q))
    bm25_scores = bm25_scores / (bm25_scores.max() + 1e-9)  # 归一化

    # 向量相似度分数
    q_emb = model.encode([query])
    faiss.normalize_L2(q_emb)
    dense_scores, _ = index.search(q_emb.astype(np.float32), k=len(corpus))
    # FAISS返回的是排序后的,需要还原顺序
    vec_scores = np.zeros(len(corpus))
    for score, idx in zip(dense_scores[0], _[0]):
        if idx < len(corpus):
            vec_scores[idx] = score

    # 加权融合
    final_scores = alpha * bm25_scores + (1 - alpha) * vec_scores
    top_indices = np.argsort(final_scores)[::-1][:top_k]
    return [(corpus[i], final_scores[i]) for i in top_indices]

results = hybrid_search("深度学习图像识别", corpus, bm25, model)
print("\n混合检索结果:")
for doc, score in results:
    print(f"  {score:.4f}: {doc}")

# ---- 4. NDCG评估 ----
from sklearn.metrics import ndcg_score

# 真实相关性标签(0=不相关,1=相关,2=非常相关)
true_relevance = np.array([[2, 1, 0, 0, 0]])
predicted_scores = np.array([[0.9, 0.7, 0.3, 0.2, 0.1]])

ndcg = ndcg_score(true_relevance, predicted_scores, k=3)
print(f"\nNDCG@3: {ndcg:.4f}")

面试常问点

  1. BM25相比TF-IDF的改进?
  2. 词频饱和:避免某词出现100次比10次有10倍权重
  3. 文档长度归一化:短文档不因词少而被惩罚

  4. NDCG和MAP的区别?

  5. MAP:只考虑相关/不相关二值;NDCG:考虑多级相关性,且位置靠前权重更高

  6. 工业搜索系统架构?

  7. 召回层(BM25+向量双路)→ 粗排(LightGBM)→ 精排(BERT)→ 重排(多样性/商业策略)

  8. 向量检索FAISS有哪些索引类型?

  9. IndexFlatL2:暴力搜索,精确;IndexIVFFlat:聚类+倒排,快但近似;HNSW:图索引,精度速度均衡

速查表

组件推荐工具
关键词检索Elasticsearch / BM25Okapi
向量检索FAISS / Milvus / Weaviate
Embedding模型Sentence-BERT / BGE / E5
精排模型Cross-Encoder / BERT
评估NDCG / MAP / MRR