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

LlamaIndex 实测 RAG 系统的最佳 chunk size

Evaluating the Ideal Chunk Size for a RAG System using LlamaIndex

AI 导读

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

推荐理由

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

正文 · AI 翻译

引言

检索增强生成(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