DINOv3 零样本语义分割完全指南:从逐类标注到像素级类别图
【免费下载链接】dinov3Reference PyTorch implementation and models for DINOv3项目地址: https://gitcode.com/GitHub_Trending/di/dinov3
DINOv3 是 Meta 的自监督视觉基础模型家族,其中 dino.txt 分支把视觉与文本对齐到同一特征空间,让你不标注、不训练就能做 DINOv3 零样本分割。读完本文,你可以写出可运行的像素级类别图推理脚本。
一、为什么需要它:问题与方案对比 🔍
传统语义分割流程从像素级标注开始,而这一步恰恰最贵:换一个新任务就要重新标一轮,类别一变又要重标。部分团队尝试 CLIP 类开放词汇方案,但它的视觉侧特征主要依赖单个 CLS token,遇到细线结构和小目标时,密集的空间细节明显掉档。
DINOv3 换了路线:骨干先用自监督学出 patch 级密集特征,再由 dino.txt 通过对比学习把文本编码器对齐到同一空间,于是任意像素的特征都能直接与任意文本匹配。
- 密集特征在 patch 粒度,空间细节更强
- 零样本换类别,不训练即可用
- 文本对齐靠 dino.txt 直接给出
- 多头复用分类检测深度分割共用
二、5 分钟跑通:准备环境并运行首个推理代码 ⚡
先确认你有 Linux 环境、PyTorch ≥ 2.7.1 和可用的 micromamba。
- 克隆仓库并创建环境:
git clone https://gitcode.com/GitHub_Trending/di/dinov3 cd dinov3 micromamba env create -f conda.yaml micromamba activate dinov3- 运行最小脚本,验证双塔能输出同空间特征:
import torch from PIL import Image from torchvision import transforms from dinov3.hub.dinotxt import dinov3_vitl16_dinotxt_tet1280d20h24l model, tokenizer = dinov3_vitl16_dinotxt_tet1280d20h24l() # 权重拉不下来时,用 weights= 与 backbone_weights= 指到本地 .pth model.to("cuda").eval() text_feats = model.encode_text(tokenizer.tokenize(["a photo of a cat."]).to("cuda"), normalize=True) t = transforms.Compose([ transforms.Resize(512, antialias=True), # 短边对齐,保持原图纵横比 transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), ]) img_feats = model.encode_image(t(Image.open("cat.jpg")).unsqueeze(0).to("cuda"), normalize=True) print((img_feats * text_feats).sum(-1)) # 该相似度越高,图像被判为 cat 的把握越大如果你看到下载失败或哈希校验报错,通常是权重 URL 不对或本地文件不完整,核对
dinov3/hub/backbones.py中的默认下载地址,并指向正确的本地路径。
三、拆解零样本分割工作流:三步代码走读 🔧
把整条流程拆成三个操作:编码文本 → 提取密集特征 → 映射相似度,每一步的输出都是下一步的输入。
3.1 批量编码类名,生成 dino.txt 文本特征
这一步把类名变成归一化的文本向量,多个提示模板取平均以抵消措辞差异。
import torch.nn.functional as F PROMPT_TEMPLATES = ["a photo of a {0}.", "a close-up photo of a {0}."] text_feats = [] for name in class_names: # class_names 是目标类名字符组 tokens = tokenizer.tokenize([t.format(name) for t in PROMPT_TEMPLATES]).to("cuda") feats = model.encode_text(tokens) feats = feats[:, feats.shape[1] // 2:] # 前半段对应 CLS token,密集任务用不到 feats = F.normalize(feats, dim=-1).mean(0) # 对模板取平均,抹平措辞随机性 text_feats.append(F.normalize(feats, dim=-1)) text_feats = torch.stack(text_feats) # [num_classes, 1024]关键要点:文本特征只需算一次,循环外缓存,之后所有图像复用。
→ 拿到类的向量后,还需要给图像上每个 patch 一个同维向量。
3.2 从图像提取 DINOv3 密集特征
这一步把图像送进骨干和 2 个头部块,输出每个 16×16 patch 一条向量。
import math import torch.nn.functional as F def encode_image(img): # img: [B, 3, H, W] B, _, H, W = img.shape P = model.visual_model.backbone.patch_size new_H, new_W = math.ceil(H / P) * P, math.ceil(W / P) * P if (H, W) != (new_H, new_W): # 先补齐到 patch 整数倍,网格才不会越界 img = F.interpolate(img, size=(new_H, new_W), mode="bicubic") B, _, h_i, w_i = img.shape _, _, patch_tokens = model.visual_model.get_class_and_patch_tokens(img) return patch_tokens.reshape(B, h_i // P, w_i // P, -1) # [B, h, w, D]关键要点:特征网格尺寸为“图像尺寸除以 16”,这就是 DINOv3 密集特征的像素级基础。
→ 两侧都是向量之后,剩下的事就是一次点积。
3.3 计算相似度图,输出 DINOv3 零样本分割结果
这一步用 einsum 一次对齐所有类向量和所有 patch 向量,相似度最高的类即为该位置类别。
def predict_whole(img, text_feats): blocks = encode_image(img.unsqueeze(0))[0] # [h, w, D] blocks = F.normalize(blocks, dim=-1) cos = torch.einsum("cd,hwd->chw", text_feats, blocks) # [num_classes, h, w] return cos # 低分辨率相似度图,放大回原图尺寸后 argmax 即类别图高分辨率图像改用滑动窗口:参考实现中side=384、stride=192,每个窗口内做 softmax 后放大,重叠区域取平均。
关键要点:cosine 相似度要求两侧都先做 L2 归一化,顺序不能反。
四、速览底层机制:从双塔到相似度图 🔬
两个塔最终都投影进同一个 2048 维空间,分割本质上是“patch 特征”与“文本向量”之间的矩阵乘法。
输入 → 变换 → 输出:
image → ViT-L/16(24 层,patch=16)→ patch tokens [h, w, 1024] → 2 个自注意力头部块 + 线性投影 → [h, w, 1024] text → 24 层因果 Transformer(77 tokens)→ 文本 tokens → argmax 池化 + 线性投影 → [1024] align: cos(patch, text) × logit_scale → [num_classes, h, w] 相似度图视觉侧骨干在 dino.txt 训练时冻结,只学 2 个头部块和投影层,因此骨干的密集特征不会被对齐任务带偏。对齐损失是双向 InfoNCE:图像-文本正对拉近,负对推远,logit_scale是可学习的温度系数,等价于把原始点积换算成 softmax 前的 logit。
关键实现分别位于dinov3/models/vision_transformer.py(骨干)、dinov3/eval/text/vision_tower.py(视觉塔)、dinov3/eval/text/dinotxt_model.py(双塔与 logits)。
五、实战场景与调优:两套可直接改的领域配置 🎯
把class_names换成你的领域词表,再调“输入分辨率”和“推理模式”两个参数即可迁移。
5.1 对城市街景做 DINOv3 零样本分割
Cityscapes 共 19 类且图像尺寸大,必须用滑动窗口推理。
class_names = ("road", "sidewalk", "building", "wall", "fence", "pole", "traffic light", "traffic sign", "vegetation", "terrain", "sky", "person", "rider", "car", "truck", "bus", "train", "motorcycle", "bicycle") pred = predict_slide(img, text_feats, side=384, stride=192) # 大图走窗口推理 pred = pred.argmax(0) # [H, W] 的类别索引图| 参数 | 典型值 | 影响 |
|---|---|---|
resize(短边) | 512 | 越大细节越好,显存约按平方增长 |
side/stride | 384 / 192 | side 越大边界越细;stride 小于 side 产生重叠平均 |
| 提示模板数 | 2~4 条 | 细粒度类别下越多越稳 |
⚠️ 滑动窗口模式累加的是各窗口 softmax 的局部平均,不是全局真实概率,不要直接拿去做置信度阈值过滤。
5.2 医学器官分割
医学图像通常不超过 2048 像素,整图模式就够用,重点是控制类别数和模板措辞。
medical = ("lung", "liver", "kidney", "bone", "tumor") tf = encode_text_feats(medical) # 流程同 3.1 pred = F.interpolate( predict_whole(img, tf)[None], size=img.shape[1:], mode="bilinear" )[0].argmax(0) # 低分辨率相似度图放大回原图再取类别| 参数 | 典型值 | 影响 |
|---|---|---|
| 类别数 | 5~10 | 类别过多会拉低 top-1 分布的信噪比 |
| 输入分辨率 | 512~768 | 小病灶靠更高分辨率,先放大图像而非指望模型内插 |
| 提示模板 | 2~3 条 | 器官词歧义少,模板不必多 |
六、速查表与下一步:五处文件先记住 📋
先记住这张表,再决定往哪个方向深入。
| 关键文件/目录 | 用途 | 何时需要修改 |
|---|---|---|
dinov3/hub/dinotxt.py | dino.txt 加载入口,返回模型与分词器 | 需要指定本地权重时 |
dinov3/eval/text/dinotxt_model.py | 定义双塔结构与对齐 logits | 想改池化方式或特征维度时 |
notebooks/dinotxt_segmentation_inference.ipynb | 零样本分割参考实现 | 换数据集或评估指标时 |
dinov3/configs/train/ | 预训练与蒸馏配置 | 自训模型时 |
dinov3/data/datasets/ | ImageNet、COCO 等数据集类 | 接入自定义数据时 |
下一步可以做的事:
- 跑通
notebooks/里其余四个 notebook:PCA 可视化、前景分割、稠密/稀疏匹配、分割跟踪 - 换用 SAT-493M 卫星权重,在遥感数据上评估零样本效果
- 通过 Hugging Face Transformers 或 timm 直接复用骨干,省掉本地加载
- 有自己的图文对时,参照
dinov3/eval/text/configs/dinov3_vitl_text.yaml训练自己的对齐模型
现在你已经拥有一套可运行的 DINOv3 零样本分割流程,下一步就是把你的类别和数据换进去。
【免费下载链接】dinov3Reference PyTorch implementation and models for DINOv3项目地址: https://gitcode.com/GitHub_Trending/di/dinov3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考