使用DINOv2嵌入进行图像分类

在本教程中,将探讨如何利用DINOv2嵌入和C-支持向量分类(SVC)线性分类模型来对图像进行分类。教程结束时,将在MIT室内场景识别数据集上训练一个模型,该模型能够对图像中的场景进行分类。

开始之前

本教程附带一个交互式笔记本,可以用来跟随指南。建议在GPU上运行DINOv2,因为GPU可以显著加速训练过程。如果没有GPU,可以在Google Colab上复制笔记本或创建一个新的。Google Colab为笔记本提供了GPU资源。

步骤1:安装依赖

在开始构建分类模型之前,需要将一些依赖项导入到项目中。如果还没有安装numpy、opencv-python、scikit-learn、TQDM和PyTorch,请使用以下命令安装它们:

pip install torch numpy opencv-python scikit-learn

还需要安装roboflow pip包,将使用它来加载项目的数据:

pip install roboflow

接下来,让将所需的依赖项导入到项目中:

import numpy as np import torch import torchvision.transforms as T from PIL import Image import os import cv2 import json import glob from tqdm.notebook import tqdm

步骤2:加载MIT场景识别数据集

安装了项目所需的依赖项后,可以开始下载项目的数据了。在本指南中,将使用MIT室内场景识别数据集(尽管可以在项目中使用任何想要的数据集。这个数据集的一个版本可以在Roboflow Universe上找到,这是一个在线存储库,包含超过200,000个计算机视觉数据集。需要一个免费的Roboflow账户才能从Roboflow Universe下载数据集。

创建一个新的Python文件,用于本项目,然后添加以下代码:

from roboflow import Roboflow rf = Roboflow.login() project = rf.workspace("popular-benchmarks").project("mit-indoor-scene-recognition") dataset = project.version(5).download("folder")

已经以分类文件夹格式下载了数据集。在这种格式中,每个图像都在一个文件夹中,文件夹的名称等于与图像相关的标签。例如,训练数据集中的图像train/x/y.jpg的标签是x。

需要创建一个字典,将所有文件名映射到它们所在的文件夹名称,以便知道每个图像的标签。可以使用以下代码来实现:

cwd = os.getcwd() ROOT_DIR = os.path.join(cwd, "MIT-Indoor-Scene-Recognition-5/train") labels = {} for folder in os.listdir(ROOT_DIR): for file in os.listdir(os.path.join(ROOT_DIR, folder)): if file.endswith(".jpg"): full_name = os.path.join(ROOT_DIR, folder, file) labels[full_name] = folder files = list(labels.keys())

步骤3:计算图像嵌入

将使用线性分类模型SVC来对图像进行分类。在能够训练模型之前,需要为模型准备输入。对于这个项目,需要两个数据点:每个图像的DINOv2嵌入,以及与每个图像相关的标签。在这一步中,将为项目中的每个图像计算嵌入。

首先,让加载DINOv2模型。将使用最小的模型dino_vits14('s'表示小)。这个模型比默认的CLIP权重小3倍以上(~84 MB vs. ~300 MB)。

dinov2_vits14 = torch.hub.load("facebookresearch/dinov2", "dinov2_vits14") device = torch.device('cuda' if torch.cuda.is_available() else "cpu") dinov2_vits14.to(device) transform_image = T.Compose([T.ToTensor(), T.Resize(244), T.CenterCrop(224), T.Normalize([0.5], [0.5])])

在上述代码的最后一行,定义了一个函数,将图像转换为DINOv2接受的格式。接下来,可以编写函数来加载图像,并为图像列表中的每个图像计算嵌入:

def load_image(img: str) -> torch.Tensor: """ 加载图像并返回一个张量,该张量可以作为DINOv2的输入。 """ img = Image.open(img) transformed_img = transform_image(img)[:3].unsqueeze(0) return transformed_img def compute_embeddings(files: list) -> dict: """ 创建一个索引,其中包含指定文件列表中的所有图像。 """ all_embeddings = {} with torch.no_grad(): for i, file in enumerate(tqdm(files)): embeddings = dinov2_vits14(load_image(file).to(device)) all_embeddings[file] = np.array(embeddings[0].cpu().numpy()).reshape(1, -1).tolist() with open("all_embeddings.json", "w") as f: f.write(json.dumps(all_embeddings)) return all_embeddings

有了这些函数,可以开始为训练数据集中的图像计算嵌入了。为此,可以通过在教程中定义的文件列表通过compute_embeddings()函数。

embeddings = compute_embeddings(files)

这段代码可能需要几分钟才能运行完毕,具体取决于数据集大小。

步骤4:训练SVC分类模型

已经准备好使用嵌入和标签来拟合分类模型了:

from sklearn import svm clf = svm.SVC(gamma='scale') y = [labels[file] for file in files] print(len(embeddings.values())) embedding_list = list(embeddings.values()) clf.fit(np.array(embedding_list).reshape(-1, 384), y)

运行此代码后,得到了一个SVC模型,可以在该模型上运行推理。要使用该模型,需要:

  • 加载要分类的图像;
  • 计算与图像相关的嵌入;
  • 将嵌入传递给模型进行分类。

对于测试,可以使用数据集中的测试集或验证集中的图像。以下示例中,将对一张电梯图像进行推理:

input_file = "MIT-Indoor-Scene-Recognition-5/test/elevator/elevator_google_0053_jpg.rf.41487c3b9c1690a5de26ee0218452627.jpg" new_image = load_image(input_file) with torch.no_grad(): embedding = dinov2_vits14(new_image.to(device)) prediction = clf.predict(np.array(embedding[0].cpu()).reshape(1, -1)) print("Predicted class: " + prediction[0])

此代码将返回以下输出:

Predicted class: elevator

代码已成功对图像进行了分类!Roboflow团队在MIT室内场景识别上对SVC性能进行了基准测试,达到了0.884的准确率(与CLIP的0.883准确率相比)。

在本指南中,已经展示了如何使用DINOv2嵌入构建分类模型。从Roboflow Universe加载了MIT室内场景识别数据集,为数据集中的每个图像计算了嵌入,然后在嵌入上训练了一个SVC模型。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485