在本文中,我将比较 EfficientNet 、ViT 、DINO-v2 、CLIP 和 BLIP-2 的视觉嵌入,并使用 Flickr 数据集 进行图像相似性搜索。我将主要使用 Huggingface 和 Faiss 库来实现。首先,我将简要介绍每种深度学习模型。接下来,我将向大家展示代码实现和对比结果。
1. EfficientNet、ViT、DINO-v2、CLIP、BLIP-2简介
在本节中,我将介绍用于实验的几种深度学习模型。请注意,我使用的嵌入(embedding)和特征(feature)等词含义相同。我使用它们只是为了符合本文的描述。让我们深入了解它们!
EfficientNet
EfficientNet是一种卷积神经网络,侧重于在保持计算效率的同时实现准确性。它被归类为监督学习。
如下图所示,根据模型大小,有几种变体,分别称为 B0 至 B7。模型尺寸越大,精度越高。
正如你所看到的,它们对 ImageNet 的准确度相当高,但与最近的巨型基础模型相比,它们的模型尺寸非常紧凑。在本博客中,我将使用 EfficientNet-B7 进行实验。将提取的嵌入是最后一个隐藏状态的输出,因为深层比浅层拥有更多的语义信息。
视觉转换器(ViT)
Vision Transformer是第一篇成功将 Transformer 架构应用于计算机视觉领域的论文,由谷歌开发。它也被归类为监督学习。它将输入图像分割成若干补丁,并将其输入变换器编码器。这些补丁相当于自然语言处理设置中的标记。在分类任务中,ViT 引入了一个名为 “类标记”(class-token)的标记,该标记在最后一个注意力层的输出中包含整个图像表示。架构图如下所示。
与 NLP Transformer类似,它需要使用大型数据集进行预训练,并根据下游任务进行微调。与 CNN 相比,它的一个优势是由于具有自我注意功能,可以利用图像的全部信息。与 EfficientNet 一样,模型规模越大,能力就越强。
正如你所看到的,较大的型号比效能网更精确。在本文中,我将使用 ViT-Large。将提取的嵌入是 class-token 的输出,因为它拥有整个图像的语义信息。
DINO-v2
DINO-v2 是由 Meta 公司开发的用于生成计算机视觉通用视觉特征的基础模型。作者将自监督方法应用于 ViT 架构,以理解图像和像素级别的图像特征;因此,DINO-v2 可以执行任何计算机视觉任务,如分类或分割。在体系结构方面,DINO-v2 基于其前身 DINO,DINO 是 “无标签知识提炼 ”的缩写,如下图所示。
DINO 有两个网络:学生网络和教师网络。它采用共同蒸馏法,即学生和教师网络具有相同的架构,蒸馏法在训练过程中双向应用,即教师对学生和学生对教师。需要注意的是,学生对教师的蒸馏使用的是学生网络输出的平均值。
在 DINO-v2 中,作者更新了训练方法,增加了一些损失和正则化。此外,他们还策划了一个高质量的数据集,以获得更高质量的图像特征。
在实验中,我们将使用类标记符号的输出,因为它们与 ViT 一样拥有完整的图像语义信息。
CLIP
CLIP 是 OpenAI 开发的改变游戏规则的多模态模型之一。它被归类为弱监督学习,基于 Transformer 架构。得益于其独特的架构,它能够实现零镜头图像分类。其架构如下所示。
CLIP 架构包含文本和图像编码器。它通过对比损失(contrastive loss)对文本和图像特征进行对齐,从而获得多模态能力。因此,它可以共享文本和图像特征之间的相同特征空间,并通过找到最相似的文本特征来实现零镜头图像分类,如上图“用于零镜头预测”。
CLIP 编码器基于Transformer。因此,我们将在图像编码器中使用类标记的输出,同样也是 ViT。
BLIP-2
BLIP-2 是 SalesForce 于 2023 年开发的开源多模态模型。它被归类为监督学习,基于 Transformer 架构。它主要利用预先训练好的大型模型(如 FlanT5 和 CLIP)来实现高效训练(因为以一般预算很难从头开始训练大型模型)。
BLIP-2 包括两个阶段。第一阶段使用多种损失(如图像-文本匹配、图像-文本对比损失和图像-环绕文本生成)训练 Q-Former 对文本特征和来自预训练图像编码器的图像特征进行对齐。第二阶段再次训练 Q-Former,使其特征空间与 FlanT5 等大型语言模型保持一致。因此,Q-Former 可以同时理解来自文本和图像的特征。
顾名思义,Q-Former 架构基于 Transformer。我们将使用 Q-Former 的输出作为特征提取层。
2. EfficientNet、ViT、DINO-v2、CLIP 和 BLIP-2 在图像相似性搜索方面的嵌入比较
在本节中,我们将比较 EfficientNet、ViT、DINO-v2、CLIP 和 BLIP-2 的图像相似性搜索结果。这些模型的架构和训练损失各不相同。会有什么不同呢?让我们从设置环境开始。
环境设置
我使用的是带有 Python 3.10 的 conda 环境。我在 Ubuntu 20.04 上使用 cuda 11.0、16 GB GPU 和 16 GB 内存进行了实验。
conda create -n transformers-env python=3.10 -ycreate -n transformers-env python=3.10 -y
conda activate transformers-env
接下来,我们需要通过 conda 和 pip 安装下面的库。
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia-cuda=11.8 -c pytorch -c nvidia
conda install -c pytorch faiss-cpu=1.8.0
conda install -c conda-forge pandas
pip install transformers
准备工作已经完成!现在,让我们执行代码。我们将使用 Faiss 库 来测量图像相似性,以进行图像相似性搜索。Faiss 是一个基于近似近邻搜索算法的高效相似性搜索库。此外,我们还将使用 Flickr30k 数据集进行实验。在直接进入图像相似性搜索之前,我们将探讨如何从每个模型中提取嵌入(特征)。
从每个模型中提取特征
在本实验中,我将使用 Huggingface 变换器库来提取嵌入。与天真的 Pytorch 实现相比,我们可以轻松提取隐藏状态。这部分代码会检查输入和输出维度,因此我们将在 CPU 上运行它们。
高效网络
效能网络的特征提取代码如下所示。
import torch
from transformers import AutoImageProcessor, EfficientNetModel
# load pre-trained image processor for efficientnet-b7 and model weight
image_processor = AutoImageProcessor.from_pretrained("google/efficientnet-b7")
model = EfficientNetModel.from_pretrained("google/efficientnet-b7")
# prepare input image
inputs = image_processor(test_image, return_tensors='pt')
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
embedding = outputs.hidden_states[-1]
print('embedding shape: ', embedding.shape)
embedding = torch.mean(embedding, dim=[2,3])
print('after reducing: ', embedding.shape)
### input shape: torch.Size([1, 3, 600, 600])
### embedding shape: torch.Size([1, 640, 19, 19])
### after reducing by taking mean: torch.Size([1, 640])
首先,我们需要准备一个输入。预先定义的 EfficientNet 图像处理器会自动将输入形状处理为(batch_size, 3, 600, 600)。经过模型处理后,我们可以得到带有隐藏状态的输出。最后一个隐藏状态的维数为 (batch_size, 640, 19, 19),因此我们可以对得到的嵌入应用减均值处理。
ViT
对于 ViT 的特征提取,提取代码如下所示。
# load pre-trained image processor for ViT-large and model weight
image_processor = AutoImageProcessor.from_pretrained("google/vit-large-patch16-224-in21k")
model = ViTModel.from_pretrained("google/vit-large-patch16-224-in21k")
# prepare input image
inputs = image_processor(test_image, return_tensors='pt')
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
outputs = model(**inputs)
embedding = outputs.last_hidden_state
embedding = embedding[:, 0, :].squeeze(1)
print('embedding shape: ', embedding.shape)
### input shape: torch.Size([1, 3, 224, 224])
### embedding shape: torch.Size([1, 1024])
同样,预定义的 ViT 图像处理器会自动将输入形状处理为(batch_size, 3, 224, 224)。最后一个隐藏状态有(batch_size, 197, 1024)个维度,我们只需要类标记,因此提取第二个维度的第一个索引(197)。
DINO-v2
DINO-v2 基于 ViT,因此基本代码几乎相同。不同之处在于我们为 DINO-v2 加载了图像处理器和模型。提取代码如下所示。
# load pre-trained image processor for DINO-v2 and model weight
image_processor = AutoImageProcessor.from_pretrained('facebook/dinov2-base')
model = AutoModel.from_pretrained('facebook/dinov2-base')
# prepare input image
inputs = image_processor(images=test_image, return_tensors='pt')
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
outputs = model(**inputs)
embedding = outputs.last_hidden_state
embedding = embedding[:, 0, :].squeeze(1)
print('embedding shape: ', embedding.shape)
### input shape: torch.Size([1, 3, 224, 224])
### embedding shape: torch.Size([1, 1024])
基本上,我们使用相同的图像处理器。预先定义的 ViT 图像处理器会自动将输入形状处理为(batch_size, 3, 224, 224)。最后一个隐藏状态有(batch_size, 197, 1024)个维度,我们只需要类标记,因此提取第二个维度的第一个索引(197)。
CLIP
CLIP 也是基于 ViT,因此过程相同。huggingface transformers 库已经有了 CLIP 的特征提取方法,因此实现起来更简单。
# load pre-trained image processor for CLIP and model weight
image_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
# prepare input image
inputs = image_processor(images=test_image, return_tensors='pt', padding=True)
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
outputs = model.get_image_features(**inputs)
print('embedding shape: ', outputs.shape)
### input shape: torch.Size([1, 3, 224, 224])
### embedding shape: torch.Size([1, 512])
我们使用相同的图像处理器。预先定义的 ViT 图像处理器会自动将输入形状处理为(batch_size, 3, 224, 224)。get_image_features 方法可以提取给定图像的嵌入,输出维度为(batch_size, 512)。它与 ViT 和 DINO-v2 不同。
BLIP-2
我们可以从 ViT 和 Q-Former 的输出中提取图像嵌入。在这种情况下,Q-Former 输出可以同时包含图像和文本的语义,因此我们将使用它。
processor = Blip2Processor.from_pretrained("Salesforce/blip2-opt-2.7b")"Salesforce/blip2-opt-2.7b")
model = Blip2Model.from_pretrained("Salesforce/blip2-opt-2.7b", torch_dtype=torch.float16)
# prepare input image
inputs = processor(images=test_image, return_tensors='pt', padding=True)
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
outputs = model.get_qformer_features(**inputs)
print('embedding shape: ', outputs.shape)
我们使用的 BLIP-2 处理器可以处理图像和文本输入。它会自动将图像输入形状处理为(batch_size, 3, 224, 224)。我们可以使用 get_qformer_features 提取 Q-Former 输出,输出维度为(batch_size, 32, 768)。我们通过取平均值来减少输出,嵌入维度将为(batch_size, 768)。
图像相似性搜索
只需几行代码,我们就能使用 Faiss 接口轻松实现图像相似性搜索。我们假设有一个名为 “特征 ”的变量。具体过程如下:
我们可以选择如何测量向量之间的距离,例如欧氏距离或余弦相似度。在本文中,我们使用余弦相似度。伪代码可编写如下。
# convert features type to np.float32
features = features.astype(np.float32)
# get embedding dimension
vector_dim = features.shape[1]
# register embedding to faiss vector store
index = faiss.IndexFlatIP(vector_dim)
faiss.normalize_L2(features)
index.add(features)
# For vector search, we just call search method.
top_k = 5
faiss.normalize_L2(embed)
distances, ann = index.search(embed, k=top_k)
现在,比较图像相似性搜索结果的所有前提条件都已具备。
图像相似性搜索结果比较
在本节中,我将比较使用五种模型进行图像相似性搜索的结果。在数据集方面,我使用了从 Flickr30k 中随机抽取的 10k 张图片。我为每个模型定制了一个管道,以实现批量特征提取。
3637013.jpg" 的结果如下。
这种情况比其他图像要简单得多,因此所有模型都能提取出类似的语义图像。
3662865.jpg" 的结果如下。
在这种情况下,DINO-v2 和 CLIP 可以捕捉到 “铲雪 ”的语义,但其他模型有时只能捕捉到 “雪”。
440375442.jpg" 的结果如下。
EfficientNet 和 ViT 可能会将工作服误解为手术服,因此无法捕捉目标图像的语义。DINO-v2 可以理解 “垃圾和穿工作服的人 ”的语义,CLIP 专注于穿工作服的人,而 BLIP2 则专注于垃圾。我认为 DINO-v2、CLIP 和 BLIP2 都能捕捉到语义。
1377428277.jpg" 的结果如下。
这幅图的语义是 “街上有很多人在欣赏什么节日或街头表演"。EfficientNet 和 ViT 专注于雨伞,因此无法捕捉到语义。另一方面,DINO 的重点是婴儿车,在性能方面落后一步。CLIP 尝试捕捉节日和街道部分,但也落后了一步。BLIP2 可以捕捉街头表演和婴儿车。
57193495.jpg" 的结果如下。
在这种情况下,EfficientNet、ViT 和 CLIP 有时可以捕捉到 “女扮男装、粉墨登场 ”的语义。但是,它们的功能相对不足。相比之下,DINO-v2 和 BLIP2 可以捕捉到 “着装或戏装 ”的语义。
1393947190.jpg" 的最后一次图像搜索结果如下。
不同的架构、CNN 和 Transformer 会产生不同的结果。EfficientNet 可能侧重于图像的白色和褐色,而其他模型则可以捕捉到 “人在缫丝 ”的语义。CLIP 可能侧重于传统手工艺品,但其他模型也能捕捉到其语义。
综上所述,我们得出以下结论。