跳到正文
原文
LlamaIndex:产品、工程与评测· LlamaIndex:产品、工程与评测·· 2023-10-05精选AI 评分62

LlamaIndex 实测 RAG 系统的最佳 chunk size

AI 导读

LlamaIndex 演示如何用 Response Evaluation 模块评测 RAG 系统的最佳 chunk size,以 Uber 2021 10K 文件为数据。

推荐理由

原文给出可复现的评测流程和具体数据,读者可以据此在自己的 RAG 场景中测试并选定合适的 chunk size。

正文

引言

检索增强生成(RAG)引入了一种创新方法,将搜索系统的广泛检索能力与LLM融合在一起。在实施RAG系统时,一个关键参数决定了系统的效率和性能,那就是chunk_size。如何确定无缝检索的最佳块大小?这就是LlamaIndex Response Evaluation派上用场的地方。在这篇博客文章中,我们将指导您通过步骤,使用LlamaIndex的Response Evaluation模块来确定最佳的chunk size。如果您不熟悉Response评估模块,我们建议在继续之前查看其文档。

立即探索我们的免费和付费计划。

为什么块大小很重要

选择合适的chunk_size是一个关键决策,可以在多个方面影响RAG系统的效率和准确性:

  1. 相关性和粒度:较小的chunk_size,如128,会产生更细粒度的块。然而,这种粒度带来风险:重要信息可能不在顶部检索到的块中,特别是如果similarity_top_k设置像2这样严格。相反,块大小为512很可能在顶部块中包含所有必要信息,确保查询答案随时可用。为了应对这一点,我们采用忠实度和相关性指标。这些分别衡量基于查询和检索上下文的响应中‘幻觉’的缺失和‘相关性’。
  2. 响应生成时间:随着chunk_size增加,输入到LLM以生成答案的信息量也增加。虽然这可以确保更全面的上下文,但也可能减慢系统速度。确保增加的深度不损害系统的响应性至关重要。

本质上,确定最佳chunk_size就是寻求平衡:捕捉所有必要信息而不牺牲速度。使用各种大小进行彻底测试,以找到适合特定用例和数据集的配置至关重要。

为了在选择正确的chunk_size时进行实际评估,您可以访问并运行以下设置,在这个Google Colab Notebook上。

设置

在开始实验之前,我们需要确保所有必需的模块都已导入:

import nest_asyncio

nest_asyncio.apply()

from llama_index import (
    SimpleDirectoryReader,
    VectorStoreIndex,
    ServiceContext,
)
from llama_index.evaluation import (
    DatasetGenerator,
    FaithfulnessEvaluator,
    RelevancyEvaluator
)
from llama_index.llms import OpenAI

import openai
import time
openai.api_key = 'OPENAI-API-KEY'

下载数据

我们将使用2021年的Uber 10K SEC文件进行这个实验。

!mkdir -p 'data/10k/'
!wget 'https://raw.githubusercontent.com/jerryjliu/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf'

加载数据

让我们加载我们的文档。

documents = SimpleDirectoryReader("./data/10k/").load_data()

问题生成

为了选择合适的chunk_size,我们将为各种chunk_sizes计算平均响应时间、忠实度和相关性等指标。DatasetGenerator将帮助我们生成文档中的问题。

data_generator = DatasetGenerator.from_documents(documents)
eval_questions = data_generator.generate_questions_from_nodes()

设置评估器

我们正在设置GPT-4模型,作为评估实验中生成响应的骨干。两个评估器,FaithfulnessEvaluator和RelevancyEvaluator,使用service_context初始化。

  1. 忠实度评估器 — 它用于衡量响应是否幻觉,并衡量查询引擎的响应是否匹配任何源节点。
  2. 相关性评估器 — 它用于衡量查询是否实际由响应回答,并衡量响应+源节点是否匹配查询。
# We will use GPT-4 for evaluating the responses
gpt4 = OpenAI(temperature=0, model="gpt-4")

# Define service context for GPT-4 for evaluation
service_context_gpt4 = ServiceContext.from_defaults(llm=gpt4)

# Define Faithfulness and Relevancy Evaluators which are based on GPT-4
faithfulness_gpt4 = FaithfulnessEvaluator(service_context=service_context_gpt4)
relevancy_gpt4 = RelevancyEvaluator(service_context=service_context_gpt4)

针对块大小的响应评估

我们基于3个指标评估每个chunk_size。

  1. 平均响应时间。
  2. 平均忠实度。
  3. 平均相关性。

这里有一个函数,evaluate_response_time_and_accuracy,它正好做到这一点,包括:

  1. VectorIndex创建。
  2. 构建查询引擎。
  3. 指标计算。


