混合搜索与多重嵌入:一次有趣又毛茸茸的猫咪搜索之旅!(一)

作者:来自 Elastic  Jo Ann de Leon

演示如何在多个嵌入(文本和图像)上实现不同类型的搜索 - 词汇、向量和混合。它使用一个简单而有趣的猫搜索应用程序。

你知道 Elastic 可以用作强大的向量数据库吗?在本博客中,我们将探讨如何在传统词汇搜索的同时生成、存储和查询向量嵌入。Elastic 的优势在于其灵活性和可扩展性,使其成为现代搜索用例的绝佳选择。通过将向量嵌入与 Elastic 集成,你可以提高搜索相关性,并增强各种数据类型(包括图像等非文本文档)的搜索能力。

但它会变得更好!学习 Elastic 的搜索功能也很有趣。在本文中,我们将向你展示如何使用 Elastic 搜索你最喜欢的猫,以搜索猫的文本描述和图像。通过本文附带的一个简单的 Python 应用程序,你将学习如何实现基于向量和关键字的搜索。我们将指导你生成自己的向量嵌入,将它们存储在 Elastic 中并运行混合查询 - 所有这些都是在搜索可爱的猫科动物朋友的同时进行的。

无论你是经验丰富的开发人员还是 Elasticsearch 新手,这个有趣的项目都是了解现代搜索技术如何运作的好方法。此外,如果你喜欢猫,你会发现它更具吸引力。所以让我们深入研究并设置 Elasticats 应用程序,同时探索 Elasticsearch 的强大功能。

在开始之前,让我们确保你已准备好 Elastic 云 ID 和 API 密钥。复制 .env-template 文件,将其另存为 .env 并插入你的 Elastic 云凭据。

应用程序架构

以下是描述我们应用程序架构的高层次图表:

生成和存储向量嵌入

在执行任何类型的搜索之前,我们首先需要有数据。我们的 data.json 包含我们将在 Elasticsearch 中索引的猫文档列表。每个文档描述一只猫并具有以下映射:

mappings= {
      	"properties": {
            	"img_embedding": {
                       "type": "dense_vector",
                       "dims": 512,
                       "index": True,
                       "similarity": "cosine"
                 },
                 "photo": {
                       "type": "keyword"
                 },
                 "cat_id": {
                       "type": "keyword"
                 },
                 "name": {
                       "type" : "text"
                 },
                 "url" : {
                       "type" : "keyword"
                 },
                 "summary" : {
                       "type" : "text"
                 },
                 "summary_embedding": {
                       "type": "dense_vector",
                       "dims": 384
                 },
                 "age": {
                       "type": "keyword"
                 },
                 "gender": {
                       "type": "keyword"
                 },
                 "size": {
                       "type": "keyword"
                 },
                 "coat": {
                       "type": "keyword"
                 },
                 "breed": {
                       "type": "keyword"
                 }
       }
}

每只猫的照片属性都指向猫图像的位置。当我们在应用程序中调用 reindex 函数时,它将生成两个嵌入:

1) 首先是每只猫图像的向量嵌入。我们使用 clip-ViT-B-32 模型。图像模型允许你将图像和文本嵌入到同一个向量空间中。这允许你将图像搜索实现为文本到图像或图像到图像的搜索。

       self.img_model = SentenceTransformer('clip-ViT-B-32')
   def get_img_embedding(self, text='', image_path=''):
       if text:
           print(f'Encoding text: {text}')
           return self.img_model.encode(text)
       else:
           print(f'Encoding image: {image_path}')
           temp_image = Image.open(image_path)
           return self.img_model.encode(temp_image)

2) 第二个嵌入是针对每只待领养猫的摘要文本。我们使用了不同的模型,即 all-MiniLM-L6-v2。

       self.text_model = SentenceTransformer('all-MiniLM-L6-v2')
   def get_text_embedding(self, text):
       return self.text_model.encode(text)

