链载Ai

标题: 做好 RAG 落地最后环节 —— 评估 RAG 应用 [打印本页]

作者: 链载Ai    时间: 3 小时前
标题: 做好 RAG 落地最后环节 —— 评估 RAG 应用

一、为什么RAG应用需要评估

随着大模型技术的发展,我们已经具备了开发完整 RAG(Retrieval-Augmented Generation,检索增强生成)应用的技术能力。借助 LlamaIndex、LangChain 等成熟框架,可以在较短时间内实现从原型到应用的快速构建。然而,真正将 RAG 应用推向生产环境,远不只是“搭建起来”那么简单,仍有许多问题值得提前思考与应对。


RAG 应用的快速开发并非难事,真正的挑战在于如何在生产环境中保持其稳定性、可控性和持续改进的能力。唯有建立起科学的评估与运维体系,才能让 RAG 应用真正发挥价值。

二、RAG应用的评估依据与指标

基于大模型的 RAG 应用与传统应用存在显著差异:传统应用的输出多具备确定性与可量化性,例如直接输出明确数值,评估标准清晰且易于衡量;而 RAG 应用以自然语言作为输入与输出载体,其结果通常表现为一段文本内容 —— 这类输出的相关性、准确性等核心指标,难以通过简单的定量方式直接判断,往往需要依托更智能的评估工具与专业模型完成综合评估。

RAG 应用的评估依据,即评估模块的输入一般包括以下要素。

  1. 输入问题(question):用户在使用 RAG 应用时的输入问题。

  2. 生成的答案(answer):需要评估的 RAG 应用的输出,即问题的答案。

  3. 上下文(context):用于增强 RAG 应用输出的参考上下文,通常在检索阶段生成。

  4. 参考答案(reference_answer):输入问题的真实的正确答案,通常需要人类标注。

基于这些评估依据,对 RAG 应用进行评估的常见指标见表1 。

三、RAG应用的评估流程与方法

  1. 确定评估的目的、维度与指标。

  2. 准备评估数据集,可以自行准备与标注,也可以使用大模型生成。根

  3. 据 评 估 指 标 , 你 可 能 需 要 准 备 不 同 的 输 入 问 题 ( question ) 与 参 考 答 案(reference_answer)。

  4. 将评估数据集输入 RAG 应用,获得检索结果(即上下文,context)与生成的答案(answer)。

  5. 将评估依据输入评估器,计算各类评估指标,分析 RAG 应用的整体性能。

  6. 对 RAG 应用的组件级评估通常侧重于检索与生成两个最关键的阶段。通过对这两个阶段单独评估,可以更细致地观察与分析问题,从而有针对性地优化与增强 RAG 应用。


四、评估检索质量

利用检索评估组件 RetrieverEvaluator 可以对任何检索模块进行质量评估。

该评估的主要指标如下。

评估检索质量的主要依据为输入问题与期望的上下文(检索出的 Node)。

1、生成检索评估数据集

评估检索过程有两种方式:用 evaluate 方法评估单次查询,或用 evaluate_dataset 方法批量评估构造好的检索评估数据集。由于评估的是检索上下文与问题的相关性,需要问题与参考上下文的数据对,因此首先需用 generate_question_context_pairs 方法借助大模型从已有数据生成检索评估数据集(也可自行构造)。
# 读取文档,构造 Node,用于生成检索评估数据集frompathlibimportPathfromllama_index.coreimportSimpleDirectoryReader, VectorStoreIndexfromllama_index.core.node_parserimportSentenceSplitterfromllama_index.core.evaluationimport(  generate_question_context_pairs,  EmbeddingQAFinetuneDataset,)
# 加载大模型的代码,请勿删除 startfromcommon.llm_model_helperimportllm_settings# 加载大模型的代码,请勿删除 end
documents = SimpleDirectoryReader(input_files=[r"D:\muxue\common_docs\jianlai.txt"]).load_data()
node_parser = SentenceSplitter(chunk_size=1024)nodes = node_parser.get_nodes_from_documents(documents)

