ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

nerfstudio 损失函数体系深度解析:从 MipNeRF-360 采样损失到深度与法线监督

2026/9/15 15:33:51 拓冰建站 浏览量
nerfstudio 损失函数体系深度解析:从 MipNeRF-360 采样损失到深度与法线监督 nerfstudio 损失函数体系深度解析从 MipNeRF-360 采样损失到深度与法线监督【免费下载链接】nerfstudioA collaboration friendly studio for NeRFs项目地址: https://gitcode.com/GitHub_Trending/ne/nerfstudio导读损失函数是 NeRF 训练的优化核心决定了几何重建的精度与渲染质量。本文以 nerfstudio 官方 API 参考文档 losses.rst 为骨架深入剖析其在nerfstudio/model_components/losses.py中的全部实现包括 MipNeRF-360 的 interlevel/distortion 采样损失、Ref-NeRF 法线损失、Depth-supervised NeRF 与 Urban Radiance Fields 深度监督、monosdf 单目深度损失、显式网格的 TV 正则等并结合 nerfacto、depth-nerfacto、tensorf 等模型中的实际调用与测试用例帮助你理解每类损失的数学动机、源码实现与调参方法。一、损失模块总览一个函数库而非框架在 nerfstudio 中损失函数并非由框架统一强制调用而是作为一个工具箱提供给各模型自行组合。核心文件是 nerfstudio/model_components/losses.py其 docstring 只有一句话Collection of Losses损失函数的集合。模块开头的关键定义如下L1Loss nn.L1Loss MSELoss nn.MSELoss LOSSES {L1: L1Loss, MSE: MSELoss} EPS 1.0e-7L1Loss、MSELoss直接复用 PyTorch 的nn.L1Loss与nn.MSELoss用于最基础的 RGB 颜色损失LOSSES字典是一个小型注册表把字符串L1、MSE映射到对应损失类方便配置系统按名字选取EPS 1.0e-7是全局数值稳定常数用于所有可能出现除零或log(0)的公式中。除了这两个基础损失模块还包含约十余个函数式损失与三个nn.Module类它们按用途可以划分为六大类采样/分布损失interlevel、distortion、法线监督损失orientation、pred_normal、monosdf、深度监督损失ds-nerf、URF、ranking、单目深度专用损失MiDaS、Gradient、ScaleAndShiftInvariant、显式网格正则TV loss以及梯度缩放工具。下文逐一展开。二、RGB 主损失与损失组合流程所有以光度重建为目标的 NeRF 模型nerfacto、vanilla_nerf、mipnerf、instant-ngp 等都以渲染像素与真实像素之间的 L1 或 MSE 作为主损失。以 nerfacto.py 的get_loss_dict为例loss_dict[rgb_loss] self.rgb_loss(gt_rgb, pred_rgb) if self.training: loss_dict[interlevel_loss] self.config.interlevel_loss_mult * interlevel_loss( outputs[weights_list], outputs[ray_samples_list] ) loss_dict[distortion_loss] self.config.distortion_loss_mult * metrics_dict[distortion]可以看到训练损失是一个加权求和的字典rgb_loss打底再叠加采样类正则项。每个项的权重由模型配置控制例如 nerfacto 的默认配置为见 nerfacto.py配置项默认值含义interlevel_loss_mult1.0Proposal 网络蒸馏损失interlevel loss权重distortion_loss_mult0.002MipNeRF-360 畸变损失权重orientation_loss_mult0.0001Ref-NeRF 法线朝向损失权重pred_normal_loss_mult0.001预测法线监督损失权重注意各个量级差异巨大0.002、0.0001这是因为不同损失项的数值范围不同调参时应先观察各项量级再缩放而不是直接改大改小。三、采样与分布正则MipNeRF-360 的两大支柱3.1 interlevel_lossProposal 网络的蒸馏损失多级采样coarse-to-fine / proposal架构中浅层网络负责快速估计密度分布深层网络负责精细渲染。MipNeRF-360 提出用interlevel loss即 proposal loss约束浅层分布的权重视图与深层一致。源码实现losses.pydef interlevel_loss(weights_list, ray_samples_list) - torch.Tensor: c ray_samples_to_sdist(ray_samples_list[-1]).detach() w weights_list[-1][..., 0].detach() loss_interlevel 0.0 for ray_samples, weights in zip(ray_samples_list[:-1], weights_list[:-1]): sdist ray_samples_to_sdist(ray_samples) cp sdist wp weights[..., 0] loss_interlevel torch.mean(lossfun_outer(c, w, cp, wp)) return loss_interlevel关键细节以最后一层最精细的ray_samples和weights作为监督目标且通过.detach()切断梯度避免深层网络被浅层反向影响对前面每一层分别计算lossfun_outer并累加取均值ray_samples_to_sdist把采样的区间端点转换为s空间标准化射线距离坐标(num_rays, num_samples 1)。核心计算lossfun_outerlosses.py度量浅层直方图权重w超过深层上包络w_env的程度w_outer outer(t[..., :-1], t[..., 1:], t_env[..., :-1], t_env[..., 1:], w_env) return torch.clip(w - w_outer, min0) ** 2 / (w EPS)outer函数losses.py是一个高效实现通过torch.cumsum构造权重累积分布cy1再用torch.searchsorted在浅层区间边界处二分查找落在深层包络中的累计质量避免显式构造双重循环。这也是该方法被 MipNeRF-360 官方代码Google Research 的stepfun.py采用的经典技巧。在模型中的使用除 nerfacto 外neus_facto.py、generfacto.py、semantic_nerfw.py 均在训练时叠加该项。在 method_configs.py 中可以看到某些配置把interlevel_loss_mult设为 100.0、distortion_loss_mult设为 1.0说明对于不同方法需要按数值量级大幅调整权重。3.2 distortion_loss 与 nerfstudio_distortion_loss消除漂浮伪影MipNeRF-360 的畸变损失鼓励单条射线上的权重分布紧凑、不产生分散在空白区域的权重对应训练初期常见的floaters伪影。原始公式为$$\mathcal{L}(\mathbf{s}, \mathbf{w}) \iint \mathbf{w}\mathbf{s}(u),\mathbf{w}\mathbf{s}(v),|u-v|,du,dv$$即对所有样本对的权重乘积乘以距离间隔加权求和物理含义是惩罚权重在射线上的分散程度。模块中提供了两个变体distortion_loss(weights_list, ray_samples_list)losses.py直接取最后一层样本计算lossfun_distortionlosses.py后者把区间分为inter跨区间对与intra区间内两部分求和其中intra项系数为 1/3nerfstudio_distortion_loss(ray_samples, densities, weights)losses.pynerfstudio 风格的 ray-based 实现接受RaySamples与可选的densities或weights——若只给densities会先通过ray_samples.get_weights(densities)计算权重若同时传入两者则断言报错。它基于spacing_starts/spacing_ends的中点差计算最终形状为(*bs, 1)。nerfacto 中 distortion 的调用路径值得注意它先在get_metrics_dict中计算nerfacto.py再在get_loss_dict中乘权重nerfacto.py这种先记指标、后计损失的模式便于训练日志中单独观测该项数值。四、法线监督Ref-NeRF 与 monosdf 的思路4.1 orientation_loss可见法线必须朝向相机出自 Ref-NeRF 的朝向损失losses.py惩罚被观察到的表面法线背对相机的情况def orientation_loss(weights, normals, viewdirs): w weights n normals v viewdirs * -1 # 视线方向反向即朝向相机的方向 n_dot_v (n * v[..., None, :]).sum(dim-1) return (w[..., 0] * torch.fmin(torch.zeros_like(n_dot_v), n_dot_v) ** 2).sum(dim-1)视线方向取反得到指向相机的向量v只惩罚n_dot_v 0法线背向相机的部分用torch.fmin(0, n_dot_v)截断正值按权重w[..., 0]加权即可见性越高的样本对损失贡献越大。4.2 pred_normal_loss预测法线与密度法线一致性当网络同时输出预测法线PRED_NORMALS时pred_normal_losslosses.py约束其与从密度场计算的解析法线一致return (weights[..., 0] * (1.0 - torch.sum(normals * pred_normals, dim-1))).sum(dim-1)1 - n·n_pred等价于余弦距离权重加权同上。nerfacto 中的完整用法nerfacto.py展示了计算法线→监督的流水线if self.training and self.config.predict_normals: outputs[rendered_orientation_loss] orientation_loss( weights.detach(), field_outputs[FieldHeadNames.NORMALS], ray_bundle.directions) outputs[rendered_pred_normal_loss] pred_normal_loss( weights.detach(), field_outputs[FieldHeadNames.NORMALS].detach(), field_outputs[FieldHeadNames.PRED_NORMALS])注意weights.detach()与NORMALS.detach()pred_normal_loss 只更新预测法线分支不反向干扰密度与权重分支而 orientation_loss 不 detach 法线让密度场也能通过解析法线收到梯度。4.3 monosdf_normal_loss与单目法线对齐针对表面重建模型如 neusmonosdf_normal_losslosses.py同时用L1 角度误差 余弦相似度约束体积渲染法线normal_pred与单目预测法线normal_gtmonosdf 论文 Eq.14l1 torch.abs(normal_pred - normal_gt).sum(dim-1).mean() cos (1.0 - torch.sum(normal_pred * normal_gt, dim-1)).mean() return l1 cos两者先各自 L2 归一化。该损失在 base_surface_model.py 中被调用权重由mono_normal_loss_mult配置控制。五、深度监督从稀疏深度到单目深度深度信息能显著改善 NeRF 的几何质量。nerfstudio 通过枚举DepthLossTypelosses.py支持三种深度损失风格class DepthLossType(Enum): DS_NERF 1 URF 2 SPARSENERF_RANKING 3DS_NERFDepth-supervised NeRFDeng et al., 2022的分布匹配损失URFUrban Radiance FieldsRematas et al., 2022的 LiDAR 损失SPARSENERF_RANKINGSparseNeRF 的深度排序损失。顶层入口是分发函数depth_losslosses.py它会先处理非欧氏深度当is_euclideanFalse时将 ground-truth 深度乘以射线方向模长directions_norm归一化再按类型分发。5.1 DS_NERF软高斯分布的负对数似然ds_nerf_depth_losslosses.py假设射线终止深度附近呈高斯分布鼓励权重集中在 GT 深度周围depth_mask termination_depth 0 loss -torch.log(weights EPS) * torch.exp(-((steps - termination_depth[:, None]) ** 2) / (2 * sigma)) * lengths loss loss.sum(-2) * depth_mask return torch.mean(loss)sigma为深度不确定度可学习控制高斯峰的宽度lengths相邻采样步长作为积分权重仅对termination_depth 0的有效像素计算。5.2 URF期望深度 视线方向双重约束urban_radiance_field_depth_losslosses.py更复杂包含三部分期望深度损失(termination_depth - predicted_depth) ** 2直接监督网络预测的期望深度视线附近损失在[depth - sigma, depth sigma]区间内让权重分布匹配以 0 为中心、标准差sigma / URF_SIGMA_SCALE_FACTOR的高斯分布其中URF_SIGMA_SCALE_FACTOR 3.0losses.py空域损失对steps termination_depth - sigma遮挡物之前的区域惩罚权重平方强制权重为 0。这三项求和后同样乘以有效像素掩码。整体体现 URF 对 LiDAR 深度前无遮挡、后有物体的物理建模。5.3 SPARSENERF_RANKING无需精确深度的排序损失depth_ranking_losslosses.py是 SparseNeRF 提出的方案只要求深度相对关系正确因此对稀疏/粗略深度尤其鲁棒dpt_diff gt_depth[::2, :] - gt_depth[1::2, :] out_diff rendered_depth[::2, :] - rendered_depth[1::2, :] m differing_signs torch.sign(dpt_diff) ! torch.sign(out_diff) return torch.nanmean((out_diff[differing_signs] * torch.sign(out_diff[differing_signs])))依赖PairPixelSampler的成对采样布局batch 中相邻两个样本来自彼此邻近的像素若批次为奇数先裁掉最后一个样本m 1e-4是 margin避免渲染深度恰好相等只对 GT 与渲染符号不一致的样本对惩罚且惩罚值与符号保持一致方向。5.4 深度损失在 depth-nerfacto 中的装配depth_nerfacto.py 展示了完整的深度监督流水线配置项depth_loss_type: DepthLossType DepthLossType.DS_NERF选择类型depth_loss_mult: float 1e-3控制权重若使用伪深度pseudodepth模块级常量FORCE_PSEUDODEPTH_LOSS与PSEUDODEPTH_COMPATIBLE_LOSSESlosses.py会强制校验类型必须为SPARSENERF_RANKINGdepth_nerfacto.pyDS_NERF/URF走depth_loss记入metrics_dict[depth_loss]SPARSENERF_RANKING走depth_ranking_loss记入metrics_dict[depth_ranking]最后统一乘depth_loss_mult进入loss_dict。六、单目深度专用损失MiDaS / Gradient / ScaleAndShiftInvariant这三者专为归一化的单目深度图设计来自 MiDaS 与 monosdf 相关工作作为nn.Module类存在供表面模型使用。6.1 MiDaSMSELoss掩码 MSE 数据项MiDaSMSELoss 是 MiDaS 论文的数据项逐像素 MSE 后按掩码求和再通过masked_reduction归约。关键点reduction_type支持image或batch决定是按单图归一化还是按整个批次归一化分母为2 * summed_mask源码注释写着multiply by 2 magic number?是继承自原实现的系数底层归约函数masked_reduction位于 utils/math.pybatch模式直接sum / sum(mask)image模式则逐图除以有效像素数后再取均值两者都处理了除零保护。6.2 GradientLoss多尺度梯度一致性GradientLoss 实现论文 Equation 11 的梯度匹配项在 disparity 空间中比较预测与 GT 的 x/y 方向差分并用掩码乘积过滤无效邻域。scales参数控制多尺度step pow(2, scale)逐级降采样默认 4 级使锐利不连续处与 GT 对齐。6.3 ScaleAndShiftInvariantLoss尺度平移不变组合ScaleAndShiftInvariantLoss 解决单目深度绝对尺度未知的问题先用最小二乘闭式解求出把预测深度映射到 GT 的scale/shift再在其上计算损失。其核心公式如下scale, shift normalized_depth_scale_and_shift(prediction, target, mask) self.__prediction_ssi scale.view(-1, 1, 1) * prediction shift.view(-1, 1, 1) total self.__data_loss(self.__prediction_ssi, target, mask) # MiDaS MSE 项 if self.__alpha 0: total self.__alpha * self.__regularization_loss(...) # 梯度项normalized_depth_scale_and_shiftutils/math.py通过 2×2 线性方程组A x b的闭式解求 scale/shift对行列式为 0 的像素置零避免退化alpha默认 0.5平衡数据项与正则项scales默认 4控制梯度正则的尺度数暴露prediction_ssi属性便于外部读取归一化后的预测深度该损失在 base_surface_model.py 中以ScaleAndShiftInvariantLoss(alpha0.5, scales1)实例化配合mono_depth_loss_mult权重使用并在 base_surface_model.py 中对深度做了* 50 0.5的归一化预处理。七、显式网格正则TV 损失Total Variation对 TensoRF 等使用显式特征网格的方法TV 损失鼓励相邻网格体素值平滑抑制高频噪声。实现见 losses.pyh_tv torch.pow((grids[:, :, 1:, :] - grids[:, :, :-1, :]), 2).sum() w_tv torch.pow((grids[:, :, :, 1:] - grids[:, :, :, :-1]), 2).sum() return 2 * (h_tv / h_tv_count w_tv / w_tv_count) / number_of_grids分别计算行方向height与列方向width相邻格差的平方和除以各自元素计数做平均再乘以 2、除以网格数number_of_grids得到总体均值保证与网格分辨率、通道数无关。使用示例TensoRF 模型在 tensorf.py 中对密度平面与颜色平面系数分别施加tv_loss键名为tv_reg_density、tv_reg_color。测试用例 test_losses.py 验证了其正确性全 1 网格 TV 为 0棋盘格相邻差 1网格 TV 恰好为2 * (1 1) 4.0。另外splatfacto 中 3DGS 的球谐网格也通过bilateral_grid_tv_losslib_bilagrid.py使用同类思想。八、梯度缩放工具scale_gradients_by_distance_squared这不是损失而是与损失密切配合的训练技巧来自论文Radiance Field Gradient Scaling for Unbiased Near-Camera Training。实现在 losses.pyray_dist (ray_samples.frustums.starts ray_samples.frustums.ends) / 2 scaling torch.square(ray_dist).clamp(0, 1) for key, value in field_outputs.items(): out[key], _ cast(Tuple[Tensor, Tensor], _GradientScaler.apply(value, scaling))按样本距相机距离的平方截断到 [0, 1]缩放各 field 输出的梯度缓解近相机区域训练偏差通过自定义torch.autograd.Function——_GradientScalerlosses.py——实现前向不变、仅反向乘系数forward原样返回 value 与 scalingbackward返回output_grad * scaling注意缩放仅作用于射线区间 [0, 1] 内。该方法被广泛使用nerfactonerfacto.py、instant-ngpinstant_ngp.py、mipnerfmipnerf.py、vanilla_nerfvanilla_nerf.py、tensorftensorf.py都在 field 前向输出后立即调用是各方法共享的通用技巧。九、各模型损失装配速查表模型使用的损失来自 losses.py参考源码nerfactorgb(L1/MSE)、interlevel、distortion、orientation、pred_normal、梯度缩放nerfacto.pydepth-nerfacto上述全部 ds_nerf/URF/ranking 深度损失depth_nerfacto.pymipnerf / vanilla_nerf / instant-ngprgb 梯度缩放mipnerf.pytensorfrgb TV 正则密度/颜色平面 梯度缩放tensorf.pyneus_factointerlevel monosdf 法线/深度经 base_surface_modelneus_facto.py、base_surface_model.pygenerfactointerlevel、distortion、orientation带迭代退火权重generfacto.pysemantic_nerfwinterlevel、distortionsemantic_nerfw.py其中 generfacto 展示了更精细的调度orientation_loss_mult支持(start, end)二元组通过np.interp在orientation_loss_mult_range指定的迭代区间内线性插值实现训练初期小权重、后期大权重的退火策略generfacto.py。十、工程实践要点数值稳定性所有涉及对数或除法的损失ds_nerf 的-log(weights EPS)、lossfun_outer 的/ (w EPS)都以EPS 1.0e-7兜底自行扩展损失时建议沿用梯度隔离interlevel_loss 对监督目标.detach()、pred_normal_loss 对权重与解析法线.detach()、深度损失的 GT 侧天然无梯度——正确区分被监督对象与梯度来源是组合多损失的关键权重量级差异distortion0.002、orientation0.0001这类小权重对应大数值的分布损失调参应先记录各分项量级nerfacto 将 distortion 放入get_metrics_dict正是为此深度类型匹配使用伪深度时depth_loss_type必须是SPARSENERF_RANKING否则 depth_nerfacto.py 会直接报错提示可测试性tv_loss 有明确的单元测试test_losses.py验证了常值网格 TV0、棋盘格 TV4的精确数值可作为理解其余函数式损失数值行为的参照。结语nerfstudio 的损失模块是多篇经典论文的工程化集合MipNeRF-360 的 interlevel/distortion 解决了采样分布问题Ref-NeRF 与 monosdf 的法线损失约束了表面朝向DS-NeRF/URF/SparseNeRF 三类深度损失覆盖了从稠密 LiDAR 到稀疏排序的监督需求而 TV 损失与梯度缩放则是显式网格和统一训练稳定性的基石。理解每个损失的数学动机、数值量级与梯度流向是在 nerfacto、tensorf、neus 等不同方法间迁移调参的基础。【免费下载链接】nerfstudioA collaboration friendly studio for NeRFs项目地址: https://gitcode.com/GitHub_Trending/ne/nerfstudio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考