用深度學(xué)習(xí)模型玩轉(zhuǎn)國(guó)旗相似性搜索——ViT、CLIP、DINO-v2和BLIP-2的實(shí)戰(zhàn)講解
你知道当你深夜清醒时,那些奇怪的想法会突然冒出来吗?我躺在床上,突然想到——“印尼和摩纳哥的国旗是不是完全一样?”这就是我几周前的经历。我一开始只是逛维基百科,结果却一头栽进一个利用AI比较各国国旗的项目里。
所以,我到底做了什么?我决定使用不同的AI模型,比如ViT、CLIP、BLIP、EfficientNet、DINO-v2,以及经典的VGG16模型,来比较图片并查看它们的相似之处。最初从国旗开始,但最终这让我深入研究了更广泛的图像相似性概念。
直观:图像视作数据空间中的向量图像相似性搜索背后的想法其实很简单:一张图片可以被表示为高维空间中的一个向量。当两张图片相似时,它们的向量位置应相近。我们通过测量角度(或称余弦相似度)来判断这些向量有多相似。如果角度很小,图片就非常相似;如果角度很大,图片则差异明显。这就像在公寓楼中找邻居,但要抽象得多。
图像的向量表示,
遇见模型们我用了几种深度学习模型,每种都有自己的优势和一些独特的小毛病。
- CLIP(对比语言和图像预训练):由OpenAI构建,用于学习将图像与文本进行匹配。选择它还不错,对于我们的相似度搜索来说,它是一个不错的选择。
- ViT(视觉Transformer):ViT通过将图像视为序列处理,革新了图像处理的方式,类似于Transformer处理文本的方式。
- BLIP:一个旨在对齐视觉和文本内容的视觉-语言模型。
- EfficientNet:因其高效性而闻名,非常适合进行图像识别任务。
- DINO:一种自监督的Transformer模型,擅长从图像中提取特征。
- VGG16:一个经典的卷积神经网络(CNN),虽然存在多年,但在图像识别任务中依然表现出色。
在这里我不深入讲解每个模型的架构细节,但如果你想了解更多,可以去看看这篇文章这里。
第一步:数据整理我从维基百科上获取了国旗的图片,将世界各国的国旗图片整合成了一个数据集。
import pandas as pd
# 导入pandas库并将名为'national_flags.csv'的CSV文件读入名为flags_df的数据框,然后打印数据框
flags_df = pd.read_csv('national_flags.csv')
print(flags_df)
从维基百科抓取数据表格
第二步:特征抽取拿到旗帜后,真正的乐趣开始了:提取特征值。每个模型都会将标志图像转换为特征向量。这就好比把图像转化为一组数字,这些数字捕捉了图像的特点。
在这次实验中,我将使用Hugging Face的transformer库来提取词嵌入。
- 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]
# 沿着维度2和3计算平均值
embedding = torch.mean(embedding, dim=[2,3])
- ViT: 使用其Transformer架构中第一个token的最终隐藏状态来捕捉局部和整体的视觉信息。
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: 通过自监督学习生成嵌入向量,利用第一个 token 来捕捉对象相关的细节。
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: 使用视觉和语言模型,通过其查询导向的变压器(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: 这种模型通过一系列卷积层输出旗帜嵌入,强调多层次的图像表示。
model = models.vgg16(pretrained=True)
model.eval() # 模型设为评估模式
batch_t = torch.unsqueeze(img, 0)
with torch.no_grad():
embedding = model(batch_t) # 获取输入图像的特征表示
提取所有标志
接下来,我们将依次将每个模型应用于数据框中的旗帜图像,提取它们的特征向量。这些特征向量将作为相似性分析的基础。
# 提取所有旗帜的特征信息
flags_df['features'] = flags_df['Flag Image'].应用(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个最相似的国家(K+1以排除该国家自身)
distances, top_k_idx = index.search(input_embedding, 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:评估相似性和偏见问题
事情在这里变得有趣起来——也带了几分个人色彩。图像相似度的判断非常主观。
我觉得相似的两个旗帜可能不会让别人有同样的感觉。在评估时,我发现自己的偏见慢慢渗透进来,影响了我对结果的看法。
为了应对这个问题,我发现了一篇Britannica上的文章,比较了不同国家的相似国旗,并决定用它作为参考标准。挑战是,我能否利用AI复现这些结果?
为了试一下,我将同一套旗帜图片输入所有模型,并比较了它们的推荐结果。结果如下:
测试1: Chad和罗马尼亚
所有这些模型都将罗马尼亚作为最匹配的选项。
类似于乍得旗帜
测试2:塞内加尔和马里
只有CLIP和VGG16正确识别了“马里”,但令人惊讶的是,所有模型都将“喀麦隆”选作最接近的匹配项 (这让我们比《大不列颠百科全书》还要更胜一筹)。
类似塞内加尔国旗的旗帜
测试3:印尼和摩纳哥
啊,那对让我晚上睡不着觉的组合。所有模型都返回了“摩纳哥”,但不是每次都名列前茅。
像印尼那样的旗帜
测试4:澳大利亚和新西兰的部分
所有模型都正确识别了新西兰,但这让我感到好奇,有些模型也将图瓦卢识别为匹配的国家——不得不说,真是个有趣的发现!
像澳大利亚那样的旗帜
为了完成一些作业题,我给你们留了几对,你们自己试试。
- 爱尔兰和象牙岸
- 挪威和冰岛
- 委内瑞拉、厄瓜多尔,以及哥伦比亚
- 卢森堡和荷兰
- 斯洛文尼亚、俄国,以及斯洛伐克
试试这个用于国旗相似度搜索的 Streamlit 演示应用:Streamlit演示应用
我已经把这个应用部署到Streamlit上了,你可以看看你的结果和我的是不是一样。
小心:相似模型中的上下文偏倚某些模型,如CLIP,可能会无意中根据上下文关系而非视觉相似性来分组国家。例如,印度和巴基斯坦可能在相似性搜索中被置于接近的位置,不是因为它们国旗的设计相似,而是因为它们的地缘政治联系以及在国际新闻和讨论中经常被一同提及。同样,模型可能会将以色列和巴勒斯坦以及乌克兰和俄罗斯分组在一起,尽管它们的国旗差异很大,因为这些国家在政治话语、媒体报道和历史背景中经常被联系起来。
相似性模型中的上下文偏见
这种情况发生是因为像CLIP这样的模型在图像和文本上进行训练,不仅能识别这些更广泛的关联,而不仅仅是局限于视觉数据。这一现象表明了,这些模型不仅反映了视觉上的相似性,还反映了其训练数据中所包含的上下文偏见。
结束语这个项目是一次有趣的探索之旅,研究不同的AI模型如何处理图像相似度。每个模型都带来了独特的优势,虽然没有一个模型是完美的,但各有千秋,一起提供了机器如何理解视觉信息的有趣见解。无论你是在处理旗帜、标志还是任何其他类型的图像,这些模型都适用于各种任务,如特征提取任务、图像比较和相似搜索等。如果您想了解更多细节,可以访问下面链接的GitHub仓库查看代码和结果。
最终来说,由AI驱动的相似性检索不仅限于旗帜,还可以应用于商标、艺术品、设计元素,甚至更复杂的任务如面部识别或医学成像。可能性无穷无尽。
完整的源代码以及Jupyter Notebook可在GitHub仓库中查看。如果您有任何改进建议或其它想法,欢迎随时与我们联系。
共同學(xué)習(xí),寫(xiě)下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章
100積分直接送
付費(fèi)專(zhuān)欄免費(fèi)學(xué)
大額優(yōu)惠券免費(fèi)領(lǐng)