使用SigLIP 2提升图像搜索能力

使用SigLIP 2提升图像搜索能力

提高图像搜索能力已成为数字资产管理、电子商务和社交媒体平台领域的关键重点。随着每天产生的可视化内容数量不断增加,对高效、准确的图像检索系统的需求比以往任何时候都更加迫切。SigLIP 2(用于语言图像预训练的 Sigmoid Loss)是谷歌 DeepMind 开发的最先进的多语言视觉语言编码器,有望彻底改变我们处理图像相似性和搜索任务的方式。它的创新架构不仅提高了语义理解能力,而且在零镜头分类和图像-文本检索方面表现出色。SigLIP 2 采用统一的训练方法,结合了自我监督学习和多样化的数据整理,在提取有意义的视觉表征方面优于以往的模型。

学习目标

  • 了解 CLIP 模型的基本原理及其在图像检索系统中的作用。
  • 确定基于 softmax 的损失函数在区分细微图像差异方面的局限性。
  • 探索 SigLIP 模型如何利用 sigmoid 损失函数克服这些局限性。
  • 分析 SigLIP 2 与 SigLIP 相比的主要进步和差异化特征。
  • 根据用户的图像查询实施图像检索系统。
  • 比较和评估 SigLIP 2 与 SigLIP 在图像检索任务中的性能。

对比语言-图像预训练(CLIP)

CLIP 是 Contrastive Language-Image Pre-training 的缩写,是 OpenAI 于 2021 年开发的一种突破性多模态模型。它通过学习图像和文本的共享表示空间,在计算机视觉和自然语言处理之间架起了一座桥梁。这种创新方法使 CLIP 能够同时理解和关联两种模式,从而能够执行零镜头图像分类、图像文本检索和字幕等任务。

CLIP的关键组件

CLIP 的关键组件包括文本编码器、图像编码器和对比学习机制。该机制通过最大化匹配对的相似度和最小化非匹配对的相似度来调整文本和图像的表示。

CLIP的关键组件

Source: https://openai.com/index/clip/

CLIP 是在一个大型图像-文本对数据集上进行训练的,通常涉及数以亿计的示例。该模型通过学习来预测与图像最相关的文本片段,反之亦然。

带有交叉熵损失的Softmax函数

在 CLIP 中,有一个图像编码器和另一个文本编码器,它们将输入的图像和文本转换为潜在表示。当我们从编码器得到嵌入(潜在表示)后,就可以计算出每对图像和文本之间的相似度得分(或点积)。相似度得分可以衡量图像和文本嵌入的相似程度。为了训练模型为图像标注正确的文本,或反之亦然,我们使用了一个损失函数,其目标是最大化图像和文本对之间的相似度得分。

CLIP

在 CLIP 中,softmax 函数应用于模型的输出,从而为批次中的每对图像文本获得如下的概率分布。

softmax 函数

在 CLIP 中,归一化(如分母所示)会独立进行两次:跨图像和跨文本,如下图损失函数所示:

损失函数

上式中的第一项为给定查询图像找出最佳文本匹配,第二项为给定查询文本找出最佳图像匹配。B 是批量大小。

CLIP的局限性

  • 处理高度相似配对时存在的问题。尽管 CLIP 利用 Softmax 函数计算图文配对的概率,但直接将其与余弦相似度结合使用时存在潜在问题。因为 Softmax 函数可能无法有效捕捉图像与文本嵌入之间的相对距离(尤其在处理高度相似配对时),这会导致比较结果缺乏细微区分度,并可能在需要细粒度区别的场景中影响性能。Softmax 倾向于将”错误”配对的概率推至接近零值,可能使模型忽略相似图像与文本描述间的微妙差异
  • 二次内存复杂度问题。此外,由于 CLIP 中所有正样本对的相似度都需要通过负样本对进行归一化,每个 GPU 必须维护一个 NxN 的全配对相似度矩阵,这导致了平方级内存复杂度。

采用Sigmoid损失函数的SigLIP

