AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


LlamaIndex实现的高级检索增强生成
发布日期:2024-04-08 08:22:33 浏览次数: 2243 来源:数智笔记


如何通过在Python中实现有针对性的高级RAG技术来解决简单RAG流水线的局限性

简单RAG和高级RAG的区别(作者绘制,灵感来自[1])

最近一项关于检索增强生成(RAG)的调查[1]总结了三种最近发展起来的范式:

  • 简单RAG,
  • 高级RAG,以及
  • 模块化RAG。

高级RAG范式包括一组针对简单RAG已知局限性的技术。本文首先讨论这些技术,可以分为预检索、检索和后检索优化

在下半部分,您将学习如何使用Python中的Llamaindex实现一个简单RAG流水线,然后通过选择以下高级RAG技术将其改进为高级RAG流水线:

  • 预检索优化:句子窗口检索
  • 检索优化:混合搜索
  • 后检索优化:重新排序

什么是高级RAG

随着RAG领域的最新进展,高级RAG作为一种新的范式已经发展起来,通过有针对性的增强来解决简单RAG范式的一些局限性。正如最近的一项调查[1]总结的那样,高级RAG技术可以分为预检索、检索和后检索优化。

简单RAG和高级RAG的区别(作者绘制,灵感来自[1])

预检索优化

预检索优化主要关注数据索引优化和查询优化。数据索引优化技术旨在以有助于提高检索效率的方式存储数据,例如[1]:

  • 滑动窗口使用块之间的重叠,是最简单的技术之一。
  • 增强数据粒度应用数据清理技术,例如删除无关信息、确认事实准确性、更新过时信息等。
  • 添加元数据,例如日期、目的或章节,用于过滤目的。
  • 优化索引结构涉及不同的数据索引策略,例如调整块大小或使用多索引策略。本文将实现的一种技术是句子窗口检索,它将单个句子嵌入检索并在推理时用更大的文本窗口替换它们。

句子窗口检索

此外,预检索技术不仅限于数据索引,还可以涵盖推理时的技术,例如查询路由、查询重写和查询扩展。

检索优化

检索阶段旨在识别最相关的上下文。通常,检索基于向量搜索,它计算查询和索引数据之间的语义相似性。因此,大多数检索优化技术围绕嵌入模型展开[1]:

  • 微调嵌入模型定制嵌入模型以适应特定领域的上下文,特别是对于具有不断发展或罕见术语的领域。例如,BAAI/bge-small-en是一个高性能嵌入模型,可以进行微调(参见微调指南)。
  • 动态嵌入适应单词使用的上下文,而不像静态嵌入那样为每个单词使用单个向量。例如,OpenAI的embeddings-ada-02是一个复杂的动态嵌入模型,可以捕捉上下文理解[1]。

除了向量搜索之外,还有其他检索技术,例如混合搜索,它通常指的是将向量搜索与基于关键字的搜索相结合的概念。如果您的检索需要精确的关键字匹配,这种检索技术非常有用。

后检索优化

对检索到的上下文进行额外处理可以帮助解决超出上下文窗口限制或引入噪声等问题,从而阻碍对关键信息的关注。RAG调查[1]总结的后检索优化技术有:

  • 提示压缩通过删除无关内容和突出重要上下文来减少总提示长度。
  • 重新排序使用机器学习模型重新计算检索到的上下文的相关性分数。

重新排序

先决条件

本节讨论了在本文中跟随的所需软件包和API密钥。

所需软件包

本文将指导您如何使用Python中的LlamaIndex实现一个简单和一个高级RAG流水线。

pip install llama-index

在本文中,我们将使用LlamaIndexv0.10。如果您正在从较旧的LlamaIndex版本升级,您需要运行以下命令以正确安装和运行LlamaIndex:

pip uninstall llama-index
pip install llama-index --upgrade --no-cache-dir --force-reinstall

LlamaIndex提供了一种选项,可以将向量嵌入存储在JSON文件中以进行持久化存储,这对于快速原型设计非常有用。但是,由于高级RAG技术旨在用于生产就绪的应用程序,因此我们将使用向量数据库进行持久化存储。

由于我们需要元数据存储和混合搜索功能,除了存储向量嵌入之外,我们还将使用开源向量数据库Weaviate(v3.26.2),它支持这些功能。

pip install weaviate-client llama-index-vector-stores-weaviate

API密钥

我们将使用Weaviate嵌入式,您可以在不注册API密钥的情况下免费使用它。但是,本教程使用了一个嵌入模型和来自OpenAI的LLM,您将需要一个OpenAI API密钥。要获取API密钥,您需要一个OpenAI帐户,然后在API密钥下“创建新的秘密密钥”。

接下来,在根目录中创建一个名为.env的本地文件,并在其中定义您的API密钥:

OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

然后,您可以使用以下代码加载您的API密钥:

# !pip install python-dotenv
import os
from dotenv import load_dotenv,find_dotenv

load_dotenv(find_dotenv())

使用LlamaIndex实现简单RAG

本节讨论了如何使用LlamaIndex实现一个简单的RAG流水线。您可以在此Jupyter Notebook中找到完整的简单RAG流水线。有关使用LangChain的实现,您可以继续阅读本文(使用LangChain的简单RAG流水线)。

步骤1:定义嵌入模型和LLM

首先,您可以在全局设置对象中定义一个嵌入模型和LLM。这样做意味着您不必在代码中再次显式指定模型。

  • 嵌入模型:用于为文档块和查询生成向量嵌入。
  • LLM:用于根据用户查询和相关上下文生成答案。
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.core.settings import Settings

Settings.llm = OpenAI(model="gpt-3.5-turbo", temperature=0.1)
Settings.embed_model = OpenAIEmbedding()