def evaluate_response_time_and_accuracy(chunk_size, eval_questions):
    """
    Evaluate the average response time, faithfulness, and relevancy of responses generated by GPT-3.5-turbo for a given chunk size.
    
    Parameters:
    chunk_size (int): The size of data chunks being processed.
    
    Returns:
    tuple: A tuple containing the average response time, faithfulness, and relevancy metrics.
    """

    total_response_time = 0
    total_faithfulness = 0
    total_relevancy = 0

    
    llm = OpenAI(model="gpt-3.5-turbo")
    service_context = ServiceContext.from_defaults(llm=llm, chunk_size=chunk_size)
    vector_index = VectorStoreIndex.from_documents(
        eval_documents, service_context=service_context
    )
    
    query_engine = vector_index.as_query_engine()
    num_questions = len(eval_questions)

    
    
    
    for question in eval_questions:
        start_time = time.time()
        response_vector = query_engine.query(question)
        elapsed_time = time.time() - start_time
        
        faithfulness_result = faithfulness_gpt4.evaluate_response(
            response=response_vector
        ).passing
        
        relevancy_result = relevancy_gpt4.evaluate_response(
            query=question, response=response_vector
        ).passing

        total_response_time += elapsed_time
        total_faithfulness += faithfulness_result
        total_relevancy += relevancy_result

    average_response_time = total_response_time / num_questions
    average_faithfulness = total_faithfulness / num_questions
    average_relevancy = total_relevancy / num_questions

    return average_response_time, average_faithfulness, average_relevancy

在不同块大小上测试

我们将评估一系列分块大小,以确定哪种能提供最有前景的指标。

chunk_sizes = [128, 256, 512, 1024, 2048]

for chunk_size in chunk_sizes:
  avg_response_time, avg_faithfulness, avg_relevancy = evaluate_response_time_and_accuracy(chunk_size, eval_questions)
  print(f"Chunk size {chunk_size} - Average Response time: {avg_response_time:.2f}s, Average Faithfulness: {avg_faithfulness:.2f}, Average Relevancy: {avg_relevancy:.2f}")

整合所有内容

让我们汇总这些流程:

import nest_asyncio

nest_asyncio.apply()

from llama_index import (
    SimpleDirectoryReader,
    VectorStoreIndex,
    ServiceContext,
)
from llama_index.evaluation import (
    DatasetGenerator,
    FaithfulnessEvaluator,
    RelevancyEvaluator
)
from llama_index.llms import OpenAI

import openai
import time

openai.api_key = 'OPENAI-API-KEY'


!mkdir -p 'data/10k/'
!wget 'https://raw.githubusercontent.com/jerryjliu/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf'


reader = SimpleDirectoryReader("./data/10k/")
documents = reader.load_data()


eval_documents = documents[:20]
data_generator = DatasetGenerator.from_documents()
eval_questions = data_generator.generate_questions_from_nodes(num = 20)


gpt4 = OpenAI(temperature=0, model="gpt-4")


service_context_gpt4 = ServiceContext.from_defaults(llm=gpt4)


faithfulness_gpt4 = FaithfulnessEvaluator(service_context=service_context_gpt4)
relevancy_gpt4 = RelevancyEvaluator(service_context=service_context_gpt4)


def evaluate_response_time_and_accuracy(chunk_size):
    total_response_time = 0
    total_faithfulness = 0
    total_relevancy = 0

    
    llm = OpenAI(model="gpt-3.5-turbo")
    service_context = ServiceContext.from_defaults(llm=llm, chunk_size=chunk_size)
    vector_index = VectorStoreIndex.from_documents(
        eval_documents, service_context=service_context
    )

    query_engine = vector_index.as_query_engine()
    num_questions = len(eval_questions)

    for question in eval_questions:
        start_time = time.time()
        response_vector = query_engine.query(question)
        elapsed_time = time.time() - start_time
        
        faithfulness_result = faithfulness_gpt4.evaluate_response(
            response=response_vector
        ).passing
        
        relevancy_result = relevancy_gpt4.evaluate_response(
            query=question, response=response_vector
        ).passing

        total_response_time += elapsed_time
        total_faithfulness += faithfulness_result
        total_relevancy += relevancy_result

    average_response_time = total_response_time / num_questions
    average_faithfulness = total_faithfulness / num_questions
    average_relevancy = total_relevancy / num_questions

    return average_response_time, average_faithfulness, average_relevancy


for chunk_size in [128, 256, 512, 1024, 2048]
  avg_time, avg_faithfulness, avg_relevancy = evaluate_response_time_and_accuracy(chunk_size)
  print(f"Chunk size {chunk_size} - Average Response time: {avg_time:.2f}s, Average Faithfulness: {avg_faithfulness:.2f}, Average Relevancy: {avg_relevancy:.2f}")

结果

上表表明,随着分块大小的增加,平均响应时间略有上升。有趣的是,平均忠实度似乎在chunk_size为1024时达到顶峰,而平均相关性则随着分块大小的增加持续改善,同样在1024时达到峰值。这表明,分块大小为1024可能在响应时间与响应质量(以忠实度和相关性衡量)之间达到最佳平衡。

结论

为RAG系统确定最佳分块大小,既依赖直觉,也依赖经验证据。借助LlamaIndex的Response Evaluation模块,你可以尝试各种大小,并基于具体数据做出决策。在构建RAG系统时,请始终记住chunk_size是一个关键参数。投入时间仔细评估并调整你的分块大小,以获得无与伦比的结果。

来源:LlamaIndex:产品、工程与评测 · llamaindex.ai