在快速发展的搜索引擎世界中,理解词汇搜索和语义性搜索之间的区别对于提高搜索的相关性和用户满意度至关重要。OpenSearch,一个开源的搜索和分析套件,提供了强大的工具来实现这两种查询。这两种方法到底有什么不同,以及它们如何影响搜索性能?
来源:克劳德3.5的十四行诗
词汇搜索主要是通过精确匹配查询词和索引中的词汇,更关注关键字及其具体的排列。相比之下,语义搜索则不仅仅是匹配单词,还更注重理解查询的含义和上下文,从而提供更符合用户意图和更直观的结果。
在这篇博客中,我们将探讨在OpenSearch的场景下词典搜索查询和语义搜索之间的基本差异,强调每种方法的优势和面临的挑战。通过这篇文章,你将对这些方法如何被用来提升搜索结果的质量并优化用户搜索体验有一个概览。相信我,有很多关于它们的内容可以学习,而在下一篇文章中,我将进一步深入探讨如何使用自定义排名和高级搜索,但在此之前,基础部分必须非常清楚,所以让我们深入探讨这两种强大的搜索策略的不同之处。
OpenSearch: 搜索词汇词典搜索,通常称为基于关键字的搜索,是大多数传统搜索引擎的基础。它主要集中在查询的关键词和索引文档中的确切词汇之间进行匹配。在词典搜索中,引擎查找包含搜索词完全匹配的文档,几乎不考虑这些词的意义和上下文。当用户确切知道自己要找的内容并使用特定且清晰定义的关键词时,这种搜索方式尤其有效。
在 OpenSearch 中,词汇搜索主要依赖于使用 倒排索引。这个索引是一种数据结构,将词项(单词)映射到文档中出现的位置。当用户发出搜索查询时,OpenSearch 迅速扫描这个倒排索引以找到相关结果,并根据词频、文档频率等因素对结果进行排序。
词汇搜索的关键要素- 倒排索引:倒排索引是词汇搜索的核心,它存储词汇与它们在文档中位置的映射。OpenSearch在索引数据时自动构建此索引,从而实现快速高效的词汇查找。
- 词汇匹配:搜索引擎将查询词汇与文档中的词汇进行匹配。查询被分解为单独的词汇(标记),然后与文档中的词汇进行比较。
- 文本分析器:这些用于将文本分解成标记(词),有时还会应用如小写转换、词干提取或删除停用词等变换。OpenSearch内置了这些分析器来完成这些任务。
在使用 Python 查看如何在 OpenSearch 中进行词汇搜索之前,让我们先来深入了解一下什么是倒排索引。
倒排索引简介倒排索引(inverted index)是一种数据结构,用于搜索引擎实现快速全文搜索,它允许系统快速找到包含特定单词或短语的文档。倒排索引是搜索引擎工作原理中的一个关键部分,包括OpenSearch、Elasticsearch等类似系统。
在倒排索引中,每个独一无二的词语(通常是单词)都会映射到包含该词的文档列表。倒排索引并不存储文档本身,而是存储指向这些文档的引用,这使得搜索和检索匹配文档变得非常快捷。
倒排索引是如何工作的索引阶段:
- 在索引阶段,系统读取数据集(或语料库)中的所有文档,并处理每个文档的内容。
- 文本通常被分割成词元(通常是单词),每个独特的词元作为键存储在倒排索引中。
- 对于每个词元,索引维护一个包含该词条的文档标识列表,该列表被称为倒排列表(posting列表)。
搜索阶段如下:
- 在执行搜索查询时,搜索引擎会在倒排索引中查找包含这些术语的文档。
- 索引使得查找非常快速,因为它避免了逐个扫描语料库中的文档;相反,它只查看相关术语的倒排列表条目。
- 如果查询包含多个术语,搜索引擎可以执行像交集(用于 AND 查询)、并集(用于 OR 查询)这样的操作,还可以进行评分,以按相关性对结果进行排序。
一个倒排索引的例子
咱们来看一个简单的例子,里面有几个文档。
文件:
- “那只快速的棕色狐狸”
- “那只懒狗”
- “那只快速的狐狸跳了起来”
- “那只懒狐狸”
我们将对这些文档进行分词,建立倒排索引。这些词包括the、quick、brown、fox、lazy、dog和jumped。
来自:Chatgpt
关于倒排索引的解释:- “the” 出现在所有四个文档(1, 2, 3, 4)中,因此,它的倒排索引包括所有文档ID。
- “quick” 出现在文档 1 和 3 中,因此,它的倒排索引为 [1, 3]。
- “fox” 出现在文档 1, 3 和 4 中,因此,它的倒排索引为 [1, 3, 4]。
- “lazy” 出现在文档 2 和 4 中,因此,它的倒排索引为 [2, 4]。
- 比如说,用户搜索 “quick fox”。搜索引擎可以:
- 查找 ‘quick’ 的文档编号列表,该列表为 [1, 3]。
- 查找 ‘fox’ 的文档编号列表,该列表为 [1, 3, 4]。
- 然后,我们取这两个列表的交集,得到同时包含这两个词的文档编号:[1, 3]。
搜索引擎会返回文件1和3作为结果。因此。
OpenSearch的词典查询功能演示让我们来看看,如何使用Python客户端opensearch-py
在OpenSearch中进行全文搜索。为了说明这一点,我们将逐步介绍如何搭建一个基本的OpenSearch集群,索引一些文档,并执行一次全文搜索查询?
1. 安装并配置 OpenSearch 并安装 opensearch-py 客户端库
在我们继续之前,请确保您已经在本地运行了OpenSearch或可以访问托管服务。要安装Python客户端库(opensearch-py
),您可以使用如下命令:
pip install opensearch-py
使用pip安装opensearch-py库
2. 创建OpenSearch索引
首先,我们将创建一个索引,用来进行词汇搜索,使用标准文本分析器。
导入OpenSearch from opensearchpy
# 连接到OpenSearch
client = OpenSearch(
hosts=["http://localhost:9200"], # 如果使用的是托管的OpenSearch,请相应地更新此配置
)
# 定义索引的设置
index_name = "my_lexical_index"
index_body = {
"settings": {
"analysis": {
"tokenizer": {
"standard": {
"type": "standard"
}
},
"analyzer": {
"default": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"title": {"type": "text"},
"content": {"type": "text"}
}
}
}
# 创建名为index_name的索引
client.indices.create(index=index_name, body=index_body, ignore=400)
3. 给一些文档编制索引
现在我们将几个包含简单文本的文档索引到my_lexical_index
索引中。目的是使用词汇查询来搜索它们。
documents = [
{"title": "快速的棕色狐狸", "content": "跳过了那条懒狗。"},
{"title": "快速的动物", "content": "猎豹是陆地上跑得最快的动物之一。"},
{"title": "懒狗", "content": "狗整天都躺在太阳底下晒太阳。"},
]
for doc in documents:
client.index(index=index_name, document=doc)
4. 执行词汇搜索
我们现在执行一个词汇搜索,搜索词“狗”。在这种搜索中,只会查找包含“狗”的文档。
query = {
"query": {
"match": {
"content": "狗"
}
}
}
response = client.search(index=index_name, body=query)
# 打印结果
for hit in response['hits']['hits']:
print(f"标题是:{hit['_source']['title']},内容是:{hit['_source']['content']}")
解释一下:
- 我们使用一个
match
查询来在content
字段中查找 "dog" 单词。 - 该查询将返回所有包含 "dog" 的文档。重要的是要注意,在词汇查询中,查询词必须与文档中的完全一致(除非进行了诸如词干提取或同义词替换之类的文本预处理,不过这里没有应用此类预处理)。
- 了解词汇搜索的结果
返回的结果会包含所有含有确切术语“dog”的文档。下面是OpenSearch处理这种情况的方式说明。
- 完全匹配:术语“狗”必须出现在文档的
content
字段中。 - 排名:如果多个文档包含术语“狗”,OpenSearch 将根据该术语出现的频率和逆文档频率(IDF)对其进行排序。
- 相关性:词汇搜索的相关性评分主要基于词频和文档频率。搜索词在文档中出现得越频繁,其相关性评分就越高。
示例结果
假设提到的文件,输出可能如下所示。
标题: 那只敏捷的棕色狐狸, 内容: 跳过了那只懒散的狗狗。
标题: 那只懒狗, 内容: 那只狗整天都晒着太阳。
以下是在内容中包含“狗”这个词的两个文档。
词汇搜索的局限性
虽然词汇搜索快速且高效,特别是在OpenSearch中的倒排索引的帮助下,但它也有一些局限。
- 严格匹配:词汇搜索仅限于完全匹配。它不会很好地处理单词的变化、同义词或上下文。例如,查询“dog”这样的查询不会返回包含“canine”术语的文档。
- 缺乏上下文理解:词汇搜索不理解词背后的意义或它们之间的关系。搜索“apple”可能会同时返回水果和科技公司的结果,这种搜索结果取决于该词在文档中的出现。
- 不注重词序:在某些情况下,词序可能很重要(例如,“red apple”与“apple red”),但词汇搜索并不会像语义搜索那样会考虑到词序的含义。
在OpenSearch中的语义搜索功能注意: 关于词汇搜索还有更多内容可以探索,这些内容将在我们的下一篇文章中详细讨论。在那篇文章中,我们将深入探讨多匹配查询,并研究各种类型,如 最佳字段匹配、多字段、跨字段、短语、短语前缀 和 布尔。此外,我们还将讨论如何为短语匹配设置权重,在词元搜索中得分是如何计算的,以及其他可以根据具体需求进行调整的参数——不论是需要精确短语匹配还是处理不完整的查询(例如自动完成功能)。
语义搜索超越了简单的词汇(基于关键词)搜索,试图理解查询和文档内容背后的含义。而词汇搜索仅匹配确切的词语,语义搜索则是根据查询和文档的上下文、意图及词汇间的关联来返回结果。语义搜索通常依赖更高级的模型,例如,基于嵌入的模型,这些模型将查询和文档转换为高维向量空间中的数值表示(即嵌入)。
语义搜索的关键在于理解用户的实际需求,比如搜索“最快的陆地动物”时,即使文档中没有明确提到猎豹,也能找到相关的信息。
语义搜索的主要组成部分语义搜索在OpenSearch中依靠多种高级组件和技术来进行基于向量的相似性搜索,这使得引擎能够理解查询的意义和上下文,从而更好地回应用户的搜索需求。与基于词汇的搜索不同,语义搜索能够理解同义词、相关术语和上下文含义。例如,“狗”和“犬”或“苹果”(水果)和“苹果”(科技公司)在不同情况下可以被理解为相关或不同。下面列出了语义搜索的一些关键组成部分:
1. 密集向量表示(嵌入表示)
- 嵌入 是从单词、短语、句子或整个文档中生成的固定大小的向量表示,捕捉语义含义。与专注于匹配单个单词的词语搜索不同,语义搜索将文档和查询转换成能表达意义的向量。
- 常用的生成这些嵌入的模型包括 BERT、Word2Vec、GloVe 和 句子转换器 Sentence-Transformers。这些模型将文本映射到高维向量空间,在这个空间里,语义相近的词或短语会更靠近。
2. HNSW(层次化的可导航小世界网络)索引
- HNSW 是一种用于高效相似性搜索的流行算法,常用于大规模向量搜索中的语义搜索。它通过将向量组织成图结构来实现快速检索最近邻(即最相似的向量)。
- HNSW 以其在大规模数据集中的搜索效率和效果而著称,相比传统的最近邻搜索方法,特别是在高维向量空间中,它提供了更快且更准确的结果。在 OpenSearch 中,可以利用这种索引来进行相似性搜索,使系统能够基于查询向量快速识别出语义相似的文档。
3 矢量搜索
- 当查询和文档都被转换成向量时,OpenSearch 就使用向量搜索来比较查询向量和存储在索引中的文档向量。这通常通过 余弦相似度 或 点积 来评估向量之间的相似度。
- OpenSearch 提供了 KNN(k-最近邻) 搜索功能,可以在向量空间中实现高效和可扩展的搜索。
4. KNN 搜索.
- KNN(k-近邻) 是一种用于语义搜索的搜索算法,通过计算向量相似度来找到与给定查询最相似的文档。在 OpenSearch 中,与 HNSW 索引协同工作,高效地检索出与查询向量最接近的文档。
- KNN 搜索使 OpenSearch 能够通过专注于最相似的结果而不是对所有文档进行全面比较,来处理大规模的数据集。
5. 文本分析工具和分词工具
- 虽然语义搜索主要依赖于嵌入来表达意义,文本分析器和分词工具在生成嵌入之前对文本进行预处理仍然非常重要。它们处理诸如小写转换、词干提取和停用词移除等任务,有助于提高嵌入质量,确保文本输入的一致性和标准化。
- 这些预处理器确保文本中的变体,如拼写错误或常见词,在生成嵌入时不会引入噪音。
6. 模型调优。
- 在特定数据集或任务上微调模型可以显著提高语义搜索的准确性。OpenSearch可以整合诸如BERT、GPT或特定领域预训练模型,生成更适合搜索内容的嵌入。
- 微调有助于模型更好地理解特定领域文档的上下文,从而使这些文档在搜索中更相关和高效。
现在让我们来看看如何利用Python客户端(opensearch-py
)在OpenSearch中实现语义搜索。在这个演示中,我们将重点放在如何集成一个预训练的嵌入模型以生成并存储向量到OpenSearch中。然后我们将根据查询向量和文档向量之间的相似性来进行语义搜索。
1. 安装并配置 OpenSearch 和必要的库文件
首先,确保你已经运行了OpenSearch,并且安装了所需的库。你还需要一个预训练的模型来生成嵌入。在这个演示中,我们将使用Sentence-Transformers,一个流行的生成句子嵌入的库。如果你还没有安装opensearch py库,请运行下面的完整命令;否则,请移除该命令并安装剩余的部分。
pip install opensearch-py sentence-transformers numpy
2. 在 OpenSearch 中生成和存储嵌入
在这个例子中,我们将使用Sentence-Transformers(句子转换器,一种生成高质量的句子嵌入的模型),将文档和查询转换成向量。
从opensearchpy导入OpenSearch客户端
从sentence_transformers导入SentenceTransformer
导入numpy作为np
# 连接到OpenSearch
client = OpenSearch(
hosts=["http://localhost:9200"], # 如果使用托管的OpenSearch服务,请更新此URL
)
# 加载预训练的句子转换器模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 定义用于语义搜索的索引(包含密集向量字段)
index_name = "my_semantic_index"
# 创建用于语义搜索的索引(包含密集向量字段)
index_body = {
"settings": {
"index": {
"knn": True # 启用向量的KNN搜索
}
},
"mappings": {
"properties": {
"title": {"type": "text"},
"content": {"type": "text"},
"content_vector": {
"type": "dense_vector",
"dims": 384 # 嵌入的维度(对于'all-MiniLM-L6-v2')
}
}
}
}
# 创建索引
client.indices.create(index=index_name, body=index_body, ignore=400)
# 定义文档
documents = [
{"title": "快速棕色的狐狸", "content": "跳过了懒狗。"},
{"title": "快速的动物", "content": "猎豹是最快的陆地动物之一。"},
{"title": "懒狗", "content": "狗整天躺在太阳底下。"},
]
# 将文档索引到嵌入中
for doc in documents:
# 生成内容的嵌入向量
embedding = model.encode(doc["content"]).tolist()
doc["content_vector"] = embedding
client.index(index=index_name, document=doc)
在这一步中,我们将要:
- 创建一个具有
dense_vector
字段的 OpenSearch 索引,用于存储嵌入向量。 - 使用 Sentence-Transformers 生成每个文档内容的句子向量。
- 将文档及其相应的嵌入向量加入 OpenSearch 中。
3. 进行语义搜索
现在,我们来做个语义搜索查询。这里的思路是为用户的查询生成一个嵌入,并通过比较查询向量和文档向量的余弦相似度找到最相似的文档。
# 定义搜索查询
query_text = "什么陆地上跑得最快的动物?"
# 生成查询向量
query_vector = model.encode(query_text).tolist()
# 定义使用KNN(最近邻)搜索的OpenSearch查询
search_query = {
"query": {
"knn": {
"content_vector": {
"vector": query_vector,
"k": 3 # 检索相似文档的数量
}
}
}
}
# 执行查询
response = client.search(index=index_name, body=search_query)
# 打印结果
for hit in response['hits']['hits']:
print(f"标题: {hit['_source']['title']}, 内容: {hit['_source']['content']}")
解释一下:
- 我们首先使用相同的
SentenceTransformer
模型为查询文本生成嵌入向量。 - 然后,我们使用OpenSearch中的
knn
查询执行KNN(k近邻)搜索。此查询将生成的查询向量与存储的文档向量进行比较,并返回最相似的前k
个文档。 - 搜索结果是根据向量的相似性返回的,而不仅仅是基于单词的精确匹配。
4. 理解语搜结果
返回的结果将包括与查询最相似的文档。这些结果将按照在向量空间中的接近度排序。即使查询中的词与文档中的词并不完全一致,仍然可能捕捉到含义或上下文的相似性。
例如,对于“最快的陆地动物是哪一种?”这个查询,模型可能会返回一篇关于猎豹的内容,尽管查询中并没有提到“猎豹”这个词。
示例结果
如果按照前面说过的那些文件,输出可能看起来是这样的:
标题:快速的动物们,内容:猎豹是陆地上跑得最快的动物之一。
标题:敏捷的棕色狐狸,内容:跳过了懒狗。
在这种情况下,语义搜索正确根据查询意图找到最相关的文档(“快速的动物”)。
语义搜索的优势
- 上下文理解:语义搜索能理解词语背后的意思,并根据用户的意图给出结果,而不只是简单的关键词匹配。
- 同义词处理:即使查询和文档使用了不同的词语(例如“狗”和“犬”),它仍然能提供相关结果。
- 改善用户体验:用户不需要在查询时太过具体,这可以提升整体搜索体验。
语义搜索的限制
- 复杂度:实现语义搜索需要额外的准备工作,比如生成向量和管理存储。它比词汇搜索在计算上更费力。
- 对模型的依赖:语义搜索的质量高度依赖于所使用的嵌入式模型。较差的模型可能无法准确捕捉单词的意义。
结论.注意: 语义搜索涵盖了我们将在即将发布的博客文章中深入探讨的各种高级技术。在那篇文章中,我们将探讨诸如 嵌入模型 、 基于向量的相似度搜索 以及 使用密集向量来获得更准确的结果 的关键主题。我们还将介绍 排名检索融合 (RRF) 的概念,该概念通过结合词汇和语义搜索结果来实现混合方法中起关键作用。这种混合策略通过结合两种方法的优势从而提升搜索性能。此外,我们还将讨论如何通过微调模型和优化相似度指标来显著提高搜索的相关性,特别是在处理复杂查询和特定领域任务时尤其重要。
两者 词汇搜索 和 语义理解搜索 在搜索引擎中都起着重要作用,选择哪种搜索方式主要取决于你想要提供的搜索体验以及查询的复杂程度。
- 词汇搜索 直观、快速且有效,适用于用户确切知道自己在找什么的场景。它在文档可根据确切关键词轻松索引,且只需精确匹配就表现优异的情况下表现出色。然而,当涉及到理解上下文、处理同义词或处理更细微的查询时,它存在一定的局限性。
- 语义搜索 则提供了一种更直观且理解上下文的搜索体验。通过利用嵌入和基于向量的相似性搜索,语义搜索能够理解查询和文档背后的意义,从而返回更相关的结果。它在处理复杂或模糊的自然语言查询方面表现出色,但需要更多的计算资源和设置,特别是在生成嵌入和执行向量搜索时。
总之,词典搜索适用于更简单、定义明确的用例,而语义搜索则更适合需要理解上下文和意图的高级应用场景。通常,结合词典搜索和语义搜索的混合方法策略是最有效的策略,这样用户能够同时利用词典搜索的速度和效率,以及语义搜索的丰富且上下文相关的优点和丰富性。
请继续关注Opensearch的更深入内容!
参考文献:- OpenSearch 文档资料. (日期不详). 快速入门. OpenSearch. 2025年1月5日访问于 https://opensearch.org/docs/latest/getting-started/quickstart/
- OpenSearch 文档资料. (日期不详). 关键词搜索. OpenSearch. 2025年1月5日访问于 https://opensearch.org/docs/latest/search-plugins/keyword-search/
- OpenSearch 文档资料. (日期不详). 向量搜索. OpenSearch. 2025年1月5日访问于 https://opensearch.org/docs/latest/search-plugins/vector-search/
- OpenAI. (2023). ChatGPT:优化了对话语言模型. 2025年1月5日访问于 https://openai.com/chatgpt
共同學習,寫下你的評論
評論加載中...
作者其他優質文章