Elasticsearch:使用 Elasticsearch 矢量搜索和 FastAPI 构建文本搜索应用程序

news2024/12/23 7:44:42

在我的文章 “Elastic:开发者上手指南” 的 “NLP - 自然语言处理及矢量搜索”,我对 Elastic Stack 所提供的矢量搜索有大量的描述。其中很多的方法需要使用到 huggingface.co 及 Elastic 的机器学习。这个对于许多的开发者来说,意味着付费使用。在那些方案里,带有机器学习的 inference processor 是收费的。还有那个上传的 eland 也是收费的。

在今天的文章中,我们来介绍另外一种方法来进行矢量搜素。我们绕过使用 eland 来进行上传模型。取而代之的是使用 Python 应用来上传我们已经生成好的 dense_vector 字段值。 我们将首先使用数据摄取脚本将数据摄取到 Elasticsearch 中。 该脚本将使用本地托管的 Elasticsearch 和 SentenceTransformer 库连接到 Elasticsearch 并执行文本嵌入。

在下面的展示中,我使用最新的 Elastic Stack 8.8.1 来进行展示,尽管它适用于其他版本的 Elastic Stack 8.x 版本。

ingest.py

from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer

USERNAME = "elastic"
PASSWORD = "z5nxTriCD4fi7jSS=GFM"
ELATICSEARCH_ENDPOINT = "https://localhost:9200"
CERT_FINGERPRINT = "783663875df7ae1daf3541ab293d8cd48c068b3dbc2d9dd6fa8a668289986ac2"

# Connect to Elasticsearch
es = Elasticsearch(ELATICSEARCH_ENDPOINT, 
                   ssl_assert_fingerprint = (CERT_FINGERPRINT),
                   basic_auth=(USERNAME, PASSWORD),
                   verify_certs = False)
resp = es.info()
print(resp)

# Index name
index_name = "test1"

# Example data
data = [
    {"id": 1, "text": "The sun slowly set behind the mountains, casting a golden glow across the landscape. The air was crisp and cool, a gentle breeze rustling through the leaves of the trees. Birds chirped in the distance, their melodic songs filling the air. As I walked along the winding path, I couldn't help but marvel at the beauty of nature surrounding me. The scent of wildflowers wafted through the air, intoxicating and refreshing. It was a moment of tranquility, a moment to escape from the chaos of everyday life and immerse myself in the serenity of the natural world."},
    {"id": 2, "text": "The bustling city streets were filled with the sound of car horns and chatter. People hurried past, their faces lost in a sea of anonymity. Skyscrapers towered above, their reflective glass windows shimmering in the sunlight. The aroma of street food filled the air, mingling with the scent of exhaust fumes. Neon signs flashed with vibrant colors, advertising the latest products and services. It was a city that never slept, a constant whirlwind of activity and excitement. Amidst the chaos, I navigated through the crowds, searching for moments of connection and inspiration."},
    {"id": 3, "text": "The waves crashed against the shore, each one a powerful force of nature. The sand beneath my feet shifted with every step, as if it was alive. Seagulls soared overhead, their calls echoing through the salty air. The ocean stretched out before me, its vastness both awe-inspiring and humbling. I closed my eyes and listened to the symphony of the sea, the rhythm of the waves lulling me into a state of tranquility. It was a place of solace, a place where the worries of the world melted away and all that remained was the beauty of the natural world."},
    {"id": 4, "text": "The old bookstore was a treasure trove of knowledge and stories. Rows upon rows of bookshelves lined the walls, each one filled with books of every genre and era. The scent of aged paper and ink filled the air, creating an atmosphere of nostalgia and adventure. As I perused the shelves, my fingers lightly grazing the spines of the books, I felt a sense of wonder and curiosity. Each book held the potential to transport me to another world, to introduce me to new ideas and perspectives. It was a sanctuary for the avid reader, a place where imagination flourished and stories came to life."}
]

