首页
/ Transformers 中的 DINOv2:用 ViT 视觉基础模型做图像分类、稠密任务与特征提取的完整实战指南

Transformers 中的 DINOv2:用 ViT 视觉基础模型做图像分类、稠密任务与特征提取的完整实战指南

2026-09-07 21:58:00作者:牧宁李

本篇指南以 DINOv2 官方模型文档 为主体,围绕 Facebook AI 发布的 DINOv2 视觉基础模型(vision foundation model)在 🤗 Transformers 仓库中的实现展开。你将学会:如何用 PipelineAutoModel 一行式完成图像分类、如何正确拆分输出得到整图 CLS 向量与逐 patch 局部特征、如何借助 torchao 对超大模型(如 giant 版本)做 int4 权重量化、如何用 torch.jit.trace 加速推理,并深入理解 Dinov2Configmodeling_dinov2.py 中的架构细节,以及 DINOv2 如何作为 Backbone 接入 DETR、MaskFormer 等下游框架。

DINOv2 是什么:自监督视觉基础模型在 Transformers 中的落地

DINOv2 是基于 ViT(Vision Transformer)构建的视觉基础模型。它继承了 ViT 将图像划分为 patch 并做自注意力的骨干结构,通过自监督预训练获得了可复用于多种下游任务的高质量视觉表示,例如图像分类(image classification)与深度估计(depth estimation)。

从模型文档可以看出,DINOv2 的工程贡献主要体现在训练层面的稳定与加速,包括:

  • 更快、更省内存的注意力实现(memory-efficient attention);
  • 序列打包(sequence packing),提高批量训练吞吐;
  • 改进的随机深度(improved stochastic depth,即按样本丢弃残差分支的正则化手段);
  • 全分片数据并行(Fully Sharded Data Parallel,FSDP),用于在大量 GPU 上稳定训练超大模型;
  • 模型蒸馏(model distillation),用教师模型引导小模型学习。

在 Transformers 仓库中,DINOv2 的完整实现位于:

模型文档中给出的实践例子主要使用 facebook/dinov2-basefacebook/dinov2-small-imagenet1k-1-layerfacebook/dinov2-giant-imagenet1k-1-layer 等公开 checkpoint(原始 DINOv2 系列 checkpoint 统一收录在 facebook 的 Dinov2 collection 下),读者可按需替换为其他规模。

快速上手:获取图像 Embedding 与分类结果

方式一:用 Pipeline(一行式)

Pipeline 是 Transformers 提供的最简 API,只需指定任务类型 "image-classification" 与模型名即可完成"加载模型 + 预处理 + 后处理"全流程:

from transformers import pipeline


pipe = pipeline(
    task="image-classification",
    model="facebook/dinov2-small-imagenet1k-1-layer",
    device=0
)

pipe("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg")

参数说明:

  • task="image-classification":将自动装配图像预处理与分类头推理;
  • model:指定在 Hub 上托管、与 HF 格式兼容的 DINOv2 分类 checkpoint;
  • device=0:显式将模型放到 0 号 GPU 上(CPU 环境可省略或改为 device=-1)。

方式二:用 AutoModel 显式组合 processor 与 model

当需要手动控制预处理与推理细节(例如拿到 logits 再自行 argmax)时,使用 AutoImageProcessor + AutoModelForImageClassification 更合适:

import requests
from PIL import Image

from transformers import AutoImageProcessor, AutoModelForImageClassification


url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)

processor = AutoImageProcessor.from_pretrained("facebook/dinov2-small-imagenet1k-1-layer")
model = AutoModelForImageClassification.from_pretrained(
    "facebook/dinov2-small-imagenet1k-1-layer",
    device_map="auto",
    attn_implementation="sdpa"
)

inputs = processor(images=image, return_tensors="pt").to(model.device)
logits = model(**inputs).logits
predicted_class_idx = logits.argmax(-1).item()
print("Predicted class:", model.config.id2label[predicted_class_idx])

