Skip to main content

OpenSearch 2.9 的全新体验特性 —— 扩展

· 16 min read

Extensions 是 OpenSearch 2.9 的一个新实验性功能,它允许您扩展 OpenSearch 并在不影响集群可用性的情况下独立扩展工作负载。在本博文中,我们将介绍扩展并将其与插件进行比较。通过使用一个运行高基数异常检测的机器学习算法的 36 节点集群,我们将演示如何实现每个数据节点的成本降低 33%,性能与插件相匹配,并且仅增加了一个扩展节点的成本。

旧的方法:插件

OpenSearch 是 Elasticsearch 7.10 的一个分支,其通过插件提供扩展功能。直到现在,插件一直是扩展 OpenSearch 功能的最佳方式,我们撰写了一篇博文来帮助您了解它们的工作原理。目前,OpenSearch 项目维护着 17 个插件,并且在过去的两年中,我们在开发和运营这些插件时积累了很多经验。在此过程中,我们遇到了现有插件体系结构中的许多瓶颈。

插件在 OpenSearch 引导时被类加载到内存中,因此在 OpenSearch 进程内运行。这导致了以下三个架构问题:

  1. 插件需要与 OpenSearch 进行严格的版本兼容。每次 OpenSearch 核心的补丁版本更改都需要重新编译和重新发布插件。
  2. 插件不是隔离的,因此可能会对 OpenSearch 集群产生致命影响,并且不能独立扩展。
  3. 需要同时管理所有组件的依赖关系,这使得在单个产品分发中包含 20 多个插件变得具有挑战性。

新的方法:扩展

与插件不同,扩展以独立的进程或远程节点运行,是隔离的,并且具有干净、明确定义的接口与 OpenSearch 核心进行交互。这些接口是版本化的(遵循语义化版本规范),并且适用于次要和补丁版本,未来有可能与多个主要版本的 OpenSearch 兼容。

扩展架构如下图所示。有关详情,请参阅扩展设计文档

实验性 SDK 发布

在我们的可扩展性路线图上的第一步是推出一个 SDK,使得扩展 OpenSearch 的体验比使用插件更加简便。今天,我们推出了 Java 版本的 opensearch-sdk-java,该版本与 OpenSearch 2.9 及更高版本兼容。要了解如何启用实验性扩展功能,请参阅开发者指南。

扩展接口在 OpenSearch 的次要和补丁版本中是兼容的,因此在升级到下一个版本的 OpenSearch 2.10 时,无需重新编译、重新部署或重新安装扩展。这些 API 目前包括在扩展中公开 REST 接口的功能,并附带客户端库以与 OpenSearch 集群中的数据进行交互。

SDK 的第一个版本 v0.1.0 在 Maven 下的名称为 org.opensearch.sdk 。你可以从开发者文档开始构建第一个扩展。

我们如何编写第一个扩展?

我们选择将异常检测插件作为第一个要迁移到扩展的插件,因为它的复杂性和对性能的敏感性相结合。

在技术上,异常检测并不是我们的第一个扩展。就像任何探索新领域的人一样,我们创建了一个“Hello World”示例,以展示开始时的简易性,并演示基本接口的使用,而无需涉及真实应用程序的复杂性。然后,我们希望面对迁移到完全成熟、可投入生产的插件的挑战。

大多数插件向 OpenSearch 发出用户数据或集群状态的请求。我们的扩展需要一个 REST 客户端来匹配这种功能。我们最初选择了 OpenSearch Java 客户端。作为一个稳健、基于规范的自动生成的客户端,它似乎是一个理想的选择,特别是考虑到我们未来计划将 SDK 移植到其他编程语言,这些语言都具有类似的客户端。对于新的扩展开发,这仍然是首选的客户端。然而,对于实验性版本,异常检测非常依赖于现有的 NodeClient API,我们发现自己不得不重新编写太多的代码。

Java 高级 REST 客户端,目前是 OpenSearch 核心的一部分,具有几乎相同的接口,但已被提议废弃。我们选择将其用于早期的迁移工作,并决定在 OpenSearch 3.0 或 4.0 中删除它之前不切换到该客户端。这使我们能够最大限度地减少工作量,并通过添加少量的包装类来快速迁移所有 API。

在努力减少工作量的同时,我们实施了一些效率低下的操作。异常检测插件经常使用 OpenSearch 集群状态对象。由于集群状态在 OpenSearch 核心中始终是最新的,因此对于有效地获取信息很有用。然而,在扩展环境中,出于例如查找索引或别名是否存在的目的,多次检索整个集群状态被证明是 是一种资源浪费,导致了许多 API 超时(在我们的初始原型中,创建一个检测器通常需要近 13 秒)。为了解决这个问题,我们使用了现有的设置并针对索引 API 和集群 API 进行了有针对性的调用,将引导时间缩短为不到一秒钟。

