RAG 检索召回太宽泛如何基于语义压缩提升结果相关性

好的,我们开始。

RAG 检索召回太宽泛:语义压缩提升相关性

大家好,今天我们来探讨一个在构建 RAG(Retrieval-Augmented Generation)系统时经常遇到的问题:检索召回太宽泛,导致结果相关性不高。我们会重点讨论如何通过语义压缩技术来提升检索结果的相关性,让 RAG 系统更好地服务于我们的应用。

一、RAG 系统简介与宽泛召回的挑战

RAG 系统,简单来说,就是先从外部知识库检索相关信息,然后将检索到的信息与用户查询一起输入到生成模型中,生成最终的答案或内容。它结合了信息检索和生成模型的优势,使得模型能够利用外部知识,避免幻觉,并生成更准确、更全面的内容。

一个典型的 RAG 流程包括以下几个步骤:

  1. 文档准备: 将原始文档分割成更小的块(chunks),例如段落或句子。
  2. 向量化: 使用嵌入模型(embedding model)将每个 chunk 转换为向量表示。
  3. 索引构建: 将向量化的 chunk 存储到向量数据库中,构建索引,以便快速检索。
  4. 查询向量化: 将用户查询转换为向量表示,使用与文档向量化相同的嵌入模型。
  5. 检索: 在向量数据库中搜索与查询向量最相似的 chunk。
  6. 生成: 将检索到的 chunk 与用户查询一起输入到生成模型中,生成最终的答案。

然而,在实际应用中,我们经常会遇到检索召回太宽泛的问题。这意味着检索到的 chunk 中包含了很多与用户查询不太相关的信息,这会影响生成模型的性能,导致生成的答案不够准确、不够聚焦,甚至包含错误信息。

造成宽泛召回的原因有很多,例如:

  • 嵌入模型不够精确: 嵌入模型无法准确捕捉文档和查询的语义信息,导致相似度计算不准确。
  • chunk 大小不合适: chunk 太大,包含太多无关信息;chunk 太小,丢失上下文信息。
  • 向量数据库的距离度量方式不合适: 不同的距离度量方式对相似度的计算结果有影响。
  • 缺乏有效的过滤机制: 没有对检索到的 chunk 进行有效的过滤,将所有结果都传递给生成模型。

二、语义压缩的核心思想与方法

为了解决宽泛召回的问题,我们可以采用语义压缩技术。语义压缩的核心思想是:在不损失关键信息的前提下,减少文档的冗余信息,从而提高检索的准确性和效率。

语义压缩的方法有很多,我们可以从以下几个方面入手:

  1. 关键词提取与过滤: 提取文档中的关键词,并根据关键词的相关性对文档进行过滤。
  2. 句子压缩: 删除句子中的冗余信息,只保留关键信息。
  3. 文档摘要: 生成文档的摘要,只保留文档的核心内容。
  4. 知识图谱构建: 将文档转换为知识图谱,只保留实体和关系信息。

接下来,我们将分别介绍这些方法的具体实现。

2.1 关键词提取与过滤

关键词提取是指从文档中提取出最能代表文档主题的词语。我们可以使用多种算法进行关键词提取,例如 TF-IDF、TextRank、YAKE 等。提取到关键词后,我们可以根据关键词的相关性对文档进行过滤。

代码示例 (使用 YAKE 提取关键词):

import yake

def extract_keywords(text, n=5):
  """
  使用 YAKE 提取关键词。

  Args:
    text: 文档文本。
    n: 提取的关键词数量。

  Returns:
    关键词列表。
  """
  language = "en"
  max_ngram_size = 3
  deduplication_thresold = 0.9
  deduplication_algo = 'seqm'
  windowSize = 1
  numOfKeywords = n

  custom_kw_extractor = yake.KeywordExtractor(lan=language, n=max_ngram_size, dedupLim=deduplication_thresold, dedupFunc=deduplication_algo, windowsSize=windowSize, top=numOfKeywords, features=None)
  keywords = custom_kw_extractor.extract_keywords(text)
  return keywords

def filter_documents(query, documents, keywords_per_document=5, threshold=0.5):
  """
  根据关键词的相关性过滤文档。

  Args:
    query: 用户查询。
    documents: 文档列表。
    keywords_per_document: 每个文档提取的关键词数量。
    threshold: 相关性阈值。

  Returns:
    过滤后的文档列表。
  """
  query_keywords = [keyword for keyword, score in extract_keywords(query, keywords_per_document)]
  filtered_documents = []
  for doc in documents:
    doc_keywords = [keyword for keyword, score in extract_keywords(doc, keywords_per_document)]
    # 计算查询关键词和文档关键词的重合度
    overlap = len(set(query_keywords) & set(doc_keywords)) / len(query_keywords)
    if overlap >= threshold:
      filtered_documents.append(doc)
  return filtered_documents

# 示例用法
documents = [
    "This is a document about artificial intelligence.",
    "This is a document about machine learning.",
    "This is a document about natural language processing.",
    "This is a document about computer vision.",
    "This is a document about data science."
]
query = "artificial intelligence and machine learning"
filtered_documents = filter_documents(query, documents)
print(f"Filtered Documents: {filtered_documents}")

