在我之前的文章中,我写到了使用知识图谱与 RAG 结合以及如何使用图技术进行自适应分词来构建更具上下文意识的 LLMs。在这篇文章中,我很高兴向大家展示我结合文本嵌入和知识(图)嵌入的实验以及关于 RAG 性能的观察。我将首先独立解释文本和知识嵌入的概念,使用简单的开源框架,然后,我们将看到如何在 RAG 应用中使用两者。这是一篇相当长的文章,我故意不想分成多个部分,所以大家可以根据我下面分享的顺序尝试。
作者创建的以领域为中心的语料库
我将深入探讨,并按照以下列出的四个部分来介绍我的工作。
-
第一部分:什么是文本嵌入(TE)?它们在 RAG 实现中是如何存储和使用的?
-
第二部分:什么是知识(图)嵌入(KGE)?它们是如何存储的?
-
第三部分:知识(图)嵌入与文本嵌入有何不同,并分析它们在 RAG 使用中的互补性
-
结论:结合文本和知识嵌入在 RAG 中的好处以及总体总结
第一部分:文本嵌入和 RAG 实现
如果你尝试进入自然语言处理(NLP)领域,在探索语言模型和机器学习时可能会遇到“文本嵌入”这个术语。那么,文本嵌入究竟是什么,它们是如何工作的呢?让我以更易于理解的方式解释一下。
文本嵌入简介
文本嵌入是单词或短语的数值表示,能够有效地捕捉其意义和上下文。可以将它们视为单词的唯一标识符——简洁的向量,能够捕捉它们所代表单词的意义。这些嵌入使计算机能够增强其对文本的理解和处理能力,从而在文本分类、情感分析和机器翻译等 NLP 任务中表现出色。
我们使用预训练模型如Word2Vec、GloVe 或 BERT来生成文本嵌入。这些模型在大量文本数据上进行了广泛训练,并获得了编码关于单词及其关系语义信息的能力。
-
分词 [决定我们将使用哪种算法来生成标记。[基于单词(以下是我选择的一个),基于字符,子词等)请参阅我的关于使用图技术的高级分词的文章]
-
将标记编码为向量(字节对编码等)
现在,让我们通过一个简单的 Python 代码片段(word2vec)来探索生成文本嵌入的过程:
# Code Implementation: Generating Text Embeddings
import numpy as np
from gensim.models import Word2Vec
# Sample sentences
sentences = [
["I", "love", "natural", "language", "processing"],
["Text", "embeddings", "are", "fascinating"],
["NLP", "makes", "computers", "understand", "language"]
]
# Train Word2Vec model
model = Word2Vec(sentences, vector_size=5, window=5, min_count=1, sg=1)
# Get embeddings for words
word_embeddings = {}
for word in model.wv.index_to_key:
word_embeddings[word] = model.wv[word]
# Print embeddings
for word, embedding in word_embeddings.items():
print(f"Embedding for '{word}': {embedding}")
在此代码中,我们通过在一系列样本句子上训练来开发一个 Word2Vec 模型。然后,该模型为每个单词生成嵌入。这些嵌入捕捉了句子中单词之间的语义关系。使用 Word2Vec 生成文本嵌入的代码片段输出如下:
Embedding for 'I': [-0.01978252 0.02348454 -0.0405227 -0.01806103 0.00496107]
Embedding for 'love': [ 0.01147135 -0.00716509 -0.02319919 0.03274594 -0.00713439]
Embedding for 'natural': [ 0.03319094 0.02570618 0.02645341 -0.00284445 -0.01343429]
Embedding for 'language': [-0.01165106 -0.02851446 -0.01676577 -0.01542572 -0.02357706]
Embedding for 'processing': [-0.00205235 0.01240269 -0.03660049 -0.0240654 -0.03612582]
Embedding for 'Text': [ 0.02553515 0.03493764 0.01932768 -0.02028572 0.02185934]
Embedding for 'embeddings': [ 0.01769094 0.02836292 -0.02720205 -0.01580047 -0.0323391 ]
Embedding for 'are': [ 0.01449668 0.0178032 0.02154245 -0.02403528 -0.03612522]
Embedding for 'fascinating': [ 0.0389471 0.00991404 0.0198368 -0.02466156 -0.03446501]
Embedding for 'NLP': [ 0.00828243 -0.02125732 0.01082581 0.02250805 0.02270168]
Embedding for 'makes': [ 0.01696491 0.0151721 -0.02186292 -0.01510419 -0.02021307]
Embedding for 'computers': [ 0.00983663 -0.02679762 0.03002482 -0.02373362 -0.01307139]
Embedding for 'understand': [-0.0326019 0.01835899 0.01538064 -0.01008516 0.01717436]
在上述输出中:
-
每一行对应一个单词的嵌入向量。
-
每一行以单词开头,后面跟着表示为数值列表的嵌入向量。
-
例如,单词 “love” 的嵌入:[-0.01978252 0.02348454 -0.0405227 -0.01806103 0.00496107].
在 RAG 中实现文本嵌入
现在我们已经掌握了生成文本嵌入的过程,让我们探索它们在检索增强生成模型(RAG)中的应用。RAG 结合了基于检索和生成的方法,利用文本嵌入来理解输入查询的上下文,并在 NLP 任务的检索阶段提取相关信息。
作者提供的文本嵌入流程
步骤 1:分词和编码。
现在让我们尝试使用预训练模型,例如 BERT,来对输入查询进行分词和编码。这会将查询转换为一种数值表示,它捕捉了其语义意义和上下文。
# Code Implementation: Tokenization and Encoding
from transformers import BertTokenizer, BertModel
# Initialize BERT tokenizer and model
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# Tokenize and encode the input query
query = "What is the capital of France?"
input_ids = tokenizer.encode(query, add_special_tokens=True, return_tensors="pt")
在这里,我们使用 BERT 将输入查询分词和编码成数值 ID。由于 BERT 模型初始化和分词过程涉及加载一个大型预训练模型,因此分词和编码步骤的输出包括以下组件:
-
输入 ID:这些是输入查询中标记的数值表示。每个标记都转换为一个 ID,对应其在 BERT 词汇表中的索引。
-
注意力掩码:这是一个二进制掩码,指示哪些标记是实际单词(1)以及哪些是填充标记(0)。它确保模型在处理过程中只关注真实标记。
-
标记类型 ID(对于像 BERT 这样的模型):这表示每个标记属于哪个部分或句子,如果是多个部分。对于单句输入,所有标记类型 ID 通常设置为 0。
输出是一个包含以下组件的字典,可以用作 BERT 模型的进一步处理输入。
这里是一个输出将包含内容的示例:
{
'input_ids': tensor([[ 101, 2054, 2003, 1996, 3007, 1997, 2605, 1029, 102, 0]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 0]]),
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
}
在这个例子中:
-
input_ids包含输入查询中标记的数值 ID。 -
attention_mask表示哪些标记是实际单词(1)以及哪些是填充标记(0)。 -
token_type_ids表示每个标记所属的段或句子(在本例中为第一个句子)。
步骤 2:检索和相似度匹配
接下来,我们根据编码后的查询从语料库中检索相关段落。我们使用余弦相似度计算查询嵌入和段落嵌入之间的相似度分数。
# Code Implementation: Retrieval and Similarity Matching
from sklearn.metrics.pairwise import cosine_similarity
# Retrieve passages and compute similarity scores
query_embedding = model(input_ids)[0].mean(dim=1).detach().numpy()
passage_embeddings = ... # Retrieve passage embeddings
similarity_scores = cosine_similarity(query_embedding, passage_embeddings)
在这里,我们选择具有最高相似度分数的段落,并将其与相似度分数一起输出。
由于检索和相似度匹配步骤涉及计算查询嵌入和段落嵌入之间的相似度分数,我可以提供一个相似度分数的示例。假设我们有三个样本段落,并计算查询嵌入与这些段落嵌入之间的余弦相似度分数。以下是一个可能的输出示例:
similarity_scores = [0.75, 0.82, 0.65]
在这个例子中:
-
相似度分数为 0.75 表示查询与第一段落的相似度适中。
-
相似度分数为 0.82 表示查询与第二段落之间具有高度相似性。
-
相似度分数为 0.65 表示查询与第三段落的相似度较低。
相似度分数表明了每个段落与输入查询之间的相似程度,分数越高表示相似度越强。在 RAG 模型中,获得最高相似度分数的段落被认为是最相关的,适合进一步处理。
步骤 3:增强上下文
最后,我们将具有最高相似度分数的段落指定为最相关的段落。这个段落为模型的生成阶段提供了相关信息。
# Select passage with highest similarity score
max_similarity_index = np.argmax(similarity_scores)
selected_passage = passages[max_similarity_index]
# Output selected passage and similarity score
print("Selected Passage:")
print(selected_passage)
print("Similarity Score:", similarity_scores[0][max_similarity_index])
假设第二段落的相似度分数最高(0.82)并被选为最相关的段落。以下是一个可能的输出示例:
Selected Passage:
"Barack Obama served as the 44th president of the United States."
Similarity Score: 0.82
在这个例子中:
-
选择的段落是“巴拉克·奥巴马曾担任美国第 44 任总统。”
-
相似度分数为 0.82,表示本段落与输入查询之间具有高度相似性。
文本嵌入是自然语言处理(NLP)领域中的强大工具,使计算机能够有效地理解和处理文本信息。它们对众多任务产生了重大影响,例如问答、文本生成和情感分析。通过在 RAG 等高级模型中利用文本嵌入,我们可以显著提高其性能和精确度,从而产生更加敏锐和符合上下文的回答。随着你在 NLP 领域的探索不断深入,掌握文本嵌入技术对于释放语言模型的全部潜力以及推动自然语言处理的发展至关重要。
第二部分:知识(图)嵌入和实现
现在让我们转换到高级知识图谱的世界,并探讨定义和实现知识嵌入,即从非结构化数据中提取结构化领域构造。
知识(图谱)与嵌入简介
知识图谱是组织信息的一种非常有效的方式,以有意义的方式连接实体及其关系。这些图作为信息的有组织存储库,捕捉现实世界对象及其连接的意义。然而,这个过程并不随着知识图谱的开发而结束。探索知识图谱嵌入的领域对于释放其全部潜力至关重要。
让我们通过一个简单的文本来探索知识图谱嵌入,或简称为知识嵌入,该文本具有多种实体及其相互关系。从提取数据到生成嵌入,再到以紧凑的向量捕捉文本的真实意义。获取这些嵌入的目标是便于对图元素(实体、关系)进行各种预测任务,如实体分类、链接预测或推荐系统。
我们维护 KG 的基本结构并简化 KG 组件的利用。在将 KG 元素表示为嵌入后,使用评分函数来评估三元组的可能性,例如 ‘Tim’,‘is an’,‘Artist’。
以下是实现知识(图谱)嵌入的步骤:
知识嵌入按作者流动
第 1 步:三元组提取与处理
给定一个非结构化文本,我们首先将使用斯坦福大学的 OpenIE 框架提取关键实体、关系和属性。一旦提取了三元组,我们将对其进行清洗/协调。
Ex; 考虑以下输入文本:“夏威夷是美国的一个州。巴拉克·奥巴马曾担任美国第 44 任总统。卢浮宫博物馆位于法国巴黎。”*
from openie import StanfordOpenIE
text = "Hawaii is a state in the United States. Barack Obama served as the 44th president of the United States. The Louvre Museum is located in Paris, France."
with StanfordOpenIE() as client:
triples = client.annotate(text)
for triple in triples:
print(triple)
cleaned_triples = [(subject.lower(), relation.lower(), object.lower()) for (subject, relation, object) in triples]
print("Cleaned Triples:", cleaned_triples)
上述代码的输出是
('Hawaii', 'is', 'a state in the United States')
('Barack Obama', 'served as', 'the 44th president of the United States')
('The Louvre Museum', 'is located in', 'Paris, France')
Cleaned Triples: [('hawaii', 'is', 'a state in the united states'), ('barack obama', 'served as', 'the 44th president of the united states'), ('the louvre museum', 'is located in', 'paris, france')]
我们现在可以使用这些信息并创建一个知识图谱
第 2 步:知识图谱构建
使用 NetworkX 框架,我们现在将使用上述清洗后的三元组并构建一个知识图谱,其中实体 → 节点,关系 → 边。以下是实现代码:
import networkx as nx
# Create a directed graph
knowledge_graph = nx.DiGraph()
# Add nodes and edges from cleaned triples
for (subject, relation, object) in cleaned_triples:
knowledge_graph.add_edge(subject, object, relation=relation)
# Visualize the knowledge graph
nx.draw(knowledge_graph, with_labels=True)
输出是一个如下所示的图:
作者提供的样本图输出
第 3 步:实体解析与归因
实体解析在多种 NLP 应用中扮演着关键角色,包括信息提取、问答、知识图谱构建等。通过准确地将文本中实体的提及与结构化知识表示中的相应实体链接起来,实体解析使机器能够更有效地理解和推理自然语言,从而促进各种下游任务和应用。
基本上,实体解析解决了自然语言中模糊性和变化性的挑战。在日常语言使用中,人们、地点、组织、概念等实体通常使用不同的名称、同义词、缩写或变体来指代。例如,“Barack Obama"可能被提及为"Obama”、“前美国总统"或简单地"他”。此外,具有相似名称或属性的实体可能存在,导致潜在的混淆或模糊。例如,"Paris"可能指法国的首都,也可能指具有相同名称的不同地点。
让我们逐一介绍每种实体解析技术,并提供基于文本使用的示例:
-
精确匹配: 在文本中,提及的"夏威夷"可以直接链接到图中标记为"夏威夷"的节点,因为它们完全匹配。
-
部分匹配: 如果文本提到"USA"而不是"United States",部分匹配算法可能会识别这两个词之间的相似性,并将提及链接到图中标记为"United States"的节点。
-
命名实体识别 (NER): 使用 NER,系统可能会识别文本中提到的"Barack Obama"作为一个人实体。然后,这个提及可以链接到图中标记为"Barack Obama"的相应节点。
-
指代消解: 如果文本提到"He served as president",指代消解可能会将"He"回溯到文本中较早提到的"Barack Obama",并将其链接到图中标记为"Barack Obama"的相应节点。
-
消歧: 假设文本提到"Paris"但没有指定它是指法国的城市还是其他地点。消歧技术可能会考虑上下文信息或外部知识源来确定它指的是"Paris, France",并将其链接到图中相应的节点。
一旦确定了正确的实体链接,文本中的提及就会被链接到知识库或知识图中的相应实体。实体解析系统的性能使用诸如精确度、召回率和 F1 分数等指标进行评估,比较预测的实体链接与基准或黄金标准。以下是一个实体解析示例,用于上述构建的图。灰色圆圈表示给定实体的类别解析。
作者提供的实体解析示例
步骤 4: 知识(图)嵌入生成
作为最后一步,我们现在将生成实体和关系的嵌入。有几种模型,但在这个练习中,我们将使用 TransE。
from pykeen.pipeline import pipeline
# Define pipeline configuration
pipeline_config = {
"dataset": "nations",
"model": {
"name": "TransE",
"embedding_dim": 50
},
"training": {
"num_epochs": 100,
"learning_rate": 0.01
}
}
# Create and run pipeline
result = pipeline(
**pipeline_config,
random_seed=1234,
use_testing_data=True
)
# Access trained embeddings
entity_embeddings = result.model.entity_embeddings
relation_embeddings = result.model.relation_embeddings
# Print embeddings
print("Entity Embeddings:", entity_embeddings)
print("Relation Embeddings:", relation_embeddings)
这里是输出:
Entity Embeddings: [Embedding dimension: (120, 50)]
Relation Embeddings: [Embedding dimension: (120, 50)]
知识嵌入的向量如下所示:
实体嵌入:
-
“夏威夷”: [0.1, -0.2, 0.5, …]
-
“United States”: [0.3, 0.4, -0.1, …]
-
“Barack Obama”: [-0.2, 0.6, 0.2, …]
-
“卢浮宫”: [-0.5, 0.1, 0.7, …]
关系嵌入:
-
“是状态”: [0.2, -0.3, 0.1, …]
-
“作为”: [0.4, 0.2, -0.5, …]
-
“位于”: [-0.1, 0.5, 0.3, …]
第三部分:结合文本和知识(图)嵌入
在我们尝试结合嵌入之前,让我们首先了解这些嵌入带来的价值,并验证它们是否真的互补。
文本嵌入和知识嵌入在自然语言处理(NLP)中具有不同的用途,并代表语言和语义信息的不同方面。让我们根据提供的输出比较和对比这两种类型的嵌入:
比较和对比文本和知识嵌入 – 作者
实现文本和知识嵌入的融合
此代码通过将文本嵌入和知识嵌入结合到单个嵌入空间中,然后根据查询和段落组合嵌入之间的余弦相似度从知识库中检索相关段落。输出显示了相关段落及其与查询的相似度得分。
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# Sample knowledge embeddings
knowledge_embeddings = { } #initialise the above knowledge embeddgings output
# Sample text embeddings
text_embeddings = { } #initialise the above text embeddgings output
# Consider passages from knowledge base
knowledge_base = {
"Passage 1": "Hawaii is a state in the United States.",
"Passage 2": "Barack Obama served as the 44th president of the United States.",
# Add more passages as needed
}
# Function to combine text and knowledge embeddings
def combine_embeddings(text_emb, know_emb):
combined_emb = {}
for entity, t_emb in text_emb.items():
if entity in know_emb:
combined_emb[entity] = np.concatenate([t_emb, know_emb[entity]])
else:
combined_emb[entity] = t_emb
return combined_emb
# Function to retrieve relevant passages using combined embeddings
def retrieve_passages(query_emb, knowledge_base_emb):
similarities = {}
for passage, kb_emb in knowledge_base_emb.items():
sim = cosine_similarity([query_emb], [kb_emb])[0][0]
similarities[passage] = sim
sorted_passages = sorted(similarities.items(), key=lambda x: x[1], reverse=True)
return sorted_passages
# Example usage
combined_embeddings = combine_embeddings(text_embeddings, knowledge_embeddings)
query = "query"
relevant_passages = retrieve_passages(combined_embeddings[query], knowledge_embeddings)
# Print relevant passages
for passage, similarity in relevant_passages:
print("Passage:", passage)
print("Similarity:", similarity)
print()
带有相似度的输出如下:
Passage: Passage 1
Similarity: 0.946943628930774
Passage: Passage 2
Similarity: 0.9397945401928656
我们可以实现其他相似度框架,并选择最适合业务上下文的一个。
结论
在检索增强生成(RAG)模型中将文本嵌入和知识嵌入同时使用可以以多种方式增强模型的表现力和功能:
-
全面理解: 文本嵌入捕捉单个单词或短语的语义意义,而知识嵌入捕捉实体之间的显式关系。通过整合这两种类型的嵌入,RAG 模型能够更全面地掌握输入文本和存储在知识图谱中的组织化信息。
-
上下文相关性: 文本嵌入通过分析输入文本中的词共现提供有价值的上下文洞察,而知识嵌入通过检查知识图谱中实体之间的关系提供上下文相关性。通过结合不同类型的嵌入,RAG 模型能够生成既与输入文本语义相关又与结构化知识上下文一致的响应。
-
改进答案检索: 利用 RAG 模型中的结构化知识可以显著提高答案选择,这得益于检索组件中知识嵌入的集成。通过利用知识嵌入进行索引和从知识库中检索相关段落,RAG 模型能够检索到不仅更准确而且更具有信息量的响应。
-
增强答案生成:通过整合广泛的语言特征和语义细微差别,文本嵌入增强了 RAG 模型的生成组件。通过在答案生成过程中整合文本嵌入和知识嵌入,RAG 模型能够生成具有语言流畅性、语义相关性和结构化知识坚实基础的反应。
-
对模糊性的鲁棒性:通过利用文本嵌入和知识嵌入,RAG 模型获得了对自然语言中模糊性和变化性的更高抵抗力。文本嵌入捕捉了非结构化文本中的变化性和模糊性,而知识嵌入提供了明确的语义关系,以增强和阐明模型的认知。
-
有效整合知识:知识嵌入允许 RAG 模型无缝地将知识图谱或知识库中的结构化知识整合到生成过程中。通过知识嵌入和文本嵌入的整合,RAG 模型实现了结构化知识和非结构化文本的无缝融合,从而生成更具信息量和语境相关性的反应。
总体而言,在 RAG 模型中将文本嵌入和知识嵌入结合使用,可以实现对输入文本和结构化知识的更全面和语境丰富的表示。这种整合增强了模型在答案检索、答案生成、对模糊性的鲁棒性以及有效整合结构化知识方面的性能,最终导致更准确和富有信息量的反应。
参考论文与文章
我使用了以下参考资料来加深我的理解,并在本文中分享的上述试验中进行了探索。
-
EAGER: Embedding-Assisted Entity Resolution for Knowledge Graphs — 2021
-
“Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks” by Lewis, P., Neumann, M., et al. (2020)
-
“KagNet: Knowledge-Aware Graph Networks for Commonsense Reasoning” by Z. Hu et al. (2021)
-
“Using Graph Neural Networks to Learn Query Intent Representations in Information Retrieval” by Cui, P., et al. (2020)
-
“Improving Open-Domain Question Answering with Knowledge-Enhanced Context Embedding” by Yang, Z., et al. (2020)
1981

被折叠的 条评论
为什么被折叠?