值得注意的实现细节:

  • device_map="auto" 配合 .to(model.device),保证在多 GPU/CPU offload 场景下输入张量与模型参数所在设备一致;
  • attn_implementation="sdpa" 显式启用 PyTorch 的 scaled dot-product attention 加速路径。在 modeling_dinov2.py 中,Dinov2PreTrainedModel 声明了 _supports_sdpa = True_supports_flash_attn = True_supports_flex_attn = True 以及 _supports_attention_backend = True;前向时 Dinov2SelfAttention 通过 ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward) 动态选择注意力后端(见 src/transformers/models/dinov2/modeling_dinov2.py 的自注意力实现),文档头部徽标也标注了该模型同时支持 FlashAttention 与 SDPA;
  • model.config.id2label[predicted_class_idx] 将类别索引映射回可读标签(id2label 在加载分类 checkpoint 时自动填充)。

若仅需图像 Embedding 而非分类结果,可改用 AutoModel(对应裸编码器 Dinov2Model),下一节将详述其输出的结构。

理解输出结构:CLS token 与逐 patch 局部特征

DINOv2 基于 ViT,把输入图像切分为固定大小的 patch 后送入 Transformer。因此模型输出的每个 token 都对应着图像的一块空间区域。官方模型文档给出的核心拆解思路是:

  • CLS token:位于序列最前端的整图语义向量,适合分类、检索等全局任务;
  • 逐 patch 的局部 embedding:每个 token 对应输入图像中一个 patch_size × patch_size 的局部区域,适合语义分割等稠密任务。

原文档的完整示例(对 facebook/dinov2-base,patch size 为 14)如下:

from transformers import AutoImageProcessor, AutoModel
from PIL import Image
import requests

url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
image = Image.open(requests.get(url, stream=True).raw)
print(image.height, image.width)  # [480, 640]

processor = AutoImageProcessor.from_pretrained('facebook/dinov2-base')
model = AutoModel.from_pretrained('facebook/dinov2-base', device_map="auto")
patch_size = model.config.patch_size

inputs = processor(images=image, return_tensors="pt").to(model.device)
print(inputs.pixel_values.shape)  # [1, 3, 224, 224]
batch_size, rgb, img_height, img_width = inputs.pixel_values.shape
num_patches_height, num_patches_width = img_height // patch_size, img_width // patch_size
num_patches_flat = num_patches_height * num_patches_width

outputs = model(**inputs)
last_hidden_states = outputs[0]
print(last_hidden_states.shape)  # [1, 1 + 256, 768]
assert last_hidden_states.shape == (batch_size, 1 + num_patches_flat, model.config.hidden_size)

cls_token = last_hidden_states[:, 0, :]
patch_features = last_hidden_states[:, 1:, :].unflatten(1, (num_patches_height, num_patches_width))

从源码角度理解这套张量形状:

  • Dinov2PatchEmbeddings 使用 nn.Conv2d(num_channels, hidden_size, kernel_size=patch_size, stride=patch_size)(batch, 3, 224, 224) 的图像投影为 patch token(modeling_dinov2.py 中 PatchEmbeddings 实现),224×224 图像经 14×14 卷积后得到 16×16=256 个 patch;
  • Dinov2Embeddings 在 patch 序列前拼接一个可学习的 cls_token 参数,因此序列长度为 1 + 256(对应源码中 torch.cat((cls_tokens, embeddings), dim=1)),位置编码维度也是 num_patches + 1
  • Dinov2Model.forward 在编码器之后追加一层 LayerNorm,并取 sequence_output[:, 0, :] 作为 pooler_output,返回 BaseModelOutputWithPoolingmodeling_dinov2.pyDinov2Model 的实现);
  • patch_features 通过 .unflatten(1, (H, W)) 还原为 (batch, H, W, hidden) 的二维网格布局,便于后续做逐像素级稠密预测。

不同任务如何取用输出

任务类型 取用哪个部分 说明
图像分类 cls_token(第 0 个 token) 整图全局表征,进入分类头
图像检索/比对 cls_token 作为整图向量参与度量
语义分割等稠密任务 patch_features 每个 patch 的局部表征,可 reshape 回空间网格

