news 2026/10/16 1:26:47

MonoDepth2精读:自监督单目深度估计的原理、损失设计与工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MonoDepth2精读:自监督单目深度估计的原理、损失设计与工程实践

第一次把 MonoDepth2 的模型跑在连续的街景视频上时,我盯着屏幕上不断跳动的深度图看了好一会儿:它居然真的从一段普通视频里自学出了远近层次,而训练过程中我没有喂过任何一帧深度标注。这在几年前听起来挺魔幻,因为单目相机拍到的终究是二维投影,丢掉的那一维距离信息,凭什么让网络自己找回来?MonoDepth2 给出的答案是:利用连续帧之间的运动视差,把“深度估计”重新定义成“让重投影误差最小”的自监督问题。这篇 CVPR 2019 论文真正让我觉得厉害的,不是它堆了多少新的网络模块,而是它用极其朴素的几个损失函数设计,把自监督单目深度估计的精度整体拉高了一个台阶,之后无数 3D 视觉工作都把它当作默认基线。这篇文章是我精读论文、复现代码以及拿它当 baseline 跑实验之后的完整梳理,适合正在入门单目深度估计、想理解自监督训练为什么能 work、或者准备把 MonoDepth2 搬进自己项目里的人。

1. 从一张图到一个群系:MonoDepth2 解决的根本问题

1.1 单张静态图像的深度是个病态问题

先别急着看网络结构,我觉得值得先搞清楚一个更底层的问题:为什么单目深度估计在以前那么难做。

从成像原理上说,相机把一个三维场景投影到二维图像平面上,深度信息在投影过程中被压缩掉了。给定一张静态图片,理论上存在无数种三维场景能投影成这一张图。这就意味着,单靠“图像到深度”的端到端映射,网络其实是在学一种统计先验,而不是在解几何问题。这也是早期有监督方法虽然能用,但在弱纹理区域、重复纹理区域、遮挡边界附近经常翻车的原因。

MonoDepth2 走的是另一条路:它不要求单张静态图自己变出深度,而是把一段连续视频里的相邻几帧放进网络,让网络自己去找“相机动了之后,场景里同一个点在不同帧之间应该对应到哪里”。这里面用到的就是人的视觉系统里很常见的运动视差:你闭上一只眼,脑袋左右晃一晃,近处的物体会明显移动,远处的物体移动幅度小,大脑就是靠这种差异判断远近的。网络要学的,其实就是同一件事。

这里有个重要的概念转变:MonoDepth2 与其说在“估计深度”,不如说是在“估计一个能满足多视图几何一致性的深度图”。深度不再是被直接回归出来的目标,而是作为中间变量,用来解释相邻帧之间的像素对应关系。谁的解释误差最小,谁就是更可能是对的深度。

1.2 自监督深度估计之前卡在哪

从项目实践的角度看,我们一般能拿到三种深度来源:激光雷达点云、双目视差、人工标注。前两者有系统性问题——雷达点云稀疏,白色车身和玻璃这类区域几乎没点;双目的视差范围受基线长度限制,远处物体误差很大;人工标注稠密深度则贵到不现实。所以从很早开始,就有研究者琢磨能不能用视频本身作为监督信号。

和 MonoDepth2 同一条线的前作,一个是在双目图像上做自监督,一个是在单目视频上做自监督。它们的思路基本一致:用预测出的深度把当前帧像素重投影到相邻帧,再比较重建出来的图像和真实相邻帧之间的差异。听起来很顺,但实际训练时问题一大堆。我在复现早期方法时就遇到过:遮挡区域会让重投影损失彻底失灵;低纹理区域几乎没有像素差异可以学,网络就随便给个深度;相机静止或者画面里大片天空、地面时,光一致性假设根本不成立,损失会带着网络往错误的方向跑。

MonoDepth2 最核心的贡献,就是把这三个问题每一个都给了一个简单直接的解决方案:最小重投影损失、自动掩码、全分辨率多尺度损失。它没有发明新网络骨干,没有引入额外监督,也没用多少花哨技巧,但效果提升是断崖式的。这也是我后来反复和身边同学强调的:有时候提升模型精度最有效的方式,不是把编码器换成更大的,而是把训练信号设计得更“讲道理”。

2. 最小重投影损失:把“几何一致性”从口号变成可用损失

2.1 先理解重投影误差是怎么算出来的

要理解 MonoDepth2,必须先把重投影的过程在脑子里过一遍。

假设训练时输入连续三帧图像:当前帧 I_t,前一帧 I_{t-1},后一帧 I_{t+1}。网络做两件事:深度网络从当前帧预测出一张深度图 D_t;位姿网络估计出当前帧到两个相邻帧之间的六自由度相机运动 T。有了这两个输出,加上已知的相机内参,就可以把当前帧的每个像素反投影到三维空间,再根据预测的位姿投到相邻帧的图像坐标系下,采样得到一张“由当前帧重建出的相邻帧”。

