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

news2024/11/23 4:25:10

我最近看到的最有趣的电子商务功能之一是能够搜索与我手机上的图片相似的产品。例如,我可以拍一双鞋或其他产品的照片,然后搜索产品目录以查找类似商品。使用这样的功能可以是一个相当简单的项目,只要有合适的工具。如果我们可以将问题定义为向量搜索问题,那么我们就可以使用 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.coloradmin.cn/o/2203009.html

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

相关文章

Pycharm设置相同变量的背景颜色

在 PyCharm 中设置鼠标点击某个变量时&#xff0c;代码其他地方相同变量的背景颜色&#xff0c;可以通过调整颜色方案中的相关设置来实现。以下是详细步骤&#xff1a; 打开 PyCharm&#xff0c;选择 File -> Settings&#xff08;在 Windows/Linux 上&#xff09;或 PyChar…

基于 CSS Grid 的简易拖拉拽 Vue3 组件,从代码到NPM发布(1)- 拖拉拽交互

基于特定的应用场景&#xff0c;需要在页面中以网格的方式&#xff0c;实现目标组件在网格中可以进行拖拉拽、修改大小等交互。本章开始分享如何一步步从代码设计&#xff0c;最后到如何在 NPM 上发布。 请大家动动小手&#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug…

全网最详细k8s搭建部署

目录 Kubernetes的功能&#xff1a; Kubernetes的特点&#xff1a; 1. 安装要求 2. 部署内容 1、系统环境准备 2、所有禁用swap和本地解析 3、仓库配置&#xff0c;所有安装docker 4、所有节点设定docker的资源管理模式为systemd 5、所有阶段复制harbor仓库中的证书并…

一款电子产品图册转换器

​随着科技的不断发展&#xff0c;电子产品已经成为我们生活中不可或缺的一部分。无论是手机、平板电脑还是智能家居&#xff0c;它们都离不开电子图册的支撑。一款优秀的电子产品图册转换器&#xff0c;可以帮助我们轻松实现电子图册的转换&#xff0c;为我们的生活和工作带来…

AlphaFold加冕诺奖,DeepMind CEO获奖感言:最优秀的科学家与AI配合,将完成令人难以置信的工作

继「AI 教父」摘冠 2024 年诺贝尔物理学奖后&#xff0c;AI 再下一城&#xff0c;获得了今年的诺贝尔化学奖。 北京时间 10 月 9 日&#xff0c;瑞典皇家科学院宣布了 2024 年诺贝尔化学奖的归属&#xff0c;一半授予 David Baker&#xff0c; 以表彰其在计算蛋白设计方面的贡…

python实现音频文件mp3/m4a转.wav + windows安装ffmpeg

近期在尝试使用大模型进行音频降噪、人声分离。抱脸上的模型几乎统一输入需求都是.wav&#xff0c;直接贴代码吧 ps:使用这段代码需要提前安装好ffmpeg(安装教程请往下拉) from pydub import AudioSegment# 加载.m4a文件 audio AudioSegment.from_file(r"你文件的路径&…

Pycharm使用CV2

1、windows下已经安装好python3以及opencv2 2、安装并打开pycharm 环境中装好的包就能显示出来了&#xff0c;就可以去调用cv2的接口了

TCP/IP相关

1、关于三次握手、四次挥手和TCP的11种状态&#xff1a; 记住这张图就行了&#xff1a; 2、关于慢启动、拥塞避免、超时重传、快速重传、快速恢复 记住这张图就行了&#xff1a; 一些名词解释&#xff1a; MSS&#xff1a;Maximum Segment Size&#xff0c;最大报文长度 RT…

力扣 1206. 设计跳表

Problem: 1206. 设计跳表 &#x1f469;‍&#x1f3eb; 参考题解 class Skiplist {// 定义跳表的最大层数int level 10;// 定义跳表节点类class Node {int val; // 节点值Node[] ne new Node[level]; // 节点的下一跳节点数组&#xff0c;支持多级索引// 构造函数&#xf…

深度学习之卷积CONV2D

文章目录 1.学习目的2.填充与步幅2.1填充2.2 步幅 3.总结 1.学习目的 卷积听起来简单&#xff0c;事实上不简单&#xff0c;需要多加练习 2.填充与步幅 在前面的例子 图6.2.1中&#xff0c;输入的高度和宽度都为3&#xff0c;卷积核的高度和宽度都为2&#xff0c;生成的输出…

