资讯动态

gsplat 密度化策略(Strategy)详解:DefaultStrategy 与 MCMCStrategy 的原理、参数与实战用法

发布时间:2026/9/17 7:41:58 来源:尧图企业网站定制
gsplat 密度化策略Strategy详解DefaultStrategy 与 MCMCStrategy 的原理、参数与实战用法【免费下载链接】gsplatCUDA accelerated rasterization of gaussian splatting项目地址: https://gitcode.com/GitHub_Trending/gs/gsplat在 3D 高斯泼溅3D Gaussian Splatting训练过程中高斯点云需要根据渲染误差不断生长与剪枝否则模型要么欠拟合细节、要么被冗余球体拖垮。gsplat 把这一密度化densification与剪枝pruning过程从训练循环中抽象为独立的Strategy模块位于 gsplat/strategy定义了统一的参数格式约定与前后向回调接口并内置了两种可直接使用的策略实现忠实复刻原 3DGS 论文的DefaultStrategy与实现 MCMC 采样思想、点数恒定的MCMCStrategy。读完本文你将掌握两种策略的完整参数含义、与rasterization训练循环的正确接入方式以及底层 duplicate/split/relocate 等操作的实现细节。一、为什么把密度化抽象成 Strategy在rasterization的每个训练 step 中渲染结果info里会携带诸如 2D 投影中心means2d的梯度、radii、gaussian_ids等信息这些信息是密度化决策的依据。gsplat 将如何利用这些信息改写高斯参数封装进 Strategy 基类gsplat/strategy/base.py它定义了四件事check_sanity(params, optimizers)校验参数与优化器是否满足约定格式step_pre_backward(...)loss.backward()之前执行的回调step_post_backward(...)loss.backward()之后执行的回调是实际执行增长/剪枝/搬移的位置各子类还需提供initialize_state()返回一个可变的运行状态 dict由用户在训练循环中持有并在每一步传入。基类的check_sanity核心校验逻辑是可训练参数的键集合必须与优化器字典的键集合完全一致且每个优化器只能有一个param_group即每个优化器恰好对应一个可学习参数# gsplat/strategy/base.py trainable_params set( [name for name, param in params.items() if param.requires_grad] ) assert trainable_params set(optimizers.keys()) for optimizer in optimizers.values(): assert len(optimizer.param_groups) 1这个约定之所以严格是因为策略会就地inplace改写参数张量和优化器内部状态——增长时要给新增高斯补零动量、剪枝时要同步删掉优化器里对应的状态行因此一个参数配一个单组优化器是正确性的前提。二、参数与优化器的标准格式Strategy 要求高斯参数以Dict[str, torch.nn.Parameter]或torch.nn.ParameterDict的形式给出且至少包含四个键{means, scales, quats, opacities}在此之上新增任意数量的属性如 SH 颜色、特征也受支持。每个策略在check_sanity中都会显式断言这四个键的存在见 gsplat/strategy/default.py 与 gsplat/strategy/mcmc.py 中super().check_sanity(...)之后的循环断言。一个合法的参数与优化器组织方式如下与 tests/test_strategy.py 中的用例一致N 100 params torch.nn.ParameterDict({ means: Tensor(N, 3), scales: Tensor(N, 3), quats: Tensor(N, 4), opacities: Tensor(N), colors: Tensor(N, 3), # 任意额外属性也可以 }) # 每个参数一个独立的 Adam单 param_group optimizers {k: torch.optim.Adam([p], lr1e-3) for k, p in params.items()}注意两点细节优化器字典的键必须与可训练参数一致。对requires_gradFalse的键不需要建优化器测试 tests/test_strategy.py 中test_strategy_requires_grad专门验证了含non_trainable_featuresrequires_gradFalse的混合场景下密度化后各张量首维长度依然一致、不可训练键不会被赋予梯度内部工具函数_update_param_with_optimizergsplat/strategy/ops.py在改写参数时会遍历optimizer.param_groups同步删除旧Parameter的优化器状态、按掩码变换状态张量、再挂到新参数上这就是一个优化器单参数组约定被严格执行的原因。三、训练工作流strategy 与 rasterization 的协作文档给出的标准接入流程摘自 docs/source/apis/strategy.rst如下DefaultStrategy需要step_pre_backward在反向传播前对 2D 中心调用retain_grad()from gsplat import DefaultStrategy, rasterization # Define Gaussian parameters and optimizers params: Dict[str, torch.nn.Parameter] | torch.nn.ParameterDict ... optimizers: Dict[str, torch.optim.Optimizer] ... # Initialize the strategy strategy DefaultStrategy() # Check the sanity of the parameters and optimizers strategy.check_sanity(params, optimizers) # Initialize the strategy state strategy_state strategy.initialize_state() # Training loop for step in range(1000): # Forward pass render_image, render_alpha, info rasterization(...) # Pre-backward step strategy.step_pre_backward(params, optimizers, strategy_state, step, info) # Compute the loss loss ... # Backward pass loss.backward() # Post-backward step strategy.step_post_backward(params, optimizers, strategy_state, step, info)对照仓库中的官方示例 examples/simple_trainer.py训练器在配置类中直接持有strategy: Union[DefaultStrategy, MCMCStrategy]默认default_factoryDefaultStrategy初始化时调用self.cfg.strategy.check_sanity(self.splats, self.optimizers)训练循环中按isinstance分支分别调用step_pre_backward仅 DefaultStrategy 需要用于retain_grad与step_post_backward。两个策略的差别之一是MCMCStrategy.step_post_backward多出一个必传的lr参数means 的学习率用于计算噪声注入幅度noise_scale lr * self.noise_lr。initialize_state的设计也值得一提DefaultStrategy.initialize_state(scene_scale1.0)只返回{grad2d: None, count: None, scene_scale: ...}这样的占位 dict真正的张量被延迟到第一个 step 再按梯度所在设备分配源码注释明确说明是为了放到正确设备上MCMCStrategy.initialize_state()则预计算一个 51×51 的二项式系数表binoms供重定位时按多项分布计算新的透明度与尺度。四、DefaultStrategy复刻 3DGS 原论文的密度化DefaultStrategygsplat/strategy/default.py遵循原论文 3D Gaussian Splatting for Real-Time Radiance Field Rendering行为概述周期性**复制duplicate**2D 梯度大但 3D 尺度小的 GS周期性**分裂split**2D 梯度大且 3D 尺度大的 GS周期性**剪枝prune**透明度低于阈值的 GS周期性把不透明度**重置reset**到较低值。若设置absgradTrue则改用梯度绝对值遵循 AbsGS 论文的做法通常效果更佳但需要把grow_grad2d调高例如 0.0008并且rasterization调用时也要传absgradTrue让内核计算绝对梯度。4.1 完整参数说明参数类型默认值含义prune_opafloat0.005透明度post-sigmoid低于该值的高斯会被剪枝grow_grad2dfloat0.00022D 平均梯度超过该值的高斯会被分裂/复制grow_scale3dfloat0.013D 尺度除以scene_scale归一化低于该值的高斯被复制高于则被分裂grow_scale2dfloat0.052D 尺度按图像分辨率归一化超过该值的高斯会被分裂prune_scale3dfloat0.13D 尺度超过该值归一化后的高斯会被剪枝prune_scale2dfloat0.152D 尺度超过该值归一化后的高斯会被剪枝refine_scale2d_stop_iterint0在该迭代后停止基于 2D 尺度的精化设为正整数才启用该功能refine_start_iterint500该迭代之后开始精化refine_stop_iterint15_000该迭代之后停止精化reset_everyint3000每隔多少步重置一次不透明度refine_everyint100每隔多少步做一次生长剪枝pause_refine_after_resetint0重置后暂停精化的步数官方建议可设为训练集图像张数absgradboolFalse用绝对梯度做分裂/复制决策AbsGS 风格revised_opacityboolFalse分裂子球时使用修订透明度公式arXiv:2404.06109实验性verboseboolFalse是否打印每步复制/分裂/剪枝的数量key_for_gradientstrmeans2d从info中取哪个键作为梯度来源2DGS 场景用gradient_2dgs4.2 源码级流程剖析1梯度累积。step_pre_backward只做一件事——info[self.key_for_gradient].retain_grad()确保 2D 投影中心保留反向图。step_post_backward先调用_update_state把 2D 梯度按width/2 * n_cameras、height/2 * n_cameras归一化到 [-1, 1] 屏幕空间然后对每个可见高斯用index_add_累加梯度范数到state[grad2d]、累加可见次数到state[count]。注意它对两种渲染布局都做了兼容packedFalse时按radii 0的掩码从[C, N, 2]取可见项packedTrue时直接用info[gaussian_ids]。2周期性精化。当满足step refine_start_iter and step % refine_every 0 and step % reset_every pause_refine_after_reset时执行_grow_gs与_prune_gs_grow_gs先算平均梯度grads grad2d / count.clamp_min(1)is_grad_high is_small3D 最大尺度exp(scales).max() grow_scale3d * scene_scale判为待复制否则判为待分裂随后duplicate()→split()。先复制再分裂的顺序有讲究代码会把刚复制出来的高斯拼上全零掩码确保它们不会被紧接着的split再次分裂。_prune_gssigmoid(opacities) prune_opa的高斯必删当step reset_every后再叠加3D 尺度过大 prune_scale3d * scene_scale乃至2D 屏幕尺寸过大radii prune_scale2d仅在refine_scale2d_stop_iter 0时启用的剪枝条件。源码注释还特别指出官方 Inria 实现的屏幕尺寸剪枝因 bug 实际从未生效见其 issue #123gsplat 出于完整性实现了它但默认refine_scale2d_stop_iter0保持关闭。精化结束后grad2d、count归零并torch.cuda.empty_cache()释放内存。3不透明度重置。每逢step % reset_every 0 and step 0调用reset_opa把所有 post-sigmoid 不透明度钳制到prune_opa * 2.0并清空对应优化器状态——这与原论文每 3000 步把死球重置回可学习状态的做法一致给已死掉的高斯重新活过来的机会。4split 的细节。gsplat/strategy/ops.py 中split把被分裂高斯的两个子球位置按旋转矩阵 × 尺度 × 标准随机向量采样即在椭球体积内按均匀分布取点子球尺度取scales / 1.6若开启revised_opacity子球透明度取1 - sqrt(1 - sigmoid(p))保持透明度之和不大于 1 的修订公式。所有新增/被替换高斯的优化器动量都重置为零。4.3 一个实用提示由于refine_start_iter/refine_stop_iter等是普通 dataclass 字段训练脚本可以按需缩放这些迭代阈值examples/simple_trainer.py 的shrink_factor逻辑就是把它们按同一系数整体缩放例如对短训练任务成比例压缩refine_stop_iter即可无需改动策略源码。五、MCMCStrategy点数恒定的 MCMC 采样MCMCStrategygsplat/strategy/mcmc.py实现 3D Gaussian Splatting as Markov Chain Monte Carlo 的策略不增删高斯而是把点数维持在cap_max上限内用重定位 采样新增 位置扰动三条途径让高斯分布向数据支持区域收敛周期性把透明度低于min_opacity的死GS搬移relocate/teleport到高透明度 GS 的位置附近周期性按透明度分布**采样新增sample_add**GS目标点数为min(cap_max, 1.05 * 当前点数)即每轮最多增长 5%每个 step 都对位置做协方差加权、透明度门控的噪声注入perturb相当于 Metropolis 式随机游走。5.1 完整参数说明参数类型默认值含义cap_maxint1_000_000高斯点数上限noise_lrfloat5e5采样噪声的学习率系数实际噪声尺度为lr * noise_lrrefine_start_iterint500之后开始 relocate 新增refine_stop_iterint25_000之后停止 relocate 新增噪声注入不受此限制见下行noise_injection_stop_iterint-1停止噪声注入的迭代-1 表示永不停止例如控制器蒸馏阶段单独控制refine_everyint100每隔多少步做一次 relocate 新增min_opacityfloat0.005透明度低于该值视为死GSverboseboolFalse是否打印每步 relocate / 新增数量noise_opacity_tfloat0.005噪声抑制门的透明度过渡点sigmoid 中心noise_opacity_kfloat100.0噪声抑制门的陡峭度与DefaultStrategy不同MCMCStrategy不依赖 2D 梯度因此没有step_pre_backward源码中该函数已被注释掉用户只需在loss.backward()与optimizer.step()之后调用strategy.step_post_backward(params, optimizers, strategy_state, step, info, lr1e-3)其中lr必须是 means 参数当前的学习率。噪声抑制门sigmoid(-k * (opacity - t))的含义是透明度越低的高斯获得的扰动越大从而让死球快速离开无效区域。5.2 relocate 与 sample_add 的共用内核两者都调用 gsplat/relocation.py 的compute_relocation按透明度作多项分布采样源 GS结合被采样到的次数 1作为 ratio用initialize_state预算的二项式系数表反解出新的不透明度与尺度保证搬移/复制后总透明度守恒。relocate把死球参数替换为采样结果并清零其优化器动量sample_add则在尾部拼接新球优化器状态补零。采样时_multinomial_sample在元素数超过2^24时会自动降级到numpy.random.choice避免torch.multinomial的元素数上限。5.3 噪声注入的双后端inject_noise_to_positiongsplat/strategy/ops.py优先走融合 CUDA 内核torch.ops.gsplat.mcmc_perturb_positions实现见 gsplat/cuda/csrc/MCMCPerturbCUDA.cu要求means为连续的 float32 CUDA 张量不满足时回退到纯 PyTorch 路径quat_scale_to_covar_preci算出协方差再用einsum(bij,bj-bi, covars, noise)做协方差加权。可通过环境变量GSPLAT_MCMC_BACKEND强制选择cuda/native/不设为优先 CUDAtorch/pytorch/py强制 PyTorch 回退路径非法取值会打印 warning 并回退默认行为。对应的 CUDA 侧还有单测 tests/test_mcmc_perturb.py。六、验证与回归测试策略模块的行为由 tests/test_strategy.py 覆盖test_strategy用 100 个随机高斯 单视角rasterization分别驱动两种策略走一遍 pre/post backward 全流程test_strategy_requires_grad验证混合可训练性下密度化后所有参数张量首维仍保持一致、requires_grad属性不被破坏test_mcmc_strategy_positional_constructor则确认MCMCStrategy的位置参数顺序cap_max, noise_lr, refine_start_iter, refine_stop_iter, noise_injection_stop_iter, refine_every, min_opacity, verbose, noise_opacity_t, noise_opacity_k对按位置传参的用户是一个有用的对照。此外examples/simple_trainer.py 底部还演示了一个组合约束DefaultStrategy与eval3d渲染模式不兼容其retain_grad依赖不成立该模式下应改用MCMCStrategy即mcmc子命令——这是选型时值得注意的一点。七、小结Strategy 是 gsplat 中密度化/剪枝的统一抽象固定{means,scales,quats,opacities}键 一参数一单组优化器 的格式约定配合check_sanity → initialize_state → step_pre_backward → loss.backward() → step_post_backward的标准循环接入DefaultStrategy复刻 3DGS 原论文的复制/分裂/剪枝/重置四板斧16 个可调参数覆盖了增长阈值、剪枝阈值、精化节奏与 2D 尺寸限制并内置 AbsGS 绝对梯度模式MCMCStrategy用 relocate sample_add 位置扰动保持点数恒定规避了原策略中点数爆炸问题代价是需要额外传入 means 学习率lr所有参数改写都通过 gsplat/strategy/ops.py 中统一的_update_param_with_optimizer同步优化器状态保证 Adam 动量与参数张量始终对齐若你的场景带有额外组件多组件 sceneops 函数还接受可选的scene回调on_duplicate/on_split/on_remove/on_relocate/on_sample_add保证 gsplat/scene/components/gaussian_scene.py 等上层组件与密度化操作保持索引一致。【免费下载链接】gsplatCUDA accelerated rasterization of gaussian splatting项目地址: https://gitcode.com/GitHub_Trending/gs/gsplat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价