首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >DINOv3上手指南:改变视觉模型使用方式,一个模型搞定分割、检测、深度估计

DINOv3上手指南:改变视觉模型使用方式,一个模型搞定分割、检测、深度估计

作者头像
deephub
发布2025-11-15 11:11:06
发布2025-11-15 11:11:06
4470
举报
文章被收录于专栏:DeepHub IMBADeepHub IMBA

点击上方“Deephub Imba”,关注公众号,好文章不错过 !

DINOv3是Meta推出的自监督视觉骨干网络,最大的亮点是你可以把整个backbone冻住不动,只训练一个很小的任务头就能在各种密集预测任务上拿到SOTA结果。这对实际工程应用来说意义重大,因为大部分时候我们并不想重新训练一个几十亿参数的模型。

为什么现在要关注DINOv3

首先是训练数据的规模优势。DINOv3在LVD-1689M(16.89亿张图像)和卫星数据集SAT-493M(4.93亿张)上用SSL训练,这意味着你不需要等人工标注就能利用海量数据。

更重要的是,DINOv3的密集特征质量足够好,以至于backbone完全冻结的情况下,简单加个线性层或者轻量级头部就能在分割、深度估计、检索这些任务上达到很好的效果。这在以前是很难想象的,通常你都需要微调整个模型。

另外,DINOv3作者还证明了同一个backbone可以在完全不同的领域都work,而且是在不做任何微调的情况下就能超过那些专门为某个任务设计的解决方案。这基本上实现了"一个模型走天下"的理想。

相比DINOv2的技术改进

训练目标方面,DINOv3在原有的DINO自蒸馏和多裁剪基础上,加入了iBOT的掩码补丁建模、KoLeo对[CLS] token的正则化,以及一个新的Gram anchoring项。这个Gram anchoring主要是为了解决长时间训练时密集特征图会退化的问题。

对于最大的7B模型,他们设计了三阶段训练流程:先预训练,然后加入Gram anchoring,最后做高分辨率适应,确保特征在大尺寸输入下依然清晰。github仓库里有完整的SLURM命令,可以直接复现。

模型选择

Vision Transformer系列都用patch size 16和4个register token。从ViT-S的21M参数到ViT-7B的67亿参数,大模型切换到SwiGLU激活函数,全部用RoPE位置编码。需要注意输入尺寸必须是16的倍数,否则模型会自动裁剪。

还有个从ViT-7B蒸馏出来的ConvNeXt系列(29M到198M参数),在延迟和显存有限制的场景下很有用。

针对遥感应用,他们还训练了专门的卫星预训练版本,包括ViT-L蒸馏版和ViT-7B从头训练版。

在实际选择时,边缘设备或实时应用可以用ConvNeXt-Tiny/Small或ViT-B蒸馏版;服务器端平衡考虑选ViT-B/L蒸馏版;如果追求极致效果或处理超大图像,就上ViT-H+或ViT-7B,可以直接保持冻结状态,用tiling的方式处理输入。

性能表现

这里说的性能表现都是冻结backbone加轻量级头部的结果。ADE20K语义分割上,ViT-B达到51.8 mIoU,ViT-L是54.9,ViT-7B直接干到60.7。NYU深度估计任务上,ViT-L的AbsRel是0.352,ViT-7B降到0.309。

ConvNeXt蒸馏版本在分辨率提升时效果明显改善,Tiny从256px升到512px时ADE20K从52.6提升到58.7 mIoU,Large版本从59.3跳到65.2。

卫星数据的GEO-Bench测试中,ViT-7B在分类平均值上略胜ViT-L(81.1 vs 79.6),分割任务上也有小幅领先(75.0 vs 74.5)。

Token结构和特征图处理

DINOv3的输出结构是[CLS] + 4个register token + patch grid。对224×224输入(patch 16),总共是1 + 4 + 196 = 201个token。这4个register token相当于"内存槽",能减少patch token中的高范数伪影,对密集任务很有帮助。

