基于ViT、CLIP、EfficientNet、DINO-v2和BLIP-2构建AI图像相似性搜索

概述

为了深入探究图像相似性,这里决定采用多种先进的人工智能模型进行分析。这些模型包括视觉变换器(ViT)、对比语言-图像预训练模型(CLIP)、基于双向编码器表示的图像描述模型(BLIP)、高效网络(EfficientNet)、DINO-v2以及经典的卷积神经网络VGG16。

通过这些模型,能够从不同角度和层面捕捉到图像之间的相似之处。例如,视觉变换器(ViT)通过将图像分割成多个小块,并利用自注意力机制来分析这些图像块之间的关系,从而揭示出图像的内在相似性;对比语言-图像预训练模型(CLIP)则通过将图像与文本描述相结合,利用大规模数据集进行预训练,从而能够理解图像内容的语义相似性;而基于双向编码器表示的图像描述模型(BLIP)则专注于生成准确的图像描述,并通过比较不同图像的描述来评估它们的相似度.

此外,高效网络(EfficientNet)以其卓越的计算效率和强大的特征提取能力,在图像相似性分析中也表现出色;DINO-v2作为一种自监督学习模型,能够通过对比学习的方式,无需人工标注数据,即可学习到图像的深层特征表示,从而实现对图像相似性的有效评估;而经典的卷积神经网络VGG16则凭借其在图像处理领域的广泛应用和成熟的技术,为图像相似性分析提供了可靠的基准和参考.

直觉:图像作为空间中的向量

图像相似性搜索的核心是一个简单的想法:图像可以表示为高维空间中的向量。当两张图像相似时,它们的向量在这个空间中应该占据相似的位置。我们可以通过测量角度(或余弦相似度)来确定这些向量的相似程度。如果角度小,图像就接近(相似)。如果角度大,图像就相距较远(不同)。这就像在公寓大楼里寻找你的邻居,但方式要抽象得多。
图像的向量嵌入

认识模型

===============
这里使用了几个深度学习模型,每个模型都有其优点和特点:

  • CLIP(对比语言 - 图像预训练):由OpenAI构建,它学习将图像与文本匹配。对于我们的相似性搜索来说,这不是一个坏选择。
  • ViT(视觉Transformer):ViT通过将图像视为序列,类似于Transformer处理文本的方式,彻底改变了图像处理。
  • BLIP:一种视觉 - 语言模型,专注于对齐视觉和文本内容。
  • EfficientNet:以其效率而闻名,该模型非常适合图像识别任务。
  • DINO:一种自监督Transformer模型,擅长从图像中学习特征。
  • VGG16:一个经典的卷积神经网络(CNN),已经存在多年,在图像识别任务中仍然表现出色。

步骤1:数据准备

维基百科上抓取了国旗图像,将世界各国的国旗变成了一个数据集。

import pandas as pd
flags_df = pd.read_csv('national_flags.csv')  
print(flags_df)

从维基百科抓取后的DataFrame

步骤2:特征提取

有了国旗后,真正有趣的部分开始了:提取特征。每个模型都获取国旗图像并将其转换为特征向量。这就像是将一张图像翻译成一个数字列表,这些数字封装了它的特征。

在这个实验中,我将使用Huggingface变压器库来提取嵌入。

  • EfficientNet:通过平均最后一个隐藏层输出的空间维度来提取国旗特征,专注于细粒度模式。
image_processor = AutoImageProcessor.from_pretrained("google/efficientnet-b7")
model = EfficientNetModel.from_pretrained("google/efficientnet-b7")
# 准备输入图像
inputs = image_processor(img, return_tensors='pt')
with torch.no_grad():
    outputs = model(**inputs, output_hidden_states=True)
embedding = outputs.hidden_states[-1]
embedding = torch.mean(embedding, dim=[2,3])
  • ViT:使用其Transformer架构中第一个标记的最后隐藏状态,捕获局部和全局视觉特征。
image_processor = AutoImageProcessor.from_pretrained("google/vit-large-patch16-224-in21k")
model = ViTModel.from_pretrained("google/vit-large-patch16-224-in21k")
# 准备输入图像
inputs = image_processor(img, return_tensors='pt')
with torch.no_grad():
    outputs = model(**inputs)
embedding = outputs.last_hidden_state
embedding = embedding[:, 0, :].squeeze(1)
  • DINO-v2:通过专注于自监督学习生成嵌入,利用第一个标记捕获以对象为中心的细节。
image_processor = AutoImageProcessor.from_pretrained('facebook/dinov2-base')
model = AutoModel.from_pretrained('facebook/dinov2-base')
# 准备输入图像
inputs = image_processor(img, return_tensors='pt')
with torch.no_grad():
    outputs = model(**inputs)
