【PGCCC】在 Postgres 上构建图像搜索引擎

我最近看到的最有趣的电子商务功能之一是能够搜索与我手机上的图片相似的产品。例如,我可以拍一双鞋或其他产品的照片,然后搜索产品目录以查找类似商品。使用这样的功能可以是一个相当简单的项目,只要有合适的工具。如果我们可以将问题定义为向量搜索问题,那么我们就可以使用 Postgres 来解决它!

在这篇博文中,我们将使用Postgres构建一个基本的图像搜索引擎。我们将使用预先训练的模型为图像和文本生成嵌入,然后将这些嵌入存储在 Postgres 中。pgvector扩展将使我们能够使用图像和原始文本作为查询对这些嵌入进行相似性搜索。

使用 CLIP 和 Postgres 进行图像搜索

2021 年,OpenAI 发表了一篇论文和CLIP(对比语言-图像预训练)的模型权重,该模型经过训练可以预测给定图像的最相关文本片段。通过一些巧妙的实现,此模型还可以用作搜索引擎的主干,该搜索引擎接受图像和文本作为输入查询。我们可以将图像转换为向量(嵌入),将图像的嵌入存储在 Postgres 中,使用扩展对这些向量进行相似性搜索,并使用它在 Postgres 之上构建图像搜索引擎。Hugging Face 上有许多CLIP 模型的开源变体,但我们将使用 OpenAI 的clip-vit-base-patch32 模式进行演示。

在之前的博客中,我们写过关于为语义文本搜索生成嵌入的内容。其中一些原则也适用于此。我们将为数据存储库生成嵌入,在本例中是图像目录。然后我们将这些嵌入存储在 Postgres 中。当我们查询数据时,我们需要使用相同的模型来为查询生成嵌入。不同之处在于,在这种情况下,我们的模型将为文本和图像生成嵌入。

在本示例中,我们将使用 OpenAI 在 Hugging Face 上提供的开源CLIP模型之一。请注意, CLIP 在生产中的使用存在限制。使用这些模型非常方便,因为它们的接口可在transformers Python 库中找到。

使用图像嵌入加载 Postgres

首先,我们需要获取原始图像。我们使用来自 Kaggle 的 Amazon Products 数据集。该数据集包含每个示例产品的图像 URL,因此我们将下载图像并将其存储在目录中。

在本例中,我们将把图像文件存储在本地,但在生产系统中,您可以将它们存储在 S3 等云存储服务中。

import pandas as pd

df = pd.read_csv("data/amazon_product.csv")

for i, row in df.iterrows():
    url = row["product_photo"]
    asin = row["asin"]
    response = requests.get(url)
    img = Image.open(BytesIO(response.content))
    if img.mode == 'RGBA':
        img = img.convert('RGB')

    img.save(f"./data/{asin}.jpg")

接下来,我们需要为获取的图像生成嵌入。我们将在 Postgres 中设置一个表来存储嵌入。

CREATE TABLE IF NOT EXISTS image_embeddings (
    image_path TEXT PRIMARY KEY,
    embeddings VECTOR(512)
);

我们将使用 CLIP 模型为每个图像生成嵌入,并将它们保存到 Postgres 表中。并创建一些辅助函数来加载图像、生成嵌入并将它们插入到 Postgres 中。

from pydantic import BaseModel
from transformers import (
    CLIPImageProcessor,
    CLIPModel,
)

MODEL = "openai/clip-vit-base-patch32"

image_processor = CLIPImageProcessor.from_pretrained(MODEL)
image_model = CLIPModel.from_pretrained(MODEL)

class ImageEmbedding(BaseModel):
    image_path: str
    embeddings: list[float]

def get_image_embeddings(
    image_paths: list[str], normalize=True
) -> list[ImageEmbedding]:
    # Process image and generate embeddings
    images = []
    for path in image_paths:
        images.append(Image.open(path))
    inputs = image_processor(images=images, return_tensors="pt")
    with torch.no_grad():
        outputs = image_model.get_image_features(**inputs)

    image_embeddings: list[ImageEmbedding] = []
    for image_p, embedding in zip(image_paths, outputs):
        if normalize:
            embeds = F.normalize(embedding, p=2, dim=-1)
        else:
            embeds = embedding
        image_embeddings.append(
            ImageEmbedding(
                image_path=image_p,
                embeddings=embeds.tolist(),
            )
        )
    return image_embeddings


