3DGS 模型保存与加载完整指南PLY 格式扩展与断点续训实战【免费下载链接】gaussian-splattingOriginal reference implementation of 3D Gaussian Splatting for Real-Time Radiance Field Rendering项目地址: https://gitcode.com/gh_mirrors/ga/gaussian-splatting跑 3D Gaussian Splatting3DGS训练的人大概都经历过这种时刻迭代到 3 万步机器断电 / 显存爆了 / 被同事的脚本抢卡。训练目录里只有一个 .ply重新加载后发现优化器状态全丢等于从半成品倒退回刚起步。其实 3DGS 官方仓库 gaussian-splatting 的模型持久化方案分两条线PLY 文件负责把模型参数完整落盘checkpoint 文件负责把优化器状态落盘。本文沿着存了什么 → 怎么存 → 怎么加载 → 怎么续训 → 体积怎么省这条线把 scene/gaussian_model.py 里的保存与加载机制讲透帮你把崩溃后的损失降到最低。3DGS 模型文件到底存了什么和网格模型差在哪先说人话网格模型OBJ/GLB存的是顶点 面片拓扑而一个 3DGS 模型存的是几百万个可变形椭球每个椭球自带一套参数——位置、三个方向的缩放、朝向四元数、不透明度再加一组球谐SH系数来描述从不同角度看它是什么颜色。这决定了它的模型文件没法套用标准网格格式。官方在 scene/gaussian_model.py 的construct_list_of_attributes里给出了完整清单一张表对比清楚参数字段PLY 列名个数作用椭球中心xyz3空间坐标法向量nxnynz3纯占位值全为 0球谐 DC 项f_dc_0~23基础颜色视角无关部分球谐高阶项f_rest_0~443×(L²−1)L4 时为 45随视角变化的颜色不透明度opacity1椭球实体感强度缩放scale_0~23三轴半径旋转rot_0~34单位四元数定朝向合计23 个属性全部是 float32。注意后四类参数缩放、旋转、不透明度、SH 系数训练时会被持续优化所以文件必须把它们的当前值原样存下来——这正是save_ply的职责。PLY 格式怎么扩展的23 个属性背后的一个关键取舍PLY 本质是一张可自定义列的表格文件element vertex声明有哪些列后面每行一个顶点数据。3DGS 没有发明新格式而是像给表格追加新列一样把 23 个参数全部挂成 vertex 的自定义属性。save_ply的骨架只有四步xyz self._xyz.detach().cpu().numpy() normals np.zeros_like(xyz) # 法线列只作占位 f_dc self._features_dc.detach().transpose(1, 2).flatten(start_dim1).cpu().numpy() f_rest self._features_rest.detach().transpose(1, 2).flatten(start_dim1).cpu().numpy() scale self._scaling.detach().cpu().numpy() rotation self._rotation.detach().cpu().numpy() # ...把各数组沿 axis1 拼接成 (N, 23) 后用 PlyData 逐行写入这里有个容易忽略的关键设计所有参数存的都是原始优化值不是渲染时用的激活值。缩放存的是exp之前的对数空间值不透明度存的是sigmoid之前的值。为什么因为模型加载后往往还要继续训练参数必须和nn.Parameter里的值一一对应才能接上梯度若存激活后的值反推回去就有精度损失续训直接变味。顺带算笔账100 万高斯的 PLY 约100MB每个高斯平均 100 字节左右千万级场景则轻松突破1GB——这个数字后面讲省空间技巧时会再用到。3DGS 模型加载load_ply 如何把文件还原成可训练的模型加载是保存的逆过程load_ply里有两个值得注意的细节。第一个SH 系数数量要动态解析。高阶球谐列数取决于阶数 L3*(L²−1)文件里有多少列不能靠假设得扫描出来再核对extra_f_names [p.name for p in plydata.elements[0].properties if p.name.startswith(f_rest_)] extra_f_names sorted(extra_f_names, keylambda x: int(x.split(_)[-1])) assert len(extra_f_names) 3*(self.max_sh_degree 1) ** 2 - 3这里藏着最常见的坑如果加载时配置的max_sh_degree和训练保存时不一致这个assert会直接炸或者炸之前渲染出怪颜色。scale_、rot_两组列用同样的前缀扫描 数字后缀排序方式解析。第二个张量重组。解析出的 numpy 数组统一包成nn.Parameter并requires_grad_(True)直接建在 CUDA 上比如self._features_rest nn.Parameter( torch.tensor(features_extra, dtypetorch.float, devicecuda) .transpose(1, 2).contiguous().requires_grad_(True))SH 系数在文件里是每行展平的一串数加载时reshape回(N, 3, L²−1)再转置成(N, 1, C)的布局和create_from_pcd初始化时的形状对齐。函数最后一行self.active_sh_degree self.max_sh_degree也很实在既然文件里存的是满阶系数加载后直接拉满。3DGS 断点续训capture 与 restore 的双持久化机制回到开头的痛点训练中断后怎么接着跑答案是两个文件分工。PLY 只存参数而 Adam 优化器的动量exp_avg、二阶矩exp_avg_sq以及自适应点增删用到的xyz_gradient_accum、denom这些训练记忆都不在 PLY 里。train.py 的保存逻辑是每到--checkpoint_iterations指定的迭代就把gaussians.capture()的返回值连同迭代号一起存成.pth# train.py 中iteration 命中 checkpoint_iterations 时 torch.save((gaussians.capture(), iteration), scene.model_path /chkpnt str(iteration) .pth)capture()在 scene/gaussian_model.py 里返回一个 12 元组成员按职责分三类类别成员用途模型参数_xyz_features_dc_features_rest_scaling_rotation_opacity权重本体训练记忆max_radii2D、xyz_gradient_accum、denom、active_sh_degree、spatial_lr_scale点云增删与学习率调度优化器optimizer.state_dict()恢复 Adam 动量与二阶矩恢复侧是restore(model_args, training_args)解包元组后先调training_setup重建优化器再self.optimizer.load_state_dict(opt_dict)把动量灌回去。启动时加--start_checkpoint path/chkpnt30000.pthtrain.py 里就会torch.load后执行gaussians.restore(model_params, opt)从对应迭代继续——动量不丢收敛曲线能无缝接上。高斯模型文件体积怎么优化千万级高斯的三个方向千万高斯的 PLY 超过 1GB存储和加载都会疼。三个可落地的方向量化压缩features_dc/features_rest默认 float32改用numpy.astype(np.float16)落盘可省一半体积代价是加载后转回 float32 有一丁点精度损失。空间分区把点云按空间位置切成多个 PLY 文件部署时只加载视锥相关的块加载与渲染都能减负# 按坐标块切分每块独立写一个 PLY def save_partitioned_ply(xyz, params, partition_size1e6): for i in range(0, xyz.shape[0], partition_size): save_ply(fmodel_part_{i//partition_size}.ply, xyz[i:ipartition_size], params[i:ipartition_size])增量更新只存相对基线模型的 diff 文件适合高频迭代的场景版本管理。加载侧则推荐内存映射延迟读入避免大文件一次性占满内存plydata PlyData.read(path) # 先读元数据 xyz np.memmap(path, dtypef4, moder, offsetplydata.elements[0].data.offset, shape(plydata.elements[0].count, 3))三条策略可叠加先 prune 瘦身再分区最后按需 memmap 加载千万级场景的磁盘和内存压力都能压到可接受范围。3DGS 加载后渲染异常 / 续训失败的常见坑速查现象可能原因解法加载后颜色发灰 / 发怪max_sh_degree与保存时不一致SH 系数维度对不上统一 SH 阶数配置再加载优化器状态加载报错中途做过点云修剪参数数量和快照不匹配修剪走prune_points/_prune_optimizer同步剪 optimizer 状态PLY 体积过大大量低贡献高斯没被清掉保存前执行prune_points按不透明度阈值 0.01 过滤大模型加载卡内存整个文件一次读入分块 PLY numpy.memmap延迟加载最后一行效果直观对比一下低质量阶段的渲染左和收敛后的渲染右差距明显——所以体积优化时别为了省空间把有用的高斯也剪没了。小结一句话收束PLY 存参数.pth 存记忆——save_ply落盘 23 个 float32 属性且坚持存原始优化值capture/restore负责把 Adam 动量和训练统计量完整续上体积问题交给 prune阈值 0.01、量化、分区和 memmap 组合拳。随着实时渲染需求增长更紧凑的专用二进制格式与硬件加速的加载路径大概率会登场但理解这套 PLY checkpoint 的双持久化机制永远是你玩转 3DGS 工程化的基本功。【免费下载链接】gaussian-splattingOriginal reference implementation of 3D Gaussian Splatting for Real-Time Radiance Field Rendering项目地址: https://gitcode.com/gh_mirrors/ga/gaussian-splatting创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考