【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 pddf = 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: strembeddings: list[float]def get_image_embeddings(image_paths: list[str], normalize=True
) -> list[ImageEmbedding]:# Process image and generate embeddingsimages = []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 = embeddingimage_embeddings.append(ImageEmbedding(image_path=image_p,embeddings=embeds.tolist(),))return image_embeddingsdef list_jpg_files(directory: str) -> list[str]:# List to hold the full paths of filesfull_paths = []# Loop through the directoryfor filename in os.listdir(directory):# Check if the file ends with .jpgif filename.endswith(".jpg"):# Construct full path and add it to the listfull_paths.append(os.path.join(directory, filename))return full_pathsdef 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("""SELECTimage_path,1 - (embeddings <=> %s::vector) AS similarity_scoreFROM image_embeddingsORDER BY similarity_score DESCLIMIT 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认证
相关文章:

【PGCCC】在 Postgres 上构建图像搜索引擎
我最近看到的最有趣的电子商务功能之一是能够搜索与我手机上的图片相似的产品。例如,我可以拍一双鞋或其他产品的照片,然后搜索产品目录以查找类似商品。使用这样的功能可以是一个相当简单的项目,只要有合适的工具。如果我们可以将问题定义为…...

性能测试之性能问题分析
开始性能测试前需要了解的内容: 1、项目具体需求。 2、指标:响应时间在多少以内,并发数多少,tps多少,总tps多少,稳定性交易总量多少,事务成功率,交易波动范围,稳定运行…...

错过了A股,别再错过AI表情包!N款变现攻略,你选哪个?
本文背景 据 Swyft Media 统计,全世界每天各类聊天 app 发送的表情符号有 60 多亿,我们国家每天表情包发送量大概 6 亿次。 表情包简直就是个大淘金池,最近用 AI 做表情包也挺火。所以今天给大家讲讲一个用 AI 做表情包变现的项目。 以前没…...

SpringBoot驱动的美发沙龙管理系统:优雅地管理您的业务
1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理美发门店管理系统的相关信息成为必然。开发…...

prometheus + alertmanager 搭建告警通知
prometheus 下载prometheus-2.53.2 prometheus.yml文件修改 global:scrape_interval: 15sevaluation_interval: 15salerting:alertmanagers:- static_configs:- targets:- 127.0.0.1:9093rule_files:- "rules/rule-*.yml"scrape_configs:- job_name: "promet…...

爬虫案例——爬取腾讯社招
案例需求: 1.爬取腾讯社招的数据(搜索 | 腾讯招聘)包括岗位名称链接时间公司名称 2.爬取所有页(翻页) 3.利用jsonpath进行数据解析 4.保存数据:txt文本形式和excel文件两种形式 解析: 1.分…...

VAS1800Q奇力科技线性芯片电荷泵热处理
高效恒流LED驱动器——VAS1800Q在汽车应用中的卓越表现 VAS1800Q是一款专为汽车应用设计的高效恒流LED驱动器。它具备多个显著特点,不仅提升了LED驱动效率,还大大减少了热量的产生,使其在汽车照明领域中具有极高的应用价值。本文将详细介绍VA…...

SQL Inject-基于报错的信息获取
常用的用来报错的函数 updatexml() : 函数是MYSQL对XML文档数据进行查询和修改的XPATH函数。 extractvalue(): 函数也是MYSQL对XML文档数据进行查询的XPATH函数。 floor(): MYSQL中用来取整的函数。 思路: 在MySQL中使用一些指定的函数来制造报错&am…...

redistemplate宇jedis区别
redistemplate是Spring Data Redis提供的一个模板类,用于简化Redis操作的代码编写。它提供了常见的操作方法,如存储、读取、删除等,可以更方便地操作Redis数据库。 而Jedis是Redis官方推荐的Java客户端库之一。它提供了丰富的功能和灵活的接…...

JavaWeb--09Servlet深入:JavaWeb三层架构---注册系统
一套完整的网页到Java到数据库的创建: html:进行数据收集以及呈现 第一层:根据servlet处理前台html的响应和请求,对数据进行接收,封装和验证 第二层:业务,验证是否存在调用创建的dao查&#x…...

教育技术革新:SpringBoot在线教育系统开发指南
6系统测试 6.1概念和意义 测试的定义:程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为: 目的:发现程序的错误; 任务:通过在计算机上执行程序,暴露程序中潜在的错误。 另一个…...

EasyAnimate
https://github.com/aigc-apps/EasyAnimate/blob/main/README_zh-CN.mdhttps://github.com/aigc-apps/EasyAnimate/blob/main/README_zh-CN.md EasyAnimate v4是一个用于生成高分辨率和长视频的端到端解决方案。我们可以训练基于转换器的扩散生成器,训练用于处理长视频的VAE,…...

Unity实现自定义图集(五)
以下内容是根据Unity 2020.1.0f1版本进行编写的 在Unity编辑器上的自定义图集已经完成了,但是如何将自定义图集文件打包,以及在移动平台将自定义图集和对应的纹理图(Texture)加载出来是个问题,本篇就来解决这些问题 1、思路 首先是自定义图集的打包。 自定义图集实际…...