有关实施细节和将异常检测作为插件和扩展的差异,请参阅异常检测作为扩展的代码

性能测试

为了以扩展形式测试高基数异常检测(HCAD),我们复制了在此博文中描述的设置,并用 28 个 r5.2xlarge 数据节点(在相同成本下替换 36 个节点),加上一个运行异常检测扩展的 r5.16xlarge 实例。我们分析了 1,000 个历史时间段,每个时间段有 1,000 个实体,生成了 1 百万个结果:

GET /_extensions/_ad/detectors/results/_search
{
"query": {
"range": {
"execution_start_time": {
"gte": 1687456200000,
"lte": 1687456260000
}
}
},
"track_total_hits": true
}
{
...
"hits": {
"total": {
"value": 1000000,
"relation": "eq"
},
...
...
}

为确保稳定的集群性能,插件限制了这些分析速率。经过一些微调(我们将同时检测器任务从 10 个增加到 96 个,并将批处理任务时间间隔从 5 秒减少到 3 秒),扩展版本的异常检测能够在 55 秒内成功完成此分析。这并不完全是一比一的比较,因为之前提到的博文中的测试涉及 100 万个较小的模型 x 1 个时间片生成 100 万个结果(我们需要更多的时间来实现对扩展中实时异常检测的支持)。然而,我们相信一旦扩展能够在该环境中运行,我们应该能够在单个节点上或少量节点上支持 100 万个模型。

由于我们的扩展在远程节点上运行,我们观察到了 API 调用延迟的预期增加。例如,启动/停止检测器的调用从 50-100 毫秒增加到 200-300 毫秒。这些调用是用于设置异常检测的边缘 CRUD 操作。以下图表提供了扩展和插件之间延迟的比较。有关更多信息,请参阅异常检测扩展的性能测试

扩展与插件延迟对比图

性能改进

我们证明将异常检测从插件升级为扩展可以与现有用户的性能相匹配。但我们相信我们可以通过以下三种方式进一步改进:

  1. 使用更便宜的集群 我们发现通过将异常检测编写为扩展,我们可以减少集群的成本,并且仍然能够满足相同的性能要求。因为异常检测不再在数据节点上运行,我们能够将所有内存优化的 r5.2xlarge 节点替换为通用型 c5.2xlarge 节点,从而使历史分析每个数据节点的成本降低 33%,并且只增加了一个扩展节点的成本。

    我们还可以进一步减少数据节点的数量,以优化搜索数据和索引结果。例如,因为原始性能基准测试中的大多数数据节点除了索引结果外几乎没有使用,我们测试了一个只有 24 个 c5.2xlarge 数据节点的集群,再加上 1 个 r5.16xlarge 的扩展节点,进一步减少了 33%。我们相信通过进一步试验和微调,您可以找到更优化的混合方案,具体取决于集群的其他资源需求。

  2. 增加索引吞吐量 虽然我们尚未进行此实验,但我们相信您可以通过增加分片来进一步独立微调集群,从而在索引异常检测结果时获得更好的吞吐量。

  3. 提高历史结果生成速率 异常检测的历史 HCAD 限制了历史时间段结果的速率,限制了异常检测使用堆的一半,并将检测的最大限制设置为 10 个并行线程,以保护集群不会超出极限。您可以通过添加更多的 CPU 或 RAM,与集群的其他部分独立交换空间,或者横向扩展扩展节点来提高这些限制。

结论

通过将所有资源正确配置到正在执行的任务,扩展可以节省成本并提高性能。在我们的测试中,我们不需要将整个集群都设置为内存优化服务器,以执行历史异常检测,因此每个数据节点的成本降低了 33%。

作为插件,异常检测与 OpenSearch I/O 共享 CPU 和内存,并且必须限制自己的资源消耗,以确保集群的稳定性。例如,在异常检测插件 HCAD 测试中,模型被限制在使用堆的一半,堆本身又是节点内存的一半。在异常检测扩展场景中,可以将堆缩放以使用服务器大部分内存,并充分利用专用于异常检测任务的 64 个虚拟 CPU。

其他现有的插件也可以从这种模式中受益,包括所有具有内存或 CPU 限制的插件,例如 ML Commons、k-NN 和 Reporting。这项新技术还允许实现新的资源密集型扩展,例如重排、数据转换、大数据处理或视频索引等。

下一步

我们对可扩展性取得的进展和早期性能基准测试结果感到非常高兴。未来,短期内我们将把重点转向使用可扩展性项目的经验,来实现 OpenSearch 机器学习(ML)产品的可扩展性。我们的目标是提供基于规范的应用程序框架,让用户能够在 OpenSearch 中创建无代码的 ML 驱动应用程序,并最终提供一种简单的一键方式,让用户可以测试、构建和部署 ML 驱动的应用程序。此外,在我们决定发布这个实验性功能和 SDK 的普遍可用版本之前,我们希望得到您的反馈。阅读扩展文档后,请尝试实现一个实验性扩展,并通过在 OpenSearch 论坛上发布反馈或在 opensearch-sdk-java 存储库中开启问题来告诉我们您的意见。我们迫不及待地希望听到您的声音!

以上就是关于 OpenSearch 2.9 中扩展功能的介绍以及与插件的比较,以及对异常检测插件迁移到扩展的经验分享和性能测试结果。OpenSearch 团队致力于不断改进和扩展 OpenSearch,提供更好的性能和更灵活的功能,以满足用户的需求。如果您对 OpenSearch 感兴趣,我们鼓励您尝试使用扩展功能并提供反馈,以帮助改进这一功能。谢谢您的阅读!

OpenSearch 2.9.0 新特性介绍

· 15 min read

OpenSearch 2.9.0 已经发布,带来了一些新功能,旨在帮助您构建更好的搜索解决方案,并将更多机器学习(ML)整合到您的应用程序中,同时还更新了用于安全分析工作负载、地理空间可视化等方面的功能。此版本还提供了新的压缩编解码器,可以显著提高性能并减小索引大小。对于开发人员而言,实验性扩展软件开发工具包(SDK)简化了在 OpenSearch 之上构建功能和功能的工作。以下是 OpenSearch 和 OpenSearch Dashboards 最新版本的一些亮点;若想了解更详细的内容,请查阅发布说明

通过搜索管道提升搜索功能

搜索专业人员希望引入新的方式来增强搜索查询和结果。通过搜索管道的普遍可用性,您可以配置一个或多个处理器的列表,以在 OpenSearch 内部转换搜索请求和响应。通过在 OpenSearch 中集成处理器,如查询重写器或结果重新排序器,您可以使搜索应用程序更准确高效,并减少对定制开发的需求。

搜索管道包含三个内置处理器,filter_query、rename_field 和 script request,以及新的面向开发人员的 API,可使希望构建自己处理器的开发人员能够这样做。您可以在 OpenSearch Playground 中自行探索搜索管道。这些功能是项目学习和开发的持续优先事项;如果您想对新的处理器或其他想法提供意见,请查阅意见征询

更轻松地构建语义搜索应用程序

OpenSearch 项目在 OpenSearch 2.4.0 中发布了实验性的神经搜索功能。随着 2.9.0 版本的发布,神经搜索已经可以在您的搜索工作负载中进行生产使用。这些工具允许您将文档和查询向量化,并使用 k 最近邻(k-NN) 搜索这些转换后的向量,因此您可以利用 OpenSearch 的向量数据库功能来支持语义搜索等应用程序。现在,您可以将传统的 BM25 词汇搜索与深度学习支持的语义搜索相结合,以及为提高搜索相关性调整查询的新方法。

集成和管理您的 ML 模型在 OpenSearch 集群中

像语义搜索这样的应用程序需要集成 ML 模型。此版本通过 ML 框架的正式推出,使操作化和集成 ML 模型变得更加简单。ML 框架最初作为 2.4.0 版本的实验性功能推出,它允许您将自己的 ML 模型上传到 OpenSearch,支持从多种工具(如 PyTorch 和 ONNX)加载文本嵌入模型,并准备将模型用于神经搜索和其他应用程序的部署。在为正式推出做准备的过程中,OpenSearch 2.9.0 还引入了 ML 模型访问控制功能,允许管理员管理通过该框架集成的单个模型的访问权限。

集成外部管理的 ML 模型

OpenSearch 2.9.0 通过启用集成器,使集成者能够轻松地创建连接到人工智能(AI)服务和 ML 平台的连接器,只需在 JSON 中定义蓝图即可。这些 AI 连接器使用户能够使用托管在连接的服务和平台上的模型来支持语义搜索等 ML 工作负载。构建连接器的说明已包含在文档中。例如,此版本包含了适用于 Amazon SageMaker 托管模型的连接器。该项目将在不久的将来发布 Cohere Rerank 和 OpenAI ChatGPT 的连接器,并将添加其他集成。

使用向量数据库增强搜索功能

随着此版本的发布,OpenSearch 的近似 k-NN 实现支持使用 Facebook AI Similarity Search(FAISS)引擎进行查询的预过滤。现在,您可以在使用 FAISS 构建的 k-NN 索引上,在执行最近邻搜索之前使用元数据来过滤查询,从而提供更高效的 k-NN 搜索和更好的性能。之前,OpenSearch 仅支持 Lucene 索引的预过滤。

OpenSearch k-NN 的另一个更新是支持 Lucene 的字节大小向量。用户现在可以选择摄取和使用每个维度压缩为一个字节大小的向量。这样可以减小加载、保存和执行向量搜索所需的存储和内存要求,但可能会导致精度下降。

在 OpenSearch Dashboards 中构建监视器和探测器

现在,您可以直接将警报和异常与您用于监视环境的主要仪表板叠加在一起。通过将 OpenSearch 的警报和异常检测工具与 OpenSearch 可视化工具集成,此版本简化了监视系统和基础架构的用户工作。用户还可以直接从 OpenSearch Dashboards VISLIB 图表或线性可视化创建警报监视器或异常探测器,然后查看叠加在配置的可视化上的警报或异常。已经定义了监视器或探测器的用户现在可以将其与可视化关联起来。这样就不再需要在可视化工具和创建警报或异常探测器所需的信息之间切换,更便于探索数据并发现发现。

使用复合监视器获取更有意义的警报通知

2.9.0 版本中新增的复合监视器是 OpenSearch 警报工具箱的又一个补充。 复合监视器允许用户将由多个单独监视器生成的警报链接成单一的工作流程。只有当所有监视器之间的组合触发条件都满足时,用户才会收到通知。这使得用户可以基于多个标准分析数据源,并更细致地了解数据。现在,用户有机会创建针对特定通知对象的目标通知,同时减少警报的总量。

通过新的索引压缩选项提高性能

OpenSearch 提供内置编解码器,对索引进行压缩,影响索引文件的大小和索引操作的性能。之前的版本包括两种编解码器类型:默认索引编解码器优先考虑性能,而 best_compression 编解码器在索引时实现高压缩和较小的索引大小,但可能导致索引时 CPU 使用率增加,并且可能导致较高的延迟。随着 2.9.0 版本的发布,OpenSearch 新增了两个新的编解码器,即 zstd 和 zstd_no_dict,它们使用了 Facebook 的 Zstandard 压缩算法。这两个编解码器旨在在压缩和性能之间取得平衡,其中 zstd_no_dict 不包含字典压缩功能,因此在索引和搜索性能上可能会略微增加索引大小。

这两个新的编解码器 zstd 和 zstd_no_dict 提供了一种配置压缩级别作为索引设置(index.codec.compression_level)的选项,其他编解码器不支持这一设置。压缩级别在压缩比和速度之间进行权衡;较高的压缩级别会导致较小的存储空间,但在压缩时会消耗更多的 CPU 资源。优化这些权衡取决于工作负载的多个方面,为了获得整体性能的最佳优化,最好尝试多种级别。

通过上面的表格可以看出,使用 NYC 出租车数据集进行的测试显示,与默认编解码器相比,使用 zstd 可以实现高达 35%的压缩改进,并且 zstd 的吞吐量增加了 7%,而 zstd_no_dict 的吞吐量增加了 14%。在升级到 2.9.0 版本后,用户可以通过修改新建或现有索引的设置来实现性能改进。

使用安全分析简化威胁检测

安全分析现在支持遵循开放网络安全模式架构 OCSF 的日志数据新摄取模式,可以支持 Amazon Route 53、AWS CloudTrail 和 Amazon Virtual Private Cloud(Amazon VPC)的 OCSF 日志。OpenSearch 安全分析的关联引擎也已经正式推出。您可以创建自定义关联规则,显示跨不同日志源(如 DNS、Netflow 和 Active Directory)的可视化知识图和发现列表。这些知识图可用于安全调查,以分析相关发现,帮助您识别组织中不同系统之间的威胁模式和关系。使用这些知识图可以帮助您更快地应对潜在的安全威胁。

对地理空间形状数据进行聚合指标

OpenSearch 使用 geoshape 和 geopoint 字段类型存储地理空间数据。在之前的版本中,用户能够对 geopoint 数据类型执行聚合;而在 2.9.0 版本中,OpenSearch 还通过 API 支持对 geoshape 数据类型进行聚合作为后端功能。此版本增加了对 geoshape 数据类型的支持,包括三种类型的聚合:geo_bounds,一种度量聚合,计算包含字段中所有地理值的边界框;geo_hash,一种多桶聚合,将地理形状分组到表示网格中的桶中;geo_tile,一种多桶聚合,将地理形状分组到表示地图瓦片的桶中。

监控您的分片和索引的健康状态

OpenSearch 提供了一系列指标,帮助您监控 OpenSearch 集群的健康状态。此版本增加了 CAT shards 和 CAT indices 监视器。这些更新允许您监控所有主分片和副本分片的状态,以及与索引健康和资源使用相关的信息,以支持运行时间。

使用新的 SDK 扩展 OpenSearch 的功能

OpenSearch 项目维护了一组插件库,可以在不同的工作负载和用例中扩展 OpenSearch 的功能。由于某些原因,插件在 OpenSearch 进程内运行,这意味着它们必须保持版本兼容,并且它们的资源会随着 OpenSearch 集群的规模而扩展。对于开发人员来说,构建新的插件需要相当熟悉 OpenSearch 的架构和代码库。

一些开发人员希望有一种构建与 OpenSearch 兼容工具的方法,可以缓解这些限制。为了解决这个问题,此版本引入了一个实验性的 SDK,为开发人员提供了构建 OpenSearch 扩展的工具。OpenSearch Extensions SDK 提供了与 OpenSearch 和其他已安装的插件和扩展进行通信的规范化接口,允许扩展与 OpenSearch 版本解耦,解耦资源,并引入许多其他设计变更,旨在使开发人员更容易构建和用户部署在 OpenSearch 上的新创新。如要访问 SDK,请查看 opensearch-sdk-java 存储库。

开始使用

您可以在此处下载最新版本的 OpenSearch,并在 Playground 中探索 OpenSearch Dashboards。有关此版本的更多信息,请参阅发布说明和文档发布说明。你也可以在社区论坛上发表您对此版本的反馈!

(注意:以上内容是对原文的翻译,可能会有细微的语义调整,但力求保持原意和信息准确性。如果您对某些特定术语或内容有疑问,欢迎进一步探讨。)

使用 ElasticSearch 和 Transformers 实现语义搜索

· 11 min read

语义/矢量搜索是一种强大的技术,可以大大提高搜索结果的准确性和相关性。与传统的基于关键字的搜索方法不同,语义搜索使用单词的含义和上下文来理解查询背后的意图并提供更准确的结果。实现语义搜索的最流行的工具之一是 Elasticsearch,这是一个高度可扩展且功能强大的搜索引擎,可用于索引和搜索大量数据。在本文中,我们将探讨语义搜索的基础知识以及如何使用 Elasticsearch 实现它。在本文结束时,您将对语义搜索的工作原理以及在项目中实现语义搜索的实用技能有深入的了解。

Elasticsearch

Elasticsearch 是一个基于 Lucene 库的强大且可扩展的开源搜索引擎。它旨在处理大量非结构化数据并提供快速准确的搜索结果。Elasticsearch 使用分布式架构,这意味着它可以在多个服务器上水平扩展,以处理大量数据和流量。

Elasticsearch 建立在 RESTful API 之上,这使得它很容易与各种编程语言和工具集成。它支持复杂的搜索查询,包括全文搜索、分面搜索和地理搜索。Elasticsearch 还提供了一个强大的聚合框架,允许您对搜索结果执行复杂的数据分析。

Transformers

Transformers 是一种机器学习模型,它彻底改变了自然语言处理 (NLP) 任务,例如语言翻译、文本摘要和情绪分析。Transformers 最初是由 Vaswani 等人在 2017 年的一篇论文“注意力是你所需要的一切”中引入的,此后成为许多 NLP 任务的最新模型。

与依赖于递归神经网络(RNN)和卷积神经网络(CNN)的传统 NLP 模型不同,Transformers 使用自我注意机制来捕获句子中单词之间的关系。自我注意允许模型专注于输入序列的不同部分,以确定单词之间最重要的关系。这使得转换器在处理单词之间的长期依赖关系和上下文关系方面比传统模型更有效。

在本文中,我将使用 TensorFlow 的通用句子编码器来编码/矢量化数据。您也可以选择任何其他形式的编码器。

先决条件

在开始之前,需要如下准备工作:

  • 了解 python,包括使用外部库和 IDE 的知识。
  • 了解 Transformers / Vectorizers 及其输出的维度数组。
  • Elasticsearch 8.6 及更高版本。

开始构建

为了构建语义搜索功能,我们使用 Python 作为后端,Elasticsearch 8.6.1 ,以及 TensorFlow 的 Universal Sentence Encoder 用于获取嵌入/向量。

先安装依赖项:

pip install elasticsearch==8.6.1
pip install tensorflow==2.10.0

安装依赖项后,首先需要文本数据。获取文本数据后,在首选 IDE 中使用 python 读取它。

from elasticsearch import Elasticsearch
import pandas as pd
import numpy as np

df = pd.read_csv('your_text_dataset.csv')

现在读取文本数据后,第一个任务是将其转换为向量或嵌入。正如我之前提到的,我使用的是 TensorFlow 的通用句子编码器,它在提供字符串时输出“512”维度的向量/嵌入。

对于其他转换器/矢量器,这将有所不同,您需要记住这一点以进一步的步骤。

让我们首先加载通用句子编码器模型,这里我已将其存储在系统中。您也可以在其 网站 上找到相关 API。

model = hub.load("path/model v4")

成功加载模型后,现在我们的下一个任务是将数据集中的文本转换为向量/嵌入,并将其存储在名为 Embeddings 的新字段/列中。

with tf.compat.v1.Session() as session:
session.run([tf.compat.v1.global_variables_initializer(), tf.compat.v1.tables_initializer()])
query_array = session.run(model(df["Text"]))

vectors = []
for i in query_array:
vectors.append(i)

df["Embeddings"] = vectors

df.to_csv("your_text_dataset_with_embeddings.csv", index=False)

注意:在这个的数据集中,有一个名为 Text 的字段/列。请根据您的数据集的情况来修改该字段名。

一旦嵌入完成并存储在新字段中,就可以将这些数据插入到前面我们安装的 Elasticsearch 中。

要插入数据,我们首先必须连接到 Elasticsearch,我们使用 python 来实现这个操作:

http_auth = ("elastic_username", "elastic_password")
es_host = "https://localhost:9200"
context = create_default_context(cafile="http_ca.crt")
es = Elasticsearch(
es_host,
basic_auth=http_auth,
ssl_context=context
)

要验证是否已建立连接,您可以先在浏览器上打开 https://localhost:9200 检查是否可以正常访问。您还可以通过运行 es.ping() 来检查来自 IDE 的连接。如果成功连接,输出应为 True

现在我们已经建立了与 Elasticsearch 的连接,接下来继续配置 Elasticsearch 索引:

configurations = {
"settings": {
"analysis": {
"filter": {
"ngram_filter": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 15,
},
"english_stop": {
"type": "stop",
"stopwords": "_english_"
},
"english_keywords": {
"type": "keyword_marker",
"keywords": ["example"]
},
"english_stemmer": {
"type": "stemmer",
"language": "english"
},
"english_possessive_stemmer": {
"type": "stemmer",
"language": "possessive_english"
}
},
"analyzer": {
"en_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase",
"ngram_filter",
"english_stemmer",
"english_possessive_stemmer",
"english_stop"
"english_keywords",
]
}
}
}
},
"mappings": {
"properties": {
"Embeddings": {
"type": "dense_vector",
"dims": 512,
"index": True,
"similarity": "cosine"
},
}
}
}
configurations["settings"]

