为了账号安全,请及时绑定邮箱和手机立即绑定

评估与提升RAG性能:第1部分——开发阶段的评估技巧

这张照片由 Adam Nowakowski 拍摄,来自 Unsplash

检索增强生成(RAG)系统很受欢迎,它们结合了信息检索与生成式大规模语言模型的强大功能。要在生产环境中实现高性能,这颇具挑战性,需要仔细开发、详细记录日志、评估以及不断迭代。

在这两个由人类生成的 — 由人类生成的;) — 博文中,我们将探讨评估RAG系统的关键方面,包括开发阶段(预生产)和正式上线后的评估。预生产评估在开发过程中进行。这一阶段的评估主要由AI(作为评判的大型语言模型)或领域专家进行,主要评估幻觉、简洁性、有用性、相关性、答案相似度等。在正式上线后,还可以通过用户反馈进一步评估你的RAG系统。

这篇第一篇文章是关于生产和评估前的步骤。这篇帖子展示了你可以如何使用三个优秀的LLM工具或平台来构建和评估系统。使用LlamaIndex作为构建简单RAG系统框架,使用Ragas作为评估框架,并使用Literal AI这一可视化和交互式界面来帮助评估。

本文内容概览

  1. 初始设置和数据集
  2. 使用LlamaIndex构建RAG系统
  3. 用Ragas评估RAG系统
  4. 在“Literal AI”中分析评估结果
1. 初始设置

在本教程中,我们将使用LlamaIndex、OpenAI、Ragas和LiteralAI,所以需要安装几个相关的库。

在终端中运行以下命令来安装所需的库:

!pip install llama-index, !pip install ragas, !pip install literalai, !pip install python-dotenv

无论是OpenAI(付费的)还是Literal AI(免费的),都需要一个 API 密钥。可以将这些密钥保存在环境变量里或在 .env 文件里。

作为开始,我们将初始化Literal AI为LlamaIndex应用程序添加监控工具。我们将使用Literal AI来查看聊天记录和评估。

    import os   

    from literalai import LiteralClient   

    from dotenv import load_dotenv  
    load_dotenv()   

    # 要获取Literal API密钥,可以访问https://docs.getliteral.ai/get-started/installation#how-to-get-my-api-key  
    literalai_client = LiteralClient(api_key=os.getenv("LITERAL_API_KEY"))   

    # 将客户端集成到LlamaIndex中
    literalai_client.instrument_llamaindex()

在我们开始构建我们的RAG应用之前,我们需要一个包含数据集。在此示例中,我们使用了Kaggle上发布的播客文字稿。该数据集的好处是它还包括问题和人工标注的答案,我们将在稍后的评估中使用这些内容。请从这里下载数据并存放在/data文件夹中。

2. 简单地构建一个RAG系统,使用LlamaIndex

LlamaIndex 是一个用于构建 RAG 应用的框架。它包含了从数据加载到评估的全流程所需的所有组件。下面我们将使用以下内容:

  1. 一个嵌入模型。这是一个为应用程序中的数据创建向量嵌入表示的模型。LlamaIndex 默认使用 OpenAI 的 text-embedding-ada-002 模型。
  2. 一个大语言模型。这是一个根据输入生成回复的模型,输入包括一个问题和上下文。LlamaIndex 默认使用 OpenAI 的 gpt-3.5-turbo 模型。为了简单起见,我们将在这个示例中使用默认模型。
  3. 一个文档加载器:用于从源读取文档到应用程序中。我们将使用 SimpleDirectoryReader,因为我们已经将文档保存在本地目录中,但 LlamaIndex 提供了各种 DataConnectors 来从其他来源加载数据。
  4. 索引和存储,根据嵌入表示来存储数据。这里我们采用 LlamaIndex 提供的默认内存向量存储。如果您正在为生产构建 RAG 应用程序,您应该将数据存储在向量存储中,例如 WeaviateVespa
  5. 一个提示模板。给生成式大语言模型的指令,使其根据应用程序的目的或场景来回答用户的问题。在本文中,我们将使用默认提示,以便我们可以专注于评估而不是在提示模板上过多纠结。
  6. 一个查询引擎,一个允许向索引数据提问的接口。查询引擎是建立在索引基础上,并通过检索器来工作的。
    从 llama_index.core 引入 VectorStoreIndex 和 SimpleDirectoryReader  

    # 从本地目录加载播客的文字稿
    documents = SimpleDirectoryReader("data/acquired-individual-transcripts/acquired-individual-transcripts").load_data()    

    # 使用 text-embedding-ada-002 对数据进行索引
    index = VectorStoreIndex.from_documents(documents, show_progress=True)
    # 显示进度

