特征提取 | DINO 到 DINOv3

从 DINO 到 DINOv3:没有人工标签,视觉模型为什么依然能够"看懂"图像?

DINO 让模型在没有标签的情况下学会观察,DINOv2 将这种能力变成通用视觉特征,DINOv3 则进一步增强了对局部结构和空间关系的理解。

1. 从一个简单问题开始

训练传统图像分类网络时,我们通常需要告诉模型:

  • 这是一棵苹果树;
  • 这是主干;
  • 这是侧枝;
  • 这里是背景。

模型通过人工标签学习不同类别之间的边界。但是,真实世界中的图像远比预先定义的标签复杂。

例如,同一根苹果树枝条可能出现在:

  • 晴天或阴天;
  • 正面或侧面视角;
  • 室内或室外背景;
  • 冬季无叶或春季萌芽状态;
  • 不同相机、焦距和曝光条件下。

虽然图像像素发生了明显变化,人仍然能够判断这些图像描述的是同一类结构。原因是人不会只记忆具体像素,而会关注更加稳定的形状、纹理、结构和语义。

DINO 要解决的核心问题就是:

能否不提供人工标签,只通过观察同一图像的不同变化,让网络自己学习稳定的视觉表示?

DINO 的全称是 Self-Distillation with No Labels,即"无标签自蒸馏"。它不是一个专门用于分类、分割或检测的任务模型,而是一种学习通用视觉特征的方法。


2. DINO:让学生从"过去的自己"学习

2.1 教师---学生结构

DINO 使用两个结构相同的网络:

  • Student Network:学生网络,通过梯度下降更新;
  • Teacher Network:教师网络,不直接进行反向传播。

训练时,同一张图像会经过不同的数据增强,然后分别输入教师和学生网络。

一个直观例子

假设原始图像是一棵冬季苹果树:

  1. 教师看到完整的树,但图像亮度较低;
  2. 学生只看到一部分侧枝,而且图像经过颜色扰动;
  3. 学生需要预测教师对这张图像的理解。

教师并不会告诉学生"这是苹果树"或"这是侧枝"。它只提供一个特征分布作为学习目标。学生必须从有限的局部视图中找到与完整图像一致的信息。

如果这个过程在大量图像上不断重复,学生逐渐会发现:

  • 亮度变化通常不改变目标身份;
  • 随机裁剪可能只保留目标的一部分;
  • 背景变化不一定代表前景目标变化;
  • 某些纹理和结构比单个像素更加稳定。

2.2 教师从哪里获得知识?

这里容易产生一个疑问:如果没有人工标签,教师网络又是从谁那里学习的?

DINO 的答案是:教师是学生参数的缓慢移动平均。

教师参数并不是直接复制学生参数,而是由"原有教师参数"和"当前学生参数"加权得到。原有教师参数占绝大部分,当前学生参数只占很小一部分。因此,教师能够跟随学生持续更新,但不会因学生某一次训练产生剧烈变化。

可以把教师理解为学生过去多个训练状态的"稳定平均"。学生每一步变化较快,而教师变化较慢,因此能够提供相对平稳的训练目标。

这有点像一个人学习摄影:

今天的自己向过去几个月积累下来的经验学习;新的经验又会逐渐更新长期认知。

2.3 如何避免所有图像得到相同特征?

如果只要求学生模仿教师,网络可能找到一个没有意义的捷径:无论输入什么图像,都输出相同结果。这被称为表示坍塌

DINO 使用两个关键机制缓解这一问题:

  • Centering:对教师输出进行中心化,避免少数维度长期占据优势;
  • Sharpening:降低教师端 Softmax 温度,使输出更加明确。

DINO 最有意思的发现之一是:经过自监督训练后,Vision Transformer 的注意力图能够自然覆盖图像中的前景目标。即使没有像素级分割标签,网络也可能自动区分物体与背景。

这说明网络学到的不只是颜色统计,而是带有一定语义和空间结构的信息。

参考:DINO 原始论文


3. 为什么 DINO 和 Vision Transformer 很合拍?

DINO 通常使用 Vision Transformer(ViT)作为视觉骨干网络。

3.1 先把图像切成 Patch

