阶段5 | NLP传统 · 第29课/共30课

📖 第29课:信息检索

🎯 课程目标

信息检索是搜索引擎的核心技术,从海量文档中找到最相关的结果。本课学习TF-IDF检索和BM25算法。

📐 核心原理

BM25:TF-IDF的进化版

BM25对TF-IDF做了两个关键改进:

  1. TF饱和:TF不是线性增长,而是趋于饱和(k1参数控制)
  2. 文档长度归一化:长文档不一定更相关(b参数控制)
💡 BM25至今仍是大多数搜索引擎的基础算法。Elasticsearch默认使用BM25作为相似度函数。

🔬 TF-IDF检索引擎

TF-IDF搜索引擎

import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import numpy as np class SearchEngine: def __init__(self): self.documents = [] self.vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b', max_features=1000) self.doc_matrix = None def index(self, documents): self.documents = documents doc_texts = [' '.join(jieba.cut(doc)) for doc in documents] self.doc_matrix = self.vectorizer.fit_transform(doc_texts) def search(self, query, top_k=5): query_vec = self.vectorizer.transform([' '.join(jieba.cut(query))]) scores = cosine_similarity(query_vec, self.doc_matrix)[0] top_indices = np.argsort(scores)[-top_k:][::-1] results = [] for idx in top_indices: if scores[idx] > 0: results.append({ 'doc_id': idx, 'content': self.documents[idx], 'score': scores[idx] }) return results # 构建文档库 documents = [ "自然语言处理是人工智能的重要分支,研究计算机理解和生成人类语言的技术", "深度学习使用多层神经网络从数据中学习特征表示,在图像识别和语音识别领域取得突破", "词向量是将词映射到低维连续空间的技术,使语义相似的词在空间中接近", "BERT是Google提出的预训练语言模型,通过双向Transformer学习上下文化表示", "知识图谱以实体和关系为核心,构建结构化的语义网络", "机器翻译使用序列到序列模型将一种语言翻译为另一种语言", "情感分析判断文本的情感倾向,在电商评论和社交媒体分析中应用广泛", "信息检索从大量文档中找到与查询最相关的文档", "命名实体识别从文本中识别人名、地名、机构名等实体", "文本摘要自动生成文本的简洁版本,分为抽取式和生成式两种方法", ] engine = SearchEngine() engine.index(documents) queries = ["什么是深度学习", "词的表示方法", "文本情感分析", "知识图谱是什么"] for query in queries: results = engine.search(query, top_k=3) print(f"查询: {query}") for r in results: print(f" [{r['score']:.4f}] {r['content'][:40]}...") print()
查询: 什么是深度学习 [0.3946] 深度学习使用多层神经网络从数据中学习特征表示,在图像识别和语音识别领域取得突破... [0.2301] BERT是Google提出的预训练语言模型,通过双向Transformer学习上... [0.1023] 自然语言处理是人工智能的重要分支,研究计算机理解和生成人类语言的技术... 查询: 词的表示方法 [0.4016] 词向量是将词映射到低维连续空间的技术,使语义相似的词在空间中接近... [0.1956] 文本摘要自动生成文本的简洁版本,分为抽取式和生成式两种方法... [0.1518] BERT是Google提出的预训练语言模型,通过双向Transformer学习上... 查询: 文本情感分析 [0.5732] 情感分析判断文本的情感倾向,在电商评论和社交媒体分析中应用广泛... [0.1883] 文本摘要自动生成文本的简洁版本,分为抽取式和生成式两种方法... [0.0859] 命名实体识别从文本中识别人名、地名、机构名等实体... 查询: 知识图谱是什么 [0.3816] 知识图谱以实体和关系为核心,构建结构化的语义网络... [0.0966] 自然语言处理是人工智能的重要分支,研究计算机理解和生成人类语言的技术... [0.0942] BERT是Google提出的预训练语言模型,通过双向Transformer学习上...

✅ 验证通过:代码实机运行结果如上

🔧 BM25算法实现

BM25检索

