Elasticsearch 和 Go 中使用向量搜索寻找地鼠

作者:CARLY RICHMOND,LAURENT SAINT-FÉLIX

就像动物和编程语言一样,搜索也经历了不同实践的演变,很难在其中做出选择。 加入我们的第二部分,通过 Elasticsearch 中的矢量搜索在 Go 中狩猎地鼠(gophers)。

在 Elasticsearch 和 Go 中使用向量搜索进行 gopher 搜索

使用任何编程语言(包括 Go)构建软件都需要一生的学习。 在她的大学和工作生涯中,Carly 涉足了许多编程语言和技术,包括向量搜索的最新和最好的实现。 但这还不够! 最近 Carly 也开始玩 Go。

就像动物、编程语言和友好的作者一样,搜索也经历了不同实践的演变,你可能很难在自己的搜索用例中做出选择。 在本博客中,我们将分享向量搜索的概述以及使用 Elasticsearch 和 Elasticsearch Go 客户端的每种方法的示例。

先决条件

要遵循此示例,请确保满足以下先决条件:

  • 安装 Go 版本 1.13 或更高版本
  • 使用以下命令创建你自己的 Go 存储库
  • 创建您自己的 Elasticsearch 集群,其中填充了一组基于啮齿动物的页面,包括来自维基百科的我们友好的 Gopher:

连接到 Elasticsearch

在我们的示例中,我们将使用 Go 客户端提供的 Typed API。 为任何查询建立安全连接需要使用以下任一配置客户端:

  • 云 ID 和 API 密钥(如果使用 Elastic Cloud)。
  • 集群 URL、用户名、密码和证书。

连接到位于 Elastic Cloud 上的集群如下所示:

func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {
	var cloudID = os.Getenv("ELASTIC_CLOUD_ID")
	var apiKey = os.Getenv("ELASTIC_API_KEY")

	var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{
		CloudID: cloudID,
		APIKey:  apiKey,
		Logger:  &elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}

然后,客户端连接可用于向量搜索,如后续部分所示。

如果你是使用自己部署的 Elasticsearch 集群,你可以参考文章 “Elasticsearch:运用 Go 语言实现 Elasticsearch 搜索 - 8.x”。

向量搜索

向量搜索试图通过将搜索问题转换为使用向量的数学比较来解决这个问题。 文档嵌入过程还有一个额外的阶段,即使用模型将文档转换为密集向量表示或简单的数字流。 这种方法的优点是,你可以通过将非文本文档(例如图像和音频)与查询一起转换为向量来搜索它们。

简单来说,向量搜索是一组向量距离计算。 在下图中,我们的查询 “Go Gopher” 的向量表示与向量空间中的文档进行比较,并返回最接近的结果(由常数 k 表示):

根据为文档生成嵌入的方法,有两种不同的方法可以找出地鼠吃什么。

方法一:自带模型

凭借白金许可证,可以通过上传模型并使用推理 API 在 Elasticsearch 中生成嵌入。 建立模型涉及六个步骤:

1)选择要从模型存储库上传的 PyTorch 模型。 在此示例中,我们使用 Hugging Face 中的 Sentence-transformers/msmarco-MiniLM-L-12-v3 来生成嵌入。

2)使用适用于 Python 的 Eland 机器学习客户端,使用 Elasticsearch 集群的 credentials 和任务类型 text_embeddings 将模型加载到 Elastic 中:

eland_import_hub_model
--cloud-id $ELASTIC_CLOUD_ID \
--es-api-key $ELASTIC_API_KEY \
--hub-model-id sentence-transformers/msmarco-MiniLM-L-12-v3 \
--task-type text_embedding \ 
--start

如果你是自管型的 Elasticsearch 集群,请参考文章 “Elasticsearch:如何部署 NLP:文本嵌入和向量搜索” 来完成模型的上传。

3)上传后,使用示例文档快速测试 sentence-transformers__msmarco-minilm-l-12-v3,以确保按预期生成嵌入:

4)创建包含推理处理器的摄取管道。 这将允许使用上传的模型生成向量表示:

