现代问答系统核心组件深度解析:从检索到生成的架构演进
引言
问答系统作为自然语言处理领域的重要研究方向,已经从早期的基于规则的系统发展到如今基于深度学习的端到端解决方案。随着大语言模型的兴起,问答系统的架构设计面临着新的挑战和机遇。本文将深入探讨现代问答系统的核心组件,分析其技术实现细节,并分享在实际工业场景中的优化经验。
问答系统架构概览
传统流水线架构 vs 端到端架构
传统问答系统通常采用模块化的流水线设计,而现代系统则趋向于端到端的一体化解决方案。
class TraditionalQAPipeline:
def __init__(self):
self.retriever = DenseRetriever()
self.reader = ReaderModel()
self.reranker = Reranker()
def answer_question(self, question: str) -> str:
# 检索相关文档
documents = self.retriever.retrieve(question, top_k=10)
# 阅读理解
candidate_answers = self.reader.extract_answers(question, documents)
# 重排序
ranked_answers = self.reranker.rerank(question, candidate_answers)
return ranked_answers[0]
混合检索架构
在实际应用中,混合检索架构往往能提供更好的效果和鲁棒性:
class HybridRetrievalSystem:
def __init__(self):
self.sparse_retriever = BM25Retriever()
self.dense_retriever = DenseRetriever()
self.fusion_strategy = ReciprocalRankFusion()
def hybrid_retrieve(self, query: str, top_k: int = 20):
sparse_results = self.sparse_retriever.retrieve(query, top_k * 2)
dense_results = self.dense_retriever.retrieve(query, top_k * 2)
fused_results = self.fusion_strategy.fuse(
sparse_results, dense_results
)
return fused_results[:top_k]
检索组件深度优化
稠密检索的技术演进
稠密检索已经成为现代问答系统的核心组件,其性能直接影响整体系统的效果。
import torch
import torch.nn as nn
from transformers import AutoModel, AutoTokenizer
class AdvancedDenseRetriever:
def __init__(self, model_name: str = "bert-base-uncased"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.query_encoder = AutoModel.from_pretrained(model_name)
self.doc_encoder = AutoModel.from_pretrained(model_name)
self.margin = 0.1
# 投影层,将向量映射到统一的嵌入空间
self.query_proj = nn.Linear(768, 256)
self.doc_proj = nn.Linear(768, 256)
def encode_queries(self, queries: List[str]) -> torch.Tensor:
inputs = self.tokenizer(
queries, padding=True, truncation=True,
max_length=128, return_tensors="pt"
)
with torch.no_grad():
outputs = self.query_encoder(**inputs)
embeddings = self._mean_pooling(outputs, inputs['attention_mask'])
return self.query_proj(embeddings)
def encode_documents(self, docs: List[str]) -> torch.Tensor:
inputs = self.tokenizer(
docs, padding=True, truncation=True,
max_length=512, return_tensors="pt"
)
with torch.no_grad():
outputs = self.doc_encoder(**inputs)
embeddings = self._mean_pooling(outputs, inputs['attention_mask'])
return self.doc_proj(embeddings)
def _mean_pooling(self, model_output, attention_mask):
token_embeddings = model_output[0]
input_mask_expanded = (
attention_mask
.unsqueeze(-1)
.expand(token_embeddings.size())
.float()
)
return torch.sum(
token_embeddings * input_mask_expanded, 1
) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
负样本挖掘策略
有效的负样本挖掘是提升检索器性能的关键:
class HardNegativeMining:
def __init__(self, retriever, num_hard_negatives: int = 5):
self.retriever = retriever
self.num_hard_negatives = num_hard_negatives
def mine_hard_negatives(self, query: str, positive_docs: List[str]):
# 检索与查询相似但不是正确答案的文档
retrieved_docs = self.retriever.retrieve(query, top_k=50)
hard_negatives = []
for doc in retrieved_docs:
if not self._is_relevant(doc, positive_docs):
hard_negatives.append(doc)
if len(hard_negatives) >= self.num_hard_negatives:
break
return hard_negatives
def _is_relevant(self, doc: str, positive_docs: List[str]) -> bool:
# 使用简单的文本相似度判断相关性
doc_embedding = self.retriever.encode_documents([doc])
pos_embeddings = self.retriever.encode_documents(positive_docs)
similarities = torch.matmul(doc_embedding, pos_embeddings.T)
return torch.any(similarities > 0.8)
阅读理解组件的高级特性
多任务阅读理解框架
现代阅读理解系统往往需要处理多种类型的问答任务:
class MultiTaskReader:
def __init__(self, model_name: str = "roberta-large"):
self.model = AutoModelForQuestionAnswering.from_pretrained(model_name)
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.answer_type_classifier = nn.Linear(768, 4) # 答案类型分类
def forward(self, question: str, context: str):
inputs = self.tokenizer(
question, context,
truncation=True,
padding=True,
max_length=512,
return_tensors="pt"
)
outputs = self.model(**inputs, output_hidden_states=True)
# 答案跨度预测
start_logits = outputs.start_logits
end_logits = outputs.end_logits
# 答案类型预测
hidden_states = outputs.hidden_states[-1]
cls_embedding = hidden_states[:, 0, :]
answer_type_logits = self.answer_type_classifier(cls_embedding)
return {
'start_logits': start_logits,
'end_logits': end_logits,
'answer_type_logits': answer_type_logits
}
基于置信度校准的答案选择
class ConfidenceCalibratedAnswerSelector:
def __init__(self, temperature: float = 1.0):
self.temperature = temperature
def calibrate_confidence(self, start_logits, end_logits,
answer_type_logits, context_length: int):
# 应用温度缩放进行置信度校准
start_probs = self._temperature_scale(start_logits)
end_probs = self._temperature_scale(end_logits)
# 计算联合概率
joint_probs = start_probs.unsqueeze(2) * end_probs.unsqueeze(1)
# 考虑答案类型置信度
answer_type_probs = F.softmax(answer_type_logits, dim=-1)
type_confidence = answer_type_probs[:, 0] # 提取答案存在的置信度
# 应用约束:结束位置不能在开始位置之前
mask = torch.triu(torch.ones_like(joint_probs))
joint_probs = joint_probs * mask
# 找到最佳答案跨度
max_prob, best_span = self._find_best_span(joint_probs)
# 综合置信度
final_confidence = max_prob * type_confidence
return best_span, final_confidence
def _temperature_scale(self, logits):
return F.softmax(logits / self.temperature, dim=-1)
生成式问答的创新方法
检索增强生成 (RAG) 架构
class RAGQASystem:
def __init__(self, retriever, generator):
self.retriever = retriever
self.generator = generator
def generate_answer(self, question: str, max_length: int = 100):
# 检索相关文档
relevant_docs = self.retriever.retrieve(question, top_k=5)
# 构建生成器的输入
context = " ".join([doc['content'] for doc in relevant_docs])
input_text = f"基于以下信息回答问题:{context} 问题:{question} 答案:"
# 生成答案
generated_answer = self.generator.generate(
input_text,
max_length=max_length,
num_return_sequences=1,
temperature=0.7,
do_sample=True
)
return {
'answer': generated_answer[0]['generated_text'],
'source_documents': relevant_docs
}
融合检索与生成的端到端训练
class EndToEndRAG(nn.Module):
def __init__(self, retriever, generator):
super().__init__()
self.retriever = retriever
self.generator = generator
# 连接检索器和生成器的适配层
self.adapter = nn.Linear(
retriever.embedding_dim + generator.hidden_size,
generator.hidden_size
)
def forward(self, question: str, target_answer: str = None):
# 检索文档嵌入
doc_embeddings = self.retriever.encode_queries([question])
# 生成器输入编码
generator_inputs = self.generator.tokenizer(
question, return_tensors="pt"
)
# 融合检索信息
expanded_doc_embeds = doc_embeddings.expand(
generator_inputs.input_ids.size(0), -1
)
# 前向传播
if target_answer is not None:
# 训练模式
labels = self.generator.tokenizer(
target_answer, return_tensors="pt"
).input_ids
outputs = self.generator(
input_ids=generator_inputs.input_ids,
attention_mask=generator_inputs.attention_mask,
labels=labels,
encoder_hidden_states=expanded_doc_embeds
)
return outputs.loss
else:
# 推理模式
outputs = self.generator.generate(
input_ids=generator_inputs.input_ids,
attention_mask=generator_inputs.attention_mask,
encoder_hidden_states=expanded_doc_embeds,
max_length=100
)
return outputs
多模态问答系统
图像-文本联合理解
class MultimodalQASystem:
def __init__(self, text_model, vision_model, fusion_model):
self.text_model = text_model
self.vision_model = vision_model
self.fusion_model = fusion_model
def answer_question(self, question: str, image: Image) -> str:
# 文本特征提取
text_features = self.text_model.encode_text(question)
# 视觉特征提取
image_features = self.vision_model.encode_image(image)
# 多模态融合
fused_features = self.fusion_model.fuse(
text_features, image_features
)
# 答案生成
answer = self.fusion_model.generate_answer(fused_features)
return answer
class CrossModalFusion(nn.Module):
def __init__(self, text_dim: int, image_dim: int, hidden_dim: int):
super().__init__()
self.text_proj = nn.Linear(text_dim, hidden_dim)
self.image_proj = nn.Linear(image_dim, hidden_dim)
self.cross_attention = nn.MultiheadAttention(hidden_dim, num_heads=8)
def fuse(self, text_features, image_features):
text_proj = self.text_proj(text_features)
image_proj = self.image_proj(image_features)
# 交叉注意力机制
attended_text, _ = self.cross_attention(
text_proj, image_proj, image_proj
)
attended_image, _ = self.cross_attention(
image_proj, text_proj, text_proj
)
# 特征融合
fused_features = torch.cat([attended_text, attended_image], dim=-1)
return fused_features
系统优化与部署考量
检索性能优化
class OptimizedRetrievalSystem:
def __init__(self, encoder, index_type: str = "HNSW"):
self.encoder = encoder
self.index = self._build_index(index_type)
def _build_index(self, index_type: str):
if index_type == "HNSW":
# 使用Hierarchical Navigable Small World图进行近似最近邻搜索
index = hnswlib.Index(space='cosine', dim=768)
index.init_index(max_elements=1000000, ef_construction=200, M=16)
return index
elif index_type == "IVF":
# 使用倒排文件索引
quantizer = faiss.IndexFlatIP(768)
return faiss.IndexIVFFlat(quantizer, 768, 100)
def batch_retrieve(self, queries: List[str], top_k: int = 10):
# 批量编码查询
query_embeddings = self.encoder.encode_queries(queries)
# 批量检索
distances, indices = self.index.knn_query(
query_embeddings, k=top_k
)
return [
{
'doc_id': idx,
'score': 1 - dist, # 转换为相似度分数
'content': self.doc_store.get_content(idx)
}
for dist, idx in zip(distances, indices)
]
缓存与预热策略
class SmartCacheSystem:
def __init__(self, max_size: int = 10000):
self.cache = LRUCache(max_size=max_size)
self.query_analyzer = QueryAnalyzer()
def get_cached_answer(self, question: str):
# 查询规范化
normalized_query = self.query_analyzer.normalize(question)
# 语义缓存查找
if normalized_query in self.cache:
return self.cache[normalized_query]
# 相似查询查找
similar_queries = self.find_similar_queries(normalized_query)
if similar_queries:
best_match = max(similar_queries, key=lambda x: x[1])
if best_match[1] > 0.9: # 相似度阈值
return self.cache[best_match[0]]
return None
def warmup_cache(self, common_queries: List[str]):
"""预热缓存,预加载常见查询的结果"""
for query in common_queries:
if query not in self.cache:
# 异步预计算答案
asyncio.create_task(self.prefetch_answer(query))
评估与监控体系
多维度评估框架
class QAEvaluationFramework:
def __init__(self):
self.metrics = {
'retrieval': RetrievalMetrics(),
'reading': ReadingMetrics(),
'generation': GenerationMetrics()
}
def comprehensive_eval(self, system, test_dataset):
results = {}
# 检索效果评估
retrieval_scores = self.metrics['retrieval'].evaluate(
system.retriever, test_dataset
)
results.update(retrieval_scores)
# 阅读理解评估
reading_scores = self.metrics['reading'].evaluate(
system.reader, test_dataset
)
results.update(reading_scores)
# 生成质量评估
generation_scores = self.metrics['generation'].evaluate(
system.generator, test_dataset
)
results.update(generation_scores)
return results
class OnlineMonitoring:
def __init__(self, system):
1198

被折叠的 条评论
为什么被折叠?