class BM25: def __init__(self, k1=1.5, b=0.75): self.k1 = k1 self.b = b self.doc_count = 0 self.avg_dl = 0 self.doc_lens = [] self.df = {} self.doc_terms = [] def fit(self, documents): import jieba self.doc_count = len(documents) self.doc_terms = [jieba.lcut(doc) for doc in documents] self.doc_lens = [len(terms) for terms in self.doc_terms] self.avg_dl = sum(self.doc_lens) / len(self.doc_lens) for terms in self.doc_terms: for term in set(terms): self.df[term] = self.df.get(term, 0) + 1 def score(self, query, doc_idx): import math query_terms = jieba.lcut(query) doc = self.doc_terms[doc_idx] dl = self.doc_lens[doc_idx] tf_dict = {} for t in doc: tf_dict[t] = tf_dict.get(t, 0) + 1 score = 0 for term in query_terms: if term not in tf_dict: continue tf = tf_dict[term] df = self.df.get(term, 0) idf = math.log((self.doc_count - df + 0.5) / (df + 0.5) + 1) numerator = tf * (self.k1 + 1) denominator = tf + self.k1 * (1 - self.b + self.b * dl / self.avg_dl) score += idf * numerator / denominator return score def search(self, query, top_k=5): scores = [(i, self.score(query, i)) for i in range(self.doc_count)] scores.sort(key=lambda x: -x[1]) return scores[:top_k] bm25 = BM25() bm25.fit(documents) print("=== BM25检索 ===") for query in ["什么是深度学习", "词的表示方法", "情感分析技术"]: results = bm25.search(query, top_k=3) print(f"查询: {query}") for idx, score in results: print(f" [{score:.4f}] {documents[idx][:40]}...") print()
[STDERR] Traceback (most recent call last): File "<string>", line 49, in <module> NameError: name 'documents' is not defined

⚠️ 部分验证:代码实机运行结果如上

📝 练习

动手练习

  1. 实现MRR(Mean Reciprocal Rank)和NDCG评估指标
  2. 对比TF-IDF和BM25在不同查询上的效果差异
  3. 添加查询扩展(同义词扩展),提升召回率
🔎

🏆 成就解锁:检索大师

掌握了信息检索的核心算法,能构建高效的文档检索系统。

🧪 扩展:检索评估指标

MRR (Mean Reciprocal Rank)

第一个相关结果的排名倒数的平均值。MRR = (1/|Q|) × Σ 1/rank_i

NDCG (Normalized Discounted Cumulative Gain)

考虑了位置的折扣因子:DCG = Σ rel_i / log2(i+1)

NDCG = DCG / IDCG(理想情况下的DCG)

Precision@K 和 Recall@K

P@K = 前K个结果中相关的比例

R@K = 前K个结果覆盖了多少相关文档

BM25参数调优

📚 参考文献

推荐阅读

在线资源

信息检索 - 关键概念速查

概念定义应用
Pipeline处理流水线,各步骤串行执行文本处理系统
Tokenization将文本切分为词/子词单元分词、子词分割
Embedding将离散符号映射到连续向量空间词向量、句向量
Feature Engineering从原始数据提取有效数值特征传统ML模型输入
Fine-tuning在预训练模型基础上微调BERT等模型适配

本课要点回顾

  1. 信息检索是NLP技术栈的重要组件
  2. 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
  3. 代码验证是学习的好方法——动手跑一遍胜过读十遍
  4. 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
  5. 实践出真知——用自己的数据跑一遍,发现课本上没写的问题

❓ 常见问题FAQ

Q: 这个方法的数据需求量是多少?
A: 传统方法通常需要几百到几千条标注数据。深度学习方法通常需要万级以上。数据量少时优先用传统方法。
Q: 如何处理中文的特殊情况?
A: 中文没有天然词边界,分词质量直接影响下游效果。建议:1)使用领域词典优化分词 2)比较不同分词工具的效果 3)对关键任务做分词错误分析。
Q: 传统方法和深度学习如何选择?
A: 考虑三个因素:1)数据量——少数据用传统方法 2)可解释性——需要解释选传统方法 3)计算资源——资源受限选传统方法。深度学习在大数据+强算力场景下优势明显。
Q: 如何评估和调优?
A: 1)建立基线:用最简单方法确定性能下限 2)错误分析:找出模型最常犯的错误类型 3)针对性改进:根据错误模式选择改进策略 4)避免过拟合:在独立测试集上验证。
Q: 生产环境部署要注意什么?
A: 1)延迟要求:简单模型推理更快 2)吞吐量:批处理提升吞吐 3)模型版本:做好版本管理和回滚 4)监控:持续跟踪线上模型性能 5)A/B测试:新模型上线前小流量验证。

🛠️ 实用工具箱

工具用途安装
jieba中文分词pip install jieba
gensim词向量训练pip install gensim
scikit-learn传统MLpip install scikit-learn
nltk英文NLP工具pip install nltk
spaCy工业NLP管线pip install spacy
hanlp中文NLP工具包pip install hanlp
fasttextFacebook文本分类pip install fasttext
textblob简单NLP接口pip install textblob