值得一提:Dinov2ForImageClassification 的分类头并非只用 CLS token。查看 modeling_dinov2.py 中分类模型的前向代码可以看到,它把 [CLS] token 与全部 patch token 的均值向量拼接为 hidden_size * 2 维后送入 nn.Linear 分类层,即 logits = classifier(cat([cls, patch_tokens.mean(dim=1)]))——这是一种全局+局部信息融合的分类策略,对 ImageNet 等任务效果稳定。

处理高分辨率输入:位置编码插值(interpolate_pos_encoding)

文档示例中输入图像为 224×224,与预训练分辨率一致。若读者传入更高分辨率图像,Dinov2Embeddings 内置的 interpolate_pos_encoding 方法(modeling_dinov2.py)会自动处理:

  • 当 patch 数量与预训练位置编码数量一致且宽高相等时,直接复用预训练位置编码;
  • 否则将 patch 位置编码按空间网格重排,用 nn.functional.interpolate 以 bicubic 模式在 float32 精度下插值到新的 (height/patch_size, width/patch_size) 网格,再做 torch.cat 拼接回 CLS 位置编码;
  • 该方法对 torch.jit 追踪做了专门适配:追踪时始终执行插值分支,保证导出模型在动态输入尺寸下依然正确。

这意味着 Dinov2Model 天然支持任意分辨率的图像输入(patch 数整除即可),无需重新训练。

用 torchao 做 int4 权重量化,降低大模型显存负担

DINOv2 的 giant 等大版本参数量可观,权重量化(将权重表示为更低精度)可以显著降低内存占用。文档推荐的做法是使用 torchao 对权重做 int4 量化的方案(Int4WeightOnlyConfig),仅量化权重、不量化激活:

# pip install torchao
import requests
from PIL import Image
from torchao.quantization import Int4WeightOnlyConfig

from transformers import AutoImageProcessor, AutoModelForImageClassification, TorchAoConfig


url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
image = Image.open(requests.get(url, stream=True).raw)

processor = AutoImageProcessor.from_pretrained('facebook/dinov2-giant-imagenet1k-1-layer')

quant_config = Int4WeightOnlyConfig(group_size=128)
quantization_config = TorchAoConfig(quant_type=quant_config)

model = AutoModelForImageClassification.from_pretrained(
    'facebook/dinov2-giant-imagenet1k-1-layer',
    device_map="auto",
    quantization_config=quantization_config
)

inputs = processor(images=image, return_tensors="pt").to(model.device)
outputs = model(**inputs)
logits = outputs.logits
predicted_class_idx = logits.argmax(-1).item()
print("Predicted class:", model.config.id2label[predicted_class_idx])

要点:

  • 先安装 torchao# pip install torchao),并导入 Int4WeightOnlyConfig
  • Int4WeightOnlyConfig(group_size=128) 表示按 128 个权重组共享缩放因子进行分组 int4 量化;
  • 通过 TorchAoConfig 把量化配置交给 AutoModelForImageClassification.from_pretrained,加载时即完成量化,无需手动改模型结构;
  • 更适合量化收益大的大 checkpoint(文档示例即选用 facebook/dinov2-giant-imagenet1k-1-layer)。

Transformers 支持多种量化后端,各方案的完整说明可参考 Quantization 概念指南torchao 量化文档

推理加速:使用 torch.jit.trace

对已经训练好的模型,可用 torch.jit.trace 将其编译为 TorchScript 图来加速推理。模型文档给出如下做法,并明确提示追踪会产生轻微数值差异:原始模型与 traced 模型输出的最大绝对差值约为 1e-4,属正常范围。

import torch
from transformers import AutoImageProcessor, AutoModel
from PIL import Image
import requests

url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
image = Image.open(requests.get(url, stream=True).raw)

processor = AutoImageProcessor.from_pretrained('facebook/dinov2-base')
model = AutoModel.from_pretrained('facebook/dinov2-base', device_map="auto")