embedding = outputs.last_hidden_state
embedding = embedding[:, 0, :].squeeze(1)
  • CLIP:结合图像和文本嵌入,使用图像特征理解视觉概念以及来自配对文本的上下文数据。
image_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
# 准备输入图像
inputs = image_processor(images=img, return_tensors='pt', padding=True)
with torch.no_grad():
    embedding = model.get_image_features(**inputs)
  • BLIP-2:采用视觉 - 语言模型,通过其面向查询的Transformer(Q-Former)提取特征,以捕获图像语义和关系。
image_processor = Blip2Processor.from_pretrained("Salesforce/blip2-opt-2.7b")
model = Blip2Model.from_pretrained("Salesforce/blip2-opt-2.7b", torch_dtype=torch.float16)
inputs = image_processor(images=img, return_tensors='pt', padding=True)
print('input shape: ', inputs['pixel_values'].shape)
with torch.no_grad():
    outputs = model.get_qformer_features(**inputs)
embedding = outputs.last_hidden_state
embedding = embedding[:, 0, :].squeeze(1)
  • VGG16:一个CNN模型,通过应用一堆卷积层输出国旗嵌入,强调分层图像表示。
model = models.vgg16(pretrained=True) 
model.eval()  # 设置模型为评估模式
batch_t = torch.unsqueeze(img, 0)
with torch.no_grad():
    embedding = model(batch_t)

对所有国旗进行提取

接下来,将把每个模型应用于DataFrame中的国旗图像,提取它们的特征嵌入,这将作为相似性分析的基础。

# 提取所有国旗的特征
flags_df['features'] = flags_df['Flag Image'].apply(extract_features)

步骤3:使用FAISS进行余弦相似度计算

现在已经准备好了国旗嵌入,将使用余弦相似度来测量国旗嵌入的相似程度。余弦相似度比较两个向量的方向,使其在基于模式而不是幅度识别关系方面非常有效。这种方法在分析国旗嵌入时特别有用,因为这里关注的是国旗图案的相对形状和设计元素,而不是特征向量的绝对大小。

要实现这一点:

  • 归一化:每个特征向量都被归一化为单位长度,这样余弦相似度就可以计算为向量的点积。这确保了相似度反映向量之间的角度。
  • 使用FAISS进行相似性搜索:我们利用FAISS,一个优化用于高效相似性搜索的库,根据归一化的国旗嵌入找到最相似的前K个国家。这允许在大型国旗图像数据集上进行快速和可扩展的比较。

这种方法提供了一种更具上下文感知的相似性搜索,捕捉了国旗图案的复杂细节,并确保最相似的国旗在视觉上是有意义的。

def clean_feature_string(feature_str):
    cleaned_str = re.sub(r'[[]]', '', feature_str)  # 移除括号
    cleaned_values = np.fromstring(cleaned_str, sep=' ')  # 将值解析为numpy数组
    return cleaned_values

# 使用FAISS获取最相似的前K个国家的函数
def get_top_k_similar_countries(input_country, df, k=5):
    countries = df['Country'].values
    features = np.array([clean_feature_string(f) for f in df['features'].values])
    
    # 找到输入国家的索引
    try:
        input_idx = list(countries).index(input_country)
    except ValueError:
        return f"国家 '{input_country}' 未在数据集中找到。"
    
    input_embedding = features[input_idx].reshape(1, -1)
    # 归一化特征向量以进行余弦相似度计算
    features_normalized = features / np.linalg.norm(features, axis=1, keepdims=True)
    # 创建一个FAISS索引用于相似性搜索
    dim = features.shape[1]
    index = faiss.IndexFlatIP(dim)  
    
    # 将所有特征添加到FAISS索引中
    index.add(features_normalized)
    
    # 搜索最相似的前K个国家
    distances, top_k_idx = index.search(input_embedding, k+1)  # k+1是为了排除国家本身
    
    # 返回前K个国家及其相似度得分
    return [(countries[i], distances[0][j]) for j, i in enumerate(top_k_idx[0]) if i!= input_idx]

# 显示前5个相似国旗
top_5_countries = get_top_k_similar_countries(country, k=5)
for idx, (country, score) in enumerate(top_5_countries):
    # 从本地文件夹加载每个国家的国旗图像
    img = load_local_image(country)
    display(img)

步骤4:评估——国旗相似性和偏差

这就是事情变得有趣的地方——也有点个人化。图像相似性是一个非常主观的领域。

为了应对这个问题,在大英百科全书上找到了一篇比较不同国家相似国旗的文章,并决定将其作为基准。挑战是什么呢?我能否使用人工智能复制这些结果?

