基于火山引擎云搜索的混合搜索实战

news2024/11/15 11:58:01

在搜索应用中,传统的 Keyword Search 一直是主要的搜索方法,它适合精确匹配查询的场景,能够提供低延迟和良好的结果可解释性,但是 Keyword Search 并没有考虑上下文信息,可能产生不相关的结果。最近几年,基于向量检索技术的搜索增强技术 Semantic Search 越来越流行,通过使用机器学习模型将数据对象(文本、图像、音视频等)转化成向量,向量距离代表对象间的相似性,如果使用的模型和问题领域相关性高,则往往能更好地理解上下文和搜索意图,进而提高搜索结果的相关性,反之,如果模型和问题领域相关性不高,则效果会大打折扣。

Keyword Search 和 Semantic Search 都存在明显的优劣势,那么是否可以通过组合它们的优点来整体提高搜索的相关性?答案是,简单的算术组合并不能收到预期的效果,主要原因有两个:

  • 首先是不同类型查询的评分并不在同一个可比较的维度,因此不能直接进行简单的算术计算。

  • 其次是在分布式检索系统中,评分通常在分片级别,需要对所有分片的评分进行全局归一化处理。

综上,我们需要寻找一种理想的查询类型来解决这些问题,它能单独执行每个查询子句,同时收集分片级别的查询结果,最后对所有查询的评分进行归一化合并后返回最终的结果,这就是混合搜索(Hybrid Search) 方案。

通常一次混合搜索查询可以分为以下几步:

  1. 查询阶段:使用混合查询子句进行 Keyword Search 和 Semantic Search。

  2. 评分归一化和合并阶段,该阶段在查询阶段之后。

    1. 由于每种查询类型都会提供不同范围的评分,该阶段对每一个查询子句的评分结果执行归一化操作,支持的归一化方法有 min_max、l2、rrf。

    2. 对归一化后的评分进行组合,组合方法有 arithmetic_mean、geometric_mean、harmonic_mean。

  3. 根据组合后的评分对文档重新排序并返回给用户。

实现思路

从前面原理介绍,我们可以看到要实现一个混合检索应用,至少需要用到这些基础技术设施

  • 全文检索引擎

  • 向量检索引擎

  • 用于向量 Embedding 的机器学习模型

  • 将文本、音频、视频等数据转化成向量的数据管道

  • 融合排序

火山引擎云搜索构建在开源的 Elasticsearch 和 OpenSearch 项目上,从第一天上线就支持了完善成熟的文本检索和向量检索能力,同时针对混合搜索场景也进行了一系列的功能迭代和演进,提供了开箱即用的混合搜索解决方案。本文将以图像搜索应用为例,介绍如何借助火山引擎云搜索服务的解决方案快速开发一个混合搜索应用。

其端到端流程概括如下:

  1. 配置创建相关对象

    1. Ingestion Pipeline:支持自动调用模型把图片转换向量并存到索引中

    2. Search Pipeline:支持把文本查询语句自动转换成向量以便进行相似度计算

    3. k-NN索引:存放向量的索引

  2. 将图像数据集数据写入 OpenSearch 实例,OpenSearch 会自动调用机器学习模型将文本转为 Embedding 向量。

  3. Client 端发起混合搜索查询时,OpenSearch 调用机器学习模型将传入的查询转为 Embedding 向量。

  4. OpenSearch 执行混合搜索请求处理,组合 Keyword Seach 和 Semantic Seach 的评分,返回搜索结果。

方案实战

环境准备