ViT 不直接逐像素处理图像,而是先将图像划分为固定大小的图像块,即 Patch

以 DINOv2 ViT-S/14 为例:

  • 输入图像尺寸:518 × 518;
  • Patch 尺寸:14 × 14;
  • 每行 Patch 数量:518 ÷ 14 = 37;
  • Patch 总数:37 × 37 = 1369。

因此,一张图像被转换为 1369 个 Patch Token。

"1369张信息卡片"的例子

可以把整张图像想象成一块被切成 1369 格的拼图。每一格都会生成一张"信息卡片",上面记录该区域的视觉特征。

如果某个 Patch 位于枝条上,它的卡片可能包含:

  • 棕灰色的局部纹理;
  • 细长的边缘方向;
  • 与相邻枝条区域的连续关系;
  • 它在整棵树结构中的上下文。

这些卡片随后进入 Transformer,通过自注意力机制相互交流。

3.2 自注意力为什么重要?

卷积神经网络主要通过局部卷积逐层扩大感受野。Transformer 则允许不同 Patch 直接计算相关性。

例如,一根枝条可能被其他枝条遮挡,导致图像中间出现短暂断裂。只观察断裂位置附近的像素,模型可能认为这是两个独立目标。但是,自注意力可以同时参考:

  • 断裂两侧的方向;
  • 相似的颜色和纹理;
  • 它们与同一主干之间的关系;
  • 更远位置的整体树形结构。

因此,Transformer 更容易建立长距离上下文。对于枝条、道路、血管和电线等细长结构,这种能力尤其有价值。


4. 为什么 ViT-S 的特征维度是 384?

DINOv2 ViT-S/14 中,每个 Patch 对应一个 384 维特征。

384 不是根据图像类别统计得到的数字,也不是图像中只包含 384 个信息。它是 Transformer 的隐藏层宽度,由多头注意力结构决定。ViT-S 包含 6 个注意力头,每个头使用 64 维特征,因此总特征维度为 6 × 64 = 384。

"六位观察员"的例子

可以把多头注意力想象成六位观察员同时分析同一个 Patch。每位观察员都有一个 64 维的内部表示,但他们可以关注不同类型的关系,例如:

  • 一位更关注纹理相似性;
  • 一位更关注边缘方向;
  • 一位更关注远距离区域;
  • 一位更关注前景与背景;
  • 一位更关注局部连续性;
  • 一位更关注全局语义。

这只是帮助理解的比喻。实际训练中,并没有人为规定每个注意力头必须负责什么,它们的分工是从数据中学习出来的。

六个注意力头的结果组合后,便形成 384 维表示。

DINOv2 模型 注意力头数 单头维度 输出特征维度
ViT-S/14 6 64 384
ViT-B/14 12 64 768
ViT-L/14 16 64 1024
ViT-g/14 24 64 1536

传统 CNN 经常使用 256、512、1024 或 2048 个通道,因为 CNN 通常按照两倍扩大通道数。Transformer 更关心"注意力头数 × 单头维度",因此 384 和 768 都很常见。

对于一张 518 × 518 的图像,ViT-S/14 的 Patch 特征形状通常为:

text 复制代码
[B, 1369, 384]

这表示图像具有 1369 个空间位置,每个位置由 384 个数描述,而不是整张图像只有一个 384 维特征。


5. CLS Token 和 Patch Token 有什么区别?

DINO 通常提供两类特征:

CLS Token:描述整张图像

CLS Token 是一个全局特征,可以理解为整张图像的摘要。

典型形状为:

text 复制代码
[B, 384]

它适合:

  • 图像分类;
  • 图像检索;
  • 场景识别;
  • 判断两张图片的整体内容是否相似。

例如,判断一张图片是苹果树、建筑还是车辆,可以优先使用 CLS Token。

Patch Token:描述局部区域

Patch Token 保留空间位置,每个 Patch 都有自己的特征。

典型形状为:

text 复制代码
[B, 1369, 384]

它适合:

  • 语义分割;
  • 局部特征匹配;
  • 深度估计;
  • 目标定位;
  • 像素或区域级对应。

