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

📅 发布时间:2026/8/18 17:03:28
DINOv3 上手指南:改变视觉模型使用方式,一个模型搞定分割、检测、深度估计
DINOv3 是 Meta 推出的自监督视觉骨干网络最大的亮点是你可以把整个backbone冻住不动只训练一个很小的任务头就能在各种密集预测任务上拿到 SOTA 结果。这对实际工程应用来说意义重大因为大部分时候我们并不想重新训练一个几十亿参数的模型。1. 为什么现在要关注 DINOv3首先是训练数据的规模优势。DINOv3 在 LVD-1689M16.89 亿张图像和卫星数据集 SAT-493M4.93亿张上用 SSL 训练这意味着你不需要等人工标注就能利用海量数据。更重要的是DINOv3 的密集特征质量足够好以至于 backbone 完全冻结的情况下简单加个线性层或者轻量级头部就能在分割、深度估计、检索这些任务上达到很好的效果。这在以前是很难想象的通常你都需要微调整个模型。另外DINOv3 作者还证明了同一个 backbone 可以在完全不同的领域都work而且是在不做任何微调的情况下就能超过那些专门为某个任务设计的解决方案。这基本上实现了一个模型走天下的理想。2. 相比DINOv2的技术改进训练目标方面DINOv3 在原有的DINO自蒸馏和多裁剪基础上加入了iBOT的掩码补丁建模、KoLeo 对[CLS] token的正则化以及一个新的 Gram anchoring 项。这个 Gram anchoring 主要是为了解决长时间训练时密集特征图会退化的问题。对于最大的7B模型他们设计了三阶段训练流程先预训练然后加入 Gram anchoring最后做高分辨率适应确保特征在大尺寸输入下依然清晰。github仓库里有完整的SLURM命令可以直接复现。3. 模型选择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的方式处理输入。4. 性能表现这里说的性能表现都是冻结backbone加轻量级头部的结果。ADE20K语义分割上ViT-B达到51.8 mIoUViT-L是54.9ViT-7B直接干到60.7。NYU深度估计任务上ViT-L的AbsRel是0.352ViT-7B降到0.309。ConvNeXt蒸馏版本在分辨率提升时效果明显改善Tiny从256px升到512px时ADE20K从52.6提升到58.7 mIoULarge版本从59.3跳到65.2。卫星数据的GEO-Bench测试中ViT-7B在分类平均值上略胜ViT-L (81.1 vs. 79.6)分割任务上也有小幅领先 (75.0 vs. 74.5)。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而不是让模型自动裁剪。6. 代码实现特征提取# 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_mapauto) img load_image(http://images.cocodataset.org/val2017/000000039769.jpg) inputs processor(imagesimg, return_tensorspt).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的处理和patch16的设定。分割头实现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_factor16, modebilinear, align_cornersFalse) def forward(self, fmap): # fmap: [B, C, H, W]步长16 return self.up(self.proj(fmap)) # 输入分辨率的logits head LinearSegHead(in_chfeat_map.shape[1], n_classes150).to(model.device) for p in model.parameters(): p.requires_grad_(False) # 保持骨干网络冻结这就是冻结backbone的经典用法。你可以从最简单的线性层开始只有在效果遇到瓶颈时才考虑加更复杂的decoder。官方仓库还提供了预训练好的分类、深度估计、分割头可以通过torch.hub直接加载。对于高分辨率图像他们也给出了tiling和滑动窗口推理的示例代码这些对于处理地图和城市场景时是可以拿来直接用的。7. 实际应用场景DINOv3最适合的场景是那些需要零微调的应用。比如图像检索用最近邻搜索分类任务直接在[CLS] token上跑k-NN或者逻辑回归语义分割和深度估计在密集特征上加线性层还有无监督的对象发现、几何语义匹配、视频分割跟踪等等。视频分类只需要一个4层的 attention probe 就够了。一个很好的案例是世界资源研究所 (WRI) 用DINOv3来做树木计数和高度估计。他们用高分辨率卫星和无人机图像来监测森林恢复项目为环保融资提供数据支持。据他们的报告DINOv3基本上是开箱即用的而且在不同传感器之间的泛化能力很强。在GEO-Bench这个遥感基准测试上用SAT-493M预训练的DINOv3确实表现不错ViT-7B在分类和分割任务上都略胜ViT-L。如果你做农业、森林或灾害监测相关的工作这个信号还是很有参考价值的。8. 训练细节对于7B模型的完整训练流程仓库里有具体的命令包括预训练到Gram anchoring再到高分辨率适应的三个阶段。他们还提供了一个快速demo可以在32张H100上用ViT-L对ImageNet-1k进行14小时训练技术栈方面用的是DINO loss multi-croppatch token上跑iBOT[CLS]上用KoLeo正则化再加上Gram anchoring。底层是FSDP2bf16和fp8的matmul优化。训练的透明度也是非常哇塞他们公开了ViT-7B大概需要61,440 GPU小时碳排放约18 tCO₂e。这种transparency在学术界还是比较少见的对工业界做预算规划很有帮助。9. 部署时的注意事项图像尺寸一定要是16的倍数否则模型会自动裁剪。对于医学影像、地图这种细节很重要的场景你最好在预处理时做padding或者用带重叠的tiling推理。构建密集预测的头部时记得把[CLS]和register token丢掉只用patch token来reshape特征图。如果要压缩模型官方文档里有int4 TorchAO的示例对ViT-7B的推理压缩很有用不过记得在你的具体任务上验证一下数值精度。最后还有个问题模型提到了地域和收入水平的差异。如果你要在国家级别部署比如跨印度不同邦的不同地貌可能需要考虑小的domain adapter或者平衡采样。10. 完整的训练示例 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_factor16, modebilinear, align_cornersFalse) def forward(self, fmap): return self.up(self.proj(fmap)) def extract_fmap(model, proc, image): inputs proc(imagesimage, return_tensorspt).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, epochs10, lr1e-3): proc AutoImageProcessor.from_pretrained(MODEL_ID) model AutoModel.from_pretrained(MODEL_ID, device_mapauto) 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(), lrlr) loss nn.CrossEntropyLoss() train DataLoader(train_set, batch_size4, shuffleTrue) val DataLoader(val_set, batch_size4) 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和冻结特征的使用方式来写。11. 总结对于做地理空间分析、工业检测、零售货架规划、交通智能或智慧城市平台的团队来说DINOv3提供了一个很实用的方案用一个backbone应对多种任务保持冻结状态只加很小的头部对高分辨率输入做tiling处理就能部署不用陷入不断重新微调大模型的循环里。这才是DINOv3真正超模的地方。