由谷歌开发的 SigLIP 沿用了 CLIP 的框架,但通过使用基于 Sigmoid 的损失函数(替代基于 Softmax 的损失)克服了上述问题。该函数可独立处理每个图文配对,其Sigmoid损失函数定义如下:

Sigmoid损失函数

Source: https://ahmdtaha.medium.com/sigmoid-loss-for-language-image-pre-training-2dd5e7d1af84

  •  分母中的 “N” 代表批次大小,用于确保不同批次规模下损失值的归一化
  • Σ(i=1到N) Σ(j=1到N)” 表示对所有图像(i)与文本(j)配对组合的损失求和
  • “z_ij” 用于标记图文配对的正负属性(1表示正样本,-1表示负样本)
  • “t” 控制Sigmoid曲线的陡峭程度
  • “xi·yj” 衡量图像嵌入与文本嵌入的相似度(通过点积运算)

与CLIP的差异

CLIP SigLIP  Inference
基于 Softmax 的损失 基于 Sigmoid 的损失 SigLIP 既不对称,也不依赖于全局归一化因子。因此,每个数据对的损失(无论是正损失还是负损失)都与迷你批次中的其他数据对无关。
每个 GPU 存储一个 NxN 矩阵,用于计算所有成对相似性 无需存储 NXN 矩阵,因为每个正/负对都是独立运行的。 通过内存高效损耗计算减少计算开销

SigLIP 2优于SigLIP

SigLIP 2 模型在零镜头分类、图像文本检索以及为视觉语言模型(VLM)提取视觉表征时的传输性能等关键领域的所有模型规模上都优于之前的 SigLIP 版本。一个突出的特点是动态分辨率(naflex)版本,该版本尤其适用于对长宽比和分辨率敏感的任务。

SigLIP 2的主要功能

SigLIP 2的主要功能

使用Sigmoid和位置感知字幕机 (LocCa) 解码器进行培训

SigLIP 2 在训练过程中引入了文本解码器,以及现有的图像和文本视觉编码器。对于 LocCa,视觉编码器中增加了一个具有交叉注意功能的转换解码器,以实现两个关键目标:

  1. 参照表达 (REF) :预测文本描述中提到的特定位置的边界框坐标。
  2. 基础字幕 (GCAP):根据图像中特定物体的位置创建标题。

改进的细粒度局部语义

为了改进图像表示中的细粒度局部语义,SigLIP 2 增加了两个额外的目标: 全局-局部损失(Global-Local Loss)和屏蔽预测损失(Masked Prediction Loss)。

  • 自我提炼:传统的知识蒸馏使用大型“教师”模型来训练较小的“学生”模型,而自蒸馏则不同,它使用相同的模型来扮演这两种角色。它有助于将知识从较深的网络层转移到较浅的网络层,或从较早的训练阶段转移到较晚的训练阶段。
  • 全局-局部损耗:这种损耗鼓励局部到全局的一致性。视觉编码器(作为学生)处理小图像片段,并学习如何与教师网络创建的完整图像表示相匹配。
  • 掩码预测损失:这种损失的工作原理是用掩码标记替换 50%的嵌入式图像补丁,促使学生模型在掩码位置匹配教师的特征。这有助于将注意力集中在每个补丁的单个特征上,而不是整个图像上。

更好地适应不同分辨率

由于图像模型对分辨率和宽高比的变化非常敏感,因此 SigLIP 2 引入了两种处理方法:

  • 固定分辨率变体: 在该版本中,训练从模型已经学习了大部分模式(完成了 95% 的训练)的检查点开始。位置嵌入的大小会进行调整,以匹配目标序列长度,并以新的分辨率继续训练。
  • 动态分辨率(NaFlex)变体: NaFlex 变体以 FlexiViT 和 NaViT 的概念为基础,使单个模型能够处理多个序列长度,并保持图像的原始纵横比。这减少了长宽比失真,对 OCR 和文档图像处理等任务特别有用。