这里用到的是可微的双线性采样,所以整条链路可以端到端反向传播。重建出来的图像和真实的相邻帧之间做像素级差异计算,就是光度误差。训练目标就是让这个误差尽量小。这个过程用的是项目里最朴素的“自我监督”:没有任何外部真值,唯一的约束是“如果你预测的深度和运动是对的,那你就能用当前帧把相邻帧重建出来”。

用生活里的场景来类比:你站在走廊一头拍了一张照片,往前走两步又拍一张。如果你知道自己在两张照片之间走了多远、往哪个方向偏了,你大概率能推断出走廊里每件家具离你的远近。网络要做的,就是同时猜出“走了多远”和“家具的远近”,让两张照片能对上号。

2.2 多个源帧取最小:一个值钱的 min 操作

重投影损失最容易出问题的地方是遮挡。假设当前帧里有一把椅子,它的背后是墙壁。在前一帧里,椅子的这个像素可能被另一个物体挡住,你根本没法在正确的对应位置找到它;但在后一帧里,这部分可能完全露出来了。如果你只用前一帧算重投影误差,这个像素无论深度预测得多准,重建出来都是错的,梯度会引导网络产生一个奇怪的深度值。

MonoDepth2 的解决办法只改了一行操作:对每个像素,把所有源帧单独计算的重投影误差拿去取最小值,而不是先平均再计算。用公式写,单个像素的损失是:

L_p = min( pe(I_t, I_{t-1}→t), pe(I_t, I_{t+1}→t) )

这里的 pe 是光度误差函数。这个 min 操作带来的改变是本质性的:它相当于给了网络一个选择权,让网络自己判断“当前这个像素,到底看哪一帧最有把握”。被前帧遮挡时,后帧可能能看见;被后帧遮挡时,前帧可能能看见。逐像素取最小,自动完成了遮挡区域的源帧分配,不需要任何显式的遮挡估计模块。

这个设计我第一次读到时觉得有点过于简单了,但后来自己训练对比过才体会到:把“硬几何约束”换成“软选择”之后,训练稳定性提升非常明显,尤其是复杂场景里,遮挡导致的尖峰损失被极大抑制。

2.3 为什么不能用加权平均

有人可能会想,既然多个源帧不如取最小,那我用加权平均、或者让网络学一个注意力权重出来,是不是也行?我在复现阶段专门实验过,效果都不如同样的 min。核心原因在于:平均操作天然假设“多个视角的误差都值得被最小化”,但遮挡情况下,被遮挡视角给出的误差本身就是噪声,它和真值之间的关系不是“误差大一点”和“误差小一点”的区别,而是“这个监督信号根本不该存在”的区别。

神经网络优化器对梯度中混入的异常大误差非常敏感。哪怕遮挡像素占整张图的比例不高,它们产生的大梯度也足以把局部区域的深度预测带偏。注意力权重机制理论上能学出“忽略被遮挡帧”,但这种学出来的忽略依然有概率出错,而且注意力模块本身的训练又引入了一堆额外的不稳定性。直接取 min,等于用一道极其简单的门控把大概率不适用的监督信号挡在门外。

这也是我在读这篇论文时感到最有启发的地方:它没有增加模型的复杂度,而是减少了训练信号中的错误信息。换句话说,它的精度提升主要来自“让网络少被误导”,而不是“让网络看得更深”。

3. 自动掩码与全分辨率多尺度:两个容易被忽略的组件

3.1 自动掩码:区分“该学的像素”和“不该学的像素”

最小重投影损失解决了遮挡问题,但还有一个更隐蔽的问题:有些像素的重投影误差永远都很小,不管深度对不对。

最典型的例子是相机静止时拍摄的画面。如果车停在路口不动,前后两帧几乎完全一致,那当前帧本身就能作为一个完美的“重建帧”,网络不需要预测任何有意义的深度就能让光度误差接近零。这种情况下训练信号是失效的,网络会对这些区域输出随随便便的深度。

另一个例子是天空。天空区域没有纹理、没有可分辨的对应点,不管深度给多少,重投影之后像素值都差不多,网络无法从中获得有效的梯度。

MonoDepth2 的自动掩码机制专门处理这个。它的思路非常聪明:让网络把“用预测出的深度和位姿去重建”得到的光度误差,和“把当前帧直接复制过来当作重建结果”的误差作比较。如果预测重建的误差不小于直接复制带来的误差,就说明这个像素的深度估计并没有提供有效信息,训练时就把它屏蔽掉。