inputs = processor(images=image, return_tensors="pt").to(model.device)
outputs = model(**inputs)
last_hidden_states = outputs[0]

# We have to force return_dict=False for tracing
model.config.return_dict = False

with torch.no_grad():
    traced_model = torch.jit.trace(model, [inputs.pixel_values])
    traced_outputs = traced_model(inputs.pixel_values)

print((last_hidden_states - traced_outputs[0]).abs().max())

两个关键细节:

  1. 追踪前必须设置 model.config.return_dict = Falsetorch.jit.trace 要求输入输出为张量(或张量元组),而默认的 dict 输出无法直接被 trace,因此需要关闭 return_dict
  2. 外层包裹 torch.no_grad(),避免追踪过程记录计算图;
  3. 追踪后建议自行验证数值一致性(文档示例即打印原始输出与追踪输出的最大绝对差),据此判断精度损失是否在任务容忍范围内。

此外,由于 DINOv2 支持 _supports_sdpa,在支持的 GPU 上还可通过 attn_implementation="sdpa"(甚至 FlashAttention)进一步降低注意力计算开销,两者可叠加使用。

Dinov2Config:配置项与底层架构解析

Dinov2Configconfiguration_dinov2.py)继承自 PreTrainedConfig,并混入了 BackboneConfigMixin(使其可作为 Backbone 输出多尺度特征)。除继承通用字段外,其独有及常用配置项如下:

尺寸与输入

配置项 默认值 说明
image_size 224 预训练输入分辨率,支持 int(H, W) 元组
patch_size 14 patch 边长;patch 数由 (H // patch) * (W // patch) 决定
num_channels 3 输入图像通道数
hidden_size 768 隐藏层维度(base 规模)
num_hidden_layers 12 Transformer 编码器层数
num_attention_heads 12 自注意力头数
mlp_ratio 4 MLP 隐藏维度相对 hidden_size 的倍数
hidden_act "gelu" 激活函数
hidden_dropout_prob 0.0 token 序列 Dropout 概率
attention_probs_dropout_prob 0.0 注意力权重 Dropout 概率
initializer_range 0.02 权重初始化标准差
layer_norm_eps 1e-6 LayerNorm 的 epsilon
qkv_bias True 注意力 Q/K/V 投影是否带偏置

DINOv2 特有训练/结构开关

配置项 默认值 说明
layerscale_value 1.0 LayerScale 初始值。DINOv2 在每块 Transformer 输出前乘一个可学习的逐维缩放向量 lambda1Dinov2LayerScale),初始化为该值,用于稳定深层 ViT 训练
drop_path_rate 0.0 随机深度(Stochastic Depth)丢弃概率。对应 Dinov2DropPath 模块,按样本以 keep_prob 保留残差分支;为 0 时退化为恒等映射
use_swiglu_ffn False 是否使用 SwiGLU 前馈网络。开启后使用 Dinov2SwiGLUFFNsilu(x1) * x2 门控结构,隐藏维度按 2/3 缩放并向上取整到 8 的倍数;关闭时用标准双层 Dinov2MLP
use_mask_token True 是否在 embedding 中使用可学习的 mask_token。预训练(配合 bool_masked_pos)时需要,推理时传 bool_masked_pos=None 即可
apply_layernorm True 模型作为 Backbone 使用时,是否对特征图应用 LayerNorm
reshape_hidden_states True 作为 Backbone 时,特征图是否重塑为 4D (batch, hidden, height, width);若为 False 则保持 3D (batch, seq_len, hidden)

配置类的 __post_init__ 会按层数自动生成 stage_names = ["stem", "stage1", ..., "stageN"],并通过 set_output_features_output_indices 接收 out_features/out_indices 参数(供 Backbone 抽取指定 stage 使用)。

从代码结构看 DINOv2 编码器的设计