现在,我们已经介绍了 SigLIP 2 的一些主要区别特性,让我们用 Python 来构建一个图像检索系统。

使用SigLIP 2构建图像检索系统以及与SigLIP的比较

在下面的上机教程中,我们将在用户根据图像查询进行搜索时构建一个图像检索系统。我们还将比较 SigLIP 2 和 SigLIP 的响应。我们将使用 Google Colab 上的 T4 GPU(免费层)来实现这一功能。

Step 1. 安装必要的库

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
!pip install datasets sentencepiece
!pip install faiss-cpu
#update latest version of transformers
!pip install git+https://github.com/huggingface/transformersCopy Code
!pip install datasets sentencepiece !pip install faiss-cpu #update latest version of transformers !pip install git+https://github.com/huggingface/transformersCopy Code
!pip install datasets sentencepiece
!pip install faiss-cpu
#update latest version of transformers
!pip install git+https://github.com/huggingface/transformersCopy Code

Step 2. 加载SigLIP模型

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import torch
import faiss
from torchvision import transforms
from PIL import Image
from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer
import numpy as np
import requests
device = torch.device('cuda' if torch.cuda.is_available() else "cpu")
model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device)
processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384")
tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")
import torch import faiss from torchvision import transforms from PIL import Image from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer import numpy as np import requests device = torch.device('cuda' if torch.cuda.is_available() else "cpu") model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device) processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384") tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")
import torch
import faiss
from torchvision import transforms
from PIL import Image
from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer
import numpy as np
import requests
device = torch.device('cuda' if torch.cuda.is_available() else "cpu")
model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device)
processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384")
tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")

Step 3. 处理输入图像、生成嵌入信息并将其保存在FAISS中的功能

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def add_vector(embedding, index):
vector = embedding.detach().cpu().numpy()
vector = np.float32(vector)
faiss.normalize_L2(vector)
index.add(vector)
def embed_siglip(image):
with torch.no_grad():
inputs = processor(images=image, return_tensors="pt").to(device)
image_features = model.get_image_features(**inputs)
return image_features
def add_vector(embedding, index): vector = embedding.detach().cpu().numpy() vector = np.float32(vector) faiss.normalize_L2(vector) index.add(vector) def embed_siglip(image): with torch.no_grad(): inputs = processor(images=image, return_tensors="pt").to(device) image_features = model.get_image_features(**inputs) return image_features
def add_vector(embedding, index):
vector = embedding.detach().cpu().numpy()
vector = np.float32(vector)
faiss.normalize_L2(vector)
index.add(vector)
def embed_siglip(image):
with torch.no_grad():
inputs = processor(images=image, return_tensors="pt").to(device)
image_features = model.get_image_features(**inputs)
return image_features

add_vector:该函数获取张量嵌入,对其进行归一化处理,并将其添加到 FAISS 索引中,以便进行高效的相似性搜索。

embed_siglip:该函数获取图像,对其进行处理,通过一个模型获取其嵌入(特征表示),并返回这些特征。

Step 4. 加载图像数据集

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
API_TOKEN=""
headers = {"Authorization": f"Bearer {API_TOKEN}"}
API_URL = "https://datasets-server.huggingface.co/rows?dataset=ceyda/fashion-products-small&config=default&split=train"
def query():
response = requests.get(API_URL, headers=headers)
return response.json()
data = query()
API_TOKEN="" headers = {"Authorization": f"Bearer {API_TOKEN}"} API_URL = "https://datasets-server.huggingface.co/rows?dataset=ceyda/fashion-products-small&config=default&split=train" def query(): response = requests.get(API_URL, headers=headers) return response.json() data = query()
API_TOKEN=""
headers = {"Authorization": f"Bearer {API_TOKEN}"}
API_URL = "https://datasets-server.huggingface.co/rows?dataset=ceyda/fashion-products-small&config=default&split=train"
def query():
response = requests.get(API_URL, headers=headers)
return response.json()
data = query()