例如,要寻找两张苹果树图像中同一根侧枝的位置,只使用整幅图像的 CLS Token 是不够的,需要比较对应区域的 Patch Token。


6. DINOv2:从实验方法变成通用特征提取器

初代 DINO 证明了无标签自蒸馏的可行性,但其数据规模和训练流程仍有提升空间。

DINOv2 将这种方法扩展为更成熟的视觉基础模型。官方模型使用约 1.42 亿张经过筛选的无标签图像进行预训练,并将图像级自蒸馏与 Patch 级掩码学习结合起来。

掩码图像建模的例子

假设模型看到一张苹果树图像,但部分 Patch 被遮挡:

text 复制代码
树干 ── [被遮挡] ── 侧枝

模型需要根据周围区域推测被遮挡位置应该具有怎样的特征。它不能只记住单个像素,而必须利用:

  • 邻近区域的纹理;
  • 枝条延伸方向;
  • 上下文结构;
  • 整张图像的语义。

因此,DINOv2 同时学习:

  1. 图像级一致性:同一张图像的不同增强视图应该具有相近的整体表示;
  2. Patch 级理解:局部区域即使被遮挡,也可以通过上下文恢复其特征。

这使 DINOv2 特征能够直接用于分类、分割、深度估计、图像检索、特征匹配和视觉定位等任务。

参考:DINOv2 官方代码


7. DINOv3:为什么还需要第三代?

扩大模型和训练数据通常会增强全局语义能力,但长时间训练也可能损害局部 Patch 特征的空间质量。

对于图像分类,这个问题可能并不明显。模型只要判断图像中存在一棵树即可,不一定要准确区分每根细枝。

但对于以下任务,局部特征质量非常重要:

  • 语义分割;
  • 单目深度估计;
  • 局部特征匹配;
  • 像素级对应;
  • 细小目标定位。

DINOv3 引入 Gram Anchoring,用于维持 Patch 之间的特征关系。

它会根据所有 Patch 特征计算一个关系矩阵,记录图像中不同区域彼此有多相似。Gram Anchoring 关注的不只是某个 Patch 的单独特征,还包括整幅图像内部各区域之间的关系。

一个直观例子

假设一张树木图像中包含:

  • 主干区域 A;
  • 与主干连接的侧枝区域 B;
  • 背景区域 C。

理想情况下,A 与 B 的结构关系应该强于 A 与 C 的关系。如果长时间训练导致所有局部区域逐渐变得相似,模型虽然仍能判断"这是一棵树",却可能无法准确区分枝条和背景。

Gram Anchoring 的作用,可以理解为在大规模训练过程中给局部结构设置一个"锚点",帮助模型保留这些区域关系。

DINOv3 还进一步扩大了模型和数据规模,并提供高分辨率适配等策略。因此,它不仅擅长判断图像中"有什么",也更擅长判断目标"具体在哪里"。

参考:DINOv3 论文Meta DINOv3 官方页面


8. DINO 为什么强大?

8.1 不受固定标签体系限制

监督分类模型主要学习人工定义的类别边界。DINO 学习的是图像不同视图之间稳定的视觉关系,因此更容易迁移到训练时没有明确标注的新领域。

例如,通用数据集中可能没有"苹果树休眠期一年生枝"这一类别,但 DINO 仍可能提供有效的形状、纹理和语义特征。我们只需要在这些特征上训练一个较小的任务头。

8.2 同时保留全局与局部信息

  • CLS Token 表示整幅图像;
  • Patch Token 表示局部区域;
  • 自注意力负责建立不同区域之间的关系。

因此,同一个模型可以服务于分类、检索、分割、匹配和定位等不同任务。

8.3 能够利用海量无标签图像

互联网上有大量图像,但高质量标注有限。DINO 不需要为每张图片准备类别或像素标签,因此可以从更大规模的数据中学习视觉规律。

8.4 下游任务不一定需要完整微调

在专业领域数据较少时,从头训练大型模型容易过拟合。使用 DINO 时,可以冻结预训练骨干,只训练轻量分类器、分割头或匹配模块。

例如,一个苹果树枝条分类任务可以采用:

text 复制代码
输入图像
   ↓
