知识图谱

定义

图的数据结构,由节点(Point)和边(Edge)组成。描述实体之间的关系

主要用来优化现有的搜索引擎。不同于基于关键词搜索的传统搜索引擎,知识图谱可用来更好地查询复杂的关联信息。

本质上就是知识库是用于知识管理的一种特殊的数据库,以便于有关领域知识的采集、整理以及提取。

构建

应用要建立,首先要搞知识图谱的构建(来源 业务数据和爬虫),构建时候可能会涉及到NLP

  • 实体命名识别(Name Entity Recognition)
  • 关系抽取(Relation Extraction)
  • 实体统一(Entity Resolution)
  • 指代消解(Coreference Resolution)

通常步骤

  • 定义具体的业务问题

  • 数据的收集

  • 预处理

  • 知识图谱的设计

    • 需要哪些实体、关系和属性?
    • 哪些属性可以做为实体,哪些实体可以作为属性?
    • 哪些信息不需要放在知识图谱中?

    让知识图谱尽量轻量化、并决定哪些数据放在知识图谱,哪些数据不需要放在知识图谱

    把常用的信息存放在知识图谱中,把那些访问频率不高,对关系分析无关紧要的信息放在传统的关系型数据库当中。

  • 存入知识图谱
  • 上层应用的开发,以及系统的评估

    • 基于规则 通过一些人为提前定义规则去找出潜在的矛盾点
    • 基于概率

Knowledge Graph langchain

knowledge-graphs-rag

与仅使用文本嵌入进行相似性搜索不同,你可以检索一个块,然后遍历图形以找到其他相关的文本块,这给你的LLM提供了更完整的上下文。

知识图谱是一个将信息存储在节点和关系中的数据库,节点和关系都可以有属性,键值对,节点可以被赋予标签以帮助将它们分组在一起。关系始终有类型和方向

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
71
72
73
74
75
76
77
78
79
80
81
82
# Connect to the knowledge graph instance using LangChain
kg = Neo4jGraph(
    url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE
)

# Create a vector index
kg.query("""
         CREATE VECTOR INDEX `form_10k_chunks` IF NOT EXISTS
          FOR (c:Chunk) ON (c.textEmbedding) 
          OPTIONS { indexConfig: {
            `vector.dimensions`: 1536,
            `vector.similarity_function`: 'cosine'    
         }}
""")


# Create graph nodes using text chunks
merge_chunk_node_query = """
MERGE(mergedChunk:Chunk {chunkId: $chunkParam.chunkId})
    ON CREATE SET 
        mergedChunk.names = $chunkParam.names,
        mergedChunk.formId = $chunkParam.formId, 
        mergedChunk.cik = $chunkParam.cik, 
        mergedChunk.cusip6 = $chunkParam.cusip6, 
        mergedChunk.source = $chunkParam.source, 
        mergedChunk.f10kItem = $chunkParam.f10kItem, 
        mergedChunk.chunkSeqId = $chunkParam.chunkSeqId, 
        mergedChunk.text = $chunkParam.text
RETURN mergedChunk
"""

node_count = 0
for chunk in first_file_chunks:
    print(f"Creating `:Chunk` node for chunk ID {chunk['chunkId']}")
    kg.query(merge_chunk_node_query, 
            params={
                'chunkParam': chunk
            })
    node_count += 1
print(f"Created {node_count} nodes")





# Calculate embedding vectors for chunks and populate index
kg.query("""
    MATCH (chunk:Chunk) WHERE chunk.textEmbedding IS NULL
    WITH chunk, genai.vector.encode(
      chunk.text, 
      "OpenAI", 
      {
        token: $openAiApiKey, 
        endpoint: $openAiEndpoint
      }) AS vector
    CALL db.create.setNodeVectorProperty(chunk, "textEmbedding", vector)
    """, 
    params={"openAiApiKey":OPENAI_API_KEY, "openAiEndpoint": OPENAI_ENDPOINT} )

# Use similarity search to find relevant chunks
def neo4j_vector_search(question):
  """Search for similar nodes using the Neo4j vector index"""
  vector_search_query = """
    WITH genai.vector.encode(
      $question, 
      "OpenAI", 
      {
        token: $openAiApiKey,
        endpoint: $openAiEndpoint
      }) AS question_embedding
    CALL db.index.vector.queryNodes($index_name, $top_k, question_embedding) yield node, score
    RETURN score, node.text AS text
  """
  similar = kg.query(vector_search_query, 
                     params={
                      'question': question, 
                      'openAiApiKey':OPENAI_API_KEY,
                      'openAiEndpoint': OPENAI_ENDPOINT,
                      'index_name':VECTOR_INDEX_NAME, 
                      'top_k': 10})
  return similar