步骤2:加载数据

接下来,您将在根目录中创建一个名为data的本地目录,并从LlamaIndex GitHub存储库(MIT许可证)下载一些示例数据。

!mkdir -p 'data'
!wget '<https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt>' -O 'data/paul_graham_essay.txt'

然后,您可以加载数据以进行进一步处理:

from llama_index.core import SimpleDirectoryReader

# Load data
documents = SimpleDirectoryReader(
        input_files=["./data/paul_graham_essay.txt"]
).load_data()

步骤3:将文档分块为节点

from llama_index.core.node_parser import SimpleNodeParser

node_parser = SimpleNodeParser.from_defaults(chunk_size=1024)

# 从文档中提取节点
nodes = node_parser.get_nodes_from_documents(documents)

步骤 4:构建索引

接下来,您将构建索引,将所有外部知识存储在开源向量数据库 Weaviate 中。

首先,您需要连接到 Weaviate 实例。在本例中,我们使用 Weaviate Embedded,它允许您在没有 API 密钥的情况下免费在笔记本中进行实验。对于生产环境的解决方案,建议您自己部署 Weaviate,例如 通过 Docker 或使用 托管服务。

import weaviate

# 连接到 Weaviate 实例
client = weaviate.Client(
    embedded_options=weaviate.embedded.EmbeddedOptions(), 
)

接下来,您将使用 Weaviate 客户端构建一个 VectorStoreIndex,用于存储和交互数据。

from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.vector_stores.weaviate import WeaviateVectorStore

index_name = "MyExternalContext"

# 构建向量存储
vector_store = WeaviateVectorStore(
    weaviate_client = client, 
    index_name = index_name
)

# 设置嵌入存储
storage_context = StorageContext.from_defaults(vector_store=vector_store)

# 设置索引
# 构建 VectorStoreIndex,负责将文档分块并将块编码为嵌入以供将来检索
index = VectorStoreIndex(
    nodes,
    storage_context = storage_context,
)

步骤 5:设置查询引擎

最后,您将将索引设置为查询引擎。

# QueryEngine 类配备了生成器
# 并且方便检索和生成步骤
query_engine = index.as_query_engine()

步骤 6:在数据上运行一个简单的 RAG 查询

现在,您可以在数据上运行一个简单的 RAG 查询,如下所示:

# 运行简单的 RAG 查询
response = query_engine.query(
    "Interleaf 发生了什么?"
)

使用 LlamaIndex 实现高级 RAG

在本节中,我们将介绍一些简单的调整,可以将上述简单的 RAG 流程转变为高级流程。本教程将涵盖以下一些高级 RAG 技术:

  • 预检索优化:句子窗口检索
  • 检索优化:混合搜索
  • 后检索优化:重新排序

由于我们只涵盖了这里的修改内容,您可以在此 Jupyter Notebook 中找到完整的端到端高级 RAG 流程。

索引优化示例:句子窗口检索

对于句子窗口检索技术,您需要进行两个调整:首先,您必须调整数据的存储和后处理方式。我们将使用 SentenceWindowNodeParser 替代 SimpleNodeParser

from llama_index.core.node_parser import SentenceWindowNodeParser

# 使用默认设置创建句子窗口节点解析器
node_parser = SentenceWindowNodeParser.from_defaults(
    window_size=3,
    window_metadata_key="window",
    original_text_metadata_key="original_text",
)

SentenceWindowNodeParser 执行两个操作:

  1. 将文档分割为单个句子,并对其进行嵌入。
  2. 对于每个句子,创建一个上下文窗口。如果指定 window_size = 3,则生成的窗口将由三个句子组成,从嵌入句子的前一个句子开始,跨越下一个句子。窗口将存储为元数据。

在检索过程中,返回与查询最匹配的句子。在检索之后,您需要通过定义 MetadataReplacementPostProcessor 并将其用于 node_postprocessors 列表中,将句子替换为元数据中的整个窗口。

from llama_index.core.postprocessor import MetadataReplacementPostProcessor

# 目标键默认为 `window`,以匹配 node_parser 的默认设置
postproc = MetadataReplacementPostProcessor(
    target_metadata_key="window"
)

...

query_engine = index.as_query_engine( 
    node_postprocessors = [postproc],
)

检索优化示例:混合搜索

在 LlamaIndex 中实现混合搜索只需要对 query_engine 进行两个参数更改,如果底层向量数据库支持混合搜索查询。alpha 参数指定向量搜索和基于关键字的搜索之间的加权比例,其中 alpha=0 表示基于关键字的搜索,alpha=1 表示纯向量搜索。

query_engine = index.as_query_engine(
    ...,
    vector_store_query_mode="hybrid"
    alpha=0.5,
    ...
)

后检索优化示例:重新排序

将重新排序器添加到您的高级 RAG 流程只需要三个简单的步骤:

  1. 首先,定义一个重新排序器模型。在这里,我们使用来自 Hugging Face 的 BAAI/bge-reranker-base 模型。
  2. 在查询引擎中,将重新排序器模型添加到 node_postprocessors 列表中。
  3. 增加查询引擎中的 similarity_top_k,以检索更多上下文段落,可以在重新排序后减少为 top_n
# !pip install torch sentence-transformers
from llama_index.core.postprocessor import SentenceTransformerRerank

# 定义重新排序器模型
rerank = SentenceTransformerRerank(
    top_n = 2
    model = "BAAI/bge-reranker-base"
)

...

# 将重新排序器添加到查询引擎中
query_engine = index.as_query_engine(
  similarity_top_k = 6,
  ...,
                node_postprocessors = [rerank],
  ...,
)



53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询