冻结的 DINOv2
   ↓
384维 Patch 特征
   ↓
轻量 MLP / CNN / GNN
   ↓
枝条类别或目标位置

这样既可以利用大规模预训练知识,又能降低训练数据和计算资源需求。


9. DINO 的典型技术栈

一个实际的 DINO 应用系统通常包含以下层级:

层级 常用工具 主要作用
数据读取 PIL、OpenCV 读取、裁剪和处理图像
数据增强 torchvision、Albumentations 缩放、颜色扰动、模糊和翻转
深度学习框架 PyTorch 张量计算、模型加载和推理
视觉骨干 ViT-S/B/L/g 生成 CLS 与 Patch 特征
预训练权重 DINOv2、DINOv3 提供通用视觉表示
下游任务头 Linear、MLP、CNN Decoder、GNN 完成分类、分割、匹配或定位
几何验证 OpenCV、COLMAP、RANSAC 剔除错误匹配并恢复空间关系

10. 使用 PyTorch 提取 DINOv2 特征

下面以 DINOv2 ViT-S/14 为例:

python 复制代码
import torch
from PIL import Image
from torchvision import transforms

device = "cuda" if torch.cuda.is_available() else "cpu"

model = torch.hub.load(
    "facebookresearch/dinov2",
    "dinov2_vits14"
).to(device)

model.eval()

transform = transforms.Compose([
    transforms.Resize((518, 518)),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=(0.485, 0.456, 0.406),
        std=(0.229, 0.224, 0.225)
    )
])

image = Image.open("tree.jpg").convert("RGB")
image = transform(image).unsqueeze(0).to(device)

with torch.no_grad():
    outputs = model.forward_features(image)

cls_feature = outputs["x_norm_clstoken"]
patch_features = outputs["x_norm_patchtokens"]

print("CLS:", cls_feature.shape)
print("Patch:", patch_features.shape)

输出通常为:

text 复制代码
CLS:   torch.Size([1, 384])
Patch: torch.Size([1, 1369, 384])

其中:

  • cls_feature 可以用于整图分类或检索;
  • patch_features 可以用于局部匹配、分割或定位。

11. 将 Patch Token 恢复成二维特征图

Transformer 输出的 Patch Token 是序列形式:

text 复制代码
[B, 1369, 384]

因为 1369 = 37 × 37,所以可以将这些 Token 重新排列为二维特征图:

python 复制代码
batch_size, num_tokens, channels = patch_features.shape

feature_map = patch_features.reshape(
    batch_size, 37, 37, channels
)

feature_map = feature_map.permute(0, 3, 1, 2)

print(feature_map.shape)

输出为:

text 复制代码
[1, 384, 37, 37]

如果下游模块需要与原图对齐,还可以进行双线性插值:

python 复制代码
import torch.nn.functional as F

dense_features = F.interpolate(
    feature_map,
    size=(518, 518),
    mode="bilinear",
    align_corners=False
)

print(dense_features.shape)

输出为:

text 复制代码
[1, 384, 518, 518]

需要注意:插值只是将特征映射回原图坐标,并不会创造新的细节。DINOv2 ViT-S/14 的原始空间粒度仍然由 14 × 14 Patch 决定。


12. DINO 特征如何用于图像匹配?

假设我们有同一棵苹果树的两张图像:

  • 图像 A 从正面拍摄;
  • 图像 B 从侧面拍摄;
  • 希望找到图像 A 中某个剪枝点在图像 B 中的位置。

首先分别提取两张图像的 Patch 特征,然后计算任意两个 Patch 之间的余弦相似度。

余弦相似度可以理解为比较两个特征向量的"方向"是否一致。结果越接近 1,说明两个区域在 DINO 特征空间中越相似。例如,图像 A 中某个剪枝点附近的 Patch,可以与图像 B 中所有 Patch 逐一比较,再选择相似度最高的区域作为初始匹配。

PyTorch 示例:

python 复制代码
import torch
import torch.nn.functional as F

feat_a = F.normalize(patch_features_a, dim=-1)
feat_b = F.normalize(patch_features_b, dim=-1)

similarity = torch.matmul(
    feat_a,
    feat_b.transpose(-1, -2)
)