如果输入图像尺寸不是16的倍数,DINOv3会裁剪到最接近的较小倍数。但对于需要保留细节的密集任务,你可以更愿意在预处理时做padding而不是让模型自动裁剪。

代码实现

特征提取

代码语言:javascript
复制
 # pip install torch torchvision transformers pillow  
from transformers import AutoModel, AutoImageProcessor  
from transformers.image_utils import load_image  
import torch  

model_id = "facebook/dinov3-vitb16-pretrain-lvd1689m"  # 如果你有访问权限,可以换成vitl/7b
processor = AutoImageProcessor.from_pretrained(model_id)  
model = AutoModel.from_pretrained(model_id, device_map="auto")  
img = load_image("http://images.cocodataset.org/val2017/000000039769.jpg")  
inputs = processor(images=img, return_tensors="pt").to(model.device)  
with torch.inference_mode():  
    outputs = model(**inputs)  
cls = outputs.last_hidden_state[:, 0]  # 全局([CLS])
num_regs = model.config.num_register_tokens  
patch_flat = outputs.last_hidden_state[:, 1 + num_regs:, :]  
# 重塑为[B, C, H, W],步长=16
B, N, C = patch_flat.shape  
H = W = int((N) ** 0.5)  
 feat_map = patch_flat.reshape(B, H, W, C).permute(0, 3, 1, 2)

这段代码完全按照官方文档的token layout来处理,包括register token的处理和patch=16的设定。

分割头实现

代码语言:javascript
复制
 import torch.nn as nn  

class LinearSegHead(nn.Module):  
    def __init__(self, in_ch, n_classes):  
        super().__init__()  
        self.proj = nn.Conv2d(in_ch, n_classes, 1)  
        self.up = nn.Upsample(scale_factor=16, mode="bilinear", align_corners=False)  
    def forward(self, fmap):  # fmap: [B, C, H, W](步长16)
        return self.up(self.proj(fmap))  # 输入分辨率的logits
head = LinearSegHead(in_ch=feat_map.shape[1], n_classes=150).to(model.device)  
 for p in model.parameters(): p.requires_grad_(False)  # 保持骨干网络冻结

这就是冻结backbone的经典用法。你可以从最简单的线性层开始,只有在效果遇到瓶颈时才考虑加更复杂的decoder。

官方仓库还提供了预训练好的分类、深度估计、分割头,可以通过torch.hub直接加载。对于高分辨率图像,他们也给出了tiling和滑动窗口推理的示例代码,这些对于处理地图和城市场景时是可以拿来直接用的。

实际应用场景

DINOv3最适合的场景是那些需要"零微调"的应用。比如图像检索用最近邻搜索,分类任务直接在[CLS] token上跑k-NN或者逻辑回归,语义分割和深度估计在密集特征上加线性层,还有无监督的对象发现、几何语义匹配、视频分割跟踪等等。视频分类只需要一个4层的attention probe就够了。

一个很好的案例是世界资源研究所(WRI)用DINOv3来做树木计数和高度估计。他们用高分辨率卫星和无人机图像来监测森林恢复项目,为环保融资提供数据支持。据他们的报告,DINOv3基本上是开箱即用的,而且在不同传感器之间的泛化能力很强。

在GEO-Bench这个遥感基准测试上,用SAT-493M预训练的DINOv3确实表现不错,ViT-7B在分类和分割任务上都略胜ViT-L。如果你做农业、森林或灾害监测相关的工作,这个信号还是很有参考价值的。

训练细节

对于7B模型的完整训练流程,仓库里有具体的命令,包括预训练到Gram anchoring再到高分辨率适应的三个阶段。他们还提供了一个快速demo,可以在32张H100上用ViT-L对ImageNet-1k进行14小时训练

技术栈方面,用的是DINO loss + multi-crop,patch token上跑iBOT,[CLS]上用KoLeo正则化,再加上Gram anchoring。底层是FSDP2,bf16和fp8的matmul优化。

训练的透明度也是非常哇塞,他们公开了ViT-7B大概需要61,440 GPU小时,碳排放约18 tCO₂e。这种transparency在学术界还是比较少见的,对工业界做预算规划很有帮助。

