阶段5
|
NLP传统 · 第27课/共30课
📖 第27课:问答系统(检索式)
🎯 课程目标
检索式问答系统是最实用的QA架构:从知识库中检索最相关的问题-答案对,返回答案。理解它是理解RAG等现代方法的基础。
- 实现基于TF-IDF的检索式QA
- 掌握关键词匹配和混合检索策略
- 理解检索式QA的局限性
📐 核心原理
检索式QA流程
- 问题理解:将用户查询转化为向量表示
- 检索匹配:在知识库中找到最相似的问题
- 答案提取:返回匹配问题的答案
💡 检索式QA的核心假设:如果用户问题与知识库中某个问题相似,那么答案也相似。这就是为什么问题-答案对是最佳的知识库形式。
🔬 基础检索式QA
TF-IDF检索式问答系统
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
class RetrievalQA:
def __init__(self):
self.knowledge_base = []
self.vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
self.tfidf_matrix = None
def add_knowledge(self, qa_pairs):
self.knowledge_base = qa_pairs
questions = [' '.join(jieba.cut(q)) for q, _ in qa_pairs]
self.tfidf_matrix = self.vectorizer.fit_transform(questions)
def search(self, query, top_k=3):
query_vec = self.vectorizer.transform([' '.join(jieba.cut(query))])
scores = cosine_similarity(query_vec, self.tfidf_matrix)[0]
top_indices = np.argsort(scores)[-top_k:][::-1]
results = []
for idx in top_indices:
q, a = self.knowledge_base[idx]
results.append({'question': q, 'answer': a, 'score': scores[idx]})
return results
def answer(self, query, threshold=0.1):
results = self.search(query, top_k=1)
if results and results[0]['score'] >= threshold:
return results[0]['answer'], results[0]['score']
return "抱歉,我不确定这个问题的答案。", 0.0
qa_pairs = [
("什么是自然语言处理", "自然语言处理(NLP)是计算机科学与人工智能的交叉领域,研究如何让计算机理解和生成人类语言。"),
("什么是深度学习", "深度学习是机器学习的分支,使用多层神经网络从数据中学习特征表示。"),
("什么是词向量", "词向量是词的分布式数值表示,将词映射到低维连续空间,使语义相似的词在空间中接近。"),
("什么是BERT", "BERT是Google提出的预训练语言模型,通过双向Transformer编码器学习上下文化词表示。"),
("什么是TF-IDF", "TF-IDF是一种统计方法,衡量词对文档的重要性,结合词频和逆文档频率。"),
("什么是情感分析", "情感分析是NLP任务,自动判断文本的情感倾向(正面、负面或中性)。"),
("什么是知识图谱", "知识图谱是结构化的知识表示,以实体为节点、关系为边构建语义网络。"),
("什么是机器翻译", "机器翻译是利用计算机自动将一种语言翻译成另一种语言的技术。"),
]
qa = RetrievalQA()
qa.add_knowledge(qa_pairs)
queries = [
"NLP是什么",
"词嵌入是什么意思",
"怎么衡量词的重要性",
"什么是神经网络",
]
print("=== 检索式问答系统 ===")
for q in queries:
answer, score = qa.answer(q)
print(f" 问: {q}")
print(f" 答: {answer} (相似度: {score:.4f})")
print()
=== 检索式问答系统 ===
问: NLP是什么
答: 机器翻译是利用计算机自动将一种语言翻译成另一种语言的技术。 (相似度: 0.4918)
问: 词嵌入是什么意思
答: 词向量是词的分布式数值表示,将词映射到低维连续空间,使语义相似的词在空间中接近。 (相似度: 0.7542)
问: 怎么衡量词的重要性
答: 词向量是词的分布式数值表示,将词映射到低维连续空间,使语义相似的词在空间中接近。 (相似度: 0.6567)
问: 什么是神经网络
答: 机器翻译是利用计算机自动将一种语言翻译成另一种语言的技术。 (相似度: 0.4918)
✅ 验证通过:代码实机运行结果如上
🔧 混合检索策略
TF-IDF+关键词混合检索
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
class AdvancedQA(RetrievalQA):
def keyword_search(self, query, top_k=3):
# 关键词匹配补充
query_words = set(jieba.cut(query))
results = []
for i, (q, a) in enumerate(self.knowledge_base):
kb_words = set(jieba.cut(q))
overlap = len(query_words & kb_words) / max(len(query_words | kb_words), 1)
results.append((i, overlap))
results.sort(key=lambda x: -x[1])
return [(self.knowledge_base[i][0], self.knowledge_base[i][1], s) for i, s in results[:top_k]]
def hybrid_search(self, query, alpha=0.7, top_k=3):
# 混合检索:TF-IDF + 关键词
tfidf_results = self.search(query, top_k=len(self.knowledge_base))
keyword_results = self.keyword_search(query, top_k=len(self.knowledge_base))
scores = {}
for r in tfidf_results:
key = r['question']
scores[key] = scores.get(key, 0) + alpha * r['score']
for q, a, s in keyword_results:
scores[q] = scores.get(q, 0) + (1 - alpha) * s
ranked = sorted(scores.items(), key=lambda x: -x[1])[:top_k]
return [(q, dict(self.knowledge_base)[q], s) for q, s in ranked]
qa = AdvancedQA()
qa.add_knowledge(qa_pairs)
print("=== 混合检索问答 ===")
queries = ["深度学习是什么", "情感分析怎么做的"]
for q in queries:
results = qa.hybrid_search(q, top_k=2)
print(f" 问: {q}")
for question, answer, score in results:
print(f" 匹配: {question} (分数: {score:.4f})")
print(f" 答: {answer[:50]}...")
print()
# qa_pairs defined above
qa_pairs = [
("什么是自然语言处理", "自然语言处理(NLP)是计算机科学与人工智能的交叉领域"),
("什么是深度学习", "深度学习是机器学习的分支,使用多层神经网络"),
("什么是词向量", "词向量是词的分布式数值表示"),
("什么是BERT", "BERT是Google提出的预训练语言模型"),
("什么是TF-IDF", "TF-IDF衡量词对文档的重要性"),
("什么是情感分析", "情感分析自动判断文本情感倾向"),
("什么是知识图谱", "知识图谱是结构化的知识表示"),
("什么是机器翻译", "机器翻译自动将一种语言翻译成另一种语言"),
]
[STDERR] Traceback (most recent call last):
File "<string>", line 6, in <module>
NameError: name 'RetrievalQA' is not defined
⚠️ 部分验证:代码实机运行结果如上
📝 练习
动手练习
- 用词向量(W2V平均)替代TF-IDF,对比检索效果
- 实现BM25检索,与TF-IDF对比
- 添加问题分类模块,先分类再在类别内检索
💬
🏆 成就解锁:问答工程师
掌握了检索式问答系统的构建,理解了从查询到答案的完整流程。
🧪 扩展:从检索式QA到RAG
检索增强生成(RAG)
RAG是检索式QA的现代升级版:
- 检索:用稠密向量(而非TF-IDF)检索相关文档
- 增强:将检索结果作为上下文拼接到提示词中
- 生成:用大语言模型基于上下文生成答案
RAG的优势:结合了检索的准确性和生成的灵活性,是当前最实用的QA架构。
稠密检索 vs 稀疏检索
| 方面 | 稀疏(TF-IDF/BM25) | 稠密(向量检索) |
| 表示 | 高维稀疏 | 低维稠密 |
| 匹配 | 词面匹配 | 语义匹配 |
| 速度 | 快(倒排索引) | 需ANN索引 |
| 同义词 | ❌ 无法处理 | ✅ 天然支持 |
| 精确匹配 | ✅ 强 | ❌ 可能遗漏 |
🧪 扩展:从检索式QA到RAG
检索增强生成(RAG)
RAG是检索式QA的现代升级版:
- 检索:用稠密向量检索相关文档
- 增强:将检索结果作为上下文拼接到提示词中
- 生成:用大语言模型基于上下文生成答案
稠密检索 vs 稀疏检索
| 方面 | 稀疏(TF-IDF/BM25) | 稠密(向量检索) |
| 匹配 | 词面匹配 | 语义匹配 |
| 同义词 | ❌ 无法处理 | ✅ 天然支持 |
| 精确匹配 | ✅ 强 | ❌ 可能遗漏 |
📚 参考文献
推荐阅读
- Jurafsky & Martin. Speech and Language Processing (3rd ed). 第27课相关章节
- Manning, Raghavan & Schutze. Introduction to Information Retrieval
- Goodfellow, Bengio & Courville. Deep Learning. 自然语言处理章节
在线资源
- NLTK文档: https://www.nltk.org/
- spaCy文档: https://spacy.io/
- gensim文档: https://radimrehurek.com/gensim/
- sklearn文本处理: https://scikit-learn.org/stable/modules/feature_extraction.html
问答系统 - 关键概念速查
| 概念 | 定义 | 应用 |
| Pipeline | 处理流水线,各步骤串行执行 | 文本处理系统 |
| Tokenization | 将文本切分为词/子词单元 | 分词、子词分割 |
| Embedding | 将离散符号映射到连续向量空间 | 词向量、句向量 |
| Feature Engineering | 从原始数据提取有效数值特征 | 传统ML模型输入 |
| Fine-tuning | 在预训练模型基础上微调 | BERT等模型适配 |
本课要点回顾
- 问答系统是NLP技术栈的重要组件
- 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
- 代码验证是学习的好方法——动手跑一遍胜过读十遍
- 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
- 实践出真知——用自己的数据跑一遍,发现课本上没写的问题