2.2 句子压缩

句子压缩是指删除句子中的冗余信息,只保留关键信息。我们可以使用多种方法进行句子压缩,例如基于规则的方法、基于统计的方法、基于深度学习的方法等。

代码示例 (使用 Transformers 库和 BART 模型进行句子压缩):

from transformers import BartTokenizer, BartForConditionalGeneration

def compress_sentence(sentence, compression_ratio=0.7):
  """
  使用 BART 模型压缩句子。

  Args:
    sentence: 要压缩的句子。
    compression_ratio: 压缩比例,例如 0.7 表示将句子压缩到原来的 70%。

  Returns:
    压缩后的句子。
  """
  model_name = "facebook/bart-large-cnn" # 可以根据需求选择其他 BART 模型
  tokenizer = BartTokenizer.from_pretrained(model_name)
  model = BartForConditionalGeneration.from_pretrained(model_name)

  inputs = tokenizer([sentence], max_length=1024, return_tensors="pt", truncation=True)
  summary_ids = model.generate(inputs["input_ids"], num_beams=4, length_penalty=2.0, max_length=int(len(sentence.split()) * compression_ratio) + 10, min_length=int(len(sentence.split()) * compression_ratio) - 5, early_stopping=True)
  compressed_sentence = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
  return compressed_sentence

# 示例用法
sentence = "The quick brown fox jumps over the lazy dog, which is sleeping under the tree and dreaming about a delicious bone."
compressed_sentence = compress_sentence(sentence)
print(f"Original Sentence: {sentence}")
print(f"Compressed Sentence: {compressed_sentence}")

2.3 文档摘要

文档摘要是指生成文档的摘要,只保留文档的核心内容。我们可以使用多种方法生成文档摘要,例如抽取式摘要、生成式摘要等。

代码示例 (使用 Transformers 库和 T5 模型生成文档摘要):

from transformers import T5Tokenizer, T5ForConditionalGeneration

def generate_summary(text, max_length=150):
  """
  使用 T5 模型生成文档摘要。

  Args:
    text: 文档文本。
    max_length: 摘要的最大长度。

  Returns:
    文档摘要。
  """
  model_name = "t5-small" # 可以根据需求选择其他 T5 模型
  tokenizer = T5Tokenizer.from_pretrained(model_name)
  model = T5ForConditionalGeneration.from_pretrained(model_name)

  inputs = tokenizer.encode("summarize: " + text, return_tensors="pt", max_length=1024, truncation=True)
  summary_ids = model.generate(inputs, max_length=max_length, min_length=30, length_penalty=2.0, num_beams=4, early_stopping=True)
  summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
  return summary

# 示例用法
document = """
Artificial intelligence (AI) is revolutionizing various aspects of our lives. From self-driving cars to personalized medicine, AI is transforming industries and creating new opportunities. Machine learning, a subset of AI, enables computers to learn from data without being explicitly programmed. Deep learning, a more advanced form of machine learning, uses artificial neural networks with multiple layers to analyze data and make predictions. Natural language processing (NLP) allows computers to understand and process human language, enabling applications such as chatbots and machine translation. Computer vision enables computers to "see" and interpret images and videos, enabling applications such as facial recognition and object detection.
"""
summary = generate_summary(document)
print(f"Original Document: {document}")
print(f"Summary: {summary}")

2.4 知识图谱构建

知识图谱是一种结构化的知识表示形式,它将实体和关系表示为节点和边。我们可以将文档转换为知识图谱,只保留实体和关系信息,从而减少文档的冗余信息。

代码示例 (使用 spaCy 和 NetworkX 构建知识图谱):

import spacy
import networkx as nx
import matplotlib.pyplot as plt

def build_knowledge_graph(text):
  """
  使用 spaCy 和 NetworkX 构建知识图谱。

  Args:
    text: 文档文本。

  Returns:
    知识图谱。
  """
  nlp = spacy.load("en_core_web_sm")
  doc = nlp(text)

  graph = nx.Graph()

  for token in doc:
    # 提取实体和关系
    if token.dep_ in ("nsubj", "dobj"): # 主语和宾语
      subject = token.text
      object_ = [t.text for t in token.head.rights if t.dep_ in ("dobj", "attr")] #直接宾语或属性
      if object_:
        relation = token.head.text
        object_ = object_[0]
        graph.add_edge(subject, object_, relation=relation)

  return graph

def visualize_knowledge_graph(graph):
  """
  可视化知识图谱。

  Args:
    graph: 知识图谱。
  """
  pos = nx.spring_layout(graph)
  nx.draw(graph, pos, with_labels=True, node_color="skyblue", node_size=1500, font_size=10, font_weight="bold")
  edge_labels = nx.get_edge_attributes(graph, "relation")
  nx.draw_networkx_edge_labels(graph, pos, edge_labels=edge_labels, font_size=8)
  plt.show()

# 示例用法
document = "Artificial intelligence is a branch of computer science. Machine learning is a subset of artificial intelligence. Deep learning is a type of machine learning."
graph = build_knowledge_graph(document)
visualize_knowledge_graph(graph)

