从 DINO 到 DINOv3:没有人工标签,视觉模型为什么依然能够"看懂"图像?
DINO 让模型在没有标签的情况下学会观察,DINOv2 将这种能力变成通用视觉特征,DINOv3 则进一步增强了对局部结构和空间关系的理解。
1. 从一个简单问题开始
训练传统图像分类网络时,我们通常需要告诉模型:
- 这是一棵苹果树;
- 这是主干;
- 这是侧枝;
- 这里是背景。
模型通过人工标签学习不同类别之间的边界。但是,真实世界中的图像远比预先定义的标签复杂。
例如,同一根苹果树枝条可能出现在:
- 晴天或阴天;
- 正面或侧面视角;
- 室内或室外背景;
- 冬季无叶或春季萌芽状态;
- 不同相机、焦距和曝光条件下。
虽然图像像素发生了明显变化,人仍然能够判断这些图像描述的是同一类结构。原因是人不会只记忆具体像素,而会关注更加稳定的形状、纹理、结构和语义。
DINO 要解决的核心问题就是:
能否不提供人工标签,只通过观察同一图像的不同变化,让网络自己学习稳定的视觉表示?
DINO 的全称是 Self-Distillation with No Labels,即"无标签自蒸馏"。它不是一个专门用于分类、分割或检测的任务模型,而是一种学习通用视觉特征的方法。
2. DINO:让学生从"过去的自己"学习
2.1 教师---学生结构
DINO 使用两个结构相同的网络:
- Student Network:学生网络,通过梯度下降更新;
- Teacher Network:教师网络,不直接进行反向传播。
训练时,同一张图像会经过不同的数据增强,然后分别输入教师和学生网络。
一个直观例子
假设原始图像是一棵冬季苹果树:
- 教师看到完整的树,但图像亮度较低;
- 学生只看到一部分侧枝,而且图像经过颜色扰动;
- 学生需要预测教师对这张图像的理解。
教师并不会告诉学生"这是苹果树"或"这是侧枝"。它只提供一个特征分布作为学习目标。学生必须从有限的局部视图中找到与完整图像一致的信息。
如果这个过程在大量图像上不断重复,学生逐渐会发现:
- 亮度变化通常不改变目标身份;
- 随机裁剪可能只保留目标的一部分;
- 背景变化不一定代表前景目标变化;
- 某些纹理和结构比单个像素更加稳定。
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 同时学习:
- 图像级一致性:同一张图像的不同增强视图应该具有相近的整体表示;
- Patch 级理解:局部区域即使被遮挡,也可以通过上下文恢复其特征。
这使 DINOv2 特征能够直接用于分类、分割、深度估计、图像检索、特征匹配和视觉定位等任务。
参考:DINOv2 官方代码
7. DINOv3:为什么还需要第三代?
扩大模型和训练数据通常会增强全局语义能力,但长时间训练也可能损害局部 Patch 特征的空间质量。
对于图像分类,这个问题可能并不明显。模型只要判断图像中存在一棵树即可,不一定要准确区分每根细枝。
但对于以下任务,局部特征质量非常重要:
- 语义分割;
- 单目深度估计;
- 局部特征匹配;
- 像素级对应;
- 细小目标定位。
DINOv3 引入 Gram Anchoring,用于维持 Patch 之间的特征关系。
它会根据所有 Patch 特征计算一个关系矩阵,记录图像中不同区域彼此有多相似。Gram Anchoring 关注的不只是某个 Patch 的单独特征,还包括整幅图像内部各区域之间的关系。
一个直观例子
假设一张树木图像中包含:
- 主干区域 A;
- 与主干连接的侧枝区域 B;
- 背景区域 C。
理想情况下,A 与 B 的结构关系应该强于 A 与 C 的关系。如果长时间训练导致所有局部区域逐渐变得相似,模型虽然仍能判断"这是一棵树",却可能无法准确区分枝条和背景。
Gram Anchoring 的作用,可以理解为在大规模训练过程中给局部结构设置一个"锚点",帮助模型保留这些区域关系。
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 系列展示了一条不同于传统监督学习的发展路线:
- DINO 证明无标签自蒸馏能够让 ViT 自发形成目标级语义;
- DINOv2 通过更大的数据规模和改进训练策略,将其发展为通用视觉特征;
- DINOv3 进一步强化局部区域之间的空间关系和稠密特征质量。
DINO 真正强大的地方,不是它能够完成某一个特定任务,而是它提供了一个可以被多种任务复用的视觉基础。
对于果树枝条分析、三维重建和机器人剪枝,DINO 可以提供对外观变化更加稳定的局部特征。但它不是完整解决方案。最可靠的系统通常会将 DINO 的语义特征与前景 Mask、相机几何、三维点云和枝条拓扑共同使用。
最终可以用一句话概括 DINO 系列:
DINO 让模型在没有标签的情况下学会观察,DINOv2 让这种观察能力可以广泛复用,DINOv3 则让模型对局部结构和真实场景看得更加清楚。