部署时的注意事项

图像尺寸一定要是16的倍数,否则模型会自动裁剪。对于医学影像、地图这种细节很重要的场景,你最好在预处理时做padding,或者用带重叠的tiling推理。

构建密集预测的头部时,记得把[CLS]和register token丢掉,只用patch token来reshape特征图。

如果要压缩模型,官方文档里有int4 TorchAO的示例,对ViT-7B的推理压缩很有用,不过记得在你的具体任务上验证一下数值精度。

最后还有个问题,模型提到了地域和收入水平的差异。如果你要在国家级别部署(比如跨印度不同邦的不同地貌),可能需要考虑小的domain adapter或者平衡采样。

完整的训练示例

代码语言:javascript
复制
 """  
train_seg_head.py — 在冻结DINOv3特征上的最小训练循环。
用ADE20K/Cityscapes等替换`YourDataset`(图像、掩码)。
"""  
import torch, torch.nn as nn, torch.optim as optim  
from torch.utils.data import DataLoader  
from transformers import AutoModel, AutoImageProcessor  

MODEL_ID = "facebook/dinov3-vitb16-pretrain-lvd1689m"  
N_CLASSES = 150  
class LinearSegHead(nn.Module):  
    def __init__(self, in_ch, n_classes):  
        super().__init__()  
        self.proj = nn.Conv2d(in_ch, n_classes, 1)  
        self.up = nn.Upsample(scale_factor=16, mode="bilinear", align_corners=False)  
    def forward(self, fmap): return self.up(self.proj(fmap))  
def extract_fmap(model, proc, image):  
    inputs = proc(images=image, return_tensors="pt").to(model.device)  
    with torch.inference_mode():  
        out = model(**inputs)  
    num_regs = model.config.num_register_tokens  
    grid = out.last_hidden_state[:, 1 + num_regs:, :]  # 丢弃CLS+寄存器
    B, N, C = grid.shape  
    H = W = int(N ** 0.5)  
    return grid.reshape(B, H, W, C).permute(0,3,1,2)  
def main(train_set, val_set, epochs=10, lr=1e-3):  
    proc = AutoImageProcessor.from_pretrained(MODEL_ID)  
    model = AutoModel.from_pretrained(MODEL_ID, device_map="auto")  
    for p in model.parameters(): p.requires_grad_(False)  
    # 推断通道数一次
    x0, _ = train_set[0]  
    fmap0 = extract_fmap(model, proc, x0)  
    head = LinearSegHead(fmap0.shape[1], N_CLASSES).to(model.device)  
    opt = optim.AdamW(head.parameters(), lr=lr)  
    loss = nn.CrossEntropyLoss()  
    train = DataLoader(train_set, batch_size=4, shuffle=True)  
    val = DataLoader(val_set, batch_size=4)  
    for ep in range(epochs):  
        head.train()  
        for img, mask in train:  
            fmap = extract_fmap(model, proc, img)  
            logits = head(fmap)  
            L = loss(logits, mask.to(logits.device).long())  
            opt.zero_grad(); L.backward(); opt.step()  
        # TODO: 在`val`上添加简单的mIoU
     torch.save(head.state_dict(), "dinov3_seg_head.pth")

这个示例严格按照官方的token layout和冻结特征的使用方式来写。

总结

对于做地理空间分析、工业检测、零售货架规划、交通智能或智慧城市平台的团队来说,DINOv3提供了一个很实用的方案:用一个backbone应对多种任务,保持冻结状态,只加很小的头部,对高分辨率输入做tiling处理就能部署,不用陷入不断重新微调大模型的循环里。这才是DINOv3真正超模的地方。


喜欢就关注一下吧:

点个 在看 你最好看!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DeepHub IMBA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么现在要关注DINOv3
  • 相比DINOv2的技术改进
  • 模型选择
  • 性能表现
  • Token结构和特征图处理
  • 代码实现
  • 实际应用场景
  • 训练细节
  • 部署时的注意事项
  • 完整的训练示例
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档