GLPN 单目深度估计模型完全指南:从 Mix-Transformer 编码器到轻量解码器(Transformers 仓库实战)
GLPN(Global-Local Path Networks,全局-局部路径网络)是 2022 年提出的单目深度估计(Monocular Depth Estimation)模型,它把 SegFormer 的分层 Mix-Transformer 编码器与一个轻量级解码器组合在一起,从单张 RGB 图像直接回归出稠密深度图。本文基于当前仓库中 docs/source/en/model_doc/glpn.md 的核心内容,结合 src/transformers/models/glpn/ 下的配置、建模与图像预处理源码,系统讲解 GLPN 的架构原理、GLPNConfig 全部超参数、AutoImageProcessor 预处理细节,以及端到端的推理与可视化代码。读完本文,你将能在 Transformers 生态内加载 vinvino02/glpn-kitti、vinvino02/glpn-nyu 等 GLPN 权重完成真实场景的深度估计,并能读懂该模型各模块在仓库中的落点,方便自行微调或二次开发。
GLPN 概览:为深度估计而生
GLPN 由 Doyeon Kim、Woonghyun Ga、Pyungwhan Ahn、Donggyu Joo、Sehwan Chun、Junmo Kim 在论文 Global-Local Path Networks for Monocular Depth Estimation with Vertical CutDepth(论文链接对应 2201.07436)中提出,作者于 2022-01-19 在 HF Papers 发布论文,并于 2022-03-22 将其代码贡献进 Transformers(贡献者 nielsr,社区实现可参考仓库根目录模型说明中给出的原始 GLPDepth 实现线索)。
论文摘要的核心观点可以概括为三点:
- 部署分层 Transformer 编码器来捕获并传递全局上下文;
- 设计轻量却强大的解码器,在考虑局部连接性的同时生成深度图——通过在多个尺度的局部特征与全局解码流之间构建连接路径(selective feature fusion module,选择性特征融合模块),网络能把全局与局部表征整合起来,恢复细节;
- 改进深度专用的数据增强方法(利用深度估计中的重要观测来增强模型)。
实验表明该解码器相比此前提出的解码器,在显著降低计算复杂度的同时取得了更好的性能,并在极具挑战性的 NYU Depth V2 数据集上达到了当时的领先水平,同时展现出更好的泛化与鲁棒性。注意,仓库中并未附带任何性能基准数字表,本文不臆造具体指标——上述结论仅转述原论文摘要文字本身。
需要说明的是,文档开头有一个官方 Tip:由于这是较新引入的模型,API 尚未被广泛测试,可能存在少量 bug 或未来会发生轻微破坏性变更。因此在生产使用前请核对当前仓库版本的实际 API 形态。
架构剖析:SegFormer 风格分层编码器 + 轻量解码器
从 modeling_glpn.py 的类结构可以清晰还原整条前向链路。源码中大量模块带有 "Copied from transformers.models.segformer..." 注释,印证了 GLPN 编码器直接继承自 SegFormer 的分层设计。
分层 Mix-Transformer 编码器(GLPNEncoder)
编码器由 GLPNEncoder 组装,其结构是标准的四阶段金字塔:
- 每个阶段前先经过
GLPNOverlapPatchEmbeddings重叠 patch 嵌入(kernel_size 对应patch_sizes、stride 对应strides,padding 取patch_size // 2),再送入若干GLPNLayer; GLPNLayer内部是 Pre-LN 结构:先 LayerNorm 再做GLPNAttention(注意力内部使用 efficient self-attention / sequence reduction,即把高分辨率阶段的 key/value 用sr_ratios步长的卷积压缩后参与注意力),随后是 Mix-FFN 前馈与残差连接(残差上可施加随机深度GlpnDropPath);GLPNMixFFN内含GLPNDepthWiseConv深度可分离卷积,这正是 Mix-Transformer 用卷积"隐式编码位置信息"、从而无需显式位置编码的关键;- 每个阶段结束后经 LayerNorm 并将序列重新 reshape 回
(batch, channels, height, width)的空间形式,输出到下一阶段,形成多尺度特征金字塔hidden_sizes = (32, 64, 160, 256)(默认小配置)或 (64, 128, 320, 512)(SegFormer-B4 规模,见下文转换脚本)。
注意 GLPNModel 只返回最后一个阶段(即最深层、分辨率最低)的特征作为 last_hidden_state;而解码器需要全部中间特征,因此 GLPNForDepthEstimation.forward 内部会强制以 output_hidden_states=True 调用编码器。
全局-局部解码器与选择性特征融合
GLPNDecoder 是本文标题"Global-Local Path Networks"的落点,代码结构如下:
- 对
config.hidden_sizes反转得到(256, 160, 64, 32),每一层构造一个GLPNDecoderStage(in_channel, out_channel),其中out_channel = decoder_hidden_size(默认 64); - 每一 stage 先用 1x1 卷积把不同通道数统一,再调用
GLPNSelectiveFeatureFusion把上一 stage 上采样后的全局流与当前层的局部特征按注意力权重融合,最后用双线性nn.Upsample(scale_factor=2)上采样; - 最深的第一个 stage(源码
self.stages[0].fusion = None)不做融合,仅做投影与上采样; - 末级
final_upsample再上采样一次,恢复空间分辨率。
选择性特征融合模块(GLPNSelectiveFeatureFusion)正是论文解码器的核心:把局部特征与全局特征沿通道 concat 后,经 3 层 3x3 卷积(Conv2d → BN → ReLU 逐步降维)再通过 Sigmoid 生成双通道空间注意力图,用 local * attn[:,0] + global * attn[:,1] 逐元素加权相加,让网络自适应决定每个空间位置更信任局部细节还是全局语义。
深度估计头与训练损失
GLPNDepthEstimationHead 由两层 3x3 卷积(中间带 ReLU)构成,把 64 通道特征压到 1 通道,然后做关键一步:
predicted_depth = torch.sigmoid(hidden_states) * self.config.max_depth
predicted_depth = predicted_depth.squeeze(dim=1) # 输出形状 (batch, height, width)
即输出值被约束在 [0, max_depth](max_depth 默认 10,单位为米量级的相对深度尺度)。模型也内置了训练损失函数 SiLogLoss(Scale-invariant Logarithmic Loss,源自 Eigen et al. 2014),只对 target > 0 的像素计算 sqrt(mean(d^2) - λ·mean(d)^2)(默认 lambd=0.5),因此在 GLPNForDepthEstimation 中传入真实深度图 labels 即可直接训练。
顶层模型与前向输出
GLPNModel(config):裸编码器(分层 Transformer),输出BaseModelOutput,可用于提取多尺度视觉特征;GLPNForDepthEstimation(config):glpn(编码器) + decoder + head三层组合,返回DepthEstimatorOutput,其中loss(提供 labels 时)、predicted_depth、hidden_states、attentions一应俱全。
两个模型类的 __all__ 导出与 modeling_auto.py 中的映射均可在 src/transformers/models/glpn/modeling_glpn.py 与 auto_mappings.py("glpn" -> GLPNModel / GLPNForDepthEstimation)中找到证据。源码注释中类结构上标明它们由 KAIST 与 HuggingFace 团队维护,仓库归属清晰。
GLPNConfig:全部超参数详解
GLPNConfig 继承自 PreTrainedConfig,model_type = "glpn",默认配置为 vinvino02/glpn-kitti 风格(见 configuration_glpn.py 顶部的 @auto_docstring(checkpoint="vinvino02/glpn-kitti"))。下面把所有字段、默认值与语义整理如下(均可被 GLPNConfig(...) 覆盖):
| 配置字段 | 默认值 | 含义 |
|---|---|---|
num_encoder_blocks |
4 | Mix-Transformer 编码器的阶段(stage)数 |
depths |
[2, 2, 2, 2] |
每个阶段内 Transformer block(层)数量 |
hidden_sizes |
(32, 64, 160, 256) |
每阶段输出的通道数(金字塔宽度);源码类属性中可见 |
sr_ratios |
[8, 4, 2, 1] |
各阶段 attention 的序列缩减(sequence reduction)比 |
patch_sizes |
[7, 3, 3, 3] |
每个阶段 patch 嵌入的卷积核大小 |
strides |
[4, 2, 2, 2] |
每个阶段 patch 嵌入的卷积步长 |
num_attention_heads |
[1, 2, 5, 8] |
各阶段每层注意力头数 |
mlp_ratios |
[4, 4, 4, 4] |
Mix-FFN 隐层相对于输入维度的比例 |
num_channels |
3 | 输入图像通道数 |
hidden_act |
"gelu" |
FFN 激活函数 |
hidden_dropout_prob / attention_probs_dropout_prob |
0.0 | 隐藏层 / 注意力 dropout |
initializer_range |
0.02 | 权重初始化标准差 |
drop_path_rate |
0.1 | 随机深度(DropPath)上限,实际按层线性插值 |
layer_norm_eps |
1e-6 | LayerNorm epsilon |
decoder_hidden_size |
64 | 解码器各 stage 的统一通道维度 |
max_depth |
10 | 深度头 Sigmoid 输出的放大上界(米) |
head_in_index |
-1 | 取解码器输出列表的哪个元素送入 head(默认取最后一个) |
值得注意的底层联动(可在 GLPNEncoder 源码看到):dpr = linspace(0, drop_path_rate, sum(depths)) 会为所有阶段的每一层分配逐步递增的随机深度概率;每个阶段输入通道是上一个阶段的 hidden_sizes[i-1](首个阶段用 num_channels)。
标准用法(与配置 docstring 一致):
>>> from transformers import GLPNConfig, GLPNModel
>>> # 以 vinvino02/glpn-kitti 风格初始化
>>> configuration = GLPNConfig()
>>> model = GLPNModel(configuration) # 随机初始化
>>> configuration = model.config # 取回配置
图像处理器:size_divisor 动态缩放与后处理
GLPN 使用的图像处理器有两个实现:
GLPNImageProcessor:Torchvision 后端(torch 张量流水线);GLPNImageProcessorPil:PIL 后端(numpy 流水线)。
两者在 auto_mappings.py 中通过 AutoImageProcessor.from_pretrained 按 "torchvision"/"pil" 自动路由。它们的默认流水线一致:do_resize=True(BILINEAR 重采样)、do_rescale=True(rescale_factor = 1/255)、归一化由 checkpoint 的 preprocessor_config.json 决定。
GLPN 与一般视觉模型最不同的点在于 resize:不把图像缩放到固定 size,而是把高、宽向下取整到 size_divisor(默认 32)的整数倍(new_h = h // 32 * 32)。这一设计是为了配合分层编码器,使 4 级 [4,2,2,2] 总步长 32 的下采样得到整数空间维度,避免特征图错位。因此文档模型页与 docstring 都把 do_resize/size 的校验逻辑特殊处理(见 _validate_preprocess_kwargs 中 pop 掉 do_resize)。
此外还有 post_process_depth_estimation(outputs, target_sizes) 后处理接口:当提供 target_sizes=[(h, w)] 时,用 F.interpolate(..., mode="bicubic", align_corners=False) 把模型输出(通常为原图缩放到 32 倍数后的预测)插值回原始分辨率;要求 target_sizes 数量与 batch 相等。输出是一个 dict 列表,每项含 predicted_depth。
实战:用 GLPNForDepthEstimation 做单目深度估计
快速推理与可视化
以下是模型 docstring 与源码自带的完整可用示例(数据加载用 httpx + BytesIO,避免额外依赖 requests/datasets),配合上述图像处理器即可零基础跑通推理:
>>> from transformers import AutoImageProcessor, GLPNForDepthEstimation
>>> import torch
>>> import numpy as np
>>> from PIL import Image
>>> import httpx
>>> from io import BytesIO
>>> # 1. 读取一张示例图片(COCO 验证集图片)
>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
>>> with httpx.stream("GET", url) as response:
... image = Image.open(BytesIO(response.read()))
>>> # 2. 加载处理器与模型
>>> image_processor = AutoImageProcessor.from_pretrained("vinvino02/glpn-kitti")
>>> model = GLPNForDepthEstimation.from_pretrained("vinvino02/glpn-kitti")
>>> # 3. 预处理:自动完成 size_divisor 缩放 + 归一化
>>> inputs = image_processor(images=image, return_tensors="pt")
>>> # 4. 推理
>>> with torch.no_grad():
... outputs = model(**inputs) # DepthEstimatorOutput
>>> # 5. 插值回原图尺寸
>>> post_processed_output = image_processor.post_process_depth_estimation(
... outputs,
... target_sizes=[(image.height, image.width)],
... )
>>> # 6. 归一化到 0~255 后可视化深度图
>>> 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"))
关键点:模型输入张量名是 pixel_values(main_input_name = "pixel_values"),predicted_depth 的原始形状为 (batch, H', W')(H', W' 是向下取 32 倍数后的分辨率),后处理会自动回插到 (H, W)。
用 pipeline 一行调用
除手动加载外,仓库在 src/transformers/pipelines/init.py 中已导出 DepthEstimationPipeline(transformers.pipelines.depth_estimation)。在安装 transformers 与 PyTorch 的前提下,可参考以下等价写法:
>>> from transformers import pipeline
>>> depth_estimator = pipeline(task="depth-estimation", model="vinvino02/glpn-kitti")
>>> result = depth_estimator(image) # 返回包含深度预测的结果
提示:具体可用的 task/model 组合以当前仓库版本为准;小批量图像尺寸一致性对 batched 推理有要求,跨尺寸输入建议走
AutoImageProcessor的分组路径。
训练/微调与 loss
如需在自有数据上微调,把真实深度图作为 labels 传入 GLPNForDepthEstimation 即可自动计算 SiLogLoss:
>>> outputs = model(pixel_values=..., labels=depth_map) # depth_map 形状 (batch, H, W)
>>> loss = outputs.loss
损失对深度对数域的方差建模天然具备尺度不变性,适用于以米为单位的 KITTI / NYUv2 数据。
仓库内配套资源
- 模型文档页:docs/source/en/model_doc/glpn.md,其中把 GLPN 定位为
SegFormer(见 SegFormer 文档)的后继,并链向 单目深度估计任务指南(原文档的../tasks/monocular_depth_estimation局部链接在此已按仓库根路径还原)。 - 权重转换脚本:convert_glpn_to_pytorch.py 展示了如何把原始 GLPDepth 的 state_dict 改名为 HF 结构。脚本内可看到官方 checkpoint 的骨架即为 SegFormer-B4 规模:
GLPNConfig(hidden_sizes=[64, 128, 320, 512], decoder_hidden_size=64, depths=[3, 8, 27, 3]),并以expected_shape = torch.Size([1, 480, 640])与torch.allclose数值切片(nyu/kitti各有独立期望值)做端到端一致性校验——这从侧面说明vinvino02/glpn-kitti、vinvino02/glpn-nyu两个官方权重在 480x640 输入下可用。 - 测试用例:tests/models/glpn/test_modeling_glpn.py 覆盖
GLPNModel/GLPNForDepthEstimation的 forward 与 hidden_states/attentions 输出,tests/models/glpn/test_image_processing_glpn.py 覆盖size_divisor缩放与post_process_depth_estimation,可作为复现与回归验证的入口。
小结
GLPN 在 Transformers 仓库中的实现可概括为一句话:SegFormer 分层 Mix-Transformer 负责全局语义,带选择性特征融合的轻量解码器负责逐级融合局部细节,Sigmoid×max_depth 的深度头输出稠密深度图。本文覆盖了从 GLPNConfig 全部超参数、图像处理器的 32 整除缩放策略,到端到端推理、后处理与 SiLogLoss 微调的完整链路。对单目深度估计任务感兴趣的读者,可继续阅读本文引用的 单目深度估计任务指南,并结合 GLPN 建模源码 逐行对照论文中的全局-局部路径设计。
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 StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00