三、将语义压缩应用于 RAG 系统

现在,让我们看看如何将语义压缩技术应用于 RAG 系统,以提升检索结果的相关性。

  1. 文档预处理阶段: 在将文档分割成 chunk 之后,我们可以使用语义压缩技术对每个 chunk 进行处理,例如提取关键词、压缩句子、生成摘要等。
  2. 向量化阶段: 对压缩后的 chunk 进行向量化,而不是对原始 chunk 进行向量化。
  3. 检索阶段: 使用压缩后的 chunk 的向量进行检索。
  4. 后处理阶段: 可以对检索到的 chunk 进行进一步的过滤,例如根据关键词的相关性进行排序。

3.1 RAG 系统流程图(包含语义压缩)

[原始文档] --> [Chunking] --> [语义压缩 (关键词提取/句子压缩/文档摘要/知识图谱构建)] --> [向量化] --> [向量数据库索引]

[用户查询] --> [向量化] --> [检索] --> [可选: 后处理 (排序/过滤)] --> [生成模型] --> [最终答案]

3.2 代码示例 (整合关键词提取和 RAG 流程):

from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.llms import OpenAI
from langchain.chains import RetrievalQA
import os

# 假设我们已经有了 extract_keywords 和 filter_documents 函数

# 设置 OpenAI API 密钥
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"

def rag_with_keyword_filtering(documents, query, keywords_per_document=5, threshold=0.5):
    """
    使用关键词过滤的 RAG 系统。

    Args:
        documents: 文档列表。
        query: 用户查询。
        keywords_per_document: 每个文档提取的关键词数量。
        threshold: 相关性阈值。

    Returns:
        生成模型生成的答案。
    """

    # 1. 关键词过滤
    filtered_documents = filter_documents(query, documents, keywords_per_document, threshold)

    # 2. Chunking
    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
    texts = text_splitter.create_documents(filtered_documents) # 需要将filtered_documents 转化成字符串

    # 3. 向量化
    embeddings = OpenAIEmbeddings()
    db = Chroma.from_documents(texts, embeddings) # 需要传入Document 对象

    # 4. 检索
    qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=db.as_retriever())

    # 5. 生成
    answer = qa.run(query)
    return answer

# 示例用法
documents = [
    "This is a document about artificial intelligence and its applications in healthcare.",
    "This is a document about machine learning algorithms for image recognition.",
    "This is a document about natural language processing techniques for sentiment analysis.",
    "This is a document about computer vision and its use in autonomous vehicles.",
    "This is a document about data science and its role in business intelligence."
]

query = "What are the applications of artificial intelligence in healthcare?"
answer = rag_with_keyword_filtering(documents, query)
print(f"Answer: {answer}")

四、实验结果与分析

为了验证语义压缩的有效性,我们可以进行实验。我们可以使用不同的数据集和不同的语义压缩方法,比较 RAG 系统的性能。

我们可以使用以下指标来评估 RAG 系统的性能:

  • 准确率: 生成的答案的准确程度。
  • 召回率: 检索到的相关文档的比例。
  • F1 值: 准确率和召回率的调和平均值。
  • 相关性: 检索到的文档与用户查询的相关程度。

通过实验,我们可以发现,语义压缩可以显著提高 RAG 系统的性能。例如,使用关键词提取可以减少检索到的无关文档,提高检索的准确率;使用句子压缩可以减少文档的冗余信息,提高生成模型的效率;使用文档摘要可以减少文档的长度,提高检索的速度。

表格:不同语义压缩方法对 RAG 系统性能的影响 (示例)

语义压缩方法 准确率 召回率 F1 值 相关性
0.70 0.80 0.75 0.65
关键词提取 0.80 0.75 0.77 0.75
句子压缩 0.75 0.85 0.80 0.70
文档摘要 0.85 0.70 0.77 0.80
知识图谱构建 0.90 0.65 0.76 0.85

五、总结与展望

今天,我们讨论了 RAG 系统中宽泛召回的问题,并重点介绍了如何通过语义压缩技术来提升检索结果的相关性。我们介绍了关键词提取、句子压缩、文档摘要、知识图谱构建等多种语义压缩方法,并提供了相应的代码示例。通过实验,我们可以发现,语义压缩可以显著提高 RAG 系统的性能。

未来,我们可以进一步研究以下几个方面:

  • 更先进的语义压缩算法: 例如,使用更先进的深度学习模型进行句子压缩和文档摘要。
  • 自适应的语义压缩: 根据不同的文档和查询,选择不同的语义压缩方法。
  • 端到端的 RAG 系统优化: 将语义压缩与其他 RAG 系统组件进行联合优化,例如嵌入模型和生成模型。
  • 结合Prompt Engineering: 调整Prompt,让生成模型更好地利用压缩后的信息。

通过不断的研究和探索,我们可以构建更高效、更准确的 RAG 系统,更好地服务于我们的应用。

针对宽泛召回问题,语义压缩是有效的策略

通过减少文档冗余,提高检索准确性,语义压缩能显著改善 RAG 系统的性能。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注