当前位置:   article > 正文

手把手教你构建基于知识图谱的GraphRAG之结构化数据篇【LangChain+Neo4j】_graphrag教程

graphrag教程

检索增强生成(RAG)是一种利用外部知识来增强大模型生成能力减少幻觉的主流方法,而对知识最常见的一种组织与索引的形式是向量化及基于向量相近性的检索。但除此之外,基于Graph图结构的知识图谱也是一种强大的知识组织工具,在很多场景下它可以实现更有意义的上下文检索并帮助模型输出更加准确的响应内容。我们将用实例来学习基于知识图谱的GraphRAG应用的构建:

  • 预备知识:GraphRAG基础

  • 构建GraphRAG:结构化数据

  • 构建GraphRAG:非结构化数据

  • 认识微软开源项目:GraphRAG

预备知识:GraphRAG基础

在开始之前,我们先快速了解图(Graph)、图数据库(GraphDB)、知识图谱(Knowledge Graph)以及GraphRAG的基础知识。

图(Graph)

图(Graph)是一种用来表示对象以及它们之间关系的数学结构。任何两个对象之间都可以直接发生联系,所以适合表达更复杂的关系信息。一个图结构的主要的组成是节点和边。

  • 节点:用来表示一个对象。比如社交网络的一个用户。

  • :用来表示对象之间的关系。比如用户之间的关系,如相互关注。

下面是一个关于明星、电影、公司这几种实体的一个Graph例子:

图数据库(GraphDB)

图数据库是一种专门用于存储和操作图结构数据的数据库管理系统。与关系型数据库不同,图数据库使用节点、边和属性来表示和存储数据。这使得它们非常适合处理高度连接的数据,提供高性能的复杂查询能力,用来遍历与发现有洞察力的数据关系。其最大特点是:

  • 灵活的模型:可以方便地表示复杂的关系。

  • 高效的查询:特别是多跳关系的查询,比关系数据库更高效。

  • 可扩展性:能够处理大量节点和边。

图数据库通常具备强大的知识图谱的多跳检索能力:通过多次的关系跳跃来发现相关的信息,这在处理复杂查询与发现关系时特别有用。比如对上面的图提问:

“查找Tom的朋友主演的电影的制作公司的母公司”

这样的查询借助于图数据库的效率要远远高于传统的关系数据库的表连接。

最常见的图数据库管理系统有Neo4j**、**Amazon Neptune、OrientDB、TigerGraph等,被广泛应用于社交网络分析、推荐系统、金融欺诈检测等。

知识图谱与GraphRAG

知识图谱是一种基于图结构的语义网络,用于表示现实世界中的知识。知识图谱不仅包含实体和关系,还包含它们的语义信息。它通常使用图数据库来存储和管理数据,但增加了语义层次,以提供更高级的知识表示和推理能力。

GraphRAG就是一种对存储在图数据库中的知识图谱(而非存储在向量数据库中的知识向量)进行检索,获得关联知识并实现增强生成的LLM应用。这种方法可以更好的表示人类复杂的知识及其关系,并提供高效的检索能力,产生更加相关与丰富的上下文,让LLM生成最佳答案。

  • 知识图谱更适合于需要精确表示实体间关系和进行复杂推理的场景。

  • 向量化方案则更适合于基于相似性匹配和快速检索自然语言知识的应用。

GraphRAG在整体架构与传统RAG并无更大区别,区别在于检索的知识采用图结构的方式进行构建、存储并检索:

基于图数据库进行知识图谱检索的时候,又可以有两种常见方式(取决于图数据库的支持能力):

借助Text-to-GraphQL:把自然语言的输入借助LLM与Text-to-GraphQL技术转换为图数据库的查询语言(比如Neo4j的Cypher语言),再使用图数据库的查询语言从知识图谱中检索出需要的知识。

借助Vector索引:在构建的Graph基础上进一步对其中的节点与关系创建向量索引,并通过向量相似性来检索出相关的节点和关系信息;还可以结合传统的关键词做混合检索(注意区分直接对原始知识做向量检索)。

Cypher 是 Neo4j 图数据库的查询语言,设计用于执行各种图数据操作。它的语法直观且类似于 SQL,可以理解为一种图数据库的SQL。

图数据库的强大之处在于你可以同时把结构化与非结构化的数据通过转化映射,以知识图谱的方式存储到单一的库中。通常来说,Text-to-GraphQL更适合对结构化数据生成的Graph进行查询,而Vector索引更适合对非结构化数据生成的Graph进行查询。

这里将首先展示如何从结构化的内容生成知识图谱并用于RAG查询生成。

构建GraphRAG:结构化数据

在这个例子中,我们把存储在MySQL关系型数据库中的一组信息转化为知识图谱存储到图数据库中,并基于此进行自然语言的提问,以验证基于结构化数据的GraphRAG的构建。

原始数据准备

原始数据以表的形式存储在MySQL数据库中,具有如下的主要实体和关系:

首先创建几个简单的表,借助工具生成必要的测试数据:

  • orders:订单表。包含客户id,产品id,数量,销售员,订单日期等。

  • custoemrs:客户信息表。包含客户id,姓名,电话,电子邮件,城市等。

  • products:产品信息表。包含产品ID,名称,单价等。

  • salespersons:销售员信息表。包含人员id,姓名,电话,所属部门等。

  • departs:部门信息表。包含部门id,部门名称等。

启动Neo4j图数据库

使用如下命令快速启动一个带有APOC插件的Neo4j数据库容器(APOC是一个强大的Neo4j增强功能插件):

docker run \  
    -p 7474:7474 -p 7687:7687 \  
    -v $PWD/data:/data -v $PWD/plugins:/plugins \  
    --name neo4j-apoc \  
    -e NEO4J_apoc_export_file_enabled=true \  
    -e NEO4J_apoc_import_file_enabled=true \  
    -e NEO4J_apoc_import_file_use__neo4j__config=true \  
    -e NEO4JLABS_PLUGINS=\[\"apoc\"\] \  
    neo4j:latest
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

启动完成后,访问**http://localhost:7474/**即可管理Neo4j数据库(注意更改默认密码),在这里你可以更直观的查看数据库中的节点与关系信息,并发起一系列交互命令,比如Cypher查询。

生成Graph

我们需要把传统的RDBMS中的表数据转化为Graph的结构进行存储。参考上文的实体与关系图,这里借助Neo4j的SDK可以快速实现。按照如下顺序来进行:

1. 将数据表读取到本地的pandas的dataframe:

import pandas as pd  
import mysql.connector  
from neo4j import GraphDatabase  
  
def fetch_table_data(table_name):  
    cnx = mysql.connector.connect(  
        host='你的mysql主机',  
        user='你的mysql用户',  
        password='******',  
        database='sales'  
    )  
    cursor = cnx.cursor()  
    query = f"SELECT * FROM {table_name}"  
    cursor.execute(query)  
    rows = cursor.fetchall()  
    column_names = [desc[0] for desc in cursor.description]  
    df = pd.DataFrame(rows, columns=column_names)  
    cursor.close()  
    cnx.close()  
    return df  
  
# 读取到dataframe  
orders_df = fetch_table_data("orders")  
customers_df = fetch_table_data("customers")  
products_df = fetch_table_data("products")  
salespersons_df = fetch_table_data("salespersons")  
departs_df = fetch_table_data("departs")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

2. 创建Graph的节点。即把读取的数据转化为Neo4j中的Node,这里的技巧是读取dataframe的每条记录的key和value来构建节点的属性,并使用Cypher的CREATE语句来批量创建节点;为了在测试时能够不重复创建,这里通过指定的唯一键来防止重复创建(也可以通过CREATE CONSTRAINT给节点类型创建约束):

def create_unique_nodes_from_dataframe(df, label, unique_id_property):  
    # 连接到Neo4j数据库  
    driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "******"))  
    # 创建一个会话来执行Cypher查询  
    with driver.session() as session:  
        # 遍历DataFrame中的每一行  
        for _, row in df.iterrows():  
            # 从行中获取唯一id的值  
            unique_id_value = row[unique_id_property]  
            # 检查是否已经存在具有相同唯一id的节点  
            query = f"MATCH (n:{label} {{{unique_id_property}: '{unique_id_value}'}}) RETURN n"  
            result = session.run(query)  
            if result.single() is not None:  
                # 已经存在具有相同唯一id的节点,跳过创建新节点  
                continue  
            # 创建一个Cypher查询来创建具有给定标签和属性的节点  
            properties = ", ".join(f"{key}: '{value}'" for key, value in row.to_dict().items())  
            query = f"CREATE (n:{label} {{ {properties} }})"  
            # 执行Cypher查询  
            session.run(query)  
  
# 为订单创建节点  
create_unique_nodes_from_dataframe(orders_df, "Order","order_id")  
create_unique_nodes_from_dataframe(customers_df, "Customer","customer_id")  
create_unique_nodes_from_dataframe(products_df, "Product","product_id")  
create_unique_nodes_from_dataframe(salespersons_df, "Salesperson","salesperson_id")  
create_unique_nodes_from_dataframe(departs_df, "Depart","depart_id")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3. 创建Graph的节点关系。关系是Graph最重要的特征,我们通过如下的方法来构建不同类型节点之间的关系,在创建关系时,节点与节点的关系通过指定连接条件(类似RDBMS中的表连接)与关系名称:

def create_relationships():  
    # 连接到Neo4j数据库  
    driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "******"))  
    # 创建一个会话来执行Cypher查询  
    with driver.session() as session:  
        # 在顾客和订单之间创建关系  
        query = "MATCH (c:Customer), (o:Order) WHERE c.customer_id = o.customer_id MERGE (c)-[:ORDERED]->(o)"  
        session.run(query)  
         
        # 在销售人员和订单之间创建关系  
        query = "MATCH (s:Salesperson), (o:Order) WHERE s.salesperson_id = o.salesperson_id MERGE (s)-[:CREATED]->(o)"  
        session.run(query)  
  
        # 在产品和订单之间创建关系  
        query = "MATCH (p:Product), (o:Order) WHERE p.product_id = o.product_id MERGE (p)-[:IS_ORDERED_IN]->(o)"  
        session.run(query)  
  
        # 在销售人员和部门之间创建关系  
        query = "MATCH (s:Salesperson), (d:Depart) WHERE s.depart_id = d.depart_id MERGE (s)-[:BELONGS]->(d)"  
        session.run(query)  
  