上述配置可以让我们在插入数据时进行索引配置,让我们仔细看看一些重要的参数。

  • “type”:类型必须始终设置为“dense_vector”。这样做是为了让 ElasticSearch 了解这些是向量,并且不会自行为此字段分配浮点类型。
  • “dims”:也就是维度。就像我之前提到的,通用句子编码器产生并输出“512”维度,这就是为什么我们在参数中提供“512”的原因。
  • “index”:索引必须设置为“True”,以便创建此字段并在 ElasticSearch 中具有 dense_vector 类型的特性。
  • “similarity”:求余弦相似性。

配置索引后,继续创建索引:

es.indices.create(index='my_new_index',
settings=configurations["settings"],
mappings=configurations["mappings"]
)

这里我们将索引命名为 “my_new_index”,最后我们准备将数据插入到 Elasticsearch 上的这个索引中:

actions = []
index_name = 'my_new_index'
for index, row in df.iterrows():
action = {"index": {"_index": index_name, "_id": index}}
doc = {
"id": index,
"Text": row["Text"],
"Price": row["Price"],
"Quantity": row["Quantity"],
"Embeddings": row["Embeddings"]
}
actions.append(action)
actions.append(doc)

es.bulk(index=index_name, operations=actions)

搜索

一旦完成数据插入后,就可以搜索这些数据并提出一些相关问题。我们从一个我们想要获得答案的问题开始。