用Python将HTML转换为Excel文件

在数据处理和分析的过程中&#xff0c;经常需要从网页上抓取信息&#xff0c;并将其转换为更易于操作的格式。HTML表格作为一种常见的数据展示方式&#xff0c;在线报告、统计资料等场景中广泛存在&#xff0c;但其结构化程度较低&#xff0c;不利于进一步的数据清洗和分析。将…

微信小程序-APP-软件开发

微信小程序开发&#xff0c;作为当下移动互联网领域的一股强劲势力&#xff0c;正以其便捷性、轻量化及高用户粘性的特点&#xff0c;深刻改变着我们的生活与工作方式。它不仅为企业和个人开发者提供了一个全新的服务入口&#xff0c;更极大地拓宽了商业应用的边界。 在微信小…

2015年国赛高教杯数学建模D题众筹筑屋规划方案设计解题全过程文档及程序

2015年国赛高教杯数学建模 D题 众筹筑屋规划方案设计 众筹筑屋是互联网时代一种新型的房地产形式。现有占地面积为102077.6平方米的众筹筑屋项目&#xff08;详情见附件1&#xff09;。项目推出后&#xff0c;有上万户购房者登记参筹。项目规定参筹者每户只能认购一套住房。  …

游戏录屏必备!五款超实用软件让你轻松记录精彩游戏瞬间

在游戏的世界里&#xff0c;每一个精彩的操作、每一场激烈的对战都值得被记录下来。无论是想要分享给朋友&#xff0c;还是留作自己的游戏回忆&#xff0c;一款优秀的游戏录屏软件都是必不可少的。下面就为大家介绍五款备受好评的游戏录屏软件&#xff0c;让你轻松成为游戏录屏…

【读书笔记·VLSI电路设计方法解密】问题9:什么是SOC发展趋势的推动力

推动SoC趋势的主要力量之一是成本。将更多功能集成到单一芯片中可以减少系统中的芯片数量&#xff0c;从而缩小封装和电路板的成本。这可能会降低整个系统的成本&#xff0c;使产品更具竞争力。在当今的消费电子市场和其他市场中&#xff0c;较低的价格总能带来获得市场份额的优…

激光避障的运行算法!

一、激光传感器的工作原理 激光避障技术利用激光束的直线传播和反射特性&#xff0c;通过发送激光束并接收反射回来的信号&#xff0c;来检测和计算周围障碍物的距离和位置。激光传感器能够生成高精度的距离数据和三维环境信息&#xff0c;为机器人或无人机提供详细的障碍物分…

留学期间如何提高职业竞争力?

留学期间是提高职业竞争力的关键时期&#xff0c;以下是一些具体的建议&#xff0c;帮助留学生在留学期间增强自身的职业竞争力&#xff1a; 一、深化专业知识与技能 1. 专注于课程学习&#xff1a;努力学习专业课程&#xff0c;掌握扎实的专业知识&#xff0c;这是提高职业竞…

qiankun 主项目和子项目都是 vue2,部署在同一台服务器上,nginx 配置

1、主项目配置 1.1 micro.vue 组件 <template><div id"container-sub-app"></div> </template><script> import { loadMicroApp } from qiankun; import actions from /utils/actions.js;export default {name: microApp,mixins: [ac…

颠覆传统!团购新玩法带你零风险狂赚社交红利

你是否曾经被某个看似大胆且充满挑战的商业策略深深吸引&#xff0c;最终却惊喜地发现它在短时间内创造了惊人的价值&#xff1f;今天&#xff0c;我们将一起探索一个别出心裁的商业模式&#xff0c;看看它是如何在短短一个月内实现超过600万的利润奇迹。这不仅仅是一次对商业机…

第十一章:规划过程组 (11.1制定项目管理计划--11.5创建WBS)

11.1 制定项目管理计划 • 项目管理计划可以是概括或详细的&#xff0c;每个组成部分的详细程度取决于具体项目的要求 • 项目管理计划应基准化&#xff0c;即至少应规定项目的范围、时间和成本方面的基准以便据此考核项目执行情况和管理项目绩效。 • 在确定基准之前&#xf…