# 调用函数来创建关系  
create_relationships()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里的Cypher语句是不是有点类似SQL语句?注意创建关系时我们使用的是MERGE,这也是为了防止重复生成关系。

运行以上代码,如果一切正常,将会在Neo4j数据库中看到你创建所有的Node以及Relationship信息。可以在管理台看到类似下面的统计信息,能够看到创建的Node类型(用label标记)、Relationship类型以及具体数量:

基于Graph实现RAG

你可以借助LangChain中的GraphCypherQAChain组件来快速实现对Graph的检索与答案生成。GraphCypherQAChain的基本原理是把输入的自然语言借助LLM生成Graph查询的Cypher语句,然后获得查询结果,并把结果用于答案生成(Text-to-Cypher)。

使用如下代码(此处使用gpt-4o模型,你也可以尝试使用本地ollma模型),为了更好的控制生成与帮助理解,这里对LangChain的提示词做了显式设置:

from langchain.prompts import PromptTemplate  
cypher_generation_template = """  
任务:  
为Neo4j图数据库生成Cypher查询。  
说明:  
仅使用提供的模式中的关系类型和属性。  
不要使用任何未提供的其他关系类型或属性。  
模式:  
{schema}  
注意:  
在回答中不要包含任何解释或道歉。  
不要回答任何可能要求你构建除Cypher语句之外的任何文本的问题。  
确保查询中的关系方向是正确的。  
确保正确地为实体和关系设置别名。  
不要运行会向数据库添加或删除内容的任何查询。  
确保将后续所有语句都设置别名为with语句。  
如果需要除法运算,请确保将分母过滤为非零值。  
问题是:  
{question}  
"""  
  
cypher_generation_prompt = PromptTemplate(  
    input_variables=["schema", "question"], template=cypher_generation_template  
)  
  
qa_generation_template = """您是一个助手,根据Neo4j Cypher查询的结果生成人类可读的响应。  
查询结果部分包含基于用户自然语言问题生成的Cypher查询的结果。  
提供的信息是权威的,您绝不能怀疑它或尝试使用内部知识来纠正它。  
使答案听起来像对问题的回答。  
查询结果:  
{context}  
问题:  
{question}  
如果提供的信息为空,请说您不知道答案。  
空信息的样子是这样的:[]  
如果信息不为空,您必须使用结果提供答案。  
如果问题涉及时间持续时间,请假设查询结果以天为单位,除非另有说明。  
如果查询结果中有数据,永远不要说您没有正确的信息。始终使用查询结果中的数据。  
有用的回答:  
"""  
  
qa_generation_prompt = PromptTemplate(  
    input_variables=["context", "question"], template=qa_generation_template  
)  
  
from langchain_community.graphs import Neo4jGraph  
from langchain.chains import GraphCypherQAChain  
from langchain_openai import ChatOpenAI  
  
graph = Neo4jGraph(  
    url="bolt://localhost:7687",  
    username="neo4j",  
    password="******",  
)  
  
graph.refresh_schema()  
  
hospital_cypher_chain = GraphCypherQAChain.from_llm(  
    cypher_llm=ChatOpenAI(model='gpt-4o'),  
    qa_llm=ChatOpenAI(model='gpt-4o'),  
    graph=graph,  
    verbose=True,  
    qa_prompt=qa_generation_prompt,  
    cypher_prompt=cypher_generation_prompt,  
    validate_cypher=True,  
    top_k=100,  
)  
  
response = hospital_cypher_chain.invoke("黄彬伟订购了哪些产品?总金额是多少")  
print(response['result'])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

在控制台运行可以观察到如下的输出提示与结果,你可以看到生成的完整Cypher语句和运行结果,以及最后LLM的生成答案:

再测试一些常见的查询问题,都能得到满意的答案。比如:

response = hospital_cypher_chain.invoke("对比下不同产品的销售总额")  
print(response['result'])  
  
response = hospital_cypher_chain.invoke("销售金额最高的是哪一个产品?")  
print(response['result'])
  • 1
  • 2
  • 3
  • 4
  • 5

一个很有意思的尝试是构建一个基于关系型数据库和Text-to-SQL检索技术的RAG查询引擎,然后对两者查询的功能与性能进行对比。根据简单的测试,在关系比较简单的场景下,两者在功能上并没有太大的区别,大部分任务都可以完成;但是如果涉及较复杂的多跳查询,且在数据量较大(如100万以上)时,基于Graph的检索性能会更好。感兴趣的朋友可以自行做深入的研究。

以上我们介绍了如何把结构化的数据通过处理转化成基于Graph结构的知识图谱,并借助于Text-to-GraphQL技术进行RAG应用的检索与增强生成。我们将在下篇介绍如何把非结构化的文本知识抽取形成知识图谱,并构建GraphRAG。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号