我们现在可以使用LlamaIndex提供的默认提示来构建查询引擎,该查询引擎默认检索2个数据块,但我们将会把它改为仅检索一个数据块(块)。

    # 构建一个简单的查询引擎,支持每次检索一个块的上下文检索  
    query_engine = index.as_query_engine(similarity_top_k=1)

我们现在已经对文档进行了索引,并构建了一个查询引擎,它检索两段数据并生成答案。这样,我们现在就可以向这个系统发送查询,关于其中一个播客的内容。如果在特定上下文中RAG系统已经被定义,可以保持使用“RAG系统”,否则解释一下RAG(检索、获取、生成)会更有助于理解。

    # 测试这个查询引擎
    response = query_engine.query("杰夫·贝索斯经常穿什么靴子和夹克?")
    print(response)

自从我们最初为这个RAG应用添加了监控功能后,我们现在可以在Literal AI平台上看到这个query、步骤和answer。你可以仔细查看问题、vector embedding、检索到的文档、prompt以及答案。

Log in Literal AI

在Literal AI中的查询和大型语言模型回复日志

3. 使用 Ragas 评估 RAG 系统

现在我们有了一个简单的检索和生成应用(RAG),接下来进行评估。在这里,我们将重点放在应用正式上线前你可以做的评估上。

通常来说,评估主要有两种类型,通常在生产前阶段进行:

  1. 检索评价
  2. 响应与生成评价

在检索评估中,你判断找到的信息来源是否与查询相关。例如,检索评估的一些指标有:

  • 上下文精度:衡量检索到的文档中实际与生成正确回复相关的文档比例。它使用问题、真实信息和上下文来计算。
  • 上下文召回率:衡量检索器找到所有相关上下文的能力。召回率是相关真实信息的比例。它使用问题、真实信息和上下文。如果不存在真实答案作为参考,可以使用上下文使用率作为指标。
  • 平均倒数排名 (MRR):衡量首个相关文档的排名。它显示相关信息在检索出的文档列表中出现的早迟程度。

在响应或生成的评估中,我们会检查响应是否与检索到的上下文、查询以及可能存在的任何指导方针相匹配。响应评估通常不使用任何参考标准,而仅依据查询、上下文和响应。响应评估的示例指标如下:

  • 忠实性:衡量回答中的陈述是否可以从检索到的上下文中推断出来。如果得分低,这意味着模型在胡言乱语(做出了无法在上下文中找到的陈述)。
  • 答案相关性:衡量回答是否与查询相关。
  • 噪声敏感度:衡量系统是否会在响应中使用相关或不相关的检索文档生成错误的陈述。此指标需要一个事实依据(参考或标签)。
  • 正确性:衡量生成的答案是否与给定查询的实际情况相符。

了解更多关于如何评估大型语言模型系统的信息,请访问Literal AI的评估页面

在这里,我们将使用Ragas来评估我们的简单RAG系统。Ragas是一个用于评估RAG的Python框架。Ragas与LlamaIndex集成了,这使得它使用起来非常方便。评估完成后,我们可以将结果存储到Literal AI中进行查看。

