在神经网络技术出现之前,通常需要依赖文件名、图像元数据以及图像上下文(例如,如果图像出现在文本段落中,则使用alt文本或周围的文本)来提供更丰富的搜索功能。现在,OpenAI的对比语言图像预训练(CLIP)模型为提供了一种方法,只需几十行代码就能实现语义搜索引擎。CLIP模型在数百万对文本和图像上进行了训练,结合编码了图像和文本的语义。使用CLIP,可以提供文本查询,CLIP将返回与查询最相关的图像。
构建语义搜索引擎
本指南将带了解如何使用Supabase和通过Roboflow托管的OpenAI CLIP模型来构建语义搜索引擎。将通过以下步骤来实现这一目标:
- 计算数据集中所有图像的嵌入。
- 为用户查询(例如“安全帽”或“汽车”)计算文本嵌入。
- 比较文本嵌入和图像嵌入以找到相关的嵌入。
两个嵌入越接近,它们所代表的内容就越相似。
通过Roboflow API使用CLIP
将通过Roboflow的托管API端点使用CLIP,这个端点可以根据需要无限扩展/缩减。为了使用Roboflow API,必须拥有一个API密钥,可以按照这些在账户仪表板中找到它。还使用axios来进行API请求,可以从npm下载它。
获取CLIP嵌入
Roboflow API提供了一个/clip端点,可以通过它为文本和图像生成嵌入。以下是使用TypeScript代码嵌入文本的示例:
async function embedText(text: string, apiKey: string) {
const response = await axios({
method: "POST",
url: "https://infer.roboflow.com/clip/embed_text",
params: {
api_key: apiKey
},
data: {
clip_version_id: "ViT-B-16",
text: text
},
headers: {
"Content-Type": "application/json"
}
});
return response.data.embeddings[0];
}
类似地,要通过使用base64编码上传图像来嵌入图像:
async function embedImage(file: string, apiKey: string) {
const response = await axios({
method: "POST",
url: `https://infer.roboflow.com/clip/embed_image`,
params: {
api_key: apiKey
},
data: {
clip_version_id: "ViT-B-16",
image: [
{
type: "base64",
value: file
}
]
},
headers: {
"Content-Type": "application/json"
}
});
return response.data.embeddings[0];
}
这两个函数都返回了一个长度为512的向量,代表内容的语义。这个嵌入可以用来交叉比较文本和图像,例如根据文本搜索图像或根据文本标签数据库对图像进行分类。
使用Supabase建立支持搜索的基础设施
为了构建支持搜索的基础设施,可以使用Supabase的pg_vector扩展,它添加了比较嵌入的函数,例如使用余弦相似性操作。以下是设置Supabase的步骤,这些步骤是根据他们关于搜索ChatGPT文本嵌入的改编的。
- 首先,需要创建一个Supabase项目,启用pg_vector扩展,并创建一个表来存储图像嵌入。
- 可以在创建一个Supabase项目。然后打开SQL控制台并运行以下命令来启用pg_vector并创建一个表。
create extension vector;
create table images (
id bigserial primary key,
image text,
embedding vector(512)
);
新表images将存储图像url在image列作为文本字符串,以及从CLIP API返回的嵌入在embedding列作为向量。
为了以后搜索这些图像,需要添加一个PostgreSQL函数来使用余弦相似性比较向量。运行以下SQL命令来添加可以稍后使用RPC调用的函数。
create or replace function match_images (
query_embedding vector(512),
match_threshold float,
match_count int
)
returns table (
id bigint,
image text,
similarity float
)
language sql stable
as $$
select
images.id,
images.image,
1 - (images.embedding <=> query_embedding) as similarity
from images
where 1 - (images.embedding <=> query_embedding) > match_threshold
order by similarity desc
limit match_count;
$$;
由于数据库可能会变得非常大,需要一个索引来对上传的图像进行排序,以使搜索算法更高效。运行以下SQL来添加索引:
create index on images using ivfflat (embedding vector_cosine_ops)
with
(lists = 100);
最后,必须安装并初始化Supabase JS API,可以使用以下和来完成。
将图像和嵌入添加到Supabase
为了以后搜索图像,首先需要将它们嵌入并上传到Supabase。以下函数将嵌入一个base64图像,将其上传到Supabase Storage,并插入到images表中。
export const uploadImage = async (image: File) => new Promise((resolve, reject) => {
var reader = new FileReader();
reader.onloadend = async () => {
const embedding = await embedImage(reader.result as string, roboflowAPIKey)
const imageName = `${Date.now()}${image.name.split('.').slice(-1)}`
const { data, error } = await supabase
.storage
.from('images')
.upload(imageName, image, {
cacheControl: '3600',
upsert: false
})
await supabase.from("images").insert({image: imageName, embedding})
resolve({})
}
reader.readAsDataURL(image);
})
只需将一个File对象(例如由HTML创建的)传递给uploadImage函数,它将被嵌入并上传到数据库。
使用Supabase通过嵌入搜索图像
现在可以使用文本在数据库中语义搜索图像。首先,必须使用CLIP API嵌入文本,然后调用之前创建的RPC函数,使用余弦相似性查询数据库。
export const searchDatabase = async (text: string, threshold: number, count: number) => {
const embedding = await embedText(text, roboflowAPIKey)
const { data, error } = await supabase.rpc('match_images', {
query_embedding: embedding,
match_threshold: threshold,
match_count: count,
})
return data
}
这个函数返回一个搜索对象列表,如下所示,并从最相似到最不相似排序:
{id: 8, image: '123.jpg', similarity: 0.263024778339981}
最后,必须从Supabase Storage下载图像以查看搜索内容。以下函数将接受一个图像名称(由searchDatabase函数提供为image),并返回图像数据。
export const downloadImage = async (image: string) => {
const { data, error } = await supabase
.storage
.from('images')
.download(image)
return data
}
这只是开始。存储和搜索图像嵌入为提供了广泛的能力。用户可以搜索“狗”,并自动根据它们的CLIP嵌入返回所有狗的图像。可以将这种方法适应于内容过滤。