然后我们将嵌入存储为文档的一部分。

   def insert_documents(self, documents):
       operations = []
       for document in documents:
           operations.append({'index': {'_index': self.index}})
           operations.append({
               **document,
               'img_embedding': self.get_img_embedding(image_path="static/"+document['photo']),
               'summary_embedding': self.get_text_embedding(document['summary'])
           })
       return self.es.bulk(operations=operations)

我们现在准备调用 reindex 函数。

@app.cli.command()
def reindex():
   """Regenerate the Elasticsearch index."""
   response = es.reindex()
   print (response)
   print(f'Index with {len(response["items"])} documents created '
         f'in {response["took"]} milliseconds.')
   def reindex(self):
       self.create_index()
       with open('data.json', 'rt') as f:
           documents = json.loads(f.read())
       return self.insert_documents(documents)

从终端运行以下命令:

(.venv) $> flask reindex

现在我们可以运行我们的 Web 应用程序了:

(.venv) $> flask run

我们的初始表单如下所示:

如你所见,我们已经将一些关键词作为过滤器(例如年龄、性别、尺寸等)公开,我们将在查询中使用它们。

执行不同类型的搜索

以下工作流程图显示了我们的 Web 应用程序中可用的不同搜索路径。我们将介绍每种情况。

词汇搜索

最简单的情况是 “match all” 查询,基本上会返回索引中的所有猫。我们不使用任何过滤器,也不输入描述或上传图片。

       search_query = {
           'must': {
               'match_all': {}
           }
       }

如果表单中提供了任何过滤器,我们将执行布尔查询。在这种情况下,没有输入任何描述,因此我们将在 “match all” 查询中应用过滤器。

def extract_filters(form_data):
   filters = []


   for key, val in form_data.items():
       if (key == "imageQuery" or key == "inputQuery" or key == "from_"):
           continue


       if (key != "age" and key != "breed"):
           if (val[0] != ''): #only apply the filter if value is not empty
               filters.append({
                   "term": {
                       f"{key}": {
                           "value": val[0]
                       }
                   },
               })
       else:
           #remove any empty values first
           cleaned_list = [item for item in val if item]


           if (len(cleaned_list) > 0): #only apply the filter if list is not empty
               filters.append({
                   "terms": {
                       f"{key}": cleaned_list
                   },
               })


   return {'filter': filters}
   filters = extract_filters(form_data)
   if search_query:
       search_params['query'] = {
           'bool': {
               **search_query,
               **filters
           }
       }

向量搜索

在我们的 Web 表单中,我们可以上传猫的类似图像。通过上传图像,我们可以进行向量搜索,方法是将上传的图像转换为嵌入,然后对之前存储的图像嵌入执行 knn 搜索。

首先,我们将上传的图像保存在 uploads 文件夹中。

   if 'imageQuery' in request.files:
       file = request.files['imageQuery']


       if file:
           filename = file.filename
           filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
           # Process the image as needed
           file.save(filepath)
           imageSearch = True;

然后我们为图像嵌入创建一个 knn 查询。

   elif imageSearch:
       search_query = None
       # add knn image if there's image
       knn_query.append({
           'field': 'img_embedding',
           'query_vector': es.get_img_embedding(image_path=filepath),
           'k': 5,
           'num_candidates': 15,
           **filters,
       })
   search_params = {
       'knn': knn_query,
       'from_': from_,
       'size': 5
   }

请注意,可以使用或不使用过滤器(来自布尔查询)来执行向量搜索。另外,请注意 k=5,这意味着我们只返回前 5 个相似文档(猫)。

尝试存储在 images/<breed> 文件夹中的以下任何图像:

  1. Abyssinian
    1. Dahlia - 72245105_3.jpg
  2. American shorthair
    1. Uni - 64635658_2.jpg
    2. Sugarplum - 72157682_4.jpeg
  3. Persian
    1. Sugar - 72528240_2.jpeg