foridx, nodeinenumerate(nodes): node.id_ =f"node_{idx}"
# 准备一个检索器,后面使用# vector_index = VectorStoreIndex(nodes)# retriever = vector_index.as_retriever(similarity_top_k=2)QA_GENERATE_PROMPT_TMPL ="""以下是上下文:---------------------{context_str}---------------------你是一位专业教授。你的任务是基于以上的上下文,为即将到来的考试设置{num_questions_per_chunk} 个问题。这些问题必须基于提供的上下文生成,并确保上下文能够回答这些问题。确保每一行都只有一个独立的问题。不要有多余解释。不要给问题编号。"""print("Generating question-context pairs...")qa_dataset = generate_question_context_pairs( nodes, # llm=llm_Ollama, num_questions_per_chunk=1, qa_generate_prompt_tmpl=QA_GENERATE_PROMPT_TMPL)print("Saving dataset...")qa_dataset.save_json("retriever_eval_dataset.json")

在这段代码中,手工设置了中文的 Prompt,一方面有助于了解生成原理与调试生成结果,另一方面用于生成中文问题。最后,把生成的检索评估数据集保存到本地的 JSON 文档中,以减少不必要的重复生成,后面只需要从本地文档中加载即可。

2、运行评估检索过程的程序

我们已经构造了一个检索器,并且借助大模型生成了检索评估数据集。下面构造与运行检索评估器。基本的流程如下。

# 读取文档,构造 Node,用于生成检索评估数据集

# 读取文档,构造 Node,用于生成检索评估数据集frompathlibimportPathfromllama_index.coreimportSimpleDirectoryReader, VectorStoreIndexfromllama_index.core.node_parserimportSentenceSplitterfromllama_index.core.evaluationimport(  generate_question_context_pairs,  EmbeddingQAFinetuneDataset,)fromllama_index.core.evaluationimportRetrieverEvaluatorfromevaluation_commimportget_retriever

print("Loading dataset...")# 从保存的 JSON 文档中加载检索评估数据集qa_dataset = EmbeddingQAFinetuneDataset.from_json("retriever_eval_dataset.json")eval_querys =list(qa_dataset.queries.items())

# 构造一个检索评估器,设定两个评估指标metrics = ["mrr","hit_rate"]retriever_evaluator = RetrieverEvaluator.from_metric_names(metrics, retriever=get_retriever())# 简单评估前 10 个评估用例foreval_id, eval_queryineval_querys[:10]: expect_docs = qa_dataset.relevant_docs[eval_id] print(f"Query:{eval_query}, Expected docs:{expect_docs}") # 评估,输入评估问题与预期检索出的 Node eval_result = retriever_evaluator.evaluate(query=eval_query, expected_ids=expect_docs) print(eval_result)
# 对整个评估数据集进行评估# eval_results = retriever_evaluator.evaluate_dataset(qa_dataset)

从打印的结果中可以看到评估指标,你可以对这些评估指标进行汇总计算,得出检索评估器的质量评估结果。如果需要对整个检索评估数据集直接进行评估,那么可以使用更简单的方式:

eval_results=retriever_evaluator.evaluate_dataset(qa_dataset)

直接在检索评估数据集上调用 evaluate_dataset 方法,其效果与逐个循环调用 evaluate 方法的效果是一致的。

五、评估响应质量

响应质量是 RAG 应用评估的重点,关乎端到端客户体验。可评估单个指标、单次响应,也可基于批量数据集自动运行多个响应评估器以获取整体结果。

1、 生成响应评估数据集

和评估检索过程类似,仍可借助大模型生成响应评估数据集(也可按格式自行标注)。它与检索评估数据集不同,因评估响应质量除需问题和检索上下文外,还需大模型生成的答案甚至参考答案。用 generate_dataset_from_nodes 方法能让大模型批量生成响应评估数据集。
frompathlibimportPathfromllama_index.coreimportSimpleDirectoryReader, VectorStoreIndexfromllama_index.core.llama_dataset.generatorimportRagDatasetGeneratorfromllama_index.core.llama_datasetimportLabelledRagDataset
# 加载大模型的代码fromcommon.llm_model_helperimportllm_settings

