从文本构建知识图谱一直是研究者们感兴趣的一个领域。随着大型语言模型(LLMs)的兴起,这一领域得到了更多的公众关注。然而,使用LLMs的成本较高。另一种方法是微调较小的模型,这种方法得到了学术界的认可,从而产生了更高效的解决方案。今天我们来聊聊由罗马萨皮恩扎大学自然语言处理小组开发的Relik,这是一个快速且轻量级的信息提取模型运行框架。
如下是一个典型的不使用大型语言模型的信息抽取流程:
信息抽取流程 — 作者提供图片
该图展示了信息提取管道,从包含文本“Tomaz喜欢写博客文章。他特别感兴趣于绘制图表。”的输入数据开始。流程首先通过共指消解来识别“Tomaz”和“他”是同一个实体。接着,命名实体识别(NER)识别了“Tomaz”、“博客”和“图表”等实体。
实体链接是在命名实体识别(NER)之后的一个步骤,其中识别出的实体会被映射到数据库或知识库中的相应条目上。例如,“Tomaz”链接到“Tomaz Bratanic (Q12345)”,“Blog”链接到“Blog (Q321)”,但“Diagram”在知识库中找不到匹配项。
例如,系统识别出Tomaz和Blog之间存在由“WRITES”描述的关系,表明Tomaz写博客。另外,系统还识别出Tomaz和Diagram之间存在由“INTERESTED_IN”描述的关系,表明Tomaz对图表感兴趣。
最后,这些结构化的信息,包括实体及其关系,会被存入一个知识图中,从而使数据易于组织和访问,以便进一步分析或检索信息。
在过去,没有大型语言模型(LLM)的能力时,整个过程依赖于一系列专门的模型,每个模型负责从共指解析到关系抽取等特定任务上。虽然整合这些模型需要更多的努力和协调,但是可以带来显著的优势:更低的成本。通过微调较小的、特定任务的模型,可以把整个系统的构建和维护成本控制在较低水平。
代码可以在 GitHub 上找到:GitHub。
环境搭建我建议你使用一个单独的Python环境,比如 Google Colab,因为我们可能需要调整一些依赖项。模型在GPU上运行更快,所以如果你有Pro版本,你可以使用一个带有GPU的运行时环境。
此外,我们需要部署Neo4j,一个原生图数据库,来存储提取的信息。有多种方式可以部署数据库实例。然而,推荐使用Neo4j Aura,它提供了一个免费的云实例,可以从Google Colab笔记本中轻松访问。
Neo4j Aura - 全面托管的云图数据库解决方案使用 AuraDB(我们的图数据库服务)构建应用程序,处理高度关联的数据,实现闪电般的查询性能…neo4j.com创建数据库后,我们可以使用 LlamaIndex 来定义连接:
从 llama_index.graph_stores.neo4j 中导入 Neo4jPGStore username="neo4j" password="rubber-cuffs-radiator" url="bolt://54.89.19.156:7687" graph_store = Neo4jPGStore( username=username, password=password, url=url, refresh_schema=False )数据集(也就是数据集合的意思)
数据集就是...
我们将使用一个新闻数据集,这个数据集是我之前从Diffbot API获得的。该数据集已经在GitHub上提供了方便的重复利用:
import pandas as pd # 文章数量设置为100 文章数量 = 100 news = pd.read_csv( "https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/news_articles.csv" ) # 获取前100条新闻数据 news = news.head(文章数量)核心指代消解
管道的第一步是核心指代消解模型。指代消解的任务是识别文本中指代同一实体的所有表达。
据我了解,可供使用的开源核心指代消解模型并不多。我尝试了maverick-coref,但在我的测试中,来自spaCy的Coreferee表现更好,所以我们会选择使用它。使用Coreferee的唯一缺点是会遇到依赖冲突的问题,这个问题在笔记本里解决了,但这里我们就不细说了。
下面的代码可以用来加载spaCy的共指消解模型:
# 加载代码在这里
导入spacy和coreferee coref_nlp = spacy.load('en_core_web_lg') # 'en_core_web_lg' 是一个预训练的英文模型 coref_nlp.add_pipe('coreferee') # 将 'coreferee' 添加到处理管道中,用于处理共指消解
Coreferee模型能够检测指同一个实体或一组实体的表达群。要根据这些群来重写文本,我们需要实现我们自己的重写函数。
def coref_text(text): coref_doc = coref_nlp(text) resolved_text = "" for token in coref_doc: repres = coref_doc._.coref_chains.resolve(token) if repres: resolved_text += " " + " and ".join( [ t.text if t.ent_type_ == "" else [e.text for e in coref_doc.ents if t in e][0] for t in repres ] ) else: resolved_text += " " + token.text return resolved_text
咱们试试这个功能,确保一下模型和相关依赖正确设置,看看有没有问题。
打印( coref_text("Tomaz 很酷。他可以解决各种 Python 依赖问题而不会哭") ) # Tomaz 很酷。他可以解决各种 Python 依赖问题而不会哭
在这个例子中,模型识别到了“Tomaz”和“他”指的是同一个实体。通过使用coref_text
函数,用“Tomaz”替换了“他”。
注意,重写不一定总是能得到语法正确的句子,因为实体集内的实体使用了简单的替换方法。不过,对于大多数情况来说,这样的重写结果应该已经足够用了。
现在我们将核心指代消解应用到我们的新闻数据集,并将结果封装为LlamaIndex文档,
从 llama_index.core 导入 Document news["coref_text"] = news["text"].apply(coref_text) documents = [Document(text=f"{row['title']}: {row['coref_text']}") for row in news.iterrows()]实体识别与链接和关系抽取
Relik 是一个包含实体链接(EL)和关系抽取(RE)功能的库,并且它还支持结合这两种功能的模型。在实体链接中,维基百科被用作目标知识库,将文本中的实体映射到维基百科中的相应条目。
将实体链接到维基百科 - 图片作者
另一方面,关系提取涉及识别并分类文本中实体间的关系,从而从非结构化数据中提取出结构化的信息,从而使人们能够更好地理解和利用这些信息。
关系抽取 - 图片作者:
如果你使用的是免费的Colab版本的话,请使用relik-ie/relik-relation-extraction-small
模型,该模型仅执行关系抽取。如果你使用的是Pro版本,或者你打算在一台性能更强的本地机器上使用它,你也可以试试relik-ie/relik-cie-small
模型,该模型不仅可以进行实体识别,还可以进行关系抽取。
from llama_index.extractors.relik.base 导入 RelikPathExtractor relik = RelikPathExtractor( model="relik-ie/relik-relation-extraction-small" ) # 注释:在 Pro Collab 上使用带 GPU 的 RelikPathExtractor # relik = RelikPathExtractor( # model="relik-ie/relik-cie-small", model_config={"skip_metadata": True, "device": "cuda"} # )
此外,我们还需要定义实体嵌入模型以及用于问答的语言模型。
import os # 引入OpenAI嵌入模型 from llama_index.embeddings.openai import OpenAIEmbedding # 引入OpenAI大模型 from llama_index.llms.openai import OpenAI # 设置环境变量OPENAI_API_KEY os.environ["OPENAI_API_KEY"] = "sk-" # 初始化大模型对象,使用"gpt-4o"模型版本,温度设置为0.0 llm = OpenAI(model="gpt-4o", temperature=0.0) # 大模型对象 # 创建一个使用"text-embedding-3-small"模型的嵌入模型对象 embed_model = OpenAIEmbedding(model_name="text-embedding-3-small") # 嵌入模型对象
注意,在构建图时不会使用LLM(LLM: Large Language Model)。
现在我们准备好了,可以创建一个 PropertyGraphIndex
,并将新闻文档作为知识图的输入数据。
另外,我们需要将 relik
模型设置为 kg_extractors
参数,这样就能用来提取关系了。
导入 llama_index.core 中的 PropertyGraphIndex index = PropertyGraphIndex.from_documents( documents, kg_extractors=[relik], llm=llm, embed_model=embed_model, property_graph_store=graph_store, show_progress=True, )
构建图完成后,你可以打开 Neo4j Browser 来验证你导入的图。运行以下 Cypher 语句:你应该能看到类似的结果。
MATCH p=(:__Entity__)--(:__Entity__) // 返回路径p,限制结果为250条 RETURN p LIMIT 250
结果
图可视化 - 图片由作者制作
问答环节现在使用LlamaIndex进行问答变得非常简单。若要使用默认的图检索器,你可以这样提问:
query_engine = index.as_query_engine(include_text=True) response = query_engine.query("瑞安航空到底发生了什么事?") print(str(response)) # 打印响应
这里就是我们之前定义的LLM和嵌入式模型的用武之地。当然,你也可以自定义自定义检索器以提高准确性。
摘要不依赖大型语言模型(LLM)创建知识图谱不仅完全可行,而且成本低且高效。通过微调较小的任务特定模型,比如使用Relik框架的模型,你可以利用这些模型为RAG应用(即检索增强生成应用)实现高效的信息提取。
实体链接过程是这一过程中的关键步骤,确保识别的实体准确对应知识库中的条目,从而保证知识图谱的完整性和实用性更佳。
通过使用像 Relik 这样的框架和像 Neo4j 这样的平台,可以构建高级知识图,从而促进复杂的数据分析和检索工作,而无需支付部署大型语言模型的高昂费用。这种方法不仅使强大的数据处理工具更易于获得,还促进了信息提取工作流程的创新和效率提升。
别忘了给 Relik库 点个赞哦!代码可以在 GitHub 上找到。