Transformers DPT 模型完全指南:基于 Vision Transformer 的稠密预测(单目深度估计与语义分割)
DPT(Dense Prediction Transformer)是 🤗 Transformers 仓库中用于稠密预测任务的经典架构,它将 Vision Transformer 用作骨干网络而非卷积网络,从而完成单目深度估计与语义分割。本文以 DPT 官方模型文档 为核心骨架,结合 modeling_dpt.py 与 configuration_dpt.py 等源码,完整梳理其架构脉络、配置项、图像预处理方式与三个模型类的实战用法。读完本文,你将能够理解 DPT 的“多阶段特征重组 + 逐级融合上采样”解码机制,掌握如何用任意 Transformer 骨干(如 DINOv2)搭建 DPT 框架,以及如何直接跑通深度估计与语义分割的推理流程。
概览:什么是 DPT
DPT 模型由 René Ranftl、Alexey Bochkovskiy 和 Vladlen Koltun 在论文 Vision Transformers for Dense Prediction 中提出。根据官方模型文档,该模型论文于 2021-03-24 发表在 Hugging Face Papers,并于 2022-03-28 由社区贡献者 nielsr 合入 Transformers 仓库。
其核心思想非常简洁:以 Vision Transformer(ViT) 取代卷积网络作为稠密预测任务的骨干网络,服务于语义分割与深度估计。论文摘要指出:
我们提出了 dense vision transformers——一种在稠密预测任务中以视觉 Transformer 取代卷积网络作为骨干的架构。我们收集 Transformer 不同 stage 输出的 token,将它们重组成不同分辨率的类图像表示,再用一个卷积解码器逐级融合,最终产生全分辨率预测。Transformer 骨干在恒定且相对较高的分辨率下处理表示,且每一 stage 都具有全局感受野。这些特性使 dense vision transformer 相比全卷积网络能给出更细粒度、更全局一致的预测。实验显示该架构在稠密预测任务上带来了显著提升——在单目深度估计上与 SOTA 全卷积网络相比相对性能最高提升 28%;应用于语义分割时,在 ADE20K 上以 49.02% mIoU 刷新 SOTA。我们进一步验证了该架构可微调到 NYUv2、KITTI、Pascal Context 等较小数据集上并同样取得新 SOTA。
从实现层面看,当前仓库中的 DPT 与 DPT-Hybrid 均由 Intel 实验室与 OpenMMLab 的 mmsegmentation 实现启发而来,这一点在 modeling_dpt.py 的文件头注释中有明确说明。模型类同时声明了对 FlashAttention、SDPA、FlexAttention 与统一注意力后端的支持(见 DPTPreTrainedModel 中 _supports_sdpa = True、_supports_flash_attn = True、_supports_flex_attn = True,源码见 modeling_dpt.py)。
整体架构:从特征序列到稠密预测的流水线
DPT 的前向流程并不是简单地把 ViT 输出接一个上采样层。它的核心创新在于 neck(颈部)模块,该模块位于骨干与输出头之间,由两段组成:DPTReassembleStage(特征重组)+ DPTFeatureFusionStage(多尺度特征融合)。模型文档中对应的 DPTModel、DPTForDepthEstimation、DPTForSemanticSegmentation 三类模型在 modeling_dpt.py 中依次实现,整体数据流为:
- 骨干编码:将图片切分为 patch(默认 16×16),送入 ViT 或任意层次化骨干,取第 2、5、8、11 层(对应
backbone_out_indices=(2, 5, 8, 11))的中间隐状态作为多尺度特征; - 特征重组(Reassemble):把每个中间层的
(batch, seq_len, hidden)序列表示恢复成(batch, channels, height, width)的类图像特征图,并利用 [CLS] readout token 补充全局信息,再按不同 factor 做上/下采样到多分辨率(源码见 DPTReassembleStage); - 特征融合(Fusion):从最深、最粗的特征开始,逐层加入更浅、更细的特征并双线性上采样 ×2,最终得到融合的稠密特征(源码见 DPTFeatureFusionLayer);
- 任务头:将最后一级特征送入深度估计头或语义分割头,产生逐像素输出。
DPTNeck 的完整接线可以在 modeling_dpt.py 中看到:先对每个中间特征做一次 3×3 卷积把通道统一到 fusion_hidden_size(默认 256),再交由 DPTFeatureFusionStage 融合。需要注意的是,当骨干是 Swinv2 这种本身已是层次化 CNN 风格的骨干时(backbone_config.model_type == "swinv2"),会跳过 reassemble 阶段,因为其 feature map 已经是 (batch, channels, height, width) 形式。
Reassemble 中的 readout 处理:三种策略
ViT 每个 stage 的输出都带一个 [CLS] token,而语义分割/深度估计需要逐空间位置的信息。readout_type 参数提供了三种把 [CLS] 信息注入其他 token 的策略(配置校验见 configuration_dpt.py,逻辑实现见 modeling_dpt.py):
"ignore":直接丢弃 [CLS] token,仅用其余 patch token;"add":把 [CLS] 表示加到其余所有 token 上(广播相加),使每个位置都带上全局信息;"project"(默认):先把 [CLS] token 沿空间维度拼接到每个 token 上,维度翻倍为2 * hidden_size,再用一个线性层投影回原维度hidden_size,并施加hidden_act(默认 GELU)非线性。
注意约束:DPT-Hybrid 模式强制要求 readout_type == "project",否则在配置初始化阶段直接抛 ValueError(见 configuration_dpt.py)。
输出头设计
深度估计头(DPTDepthEstimationHead)由 3 个卷积层组成:首层把 fusion_hidden_size(256)减半后立即做 scale_factor=2 的双线性上采样,再经 32 通道卷积与 ReLU,最后 1×1 卷积压缩到单通道输出并再次 ReLU,最后 squeeze 掉通道维。若 add_projection=True,还会在头部前先加一层 3×3 的投影卷积。头部默认使用 head_in_index=-1 指定的最后一级融合特征。
语义分割头(DPTSemanticSegmentationHead)则是:3×3 卷积 → BatchNorm → ReLU → Dropout(比率由 semantic_classifier_dropout 控制,默认 0.1)→ 1×1 卷积映射到 num_labels → 双线性上采样 ×2。此外,训练时可开启 辅助头(DPTAuxiliaryHead,默认启用),它只接收融合特征的最深一级(hidden_states[-1]),产生一个低分辨率的中间预测用于加深监督。
DPTConfig 配置项详解
DPTConfig 继承自 PreTrainedConfig,model_type = "dpt",并声明了子配置 backbone_config(见 configuration_dpt.py)。其关键参数、默认值与含义如下表(依据源码类字段与 docstring,源码见 configuration_dpt.py):
| 参数 | 默认值 | 含义 |
|---|---|---|
hidden_size |
768 |
内置 ViT 骨干的隐层维度 |
num_hidden_layers |
12 |
内置 ViT 骨干层数 |
num_attention_heads |
12 |
注意力头数 |
intermediate_size |
3072 |
ViT MLP 中间维度 |
hidden_act |
"gelu" |
MLP 激活函数 |
hidden_dropout_prob / attention_probs_dropout_prob |
0.0 |
隐层/注意力 dropout |
image_size |
384 |
内置 ViT 期望输入尺寸(DPT-large 风格) |
patch_size |
16 |
patch 尺寸 |
num_channels |
3 |
输入通道数 |
is_hybrid |
False |
是否使用 Hybrid 模式(配合卷积式 BiT 骨干加载 DPT-Hybrid 权重) |
qkv_bias |
True |
注意力 QKV 是否带偏置 |
backbone_out_indices |
(2, 5, 8, 11) |
从骨干取哪些中间层特征;使用 AutoBackbone 时会被置空并由骨干 out_features 决定(见 configuration_dpt.py) |
readout_type |
"project" |
[CLS] readout 注入策略,枚举 "ignore" / "add" / "project" |
reassemble_factors |
(4, 2, 1, 0.5) |
四个重组层的上/下采样倍数 |
neck_hidden_sizes |
(96, 192, 384, 768) |
各骨干特征图要投影到的通道数 |
fusion_hidden_size |
256 |
融合前统一缩放的通道数 |
head_in_index |
-1 |
输出头取哪一级融合特征(-1 即最后一层) |
use_batch_norm_in_fusion_residual |
False |
融合残差块是否使用 BatchNorm |
use_bias_in_fusion_residual |
None(语义上默认 True) |
融合残差块是否使用偏置 |
add_projection |
False |
深度估计头前是否加投影卷积层 |
use_auxiliary_head |
True |
训练语义分割时是否启用辅助头 |
auxiliary_loss_weight |
0.4 |
辅助头交叉熵损失的权重系数 |
semantic_loss_ignore_index |
255 |
语义分割交叉熵损失忽略的标签值 |
semantic_classifier_dropout |
0.1 |
语义分类头 dropout |
backbone_featmap_shape |
(1, 1024, 24, 24) |
仅 Hybrid 模式使用,描述 BiT 骨干特征图形状 |
neck_ignore_stages |
(0, 1) |
仅 Hybrid 模式使用,前两路 readout 特征会被 Identity 直接透传(对应官方实现中前两层不重组的做法) |
backbone_config |
None |
外挂骨干的 PreTrainedConfig 或配置字典 |
pooler_output_size |
None(自动取 hidden_size) |
pooler 输出维度 |
pooler_act |
"tanh" |
pooler 激活函数 |
两个重要的自动行为(在 __post_init__ 中完成,见 configuration_dpt.py):
- 当
is_hybrid=True时,若未提供backbone_config,会自动按 BiT 骨干补全一组默认配置(global_padding="same"、layer_type="bottleneck"、depths=[3, 4, 9]、out_features=["stage1","stage2","stage3"]); - 只有
is_hybrid=True时backbone_featmap_shape与neck_ignore_stages才生效(否则前者为None、后者为空列表); readout_type若非法,配置初始化即报错,便于尽早暴露笔误。
使用 AutoBackbone 自由搭配骨干:以 DINOv2 为例
模型文档重点强调:DPT 框架兼容 AutoBackbone 机制,因此可以复用库中大量现成视觉骨干(如 VitDetBackbone、Dinov2Backbone),而不是被绑死在内置 ViT 上。官方示例给出了用 DINOv2 做骨干、随机初始化一个深度估计模型的完整写法:
from transformers import Dinov2Config, DPTConfig, DPTForDepthEstimation
# 以 Transformer 骨干(如 DINOv2)初始化
# 此时需设置 reshape_hidden_states=False,使骨干输出
# 形状为 (batch_size, num_channels, height, width) 的特征图
backbone_config = Dinov2Config.from_pretrained(
"facebook/dinov2-base",
out_features=["stage1", "stage2", "stage3", "stage4"],
reshape_hidden_states=False,
)
config = DPTConfig(backbone_config=backbone_config)
model = DPTForDepthEstimation(config=config)
这段代码背后依赖两个源码机制:
load_backbone:当is_hybrid=False且提供了backbone_config时,DPTForDepthEstimation会通过 backbone_utils.load_backbone 加载外部骨干,而不是走内置DPTModel(见 modeling_dpt.py);- 动态 patch 尺寸推导:由于 ViT 序列被 reshape 成方形特征图时要求边长可开方,使用非方形输入时会依据
pixel_values的实际高宽与backbone_config.patch_size计算出patch_height、patch_width,再传给重组层做 reshape(见 modeling_dpt.py)。
在仓库测试中,test_modeling_dpt_auto_backbone.py 覆盖了这一组合路径;而 DPT-Hybrid(BiT 骨干)与 Swinv2 骨干的接入逻辑分别由 convert_dpt_hybrid_to_pytorch.py、convert_dpt_swinv2_to_hf.py 等转换脚本佐证——DPT 已经从“特定 ViT 变体”演化为“可插拔骨干的稠密预测框架”。
图像处理器:DPTImageProcessor 与 PIL 后端
DPT 的图像预处理有几个容易踩坑的细节,官方通过自定义的 resize/pad 逻辑处理。仓库中同时存在两个实现文件:
- image_processing_dpt.py:Torch 后端版本的
DPTImageProcessor; - image_processing_pil_dpt.py:PIL 后端版本的
DPTImageProcessorPil,两者的 preprocess 都支持images与segmentation_maps两个输入入口。
预处理默认值与常规图像分类略有差异(依据 PIL 后端类属性,见 image_processing_pil_dpt.py):
size = {"height": 384, "width": 384},默认缩放到 384×384;- 重采样方式为 BICUBIC(双三次);
- 像素值以
1/255rescale,并使用 ImageNet 标准均值/方差归一化; - 新增两个 DPT 专有开关:
ensure_multiple_of(默认 1,保证缩放后尺寸是某数的整数倍)与keep_aspect_ratio(默认 False);两者叠加时先等比缩放到尽量大、再把宽高约束为multiple的整数倍(算法见 get_resize_output_image_size); do_pad(默认 False)配合pad_image做居中 padding,把图像补到size_divisor的整数倍(如原版 DPT 处理宽高需被 14/32 整除的场景),pad 逻辑见 image_processing_pil_dpt.py。
后处理方法同样与模型输出配套:
post_process_depth_estimation(outputs, target_sizes):接收DepthEstimatorOutput,把predicted_depth双线性插值回target_sizes(原图高宽)并返回字典列表;post_process_semantic_segmentation(outputs, target_sizes):接收分割 logits,插值回原图并返回SegmentationMask(可选用return_segmentation_scores=True保留分数),其与另一文件中的对应实现细节见 image_processing_pil_dpt.py 类定义部分。
此外分割场景还支持 do_reduce_labels=True:把标签整体减 1、0 换为 255(背景占位),适配 ADE20k 这类“0 表示背景但背景不在类别表内”的数据集(见 image_processing_pil_dpt.py)。
三个模型类与实战用法
DPTModel:仅骨干编码器
DPTModel(modeling_dpt.py)返回 BaseModelOutputWithPoolingAndIntermediateActivations:它会输出包含中间激活的特征,供上层 DPT 解码流水线取用。它可选择是否附加 DPTViTPooler(取首 token 过线性层 + pooler_act 激活),构造函数中通过 add_pooling_layer 控制;深度估计/分割子类都会以 add_pooling_layer=False 实例化它(见 modeling_dpt.py 与 modeling_dpt.py)。配置类的内置示例即展示了这一用法(见 configuration_dpt.py):
from transformers import DPTModel, DPTConfig
# 初始化一个 dpt-large 风格配置
configuration = DPTConfig()
# 由配置初始化模型
model = DPTModel(configuration)
DPTForDepthEstimation:单目深度估计
该模型由一个骨干编码(内置 ViT 或外挂骨干)、DPTNeck 与 3 层卷积深度头组成。官方 docstring 给出的端到端推理示例(见 modeling_dpt.py)如下:
from transformers import AutoImageProcessor, DPTForDepthEstimation
from PIL import Image
image = Image.open("your_image.jpg") # 换成你自己的图片
image_processor = AutoImageProcessor.from_pretrained("Intel/dpt-large")
model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large")
# 为模型准备输入
inputs = image_processor(images=image, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
# 插值回原始分辨率
post_processed_output = image_processor.post_process_depth_estimation(
outputs,
target_sizes=[(image.height, image.width)],
)
# 可视化预测结果
predicted_depth = post_processed_output[0]["predicted_depth"]
depth = predicted_depth * 255 / predicted_depth.max()
depth = depth.detach().cpu().numpy()
depth = Image.fromarray(depth.astype("uint8"))
注意两点:
- 该模型的
forward支持传入labels,但官方当前尚未实现深度估计训练——一旦传labels会直接抛出NotImplementedError("Training is not implemented yet")(见 modeling_dpt.py),因此Intel/dpt-large这类预训练权重主要用于推理; - 该模型返回
DepthEstimatorOutput(含predicted_depth、可选hidden_states与attentions),默认内部会强制开启 hidden states 输出、按backbone_out_indices裁剪后喂给 neck(见 modeling_dpt.py)。若用户未主动要求,最终输出不会携带完整 hidden states,避免内存浪费。
DPTForSemanticSegmentation:语义分割
模型结构为 DPTModel + DPTNeck + 分割头 + 可选辅助头(见 modeling_dpt.py)。官方 docstring 示例(modeling_dpt.py):
from transformers import AutoImageProcessor, DPTForSemanticSegmentation
from PIL import Image
image = Image.open("your_image.jpg") # 换成你自己的图片
image_processor = AutoImageProcessor.from_pretrained("Intel/dpt-large-ade")
model = DPTForSemanticSegmentation.from_pretrained("Intel/dpt-large-ade")
inputs = image_processor(images=image, return_tensors="pt")
outputs = model(**inputs)
logits = outputs.logits
与前向流程相关的实现事实包括:
- 返回
SemanticSegmenterOutput,主输出为原始分辨率下的logits(1/2 输入分辨率附近); - 训练时(传入
labels)会先把主头与辅助头的 logits 各自插值到labels的空间尺寸,再用CrossEntropyLoss(ignore_index=semantic_loss_ignore_index)分别计算损失,最终loss = 主损失 + auxiliary_loss_weight × 辅助损失(见 modeling_dpt.py); - 若
num_labels == 1却传入 labels,会抛出“标签数需大于 1”的ValueError(见 modeling_dpt.py)。
任务指南与进一步探索
模型文档将 DPT 定位为两个下游任务指南的核心模型实现,对应任务文档位于仓库 语义分割任务指南 与 单目深度估计任务指南,其中涵盖了完整的训练脚本、评估指标与更多调参建议,建议在正式使用前通读。
若想深入源码细节,可以按下面的路径继续阅读当前仓库:
- 配置类与全部参数:见 configuration_dpt.py;
- 完整前向实现(embeddings → encoder → neck → head):见 modeling_dpt.py,其中 DPTFeatureFusionStage 负责从深到浅逐级把低分辨率特征图融合进来;DPT 的“模块化建模”模板见 modular_dpt.py;
- 官方权重转换脚本:仓库内置了从原始 DPT、DPT-Hybrid、DPT-BEiT、DPT-SwinV2、DPT+DINOv2 权重转换到本库格式的脚本,分别对应 convert_dpt_to_pytorch.py、convert_dpt_hybrid_to_pytorch.py、convert_dpt_beit_to_hf.py、convert_dpt_swinv2_to_hf.py、convert_dinov2_depth_to_hf.py,可作为复现预训练权重的参考;
- 测试用例:模型测试见 test_modeling_dpt.py,AutoBackbone 组合路径测试见 test_modeling_dpt_auto_backbone.py(内部以
Intel/dpt-large验证了预测张量形状与数值切片),Hybrid 与 Swinv2 变体的测试分别见 test_modeling_dpt_hybrid.py 与 test_image_processing_dpt.py。
总而言之,DPT 给“Transformer 骨干 + 稠密预测”提供了清晰的模块化范式:通用 ViT 编码多尺度特征 → reassemble 恢复空间结构并注入全局 readout → 卷积解码器逐级融合 → 轻量任务头出预测。在当前仓库中,这一框架既可用于 Intel/dpt-large 等经典权重做零样本推理,也能通过 AutoBackbone 无缝替换成 DINOv2、SwinV2、ViTDet 等新骨干进行定制训练,是理解 Vision Transformer 落地稠密视觉任务的一条理想学习路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00