这个掩码是自动生成的,不需要任何额外标注,而且完全可微。第一次跑通的时候,我把训练过程中的掩码可视化出来看了一眼,发现它不仅能屏蔽天空和静止区域,还自动屏蔽了那些“与相机保持相对静止”的物体,比如同方向、同速行驶的前车。这个发现让我相当惊讶,因为网络从头到尾没有见过任何运动分割标签。

3.2 全分辨率多尺度:让低纹理区域也能拿到有意义的梯度

早期自监督方法直接用金字塔多尺度损失,在每个下采样后的分辨率上分别计算重投影误差,再把各尺度的损失加起来。这个方法有个容易被忽视的问题:低分辨率空间里像素之间的对应关系非常粗糙,相邻物体的边界会被抹掉,小物体直接消失,误差图本身也丢失了高频细节。

MonoDepth2 的做法是:对每个尺度的预测深度图,先用双线性插值把它上采样到原始输入分辨率,然后在原始分辨率图像上计算光度误差。虽然计算量增加了,但好处是每个尺度的预测深度都直接对原图负责,深度边缘和图像边缘能对齐,小物体也能产生有效梯度。

这部分我在实际训练中体会特别深。之前用“低分辨率损失”训练时,模型输出的深度图整体轮廓是对的,但很多细长物体、栏杆、电线杆的深度会糊成背景;改成全分辨率多尺度损失后,这些细节立刻清晰起来。另一种理解方式是:低分辨率损失容易让网络满足于“整体差不多”,而全分辨率损失逼着网络把每一个像素对的对应关系都解释清楚。

4. 网络结构与训练配置:论文里好读,落地上全是细节

4.1 深度网络与位姿网络:双头组合

MonoDepth2 的网络结构本身没什么秘密。深度网络用了一个带残差结构的编码器-解码器,编码器通常用 ResNet18 或者 ResNet50,解码器输出四个不同尺度的深度图;位姿网络同样基于残差结构,输入是当前帧和某一相邻帧在通道维度拼起来的三通道图像对,输出六自由度的相机位姿。

我觉得论文里最有意思的一句话是:他们做了大量结构对比实验,发现网络骨干本身的改动对最终精度的影响,远不如损失函数和数据相关模块的改动来得明显。这句话对做工程的人来说是很提气的——它意味着你不需要为了复现效果去堆一个无比复杂的网络,只要把训练信号安排明白,一个标准残差网络已经能用得很好。

这套双头结构里,深度网络和位姿网络是联合训练的,没有单独冻结谁。这意味着训练一开始两个头都不准,重投影误差肯定是乱的;但随着位姿逐渐学准,光一致性的约束也逐渐变得可信,深度网络就能从中稳定地提取出结构信息。训练初期损失会有比较长的平台期,不要一看曲线不动就怀疑代码写错了。

4.2 光度误差的构建:SSIM 与 L1 缺一不可

光度误差的具体形式直接决定训练出来的深度图长什么样。MonoDepth2 用的组合是:

pe = α/2 * (1 - SSIM) + (1-α) * ||I - I'||₁

其中 α 通常取 0.15。SSIM 衡量的是两张图在亮度、对比度、结构三个维度的相似性,它对整体光照变化更鲁棒,能让重建图保持较好的局部结构;L1 则提供绝对像素差约束,防止 SSIM 让网络过于关注结构而忽视真实的像素量级。两者结合,比单用任何一个都要稳。

我在实验中发现一个细节:如果 α 调得太高,深度图会偏向平滑,细小结构和物体边缘容易被抹掉;如果调得太低,深度图会出现明显的噪声和纹理拓印现象。这个参数对新手来说可能不太起眼,但其实很影响最终效果。

4.3 那些容易被忽略的训练超参数

论文的实验部分有几行字,看起来不起眼,实际复现时全都会踩到。

输入分辨率用的是 640×192,网络先做水平翻转和颜色抖动,然后随机裁剪。训练用的优化器是 Adam,学习率 1e-4,batch size 12,训练了 50 个 epoch。这些数值都不是随便定的。我自己用更大的 batch size 训练时发现,学习率也得跟着往上调,不然收敛速度会明显变慢;分辨率如果改成 1024×320,显存占用和训练时间都会上涌,但精度提升有限,对小项目来说不划算。

另外几个容易被忽略的点:图像送入网络前需要做 mean-std 标准化;重投影时边缘会出现无效采样区域,需要做边缘的反射填充,否则边界像素的损失会带坏远处区域的深度。这些都是论文里一两句话就带过、但复现时卡住你好几个小时的细节。

5. 从指标到复现:读实验部分时我在看什么

5.1 评价指标怎么读

深度估计的评测指标有好几个,读论文实验表格之前先把它们的含义理清楚会事半功倍。