query = "Which is the latest phone available in your shop?"

由于我们要在 Elasticsearch 上进行语义搜索,因此需要将此文本转换为嵌入/向量。

with tf.compat.v1.Session() as session:
session.run([tf.compat.v1.global_variables_initializer(), tf.compat.v1.tables_initializer()])
query_array = session.run(model([query])).tolist()[0]

将文本转换为嵌入/向量后,就根据 Elasticsearch 中的现有数据搜索此文本。为此,我们首先必须构建一个查询从 Elasticsearch 获取数据。

query_for_search = {
"knn": {
"field": "Embeddings",
"query_vector": query_array,
"k": 5,
"num_candidates": 2414
},
"_source": [ "Text"]
}

我们通过上述代码在 Elasticsearch 进行查询。在我们查看下一步之前,先看看这个查询:

  • "knn": Elasticsearch 支持 K-Nearest Neighbors 又名 kNN 算法,并且已经在 Elasticsearch 中可用。您不需要单独训练它。
  • "field": 嵌入/向量存储在 Elasticsearch 中的字段名。
  • "query_vector": 输入的向量/嵌入形式 "k": 需要获取的结果数
  • "num_candidates": 以令牌为单位的输出/搜索结果的长度。
  • "_source": 必须从中提供输出/搜索结果的字段名。