混合搜索

我们的应用程序中最复杂的场景是当一些文本输入到描述字段时。在这里,我们执行 3 种不同类型的搜索并将它们组合成混合搜索。首先,我们对实际的文本输入执行词汇 “match” 查询。

   # add text search
   if textQuery:
       search_query = {
           'must': {
               'match': {
                   'summary': textQuery
               }
           }
       }

我们还创建了 2 个 knn 查询:

  1. 使用文本嵌入模型,我们为文本输入生成一个嵌入,并对摘要嵌入执行 knn 搜索。
  2. 使用图像嵌入模型,我们为文本输入生成另一个嵌入,并对图像嵌入执行 knn 搜索。我之前提到过,图像模型不仅允许你执行我们在上面的向量搜索场景中看到的图像到图像搜索,还允许你执行文本到图像搜索。这意味着如果我在描述中输入“black”,它将搜索可能包含或类似于黑猫的图像!
       # add knn text and image search if there's a description
       knn_query.append({
           'field': 'summary_embedding',
           'query_vector': es.get_text_embedding(textQuery),
           'k': 5,
           'num_candidates': 15,
           **filters,
       })


       knn_query.append({
           'field': 'img_embedding',
           'query_vector': es.get_img_embedding(textQuery),
           'k': 5,
           'num_candidates': 15,
           **filters,
       })

然后,我们利用倒数秩融合 (RRF) 检索器有效地将所有三个查询的结果组合并排序为单个有凝聚力的结果集。

   rank = None
   if len(knn_query) > 0 and search_query:
       rank = {
           'rrf': {}
       }
   # Conditionally add the 'rank' parameter
   if rank:
       search_params['rank'] = rank

RRF 是一种将多个结果集(每个结果集可能具有不同的相关性指标)合并为一个统一集的方法。与简单地连接结果数组不同,RRF 应用特定公式根据文档在各个结果集中的位置对其进行排名。这种方法可确保出现在多个查询中的文档具有更高的重要性,从而提高最终结果的相关性和质量。通过使用 RRF,我们避免了手动调整每个查询权重的复杂性,并实现了不同搜索策略的平衡整合。

更多有关 RRF 的描述,请阅读如下的文章:

  • Elasticsearch:倒数排序融合 - Reciprocal rank fusion (RRF)

  • Elasticsearch:倒数排序融合 - Reciprocal rank fusion - 8.14

为了进一步说明,下表显示了我们搜索 “sisters” 时各个结果集的排名。使用 RRF 公式(默认排名常数 k=60),我们可以得出每个文档的最终分数。然后按降序对最终分数进行排序,即可得出文档的最终排名。“Willow & Nova” 是我们的最佳匹配项(cat)!

Cat (document)Lexical rankingknn (on img_embedding) rankingknn (on summary_embedding) rankingFinal ScoreFinal Ranking
Sugarplum130.03226645852
Willow & Nova2110.04891591751
Zoe & Zara20.016129032264
Sage320.032002048133
Primrose40.0156255
Dahlia50.015384615387
Luke & Leia40.0156256
Sugar & Garth50.015384615388

以下是你可以用于描述的其他一些测试:

  1. “sisters” vs “siblings”
  2. “tuxedo”
  3. “black cats” with “American shorthair” breed filter
  4. “white”

结论

除了显而易见的 **cats** 之外,Elasticats 是了解 Elasticsearch 的绝佳方式。这是一个有趣且实用的项目,可让你探索搜索技术,同时提醒我们技术可以带来的乐趣。随着你深入研究,你还会发现 Elasticsearch 处理向量嵌入的能力如何解锁新级别的搜索功能。无论是针对猫、图像还是其他数据类型,Elastic 都能让搜索既强大又令人愉快!

欢迎为项目做出贡献或分叉存储库以进一步定制它。祝你搜索愉快,希​​望你能找到梦寐以求的猫!😸