我们在这里加载一个图像数据集 ,并使用请求库获取它,为此我们首先预先定义了拥抱脸 API 标记。这是一个关于时尚产品的数据集。

Step 5. 在FAISS向量数据库中存储嵌入结果

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
index = faiss.IndexFlatL2(768)
# read the image and add vector
for elem in data["rows"]:
url = elem["row"]["image"]["src"]
image = Image.open(requests.get(url, stream=True).raw)
#Generate Embedding of Image
clip_features = embed_siglip(image)
#Add vector to FAISS
add_vector(clip_features,index)
#Save the index
faiss.write_index(index,"./siglip_70k.index")
index = faiss.IndexFlatL2(768) # read the image and add vector for elem in data["rows"]: url = elem["row"]["image"]["src"] image = Image.open(requests.get(url, stream=True).raw) #Generate Embedding of Image clip_features = embed_siglip(image) #Add vector to FAISS add_vector(clip_features,index) #Save the index faiss.write_index(index,"./siglip_70k.index")
index = faiss.IndexFlatL2(768)
# read the image and add vector
for elem in data["rows"]:
url = elem["row"]["image"]["src"]
image = Image.open(requests.get(url, stream=True).raw)
#Generate Embedding of Image
clip_features = embed_siglip(image)
#Add vector to FAISS
add_vector(clip_features,index)
#Save the index 
faiss.write_index(index,"./siglip_70k.index")

Step 6. 查询模型

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
url = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRsZ4PhHTilpQ5zsG51SPZVrgEhdSfQ7_cg1g&s"
image = Image.open(requests.get(url, stream=True).raw)
with torch.no_grad():
inputs = processor(images=image, return_tensors="pt").to(device)
input_features = model.get_image_features(**inputs)
input_features = input_features.detach().cpu().numpy()
input_features = np.float32(input_features)
faiss.normalize_L2(input_features)
distances, indices = index.search(input_features, 3)
url = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRsZ4PhHTilpQ5zsG51SPZVrgEhdSfQ7_cg1g&s" image = Image.open(requests.get(url, stream=True).raw) with torch.no_grad(): inputs = processor(images=image, return_tensors="pt").to(device) input_features = model.get_image_features(**inputs) input_features = input_features.detach().cpu().numpy() input_features = np.float32(input_features) faiss.normalize_L2(input_features) distances, indices = index.search(input_features, 3)
url = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRsZ4PhHTilpQ5zsG51SPZVrgEhdSfQ7_cg1g&s"
image = Image.open(requests.get(url, stream=True).raw)
with torch.no_grad():
inputs = processor(images=image, return_tensors="pt").to(device)
input_features = model.get_image_features(**inputs)
input_features = input_features.detach().cpu().numpy()
input_features = np.float32(input_features)
faiss.normalize_L2(input_features)
distances, indices = index.search(input_features, 3)

现在我们已经建立了模型,让我们用一些提示来测试一下,看看它是如何工作的。

实际检索测试

由于这是一个时尚数据集,我们想查询一些时尚产品,并检查模型是否能够从数据库中获取外观相似的产品。

我们将首先用这款棕褐色女包查询模型。

棕褐色女包

现在让我们检查一下模型根据这个查询获取的 3 个最相似的产品。

SigLIP 2 模型测试

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#DISPLAYING SIMILAR IMAGE
for elem in indices[0]:
url = data["rows"][elem]["row"]["image"]["src"]
image = Image.open(requests.get(url, stream=True).raw)
width = 300
ratio = (width / float(image.size[0]))
height = int((float(image.size[1]) * float(ratio)))
img = image.resize((width, height), Image.Resampling.LANCZOS)
display(img)
#DISPLAYING SIMILAR IMAGE for elem in indices[0]: url = data["rows"][elem]["row"]["image"]["src"] image = Image.open(requests.get(url, stream=True).raw) width = 300 ratio = (width / float(image.size[0])) height = int((float(image.size[1]) * float(ratio))) img = image.resize((width, height), Image.Resampling.LANCZOS) display(img)
#DISPLAYING SIMILAR IMAGE
for elem in indices[0]:
url = data["rows"][elem]["row"]["image"]["src"]
image = Image.open(requests.get(url, stream=True).raw)
width = 300
ratio = (width / float(image.size[0]))
height = int((float(image.size[1]) * float(ratio)))
img = image.resize((width, height), Image.Resampling.LANCZOS)
display(img)