Lanchian RAG

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
from langchain_community.vectorstores import Neo4jVector

neo4j_vector_store = Neo4jVector.from_existing_graph(
    embedding=OpenAIEmbeddings(),
    url=NEO4J_URI,
    username=NEO4J_USERNAME,
    password=NEO4J_PASSWORD,
    index_name=VECTOR_INDEX_NAME,
    node_label=VECTOR_NODE_LABEL,
    text_node_properties=[VECTOR_SOURCE_PROPERTY],
    embedding_node_property=VECTOR_EMBEDDING_PROPERTY,
)

retriever = neo4j_vector_store.as_retriever()


chain = RetrievalQAWithSourcesChain.from_chain_type(
    ChatOpenAI(temperature=0), 
    chain_type="stuff", 
    retriever=retriever
)

def prettychain(question: str) -> str:
    """Pretty print the chain's response to a question"""
    response = chain({"question": question},
        return_only_outputs=True,)
    print(textwrap.fill(response['answer'], 60))

知识图谱Knowledge Graph (KG)

参考

主要成分(Main components)

  • 顶点/节点:表示知识领域中的实体或对象。每个节点对应一个唯一的实体并由唯一的标识符标识。represents entities or objects in the domain of knowledge. Each node corresponds to a unique entity and is identified by a unique identifier.
  • 边:表示两个节点之间的关系。represents the relationship between two nodes.

三元组(Triplet)

In the following triplet example, “Philadelphia Phillies” is the subject, “compete in” is the predicate, and “Major League Baseball” is the object.

1
(Philadelphia Phillies)--[compete in]->(Major League Baseball)

Cypher

Cypher是一种声明性的图查询语言,由知识图谱支持。Cypher is a declarative graph query language that is supported by Knowledge Graph

知识图谱是一种基于三元组的数据模型:实体、属性、关系

1
2
3
4
5
6
7
8
9
10
11
kg_index = KnowledgeGraphIndex.from_documents(
    documents,
    storage_context=storage_context,
    service_context=service_context,
    max_triplets_per_chunk=10,
    space_name=space_name,
    edge_types=edge_types,
    rel_prop_names=rel_prop_names,
    tags=tags,
    include_embeddings=True,
)

KG vector-based entity retrieval

1
query_engine = kg_index.as_query_engine()

基于KG关键字的实体检索 KG keyword-based entity retrieval

1
2
3
4
5
6
kg_keyword_query_engine = kg_index.as_query_engine(
# setting to false uses the raw triplets instead of adding the text from the corresponding nodes
    include_text=False,  # 查询引擎只会使用原始的三元组进行查询;不会在响应中包含相应节点的文本内容。
    retriever_mode="keyword",# 基于query提取keyword
    response_mode="tree_summarize", # 响应将是知识图谱树结构的摘要。树将以递归方式构建,查询作为根节点,最相关的答案作为叶节点。tree_summarize响应模式对于摘要任务非常有用,比如提供一个主题的高级概述或回答需要全面回应的问题。
)

KG hybrid entity retrieval KG混合实体检索

1
2
3
4
5
6
7
8
kg_hybrid_query_engine = kg_index.as_query_engine(
    include_text=True, # 查询引擎将在响应中使用相应节点的文本内容。
    response_mode="tree_summarize",
    embedding_mode="hybrid", # 混合模式结合了关键词搜索和语义搜索,来找到相似的三元组。
    similarity_top_k=3,
    explore_global_knowledge=True, # 当设置explore_global_knowledge=True时,查询引擎不仅限制在局部上下文(即节点的直接邻居 , 还会考虑知识图谱的更广泛的全局上下文。当您想要检索与查询不直接相关但在知识图谱的更大上下文中相关的信息时,这将非常有用。
)

自定义组合查询引擎(KG检索器和向量索引检索器的组合)

LlamaIndex 精心制作了一个CustomRetriever. 您可以在下面的代码中看到它的样子。它执行知识图搜索和向量搜索。默认情况下mode OR保证两个搜索的联合,从而产生包含两全其美的搜索结果,并进行重复数据删除:

  • 来自知识图谱搜索的细致入微的细节 ( KGTableRetriever)。
  • 来自向量索引搜索的语义相似性搜索详细信息 ( VectorIndexRetriever)。
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
from llama_index import QueryBundle
from llama_index.schema import NodeWithScore
from llama_index.retrievers import BaseRetriever, VectorIndexRetriever, KGTableRetriever
from typing import List