# build documentsdocs = SimpleDirectoryReader(input_files=[r'D:\muxue\common_docs\jianlai.txt']).load_data()

# define generator, generate questionsdataset_generator = RagDatasetGenerator.from_documents( documents=docs, #llm=llm_ollama, num_questions_per_chunk=1, # 设置每个 Node 都生成的问题数量 show_progress=True, question_gen_query="您是一位老师。您的任务是为即将到来的考试设置{num_questions_per_chunk}个问题。这些问题必须基于提供的上下文生成,并确保上下文能够回答这些问题。确保每一行都只有一个独立的问题。不要有多余解释。不要给问题编号。")
# 以下代码只需要运行一次save_json_name='rag_eval_dataset_jianlai.json'print('Generating questions from nodes...\n')rag_dataset = dataset_generator.generate_dataset_from_nodes()rag_dataset.save_json(save_json_name)# 从本地文档中加载并查看print('Loading dataset...\n')rag_dataset = LabelledRagDataset.from_json(save_json_name)forexampleinrag_dataset.examples: print(f'query:{example.query}') print(f'answer:{example.reference_answer}')

在运行代码后,你可以在当前目录中看到一个 rag_eval_dataset_jianlai.json 数据集文档,打开该文档,可以看到生成的每个评估用例的数据格式。其中:

这些内容都可能被输入到后面的响应评估器中,作为评估响应质量的输入依据,如下所示。

2、单次响应评估

要想对 RAG 应用的某一次查询的响应过程进行不同维度的评估,那么只需要构造对应的评估器组件(Evaluator),然后输入必需的数据,即可获得评估结果。单次响应评估的输入参数有以下几个。

fromllama_index.coreimport(  VectorStoreIndex,  SimpleDirectoryReader,  Response,)fromllama_index.core.node_parserimportSentenceSplitter
fromllama_index.core.evaluationimport(FaithfulnessEvaluator,RelevancyEvaluator,ContextRelevancyEvaluator,AnswerRelevancyEvaluator,CorrectnessEvaluator,SemanticSimilarityEvaluator)
# 加载大模型的代码,请勿删除 startfromcommon.llm_model_helperimportllm_settings# 加载大模型的代码,请勿删除 end
# ......这里省略构造查询引擎的过程......documents = SimpleDirectoryReader(input_files=[r"D:\muxue\common_docs\jianlai.txt"]).load_data()# create vector indexsplitter = SentenceSplitter(chunk_size=512)vector_index = VectorStoreIndex.from_documents( documents, transformations=[splitter])query_engine = vector_index.as_query_engine()
# 两个重要的输入参数query ="陈平安对中年光棍的尖酸刻薄言语有何反应?"response = query_engine.query(query)# 评估忠实度的评估器evaluator = FaithfulnessEvaluator()eval_result = evaluator.evaluate_response(query=query, response=response)print(f'faithfulness score:{eval_result.score}\n')# 评估相关性的评估器(综合了上下文相关性与答案相关性)evaluator = RelevancyEvaluator()eval_result =evaluator.evaluate_response(query=query, response=response)print(f'relevancy score:{eval_result.score}\n')# 评估上下文相关性的评估器evaluator = ContextRelevancyEvaluator()eval_result =evaluator.evaluate_response(query=query, response=response)
print(f'context relevancy score:{eval_result.score}\n')# 评估答案相关性的评估器evaluator = AnswerRelevancyEvaluator()eval_result =evaluator.evaluate_response(query=query, response=response)print(f'answer relevancy score:{eval_result.score}\n')# 评估正确性的评估器,注意输入了 referenceevaluator = CorrectnessEvaluator()eval_result =evaluator.evaluate_response(query=query, response=response, reference='根据提供的上下文信息,陈平安对中年光棍的尖酸刻薄言语反应如下:他翻了个白眼,但并不以为意。原因有二:一是他生活在这座乡野地方,早已习惯此类言语,认为若因此恼火反而显得可笑;二是这中年光棍本身也是小镇百姓经常取笑的对象,陈平安对此人并无太多计较。')print(f'correctness score:{eval_result.score}\n')# 评估答案与标准答案的语义相似度(基于 embedding)的评估器,注意输入了 referenceevaluator = SemanticSimilarityEvaluator()eval_result =evaluator.evaluate_response(query=query, response=response, reference='陈平安翻了个白眼,但并不以为意,对此人并无太多计较。')print(f'semantic similarity score:{eval_result.score}\n')

