GraphRAG:利用知识图谱优化检索工作

2024年08月30日 由 alex 发表 196 0

检索增强生成(RAG)将 LLM 的自然语言生成能力与信息检索的优势结合在一起,实现了更多上下文感知、准确、相关和细微差别的响应。通过将检索纳入生成过程,RAG 系统可保持较高的相关性和事实准确性,使其成为知识管理、客户支持和研究等应用领域不可或缺的工具,因为在这些应用领域,准确且符合上下文的信息至关重要。


传统的检索增强生成(RAG)技术虽然在某些情况下很有效,但往往难以捕捉现实世界数据中错综复杂的关系和上下文的细微差别。另一方面,知识图谱提供了一种结构化的信息表示法,可以实现更高效的检索和推理。然而,如何有效地将知识图谱与 LLM 集成以提高 RAG 性能仍然是一项具有挑战性的任务。


在 RAG 系统中使用知识图谱可以改进数据组织和检索精度,这一点已得到越来越多的认可。基于图的方法现在通常与使用大型语言模型(LLMs)从文本语料库中提取和建立复杂关系相关联,微软公司在今年早些时候首次引入了这种方法。然而,使用 LLM 构建和维护精确的知识图谱仍然是资源密集型的,面临着数据稀少、重复和需要持续更新等挑战。为了解决这些问题,现在的趋势是使用模块化和分层图结构,这样可以有效管理大型数据集。为了提高基于图的 RAG 系统的可扩展性和效率,我们采用了社群检测和汇总等技术。


在本文中,我将重点介绍我称之为固定实体架构(FEA)的新方法。微软的 GraphRAG 和 FEA 这两种方法都能解决知识图构建和使用中的常见问题,如可扩展性、复杂性和精确性。这些方法代表了将知识图谱与 RAG 整合的最新进展,利用了 LLM 和高效的图谱结构技术。这是两种截然不同的方法,取决于给定的用例和给定的数据。这两种方法适用于不同类型的数据和查询,因此可广泛应用于各个行业和研究领域。


GraphRAG 和固定实体架构分析


GraphRAG - 现有微软方法概述

2024 年 4 月,微软发表了第一篇关于 GraphRAG 的论文,介绍了一种有趣的方法,即使用大型语言模型 (LLM) 从文本语料库中提取实体和关系,并基于这些实体构建知识图谱。他们将实体聚合到社区中,然后社区成为实体内容的摘要。在这些摘要上进行了实际的检索增强生成(RAG),展示了一种在信息检索方面具有巨大潜力的方法。


与任何方法一样,GraphRAG 也有其优点和缺点。下面的表 1 从我的角度强调了 Microsoft GraphRAG 方法的优点和挑战。值得注意的是,在某些使用案例中,尤其是在处理大量文本数据的案例中,事先了解实体之间的关系并不总是适用的。


本文介绍了使用固定实体架构在图上构建 RAG 的 FEA 方法。知识图谱是使用 Neo4J 创建的。


固定实体架构


固定实体架构--新方法简介

本文提出的固定实体架构基于预定义的实体和关系,这些实体和关系构成了用例域的本体论 “鱼骨”。确定在此结构中包含哪些内容往往是一个深奥的哲学问题,需要广泛的领域知识才能开发出稳健的固定实体架构。或者,你也可以考虑你需要知识库做什么,并确定关键文档或模板文档作为本体鱼骨的基础。


与微软的方法不同,固定实体架构不依赖大型语言模型(LLM)来构建图。取而代之的是,它利用专有的领域知识,结合直接的数学技术,对用例进行特定的处理。这种方法为解决与基于 LLM 的方法相关的许多缺点提供了一种非常有效的方法。


比较概述

表 1 提供了微软的 GraphRAG 和固定实体架构这两种方法的比较概述。


1

表1


总之,固定实体架构非常适合需要高精度和高控制的定义明确的狭窄领域。它具有较低的复杂性、较低的计算成本,并能最大限度地减少对 LLM 的依赖。但是,它缺乏灵活性,在大型数据集的可扩展性方面比较吃力,而且需要事先具备领域知识。


微软的 GraphRAG 在处理大型、多样化数据集和复杂查询方面表现出色,具有跨领域的可扩展性和适应性。它支持本地和全局查询,但复杂度较高、资源成本较高,而且对 LLM 有很强的依赖性。当优先考虑简单性、低维护或固定实体时,它就不那么合适了。


这里的建议是,根据数据集的性质、控制需求和可用资源进行选择。将两种方法的要素结合起来,可以优化性能。