首先,我们需要一个评估集。我们可以使用Kaggle上的播客数据集,该数据集包含示例问题和人工生成的答案,我们将这些答案用作基准答案。然后,可以将相关数据上传至Literal AI的数据集中,以便在未来的实验中进行比较使用。

    # 在Literal AI上创建数据集  
    DATASET_NAME = f"Podcast Evaluation Dataset"  

    literal_dataset = literalai_client.api.create_dataset(name=DATASET_NAME)  
    import csv  

    # 用来自Kaggle播客数据集中的问题和答案来创建Literal AI的数据集  
    with open('data/qa-evaluation.csv', newline='', encoding='ISO-8859-1') as csvfile:  
        evaluation_data = csv.reader(csvfile, delimiter=',')  
        next(evaluation_data, None) # 跳过CSV的表头  
        for row in evaluation_data:  
            literal_dataset.create_item(  
                input={  
                    "问题内容": row[0]  
                },  
                expected_output={  
                    "答案内容": row[1]  
                }  
            )

我们现在有一个存储在Literal AI中的数据集,其中包含了问题和人类的答案。

然后,我们需要将这个数据集改为此软件能看懂的格式。

    # 创建带有实际答案的ragas实验数据项

    experiment_items = literal_dataset.items

    questions = []
    ground_truths = []

    for item in experiment_items:
        question = item.input["content"]
        ground_truth = item.expected_output["content"]

        questions.append(question)
        ground_truths.append(ground_truth)

    data_samples = {
        'question': questions,
        'ground_truth': ground_truths
    }
    # 将字典转换为数据集。这样做的目的是...
    # 为了避免之后评估函数出现问题,数据集需要是这种类型,而不仅仅是字典。
    from datasets import Dataset
    data_samples_set = Dataset.from_dict(data_samples)

现在我们可以开始设置并运行Ragas实验了。让我们为上下文检索以及响应生成制定一些基本的评估指标。

    # 导入Ragas实验的度量标准

    from ragas.metrics import (  
        answer_relevancy,  
        context_precision,  
        context_recall,  
    )  

    metrics = [  
        answer_relevancy,  
        context_precision,  
        context_recall,  
    ]  
    from llama_index.llms.openai import OpenAI  
    from llama_index.embeddings.openai import OpenAIEmbedding  

    # 使用GPT 4或4-turbo可以获得更好的准确性  
    evaluator_llm = OpenAI(model="gpt-3.5-turbo")  
    from ragas.integrations.llama_index import evaluate  

    result = evaluate(  
        query_engine=query_engine, # 此函数会自动调用LlamaIndex的查询引擎  
        metrics=metrics,  
        dataset=data_samples_set,  
        llm=evaluator_llm,  
        embeddings=OpenAIEmbedding(),  
    )

我们现在完成了首次评估!让我们在Literal AI里看看结果。

4. 评估分析 Literal AI 中的评估结果
    result = result.to_pandas()  
    ## 将实验结果提交给Literal AI  
    experiment = literal_dataset.create_experiment(  
        name="实验1 - 最佳单检索",  
        params=[{ "top_k": 1 }]  
    )  

    # 记录实验1的每个结果  
    for i, row in result.iterrows():  
        scores = [{   
            "name": answer_relevancy.name,  
            "type": "AI",  
            "value": row[answer_relevancy.name]  
        }, {   
            "name": context_precision.name,  
            "type": "AI",  
            "value": row[context_precision.name]  
        }, {   
            "name": context_recall.name,  
            "type": "AI",  
            "value": row[context_recall.name]  
        }]  

        experiment_item = {  
            "datasetItemId": experiment_items[i].id,  
            "scores": scores,  
            "input": { "question": row["question"] },  
            "output": { "content": row["answer"] }  
        }  

        experiment.log(experiment_item)

恭喜!现在你可以在Literal AI中查看实验结果了。我们可以查看实验的整体摘要以及所有指标,还可以逐个查看各个项目。

更重要的是,你可以对比不同的实验。那么,我们来看看怎么改进第一个RAG(检索-阅读生成)系统,优化一些指标。

Results summary in Literal AI

Literal AI 的评估结果

正如我们所见,上下文召回相对较低,与上下文精准度和答案相关性相比,这个方面。此外,这个差异非常大。肯定还有改进的余地。

(Note: There is a minor grammatical issue in the revised translation, specifically the sentence fragment "上下文召回相对较低,与上下文精准度和答案相关性相比,这个方面". It should be corrected to "与上下文精准度和答案相关性相比,上下文召回相对较低,这个方面". Here is the corrected version):