开始搜索:

result = es.search(
index="my_new_index",
body=query_for_search)
result0["hits"]

借助上述查询,您将能够从之前存储数据的索引中获取搜索结果。

请记住,您只能对具有配置字段的索引执行语义搜索,包含嵌入/向量的字段类型必须为“dense_vector”,并且查询/问题和存储在 Elasticsearch 中的数据的向量维度必须完全相同。例如,在上面的教程中,我们在 Elasticsearch 中的数据处于“512”维度中,并且在我们继续搜索操作之前,查询/问题也转换为“512”维度。

结论

语义搜索是一种强大的工具,可以通过理解单词的含义和上下文来大大提高搜索结果的准确性和相关性。Elasticsearch 是一个高度可扩展且灵活的搜索引擎,可用于实现从电子商务到医疗保健的各种应用程序的语义搜索。通过利用 Elasticsearch 强大的搜索和索引功能,以及查询扩展、同义词检测和实体识别等技术,您可以构建一个语义搜索系统,提供快速准确的结果。无论您是开发人员、数据科学家还是企业主,使用 Elasticsearch 掌握语义搜索都可以帮助您从数据中发掘新的见解和机会。

本文的完整代码请看 https://github.com/Pritam868/Semantic-Search-ElasticSearch

本文译自 https://medium.com/@pablopaul1999/semantic-search-using-transformers-and-elasticsearch-6e968fdd85d5