为了测试,将同一组国旗图像通过所有模型运行,并比较它们的顶级推荐。结果如下:
测试1:乍得和罗马尼亚
所有模型都将“罗马尼亚”作为最佳匹配返回。
与乍得相似的国旗
测试2:塞内加尔和马里
只有CLIP和VGG16正确找到了“马里”,但令人惊讶的是,所有模型都选择“喀麦隆”作为最接近的匹配(比大英百科全书更进一步)。
与塞内加尔相似的国旗
测试3:印度尼西亚和摩纳哥
啊,这就是让我夜不能寐的一对。所有模型都返回了“摩纳哥”,尽管不是每次都是最高分。
与印度尼西亚相似的国旗
测试4:澳大利亚和新西兰
所有模型都正确识别了新西兰,但引起我注意的是,一些模型还将图瓦卢标记为匹配——一个有趣的发现!
与澳大利亚相似的国旗

注意:相似性模型中的上下文偏差

一些模型,如CLIP,可能会无意中根据上下文关系而不是视觉相似性对国家进行分组。例如,在相似性搜索中,印度和巴基斯坦可能会被放在一起,不是因为它们的国旗设计,而是因为它们的地缘政治联系以及在全球新闻和讨论中经常一起被提及。同样,模型可能会将以色列和巴勒斯坦或乌克兰和俄罗斯分组,即使它们的国旗有很大差异,因为这些国家在政治话语、媒体报道和历史背景中经常联系在一起。
相似性模型中的上下文偏差1
相似性模型中的上下文偏差2
这是因为像CLIP这样在图像和文本上都进行训练的模型可以捕捉到这些超出视觉数据的更广泛的关联。这种现象突出了模型可能不仅反映视觉相似性,还反映了其训练数据中存在的上下文偏差。

结论

本项目是对不同人工智能模型如何处理图像相似性的一次有趣探索。每个模型都有其独特的优势,虽然没有一个是完美的,但它们共同提供了关于机器如何解释视觉数据的有趣见解。无论你是在处理国旗、标志还是任何其他类型的图像,这些模型都可以在无数应用中用于特征提取、图像比较和相似性搜索等任务。如果你对细节感兴趣,可以查看下面链接的GitHub仓库中的代码和结果。

最终,人工智能驱动的相似性搜索在国旗之外有广泛的潜在应用——想想标志、艺术品、设计图案,甚至更复杂的任务,如面部识别或医学成像。可能性是无穷的。

原文地址:https://medium.com/@tapanbabbar/build-an-image-similarity-search-with-transformers-vit-clip-efficientnet-dino-v2-and-blip-2-5040d1848c00

### CLIP DINO-V2 的特性与使用差异 #### 特性对比 CLIP (Contrastive Language–Image Pre-training) 是一种多模态模型,能够理解图像文本之间的关系。通过大规模数据集上的联合嵌入空间学习,使得对于给定的一张图片可以找到最匹配的文字描述[^1]。 相比之下,DINO-v2 则是一个纯粹的视觉表征学习框架,它不依赖于任何文本信息来进行训练。此模型采用了自监督的方式,在无标签的大规模图像集合上进行预训练,从而获得强大的通用特征表达能力[^2]。 #### 使用场景区别 当涉及到跨模态的任务时,比如检索带有特定说明的照片或是生成对应某幅画作的艺术风格解释等内容创作方面的工作,则更适合选用像 CLIP 这样的具备自然语言处理功能的工具来完成;而对于那些只需要关注纯视觉输入的应用场合——例如物体检测、姿态估计等计算机视觉领域内的经典难题,则可能更倾向于采用类似于 DINO-v2 的解决方案。 #### 实现方式的不同 在实现层面来看,由于CLIP的设计初衷是为了连接两个不同类型的感知域(即文字世界同图形宇宙),因此其架构内部必然存在着负责各自领域的子模块,并且这两个部分之间还存在交互机制以便同步调整参数权重以优化最终效果。而DINO-v2作为单一模式下的深度神经网络结构,主要侧重于如何有效地从未标注的数据集中挖掘有用的信息并将其转化为可供迁移使用的知识资产。 ```python import torch from transformers import CLIPProcessor, CLIPModel from PIL import Image # 加载CLIP模型用于图文关联任务 model_clip = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") processor_clip = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") image_path = "example_image.jpg" text_query = ["a photo of a cat", "an image containing dog"] inputs = processor_clip(text=text_query, images=Image.open(image_path), return_tensors="pt", padding=True) outputs = model_clip(**inputs) logits_per_image = outputs.logits_per_image # this is the image-text similarity score probs = logits_per_image.softmax(dim=-1).cpu().detach().numpy() # 获取概率分布 print(probs) # 对比之下加载DINO-v2模型仅针对图像特征提取 dino_model_name = 'facebook/dinov2_vits14' dinov2 = torch.hub.load('facebookresearch/dinov2', dino_model_name) img_tensor = transform(Image.open(image_path)) # 假设transform已经定义好 with torch.no_grad(): img_features = dinov2(img_tensor.unsqueeze(0)) print(img_features.shape) # 输出特征向量维度 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值