本体论鱼骨

什么是本体?你是否考虑过自己微观世界的本体?每天工作时,你都在执行任务,却没有意识到大脑是如何识别这些 “事物 ”以及如何在它们之间建立联系的。试试这个练习:尝试为你每天所做或使用的 “事物 ”创建一个元模型,然后绘制出它们之间的联系。虽然这听起来很有挑战性,但我有个好消息--这是完全可能的,因为你知道自己在做什么!


本体论是帮助我们理解周围世界的强大工具。鱼骨 "比喻鼓励我们深入研究定义我们概念领域的基本构件和联系。创建本体论需要仔细考虑包括哪些内容、元素之间的关系以及哪些内容最重要。鱼骨结构可以帮助我们确定核心要素,同时承认错综复杂的联系网络使我们的想法栩栩如生。


现在,想象一下,你在一个狭窄的领域内工作,对构成你工作的 “鱼骨 ”实体有一个清晰的认识。如何才能轻松获取所有这些信息,并将其连接起来,为任何 RAG 应用程序建立一个知识库呢?


创建基本的 “鱼骨 ”结构: 爱因斯坦示例

在许多组织中,主题专家可以轻松识别定义明确的领域中的关键实体和关系。这些基础知识对于构建有效的知识图谱至关重要。利用这些专业知识,可以在几天内建立关键实体及其关系的基本 “鱼骨 ”结构,为进一步丰富详细信息奠定坚实的基础。


在创建鱼骨图时,请记住尽可能包含对实体的描述。让我们举一个著名的例子来说明知识图谱是如何构建的:“阿尔伯特-爱因斯坦提出了相对论,彻底改变了理论物理学和天文学”。这个著名的句子展示了如何提取实体和关系。下图显示了根据这句话构建的图。


2


Cypher 是一种声明式查询语言,用于与图形数据库(尤其是 Neo4j)进行交互。它允许用户用类似 SQL 的直观语法表达复杂的图模式和关系。Cypher 设计为人类可读,并为查询、更新和管理图数据提供了强大的功能。它支持节点和关系的模式匹配、过滤和操作,是涉及社交网络、推荐引擎和其他基于图形的数据模型的理想工具。


在此,我假设读者熟悉嵌入、余弦相似性、点积和向量索引等术语。


在我们的爱因斯坦示例中,我们有四个实体(阿尔伯特-爱因斯坦、相对论、理论物理学和天文学)和三条边,其中边有两种类型(发达边和革命边)。我们可以为每个实体添加简短的描述,例如 “阿尔伯特-爱因斯坦是 20 世纪伟大的物理学家 "等。将其转换为 Cypher 代码后,你创建的图将如下所示:


CREATE (a:Entity {label: 'Person' , name: 'Albert Einstein', embeddings: $person_emb}),
(b:Entity {label:'Theory' ,name: 'Theory of relativity', embeddings: $theory_emb}),
(c:Entity {label:'Field', name: 'Theoretical physics', embeddings: $field1_emb }),
(d:Entity {label:'Field', name: 'Astronomy', embeddings: $field2_emb })
// Create edges
CREATE (a)-[:DEVELOPED]->(b),
(b)-[:REVOLUTIONIZED]->(c),
(b)-[:REVOLUTIONIZED]->(d)


请注意,我创建的节点只有一个名为 “实体 ”的关联标签,但我为每个节点添加了不同的标签作为属性。这样做是为了在以后的搜索中保持清晰。所有节点在内部被称为实体,它们构成了后续操作的固定鱼骨结构。


采用这种方法的另一个原因是,我还没有找到同时为两个或多个节点标签建立统一索引的方法。


这里的 embeddings 参数只包含 “label: name ”值,但在实践中,我建议包含对实体的详细描述,并将其添加到 embeddings 向量中。


添加知识

现在,我们有了 GraphDB 的 “鱼骨”,我们将在此基础上建立知识库,以便在 RAG 应用程序中进行检索(图 2)。要创建这个知识库,我们需要文档。对于阿尔伯特-爱因斯坦,我使用了维基百科的一篇文章[6]。我使用维基百科 API 复制了文本,并将其分割成 59 个块,每个块的大小为 2000。请注意,在这个演示中,我并没有优化块的大小;所选的大小是任意的。


3


向图表添加文档

让我们按以下方式将文档块添加到图表中:


prev_node_id = None  # Initialize prev_node_id before the loop
for i, chunk in enumerate(chunks):
    # Create the chunk node
    query = f'''
    CREATE (d:Document {{
        chunkID: "{f"chunk_{i}"}",
        url: "{url}",
        docID: "{document_name}",  
        full_text: '{escaped_chunk}', 
        embeddings: {embeddings.embed_documents(chunk).tolist()}}}
    )
    RETURN ID(d)
    '''
    run_query(driver, query)
    chunk_node_id = result[0]['ID(d)']
    # If this is not the first chunk, create a NEXT relationship to the previous chunk
    if prev_node_id is not None:
        query = f'''
        MATCH (c1:Document), (c2:Document)
        WHERE ID(c1) = {prev_node_id} AND ID(c2) = {chunk_node_id}
        CREATE (c1)-[:NEXT]->(c2)
  CREATE (c2)-[:PREV]->(c1)
        '''
        run_query(driver, query)
    prev_node_id = chunk_node_id


该代码会将数据块添加为标有 “文档 ”的节点。


4


将文档块相互连接起来为以后的检索提供了很大的优势。你可以使用 Cypher 查询定义,如果找到类似的文档块,你还可以检索上一个和下一个文档块。就是这么简单!


将文档与固定实体连接起来

接下来,让我们将文本块与固定实体 “鱼骨 ”连接起来。请注意,这种方法没有使用任何昂贵的 LLM 技术,并避免了在知识库中产生重复。此外,它只需几毫秒即可执行。我们使用一个名为点积的简单数学公式,它提供了每个数据块与固定实体架构之间的余弦相似度值。下面的 Cypher 代码将实现这一目的:


MATCH (e:Entity), (d:Document)
WHERE e.embeddings IS NOT NULL AND d.embeddings IS NOT NULL AND size(e.embeddings) = size(d.embeddings)
WITH e, d, 
     reduce(numerator = 0.0, i in range(0, size(e.embeddings)-1) | numerator + toFloat(e.embeddings[i])*toFloat(d.embeddings[i])) as dotProduct,
     sqrt(reduce(eSum = 0.0, i in range(0, size(e.embeddings)-1) | eSum + toFloat(e.embeddings[i])^2)) as eNorm,
     sqrt(reduce(dSum = 0.0, i in range(0, size(d.embeddings)-1) | dSum + toFloat(d.embeddings[i])^2)) as dNorm
WITH e, d, dotProduct / (eNorm * dNorm) as cosineSimilarity
WHERE cosineSimilarity > 0.8
MERGE (e)-[r:RELATES_TO]->(d)
ON CREATE SET r.cosineSimilarity = cosineSimilarity
RETURN ID(e), e.name, ID(d), d.full_text, r.cosineSimilarity
ORDER BY cosineSimilarity DESC


在本例中,我们指示数据库将所有实体节点与每块文本进行比较,计算它们向量之间的夹角。如果余弦相似度超过给定的阈值(本例中为 0.8),数据库就会在实体节点和文档之间创建一条名为 RELATES_TO 的边。由于分块没有经过优化,而且固定实体缺乏详细描述,因此匹配度可能不是很高。本例旨在说明该技术。


5


6


下图显示了这一查询的结果。其中一些数据块自动链接到了固定实体,其余弦相似度高于 0.8 的阈值。你可以根据需要调整该阈值。这种方法可以在检索过程中进行进一步过滤,让你只选择最匹配的数据块。


不仅 “阿尔伯特-爱因斯坦 ”实体与相关文本块相连,“相对论 ”也是如此。随着文档数量的增加,“鱼骨 ”实体将越来越多地充当元素之间的连接器。这是使用图的优势之一--几乎所有东西都是相互关联的。不存在需要聚类的稀疏性问题。


7


将文档 [6-9] 附加到爱因斯坦句子中的固定实体的结果所示。

语块与相关实体无缝链接。


这种附加文档的技术用途广泛,可用于各种类型的文档。通过将数据矢量化,你可以轻松整合和连接所有信息。


8


为混合搜索建立不同类型的索引

Neo4j 的魅力在于它能为你的图表创建各种类型的索引,更妙的是,你只需使用一个 Cypher 查询就能执行混合搜索。让我们为检索准备索引。


创建向量索引

首先,我们创建向量索引:


queries = [
"DROP INDEX test_index_document IF EXISTS;",
"DROP INDEX test_index_entity IF EXISTS;"
]
for query in queries:
    run_query(driver, query)