class CustomRetriever(BaseRetriever):

    def __init__(
        self,
        vector_retriever: VectorIndexRetriever,
        kg_retriever: KGTableRetriever,
        mode: str = "OR",
    ) -> None:
        """Init params."""

        self._vector_retriever = vector_retriever
        self._kg_retriever = kg_retriever
        if mode not in ("AND", "OR"):
            raise ValueError("Invalid mode.")
        self._mode = mode

    def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
        """Retrieve nodes given query."""

        vector_nodes = self._vector_retriever.retrieve(query_bundle)
        kg_nodes = self._kg_retriever.retrieve(query_bundle)

        vector_ids = {n.node.node_id for n in vector_nodes}
        kg_ids = {n.node.node_id for n in kg_nodes}

        combined_dict = {n.node.node_id: n for n in vector_nodes}
        combined_dict.update({n.node.node_id: n for n in kg_nodes})

        if self._mode == "AND":
            retrieve_ids = vector_ids.intersection(kg_ids)
        else:
            retrieve_ids = vector_ids.union(kg_ids)

        retrieve_nodes = [combined_dict[rid] for rid in retrieve_ids]
        return retrieve_nodes


from llama_index import get_response_synthesizer
from llama_index.query_engine import RetrieverQueryEngine
from llama_index.retrievers import VectorIndexRetriever, KGTableRetriever

# create custom retriever
vector_retriever = VectorIndexRetriever(index=vector_index)
kg_retriever = KGTableRetriever(
    index=kg_index, retriever_mode="keyword", include_text=False
)
custom_retriever = CustomRetriever(vector_retriever, kg_retriever)

# create response synthesizer
response_synthesizer = get_response_synthesizer(
    service_context=service_context,
    response_mode="tree_summarize",
)

custom_query_engine = RetrieverQueryEngine(
    retriever=custom_retriever,
    response_synthesizer=response_synthesizer,
)

KnowledgeGraphQueryEngine

它使用 LLM 生成 Cypher 查询,然后在知识图上执行。这使得无需学习 Cypher 或任何其他查询语言即可查询知识图。很容易语法错误

  • 利用LLM根据提示模板将用户的自然语言查询转换为Cypher查询。
  • 将Cypher查询直接发送到图存储数据库,查询返回结果。
  • 对结果进行合成,并返回给用户。
1
2
3
4
5
6
query_engine = KnowledgeGraphQueryEngine(
    storage_context=storage_context,
    service_context=service_context,
    llm=llm,
    verbose=True,
)

KnowledgeGraphRAGRetriever

KnowledgeGraphRAGRetrieverRetrieverQueryEngineLlamaIndex 中的一个,用于对知识图谱执行 Graph RAG 查询。它将问题或任务作为输入并执行以下步骤:

  • 使用关键字提取或嵌入在知识图中搜索相关实体。
  • 从知识图中获取这些实体的子图,默认深度为 2。
  • 基于子图构建上下文。
1
2
3
4
5
6
7
8
9
10
graph_rag_retriever = KnowledgeGraphRAGRetriever(
    storage_context=storage_context,
    service_context=service_context,
    llm=llm,
    verbose=True,
)