def list_jpg_files(directory: str) -> list[str]:
    # List to hold the full paths of files
    full_paths = []
    # Loop through the directory
    for filename in os.listdir(directory):
        # Check if the file ends with .jpg
        if filename.endswith(".jpg"):
            # Construct full path and add it to the list
            full_paths.append(os.path.join(directory, filename))
    return full_paths


def pg_insert_embeddings(images: list[ImageEmbedding]):
    init_pg_vector = "CREATE EXTENSION IF NOT EXISTS vector;"
    init_table = """
        CREATE TABLE IF NOT EXISTS image_embeddings (image_path TEXT PRIMARY KEY, embeddings VECTOR(512));
    """
    insert_query = """
        INSERT INTO image_embeddings (image_path, embeddings)
        VALUES (%s, %s)
        ON CONFLICT (image_path)
        DO UPDATE SET embeddings = EXCLUDED.embeddings
        ;
    """
    with psycopg.connect(DATABASE_URL) as conn:
        with conn.cursor() as cur:
            cur.execute(init_pg_vector)
            cur.execute(init_table)

            for image in images:
                cur.execute(insert_query, (image.image_path, image.embeddings))

我们的辅助函数是这样的,让我们按顺序执行它们。

# get the paths to all our jpg images
images = list_jpg_files("./images")
# generate embeddings
image_embeddings = get_image_embeddings(images)
# insert them into Postgres
pg_insert_embeddings(image_embeddings)

快速验证嵌入是否已插入 Postgres。我们应该看到