1.登录火山引擎云搜索服务(https://console.volcengine.com/es),创建实例集群,版本选择 OpenSearch 2.9.0。

2.待实例创建完毕,启用 AI 节点。

3.在模型选择上,可以创建自己的模型,也可以选择公共模型。这里我们选择公共模型,完成配置后,点击立即启动

至此,准备好了 OpenSearch 实例和混合搜索所依赖的机器学习服务。

数据集准备

使用 Amazon Berkeley Objects Dataset(https://registry.opendata.aws/amazon-berkeley-objects/)作为数据集,数据集无需本地下载,直接通过代码逻辑上传到 OpenSearch,详见下面代码内容。

操作步骤

安装 Python 依赖

pip install -U elasticsearch7==7.10.1
pip install -U pandas
pip install -U jupyter
pip install -U requests
pip install -U s3fs
pip install -U alive_progress
pip install -U pillow
pip install -U ipython

连接到 OpenSearch

# Prepare opensearch info
from elasticsearch7 import Elasticsearch as CloudSearch
from ssl import create_default_context
# opensearch info
opensearch_domain = '{{ OPENSEARCH_DOMAIN }}'
opensearch_port = '9200'
opensearch_user = 'admin'
opensearch_pwd = '{{ OPENSEARCH_PWD }}'
# remote config for model server
model_remote_config = {
    "method": "POST",
    "url": "{{ REMOTE_MODEL_URL }}",
    "params": {},
    "headers": {
        "Content-Type": "application/json"
    },
    "advance_request_body": {
        "model": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
    }
}
# dimension for knn vector
knn_dimension = 384
# load cer and create ssl context
ssl_context = create_default_context(cafile='./ca.cer')
# create CloudSearch client
cloud_search_cli = CloudSearch([opensearch_domain, opensearch_port],
                               ssl_context=ssl_context,
                               scheme="https",
                               http_auth=(opensearch_user, opensearch_pwd)
                               )
# index name
index_name = 'index-test'

# pipeline id
pipeline_id = 'remote_text_embedding_test'
# search pipeline id
search_pipeline_id = 'rrf_search_pipeline_test'

1.填入 OpenSearch 链接地址和用户名密码信息。model_remote_config 是远程机器学习模型连接配置,可在模型调用信息查看,将调用信息中的 remote_config 配置全部复制到 model_remote_config

2.在实例信息->服务访问部分,下载证书到当前目录。

3.给定索引名称、Pipeline ID 和 Search Pipeline ID。

创建 Ingest Pipeline

创建 Ingest Pipeline,指定使用的机器学习模型,将指定字段转换为向量后嵌入回去。如下,将 caption 字段转为向量存储到 caption_embedding 中。

# Create ingest pipeline
pipeline_body = {
    "description": "text embedding pipeline for remote inference",
    "processors": [{
        "remote_text_embedding": {
            "remote_config": model_remote_config,
            "field_map": {
                "caption": "caption_embedding"
            }
        }
    }]
}
# create request
resp = cloud_search_cli.ingest.put_pipeline(id=pipeline_id, body=pipeline_body)
print(resp)

创建 Search Pipeline

创建查询需要使用的 Pipeline,配置好远程模型。

支持的归一化方法和加权求和方法:

  • 归一化方法:min_maxl2rrf

  • 加权求和方法:arithmetic_meangeometric_meanharmonic_mean

这里选择 rrf 归一化方法。

# Create search pipeline
import requests
search_pipeline_body = {
    "description": "post processor for hybrid search",
    "request_processors": [{
        "remote_embedding": {
            "remote_config": model_remote_config
        }
    }],
    "phase_results_processors": [  # normalization and combination
        {
            "normalization-processor": {
                "normalization": {
                    "technique": "rrf",  # the normalization technique in the processor is set to rrf
                    "parameters": {
                        "rank_constant": 60  # param
                    }
                },
                "combination": {
                    "technique": "arithmetic_mean",  # the combination technique is set to arithmetic mean
                    "parameters": {
                        "weights": [
                            0.4,
                            0.6
                        ]
                    }
                }
            }
        }
    ]
}
headers = {
    'Content-Type': 'application/json',
}
# create request
resp = requests.put(
    url="https://" + opensearch_domain + ':' + opensearch_port + '/_search/pipeline/' + search_pipeline_id,
    auth=(opensearch_user, opensearch_pwd),
    json=search_pipeline_body,
    headers=headers,
    verify='./ca.cer')
print(resp.text)

创建 k-NN 索引

  1. 将事先创建好的 Ingest Pipeline 配置到 index.default_pipeline 字段中;

  2. 同时,配置 properties,将 caption_embedding 设置为 knn_vector,这里使用 faiss 中的 hnsw。

# Create k-NN index
# create index and set settings, mappings, and properties as needed.
index_body = {
    "settings": {
        "index.knn": True,
        "number_of_shards": 1,
        "number_of_replicas": 0,
        "default_pipeline": pipeline_id  # ingest pipeline
    },
    "mappings": {
        "properties": {
            "image_url": {
                "type": "text"
            },
            "caption_embedding": {
                "type": "knn_vector",
                "dimension": knn_dimension,
                "method": {
                    "engine": "faiss",
                    "space_type": "l2",
                    "name": "hnsw",
                    "parameters": {}
                }
            },
            "caption": {
                "type": "text"
            }
        }
    }
}
# create index
resp = cloud_search_cli.indices.create(index=index_name, body=index_body)
print(resp)

加载数据集

读取数据集到内存中,并过滤出部分需要使用的数据。

# Prepare dataset
import pandas as pd
import string
appended_data = []
for character in string.digits[0:] + string.ascii_lowercase:
    if character == '1':
        break
    try:
        meta = pd.read_json("s3://amazon-berkeley-objects/listings/metadata/listings_" + character + ".json.gz",
                            lines=True)
    except FileNotFoundError:
        continue
    appended_data.append(meta)
appended_data_frame = pd.concat(appended_data)
appended_data_frame.shape
meta = appended_data_frame
def func_(x):
    us_texts = [item["value"] for item in x if item["language_tag"] == "en_US"]
    return us_texts[0] if us_texts else None
meta = meta.assign(item_name_in_en_us=meta.item_name.apply(func_))
meta = meta[~meta.item_name_in_en_us.isna()][["item_id", "item_name_in_en_us", "main_image_id"]]
print(f"#products with US English title: {len(meta)}")
meta.head()
image_meta = pd.read_csv("s3://amazon-berkeley-objects/images/metadata/images.csv.gz")
dataset = meta.merge(image_meta, left_on="main_image_id", right_on="image_id")
dataset.head()

上传数据集

上传数据集到 Opensearch,针对每条数据,传入 image_url 和 caption。无需传入 caption_embedding,将通过远程机器学习模型自动生成。

# Upload dataset
import json
from alive_progress import alive_bar
cnt = 0
batch = 0
action = json.dumps({"index": {"_index": index_name}})
body_ = ''

with alive_bar(len(dataset), force_tty=True) as bar:
    for index, row in (dataset.iterrows()):
        if row['path'] == '87/874f86c4.jpg':
            continue
        payload = {}
        payload['image_url'] = "https://amazon-berkeley-objects.s3.amazonaws.com/images/small/" + row['path']
        payload['caption'] = row['item_name_in_en_us']
        body_ = body_ + action + "\n" + json.dumps(payload) + "\n"
        cnt = cnt + 1

        if cnt == 100:
            resp = cloud_search_cli.bulk(
                request_timeout=1000,
                index=index_name,
                body=body_)
            cnt = 0
            batch = batch + 1
            body_ = ''

        bar()
print("Total Bulk batches completed: " + str(batch))

混合搜索查询

以查询 shoes 为例,查询中包含两个查询子句,一个是 match 查询,一个是 remote_neural 查询。查询时将事先创建好的 Search Pipeline 指定为查询参数,Search Pipeline 会将传入的文本转为向量,存储到 caption_embedding 字段,用于后续查询。

# Search with search pipeline
from urllib import request
from PIL import Image
import IPython.display as display
def search(text, size):
    resp = cloud_search_cli.search(
        index=index_name,
        body={
            "_source": ["image_url", "caption"],
            "query": {
                "hybrid": {
                    "queries": [
                        {
                            "match": {
                                "caption": {
                                    "query": text
                                }
                            }
                        },
                        {
                            "remote_neural": {
                                "caption_embedding": {
                                    "query_text": text,
                                    "k": size
                                }
                            }
                        }
                    ]
                }
            }
        },
        params={"search_pipeline": search_pipeline_id},
    )
    return resp
k = 10
ret = search('shoes', k)
for item in ret['hits']['hits']:
    display.display(Image.open(request.urlopen(item['_source']['image_url'])))
    print(item['_source']['caption'])

混合搜索演示

以上就是以图像搜索应用为例,介绍如何借助火山引擎云搜索服务的解决方案快速开发一个混合搜索应用的实战过程,欢迎大家登陆火山引擎控制台操作!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

淘系淘宝订单详情api接口(订单详情,订单列表,出售中,库存等属性)

淘系淘宝订单详情api接口(订单详情,订单列表,出售中,库存等属性)

免费泛域名证书申请

通配符证书是一种 SSL/TLS 证书,可用于保护多个域(主机),由域名字段中的通配符 (*) 指示。 如果您有很多需要保护的域或子域,这会很有帮助,因为它可以节省您的时间和金钱。 本文将讨论通配符证书、它们的工…

【Win】如何在Windows隐藏安装的程序

由于维护人员或用户可能无意中通过“程序和功能”选项删除对业务至关重要的软件,这导致服务中断或安全风险。为了防止此类情况发生,确保只有授权的用户才能访问和管理系统中的程序。为了实现这一目标,我们将探讨如何在Windows操作系统中隐藏特…

使用高防IP是应对网络安全的重要措施

使用高防IP(High Defense IP)在现代网络环境中显得尤为重要,这主要源于以下几个方面的原因: 一、网络安全形势严峻 随着互联网的快速发展,网络安全问题日益突出。各种网络攻击手段层出不穷,如分布式拒绝服…

R语言基础--文件读写

From生物技能树(R第五节) 文章目录 一、文件读写1.注意用project管理工作目录2、文件读取1、读取.txt文件2、读取.csv文件注意:数据框不允许重复的行名 3.数据框的导出4.读取文件的其他方式(用于读取/导出文件的R包)--经验1.base2.readr3.dat…

【会议征稿】2024年机器人前沿技术与创新国际会议(FTIR 2024, 7/19-21)

2024年机器人前沿技术与创新国际会议(FTIR 2024)将于2024年7月19-21日在中国杭州举行。FTIR 2024聚焦前沿技术与创新,将把机器人领域的创新学者和专家聚集到一个共同的论坛。会议的主要目标是促进机器人的研究和开发活动,另一个目…

使用单目相机前后帧特征点匹配进行3D深度估计的方法

在计算机视觉和机器人领域,三维空间感知是实现环境理解和交互的核心技术之一。特别是在资源受限的场合,使用针孔模型的单目相机进行深度估计成为了一种既经济又实用的解决方案。单目深度估计技术依赖于从连续视频帧中提取和匹配特征点,以估计…

企业大模型如何成为自己数据的“百科全书”?

作者 | 郭炜 编辑 | Debra Chen 在当今的商业环境中,大数据的管理和应用已经成为企业决策和运营的核心组成部分。然而,随着数据量的爆炸性增长,如何有效利用这些数据成为了一个普遍的挑战。 本文将探讨大数据架构、大模型的集成&#xff0…

线上网页点击菜单没有反应 报错ChunkLoadError:Loading chunk chunk-***** failed

现象 点击菜单无反应并且控制台报错Loading chunk chunk-***** failed 具体错误现象截图如下 分析 在线上页面已经打开的情况下,重新打包部署了前端项目。每次打包,js文件的hash值都会发生改变,因为我们的路由采用了懒加载,未…

吴恩达深度学习笔记:优化算法 (Optimization algorithms)2.7

目录 第二门课: 改善深层神经网络:超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第二周:优化算法 (Optimization algorithms)2.7 RMSprop 第二门课: 改善深层神经网络&am…

代码复现|Demucs Music Source Separation

一、背景介绍 Demucs是一个开源的音源分离项目。 Demucs在算法层面前后经历了三次大版本的进化,最原始的V1版本是:编解码LSTM。具体算法原理图如下所示。该版本在时域进行音源分离。关于阅读笔记请点击这篇文章。 V1版本原理图 V2版本是同时使用时域和频…

小白必看:新手学编程必会的100个代码

前言 我记得刚开始接触编程的时候,觉得太难了。 也很好奇,写代码的那些人也太厉害了吧?全是英文的,他们的英文水平一定很好吧? 他们是怎么记住这么多代码格式的?而且错了一个标点符号,整个程…

Flash attention入门

一、目录 flash attentionGPU运算流程flash attention 原理flash attention 与 standard attention 时间/内存 对比。flash attention 算法实现比较flash attention 计算、memory-efficient attention 等不同内核下用时 二、实现 flash attention 目的: 提高运行速…

NGM-SLAM:首创融合神经辐射场子图的3DGS-SLAM,问鼎SOTA!

论文标题: NGM-SLAM: Gaussian Splatting SLAM with Radiance Field Submap 论文作者: Mingrui Li, Jingwei Huang, Lei Sun Aaron, Xuxiang Tian, Tianchen Deng, Hongyu Wang 导读: 3DGS技术因其性能卓越而备受关注,3DGS-SLA…

GPT-4o 炸裂发布!你竟然还没用上?(附详细教程)

今天AI界的爆炸新闻非chatgpt-4o莫属,从早上到现在随处可见的文章推送,视频推送。 大家或多或少都有耳闻了,今天主要讲一讲我们普通人到底怎么用?如果不氪金行不行?我就想体验一下可不可以?带着问题往下看 …

Python 海龟画图(Turtle)命令大全

移动和绘制 forward() | fd() 使用语法: ​​turtle.forward(距离)​​ ​​turtle.fd(距离)​​ 参数说明: 距离 一个数字 (整数 或者 浮点) (注:单位是像素) 代码示例: import turtle turtle.forward(200) 效果: backward () | bk() | back() 使用语法: ​…

掏心经验分享,软考中项0基础入门篇!

想备考下半年中项(系统集成项目管理工程师)的朋友,不知道如何了解软考中项,今天给大家整理一篇关于我自己在备考软考时的一些考量和踩过的一些坑。(无广,放心看) 很多小伙伴总是听大家说软考中…

你是学会了还是学废了:Elasticsearch 7 集群拷贝到其它环境如何重置密码

欢迎您关注我的公众号【尚雷的驿站】 公众号:尚雷的驿站 CSDN :https://blog.csdn.net/shlei5580 墨天轮:https://www.modb.pro/u/2436 PGFans:https://www.pgfans.cn/user/home?userId4159 前言 本文描述了将生产ES集群打包拷贝…

线性模型之岭回归的用法

实战:使用岭回归模型 完整代码: import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split fro…

平芯微PW4056HH中文规格书

概述 PW4056HH 是一款完整的采用恒定电流/恒定电压的高压、大电流、单节锂离子电池线性充电 IC。充电电流可达 1A。输入 MAX 低工作电压 3.75V,降低充电功耗,提高效率。 PW4056HH 采用了内部 PMOS 架构,加上防反充电路,不需要外部…