2024年最佳平替电容笔对比:西圣、摩米士、倍思,哪款更适合你?
作为一位专注于数码产品的博主,我深知近年来平替电容笔在消费者中的热度不断攀升。这种电容笔以其亲民的价格和卓越的书写体验引起了广泛关注,尤其适合那些需要用iPad学习和办公的无纸化爱好者。 西圣这款自带充电仓的电容笔备受关注,尤其因…...

关系型数据库索引操作
前言:关系型数据库索引总结-作用,类型,适用场景,缺点简介 在MySQL、SQL Server、Oracle中,建立、修改、删除索引的操作有很多相似之处,但由于每个数据库系统的语法和特性不同,具体操作有一些差异。以下是三种数据库中关于索引操作…...

深度学习基础—卷积神经网络示例
1.卷积神经网络的结构 在之前的博客《深度学习—简单的卷积神经网络》,仅由卷积层构成网络的全部,这还不是标准的网络结构,本文将继续介绍标准的卷积神经网络结构有哪些? 深度学习基础—简单的卷积神经网络https://blog.csdn.net…...

vite学习教程03、vite+vue2打包配置
文章目录 前言一、修改vite.config.js二、配置文件资源/路径提示三、测试打包参考文章资料获取 前言 博主介绍:✌目前全网粉丝3W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&…...

Java | Leetcode Java题解之第461题汉明距离
题目: 题解: class Solution {public int hammingDistance(int x, int y) {int s x ^ y, ret 0;while (s ! 0) {s & s - 1;ret;}return ret;} }...

ubuntu 开放 8080 端口快捷命令
文章目录 查看防火墙状态开放 80 端口开放 8080 端口开放 22端口开启防火墙重启防火墙**使用 xhell登录**: 查看防火墙状态 sudo ufw status [sudo] password for crf: Status: inactivesudo ufw enable Firewall is active and enabled on system startup sudo…...

关于头文件为什么要加条件编译语句
因为有些情况重复包含头文件会出现重复定义或者导致多次包含导致增加编译时间。 下述是没有假如条件编译和宏定义的例子: // common_functions.h int addTwoNumbers(int a, int b); // data_structures.h #include "common_functions.h"struct MyStruc…...

strstr
strstr函数原型: char *strstr(conset char *s, conset char *s2); 功能:在字符串s中查找字符串s2出现的位置 返回值: 成功:返回第一次出现的s2的地址 失败:NULL...

C语言 | Leetcode C语言题解之第454题四数相加II
题目: 题解: struct hashTable {int key;int val;UT_hash_handle hh; };int fourSumCount(int* A, int ASize, int* B, int BSize, int* C, int CSize, int* D, int DSize) {struct hashTable* hashtable NULL;for (int i 0; i < ASize; i) {for (…...

Leetcode 删除链表倒数第 N 个节点
算法思想: 使用了双指针法。下面是详细的算法思想: 1. 引入虚拟头节点(dummy node) 为了处理链表的一些边界情况(比如删除头节点),我们在链表的头部引入了一个虚拟节点 dummy,并让…...

[移植] tgi 编译
这里写自定义目录标题 报错 报错 Collecting numpy1.26.4 (from -r requirements_cuda.txt (line 21))Downloading numpy-1.26.4.tar.gz (15.8 MB)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.8/15.8 MB 15.0 MB/s eta 0:00:00Inst…...

vue-element-admin后台集成方案
文章目录 vue-element-admin后台集成方案介绍使用安装目录介绍 vue-element-admin后台集成方案 介绍 官方网站 https://panjiachen.github.io/vue-element-admin-site/zh/guide/#%E5%8A%9F%E8%83%BD使用 安装 这里有三个模板,我们一般选择基础模板进行开发就好…...

40条经典ChatGPT论文指令,圈定选题和进行论文构思
目录 1、用ChatGPT圈定选题范围2、用ChatGPT生成研究方法和思路3、用ChatGPT扩展论文观点和论证4、用ChatGPT辅助论文结构设计5、如何直接使用ChatGPT4o、o1、OpenAI Canvas6、OpenAI Canvas增强了啥?7、编程功能增强 👇 ChatGPT o1网页入口在文末&#…...

在不支持WSL2的Windows环境下安装Redis并添加环境变量的方法
如果系统版本支持 WSL 2 可跳过本教程。使用官网提供的教程即可 官网教程 查看是否支持 WSL 2 如果不支持或者觉得麻烦可以按照下面的方式安装 下载 点击打开下载地址 下载 zip 文件即可 安装 将下载的 zip 文件解压到自己想要解压的地方即可。(注意&#x…...

《Electron 基础知识》代码打开开发者工具DevTools
初始化 const mainWindow new BrowserWindow({width: 1400,height: 800 );打开 接口 openDevTools mainWindow.webContents.openDevTools();关闭 接口 closeDevTools mainWindow.webContents.closeDevTools();...

小米R3G刷机OP
小米R3G刷机OP 22年购买了一个小米R3G路由器,刷OP系统后可以中继校园网,从而让智能开关、小爱同学可以联网。 当年的价格还是55元,现在只需要30元了,价格越来越便宜,并且OP版本越来越完善了。 之前刷机过breed系统&…...

移动机器人规划控制合集
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!…...