# create vector index on document embeddings
query = '''CREATE VECTOR INDEX test_index_document
IF NOT EXISTS
FOR (d:Document)
ON (d.embeddings)
OPTIONS {indexConfig: {
`vector.dimensions`: 768,
`vector.similarity_function`: 'cosine'
}}
'''
run_query(driver, query)
# create vector index on entity embeddings
query = '''CREATE VECTOR INDEX test_index_entity IF NOT EXISTS
FOR (n:Entity)
ON (n.embeddings)
OPTIONS {indexConfig: {
`vector.dimensions`: 768,
`vector.similarity_function`: 'cosine'
}}
'''
run_query(driver, query)


创建节点的矢量索引后,如果你有边的描述,也可以创建边的矢量索引。由于现阶段我还没有为关系创建任何文本,因此将跳过这一步。


创建文本索引

接下来,我将创建一个文本索引,它也将作为标准关键字索引添加到搜索中。


queries = [
    "DROP INDEX text_index_entity IF EXISTS;",
    "DROP INDEX text_index_document IF EXISTS;",
]
for query in queries:
    run_query(driver, query)
# create full-text index on Entity description
query = '''
CREATE FULLTEXT INDEX text_index_entity FOR (n:Entity) ON EACH [n.name]
'''
run_query(driver, query)
# create full-text index on Document full_text
query = '''
CREATE FULLTEXT INDEX text_index_document FOR (d:Document) ON EACH [d.full_text]
'''
run_query(driver, query)


检索

现在,我们的知识库已经准备好进行检索和构建 RAG 应用程序,我们可以开始查询图形数据库了。


让我们从一个简单的查询开始。我们将要求数据库找到与给定查询最匹配的答案。例如,我们将使用以下查询: “阿尔伯特-爱因斯坦的研究领域”


首先,我将使用基于实体的向量索引。该查询包括为向量索引嵌入原始用户查询和/或将其作为文本用于基于关键字的索引。我将从一个简单明了的查询(查询 1)开始:


CALL db.index.vector.queryNodes('test_index_entity', 10, $user_query)
YIELD node AS vectorNode, score as vectorScore
WITH vectorNode, vectorScore
ORDER BY vectorScore DESC
RETURN vectorNode.name AS label, vectorScore AS score


在这里,我只查询固定实体,特别是使用建立在固定实体上的矢量索引。结果如下。


9


由于我们只有四个实体,因此结果是可以预测的。仅提取实体,尤其是在此阶段不提取描述,对 RAG 应用的价值有限。为了提升结果,我们需要提取与问题相关的文档。让我们更进一步,找出与找到的实体最相关的文档(查询 2):


CALL db.index.vector.queryNodes('test_index_entity', 10, $user_query)
YIELD node AS vectorNode, score as vectorScore
WITH vectorNode, vectorScore
MATCH (vectorNode)-[r]->(d:Document)
WITH vectorNode.name AS label, vectorScore as score, d.docID as closest_document_name,
     d.full_text as closest_document_text, r.cosineSimilarity as similarity
ORDER BY similarity DESC
RETURN label, closest_document_name, closest_document_text, similarity
LIMIT 10


结果所示,与初始查询略有不同。在这里,“天文学 ”首先被返回。不过,接下来出现的不是预期的 “相对论”,而是 “阿尔伯特-爱因斯坦”。使用 GraphRAG 这种方法的优势在于它的灵活性:你可以调整搜索以更好地满足你的应用需求。让我们改进查询,将 “天文学 ”和 “相对论 ”作为这个问题的首要结果。


10


为了进一步提高结果,我们可以实施跨编码器重新排序步骤。虽然我通常会对描述进行重新排序,但在本例中,由于缺乏描述,我们将对标签进行重新排序。为此,我将使用 “sentence_transformers ”库中的 “cross-encoder/ms-Marco-MiniLM-L-6-v2 ”模型。表显示了查询 2 的重新排序结果。


11


正如你所看到的,结合查询进行重新排序并没有产生理想的结果。为了改善结果,我们可以结合全文或关键词搜索(查询 3):


CALL db.index.vector.queryNodes('test_index_entity', 10, $my_query_emb_list)
YIELD node AS vectorNode, score as vectorScore
WITH vectorNode, vectorScore
MATCH (vectorNode)-[r]->(d:Document)
WITH vectorNode.name AS label, vectorScore as score, d.docID as closest_document_name,
     d.full_text as closest_document_text, r.cosineSimilarity as similarity
