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

在我的文章 “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.mfbz.cn/a/37387.html

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

相关文章

【Linux】进程优先级

Linux 进程优先级 为什么要有优先级的划分?Linux 环境设置优先级的具体做法并发运行环境变量如何通过代码获取环境变量 环境变量的来源 为什么要有优先级的划分? 优先级的规定就是为了确定某种资源获取的先后顺序。 本质原因是因为CPU资源是有限的。进程…

KMP算法

KMP KMP 算法是一个快速查找匹配串的算法,它的作用其实就是本题问题:如何快速在「原字符串」中找到「匹配字符串」。 而 KMP 算法的复杂度为 O(mn)实际上是O(N),因为O(M)不可能大于O(N) KMP 之所以能够在 O(mn)复杂度内完成查找,是因为其能…

CentOS环境下的MYSQL8安装

MySQL 安装 参考连接:https://www.cnblogs.com/jasonx1an/p/16690866.html 下载 下载网址:https://dev.mysql.com/downloads/mysql/ 卸载 mariadb 查看 mariadb rpm -qa|grep mariadb卸载 mariadb rpm -e mariadb-libs-5.5.68-1.el7.x86_64 --nodeps再…

概率栅格

欢迎访问我的博客首页。 概率栅格 1. miss 表与 hit 表 1. miss 表与 hit 表 miss 表和 his 表是一维数组,它们存放的都是空闲值。其下标 i i i 代表旧空闲值,元素 t a b l e [ i ] table[i] table[i] 代表旧空闲值 i i i 的新空闲值。表的更新可以用…

Maven —— 项目管理工具

前言 在这篇文章中,荔枝会介绍如何在项目工程中借助Maven的力量来开发,主要涉及Maven的下载安装、环境变量的配置、IDEA中的Maven的路径配置和信息修改以及通过Maven来快速构建项目。希望能对需要配置的小伙伴们有帮助哈哈哈哈~~~ 文章目录 前言 一、初…

安全防御 --- SSL VPN

附:无线项目介绍 SSL VPN 有浏览器的设备就可以使用SSL,进而使用SSL VPN。无需担心客户端问题,所以SSL VPN也称为无客户端VPN。SSL VPN在client to lan场景下特别有优势。 实际实现过程(基于TCP实现) (1&…

MYSQL执行一条SELECT语句的具体流程

昨天CSDN突然抽风 我一个ctrlz把整篇文章给撤掉了还不能复原 直接心态崩了不想写了 不过这部分果然还是很重要,还是写出来吧 流程图 这里面总共有两层结构Server层 储存引擎 Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包…

Java-API简析_java.lang.ProcessBuilder类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/131729933 出自【进步*于辰的博客】 因为我发现目前,我对Java-API的学习意识比较薄弱…

什么是Docker

容器技术和虚拟机 虚拟机 和一个单纯的应用程序相比,操作系统是一个很重的程序,刚装好的系统还什么都没有部署,单纯的操作系统其磁盘占用至少几十G起步,内存要几个G起步。 在这台机器上开启三个虚拟机,每个虚拟机上…

Failed to connect to github.com port 443: Connection refused问题解决

文章目录 一、问题描述:Failed to connect to github.com port 443: Connection refused问题解决二、解决方法一:排查代理问题1、尝试重置代理或者取消代理的方式2、添加全局代理 三、解决方法二:排查DNS解析问题1、第一步:查找gi…

Redis解决Session共享问题

文章目录 一、集群Session共享问题二、Redis存储验证码和对象三、解决状态登录刷新问题 一、集群Session共享问题 session共享问题:多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务器时导致数据丢失的问题 tomcat可以进行多台tomcat进行…

蓝牙技术|低功耗蓝牙和LE Audio助力游戏设备行业发展

去年,蓝牙技术联盟官方宣布推出LE Audio,它以BLE为基础,旨在更好地兼顾音频质量和低功耗,以在多种潜在应用中显著增强用户体验。这在游戏行业中引起了轰动,由于其延迟显著降低,LE Audio在增强游戏体验方面展…

连接一个JavaScript文件

● 首先&#xff0c;本章我们会使用一个起始文件&#xff0c;代码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…

unidbg或者java层解密方法IDEA中打包成jar包供python调用方法

一、导出jar包方法 &#xff08;1&#xff09;配置jar包参数 &#xff08;2&#xff09;创建生成jar包 成功生成&#xff01; 二、Python代码调用 import jpypejvmPath jpype.getDefaultJVMPath() d unidbg-android.jar # 对应jar地址 jpype.startJVM(jvmPath, "-ea&q…

apple pencil一代的平替有哪些品牌?苹果平板的触控笔

随着苹果Pencil系列的推出&#xff0c;平替电容笔在国内市场得到了较好的发展&#xff0c;随之的销量&#xff0c;也开始暴涨&#xff0c;苹果pencil因为价格太高&#xff0c;导致很多人买不起。目前市场上&#xff0c;有不少的平替电容笔&#xff0c;可以替代苹果的Pencil&…

opencv-06 使用numpy.array 操作图片像素值

opencv-06 使用numpy.array 操作图片像素值 **1&#xff0e;二值图像及灰度图像****利用item 读取某一个像素值****利用itemset 修改像素值****彩色图像numpy.arry 像素值操作** numpy.array 提供了 item()和 itemset()函数来访问和修改像素值&#xff0c;而且这两个函数都是经…

C基础day9(2023.7.11)

一、Xmind整理&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;实现字符串逆置 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, const char *argv[]) {char str[]"hello";char *pstr;char *qstrstrlen…

【Android知识笔记】系统进程(一)

Android 系统进程有哪些 先来一个整体结构图从宏观上理解Android系统的进程结构布局: 这里我们简单总结一下: 系统的第一个进程其实是0号进程(又叫swapper进程/Idle进程) 0号进程fork出了1号进程(init进程)和2号进程(kthreadd进程) 1号进程是所有普通用户进程的祖先,2号进程…

CSDN-AI小组2023-半年-研发总结

目录 1.丐版「大模型」&#xff0c;Proof of concept2. LLM和AIGC的各种综述3. 基于Embedding的应用&#xff0c;问答&#xff0c;AI编程4. 评论区的AI助手5. 结合AIGC的各种数据自动计算6. 个性化推荐的系统重构7. 基于AIGC的个性化博客创作鼓励8. 博客质量分V5: 可解释性计算…

vulnhub靶机渗透:PWNLAB: INIT

PWNLAB: INIT 靶机环境介绍nmap扫描端口扫描服务扫描漏洞扫描扫描总结 80端口目录爆破LFI利用 3306端口回到80端口文件上传 获得立足点横向移动提权总结参考 靶机环境介绍 https://www.vulnhub.com/entry/skytower-1,96/ 靶机IP&#xff1a;192.168.56.103 kali IP&#xff…