使用 OpenAI 的 Embeddings 接口实现文本和代码的语义搜索

· 9 min read

本文主要介绍 OpenAI 的 Embeddings (嵌入) 接口,该接口可以轻松执行自然语言和代码任务,如语义搜索、聚类、主题建模和分类。

Embeddings 是转换为数字序列的概念的数字表示,使计算机可以轻松理解这些概念之间的关系。Embeddings 在 3 个标准基准测试中优于顶级模型,其中代码搜索的改进相对提升了 20%。

Embeddings 对于处理自然语言和代码非常有用,因为它们可以很容易地被其他机器学习模型和算法(如聚类或搜索)使用和比较。

Vector

在 Embeddings 中,两组相似的数值在语义上也是相似的。例如,“犬类同伴说”的嵌入向量将更类似于“woof”的嵌入向量,而不是“meow”的嵌入向量。

新的接口使用神经网络模型(GPT-3 的后代)将文本和代码映射到矢量数据上 - 将它们“嵌入”在高维空间中。每个维度捕获输入的某些方面。

OpenAI API 中的新 /embeddings 端点提供包含几行代码的文本和代码嵌入:

import openai
response = openai.Embedding.create(
input="canine companions say",
engine="text-similarity-davinci-001")

OpenAI 发布了三个系列的嵌入模型,每个系列都经过调整以在不同的功能上表现良好,包括:文本相似性、文本搜索和代码搜索。这些模型将文本或代码作为输入,并返回嵌入向量。

