评估 RAG 的神器来啦!TruLens + Milvus=?

大型语言模型(LLM)的日益普及引爆了向量数据库赛道,向量搜索技术也越发受到开发者关注。目前,主流的向量搜索技术提供者包括向量数据库 Milvus 和 Zilliz Cloud,向量搜索库 FAISS,以及与传统数据库集成的向量搜索插件。

与此同时,向量搜索日益成为检索增强生成(retrieval augmented generation,RAG)中的一个重要组件。RAG(https://zilliz.com.cn/use-cases/llm-retrieval-augmented-generation) 这一类的问答应用是一种重要的生成式 AI 的企业级用例。在 RAG 应用中, LLM 可以轻松访问知识库,并将其用作上下文来回答问题。而实现这一应用的关键就在向量数据库。Milvus 是一款高度可扩展的开源向量数据库,专为此应用而设计。

01.构建 RAG

在构建高效的 RAG 式 LLM 应用程序时,我们有许多可以优化的配置,不同配置的选择极大影响了检索质量。可以选择的配置包括:

向量数据库的选择

  • 数据选择

  • Embedding 模型

  • 索引类型

找到高质量、能精准符合需求的数据非常关键。如果数据不够准确,检索可能返回无关的结果。选择好相关数据之后,还要考虑使用的 Embedding 模型。因为选择的模型对检索质量也有很大影响。如果 Embedding 模型无法理解特定领域内容的语义,不论使用什么数据库,检索器都有可能给出错误的结果。上下文相关度是衡量检索质量的一个关键指标,而向量数据库的选择极大地影响了这个指标结果。

最后,索引类型可以显著影响语义搜索的效率。索引类型的选择在大型数据集面前尤其重要。不同的索引类型允许用户在召回率、速度和资源需求之间进行权衡。Milvus 支持多种索引类型,比如 FLAT、乘积量化(product quantization)索引和基于图(graph-based)的索引。

检索参数的选择

  • 检索到的上下文数量(top-K)

  • 分块大小

当进行检索时,top-K 是一个重要参数,它控制检索到的上下文分块数量。更高的 top-K 意味着检索到所需信息的可能性越高。但是,过高的 top-K 也会导致回答中带入不相关的信息。因此,对于简单的问题,一般情况下推荐较低的 top-K 值,这样检索性能和结果都更佳。

分块大小控制检索上下文的大小。对于相对复杂的问题而言,分块大小越大,检索效果越好。对于较为简单的问题,由于只需要很小一部分信息即可回答,因此分块较小时效果最好。

那是否存在一套绝对的解决方案和配置能够帮助我们应对所有情况呢?答案是没有。不同的数据类型和规模、语言模型、向量数据库组合会产生不同的性能结果。因此我们需要根据自己的使用场景和需求,用评估工具来评价不同的配置选择在具体用例中的质量。在评估时,就会用到 TruLens。

02.TruLens 用于语言模型应用跟踪和评估

TruLens 是一个用于评估语言模型应用(如 RAG)的性能的开源库。通过 TruLens,我们还可以利用语言模型本身来评估输出、检索质量等。

构建语言模型应用时,多数人最关心的问题是 AI 幻觉(hallucination)。RAG 通过为语言模型提供检索上下文来确保信息准确性,但始终无法百分百保证提供完全准确的信息。因此,应用不会产生幻觉是评估验证重点的一个重要指标。TruLens 提供了 3 项测试:

  • 上下文相关度

  • 答案准确性

  • 答案相关度

接下来,让我们逐一来看一下这三项测试:

alt

上下文相关度

所有 RAG 应用第一步是检索。为验证检索质量,要确保每个上下文块与输入查询相关。因为语言模型将使用该上下文生成答案,所以上下文中的任何不相关信息都可能导致 LLM 出现幻觉。

答案准确性

在检索上下文后,LLM 生成答案。一些情况下, LLM 往往会偏离事实,对正确的答案进行夸张或扩展。为验证回答的准确性,我们应将回复分为独立语句,并在检索上下文中独立查证每个语句的出处来源。

答案相关度

最后,我们的回复仍须解答原始问题,可以通过评估最终回复与用户输入提问的相关度来验证这一点。

如果上述三项指标结果均令人满意,那我们可以说,在经过知识库验证后,消除了 RAG 幻觉。换言之,如果向量数据库包含的信息十分准确,则 RAG 提供的答案也准确。

03.具体案例

如前所述,RAG 配置选择可能对消除幻觉产生重大影响。下文中将基于城市百科文章构建问答 RAG 应用并展示不同的配置选择是如何影响应用性能的。在搭建过程中,我们使用 LlamaIndex 作为该应用的框架。大家可以在 Google Colab( https://colab.research.google.com/github/truera/trulens/blob/main/trulens_eval/examples/expositional/vector-dbs/milvus/milvus_evals_build_better_rags.ipynb) 中所有代码。

从百科加载数据

首先需要加载数据。这里,我们使用 LlamaIndex 中的数据加载器直接从百科加载数据。

from llama_index import WikipediaReader


cities = [
"Los Angeles""Houston""Honolulu""Tucson""Mexico City",
"Cincinatti""Chicago"
]


wiki_docs = []
for city in cities:
try:
doc = WikipediaReader().load_data(pages=[city])
wiki_docs.extend(doc)
except Exception as e:
print(f"Error loading page for city {city}{e}")

设置评估器

接着,设置评估器,我们会使用前面提到的三项测试。

TruLens 提供一组使用特定模型(如 OpenAI、Anthropic 或 HuggingFace)的提示评估器或反馈功能。

# Initialize OpenAI-based feedback function collection class:openai_gpt4 = feedback.OpenAI()

设置模型后,我们先测试查询语句相关度。在每项评估中,用思维链(chain-of-thought)来更好地了解评估结果。具体请见后缀 1_with_cot_reason 表示的反馈函数。

此时,我们还需选择传递给反馈函数的文本。首先 TruLens 序列化应用,然后生成一个类 JSON 结构的索引,用此索引选择文本。TruLens 提供一系列的帮助函数来简化此操作:

  • on_input()自动找到传给 LlamaIndex 应用的主输入,作为传给反馈函数的第一个文本。

  • TruLlama.select_source_nodes()标识 LlamaIndex 检索中使用的源节点。

最后,将每个上下文相关度整合为一个分数值。本例中,我们使用最大值衡量相关度,大家在上手过程中也可使用其他指标,如平均值或最小值。

# Question/statement relevance between question and each context chunk.
f_context_relevance = Feedback(openai.qs_relevance_with_cot_reason, name = "Context Relevance").on_input().on(
TruLlama.select_source_nodes().node.text
).aggregate(np.max)

准确性设置类似,整合过程略有不同。这里,我们取每个语句准确性的最高分和各语句准确性的的平均分。

grounded = Groundedness(groundedness_provider=openai_gpt4)
f_groundedness = Feedback(grounded.groundedness_measure_with_cot_reason, name = "Groundedness").on(
TruLlama.select_source_nodes().node.text # context
).on_output().aggregate(grounded.grounded_statements_aggregator)

答案相关度的反馈函数最容易设置,因为它仅依赖输入/输出。我们可以使用新的 TruLens 帮助函数 .on_input_output()

# Question/answer relevance between overall question and answer.f_qa_relevance = Feedback(openai.relevance_with_cot_reason,
name = "Answer Relevance").on_input_output()

定义配置空间

在完成加载数据和设置评估器后,便可以开始构建 RAG。在此过程中,我们构建一系列具有不同配置的 RAG,并评估每种选择配置的性能,得出最佳配置。

在此案例中,我们测试了不同索引类型、 Embedding 模型、Top-K 和分块大小。当然,大家也可以测试其他配置,如不同相似度类型(distance metric)和搜索参数。

迭代我们的选择

定义配置空间后,我们用 itertools 尝试每种组合并进行评估。此外,Milvus 的覆盖参数也提供了便利,帮助我们可轻松迭代不同配置。否则如果使用其他向量数据库,我们可能需要重复缓慢的实例化过程。

每次迭代,我们将索引参数选择传给 MilvusVectorStore 和使用存储上下文的应用。将 Embedding 模型传给服务上下文,然后创建索引。

vector_store = MilvusVectorStore(index_params={
"index_type": index_param,
"metric_type": "L2"
},
search_params={"nprobe": 20},
overwrite=True)
llm = OpenAI(model="gpt-3.5-turbo")
storage_context = StorageContext.from_defaults(vector_store = vector_store)
service_context = ServiceContext.from_defaults(embed_model = embed_model, llm = llm, chunk_size = chunk_size)
index = VectorStoreIndex.from_documents(wiki_docs,
service_context=service_context,
storage_context=storage_context)

然后,我们可用该索引构建查询引擎 —— 定义top_k:

query_engine = index.as_query_engine(similarity_top_k = top_k)

构建后,我们用 TruLens 包装应用。这里,我们用易识别的方式命名,将配置记录为应用元数据。随后,定义反馈函数用于评估。

tru_query_engine = TruLlama(query_engine,
app_id=f"App-{index_param}-{embed_model_name}-{top_k}",
feedbacks=[f_groundedness, f_qa_relevance, f_context_relevance],
metadata={
'index_param':index_param,
'embed_model':embed_model_name,
'top_k':top_k
})

这个 tru_query_engine 将像原查询引擎一样运行。

最后,用少量测试提示进行评估,调用应用响应每个提示。由于需要短时间内快速连续调用 OpenAI API,Tenacity 可帮助我们通过指数回退的策略避免速率限制问题。

@retry(stop=stop_after_attempt(10), wait=wait_exponential(multiplier=1, min=4, max=10))def call_tru_query_engine(prompt):
return tru_query_engine.query(prompt)
for prompt in test_prompts:
call_tru_query_engine(prompt)

结果

  • 哪种配置表现最佳?
alt
alt
  • 哪种配置表现最差?
alt
alt
  • 发现了哪些错误类型?

可以观察到,一种错误类型是检索到错误的城市信息。可以从下面的思维链反馈中看到,RAG 检索到了 Tucson 而不是 Houston 的上下文。

alt

同样,我们也遇到 RAG 回答正确城市但回答与输入问题不相关、无关上下文被检索的情况。

alt

基于无关的上下文,语言模型开始出现幻觉。需要区分的是,幻觉不一定是错误的事实,它仅指模型在无证据支持下作答。

alt

此外,我们还发现 RAG 返回不相关回答的情况:

alt

04.深入了解性能

索引类型

本例中,索引类型对查询速度、token 用量或评估没有明显影响。这可能是因为数据量较小的关系。索引类型对较大语料库可能更重要。

Embedding 模型

text-embedding-ada-002 在准确性(0.72,平均 0.60)和答案相关度(0.82,平均0.62)上优于 MiniLM Embedding 模型。两者在上下文相关度上表现一致。这个结果可能是 OpenAI Embedding 更适合百科信息的缘故。

相似度 top-K

top-k 的增加可以略微提高检索质量(通过上下文相关度测量)。检索的文本块越多,检索器获取高质量上下文的可能性越大。

top-K 的增加也改善了准确性(0.71,平均 0.62)和答案相关度(0.76,平均0.68)。检索更多上下文文本块可以为语言模型提供更多支持其结论的内容。但是更高的 top-K 意味着更高的 token 使用成本(每次调用平均需要额外使用 590 个 token)。

分块大小

分块大小越大,包含与输入问题无关的文本越多,检索准确性越低。但是分块越大,为回答提供的支持内容越多。

也就是说,LLM 返回结果时,分块大小越大,可以获得的上下文越多。

分块增大,需要额外增加使用 400 个 token。

本文中,我们学习使用各种配置和参数(包括索引类型、 Embedding 模型、 top-K 和分块大小)构建 RAG。我们的测试得益于 Milvus ,Milvus 拥有各种配置且允许覆盖参数。通过 TruLens 来评估每种配置和参数组合,我们能够快速寻找到性能最佳的参数配置。

如果大家想要尝试探索,可以先安装开源工具 TruLens 和开源向量数据库 Milvus 或全托管的 Milvus 服务 Zilliz Cloud。

本文最初发布于 The New Stack,已获得转载许可。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/128853.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA 28 个天花板技巧 + 12 款神级插件,生产力起飞...

IDEA 作为Java开发工具的后起之秀,几乎以碾压之势把其他对手甩在了身后,主要原因还是归功于:好用;虽然有点重,但依旧瑕不掩瑜,内置了非常多的功能,大大提高了日常的开发效率,下面汇总…

pytest + yaml 框架 -58.运行报告总结summary.json

前言 用例运行结束后,在本地生成summary.json 文件,总结运行结果。 v1.5.1版本更新内容: 1.解决参数化,中文在控制台输出问题 2.保存用例结果summary.json 保存用例结果summary.json 命令行执行用例 pytest运行结束&#xff0…

NodeJS 入门笔记

文档地址 课程地址 源码 提取码:963h hello wrold console.log(hello, world);node hello.jsnodejs 中不能使用 DOM(document) 和 BOM(window) 的 API: documentwindowhistorynavigatorlocation 但是下面的 API 是相通的: consoletimer…

振南技术干货集:振南当年入门C语言和单片机的那些事儿(1)

目录 第一章《振南当年入门 C 语言和单片机的那些事儿》 1、注定堕入单片机 1.1 懵懂好奇的我 (小时候好奇的性格经常让我屁股开花。初中开始对计算机产生兴趣,并一发不可收拾。) 1.2 我的 C 语言学习经历 (上大学后自学 C …

制造行业怎么做?看低代码如何引领未来

随着科技的不断发展,制造行业正面临着巨大的变革和挑战。为了提高生产效率、降低成本并更好地适应快速变化的市场需求,越来越多的制造企业将目光投向了低代码开发平台。在众多低代码开发平台中,JNPF低代码快速开发平台凭借其卓越的性能和灵活…

【论文阅读笔记】Detecting AI Trojans Using Meta Neural Analysis

个人阅读笔记,如有错误欢迎指出! 会议:2021 S&P Detecting AI Trojans Using Meta Neural Analysis | IEEE Conference Publication | IEEE Xplore 问题: 当前防御方法存在一些难以实现的假设,或者要求直…

【vue 仿百度分页】

vue 仿百度分页 效果图 代码 公用组件 <template><nav class"pagination_nav"><ul class"pagination"><li :class"{ disabled: current 1 }"><a href"javascript:;" click"setCurrent(current - …

JAVA对象大小的获取

1. Java 对象的内存布局 Java的实例对象、数组对象在内存中的组成包括如下三部分&#xff1a;对象头Hearder、实例数据、内存填充。示意图如下所示 对象头 其主要包括两部分数据&#xff1a;Mark Word、Class对象指针。特别地对于数组对象而言&#xff0c;其还包括了数组长度…

2022年09月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 下列不是评判一个算法优劣的标准是?( ) A: 时间复杂度 B: 空间复杂度 C: 难易度 D: 健壮性 答案:C 评价算法的优劣是:时间复杂度,空间复杂度,健壮性,正确性,可读性。因此选…

一周成功拿下4个offer的软件测试面试题,面试必看系列

前言&#xff1a; 压到就是赚到&#xff0c;面试通过的机率就更大&#xff0c;干就完了铁子 【文章末尾给大家留下了大量的福利】 ​编辑 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平…

el-table 多表格弹窗嵌套数据显示异常错乱问题

1、业务背景 使用vueelement开发报表功能时&#xff0c;需要列表上某列的超链接按钮弹窗展示&#xff0c;在弹窗的el-table列表某列中再次使用超链接按钮点开弹窗&#xff0c;以此类推多表格弹窗嵌套&#xff0c;本文以弹窗两次为例 最终效果如下示例页面 2、具体实现和问题…

【狂神说Java】Swagger + 任务

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;狂神说Java &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c;永远…

重庆市5米数字高程(DEM)数据

重庆位于中国西南部、长江上游地区&#xff0c;地跨东经10511~11011、北纬2810~3213之间的青藏高原与长江中下游平原的过渡地带。东邻湖北、湖南&#xff0c;南靠贵州&#xff0c;西接四川&#xff0c;北连陕西&#xff1b;辖区东西长470千米&#xff0c;南北宽450千米&#xf…

uni-app——項目day01

配置uni-app開發環境 uni-app快速上手 | uni-app官网 创建项目 图中四个划线就是要配置的地方. 选择vue2还是vue3看个人选择。 目录结构 但是现在新版本创建的项目已经没有components目录了&#xff0c;需要自己创建。 项目运行到微信开发者工具 使用git管理项目 node-mod…

【神印王座】林鑫和李馨甜蜜接吻,团灭七阶恶魔,温馨结尾

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;《神印王座》80话最新剧情解析。有关李馨与林鑫的爱情故事源于一场争执。那时&#xff0c;两人都年轻气盛&#xff0c;不肯向对方低头。但是&#xff0c;经过一段时间的相处&#xff0c;…

ARM day4

LED灯亮灭控制 .text .global _start _start: 1ldr r0,0x50000a28ldr r1,[r0]orr r1,r1,#(0x3<<4)str r1,[r0] 2ldr r0,0x50006000ldr r1,[r0]bic r1,r1,#(0x3<<20)orr r1,r1,#(0x1<<20)bic r1,r1,#(0x3<<16)orr r1,r1,#(0x1<<16)str r1,[r0]…

java语法:继承与多态

导言: 在Java中&#xff0c;继承和多态是面向对象编程的两个重要概念&#xff0c;它们允许我们创建更加灵活和可扩展的代码。本文主要对继承和多态的语法和一些细节做一个介绍和解释。 目录 导言: 正文&#xff1a; 一.继承 1. 基本语法 2. 继承的特点 3.子类中访问父类…

补坑:Java的字符串String类(1)

常用方法 字符串构造 来看看源码里面String的构造方法 普通字符串 //"hello" 是字符串常量&#xff0c;没有\0标记结尾String str "hello";System.out.println(str);//helloString str2 new String();System.out.println(str2);//没有输出String str3…

“三位一体”超级混沌工程主要特点及功能

“三位一体”超级混沌工程X-Chaos主要包括基础故障编排、业务场景故障编排、演练场景编排、故障库管理、演练场景管理、演练计划管理、演练观测和演练报告等模块&#xff0c;支持对传统架构、云环境以及国产化基础环境的IT系统进行故障演练。本文将介绍混沌工程主要特点及主要功…

归并分治 笔记

归并分治 前置知识&#xff1a;讲解021-归并排序 原理&#xff1a; (1&#xff09;思考一个问题在大范围上的答案&#xff0c;是否等于&#xff0c;左部分的答案 右部分的答案 跨越左右产生的答案(2&#xff09;计算“跨越左右产生的答案”时&#xff0c;如果加上左、右各自…