PUT _ingest/pipeline/search-rodents-vector-embedding-pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
        "target_field": "text_embedding",
        "field_map": {
          "body_content": "text_field"
        }
      }
    }
  ]
}

5)创建一个包含密集向量类型的字段 text_embedding.predicted_value 的新索引,以存储为每个文档生成的向量嵌入:

PUT vector-search-rodents
{
  "mappings": {
    "properties": {
      "text_embedding.predicted_value": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      },
      "text": {
        "type": "text"
      }
    }
  }
}

6)使用新创建的摄取管道重新索引文档,以生成文本嵌入作为每个文档上的附加字段 text_embedding.predicted_value :

POST _reindex
{
  "source": {
    "index": "search-rodents"
  },
  "dest": {
    "index": "vector-search-rodents",
    "pipeline": "search-rodents-vector-embedding-pipeline"
  }
}

现在,我们可以使用新索引 vector-search-rodents 在同一搜索 API 上使用 Knn 选项,如下例所示:

func VectorSearch(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnQuery{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             10,
      # Number of candidates to evaluate in comparison
			NumCandidates: 10,
      # Generate query vector using the same model used in the inference processor
			QueryVectorBuilder: &types.QueryVectorBuilder{
				TextEmbedding: &types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).Do(context.Background())

	if err != nil {
		return nil, fmt.Errorf("error in rodents vector search: %w", err)
	}

	return getRodents(res.Hits.Hits)
}

通过解组转换 JSON 结果对象的方式与关键字搜索示例完全相同。 常量 K 和 NumCandidates 允许我们配置要返回的邻居文档的数量以及每个分片要考虑的候选者的数量。 请注意,增加候选数量会提高结果的准确性,但随着执行的比较次数增多,会导致查询运行时间更长。

当使用查询 What do Gophers eat? 执行代码时,返回的结果类似于以下内容,突出显示 Gopher 文章包含所请求的信息,这与之前的关键字搜索不同:

[
  {ID:64f74ecd4acb3df024d91112 Title:Gopher - Wikipedia Url:https://en.wikipedia.org/wiki/Gopher} 
  {ID:64f74ed34acb3d71aed91fcd Title:Squirrel - Wikipedia Url:https://en.wikipedia.org/wiki/Squirrel} 
  //Other results omitted
]

方法二:Huggingface 推理 API

另一种选择是在 Elasticsearch 之外生成这些相同的嵌入,并将它们作为文档的一部分引入。 由于此选项不使用 Elasticsearch 机器学习节点,因此可以在免费层上完成。

Hugging Face 公开了一个免费使用、速率受限的推理 API,通过帐户和 API token,可以使用该 API 手动生成相同的嵌入以进行实验和原型设计,以帮助你入门。 不建议用于生产用途。 也可以使用类似的方法在本地调用你自己的模型来生成嵌入或使用付费 API。

在下面的 GetTextEmbeddingForQuery 函数中,我们针对查询字符串使用推理 API 来生成从 POST 请求返回到端点的向量:

// HuggingFace text embedding helper
func GetTextEmbeddingForQuery(term string) []float32 {
    // HTTP endpoint
    model := "sentence-transformers/msmarco-minilm-l-12-v3"
    posturl := fmt.Sprintf("https://api-inference.huggingface.co/pipeline/feature-extraction/%s", model)

    // JSON body
    body := []byte(fmt.Sprintf(`{
        "inputs": "%s",
        "options": {"wait_for_model":True}
    }`, term))

    // Create a HTTP post request
    r, err := http.NewRequest("POST", posturl, bytes.NewBuffer(body))

    if err != nil {
        log.Fatal(err)
        return nil
    }

    token := os.Getenv("HUGGING_FACE_TOKEN")
    r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

    client := &http.Client{}
    res, err := client.Do(r)
    if err != nil {
        panic(err)
    }

    defer res.Body.Close()

    var post []float32
    derr := json.NewDecoder(res.Body).Decode(&post)

    if derr != nil {
        log.Fatal(derr)
        return nil
    }

    return post
}

然后,[]float32 类型的结果向量作为 QueryVector 传递,而不是使用 QueryVectorBuilder 选项来利用之前上传到 Elastic 的模型。

func VectorSearchWithGeneratedQueryVector(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	vector, err := GetTextEmbeddingForQuery(term)
	if err != nil {
		return nil, err
	}

	if vector == nil {
		return nil, fmt.Errorf("unable to generate vector: %w", err)
	}

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnQuery{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             10,
      # Number of candidates to evaluate in comparison
			NumCandidates: 10,
      # Query vector returned from Hugging Face inference API
			QueryVector:   vector,
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}

请注意,无论这两个选项如何,K 和 NumCandidates 选项都保持相同,并且当我们使用相同的模型生成嵌入时会生成相同的结果。

结论

在这里,我们讨论了如何使用 Elasticsearch Go 客户端在 Elasticsearch 中执行向量搜索。 查看 GitHub 存储库以获取本系列中的所有代码。 继续阅读第 3 部分,了解如何将向量搜索与Go 中第 1 部分
中介绍的关键字搜索功能相结合。

到那时,快乐地鼠狩猎!

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

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

相关文章

Install Docker in Linux

Docker官网链接: https://docs.docker.com/ 1.确定Linux版本 新版本的Docker对Linux系统版本有一定的要求。如果Linux的发行版系统是centOS,安装最新版的docker需要centOS 7以上的系统。 在Docker安装帮助页面查看支持的系统版本。 Docker帮助页面:https://docs…

13 套接字Socket

1、Socket 编程 socket编程基于 TCP 和 UDP 协议的tcp和udp是区分客户端和服务端的,所以我们的socket编程也是区分的。 2、socket是端到端的通信 1.Socket 这个名字很有意思,可以作插口或者插槽讲 2.一头插在客户端,一头插在服务端&#x…

拉晶工艺设备——切片机

单晶炉拉出硅棒后,经硅棒切断、外园整形,便用切片机切成薄片,供后续工艺使用。切片机也用于玻璃、陶瓷、 大理石、花岗岩等硬脆材料的切割。 半导体行业使用的切片机按结构形式可分为立式切片机、卧式切片机,按刀片形式可分内圆切…

魔众文库系统 v5.5.0 批量快捷上传,文档图标优化,档转换逻辑优化

魔众文库系统基于文档系统知识,建立平台与领域,打造流量、用户、付费和变现的闭环,帮助您更好的搭建文库系统。 魔众文库系统发布v5.5.0版本,新功能和Bug修复累计14项,批量快捷上传,文档图标优化&#xff…

酷柚易汛ERP - 套餐管理操作指南

1、应用场景 套餐管理应用于商品打包销售,如临期食品,促销商品等 2、主要操作 2.1 新增套餐 打开【资料】-【套餐管理】点击新增 套餐是一个组合新商品出现,价格需要重新设定,也可以设定不同等级客户对应价格

SAP ABAP基础语法-Excel上传(十)

EXCEL BDS模板上传及赋值 上传模板事务代码:OAER l 功能代码:向EXCEL模板中写入数据示例代码如下 REPORT ZEXCEL_DOI. “doi type pools TYPE-POOLS: soi. *SAP Desktop Office Integration Interfaces DATA: container TYPE REF TO cl_gui_custom_c…

arduino 简易智能花盆

编辑器:arduino IDE 主板:arduino uno 传感器: 0.96寸的OLED屏(四脚) 声音模块 土壤温湿度模块 DS18B20温度模块(这里用到防水的) 光敏电阻模块(买成三脚的了只能显示高低&#x…

软件测试的终点是“测试开发”吗?

前言 在一线大厂,没有测试这个岗位,只有测开这个岗位,即使是做业务测试,那么你的title也是测开。 所以想聊一聊测开的看法,但不代表这是正确的看法,仅供参考。 没来阿里之前我对测开的看法 一直以为专职做自…

软件测试金融项目,在测试的时候一定要避开的一些雷区

软件测试金融项目需要格外谨慎和专注,因为这些项目通常涉及大量的交易、用户隐私和其他敏感信息。以下是一些软件测试金融项目时需要关注的方面: 1. 数据保护 在测试金融项目时,必须确保用户数据和投资信息得到保护。测试人员必须确保测试环…

我干了8年测试,告诉你现在软件测试还能不能找到工作!

观点:如果你还是以前的思维来学习测试,那你肯定是找不到工作! 我做测试工作有将近8年的时间,蚂蚁金服做过2年,因为加班太多离职了。目前在一家国企上市公司,一年能拿三四十个左右,对比头部互联…

信息系统“好用”的标准探讨

数字化转型建设的关键不在建设信息系统。这是为了避免走信息化建设的老路——业务和信息化两张皮,寄希望信息系统解决业务问题。在数字化转型建设中,信息系统仍然是重要抓手和显性成果,是企业业务和数据的承载平台,也是IT厂商向客…

李峋同款的C语言动态爱心代码来了,彩色闪动,附源码

1.前言 最近一部电视剧《点燃我,温暖你》可火啦,大家纷纷在搞男主阿瑟的同款爱心代码,但是虽然网上有它的源码,但都不是 C 语言的,看不懂。我看了一下这个视频,大致分析了一下它的运行过程。用语言描述一下…

【C++】C++11常用特性梳理

C11特性梳理 1. 列表初始化2. auto & decltype3. 右值引用3.1. 左右值引用比较3.2. 右值引用的意义3.3. 万能引用与完美转发3.4. 移动构造与移动赋值 4. default & delete5. 可变参数模板6. push_back 与 emplace_back7. lambda表达式7.1. 捕捉列表 8. function包装器8…

(一)QML加载离线地图+标记坐标点

1、实现效果 加载离线地图瓦片、鼠标拖拽、滚轮缩放在地图上固定坐标位置标注地名 (一)QML加载离线地图标记坐标点:MiniMap-mini 2、实现方法 2.1、使用工具下载离线地图 不废话,直接搬别人的砖,曰:他山…

VirtualBox网络地址转换(NAT),宿主机无法访问虚拟机的问题

问题:NAT模式下,默认只能从内访问外面,而不能从外部访问里面,所以只能单向ping通,虚拟机的ip只是内部ip。 PS:桥接则是与主机公用网卡,有独立的外部ip。 解决:NAT模式可以通过配置 …

好物周刊#30:Github 上大学

https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. Fighting Design 一款灵活、优质的组件库,可在 vue3 应用程…

【api_fox】ApiFox简单操作

1、get和post请求的区别?2、接口定义时的传参格式?3、保存接口文档 apifox当中接口文档的设计和接口用例的执行是分开的。 1、get和post请求的区别? 2、接口定义时的传参格式? 3、保存接口文档 就生成如下的接口文档。

C语言实现编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n

完整代码&#xff1a; /*编写一个函数&#xff0c;输入n为偶数时&#xff0c;调用函数求1/21/4...1/n,当输入n为奇数时&#xff0c; 调用函数1/11/3...1/n */ #include<stdio.h>//n为偶数 double Odd(int n){double sum0;//i为2&#xff0c;4&#xff0c;6....for (int …

linux_day02

1、链接&#xff1a;LN 一个点表示当前工作目录&#xff0c;两个点表示上一层工作目录&#xff1b; 目录的本质&#xff1a;文件&#xff08;该文件储存目录项&#xff0c;以链表的形式链接&#xff0c;每个结点都是目录项&#xff0c;创建文件相当于把目录项添加到链表中&…

在Spring Boot中使用JTA实现对多数据源的事务管理

了解事务的都知道&#xff0c;在我们日常开发中单单靠事务管理就可以解决绝大多数问题了&#xff0c;但是为啥还要提出JTA这个玩意呢&#xff0c;到底JTA是什么呢&#xff1f;他又是具体来解决啥问题的呢&#xff1f; JTA JTA&#xff08;Java Transaction API&#xff09;是…