ORDER BY similarity DESC
RETURN label, closest_document_name, closest_document_text, similarity
LIMIT 10
UNION
CALL db.index.fulltext.queryNodes('text_index_entity', $my_query)
YIELD node AS textNode, score as textScore
WITH textNode, textScore
MATCH (textNode)-[r]->(d:Document)
WITH textNode.name AS label, textScore as score, d.docID as closest_document_name,
     d.full_text as closest_document_text, r.cosineSimilarity as similarity
ORDER BY similarity DESC
RETURN label, closest_document_name, closest_document_text, similarity
LIMIT 10


查询结果如下 所示。


12


之前的尝试,包括重新排序,都没有取得预期的效果。现在,我将展示图形数据库检索的真正威力。为了避免 APOC 库的问题以及与 Python 工具的兼容性问题,我将专注于一种清晰、透明的方法。我将使用纯数学方法来说明 GraphDB 搜索功能的有效性。下一个查询展示了我工作中所说的 “智能搜索”(查询 4)。


CALL db.index.vector.queryNodes('test_index_entity', 10, $user_query_emb)
YIELD node AS vectorNode, score as vectorScore
WITH vectorNode, vectorScore
MATCH (vectorNode)-[r]->(d:Document)
WITH DISTINCT vectorNode as e, d, r, r.cosineSimilarity as cosineSimilarity
ORDER BY cosineSimilarity DESC
WITH ID(e) as id, e.label as title, 
    cosineSimilarity,
    e.description as description, 
    head(collect(d.docID)) as document_id,
    head(collect(d.chunkID)) as chunkID,
    head(collect(d.full_text)) as document_text,
    reduce(mDot = 0.0, i IN range(0, size($user_query_emb) - 1) | mDot + $user_query_emb[i] * e.embeddings[i]) / 
    (sqrt(reduce(mSq = 0.0, x IN $user_query_emb | mSq + x^2)) * sqrt(reduce(eSq = 0.0, y IN e.embeddings | eSq + y^2))) AS entity_similarity, 
    reduce(mDot = 0.0, i IN range(0, size($user_query_emb) - 1) | mDot + $user_query_emb[i] * d.embeddings[i]) / 
    (sqrt(reduce(mSq = 0.0, x IN $user_query_emb | mSq + x^2)) * sqrt(reduce(dSq = 0.0, y IN d.embeddings | dSq + y^2))) AS document_similarity
WITH id, title, description, document_id, document_text, entity_similarity, document_similarity, chunkID, 
    (entity_similarity + document_similarity) / 2 AS similarity, cosineSimilarity
WHERE similarity > 0.8
RETURN id, title, document_id, document_text, similarity, chunkID
ORDER BY cosineSimilarity DESC, similarity DESC


对于该查询,我首先使用向量索引提取固定实体,然后找出最佳匹配文档。接着,我计算提取的实体与用户查询之间的点积,以及找到的文档与用户查询之间的点积。然后,我合并这些结果,并以 0.8 为阈值进行过滤。这种方法与重新排序相结合,得出了以下结果:


13


通过这个实验,我旨在展示 GraphDB 的检索能力。通过对固定实体和添加文档过程的完全控制,以及构建几乎任何检索 Cypher 查询的灵活性,GraphDB 的潜力是巨大的。根据特定用例定制检索过程的自由度几乎是无限的。在本实验中,我们使用的是有限的固定实体架构,缺乏广泛的描述和元素之间的关系。但是,如果加入更多的信息和联系,就能取得显著的效果。


总结

本文探讨了如何使用 GraphDB 构建和查询知识库,重点是固定实体架构及其在检索增强生成(RAG)系统中的应用。固定实体架构依靠预定义的实体和关系来构建知识图谱,具有复杂性低、精度高和计算成本低等优点。然而,它在灵活性和可扩展性方面可能会受到限制。


文章演示了使用 “鱼骨 ”本体构建知识图谱并将文档集成到图谱中的过程。通过利用矢量索引和 Cypher 查询,根据余弦相似度将文档连接到实体。这种方法增强了检索能力,但可能需要优化才能获得更好的结果。


为了评估基于图的检索方法的有效性,我们进行了一系列查询。最初的查询使用向量索引和关键词搜索来查找匹配的实体和文档。尽管存在一些挑战,如重新排序结果不理想,但文章展示了 GraphDB 在强大、灵活检索方面的潜力。最后的智能搜索查询结合了向量相似性和点积计算,说明了该方法在完善搜索结果方面的有效性。





文章来源:https://medium.com/@irina.karkkanen/rag-on-graph-db-using-fixed-entity-architecture-make-you-retrieval-work-for-you-f4bfcac5277f
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消