与上下文精准度和答案相关性相比,上下文召回相对较低,这个方面。此外,这个差异非常大。肯定还有改进的余地。

上下文召回展示了检索器从数据库中准确识别所有相关上下文的能力。它是可以归因于检索上下文的真实陈述比例。回顾查询引擎,可以看到我们从数据库中只检索了一小块数据。这虽然是一个小数字,但目的是为了演示和测试。但这可能太小了,相关的答案可能存在于其他或多个检索到的数据块中。我们可以尝试增加每次查询中检索的数据块数量,看看上下文召回是否有所提高。

我们可以再创建一个测试场景,再做一遍Ragas实验,并在Literal AI里看看结果。

    # 定义一个检索5个片段的查询引擎  
    query_engine_5 = index.as_query_engine(similarity_top_k=5)  

    # 使用ragas评估新的查询引擎(query_engine_5)  
    result_5 = evaluate(  
        query_engine=query_engine_5,  
        metrics=metrics,  
        dataset=data_samples_set,  
        llm=evaluator_llm,  
        embeddings=OpenAIEmbedding(),  
    )  
    result_5 = result_5.to_pandas()  

    ## 将实验结果提交给Literal AI  

    experiment = literal_dataset.create_experiment(  
        name="实验3 - 顶5检索结果",  
        params=[{ "top_k": 5 }]  
    )  

    # 记录实验1的每个实验结果  
    for i, row in result_5.iterrows():  
        scores = [{   
            "name": answer_relevancy.name,  
            "type": "AI",  
            "value": row[answer_relevancy.name]  
        }, {   
            "name": context_precision.name,  
            "type": "AI",  
            "value": row[context_precision.name]  
        }, {   
            "name": context_recall.name,  
            "type": "AI",  
            "value": row[context_recall.name]  
        }]  

        experiment_item = {  
            "数据项ID": experiment_items[i].id,  
            "scores": scores,  
            "input": { "question": row["question"] },  
            "output": { "content": row["answer"] }  
        }  

        experiment.log(experiment_item)

我们现在在Literal AI中有了两个实验结果。如果我们正在进行其中一个实验,我们就可以将其与在相同数据集上进行的另一个实验进行比较。

在下面的图片中,我们比较了第一次实验结果与第二次。在第二次(粉色),我们为每个查询检索了5个片段,而不是之前的1个。我们可以看到,所有指标都有所改善,其中上下文召回率改善最多(从0.561提高到0.646)。当然,这些数字并不是最高的,但请记住这只是个简单的例子。我们没有改变默认提示,使用了默认的嵌入和生成模型等。

Results summary comparison in Literal AI

比较两个实验的评估结果

当然,我们可以用多种方式进一步改进RAG系统。这里有一些想法等等,但在这篇文章中我们不会详细讨论这些内容。

嵌入策略如下:

  • 修改块大小,例如通过语义分块将文档拆分成更小、更相关的部分,从而改进检索效果并帮助模型专注于相关部分。
  • 更改嵌入模型。

检索策略如下:

  • 分段:更改检索的段数。
  • 混合搜索:结合关键词搜索和向量搜索,以同时捕获精确匹配和相关结果。
  • 重新排序:重新排列搜索结果,将最相关的文档放在前面。
  • 更改或调整生成的LLM模型

提示策略:

  • 改进提示模板,使其包含更清晰、更具体的使用案例说明。
  • 少样本提示:通过在提示中加入几个示例来指导模型输出,从而提高质量和准确性。
结果和看法

在这篇文章里,我们使用LlamaIndex建立了一个简单的RAG系统,并使用Ragas进行了评估,还用Literal AI分析了结果。我们讨论了RAG应用在预生产阶段评估策略的广泛角度。

在接下来的文章中,我们将深入探讨如何在实际生产环境中评估RAG系统。评估会基于真实用户互动中的反馈,这些反馈可以是人工或AI提供的。我们将利用Literal AI的聊天日志功能来获取实际生产环境中的评估数据。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消