模型使用场景
文本相似度: 捕获文本片段之间的语义相似性.text-similarity-{ada, babbage, curie, davinci}-001聚类、回归、异常检测、可视化
文本搜索:文档的语义信息检索。text-search-{ada, babbage, curie, davinci}-{query, doc}-001搜索, 上下文相关性, 信息检索
代码搜索:使用自然语言查询查找相关代码。code-search-{ada, babbage}-{code, text}-001代码搜索和相关性

文本相似性模型

文本相似性模型提供用于捕获文本片段的语义相似性。这些模型可用于许多任务,包括聚类分析、数据可视化和分类。

以下交互式可视化显示了来自 DBpedia 数据集的文本示例的嵌入:

clusting

来自 text-similarity-babbage-001 模型,使用的是 DBpedia 数据集。我们从涵盖 5 个类别的数据集中随机选择了 100 个样本,并通过 /embeddings 端点计算嵌入。不同的类别在嵌入空间中显示为 5 个清晰的簇。为了可视化嵌入空间,我们使用 PCA 将嵌入维度从 2048 减少到 3。有关如何在 3D 维度中可视化嵌入空间的代码,请参阅此处

要比较两段文本的相似性,您只需在文本嵌入上使用点积即可。结果是 –1 和 1 之间的“相似性得分”,有时称为“余弦相似性”,其中数字越大意味着相似性越大。在大多数应用中,可以预先计算嵌入,然后非常快速地进行点积比较。

import openai, numpy as np

resp = openai.Embedding.create(
input=["feline friends say", "meow"],
engine="text-similarity-davinci-001")

embedding_a = resp['data'][0]['embedding']
embedding_b = resp['data'][1]['embedding']

similarity_score = np.dot(embedding_a, embedding_b)

Embeddings 的一个常见用途是将它们用作机器学习任务(如分类)中的特征。在机器学习文献中,当使用线性分类器时,这种分类任务称为“线性探针”。我们的文本相似性模型在 SentEval 中实现了线性探针分类的最新结果(Conneau et al.,2018),这是评估嵌入质量的常用基准。