kg_rag_query_engine = RetrieverQueryEngine.from_args(
    graph_rag_retriever, service_context=service_context
)
Query Engine Description Pros Cons
KG vector-based entity retrieval
KG基于向量的实体检索
Looks up KG entities with vector similarity, pulls in linked text chunks, optionally explore relationships
使用向量相似性查找KG实体,提取链接的文本片段,可选择探索关系
Can answer complex questions that require knowledge of multiple entities and relationships.
能够回答需要了解多个实体和关系的复杂问题
Can be less accurate for questions that do not have a clear answer in the KG.
对于在KG中没有明确答案的问题可能不够准确
KG keyword­ based entity retrieval
KG基于关键词的实体检索
Uses keywords to search for relevant KG entities.
使用关键词搜索相关的KG实体
Faster than the vector­ based entity retrieval, easy to implement.
比基于向量的实体检索更快,易于实现
Can be less accurate for complex queries.
对于复杂查询可能不够准确
KG hybrid entity retrieval
KG混合实体检索
Uses both vector-based and keyword-based entity retrieval to search for relevant nodes and documents in a KG.
使用基于向量和关键词的实体检索来搜索KG中相关的节点和文档
More accurate than the keyword-based entity retrieval for complex queries.
对于复杂查询比基于关键词的实体检索更准确
Can be slower than both the keyword-based entity retrieval and the vector­ based entity retrieval.
比基于关键词的实体检索和基于向量的实体检索更慢
Raw vector index retrieval
原始向量索引检索
Uses a vector index to search for relevant documents, removes relationships and represents entities in a flat vector store
使用向量索引搜索相关文档,去除关系并在平面向量存储中表示实体
Faster than the KG query engines. Can be used to search for documents that are not in a KG.
比KG查询引擎更快,可以用于搜索不在KG中的文档
Requires a vector index to be built and maintained. May return less nuanced results than the KG query engines, especially for complex questions.
需要构建和维护向量索引,对于复杂问题,可能返回比KG查询引擎更少细致的结果
Custom Combo query engine
自定义组合查询引擎
Uses both vector similarity and KG entity retrieval (vector-based, keyword­ based, hybrid).
同时使用向量相似性和KG实体检索
Combines the strengths of the two query engines. Usually the most accurate and comprehensive of the seven query engines.
结合了两种查询引擎的优势,通常是七种查询引擎中最准确和全面的
Slower than the other query engines. Requires both a KG and a vector index to be built and maintained. More complex to set up and configure than the other query engines.
比其他查询引擎更慢,需要同时构建和维护KG和向量索引,比其他查询引擎更复杂的设置和配置
KnowledgeGraph QueryEngine Queries knowledge graphs using natural language, Text2Cypher.
使用自然语言查询知识图谱,Text2Cypher
No indexing required. Can query any knowledge graph type using natural language, Text2Cypher.
不需要索引,可以使用自然语言的Text2Cypher查询任何类型的知识图谱
Text2Cypher, especially for complex queries, is yet to be improved.
Text2Cypher,特别是对于复杂查询,仍有待改进
KnowledgeGraph RAGRetriever Designed to work with knowledge graphs that have been built using RAG
设计用于与使用RAG构建的知识图谱一起使用
No indexing required, allows existing KG to be queried.
不需要索引,允许查询现有的知识图谱
Can only query RAG built KG.
只能查询使用RAG构建的知识图谱
  • If the knowledge pieces in your data source are spread and fine-grained, and you require complex reasoning over your data source, such as extracting entities and their relationships in a networked format, like in the case of fraud detection, social networking, supply chain management, then a knowledge graph query engine is a better choice. The KG query engine is also helpful when your embeddings generate false correlations, which contributes to hallucinations.

    如果你的数据源中的知识片段分散而细粒度,并且你需要对数据源进行复杂的推理,比如提取实体及其在网络格式中的关系,比如在欺诈检测、社交网络、供应链管理等情况下,知识图谱查询引擎是一个更好的选择。知识图谱查询引擎在你的嵌入生成错误相关性的情况下也很有帮助,这会导致产生幻觉。

  • If you require similarity search, such as finding all of the nodes that are similar to a given node or finding all the nodes closest to a given node in a vector space, then a vector query engine may be your best choice.

    如果你需要相似性搜索,比如找到与给定节点相似的所有节点,或者找到在向量空间中与给定节点最接近的所有节点,那么向量查询引擎可能是你最好的选择。

  • If you need a query engine that can respond to queries quickly, then a vector query engine may be a better choice, as they are typically faster than KG query engines. Even without embeddings, the extraction of the task (sub-job running on a single storage service in NebulaGraph) may be the main step that contributed to the latency in KG query engines.

    如果你需要一个能够快速响应查询的查询引擎,那么向量查询引擎可能是一个更好的选择,因为它们通常比知识图谱查询引擎更快。即使没有嵌入,任务的提取(在NebulaGraph中运行的单个存储服务上运行的子任务)可能是导致知识图谱查询引擎延迟的主要步骤。

  • If you need the best quality responses, then the custom combo query engine, which combines the strength of both KG query engines and vector query engines, is your best bet.

    如果你需要最高质量的响应,则定制的组合查询引擎,结合了知识图谱查询引擎和向量查询引擎的优势,是你最好的选择。

如何将任何文本转换为概念图

RAG一些缺点:

  • 与普通的 RAG 旧版本相比,这可以给我们带来更好的结果,而 RAG 存在一些缺点。例如,通过简单的语义相似性搜索来检索与查询最相关的上下文并不总是有效。特别是当查询没有提供足够的关于其真实意图的上下文时,或者当上下文是跨大型文本语料库的片段时
  • RAG 的另一个缺点是它无法告诉您要问什么。很多时候,提出正确的问题比获得答案更重要。

创建概念图

如果你问 GPT,如何根据给定文本创建知识图?它可能会建议如下的过程。

  1. 从工作主体中提取概念和实体。这些是节点。
  2. 提取概念之间的关系。这些是边缘。
  3. 填充图数据结构或图数据库中的节点(概念)和边(关系)。
  4. 想象一下,如果没有别的目的,为了一些艺术上的满足。
  • 将文本语料库分割成块。为每个块分配一个 chunk_id。

  • 同一对概念之间可以存在多种关系。每一个这样的关系都是一对概念之间的边缘。让我们为这个关系指定一个权重 W1

  • 同一对概念可能出现在多个块中。让我们为这个关系指定一个权重 W2

  • 对相似的对进行分组,求和它们的权重,并连接它们的关系。所以现在我们在任何不同的概念对之间都只有一条边。边具有一定的权重和关系列表,如其名称所示。