psql postgres://postgres:postgres@localhost:5433/postgres
\x
select image_path, embeddings from image_embeddings limit 1;
image_path   | ./data/B086QB7WZ1.jpg
embeddings | [0.01544646,0.062326625,-0.03682831,0 ...

使用pgvector搜索相似图片

现在我们有了为文本生成嵌入的函数,我们可以在向量相似度搜索查询中使用这些嵌入。pgvector 支持多种距离运算符,但在本例中我们将使用余弦相似度。我们要搜索的嵌入存储在Postgres中,因此我们可以使用 SQL 进行余弦相似度搜索(1 - 余弦相似度)并找到嵌入与文本查询的嵌入最相似的图像。

def similarity_search(txt_embedding: list[float]) -> list[tuple[str, float]]:
    with psycopg.connect(DATABASE_URL) as conn:
        with conn.cursor() as cur:
            cur.execute(
                """
                        SELECT
                            image_path,
                            1 - (embeddings <=> %s::vector) AS similarity_score
                        FROM image_embeddings
                        ORDER BY similarity_score DESC
                        LIMIT 2;
                        """,
                (txt_embedding,),
            )
            rows = cur.fetchall()

            return [(row[0], row[1]) for row in rows]

与使用原始文本对数据进行向量搜索类似,我们将使用嵌入来搜索相似的图像。让我们抓取一张Cher的图像,我们可以从她的维基百科页面使用该图像。将其保存到./cher_wikipedia.jpg。
在这里插入图片描述
现在我们可以将单个图像传递到我们的get_image_embeddings()函数中,然后使用“similarity_search()”搜索相似的图像。

search_embeddings = get_image_embeddings(["./cher_wikipedia.jpg"])[0].embeddings
results = similarity_search(search_embeddings)

for image_path, score in results[:2]:
    print((image_path, score))
('B0DBQY1PKS.jpg', 0.5851975926639095)
('B0DBR4KDRF.jpg', 0.5125825695644287)

产品B0DBQY1PKS和B0DBR4KDRF(雪儿的“Forever”专辑)是与我们的雪儿形象最相似的两种产品。
在这里插入图片描述

使用原始文本查询图像

在搜索产品时,搜索相似的图片非常有用。但是,有时人们会希望根据给定的文本字符串来搜索图片。例如,Google 早就具备了搜索猫图片的功能。

from transformers import (
    CLIPTokenizerFast,
    CLIPTextModel,
    CLIPImageProcessor
)

MODEL = "openai/clip-vit-base-patch32"

processor = CLIPProcessor.from_pretrained(MODEL)
clip_model = CLIPModel.from_pretrained(MODEL)

def get_text_embeddings(text: str) -> list[float]:
    inputs = processor(text=[text], return_tensors="pt", padding=True)
    text_features = clip_model.get_text_features(**inputs)
    text_embedding = text_features[0].detach().numpy()
    embeds = text_embedding / np.linalg.norm(text_embedding)
    return embeds.tolist()

最后,我们可以使用这些函数生成嵌入,然后使用原始文本查询搜索我们的图像。我们将在产品目录中搜索“电话”的图像。

text_embeddings = get_text_embeddings("telephones")

results: list[tuple[str, float]] = similarity_search(search_embeddings)

for image_path, score in results[:2]:
    print((image_path, score))
('./data/B086QB7WZ1.jpg', 0.26320752344041964)
('./data/B00FRSYS12.jpg', 0.2626421138474824)

产品B086QB7WZ1和B00FRSYS12是与文本查询“电话”最相似的两幅图像。
在这里插入图片描述

Postgres 上的多模式搜索

我们已经从概念上展示了如何在 Postgres 上构建多模式搜索引擎。提醒一下,本博客中的代码可在Tembo Github 存储库中找到。我们使用 CLIP 模型为图像和文本生成嵌入,然后将这些嵌入存储在 Postgres 中。我们使用扩展pgvector对这些嵌入进行相似性搜索。这是一个强大的工具,可用于构建可以接受文本和图像查询的搜索引擎。关注 Tembo 博客,了解有关 Postgres 上矢量搜索用例的更多信息。

其他阅读材料

如果您对此主题感兴趣,请查看geoMusings博客上有关使用 pgvector 进行图像相似性分析的内容。另请阅读《视觉表征对比学习的简单框架》,ICML2020,Ting ChenSimon Kornblith、Mohammad Norouzi、Geoffrey E. Hinton。
#PG证书#PG考试#postgresql培训#postgresql考试#postgresql认证

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

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

相关文章

apisix云原生网关

定义 企业级网关通过域名、路由将请求分发到对应的应用上&#xff0c;通常承载数千个服务的流量&#xff0c;对稳定性有较高要求。 CNCF全景图 选型 Kubernetes抽象出两个核心概念&#xff1a;Service&#xff0c;为多个Pod提供统一的访问入口&#xff1b;Ingress&#xff…

汽车车轮平衡块行业前景:预计2030年全球市场规模将达到10亿美元

汽车车轮平衡块&#xff0c;也称为轮胎平衡块&#xff0c;是一种安装在车轮上的配重部件。它的主要作用是帮助车轮在高速旋转状态下保持动平衡。当车轮高速旋转时&#xff0c;由于车轮的动态不平衡状态&#xff0c;会导致车辆在行驶中出现车轮抖动和方向盘震动的现象。汽车车轮…

VSOMEIP代码阅读整理(1) - 网卡状态监听

一. 概述 在routing进程所使用的配置文件中&#xff0c;存在如下配置项目&#xff1a;{"unicast" : "192.168.56.101",..."service-discovery" :{"enable" : "true","multicast" : "224.244.224.245",…

【数据结构】栈和队列 + 经典算法题

目录 前言 一、栈 二、栈的实现 三、栈的循环遍历演示 四、栈的算法题 // 一、队列 二、队列的实现 三、使用演示 四、队列的算法题 总结 前言 本文完整实现了栈和队列的数据结构&#xff0c;以及栈和队列的一些经典算法题&#xff0c;让我们更加清楚了解这两种数据…

昇思MindSpore进阶教程--数据处理性能优化(中)

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 shuffle性能优化 shuffle操作主要是对有…

PCB缺陷检测数据集 xml 可转yolo格式 ,共10688张图片

PCB缺陷检测数据集&#xff08;yolov5,v7,v8&#xff09; 数据集总共有两个文件夹&#xff0c;一个是pcb整体标注&#xff0c;一个是pcb部分截图。 整体标注有6个分类&#xff0c;开路&#xff0c;短路等都已经标注&#xff0c;标注格式为xml&#xff0c;每个文件夹下有100多张…

vue3 环境配置vue-i8n国际化

一.依赖和插件的安装 主要是vue-i18n和 vscode的自动化插件i18n Ally https://vue-i18n.intlify.dev/ npm install vue-i18n10 pnpm add vue-i18n10 yarn add vue-i18n10 vscode在应用商城中搜索i18n Ally&#xff1a;如图 二.实操 安装完以后在对应项目中的跟package.jso…

探索Python的工业通信之光:pymodbus的奇妙之旅

文章目录 探索Python的工业通信之光&#xff1a;pymodbus的奇妙之旅背景&#xff1a;为何选择pymodbus&#xff1f;pymodbus是什么&#xff1f;如何安装pymodbus&#xff1f;5个简单的库函数使用方法3个场景使用示例常见bug及解决方案总结 探索Python的工业通信之光&#xff1a…

排序|插入排序|希尔排序|直接选择排序|堆排序的实现即特性(C)

插入排序 基本思想 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 单趟 当插入第 i ( i ≤ 1…

共享单车轨迹数据分析:以厦门市共享单车数据为例(十)

副标题&#xff1a;共享单车与地铁站出入口分布情况探究——以厦门市为例 假期结束了&#xff0c;我们满血复活&#xff0c;继续更新&#xff01; 本篇文章我们讨论共享单车与地铁出入口的关系&#xff0c;在上一篇文章中&#xff0c;我们讨论了综合得分指数最高的地铁站——…

Windows系统安装Docker

文章参考&#xff1a;Windows 安装docker&#xff08;详细图解&#xff09;-CSDN博客 安装包下载&#xff1a; 安装wsl的官方文档&#xff1a;安装 WSL | Microsoft Learn 查看windows相关配置 打开 (CTRLALTDELETE) 任务管理器 -> 选择性能 -> CPU ->虚拟化&#…

【重学 MySQL】四十七、表的操作技巧——修改、重命名、删除与清空

【重学 MySQL】四十七、表的操作技巧——修改、重命名、删除与清空 修改表添加字段语法示例注意事项 删除字段语法示例 修改字段使用 MODIFY COLUMN语法示例 使用 CHANGE COLUMN语法示例 重命名表语法示例 删除表语法示例 清空表使用 TRUNCATE TABLE使用 DELETE FROM对比 TRUNC…

处理Java内存溢出问题(java.lang.OutOfMemoryError):增加JVM堆内存与调优

处理Java内存溢出问题&#xff08;java.lang.OutOfMemoryError&#xff09;&#xff1a;增加JVM堆内存与调优 在进行压力测试时&#xff0c;遇到java.lang.OutOfMemoryError: Java heap space错误或者nginx报错no live upstreams while connecting to upstream通常意味着应用的…

Unity MVC框架演示 1-1 理论分析

本文仅作学习笔记分享与交流&#xff0c;不做任何商业用途&#xff0c;该课程资源来源于唐老狮 1.一般的图解MVC 什么是MVC我就不说了&#xff0c;老生常谈&#xff0c;网上有大量的介绍&#xff0c;想看看这三层都起到什么职责&#xff1f;那就直接上图吧 2.我举一个栗子 我有…

OpenSource - License 开源项目 TrueLicense

文章目录 官网集成Demo 官网 https://truelicense.namespace.global/ https://github.com/christian-schlichtherle/truelicense 集成Demo https://github.com/christian-schlichtherle/truelicense-maven-archetype https://github.com/zifangsky/LicenseDemo https://git…

机器学习——多模态学习

多模态学习&#xff1a;机器学习领域的新视野 引言 多模态学习&#xff08;Multimodal Learning&#xff09;是机器学习中的一个前沿领域&#xff0c;它涉及处理和整合来自多个数据模式&#xff08;如图像、文本、音频等&#xff09;的信息。随着深度学习的蓬勃发展&#xff0…

2020年华为杯数学建模竞赛D题论文和代码

无人机集群协同对抗 摘 要&#xff1a; 本文针对非线性约束条件下红蓝双方无人机集群协同对抗的最优规划问题&#xff0c;结合贪婪队形、非线性规划、内点法、蒙特卡洛方法和全联立正交配置有限元法&#xff0c;构建了无人机集群协同对抗推演模型。 针对问题一&#…

【刷题7】寻找数组的中心下标、和为k的子数组、和可被k整除的子数组

目录 一、寻找数组的中心下标二、和为k的子数组三、和可被k整除的子数组 一、寻找数组的中心下标 题目&#xff1a; 思路&#xff1a;前缀和思想 预处理一个前缀和数组和一个后缀和数组&#xff0c;当前指向的元素的值不包括在数组的元素和中&#xff1b;前缀和数组的公式…

网络受限情况下安装openpyxl模块提示缺少Jdcal,et_xmlfile

1.工作需要处理关于Excel文件内容的东西 2.用公司提供的openpyxl模块总是提示缺少jdcal文件,因为网络管控,又没办法直接使用命令下载&#xff0c;所以网上找了资源&#xff0c;下载好后上传到个人资源里了 资源路径 openpyxl jdcal et_xmlfile 以上模块来源于&#xff1a;Py…

数据湖数据仓库数据集市数据清理以及DataOps

一提到大数据我们就知道是海量数据&#xff0c;但是我们并不了解需要从哪些维度去考虑这些数据的存储。比如 数据湖、数据仓库、数据集市&#xff0c;以及数据自动化应用DataOps有哪些实现方式和实际应用&#xff0c;这篇文章将浅显的做一次介绍。 数据湖 数据湖是一种以自然…