GraphRAG的增量更新
本文将对比现有几家GraphRAG的增量更新机制实现与做法,比较其优劣,最后进行一次归纳总结,希望对你有所启发。
下面是本文引用的主要实现方案的源码、文档以及相关文献。
项目名 | 代码链接 | 博客/论文链接 |
---|---|---|
MS graphrag v0.3.6 | github.com/microsoft/g... | www.microsoft.com/en-us/resea... arxiv.org/abs/2404.16... |
nano-graphrag | github.com/gusye1234/n... | 无 |
IText2KG | github.com/AuvaLab/ite... | arxiv.org/abs/2409.03... * Lairgi Y, Moncla L, Cazabet R, et al. iText2KG: Incremental Knowledge Graphs Construction Using Large Language Models[J]. arXiv preprint arXiv:2409.03284, 2024. * Accepted at The International Web Information Systems Engineering conference (the WISE conference) 2024 * [Submitted on 5 Sep 2024] |
fast-graphrag | github.com/circlemind-... | 无 |
lightrag | github.com/HKUDS/Light... | arxiv.org/abs/2410.05... |
MS graphrag v0.4.0 | github.com/microsoft/g... 增量索引 PR:github.com/microsoft/g... 核心 issue 讨论:github.com/microsoft/g... | 同MS |
1. 增量更新概述
增量更新(Incremental Update)指在已有知识图谱的基础上,快速更新新加入的知识,避免全量重建。
GraphRAG中的增量更新本质上是一种增量索引操作,旨在通过小规模的数据更新提升系统的响应速度。
2. 现有实现方案对比
2.1 MS GraphRAG v0.3.6
Microsoft GraphRAG v0.3.6是GraphRAG的早期实现,主要支持基本的知识图谱构建与检索增强生成功能,尚未包含增量更新的直接支持。
- 优点:提供了知识图谱与检索增强生成的完整功能,技术文档详细且易于集成。
- 缺点:未包含对增量更新的支持,更新数据需重新构建索引,效率较低。
2.2 nano-GraphRAG
这是基于 MS GraphRAG的实现
- 文档级别的增量更新 :nano-GraphRAG通过对文档内容生成MD5哈希值,实现了增量插入功能,确保相同内容不会重复处理。在批量插入文档时,系统会检查内容哈希,以避免重复计算。
- 社区结构更新限制:在每次插入新文档时,nano-GraphRAG会重新计算社区结构并生成新的社区报告。这意味着尽管nano-GraphRAG能支持文档级增量更新,但对更高抽象层级的社区节点增量更新支持有限,社区结构需要重新计算。
(PS: 只是工程上过滤相同文档内容。)
参考链接
2.3 LightRAG
基于
nano-graphrag
根据LightRAG的论文描述,以下是LightRAG增量更新的核心实现和方法:
-
增量知识库更新 :LightRAG使用图结构管理实体和关系,每次有新文档加入时,会对其执行相同的图索引步骤 ,包括实体识别和关系提取。通过将新增的节点和边集与原有图的节点和边进行合并(集合并操作),实现新数据的无缝集成,无需重建整个索引结构。
-
无冲突集成:增量更新模块确保新增信息与已有结构的一致性,从而保留历史数据的完整性,同时丰富知识图谱,避免冗余或冲突关系。通过对图结构的增量合并,LightRAG避免了重新构建索引的开销,提升了系统对动态数据环境的适应性。
-
计算开销减少:由于无需重建完整的索引图,LightRAG大幅降低了计算开销,快速适应新数据。该方法保证了系统在数据变化时的准确性和资源节约,显著提升了RAG(检索增强生成)系统的更新效率。
PS: 这也是目前所见唯一一个在论文上直接对增量更新做评估分析的。
增量更新效果
在实验中,LightRAG展示出在多领域数据集中的增量更新表现,较传统方法显著减少了更新成本。例如,LightRAG避免了大规模数据更新时的重建过程,相比GraphRAG等模型在大数据集上有明显优势,且能够在复杂语境下实现更具综合性的结果。
- GraphRAG would require around 1,399 × 2 × 5,000 tokens to reconstruct both the original and new community reports
- T extract代表提取实体和关系的tokens开销。
- C extract代表提取操作需要的API调用次数。
参考链接
2.4 fast-GraphRAG
基于
nano-graphrag
fast-GraphRAG聚焦于高性能的数据处理,适合于数据量大且需要高响应速度的应用场景。
- 优点:处理速度显著提升,适合大规模知识图谱数据的增量更新需求。
- 缺点:性能优化的同时对部分功能做了取舍,可能不支持全部GraphRAG功能。
逻辑
- 数据分块(Chunking) : 在
async_insert
方法中,首先对输入数据进行分块处理(chunking_service.extract
),将长文本数据分解为更小的部分,方便后续处理。 - 过滤重复数据 : 增量更新的关键步骤是通过
state_manager.filter_new_chunks
过滤已经存在的重复块(chunks)。这一步确保只有新的内容会被插入到图中,避免重复数据冗余。 - 信息提取和图的更新 : 对新的数据块,通过
information_extraction_service.extract
从中提取出实体和关系(subgraphs)。这些实体和关系构成了新图谱的节点和边。 - 图谱增量更新(Upsert) : 使用
state_manager.upsert
将新提取的实体和关系插入到现有的图结构中,实现图的增量更新。这里的upsert
操作不仅仅是简单插入,而是更新或插入新的节点和边,根据已有的图结构动态调整。 - 插入完成通知 : 增量更新流程开始和结束时,分别调用
state_manager.insert_start
和state_manager.insert_done
来管理状态。
增量分析
如果需要进一步理解其中源码,
只需要分析节点的 NodeUpsertPolicy_SummarizeDescription
和边的 EdgeUpsertPolicy_UpsertIfValidNodes
及 EdgeUpsertPolicy_UpsertValidAndMergeSimilarByLLM
的 upsert
逻辑即可。下面是一些详细的流程说明。
节点 (`NodeUpsertPolicy_SummarizeDescription`) 的 Upsert 逻辑
1. 配置:
- max_node_description_size: 限制节点描述的最大字符长度。
- node_summarization_ratio: 描述摘要长度的比例。
- node_summarization_prompt: 生成摘要的提示语。
- is_async: 是否异步执行。
2. 逻辑流程:
- 通过异步函数
_upsert_node
遍历每个节点。 - 节点分组处理: 根据节点 ID 对输入节点进行分组。
- 获取已有节点信息: 根据节点 ID 从图数据库中查找已有节点。
- 描述合并: 如果描述过长,通过调用
LLM
摘要。 - 节点更新/插入: 将合并后的描述和选定的类型生成新的节点。
3. 异步与同步处理:
- 如果配置为异步处理,所有节点并行处理。
- 否则逐个节点同步处理。
边 (`EdgeUpsertPolicy_UpsertIfValidNodes`) 的 Upsert 逻辑
1. 配置:
- is_async: 是否异步执行。
2. 逻辑流程:
- 通过异步函数
_upsert_edge
遍历每条边。 - 验证节点存在: 只有当源节点和目标节点都存在时,才执行边的
upsert
操作。 - 边更新/插入: 将边插入图数据库中并更新索引。
3. 异步与同步处理:
- 异步情况下,所有边并行处理。
- 同步情况下,逐条边顺序处理。
边 (`EdgeUpsertPolicy_UpsertValidAndMergeSimilarByLLM`) 的 Upsert 逻辑
1. 配置:
- edge_merge_threshold: 定义边的合并阈值。
- is_async: 是否异步执行。
2. 逻辑流程:
- 边分组处理: 将相同源节点和目标节点的边分组。
- 判断是否合并: 如果分组的边数量超过阈值,调用合并逻辑。
- 边更新/插入: 逐条插入或合并边。
- 删除冗余边: 在
upsert
后删除冗余边。
3. 异步与同步处理:
- 异步时,所有边任务并行执行。
- 同步时,逐条边顺序处理。
总结:
- 节点的
upsert
逻辑 主要侧重于处理节点描述的合并和摘要、节点类型的选择,以及支持异步并行处理。 - 边的
upsert
逻辑 则关注于源节点和目标节点的验证,确保只有有效节点之间的边被处理,并根据配置合并相似的边以减少冗余,提升图存储的效率。
参考:
2.5 MS GraphRAG v0.4.0
在v0.4.0版本中,Microsoft GraphRAG引入了增量索引支持。此功能显著优化了知识图谱的增量更新效率,使得系统在处理数据更新时更加灵活高效。
- 增量索引 PR :增量索引PR链接
- 核心 Issue 讨论 :增量更新实现讨论
- 优点:支持增量更新,提升了更新效率;系统设计更加灵活,适合大规模数据。
- 缺点:增量索引功能需要更多的配置,且使用者需具备较高的技术能力。
代码分析:
增量更新的实现主要涉及以下几个步骤:
-
定义增量更新配置 :在
pipeline.py
和create_pipeline_config.py
中,定义了update_index_storage
配置,用于指定增量更新存储的位置。 -
加载旧数据 :通过
_load_table_from_storage
函数,加载存储中的旧数据。 -
合并新旧数据 :使用诸如
_update_and_merge_relationships
、_update_and_merge_entities
等函数,将旧数据与新数据(增量数据)合并。在合并过程中,处理了 ID 冲突和数据冲突,例如重新计算 ID、更新关系等。 -
保存合并后的数据:将合并后的数据保存回存储中,用于后续的增量更新。
具体实现可以参考以下代码片段:
- 合并关系数据:
python
def _update_and_merge_relationships(old_relationships: pd.DataFrame, delta_relationships: pd.DataFrame) -> pd.DataFrame:
delta_relationships["human_readable_id"] = delta_relationships["human_readable_id"].astype(int)
old_relationships["human_readable_id"] = old_relationships["human_readable_id"].astype(int)
initial_id = old_relationships["human_readable_id"].max() + 1
delta_relationships["human_readable_id"] = np.arange(initial_id, initial_id + len(delta_relationships))
final_relationships = pd.concat([old_relationships, delta_relationships], ignore_index=True, copy=False)
final_relationships["source_degree"] = final_relationships.groupby("source")["target"].transform("count")
final_relationships["target_degree"] = final_relationships.groupby("target")["source"].transform("count")
final_relationships["rank"] = final_relationships["source_degree"] + final_relationships["target_degree"]
return final_relationships
- 合并实体数据:
python
def _group_and_resolve_entities(old_entities_df: pd.DataFrame, delta_entities_df: pd.DataFrame) -> tuple[pd.DataFrame, dict]:
merged = delta_entities_df[["id", "name"]].merge(old_entities_df[["id", "name"]], on="name", suffixes=("_B", "_A"), copy=False)
id_mapping = dict(zip(merged["id_B"], merged["id_A"], strict=True))
initial_id = old_entities_df["human_readable_id"].max() + 1
delta_entities_df["human_readable_id"] = np.arange(initial_id, initial_id + len(delta_entities_df))
combined = pd.concat([old_entities_df, delta_entities_df], ignore_index=True, copy=False)
return combined, id_mapping
除了常规的节点和关系之外,社区节点是怎么做的呢?(其实同理)
-
合并和解析节点:
-
通过
_merge_and_resolve_nodes
函数,将旧节点与增量节点合并。旧节点的最大社区ID用于增量节点ID的调整。 -
合并过程中,增量节点会与合并后的实体数据框进行匹配,以获取新的
human_readable_id
。
-
-
更新和合并社区:
-
通过
_update_and_merge_communities
函数,将旧社区和增量社区合并 ,并使用社区ID映射进行更新。github.com/microsoft/g...pythondef _update_and_merge_communities( old_communities: pd.DataFrame, delta_communities: pd.DataFrame, community_id_mapping: dict, ) -> pd.DataFrame: # ... # Look for community ids in community and replace them with the corresponding id in the mapping delta_communities["id"] = ( delta_communities["id"] .astype("Int64") .apply(lambda x: community_id_mapping.get(x, x)) ) old_communities["id"] = old_communities["id"].astype("Int64") # Merge the final communities merged_communities = pd.concat( [old_communities, delta_communities], ignore_index=True, copy=False )
-
-
更新社区报告:
- 通过
_update_community_reports
函数,加载旧的社区报告,并与增量社区报告合并,使用社区ID映射进行更新。逻辑同上。
- 通过
详细实现可以参考以下代码:
2.6 iText2KG
iText2KG是一种利用大型语言模型(LLM)从文本中构建增量知识图谱的工具,适合跨领域的知识提取。本文方法允许从文本生成新的知识图谱节点,并实时更新到已有图谱。
- 文章 :arXiv预印本
- Lairgi, Y., Carta, S., & Recupero, D. R. (2024). iText2KG: Incremental Knowledge Graphs Construction Using Large Language Models. arXiv preprint arXiv:2409.03284.
- Accepted at The International Web Information Systems Engineering conference (the WISE conference) 2024
- 优点:利用LLM实现零样本学习,无需预定义本体或大量监督训练。
- 缺点:在特定领域应用时可能需要额外优化,LLM生成内容的质量控制尚需进一步提升。
这篇的特点是基于现有的图谱结构,实现的一个迭代式增量更新。
3. 增量更新的挑战与解决方案
在知识图谱增量更新过程中,需关注以下几个挑战:
- 数据一致性:确保新增或变更的数据不会与现有数据冲突,避免冗余或冲突关系的产生。
- 索引效率:快速构建和更新索引,使新数据可被系统及时检索到。
- 并发处理:在多用户访问或更新的情况下,确保系统的响应速度和数据一致性。
现有实现方案通过不同方法应对上述挑战,例如Microsoft GraphRAG v0.4.0通过引入增量索引功能,显著提升了系统的更新效率,而nano-GraphRAG通过简化代码结构,以降低实现复杂度。
4. 归纳
仔细阅读可以发现,其实现阶段的增量更新范式仅为两种(通过是否修改原数据进行判断。)
同时缺乏足够的增量更新评估机制。大部分没有给出对应的增量更新结果,或仅靠现有的下游QA任务做支撑。
5. 结论
本文对GraphRAG的增量更新实现进行了调研,分析了几种不同实现的特点。通过增量更新机制的引入,GraphRAG能够显著提高数据更新的响应效率,有助于实现更大规模、动态化的知识图谱应用。
如果更新不从融合的角度来看,按照原本CRUD的理解(删除再新增),似乎仍存在一个盲点:做不到删除?
希望读完你也有所收获,谢谢!