逐块阅读 modeling_dinov2.py 可还原出 DINOv2 与经典 ViT 的差异:

  • Pre-LN + 残差Dinov2Layer 先对输入做 norm1 再进自注意力,随后 drop_path(attn_output) + hidden_states 做第一次残差;再做 norm2 + MLP + LayerScale + drop_path 第二次残差。文档源码注释特别指出 DINOv2 在自注意力之后、MLP 之前同样施加了 LayerNorm;
  • LayerScale:每块输出前乘逐维可学习系数 lambda1(初始化为 layerscale_value);
  • 梯度检查点Dinov2Layer 继承自 GradientCheckpointingLayer,模型级 supports_gradient_checkpointing = True,可在显存受限时开启激活重计算;
  • 权重初始化_init_weights 对 Linear/Conv2d 使用截断正态分布,位置编码与 CLS token 亦用截断正态初始化,mask_token 与 LayerScale 则用常数初始化。

进阶用法:Dinov2Backbone 接入目标检测与分割框架

模型文档提到 DINOv2 适用于分类、深度估计等任务;而要让其作为通用视觉骨干接入 DETR、MaskFormer 这类需要多尺度特征图的框架,可使用 Dinov2Backbonemodeling_dinov2.py 中定义),并通过 AutoBackbone 加载。模型自身 docstring 给出了完整的可运行示例:

>>> from transformers import AutoImageProcessor, AutoBackbone
>>> import torch
>>> from PIL import Image
>>> import httpx
>>> from io import BytesIO

>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
>>> with httpx.stream("GET", url) as response:
...     image = Image.open(BytesIO(response.read()))

>>> processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
>>> model = AutoBackbone.from_pretrained(
...     "facebook/dinov2-base", out_features=["stage2", "stage5", "stage8", "stage11"]
... )

>>> inputs = processor(image, return_tensors="pt")

>>> outputs = model(**inputs)
>>> feature_maps = outputs.feature_maps
>>> list(feature_maps[-1].shape)
[1, 768, 16, 16]

Backbone 前向的核心逻辑是:强制输出全部 hidden_states,再依据 stage_namesstem + stage1..stageN)与 out_features 匹配抽取指定层,并对命中的层依次执行:可选的 LayerNorm(apply_layernorm)、去掉 CLS token([:, 1:])、按 patch 网格重塑为 4D 特征图((batch, height/patch, width/patch, hidden) 再 permute 为 (batch, hidden, H, W))。因此最后一个 stage 输出的形状为 [1, 768, 16, 16](224×224 输入、patch 14 时对应 16×16 的空间分辨率)。

需要说明的是,源码中注释提醒:reshape 时"height、width 顺序与原始实现相反"是原版 DINOv2 中即存在的行为,Transformers 出于复现一致性有意保留。

小结

本文围绕 DINOv2 模型文档完整梳理了从调用到源码的实践路径:

  1. 快速使用pipeline("image-classification", ...) 一行完成分类;AutoImageProcessor + AutoModelForImageClassification 适合精细控制,且可开启 sdpa 注意力加速;
  2. 输出语义Dinov2Model 输出为 1 + num_patches 个 token,CLS token 服务全局任务、patch 特征服务稠密任务,分类头采用 CLS 与 patch 均值拼接的策略;
  3. 大模型落地:torchao 的 Int4WeightOnlyConfig + TorchAoConfig 可在加载 giant 版本时将权重降至 int4;torch.jit.trace(关闭 return_dict)可进一步提速,精度差约在 1e-4 量级;
  4. 架构理解:Dinov2Config 的 LayerScale、DropPath、SwiGLU FFN、mask token 等开关与 Dinov2Layer 的 pre-LN + 双残差结构一一对应;
  5. 特征抽取Dinov2Backbone 支持多 stage 特征图输出,可平滑接入 DETR、MaskFormer 等下游框架。

上述所有代码均可基于本文示例在本仓库环境直接运行,更详尽的 API 签名可查阅 Dinov2ConfigDinov2Model.forwardDinov2ForImageClassification.forward 的自动生成文档(原文档以 autodoc 形式展开),以及配套测试文件 tests/models/dinov2/test_modeling_dinov2.py 中与 Backbone、Pipeline 相关的用例。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388