准备好自己尝试一下了吗?开始免费试用或使用此自定进度的动手学习来学习 Search AI。

Elasticsearch 集成了 LangChain、Cohere 等工具。加入我们的高级语义搜索网络研讨会,构建你的下一个 GenAI 应用程序!

原文:Hybrid search with multiple embeddings: A fun and furry search for cats! - Search Labs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/905761.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二:Linux学习笔记(第一阶段)-- Linux命令

目录 Linux注意事项&#xff1a; Linux目录 Linux系统基础命令 1. 文件和目录操作 2. 文件查看和编辑 3. 文件权限和所有权 4. 系统信息 5. 网络命令 6. 文件查找 7. 压缩和解压缩 8. 系统管理 Linux注意事项&#xff1a; 严格区分大小写一切皆文件windows下的程序不…

Java设计模式之代理模式(一)

什么是代理&#xff1f;可以理解为其他对象提供一种代理以控制对这个对象的访问。 举个例子&#xff0c;生活中的外卖平台&#xff0c;店铺是制作外卖的&#xff0c;然后放到平台上售卖。这里的店铺就是真实角色&#xff0c;为了能够让店铺不用担心销售等问题&#xff0c;从而…

WebSocket 连接频繁断开的问题及解决方案

文章目录 WebSocket 连接频繁断开的问题及解决方案1. 引言2. 什么是 WebSocket&#xff1f;2.1 WebSocket 的优势2.2 WebSocket 的工作原理 3. WebSocket 连接频繁断开的常见原因3.1 服务器端问题3.1.1 服务器负载过高3.1.2 服务器配置不当3.1.3 超时设置 3.2 网络问题3.2.1 网…

字符串逆序(c语言)

错误代码 #include<stdio.h>//字符串逆序 void reverse(char arr[], int n) {int j 0;//采用中间值法//访问数组中第一个元素和最后一个元素//交换他们的值&#xff0c;从而完成了字符串逆序//所以这个需要临时变量for (j 0; j < n / 2; j){char temp arr[j];arr[…

elcipse工具使用记录

安装 创建项目并运行Helloword 没有显示console? Window–>Show View–>Console 快捷键的积累 代码提示功能 windows->prference->java->Content Assist, 修改Auto…,内容为.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.

头歌——数据库系统原理(数据的简单查询)

文章目录 第1关&#xff1a;基本 SELECT 查询代码 第2关&#xff1a;带限制条件的查询和表达式查询代码 第3关&#xff1a;使用 WHERE 语句进行检索代码 第1关&#xff1a;基本 SELECT 查询 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 如何获取数据表中指…

关于我、重生到500年前凭借C语言改变世界科技vlog.13——深入理解指针(3)

文章目录 1.字符指针变量2.数组指针变量3.函数指针变量4.函数指针数组5.二维数组传参本质6.拓展补充希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力&#xff01; 本章节接着学习常见的指针变量类型 1.字符指针变量 字符指针变量&#xff0c;顾名思义就是字…

贪心算法入门(一)

1.什么是贪心算法&#xff1f; 贪心算法是一种解决问题的策略&#xff0c;它将复杂的问题分解为若干个步骤&#xff0c;并在每一步都选择当前最优的解决方案&#xff0c;最终希望能得到全局最优解。这种策略的核心在于“最优”二字&#xff0c;意味着我们追求的是以最少的时间和…

MacBook 如何设置打开json格式文件的默认程序是vs code

首先右键选中文件&#xff0c;然后选中显示简介 然后选中打开方式 设置成vs code

宝塔使用clickhouse踩坑

前言 最近有个物联网项目,需要存储物联网终端发送过来的信息(类似log日志,但又要存储在数据库里,方便后期聚合统计),本来想写文件的奈何客户要求聚合统计,所以只能用数据库才能达到更高的计算效率,当然mysql对这种日志型数据库并没有优势,数据量上去后反而不利于计算…

