拓冰建站拓冰建站
首页 / 资讯中心 / 正文

单目深度估计评价指标全解析:从RMSE到阈值精度,避开论文与实验中的坑

单目深度估计是计算机视觉里一个看起来简单、做起来却特别容易“自欺欺人”的方向。很多同学跑通第一个模型时看到彩色深度图觉得“挺像回事”结果一算指标发现完全不是那么回事反过来也有不少论文里的指标漂亮得吓人但真把深度图拿去用又觉得哪儿不对。问题往往就出在评价指标上——它不是几行公式那么简单里面藏着尺度、遮挡、度量方式、数据分布等一堆坑。这篇文章就专门把这些评价指标掰开揉碎讲清楚让做实验、写论文、复现模型的人真正看懂每个数字背后的含义。1. 单目深度估计实验评价的整体设计思路先聊点底层的东西。单目深度估计的任务是给定一张RGB图像预测每个像素到相机的距离。和分类、检测不同深度估计的输出是稠密连续值而且天然存在一个“尺度的模糊性”——单张图像本身很难确定绝对尺度一辆车到底是5米远还是10米远仅凭单张图可能有多解。这就决定了评价指标不能像分类准确率那样简单粗暴地比较预测值和真值是否一致而是要设计出能容忍某种程度差异、又能在关键维度上反映误差的度量方式。常出现的指标包括RMSE、AbsRel、SqRel、logRMSE、阈值精度delta等还有不少论文会用尺度不变误差SI、相对误差、以及专门针对深度估计的损失函数。它们各自从不同角度度量预测结果与真值之间的差距有的对数值大的深度更敏感有的对相对误差更友好有的像个“滤镜”把整体尺度偏差过滤掉只看结构。理解这些差异是做实验、选指标的前提。为什么不能只用一个指标因为任何一个单一指标都有盲区。比如RMSE容易被少数大误差像素“带偏”AbsRel倾向于惩罚近处物体的误差而阈值精度delta又只关心“大致准不准”而不关心精确程度。所以学术界的惯例是用一组指标整体评估才能还原模型在不同层面的真实表现。这也是每次实验报告里动辄列五六个指标的原因。从实用角度来说评价指标的选择还直接决定了模型优化的方向。如果训练损失用的是L1或L2损失指标可能跟损失并不完全一致这就在“训练目标”和“评测目标”之间制造了落差。很多人在实验中遇到过这种情况训练损失降了但评价指标没怎么变甚至变差大概率就是训练时用的损失和评测时用的指标对误差的“权重视角”不同。1.1 评价指标在科研和工程中的不同角色从科研角度指标是论文结论的“证据”它决定了你这个方法能不能在SOTA榜单上站住脚。从工程落地角度深度估计的指标直接关系到下游任务——比如自动驾驶中障碍物距离判断、AR场景中的虚拟物体遮挡、三维重建中的尺度正确性。两者对指标的要求并不完全一致科研看的是排名和相对提升工程看的是在特定深度区间内是否足够精准。我个人的建议是不要只盯着论文里最常用的那三四个指标先明确自己场景最关心什么。比如做近距离SLAM建图你可以重点关注近景的AbsRel和结构保持能力做无人机远距离避障就要关注logRMSE和远距离阈值精度。指标选对了实验才具有指导意义否则很容易出现“论文涨点、落地失败”的尴尬。1.2 单目深度估计中的尺度问题指标设计的根源单目深度估计里最核心、也最容易被忽略的坑就是尺度歧义性。假设你输入一张图模型预测每个像素深度为5米但真实距离其实是10米。很多指标比如AbsRel、SqRel、RMSE、logRMSE、阈值精度在计算时会显式或隐式地对整体尺度进行对齐或者直接将尺度因素考虑进误差计算中。这个设计不是随意的而是为了公平地衡量模型在“结构上”的表现而不被一个简单的全局缩放因子所欺骗。因此看到某些论文里指标极高先别急着膜拜有可能是它在评估前做了大量后处理比如中位数缩放对齐。理解这一点你就掌握了单目深度估计评价指标的一把钥匙几乎所有指标都在试图回答同一个问题——“在允许某种尺度模糊的前提下预测结构与真值结构有多接近”后面的所有公式和细节本质上都是对这个问题的不同数学表达。2. 核心指标逐个拆解从公式到直觉下面逐个过一遍最常碰到的指标。每个指标我会先给数学定义再讲它的直觉含义、敏感区间、以及计算时容易踩的坑。2.1 RMSE与SqRel大误差的“放大器”RMSERoot Mean Squared Error是深度估计最常用的指标之一定义为$$ RMSE \sqrt{\frac{1}{N} \sum_{i1}^N (d_i - d_i^*)^2} $$其中( d_i ) 是预测深度( d_i^* ) 是真实深度N是有效像素数。RMSE的直观理解是“平均误差的平方根”但它跟平均误差很不一样。由于先平方再开方它对那些预测值和真值偏差特别大的像素点非常敏感。一个极端例子如果图中有1000个像素其中999个误差都是0.1米只有1个误差是10米那么RMSE会被那一个点拉高到约0.32米而绝对平均误差只有0.01米。这意味着如果你的模型在少数边缘、遮挡、反光区域产生很大的深度残差RMSE会直接“报警”。我见过很多刚开始做深度估计的同学看到自己的RMSE降不下来以为是整体预测不行后来诊断下来发现是少数像素的深度值预测离谱——比如远处天空预测成了近处墙壁。这时候单纯调模型不见得有效先做误差可视化把高误差像素在图上高亮出来会帮你锁定问题。SqRelSquared Relative Error同样是个放大镜性质的指标$$ SqRel \frac{1}{N} \sum_{i1}^N \frac{|d_i - d_i^|^2}{d_i^}$$和RMSE相比SqRel额外除以真值深度相当于把平方误差“按真值深度归一化”。它和RMSE是“近亲”都惩罚大误差但SqRel对大误差的容忍度比RMSE更严格同时更偏向惩罚近处物体的误差——因为在分母上近处小深度值会让分数变大更明显。所以两三个远距离像素的错误就能严重破坏SqRel。实操中RMSE和SqRel适合作为“安全底线”来观察——只要它们不高说明没有灾难性的深度预测错误但只要它们不降不要无脑调参先查异常点。2.2 AbsRel最接近“人眼直觉”的相对误差AbsRelAbsolute Relative Error可以说是论文里出现频次最高的指标$$ AbsRel \frac{1}{N} \sum_{i1}^N \frac{|d_i - d_i^|}{d_i^}$$它衡量的是每个像素预测深度与真值之间的绝对误差但除以真值深度做归一化。从直觉上讲它在问“你预测的深度相对真实深度平均偏差了几个百分点”。这个刻度非常直观AbsRel等于0.1意味着平均相对偏差约10%。AbsRel对近距离物体的误差更敏感因为分母小。比如真值是1米你预测成1.2米AbsRel贡献0.2如果真值是10米预测成10.2米AbsRel只贡献0.02。这个特性使得AbsRel天然适合那些需要精确估计近处物体的任务比如桌面级AR、机械臂抓取、室内导航等。但它也有自己的局限。假设真值为20米你预测为15米AbsRel是0.25看起来很差。但真值20米本来就超出了很多常见传感器的有效范围这种“差”不一定代表模型结构能力弱可能只是尺度漂移。所以单看AbsRel容易被远距离预测误差误导。一个有效的排查方式是分深度区间计算AbsRel比如按0-2米、2-5米、5-10米、10米以上分别统计能清晰展示模型在哪一段真正弱。2.3 logRMSE让误差回归“对数尺度”logRMSE的定义$$ logRMSE \sqrt{\frac{1}{N} \sum_{i1}^N (\log d_i - \log d_i^*)^2} $$它和RMSE的唯一区别是把深度值先取对数再计算误差。这个看似微小的变化带来的效果却很大对数变换把深度从线性空间映射到近似对数空间使得远处大深度的误差权重被压缩近处小深度的误差权重相对提升。换句话说logRMSE惩罚的是“相对比例上的偏差”而非绝对距离偏差。举一个直观的例子真值1米预测1.5米对数值差异约为0.405真值10米预测15米对数值差异同样是0.405。这意味着在logRMSE视角下这两个错误是“等价的”——它们的相对偏差都是50%。而在普通RMSE视角下10米到15米的错误贡献是1米到1.5米错误贡献的100倍。因此logRMSE是一种更“结构友好”的度量它天然忽略全局尺度常数。它的训练目标也常常与深度估计中更常见的BerHu或对数损失兼容。如果你的模型在中远距离上表现波动较大用logRMSE评估会比RMSE更稳定、更有区分度。2.4 阈值精度delta百分之多少像素算“合格”阈值精度的公式虽然简单却经常被误读$$ \delta_i \frac{1}{N} \max\left( \frac{d_i}{d_i^}, \frac{d_i^}{d_i} \right) thr 的像素占比$$通常取thr 1.25、1.25²、1.25³对应δ1、δ2、δ3。它衡量的是预测深度与真值深度之比是否落在一个可控区间内。从直觉上讲它是在回答“预测值在不超出真实值±25%或56.25%、95.3%的前提下有多少像素预测合格”。这里有个关键细节比值是不对称的。预测2米真值1米比率2和预测1米真值2米比率0.5相对差异都是1米但前者max比值为2后者max比值为2不对真值2米预测1米较小值除以较大值后取倒数其实是2所以才需要max取双向比率。这个设计的巧妙在于它同时惩罚过高和过低的预测偏差公平地对待正负误差。δ1高的模型不一定RMSE低因为δ1只关心“是否落在±25%区间内”而完全不关心落在这个区间内的具体误差大小。也就是说一个深度图如果大量像素误差都在20%左右δ1可能很高但RMSE和AbsRel可能并不低。这就是为什么需要多个指标互相补充。用我的话说δ指标的定位是“守门员”——它告诉你模型有没有大面积跑偏真要追求精度还得看RMSE和AbsRel。3. 实操过程从数据预处理到指标计算落地这一章是整篇文章里最偏向“动手”的部分。我会按照一次完整的评测流程来展开从数据准备、深度表示、有效掩码处理到实际计算代码、批量实验管理。每一步都会明确为什么这么做避免大家拿着公式却算不出有意义的结果。3.1 数据准备与预处理统一深度表示和单位绝大多数公开数据集比如KITTI、NYU Depth V2、ScanNet、SUN RGB-D的深度真值都有各自的存储格式。KITTI通常用16位PNG存储毫米为单位NYU Depth V2原始深度是来自Kinect的原始传感器读数想得到米制深度需要除以某个缩放系数ScanNet的深度是16位PNG但单位可能是毫米或厘米不同版本不一致。在做评测之前第一步永远是确认你的预测输出和真值深度是不是同一个度量单位。如果一个是米一个是毫米RMSE瞬间会大上千倍。这在复现论文时特别常见我个人的经验是写一个“数据定义卡”数据集名称深度单位毫米/厘米/米深度范围最小有效距离和最大有效距离无效像素标记值0、NaN、或某个特定值尺寸是否需要裁剪/缩放。将这张卡放在评测脚本的注释里能帮自己和合作者省下大量的调试时间。接着是尺寸对齐。由于模型输出的分辨率与真值很可能不一致所有指标计算之前务必将预测深度resize到与真值完全相同的分辨率并注意插值方式。用双线性插值还是最近邻插值会影响边缘区域的误差尤其是深度骤变的物体边缘不同插值方法会让RMSE产生数个百分点级别的差异。我的建议是固定使用双线性插值并在论文里明确写出。不只如此在产生预测深度时也建议固定输入分辨率因为深度模型对输入分辨率比较敏感。3.2 有效掩码处理谁才有资格参与评价接下来是最容易“作弊”的地方有效像素掩码。深度图中的大量像素可能是无效的——传感器没测到、物体太远太近、或者深度值超出传感器精度范围。如果这些无效像素进入指标计算会严重污染结果。通常的筛选步骤是这样将深度值小于等于0的像素视为无效将深度值大于某阈值的像素视为无效阈值常取10米或80米依据数据集而定。KITTI中常做的是限制在0到80米范围且文献中还会忽略最上面一行的无效扫描线对NYU Depth V2这类室内数据集常限制有效深度在0到10米部分评测还会过滤“深度不连续处”的区域以减少网络在边缘处的固有难题。不同论文对掩码的处理差异很大这也是为什么不同论文之间的指标不能简单对比。如果A论文用了更宽松的掩码比如保留更多远处像素即使算法不如B论文也可能在指标上更漂亮。看到论文里的指标时请一定去补充材料里确认它对深度范围、掩码范围和裁剪方式的定义。实操层面我强烈建议把掩码计算和指标计算写成两个独立函数。掩码函数根据深度真值生成valid_mask指标计算函数接收pred_depth、gt_depth和valid_mask作为输入。这样你可以随时切换掩码规则对比不同规则对结果的影响。3.3 指标计算代码与批量实验管理一份可直接执行的参考下面给出一个用PyTorch实现的核心指标计算函数包含了我前面提到的全部关键细节。这个函数本身不依赖大型库便于嵌入式使用和二次开发。import torch import numpy as np def compute_depth_metrics(pred_depth, gt_depth, maskNone, depth_range(0.1, 10.0)): 计算单目深度估计的核心指标。 pred_depth, gt_depth: torch.Tensor [H, W] 或 [B, H, W]建议先统一到米制单位。 mask: 可选的bool张量True表示有效像素。 depth_range: 过滤真值深度不在该范围内的像素。 if mask is None: mask gt_depth 0 mask mask (gt_depth depth_range[0]) (gt_depth depth_range[1]) mask mask torch.isfinite(pred_depth) torch.isfinite(gt_depth) pred pred_depth[mask] gt gt_depth[mask] eps 1e-6 abs_rel torch.mean(torch.abs(pred - gt) / (gt eps)).item() sq_rel torch.mean(((pred - gt) ** 2) / (gt eps)).item() rmse torch.sqrt(torch.mean((pred - gt) ** 2)).item() log_rmse torch.sqrt(torch.mean((torch.log(pred eps) - torch.log(gt eps)) ** 2)).item() # 阈值精度 ratio torch.maximum(pred / (gt eps), gt / (pred eps)) delta1 torch.mean((ratio 1.25).float()).item() delta2 torch.mean((ratio 1.25 ** 2).float()).item() delta3 torch.mean((ratio 1.25 ** 3).float()).item() return { abs_rel: float(abs_rel), sq_rel: float(sq_rel), rmse: float(rmse), log_rmse: float(log_rmse), delta1: float(delta1), delta2: float(delta2), delta3: float(delta3), }注意这里我加了一个eps防止除零也加了一个全局的深度范围过滤。运行批量实验时我会额外维护一张Excel表或JSON文件记录每次实验的配置与指标结果。推荐按“数据集-模型-输入尺寸-指标”的目录结构保存每次实验输出便于回看。批量统计时务必将单张图的指标在“整图级别”计算而不是先对每张图单独算指标再取平均。虽然两者差异往往不大但官方评测大多采用“把所有有效像素堆在一起再算全局指标”的方式比如KITTI官方评测就是全局PixAcc和mIoU的混合思路。需要注意的是如果你在pixel级别统计时某些图有效像素多、某些图有效像素少全局指标会偏向像素多的图而图像级别平均则公平对待每张图。确定好采用哪种评测口径后论文中要写清楚否则审稿人可能会质疑。3.4 尺度对齐与后处理算指标前的“魔法操作”很多单目深度估计模型预测的深度存在全局尺度漂移尤其是那些没有使用绝对尺度监督的模型。针对这种情况几乎成为默认操作的一步是“中位数缩放对齐”——在计算指标前把预测深度整体乘以一个标量使得预测深度的中位数与真值深度的中位数相等。这步操作在KITTI Eigen split等基准测试中是标准做法比如Zhou等人的无监督方法也遵循这个流程。原理很直接如果预测和真值之间只差一个全局缩放因子那么在消除这个因子之后结构上的误差才能被“纯结构指标”反映出来。但在做这个对齐时有两点必须小心。 一是使用哪个统计量来估计尺度通常用中位数而不是均值因为中位数对离群点更鲁棒。如果你用均值做尺度估计而图中包含大片无效或极远像素尺度因子就会被这些像素带偏。 二是缩放应该基于什么样的掩码来计算。我建议使用与最终评测完全相同的掩码。否则尺度因子计算时包含了一些评测时被忽略的像素可能引入额外偏差。用代码表示的话# 先计算与最终评测相同的有效掩码 mask compute_valid_mask(gt_depth) scale torch.median(gt_depth[mask]) / torch.median(pred_depth[mask]) pred_depth_aligned pred_depth * scale # 然后用 pred_depth_aligned 计算指标代码就这几行但它对指标数字的影响可以非常大——轻松改变几个百分点。所以你看论文时一定要搞清楚它的指标是直接算的还是经过尺度对齐后算的。4. 常见问题与排查技巧实录指标计算表面上简单实际操作中的坑却多得惊人。这里列一些我踩过的、以及帮别人排查过的问题能帮大家少走弯路。4.1 指标与可视化不符的三种典型原因第一种是尺度问题。你看到的可视化深度图可能经过了归一化显示比如把深度值拉伸到0-255范围。这样的图看起来“明暗分明”但实际绝对尺度可能是错的。指标和可视化对不上的时候先检查可视化脚本是否做了隐式归一化。第二种是掩码不一致。可视化里常把无效像素显示为黑色但你计算指标时可能没有过滤掉这些黑点。结果就是可视化看起来“整体预测得挺干净”指标却被无效像素拉垮。建议调试时把mask也可视化出来看看哪些像素参与了计算。第三种是边缘误差的放大效应。深度图像的物体边缘往往存在前景和背景的错位这种错位在视觉上可能不明显但在RMSE、SqRel这类对大误差敏感的指标里会被急剧放大。如果一个模型的边缘误差偏大可视化结果会“看着还行”但指标却差强人意。4.2 不同数据集评价差异举例拿KITTI和NYU Depth V2对比。KITTI是自动驾驶道路场景深度范围大常评测0-80米NYU Depth V2是室内场景有效深度通常限制在0-10米。在这两个数据集上同一模型的指标没有直接可比性。KITTI上RMSE达到6-8米很常见但NYU上RMSE通常小于0.5米——这不是哪个模型更强而是数据的绝对深度尺度完全不同。所以评价模型时一定要结合数据集本身的特点来看指标。如果你自己的测试集和公开数据分布差异很大建议报告分深度区间近、中、远的指标同时给一张误差热力图让读者直观看到误差的空间分布。4.3 常见问题速查表问题现象可能原因排查方法RMSE很大但AbsRel还算正常少数像素存在巨大误差绘制误差热力图定位高误差像素AbsRel很差但δ1很高大量像素误差处于20%-25%之间查看误差分布直方图区分大量中等误差所有指标在训练过程中波动剧烈数据集中深度范围变化大按深度区间分桶统计指标复现论文指标差异大掩码规则、尺度对齐方式或单位不同仔细阅读论文补充材料的评测细节可视化很好但指标极差可视化归一化造成错觉确认可视化脚本用绝对深度渲染图像这个速查表是我在日常调模型中总结的大家遇到具体问题可以对照着看。我给新手的建议是把指标计算代码当成工程的一部分来管理加上单元测试。比如可以构造一个“预测真值”的输入检查所有指标是否等于最优值再构造一个“预测真值1米”的输入观察指标的合理变化。如果指标计算代码本身有bug后面所有实验结论都是建立在沙地上的。5. 教训与扩展选指标时要有全局观讲了这么多细节最后想分享一个我自己的体会。单目深度估计的评价指标本质上是“用数字描述空间感知能力的多维图谱”。一个从事这个方向的研究者不应该只追求把所有指标刷到好看而应该理解每个指标对应的能力维度RMSE和SqRel关注极端误差AbsRel关注近景的相对精准logRMSE关注结构一致性δ指标关注容错区间覆盖率。在实验设计上还有一个细节值得留意不同尺度的深度预测对指标的影响。以logRMSE为例如果你将预测和真值都取对数后再计算误差实际上是在衡量相对误差的对数标准差这会让尺度估计误差被过滤得更彻底。相反RMSE则完全无法容忍尺度误差。你可以通过对比“对齐前”和“对齐后”的指标差异估计模型的尺度漂移程度——如果对齐后指标大幅提升说明模型的主要问题不是结构而是尺度如果对齐后提升很小说明结构本身就有问题。更进一步如果你在做一个下游任务比如3D目标检测或SLAM建议根据下游任务的实际需求来定义属于自己的“任务级指标”。比如自动驾驶中关心的是近距离障碍物的绝对距离精度那可以把RMSE在0-20米范围内的子集单独拿出来算如果在做三维重建可能更关心法线方向误差或点云到网格的倒角距离。深度估计的评价指标不是终点只是通往更好模型的一把尺子。我个人的经验是每跑一批实验先固定评测脚本并且用一两个已知模型比如一个简单的Baseline或官方开源模型做“外部校验”——如果复现的指标和原论文差异在合理范围内比如±0.5%以内再做新模型实验。这样就不会出现换了一个环境、指标整体偏高或偏低却不知道问题出在评测代码还是模型上的窘境。如果你正准备在单目深度估计这个方向深入研究先把这些指标吃透后续看论文、设计实验、对比结果都会顺很多。下次看到一篇论文说“大幅度超越SOTA”不妨先看看它用的评价指标、评测范围、掩码和后处理方式再判断这个“超越”有多少含金量。这可能是这篇文章能带给你的最大价值了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门