7 个数据集的线性探针分类

linear-probe

文本搜索模型

文本搜索模型提供嵌入,支持大规模搜索任务,例如在给定文本查询的文档集合中查找相关文档。文档和查询的嵌入分别生成,然后使用余弦相似性来比较查询与每个文档之间的相似性。

基于嵌入的搜索可以比经典关键字搜索中使用的单词重叠技术更好地概括,因为它捕获文本的语义含义,并且对确切的短语或单词不太敏感。我们评估了文本搜索模型在 BEIR (Thakur, et al. 2021)搜索评估套件上的性能,并获得了比以前方法更好的搜索性能。我们的文本搜索指南提供了有关将嵌入用于搜索任务的更多详细信息。

BEIR 中 11 个搜索任务的平均准确率

BEIR

代码搜索模型

代码搜索模型为代码搜索任务提供代码和文本嵌入。给定一组代码块,任务是查找自然语言查询的相关代码块。我们在 CodeSearchNetHusian et al., 2019)评估套件上评估代码搜索模型,其中我们的嵌入比以前的方法获得了明显更好的结果。可以通过 代码搜索指南 来了解使用嵌入进行代码搜索的方法。

超过 6 种编程语言的平均准确度

code search

嵌入 API 的实际应用示例

JetBrains Research

JetBrains Research 的 Astroparticle Physics Lab 分析了 The Astronomer’s Telegram 和 NASA 的 GCN Circulars 等数据,这些报告包含传统算法无法解析的天文事件。

在 OpenAI 嵌入这些天文报告的支持下,研究人员现在能够在多个数据库和出版物中搜索诸如“蟹状脉冲星爆发”之类的事件。 嵌入还通过 k 均值聚类在数据源分类上实现了 99.85% 的准确率。

FineTune Learning

FineTune Learning 是一家为学习构建人机混合解决方案的公司,例如帮助学生达到学术标准的自适应学习循环

OpenAI 的嵌入显着改进了根据学习目标查找教科书内容的任务。 OpenAI 的文本搜索居里嵌入模型达到了 89.1% 的前 5 准确率,优于之前的方法,如 Sentence-BERT (64.5%)。 虽然人类专家仍然更好,但 FineTune 团队现在能够在几秒钟内标记整本教科书,而专家则需要数小时。

FineTune

Fabius

Fabius 帮助公司将客户对话转化为结构化的内容,为规划和优先级排序提供信息。 OpenAI 的嵌入允许公司更轻松地查找和标记具有功能请求的客户通话记录。

例如,客户可能会使用“自动化”或“易于使用”等词来要求更好的自助服务平台。 此前,Fabius 曾使用模糊关键字搜索来尝试为那些带有自助服务平台标签的成绩单打上标签。 借助 OpenAI 的嵌入,他们现在能够找到 2 倍以上的一般示例,以及 6 到 10 倍的具有抽象用例的特征示例,这些用例没有客户可能使用的明确关键字。

所有 API 客户都可以开始使用嵌入文档,以便在他们的应用程序中使用嵌入。

本文译自 https://openai.com/blog/introducing-text-and-code-embeddings

Indexea 正式上线啦

· 3 min read

欢迎来到 Indexea 的博客频道!在这里,我们将会分享 Indexea 的发展历程、技术实践、行业动态和客户案例等内容。我们希望通过这个博客频道,让大家更好地了解 Indexea,并与广大用户建立更紧密的联系和沟通。

Indexea

Indexea 是一款全文检索和搜索引擎产品,它能够快速、准确地搜索和检索各种文档、文件、图片、音视频等内容,为用户提供高效、便捷的搜索体验。我们的团队致力于为客户提供高质量、高性能的搜索服务,帮助客户在信息化时代获取更多的商业价值。

Indexea 的发展历程可以追溯到几年前。当时,我们团队对当前市场上的搜索引擎产品进行了全面的调研和分析,发现市场上缺乏一款同时具备高性能、易用性和可扩展性的搜索引擎产品。于是,我们决定开发一款全新的搜索引擎产品,以满足市场的需求。

经过多年的技术研发和市场验证,我们的团队成功地开发出了 Indexea,这是一款全面升级的全文检索和搜索引擎产品。Indexea 支持多种数据类型和数据源,具有快速、准确、可扩展等优点,已经成功地服务于各种企业和组织。

在未来,我们将继续致力于 Indexea 的研发和推广,为客户提供更好的搜索服务和技术支持。同时,我们也将不断开拓创新,推出更多优秀的产品和解决方案,为客户创造更多的商业价值。

感谢大家的支持和关注!我们将会不断更新博客内容,分享 Indexea 的最新动态和技术成果。同时,我们也欢迎大家提出宝贵的意见和建议,共同推动 Indexea 的发展。