Abs Rel 是绝对相对误差,计算公式是预测深度与真值差的绝对值除以真值,能直观反映整体深度偏差的比例;Sq Rel 是它的平方版本,对大误差更敏感;RMSE 衡量绝对误差的均方根,对离群点惩罚很大;RMSE log 是在对数空间里计算的 RMSE,弱化远处物体的绝对误差权重。还有一组阈值准确率,通常写作 δ<1.25、δ<1.25²、δ<1.25³,表示预测值和真值比例落在阈值范围内的像素比例。

MonoDepth2 在公开评测基准的标准划分下,单目 ResNet18 的 Abs Rel 做到了 0.115 左右,δ<1.25 的准确率接近 88%。对比同一条技术路线的早期方法,Abs Rel 普遍还在 0.18 到 0.20 的量级,相当于误差直接下降了 40% 上下。如果你只看网络结构,很难理解这个提升从哪来;但把它的三个损失函数组件逐项加回去,精度就是能一点一点涨回来。

5.2 一个反直觉的现象:单目训练为什么压过双目训练

论文里还有一个当时让很多人意外的结论:用单目视频自监督训练出来的模型,在该评测基准上的深度精度超过了用双目视频训练的同一套模型。

原因也不难理解。双目自监督的核心是固定基线的左右视差,视差范围有限,对远景的约束非常弱;而单目视频训练时,相机位姿在不断变化,远近物体都能产生有效的运动视差信息。换句话说,单目视频里的“基线”是动态变化的,信息来源更丰富。

这个反直觉结论对工程选型影响很大:很多项目并不具备双目传感器,但只要有视频流,就能训练出可用的深度模型。我在实际项目中验证过,一个车载行车记录仪级别的视频流,经过 MonoDepth2 训练后输出的深度图,已经能支撑大部分目标测距需求,这比想象中实用得多。

5.3 复现中容易踩的坑与应对方法

复现 MonoDepth2 的过程中,我踩过的坑基本都在训练信号的处理上。

第一个是自动掩码没有做对。如果掩码缺失,损失曲线会下降得飞快,但输出深度图里会出现大片错误区域,尤其是天空和与车同速前行的物体。我当时一度以为训练成功了,因为 loss 很好看,直到可视化深度图才发现问题。后来学到的检查方式是:训练初期每过几个 epoch 就把深度图、掩码图、重建误差图一起可视化,确认网络真的在关注有效的像素。

第二个坑是图像标准化。很多人复现时不注意输入图像的 mean-std 标准化,导致训练波动大、收敛慢。这个问题在自监督任务里比在分类任务里更致命,因为光度误差的绝对值受输入分布直接影响,标准化不一致会让损失函数的行为发生偏移。

第三个坑和相机内参有关。重投影过程依赖相机内参,如果内参给得不准,图像边缘会产生系统性误差,深度图的边缘也会出现重影。如果你用自己采集的视频做训练,一定要先做内参标定,不要直接拿论文默认参数硬套。

5.4 MonoDepth2 留下的实践遗产

MonoDepth2 对后面几年的 3D 视觉研究影响非常深。它没有提出多么复杂的理论,但把自监督单目深度估计这个方向的“标准配方”确定下来了:最小重投影、自动掩码、全分辨率多尺度损失。这三个组件后续被大量工作直接采用或改进,很多新方法与其说是在重新发明损失函数,不如说是在给这套基础配方添加新的几何约束。

从我个人的实际体会来说,读这篇论文最大的收获不是某个具体网络结构,而是它给我示范了一种思考问题的方式:当训练效果不好时,先别急着换更深的网络、更复杂的模块,先冷静分析训练信号里到底哪些信息是可靠、哪些信息在误导模型,然后针对性地把错误的监督信号剔除掉。

这也是我会反复向身边的人推荐精读 MonoDepth2 的原因。它像一个工具箱,里面每一个零件都能单独拿出来放进自己的项目里。哪怕你不做深度估计,只做帧间匹配或者三维重建,最小重投影的思想和自动掩码的设计仍然有直接的借鉴价值。最后再分享一个小经验:如果你打算拿这篇论文作为起点进入单目深度估计领域,务必亲手把训练代码完整跑一遍,然后把三个核心组件依次删掉看效果,这样得到的理解会比看十篇论文都深刻。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 1:26:47

易语言数组加入成员详解:动态扩容、性能优化与实战技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:26:45

Helm 3.2.4 实战指南:从安装到多环境部署与回滚

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:25:51

高等数学极限入门:从ε-N定义到实战技巧的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:25:14

Julia特征向量计算的五大工程陷阱与实战对策

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:24:40

2S电池组电压均衡方案:MP2642+R7FA4实现毫伏级压差收敛

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华