评估过程非常简单,各个评估指标可参考表 1中的说明。必须再次强调,有的评估器需要输入参考答案(正确性与语义相似度),否则会出现异常。在正常运行评估代码后,可以看到输出的评估结果,如下图所示。

3、批量响应评估

你可以借助批量评估器,在评估数据集的基础上并行运行多个响应评估器,并通过计算与统计获得综合的性能评估结果。以 (五-1)节生成的响应评估数据集为基础,对构造的查询引擎进行综合评估:

fromllama_index.coreimport(  VectorStoreIndex,  SimpleDirectoryReader,  Response,)fromllama_index.core.node_parserimportSentenceSplitterfromllama_index.core.llama_datasetimportLabelledRagDatasetfromllama_index.core.evaluationimport(FaithfulnessEvaluator, RelevancyEvaluator, ContextRelevancyEvaluator, AnswerRelevancyEvaluator, CorrectnessEvaluator, SemanticSimilarityEvaluator)fromllama_index.core.evaluationimportBatchEvalRunnerimportasyncio# 打印评估结果importpandasaspd# 加载大模型的代码,请勿删除 startfromcommon.llm_model_helperimportllm_settings# 加载大模型的代码,请勿删除 end
# ......这里造查询引擎......documents = SimpleDirectoryReader(input_files=[r"D:\muxue\common_docs\jianlai.txt"]).load_data()# create vector indexsplitter = SentenceSplitter(chunk_size=512)vector_index = VectorStoreIndex.from_documents( documents, transformations=[splitter])query_engine = vector_index.as_query_engine()
# 构造多个响应评估器faithfulness_evaluator = FaithfulnessEvaluator()relevancy_evaluator = RelevancyEvaluator()correctness_evaluator = CorrectnessEvaluator()similartiy_evaluator = SemanticSimilarityEvaluator()
# 加载数据集rag_dataset = LabelledRagDataset.from_json('rag_eval_dataset_jianlai.json')
# 构造一个批量评估器runner = BatchEvalRunner( {"faithfulness": faithfulness_evaluator, "relevancy": relevancy_evaluator, "correctness": correctness_evaluator, "similarity": similartiy_evaluator}, workers=4)

asyncdefevaluate_queries(): """ 为了提高性能,采用异步并行的评估方法,调用批量评估器 输入:查询引擎、批量的 query,批量的 reference 这里对响应评估数据集中的前十个评估用例进行评估 """ eval_results =awaitrunner.aevaluate_queries( query_engine, queries=[example.queryforexampleinrag_dataset.examples][:10], reference=[example.reference_answerforexampleinrag_dataset.examples][:10], ) returneval_results

eval_results = asyncio.run(evaluate_queries())
defdisplay_results(eval_results): data = {}
forkey, resultsineval_results.items(): scores = [result.scoreforresultinresults] scores.append(sum(scores) /len(scores)) data[key] = scores
data["query"] = [result.queryforresultineval_results["faithfulness"]] data["query"].append("【Average】") df = pd.DataFrame(data) print(df)
display_results(eval_results)

借助 BatchEvalRunner 组件,在调用 aevaluate_queries 方法进行批量评估时可以设置 workers 参数并行运行,从而缩短评估的时间。最后,我们把评估结果用表格的形式展示,以便更直观地观察(也可以输出 Excel 文档),输出结果如图所示。






欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5