返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

Neo4j RecallM:打造无需向量数据库的知识图谱

[复制链接]
链载Ai 显示全部楼层 发表于 半小时前 |阅读模式 打印 上一主题 下一主题

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding-right: 0.2em;padding-left: 0.2em;color: rgb(255, 255, 255);background: rgb(1, 155, 252);">引言

ingFang SC";letter-spacing: 0.1em;color: rgb(63, 63, 63);">在为大语言模型(LLM)提供长期记忆的过程中,主流方法通常涉及检索增强生成(RAG)方案,其中向量数据库充当长期记忆的存储机制。这引发了一个问题:我们能否在没有向量数据库的情况下实现相同的效果?

ingFang SC";letter-spacing: 0.1em;color: rgb(63, 63, 63);">让我们来看看 RecallM: 一种具有时间理解能力的可适应记忆机制(https://arxiv.org/abs/2307.02738),该论文由 Brandon Kynoch、Hugo Latapie 和 Dwane van der Sluis 共同提出。论文提出了一种利用自动构建的知识图谱作为 LLM 长期记忆核心的方法。

ingFang SC";letter-spacing: 0.1em;color: rgb(63, 63, 63);">本文将深入探讨 RecallM 的工作原理,重点介绍其知识图谱的更新方式及推理机制,并通过一系列示例进行说明。

ingFang SC";letter-spacing: 0.1em;color: rgb(63, 63, 63);">我们将首先探讨知识图谱的更新机制,并通过两个具体示例来阐明该过程。接下来,我们将研究 RecallM 的推理机制,并通过示例展示它如何利用知识图谱生成回答。此外,我们还会讨论时间推理的示例,展示 RecallM 在理解和应用时间相关知识方面的能力。最后,我们将分析该方法的局限性,以全面评估其能力及未来改进方向。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding-right: 0.2em;padding-left: 0.2em;color: rgb(255, 255, 255);background: rgb(1, 155, 252);">知识图谱更新

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;padding-left: 8px;color: rgb(63, 63, 63);">第一轮:添加新事实

ingFang SC";letter-spacing: 0.1em;color: rgb(63, 63, 63);">假设我们想将以下句子添加到知识图谱中:

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;padding: 1em;border-radius: 6px;color: rgba(0, 0, 0, 0.5);background: rgb(247, 247, 247);">

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1em;letter-spacing: 0.1em;color: rgb(63, 63, 63);">Brandon 喜欢咖啡

我们需要执行以下步骤:

步骤 1:识别概念

概念通常是句子中的名词,它们可以与其他概念建立关系。

这些概念将成为知识图谱中的节点。

此外,为了避免重复,我们会将概念转换为小写,并使用词干提取(stemming)技术将其归一化。

所有这些操作可以使用 NLP 工具包Stanza来完成。

以下是 Stanza 对句子 "Brandon loves coffee" 进行标注的结果:

[
[
{
"id":1,
"text":"Brandon",
"lemma":"Brandon",
"upos":"PROPN",
"xpos":"NNP",
"feats":"Number=Sing",
"head":2,
"deprel":"nsubj",
"start_char":0,
"end_char":7,
"ner":"S-PERSON",
"multi_ner":[
"S-PERSON"
]
},
{
"id":2,
"text":"loves",
"lemma":"love",
"upos":"VERB",
"xpos":"VBZ",
"feats":"Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin",
"head":0,
"deprel":"root",
"start_char":8,
"end_char":13,
"ner":"O",
"multi_ner":[
"O"
]
},
{
"id":3,
"text":"coffee",
"lemma":"coffee",
"upos":"NOUN",
"xpos":"NN",
"feats":"Number=Sing",
"head":2,
"deprel":"obj",
"start_char":14,
"end_char":20,
"ner":"O",
"multi_ner":[
"O"
],
"misc":"SpaceAfter=No"
}
]
]

在这个例子中,名词是 "Brandon" 和 "coffee"。

使用 Porter 词干提取器进行词干化,并转换为小写后,我们得到 "brandon" 和 "coffe"。

步骤 2:查找每个概念的邻居

概念的邻居是与其存在关系的其他概念。我们可以使用依存句法分析(dependency parser)来确定概念之间的关系。然而,为了简化,我们可以仅根据概念在句子中的“距离”来确定它们的关系。

“距离”指的是概念在句子中出现的位置。因此,"brandon" 位于索引 0,而 "coffe" 位于索引 1。

以下代码片段可以帮助理解“距离”的概念:

#默认距离设为1
deffetch_neigbouring_concepts(concepts,distance=1):
concepts=sorted(concepts)
foriinrange(len(concepts)):
concepts[i].related_concepts=[]
forjinrange(-distance,distance+1,1):#如果当前概念的距离小于参数distance
if0<=i+j<len(concepts):#确保索引在范围内

ifj==0:
continue

ifconcepts[i].name<concepts[i+j].name:#确保在Neo4J图数据库中仅创建一个连接
concepts[i].related_concepts.append(concepts[i+j])

执行此步骤后,"brandon" 与 "coffe" 建立了关系,而 "coffe" 没有与 "brandon" 建立关系。

步骤 3:创建概念节点及其关系

我们可以使用图数据库来存储概念及其关系。

在 Neo4J 中,最终的知识图谱如下所示:

图 1:t=1 时的知识图谱

红色节点表示概念,它们通过“RELATED”(白色箭头)关系相连。

蓝色节点是“全局时间步”(global timestep),在时间推理中起着重要作用。其值为 1,表示这是第一次更新知识图谱。

以下是创建该图的 Cypher 查询:

MERGE(c00:Concept{name:'brandon'})
MERGE(c01:Concept{name:'coffe'})
WITHc00,c01
MERGE(c00)-[rc00c01:RELATED]->(c01)
WITHc00,c01,rc00c01,COALESCE(rc00c01.strength,0)+1ASrc00c01ic
SETc00.t_index=1,c01.t_index=1
SETrc00c01.strength=rc00c01ic
SETrc00c01.t_index=1

需要注意两点:

  1. 1. 节点和关系都有一个t_index属性,用于跟踪它们上次更新的时间步。

  2. 2. “RELATED” 关系有一个strength属性,用于跟踪该关系在知识图谱更新过程中被提及的次数。

步骤 4:添加概念的上下文

概念的上下文是指它在句子中的使用情况。

因此,对于每个概念,我们首先从知识图谱中获取其上下文信息:

MATCH(n:Concept)
WHEREn.nameIN['brandon','coffe']
RETURNn.name,n.context,n.revision_count

由于这是我们首次添加这些概念,查询结果将为空。

接下来,我们将当前上下文(即句子 "Brandon loves coffee")添加到概念节点,并更新知识图谱:

MATCH(n:Concept)
WHEREn.nameIN['brandon','coffe']
WITHn,
CASEn.name
WHEN'brandon'THEN'.Brandonlovescoffee'
WHEN'coffe'THEN'.Brandonlovescoffee'
ELSEn.context
ENDASnewContext,
CASEn.name
WHEN'brandon'THEN0
WHEN'coffe'THEN0
ELSE0
ENDASrevisionCount
SETn.context=newContext
SETn.revision_count=revisionCount

请注意,"brandon" 和 "coffe" 具有相同的上下文,因为它们出现在同一句话中,而我们目前仅处理这一句话。

最终的知识图谱如下所示:

图 2:t=1 时的知识图谱(添加概念上下文后)

第二轮:扩展事实

现在,我们要将以下句子添加到知识图谱中:

Brandon 想去巴黎旅行

该句中的概念是 "Brandon" 和 "Paris"。

经过词干化和小写转换后,我们得到 "brandon" 和 "pari"。

将这些概念添加到知识图谱后,我们得到如下结构:

图 3:t=2 时的知识图谱(添加

请注意:

  • • 全局时间步(global timestep)已更新为 2。

  • • 新概念 "pari" 已添加到知识图谱中。

  • • "brandon" 和 "pari" 通过 "RELATED" 关系相连。

  • • "brandon" 的t_index更新为 2,因为它在时间步 2 进行了更新。

以下是用于更新知识图谱的 Cypher 查询:

MERGE(c00:Concept{name:'brandon'})
MERGE(c01:Concept{name:'pari'})
WITHc00,c01
MERGE(c00)-[rc00c01:RELATED]->(c01)
WITHc00,c01,rc00c01,COALESCE(rc00c01.strength,0)+1ASrc00c01ic
SETc00.t_index=2,c01.t_index=2
SETrc00c01.strength=rc00c01ic
SETrc00c01.t_index=2

更新概念的上下文

首先,我们从知识图谱中获取概念的上下文信息:

{
"brandon":{
"context":".Brandonlovescoffee",
"revision_count":0
},
"pari":{
"context":null,
"revision_count":null
}
}

可以看到,"brandon" 已经有了上下文,而 "pari" 还没有。

然后,我们将新句子追加到已有的上下文中,并更新知识图谱:

MATCH(n:Concept)
WHEREn.nameIN['brandon','pari']
WITHn,
CASEn.name
WHEN'brandon'THEN'.Brandonlovescoffee.BrandonwantstotraveltoParis'
WHEN'pari'THEN'.BrandonwantstotraveltoParis'
ELSEn.context
ENDASnewContext,
CASEn.name
WHEN'brandon'THEN1
WHEN'pari'THEN0
ELSE0
ENDASrevisionCount
SETn.context=newContext
SETn.revision_count=revisionCount

请注意,"brandon" 的上下文现在包含了之前的句子和新句子。此外,它的revision_count也更新为 1,因为它的上下文被更新了。

最终的知识图谱如下所示:

图 4:t=2 时的知识图谱(添加概念上下文后)

推理机制

假设我们要回答以下问题:

谁想去巴黎旅行?

我们可以利用知识图谱来生成答案,具体步骤如下:

步骤 1:识别概念

使用与更新知识图谱时相同的 NLP 处理流程,我们确定问题中唯一的概念是 "pari"。

步骤 2:查找相关概念

我们在知识图谱中查找与 "pari" 相关的概念:

MATCH(startNode:Concept{name:'pari'})
CALLapoc.path.spanningTree(startNode,{relationshipFilter:"",minLevel:0,maxLevel:2})YIELDpath
WITHpath,nodes(path)aspathNodes,startNode.t_indexascurrent_t
UNWINDrange(0,size(pathNodes)-1)ASindex
WITHpath,pathNodes[index]asnode,current_t
ORDERBYnode.t_indexDESC
WHEREnode.t_index<=current_tANDnode.t_index>=current_t-15
WITHDISTINCTnodeLIMIT800
MATCH()-[relation]->()
RETURNnode,relation

该查询会找到从 "pari" 出发、深度最多为 2 的所有路径,并筛选出时间步范围在current_t - 15current_t之间的概念。

查询结果如下:

{
"pari":".BrandonwantstotraveltoParis",
"brandon":".Brandonlovescoffee.BrandonwantstotraveltoParis",
"coffe":".Brandonlovescoffee"
}

步骤 3:对相关概念进行排序

我们根据t_index(时间步)和概念之间的关系强度对相关概念进行排序。

以下代码片段用于计算排序分值sort_val

graph_concepts=graph_concept_nodes.values()
forconceptingraph_concepts:
concept.sort_val=0
forrelationinconcept.related_concepts:
concept.sort_val+=(relation.t_index*3)+relation.strength#3是一个超参数,可调整
graph_concepts=sorted(graph_concepts,key=lambdac:c.sort_val,reverse=True)#按降序排列

排序后,各个概念的sort_val如下:

步骤 4:构建上下文

我们首先获取问题中涉及的“核心概念”(essential concepts),即 "pari"。

其上下文为:

.BrandonwantstotraveltoParis

然后,我们按照sort_val的降序依次添加相关概念的上下文:

. Brandon loves coffee #相关概念:coffe

. Brandon loves coffee. Brandon wants to travel to Paris #相关概念:brandon

. Brandon wants to travel to Paris #核心概念:pari

步骤 5:生成回答

接下来,我们将构建的上下文作为提示词(prompt)传递给 LLM,并生成答案。

最终的提示词如下:

使用以下陈述(statements)来回答问题(question)。这些陈述按照时间顺序排列,每句陈述在其时间步内均为真实:

statements:
.Brandonlovescoffee

.Brandonlovescoffee.BrandonwantstotraveltoParis

.BrandonwantstotraveltoParis

question:
谁想去巴黎旅行?

Answer:

由于提示词已经包含了足够的信息,GPT-3.5-Turbo 能够正确生成答案:"Brandon"。


时间推理(Temporal Reasoning)

论文作者设计了一项实验,以评估 RecallM 在时间理解和记忆更新方面的能力。

他们构建了一个包含按时间顺序排列的陈述的数据集,每个新陈述都会更新之前的事实。然后,他们在不同时间步对系统进行提问,以测试其时间推理能力。

实验包括两类问题:

  1. 1.标准时间推理问题:测试系统对时间概念的理解,以及它是否能正确更新记忆。

  2. 2.长距离时间推理问题:要求系统回忆数百个时间步之前的信息,并进行推理。例如,在 25 轮更新后,系统需要回忆 1500 多个更新之前的知识。

以下是论文提供的一些实验结果,展示了 RecallM 在时间推理方面的有效性,并与向量数据库进行了对比:

图 5:RecallM 与向量数据库在时间推理任务上的对比

局限性

该方法的主要局限性在于知识图谱的构建,特别是缺乏指代消解(coreference resolution)

例如,假设我们向系统输入以下文本:

Brandon 喜欢咖啡。他想去巴黎旅行。他喜欢猫。

理想情况下,知识图谱应如下所示:

图 6:理想的知识图谱

但实际上,我们得到的是:

图 7:实际生成的知识图谱

尽管如此,像以下问题:

  • •谁想去巴黎旅行?

  • •谁喜欢猫?

  • •谁喜欢咖啡?

仍然能够得到正确答案,因为生成的上下文仍然包含 "Brandon loves coffee",这有助于 LLM 生成正确的回答。

但在某些情况下,可能会出现错误。

一种可能的解决方案是将文本转换为“命题”,然后将这些命题存入知识图谱,而不是直接存储原始文本。这一想法在论文 Dense X Retrieval: What Retrieval Granularity Should We Use?(https://arxiv.org/abs/2312.06648) 中有所探讨。


结论

总的来说,RecallM 提出了一种仅使用图数据库就能为 LLM 提供长期记忆的方法。

该方法在知识更新和时间推理方面表现出色,但在构建精确知识图谱方面仍然存在挑战。

尽管如此,RecallM 代表了 AI 记忆机制的一项重要进展,未来的研究仍有很多改进和优化的空间。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