ML 系列:第 18 部 - 高级概率论:条件概率、随机变量和概率分布

文章目录 一、说明二、关于条件概率2.1 为什么我们说条件概率&#xff1f;2.2 为什么条件概率在统计学中很重要 三、 随机变量的定义3.1 定义3.2 条件概率中的随机变量 四、概率分布的定义五、结论 一、说明 条件概率是极其重要的概率概念&#xff0c;它是因果关系的数学表述&…

十个常见的软件测试面试题,拿走不谢

所有面试问题一般建议先总后分的方式来回答&#xff0c;这样可以让面试官感觉逻辑性很强。 1. 自我介绍 之所以让我们自我介绍&#xff0c;其实是面试官想找一些时间来看简历&#xff0c;所以自我介绍不用太长的时间&#xff0c;1-2分 钟即可。 自我介绍一般按以下方式进行介…

软考高级中哪个好考?软考5个高级资格详细分析!

计算机软件资格考试是由国家人力资源和社会保障部、工业和信息化部领导下的国家级考试&#xff0c;这个考试既是职业资格考试&#xff0c;又是职称资格考试。 软考专业资格层次对应表 计算机软件资格考试设置了27个专业资格&#xff0c;涵盖5个专业领域&#xff0c;3个级别层次…

vi —— 终端中的编辑器

目标 vi 简介打开和新建文件三种工作模式常用命令分屏命令常用命令速查图 01. vi 简介 1.1 学习 vi 的目的 在工作中&#xff0c;要对 服务器 上的文件进行 简单 的修改&#xff0c;可以使用 ssh 远程登录到服务器上&#xff0c;并且使用 vi 进行快速的编辑即可常见需要修改…

sklearn|机器学习:决策树(一)

文章目录 sklearn&#xff5c;机器学习&#xff1a;决策树&#xff08;一&#xff09;&#xff08;一&#xff09;概述&#xff08;二&#xff09;实战1. 环境配置2. sklearn 中的决策树&#xff08;1&#xff09;模块 sklearn.tree&#xff08;2&#xff09;sklearn 基本建模流…

React基础语法

1.React介绍 React由Meta公司开发&#xff0c;是一个用于构建Web和原生交互界面的库 1.1 React优势 相较于传统基于DOM开发的优势 1.组件化的开发方式 2.不错的性能 相较于其他前端框架的优势 1.丰富的生态 2.跨平台支持 1.2React的时长情况 全球最流行&#xff0c;大厂…

docker安装、设置非sudo执行、卸载

安装 sudo snap install docker 设置docker非sudo执行 sudo groupadd docker sudo usermod -aG docker $USER newgrp docker sudo chown root:docker /var/run/docker.sock 卸载docker 1.删除docker及安装时自动安装的所有包 apt-get autoremove docker docker-ce docker-…

golang的RSA加密解密

参考&#xff1a;https://blog.csdn.net/lady_killer9/article/details/118026802 1.加密解密工具类PasswordUtil.go package utilimport ("crypto/rand""crypto/rsa""crypto/x509""encoding/pem""fmt""log"&qu…

SpringSecurity框架(入门)

简介&#xff1a; Spring Security 是一个用于构建安全的 Java 应用程序的框架&#xff0c;尤其适用于基于Spring的应用程序。它提供了全面的安全控制&#xff0c;从认证&#xff08;Authentication&#xff09;到授权&#xff08;Authorization&#xff09;&#xff0c;以及…

探索C嘎嘎:初步接触STL

#1024程序员节&#xff5c;征文# 前言&#xff1a; 在前文小编讲述了模版初阶&#xff0c;其实讲述模版就是为了给讲STL提前铺垫&#xff0c;STL是C中很重要的一部分&#xff0c;各位读者朋友要知道它的份量&#xff0c;下面废话不多说&#xff0c;开始走进STL的世界。 目录&am…