SigLIP 2 模型的输出结果

三个类似的女皮包

从 SigLIP 2 模型的输出结果可以看出,所有检索到的袋图像都与我们查询到的袋很接近。

测试 SigLIP 模型

现在让我们用 SigLIP 模型来检查同样的问题。我们只需在步骤 2 中使用以下代码加载该模型

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import torch
import faiss
from torchvision import transforms
from PIL import Image
from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer
import numpy as np
import requests
device = torch.device('cuda' if torch.cuda.is_available() else "cpu")
model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device)
processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384")
tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")
import torch import faiss from torchvision import transforms from PIL import Image from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer import numpy as np import requests device = torch.device('cuda' if torch.cuda.is_available() else "cpu") model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device) processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384") tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")
import torch
import faiss
from torchvision import transforms
from PIL import Image
from transformers import AutoProcessor, SiglipModel, AutoImageProcessor, AutoModel, AutoTokenizer
import numpy as np
import requests
device = torch.device('cuda' if torch.cuda.is_available() else "cpu")
model = SiglipModel.from_pretrained("google/siglip-base-patch16-384").to(device)
processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-384")
tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-384")

其他后续步骤可按原样重新运行。

SigLIP 模型的输出结果

颜色不一致的女皮包

从 SigLIP 模型的输出结果可以看出,有两张检索到的袋子图像与 SigLIP 2 模型检索到的袋子图像相似。但是,从 SigLIP 模型中检索到的第三张图像与我们的查询图像并不接近,因为它与棕褐色并不接近。

让我们用这张输入图像来检查另一个查询。

一双红色帆布鞋

SigLIP 2 模型的输出

检索出款式类似的女帆布鞋

从 SigLIP 2 模型的输出可以看出,所有检索到的女鞋图像都是帆布鞋,与我们查询的鞋子很接近。

SigLIP 模型的输出结果

部分结果特征与输入不一致

从 SigLIP 模型的输出可以看出,有两张检索到的鞋子图片与 SigLIP 2 模型检索到的鞋子图片相似。但是,从 SigLIP 模型中检索到的第三张图片与我们的查询图片并不完全相同,因为它不是帆布鞋。

小结

SigLIP 2 代表着图像-文本检索和视觉语言模型的发展向前迈进了一大步。其先进的功能,如动态分辨率和改进的细粒度语义理解,使其成为在各种应用中增强图像搜索能力的强大工具。SigLIP 2 解决了以前模型的主要局限性,提供了更准确、更高效的图像检索,使其成为电子商务、数字资产管理和社交媒体等领域的宝贵资产。

主要启示

  • 由谷歌 DeepMind 开发的 SigLIP 2 在其前身的基础上进行了改进,采用了统一的训练方法和基于西格码的损失,提供了更准确、更高效的图像文本检索和零镜头分类。
  • CLIP 使用的 Softmax 函数在处理细微的图像-文本比较时可能会遇到困难,而 SigLIP 2 则不同,它采用了更有效的 sigmoid 损失函数,可独立处理每一对图像-文本,从而提高了性能。
  • SigLIP 2 引入了 NaFlex 变体,允许模型有效处理不同的图像分辨率和长宽比,使其成为 OCR 和文档处理等任务的理想选择。
  • SigLIP 2 通过使用自馏分和增强型训练技术(如全局-局部损失和屏蔽预测损失),提供了更好的语义理解,使其更擅长捕捉详细的视觉特征。
  • SigLIP 2 配备了位置感知字幕(LocCa)解码器,可执行接地字幕和预测边界框坐标等任务,进一步增强了准确搜索和检索图像的能力。

评论留言