# Create Elasticsearch index and mapping
if not es.indices.exists(index=index_name):
    es_index = {
        "mappings": {
            "properties": {
                "text": {"type": "text"},
                "embedding": {"type": "dense_vector", "dims": 768}
            }
        }
    }
    es.indices.create(index=index_name, body=es_index, ignore=[400])

# Upload documents to Elasticsearch with text embeddings
model = SentenceTransformer('quora-distilbert-multilingual')

for doc in data:
    # Calculate text embeddings using the SentenceTransformer model
    embedding = model.encode(doc["text"], show_progress_bar=False)

    # Create document with text and embedding
    document = {
        "text": doc["text"],
        "embedding": embedding.tolist()
    }

    # Index the document in Elasticsearch
    es.index(index=index_name, id=doc["id"], document=document)

为了运行上面的应用,我们需要安装 elasticsearch 及 sentence_transformers 包:

pip install sentence_transformers elasticsearch

如果我们对上面的 Python 连接到 Elasticsearch 还比较不清楚的话,请详细阅读我之前的文章 “Elasticsearch:关于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x”。

我们首先在数据摄取脚本中导入必要的库,包括 Elasticsearch 和 SentenceTransformer。 我们使用 Elasticsearch URL 建立与 Elasticsearch 的连接。 我们定义 index_name 变量来保存 Elasticsearch 索引的名称。

接下来,我们将示例数据定义为字典列表,其中每个字典代表一个具有 ID 和文本的文档。 这些文档模拟了我们想要搜索的数据。 你可以根据您的特定数据源和元数据提取要求自定义脚本。

我们检查 Elasticsearch 索引是否存在,如果不存在,则使用适当的映射创建它。 该映射定义了我们文档的字段类型,包括作为文本的文本字段和作为维度为 768 的密集向量的嵌入(embedding)字段。

我们使用 quora-distilbert-multilingual 预训练文本嵌入模型来初始化 SentenceTransformer 模型。 该模型可以将文本编码为长度为 768 的密集向量。

对于示例数据中的每个文档,我们使用 model.encode() 函数计算文本嵌入并将其存储在嵌入变量中。 我们使用文本和嵌入字段创建一个文档字典。 最后,我们使用 es.index() 函数在 Elasticsearch 中索引文档。

现在我们已经将数据提取到 Elasticsearch 中,让我们继续使用 FastAPI 创建搜索 API。

main.py

from fastapi import FastAPI
from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer

USERNAME = "elastic"
PASSWORD = "z5nxTriCD4fi7jSS=GFM"
ELATICSEARCH_ENDPOINT = "https://localhost:9200"
CERT_FINGERPRINT = "783663875df7ae1daf3541ab293d8cd48c068b3dbc2d9dd6fa8a668289986ac2"

# Connect to Elasticsearch
es = Elasticsearch(ELATICSEARCH_ENDPOINT, 
                   ssl_assert_fingerprint = (CERT_FINGERPRINT),
                   basic_auth=(USERNAME, PASSWORD),
                   verify_certs = False)

app = FastAPI()

@app.get("/search/")
async def search(query: str):
    print("query string is: ", query)
    model = SentenceTransformer('quora-distilbert-multilingual')
    embedding = model.encode(query, show_progress_bar=False)

    # Build the Elasticsearch script query
    script_query = {
        "script_score": {
            "query": {"match_all": {}},
            "script": {
                "source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0",
                "params": {"query_vector": embedding.tolist()}
            }
        }
    }

    # Execute the search query
    search_results = es.search(index="test1", body={"query": script_query})

    # Process and return the search results
    results = search_results["hits"]["hits"]
    return {"results": results}

@app.get("/")
async def root():
    return {"message": "Hello World"}

要运行 FastAPI 应用程序,请将代码保存在文件中(例如 main.py)并在终端中执行以下命令:

uvicorn main:app --reload
$ pwd
/Users/liuxg/python/fastapi_vector
$ uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['/Users/liuxg/python/fastapi_vector']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [95339] using WatchFiles
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/elasticsearch/_sync/client/__init__.py:395: SecurityWarning: Connecting to 'https://localhost:9200' using TLS with verify_certs=False is insecure
  _transport = transport_class(
INFO:     Started server process [95341]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:59811 - "GET / HTTP/1.1" 200 OK

这将启动 FastAPI 开发服务器。 然后,您可以访问 http://localhost:8000/search/ 的搜索端点并提供查询参数来执行搜索。 结果将作为 JSON 响应返回。

确保根据你的要求自定义代码,例如添加错误处理、身份验证和修改响应结构。我们做如下的搜索:

 

很显然,当我们搜索语句 “The sun slowly set behind the mountains” 的时候,第一个文档是最相近的。其它的文档没有那么相近,但是他们也会作为候选结果返回给用户。

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

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

相关文章

360手机 360手机刷机最高安卓版本参考

360手机 360手机刷机最高安卓版本参考 参考:360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机:360rom.github.io 【360手机(最高)安卓版本】 以下列举为常见360手机机型;其它早期系列,一般为Android4-6左右360手机UI界…

AutoHotKey面向对象编程

文章目录 **初步认识**类和继承枚举属性和For循环数组和映射 初步认识 在AHK中,对象就是一种内部封装了属性和方法的变量,而和常见的面向对象编程语言不同的是,这里面的属性和方法是可以动态添加的。下面举一个最简单的例子 person : {} …

vi/vim 如何在PowerShell里粘贴内容

vi/vim 如何在PowerShell里粘贴内容 Shift 鼠标右键 Vi/Vim 有两种主要的模式:命令模式和插入模式。 要进入命令模式,只需按下英文输入状态下的冒号(:)键。一旦进入命令模式,您可以在底部的命令行中输入各种命令。例…

【实战】爬虫风险业务防控 | 国际航班上,小“票代”在疯狂倒卖高价票

目录 乘坐国际航班,躲不开的“票代” 小“票代”的网络爬虫与高价票 某公司国际航班遭遇大量爬虫攻击 基于爬虫风险的分析与防控建议 顶象防御云业务安全情报中心监测发现,某航空国际航班,遭遇恶意网络爬虫的持续攻击。高峰时期&#xff…

C#Winform抽屉式导航栏实例讲解

Winform在UI界面设计时不如WPF灵活,如实现抽屉式导航栏功能不是很容易。 本文讲解如何采用简单代码量较少的实现该功能。 先上效果: 项目过程: 首先创建winform项目 在项目中添加对应的控件,控件列表如下: 代码如下: using System; using System.Collections.Gen…

【openeuler】openEuler kernel 技术分享 - 第2期 - 从ARM和RISC-V架构看体系结构对Linux操作

openEuler kernel 技术分享 - 第2期 - 从ARM和RISC-V架构看体系结构对Linux操作系统的支持_哔哩哔哩_bilibili The RISC-V Reader: An Open Architecture Atlas http://riscvbook.com/chinese/RISC-V-Reader-Chinese-v2p1.pdf

深入浅出设计模式 - 状态模式

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌ Java知识图谱点击链接:体系化学习Java(Java面试专题) 💕💕 感兴趣的同学可以收…

Day05

目录 1、编写mybatis插件,实现字段自动填充 注意 2、ThreadLocal的简单使用 3、问题:添加员工语句执行成功,但数据库中未添加新员工 4、问题:foreach 1、编写mybatis插件,实现字段自动填充 如何编写插件 Interc…

Jmeter之BeanShell Assertion自定义断言

在JMeter性能测试工具中,BeanShell Assertion是一种强大而灵活的自定义断言方法。 它允许用户通过编写BeanShell脚本来验证服务器返回的响应数据,从而确保系统在各种负载下的稳定性和可靠性。 无论您是初学者还是有经验的专业人士,使用Bean…

Element ui 取消点击空白处弹框关闭的效果

目录 属性: 描述 属性: element组件库的Dialog对话框默认是可以通过点击 modal 关闭 Dialog,即点击空白处弹框可关闭。 描述 在 el-dialog中close-on-click-modal含义是:点击空白处是否关闭,默认true;如…

python实现固定资产梳理的办法

一、需求; 需求:实现xxx地固定资产的计算以及梳理 1.盘点资产,通过excel表格设计了不同的区域,进行每个区域的资产的计数工作,成为了一个登记事项 2.后续形成文本汇报工作,梳理内容 3.需求把表格中同类…

2023金九银十跳槽必会Java核心知识点笔记整理

现在互联网大环境不好,互联网公司纷纷裁员并缩减 HC,更多程序员去竞争更少的就业岗位,整的 IT 行业越来越卷。身为 Java 程序员的我们就更不用说了,上班 8 小时需要做好本职工作,下班后还要不断提升技能、技术栈&#…

沐曦与百度飞桨完成兼容性测试,助力计算机视觉应用发展

近日,沐曦集成电路(上海)有限公司(以下简称“沐曦”)的曦思N100人工智能推理GPU与百度飞桨完成 I 级兼容性测试。测试结果显示,双方兼容性表现良好,整体运行稳定。这是沐曦自2022年9月加入“硬件…

五子棋AI智能算法的测试方法

先前发了几篇五子棋游戏程序设计的博文,设计了游戏程序,也设计了AI智能奕棋的算法,运行程序检测算法的可行性,完成人机模式游戏功能的设置。本文重点介绍测试方法。 对于人机对战的电脑智能应子算法,参阅很多五子棋书…

React项目请求接口跨域设置代理怎么设置

在src 目录下创建setupProxy.js const {createProxyMiddleware} require(http-proxy-middleware)module.exports function(app) {app.use(createProxyMiddleware(/api, { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)target: http://172.20.17.199:808…

学习Kotlin~函数

有名函数 函数参数 不打算传入值参,可以预先给参数指定默认值 //如果不打算传入值参,可以预先给参数指定默认值private fun fix(name: String, age: Int 2) {println(name age);}//调用的时候fix("hhhh");fix("hasee", 30);有名的…

【Html】Html+Less实现一个简单的加载动画

效果 运行环境 系统&#xff1a;Win10系统 IDE&#xff1a;Visual Studio Code v1.79.2 VSCode插件&#xff1a;Easy LESS v2.0.0 index.html代码 <!DOCTYPE html> <html><head><title>加载动画</title><link rel"stylesheet" hr…

Android的Context详解 - 揭开Context的神秘面纱

这篇文章是基于我四年前的一篇文章进行更正和深入探究。背景是&#xff0c;2019年4月份我在找工作&#xff0c;看到一个问题&#xff0c;问this&#xff0c;getBaseContext()、getApplication()、getApplicationContext()的区别。当时我写了简单的demo验证&#xff0c;得出了跟…

看看螯合物前体多肽试剂DOTA-E[c(RGDfK)2]的全面解析吧!

【产品描述】 DOTA-E[c(RGDfK)2]螯合物前体多肽试剂&#xff0c;RGD肽指含有Arg-Gly-Asp三个氨基酸组成的序列多肽&#xff0c;可以提供大量的RGD直线肽&#xff0c;RGD环肽&#xff0c;RGD双环肽、RGD模拟肽等&#xff0c;也可以根据客户需求定制RGD肽。 DOTA-E [c (RGDfK) 2…

6.3 B树

多路平衡查找树 1.定义 B树的阶&#xff1a;B树中所有结点的孩子个数的最大值&#xff0c;表示成m m阶B树&#xff1a;空树或者满足如下特性的m叉树 特性&#xff1a; 1.树中每个结点最多子树 m 关键字m-1 2.根节点不是终端结点&#xff0c;至少有两棵子树 3.根结点除外&…