DINOv2 实战教程:从零到跑通自监督特征提取的完整指南
【免费下载链接】dinov2PyTorch code and models for the DINOv2 self-supervised learning method.项目地址: https://gitcode.com/GitHub_Trending/di/dinov2
DINOv2 是 Meta 开源的自监督视觉模型,它用 1.42 亿张无标注图片学出一套通用图像特征,你无需训练就能直接搭建检索、分类、分割系统。适合想快速接入视觉特征提取器的工程师和研究者。
能力速览:DINOv2 到底是什么
把 DINOv2 理解成一台"通用视觉翻译机":你喂给它一张图,它还你一个 768 维向量(ViT-B 规格)加一组逐块的细粒度特征。不需要标签,也不关心你要做什么任务。最常被拿来做三件事:
- 图像检索:图片变成向量,余弦相似度即可找图;
- 图像分类:骨干网络冻住,只训一层线性层就能接新任务;
- 密集预测:逐 patch 的特征能接分割头、深度头,输出像素级结果。
它的价值在于:特征是在 LVD-142M 无标注数据上"盲练"出来的,迁移到下游任务时几乎不用重新教它看图,省掉的是你从头训 backbone 的时间和数据。
选型速查:四个尺寸怎么选
仓库内置四种规模的骨干网络,每个都有带 register 的_reg增强版:
| 模型名 | 参数量 | 特征维度 | 层数 | 一句话定位 |
|---|---|---|---|---|
| dinov2_vits14 | 21M | 384 | 12 | 跑得最快,适合批量处理和低配卡 |
| dinov2_vitb14 | 86M | 768 | 12 | 默认选择,速度和精度平衡 |
| dinov2_vitl14 | 300M | 1024 | 24 | 显存够就上,精度上限更高 |
| dinov2_vitg14 | 1.1B | 1536 | 40 | 只追求极限精度时考虑 |
_reg后缀的版本多了 4 个 register token——相当于给模型留的"草稿纸",专门吸收 CLS token 上容易溢出的杂散信息,让 patch 级特征更干净。做分割、深度这类密集任务时优先选它。
三条判断标准,对号入座:
- 没有特殊诉求、第一次用 →vitb14,闭眼选;
- 输入图很大(1080p 以上)或单卡要跑大批量 →vits14,省下的显存留给 batch;
- 任务是分割/深度/检测头,对逐像素质量敏感 → 对应尺寸的reg 版。
四款的 patch 都是 14、默认输入都是 518,切换尺寸不用改预处理。
⚡ 最快上手:三行加载 + 十行代码跑通特征提取
DINOv2 安装对环境的要求压成一张表:
| 组件 | 版本要求 |
|---|---|
| Python | 3.9 |
| PyTorch | 2.0.0(CUDA 11.7 编译版本) |
| torchvision | 0.15.0 |
| xformers | 0.0.18 |
| GPU 显存 | 推理 ≥8GB,训练建议 ≥16GB |
拉代码并建好环境(官方 conda 配置已锁好版本):
git clone https://gitcode.com/GitHub_Trending/di/dinov2 cd dinov2 conda env create -f conda.yaml && conda activate dinov2下面是能直接跑通的最小片段,一次拿到整图特征:
import torch from PIL import Image from torchvision import transforms model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitb14').eval() prep = transforms.Compose([transforms.Resize(518), transforms.CenterCrop(518), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) x = prep(Image.open('cat.jpg').convert('RGB')).unsqueeze(0) with torch.no_grad(): print(model(x).shape) # torch.Size([1, 768])两个容易踩的点:第一次调用会从服务器下载预训练权重并缓存到~/.cache/torch/hub,所以首跑"卡住"是在下文件,不是死机;model(x)返回的是 CLS token,想要逐块特征时改调model.forward_features(x),从返回字典里取x_norm_patchtokens。
模型入口都在 backbones.py,权重枚举和下载逻辑写在一起,想看它怎么拼 URL 可以直接翻这个文件。
特征流水线:一张图怎么变成向量
用人话拆开讲:
- 先切再译:每张 14×14 的小块被一次线性变换映射成 D 维向量,就是一个 token。整张图从此变成 1369 个 token 的"句子",交给 Transformer 逐层做注意力;
- CLS token 是"摘要":它不来自图像任何区域,靠每层注意力收集全局信息。所以分类、检索直接用它就够了;
- patch token 是"逐词注释":顺序和空间位置严格对齐,做分割、检测时就是现成的特征图原料;
- register token 是草稿纸:注意力计算中的"溢出量"会堆到这几个额外 token 上,CLS 和 patch 特征反而更干净——这就是 reg 版在密集任务上更稳的原因。
上面这张图来自仓库内 Cell-DINO(DINOv2 适配细胞显微成像的版本)的文档,右侧 B 面板画的正是 patch → token → 自注意力这条主线,可以对照流程图看。
🎯 场景实战:三个高频任务各十行以内
图像检索:把图片压成向量再找相似
import torch.nn.functional as F def vec(path): x = prep(Image.open(path).convert('RGB')).unsqueeze(0) with torch.no_grad(): return F.normalize(model(x), dim=-1) q = vec('query.jpg') db = torch.stack([vec(p) for p in gallery]) # (N, 768) print((db @ q).topk(5).indices.tolist()) # 最相似的 5 个编号L2 归一化之后内积就等于余弦相似度。跑一遍你会发现:返回的第一张图在语义上(同一只猫、同一种构图)而不是逐像素意义上最接近查询图——这正是自监督特征的价值。
图像分类:只训一层线性层
import torch.nn as nn class LinearHead(nn.Module): def __init__(self, dim=768, k=1000): super().__init__() self.fc = nn.Linear(dim, k) head = LinearHead().cuda() opt = torch.optim.LBFGS(head.parameters(), max_iter=20) # backbone 全程 eval(),只有 self.fc 在更新骨干冻结后待训练参数只有 768×1000 量级,ImageNet-1K 这类线性探测单卡几十分钟能收敛。工程化版本(多层特征拼接、KNN 评估、分布式训练)在 dinov2/eval/linear.py,KNN 评估脚本 也在同一目录,数据少于 1 万张时建议直接跑这两个脚本而不是自己写训练循环。
语义分割:先拿 37×37 的特征图
with torch.no_grad(): patch, cls = model.get_intermediate_layers( x, n=1, return_class_token=True, reshape=True) print(patch[0].shape) # (1, 768, 37, 37),可直接喂给分割头reshape=True会把 patch token 还原成和输入对齐的空间图,接一个 DPT 解码头就是完整的分割管线。完整实现(骨干 + DPT 头 + 训练循环)在 dinov2/eval/segmentation/,不想读代码的话直接打开 notebooks/semantic_segmentation.ipynb 跟着点。
🛠 微调要点与高频坑
三种策略,一句话对照
- 线性探测:骨干全冻,只训分类头。数据少于 1 万张、或想先要个基线,就它;
- 部分微调:解冻最后几层,骨干学习率压到头的 1/10 左右。万级以上数据的中档选择;
- 全量微调:所有参数都动。数据十万级起,且要盯着验证集防过拟合。
五个高频坑及解法
- CUDA 版本不匹配:torch 2.0.0 只和 CUDA 11.7 的 wheel 严格配套。装成 torch 2.1 再配 xformers 0.0.18,报错里会刷一片 undefined symbol——把两者重装回配套版本即可;
- xformers 编译失败:九成是版本对不上。优先用 conda 通道里的预编译 wheel(
conda env create会直接带上),实在要源码编译时设MAX_JOBS=4降低内存峰值; - 显存不够:vitg14 推理就要 10GB 上下。三步走:降到 vitl14/vitb14、输入从 518 缩到 224、开
torch.cuda.amp.autocast()半精度推理; - 分割输出出现花屏或黑边:多半是输入尺寸不是 14 的倍数,位置编码插值没对齐。统一用 518 或 14 的任意整数倍;
- 首次运行像卡死:在下载权重。瞄一眼
~/.cache/torch/hub里文件大小在涨就是正常的,第二次起直接命中缓存。
📌 收尾:记住这五句就够了
- DINOv2 = 开箱即用的自监督特征提取器,一行
torch.hub.load起步,四尺寸任选。 - 默认三件套:vitb14、输入 518、ImageNet 归一化,跑不通先从这三样查起。
- CLS token 管分类和检索,patch token 管密集预测,reg 版让后者更稳。
- 数据 1 万张以内上线性探测,更多数据再逐层解冻。
- 想进垂直领域,仓库里现成的 Cell-DINO(细胞成像)和 XRay-DINO 是最好的参考起点。
延伸阅读:
- 线性 / KNN / 分割 / 深度四类评估脚本全集:dinov2/eval/
- Cell-DINO 领域适配案例与架构图说明:docs/README_CELL_DINO.md
【免费下载链接】dinov2PyTorch code and models for the DINOv2 self-supervised learning method.项目地址: https://gitcode.com/GitHub_Trending/di/dinov2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考