共计 3783 个字符,预计需要花费 10 分钟才能阅读完成。
LightRAG原始论文提出了一种基于图结构的RAG方法,该方法在多个数据集上取得了SOTA的效果,本文从论文本身以及代码实现上由宏观到微观地剖析。论文链接
LightRAG的结构
我们先看原论文的这张图,宏观层面展现了LightRAG的框架工作流程。
- 最左边是原始文档txt内容,通过固定字符数来切分Chunk,形成Chunk库,后面会讲这些Chunk库会向量化存储到向量库中;
- 基于这些Chunk块,进行D、P、R的操作。
- R操作表示利用LLM对Chunk块进行实体和关系的提取
- P操作基于R操作生成的实体和关系进行总结描述
- 这里需要注意的是,对实体的描述为实体本身的描述,比如Beekeeper,对应的描述为:A beekeeper is an person who...
- 对关系的描述为关系本身的描述
- 对关系还会总结一些keywords,这些keywords的解释为:一个或多个高级关键词,这些关键词概括了关系的总体性质,侧重于概念或主题而不是具体细节
- D操作会将实体或者关系进行去重操作
- 基于以上操作,会形成一个Graph
- entity表示具体的实体,结构包含:
- entity name 实体名字
- entity type 实体类型
- description 实体描述(LLM总结而来)
- original chunks id 对应的原始chunk块
- edge表示关系,结构包含:
- source 头实体
- target 尾实体
- keywords 步骤2中对关系总结出的关键词
- description 关系描述(LLM总结而来)
- original chunks id 对应的原始chunk块
- entity表示具体的实体,结构包含:
- 查询阶段,由low-level query和high-level query两种查询方式结合召回的entities、relations、chunks给LLM进行总结回复。
运行流程
先看下这张图,我们分成图谱构建阶段和查询推理阶段来看。
图谱构建阶段
这部分我们结合代码来看。
入口在insert
函数中:
chunking_by_token_size
函数将原始文本按照token_size切分为Chunks。
extract_entities
函数是实体、关系抽取的核心函数,这个函数会基于chunk抽取出来对应的实体和关系。
其中实体结构为:
关系结构为:
其中,重点关注下实体和关系结构中的content,因为向量化的内容就是其中的content。
实体的向量化内容是:实体名称+实体描述
关系的向量化内容是:关键词+头实体+尾实体+描述
到这儿知识构建的部分就已经完成了,再总结一下,基于原始文本切分为chunk块,基于chunk块提取实体、关系以及生成对应的描述和关键词,分别存到图谱和向量数据库中。
查询阶段
入口在query
函数里面。
从代码可以看到,支持三种模型,分别是global、local和hybrid。
global和local两种方式对应的原论文中的high_level和low_level的召回方式。
从代码层面来看,两种检索方式,首先都是基于用户的查询,基于大模型提取关键词。我们来看prompt提示词(源代码是英文版本,我翻译为中文版):
PROMPTS["keywords_extraction"] = """---Role---
您是一个有帮助的助手,负责识别用户查询中的高级和低级关键字。
---Goal---
给定查询,列出高级和低级关键字。高级关键字侧重于总体概念或主题,而低级关键字侧重于特定实体、细节或具体术语。
---指令---
—以JSON格式输出关键字。
- JSON应该有两个键:
-“high_level_keywords”用于总体概念或主题。
—“low_level_keywords”用于指定实体或详细信息。
######################
-示例-
######################
例 1:
提问:国际贸易如何影响全球经济稳定?
################
输出:
{{
“high_level_keywords”:[“国际贸易”,“全球经济稳定”,“经济影响”],
“low_level_keywords”:[贸易协定,关税,货币兑换,进口,出口]
}}
#############################
例 2:
提问:“森林砍伐对生物多样性的环境后果是什么?”
################
输出:
{{
“high_level_keywords”:[“环境后果”,“森林砍伐”,“生物多样性丧失”],
“低水平关键词”:[“物种灭绝”、“栖息地破坏”、“碳排放”、“雨林”、“生态系统”]
}}
#############################
例 3:
提问:“教育在减少贫困方面的作用是什么?”
################
输出:
{{
“high_level_keywords”:[“教育”,“减贫”,“社会经济发展”],
“low_level_keywords”:[“入学机会”、“识字率”、“职业培训”、“收入不平等”]
}}
#############################
-以下是需要处理的数据-
######################
提问: {query}
######################
输出:
"""
从提示词可以看出,大模型需要从用户的查询中,提炼出来high_level_keywords和low_level_keywords ,前者更贴切的描述应该是宏观的主题词,后者更关注用户提问本身的关键词。
低级检索
这一层面主要关注检索特定实体及其关联属性或关系。这一层面的查询是细节导向的,旨在提取关于图中特定节点或边的精确信息。
得到了low_level_keywords的列表之后,首先通过构建好entity的向量库去找相应的topic_entity。然后基于topic_entity找到相关的relation和text_unit。然后将这些信息拼装为context。
高级检索
这一层面处理更广泛的话题和主题。这一层面的查询汇总了多个相关实体和关系中的信息,提供了对高层次概念和摘要的洞察,而不是具体细节。
同样,得到了high_level_keywords ,首先通过构建好的relation的向量库去找相应的relation,再通过relation去图谱里面找关联的entity和text_unit。然后将这些信息拼装为context。
查询阶段总结
通过双路召回,从查询本身的信息出发,关注具体的实体相关的关联信息,以及从查询本身的主题出发,关注涉及到该主题的关系信息出发,寻找关联信息。从高纬度和低纬度分别召回不同的信息源帮助大模型构建上下文,丰富了召回的信息语义相关度,也增加了召回信息的层次化。
评估效果
与同类RAG框架对比
结果可以看到,领先同类RAG。
不同维度召回方式对比
主要比较了原生的RAG、仅Low_level_retrieval、仅High_level_retrieval、混合模式以及去掉原文召回的retrieval四中方式。
可以看到混合模式横向比原生的RAG都有较大的提升,纵向比较单模式召回的整体准确率会高一些。
论文中也提到,令人惊讶的是,在混合模式中,去掉原始文本的这种方式,并没有显示出显著的性能下降。在某些情况下,这种变体甚至显示出改进(例如在Agriculture和Mix领域)。我们将这种现象归因于基于图索引过程中的有效提取关键信息,这为回答问题提供了足够的上下文。此外,原始文本通常包含可能引入响应中噪声的不相关信息。
实例对比
在同一个问题的情况下,比较了GraphRAG、LightRAG,分别从理解性、多样性、赋权性和整体质量四个方面来评估,LightRAG的效果都优于GraphRAG。
成本和可持续性对比
在召回阶段和增加新语料两种方式下,跟GraphRAG比较,成本有显著优势,并且增加新语料的情况下,LightRAG也是非常友好的。
总结及思考未来
LightRAG从GraphRAG的基础上,减少了社区及社区摘要的生成,同时吸纳了不同层次召回信息的思想,实现了减少成本并且提高了问答的准确性,同时可持续增加新知识,效果非常好。
但是经过实际项目测试发现,也是有较大的局限性的。
- 比如抽取实体和关系的时候,利用一个会话,让LLM同时抽取实体、关系以及对应的描述,虽然程序里面使用了迭代抽取,但仍然对LLM的理解能力有较大的考验,但是如果将任务分开让LLM专注于一个任务的执行,token的消耗又将成倍的增加。
- 另外源代码在抽取实体的时候,提前告诉了LLM需要抽取的实体类型,这在泛化性上需要考虑在新的领域下,实体类型是否容易定义,这本身也是传统知识图谱定义schema的一个老大难的问题,要么就直接舍弃掉实体类型,因为模型本身会生成对这个实体的描述信息。
- 在high_level的层面,框架依赖于实体间关系的主题词对关联信息进行召回,主题词质量的好坏决定了high_level的召回质量,在实际测试中,high_level的生成质量不是那么高。
- low_level的召回策略大家其实做得差不多,最后回答质量的提升关键在于怎样召回更高层次的语义关联信息,这个还需要做更多的思考。
未来在做Graph RAG(特指利用图结构做RAG的召回源)的过程中,重点要关注图谱构建效率、知识可持续迭代以及多层次语义召回质量。