best_match_indices = similarity.argmax(dim=-1)

DINO 特征通常比 RGB 像素更能适应亮度、颜色和一定程度的视角变化。不过,特征相似不等于几何位置一定正确

例如,苹果树上可能存在许多外观相似的细枝,模型可能将左侧枝条错误匹配到右侧枝条。因此,实际系统通常还要加入:

  • 前景 Mask,排除天空、墙面和地面;
  • 相互最近邻匹配,减少单向误匹配;
  • 相似度阈值,过滤低置信度对应;
  • RANSAC,验证匹配是否符合几何模型;
  • 相机内外参数,建立 2D--3D 投影关系;
  • 深度、点云或骨架拓扑,限制候选位置。

一个更加完整的果树重定位流程可以写成:

text 复制代码
RGB 图像
   ↓
前景 Mask
   ↓
DINO Patch 特征
   ↓
局部特征匹配
   ↓
RANSAC 几何验证
   ↓
2D--3D 对应
   ↓
三维剪枝目标重定位

DINO 在这里负责提供视觉描述子,几何模块则负责确保匹配在空间上成立。


13. DINO、DINOv2 和 DINOv3 应该如何选择?

模型 主要特点 更适合的场景
DINO 结构清楚,便于理解自蒸馏 教学、算法研究、训练机制分析
DINOv2 生态成熟,性能和计算成本均衡 分类、检索、分割、匹配和工程应用
DINOv3 稠密局部特征更强,规模选择更多 高精度分割、深度估计和复杂对应任务

如果希望快速搭建稳定系统,DINOv2 通常是一个实用起点。

如果任务高度依赖局部空间结构,例如:

  • 细枝分割;
  • 跨视角特征匹配;
  • 跨季节目标重定位;
  • 像素级或 Patch 级对应;

则可以进一步测试 DINOv3。

不过,模型越大并不一定越适合具体任务。选择模型时还应同时考虑:

  • GPU 显存;
  • 推理速度;
  • 输入分辨率;
  • 训练数据量;
  • 目标结构尺度;
  • 下游任务是否更依赖语义或几何。

14. 总结

DINO 系列展示了一条不同于传统监督学习的发展路线:

  1. DINO 证明无标签自蒸馏能够让 ViT 自发形成目标级语义;
  2. DINOv2 通过更大的数据规模和改进训练策略,将其发展为通用视觉特征;
  3. DINOv3 进一步强化局部区域之间的空间关系和稠密特征质量。

DINO 真正强大的地方,不是它能够完成某一个特定任务,而是它提供了一个可以被多种任务复用的视觉基础。

对于果树枝条分析、三维重建和机器人剪枝,DINO 可以提供对外观变化更加稳定的局部特征。但它不是完整解决方案。最可靠的系统通常会将 DINO 的语义特征与前景 Mask、相机几何、三维点云和枝条拓扑共同使用。

最终可以用一句话概括 DINO 系列:

DINO 让模型在没有标签的情况下学会观察,DINOv2 让这种观察能力可以广泛复用,DINOv3 则让模型对局部结构和真实场景看得更加清楚。

相关推荐
从零开始学习人工智能6 小时前
【踩坑实录】WSL2 解决 onnxruntime\-gpu ImportError: libcudart\.so\.13 无 CUDA13 运行库问题
python
卷无止境7 小时前
在 awesome-fastapi 里,哪些库值得一看?
后端·python
zhanghaha13147 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
Python私教7 小时前
API 输出模型怎么设计:从 model_dump() 到显式展示层
python·fastapi
Python私教8 小时前
本地 AI 工具服务该绑定 127.0.0.1 还是 0.0.0.0?
python·fastapi
卷无止境8 小时前
FastAPI 的Admin面板生态
后端·python
ctlover8 小时前
Streamlit 框架
python
ι:9 小时前
MATLAB 与 Python 搭建无人机地面站:优势、劣势与选型逻辑
python·matlab·无人机
船厂电气自动化ai大模型9 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
jufeng13079 小时前
【系列:手搓自主 AI Agent:Hermes 架构原理剖析 · 第 7 篇】
python·ai agent·权限系统