3D Gaussian splatting 05: 代码阅读-训练整体流程

目录

  • 3D Gaussian splatting 01: 环境搭建
  • 3D Gaussian splatting 02: 快速评估
  • 3D Gaussian splatting 03: 用户数据训练和结果查看
  • 3D Gaussian splatting 04: 代码阅读-提取相机位姿和稀疏点云
  • 3D Gaussian splatting 05: 代码阅读-训练整体流程
  • 3D Gaussian splatting 06: 代码阅读-训练参数
  • 3D Gaussian splatting 07: 代码阅读-训练载入数据和保存结果
  • 3D Gaussian splatting 08: 代码阅读-渲染

训练整体流程

程序入参

训练程序入参除了训练过程参数, 另外设置了ModelParams, OptimizationParams, PipelineParams三个参数组, 分别控制数据加载、渲染计算和优化训练环节, 详细的说明查看下一节 06: 代码阅读-训练参数

    # 命令行参数解析器parser = ArgumentParser(description="Training script parameters")# 模型相关参数lp = ModelParams(parser)op = OptimizationParams(parser)pp = PipelineParams(parser)parser.add_argument('--ip', type=str, default="127.0.0.1")parser.add_argument('--port', type=int, default=6009)parser.add_argument('--debug_from', type=int, default=-1)parser.add_argument('--detect_anomaly', action='store_true', default=False)parser.add_argument("--test_iterations", nargs="+", type=int, default=[7_000, 30_000])parser.add_argument("--save_iterations", nargs="+", type=int, default=[7_000, 30_000])parser.add_argument("--quiet", action="store_true")parser.add_argument('--disable_viewer', action='store_true', default=False)parser.add_argument("--checkpoint_iterations", nargs="+", type=int, default=[])parser.add_argument("--start_checkpoint", type=str, default = None)args = parser.parse_args(sys.argv[1:])

开始训练

程序调用 training() 这个方法开始训练

torch.autograd.set_detect_anomaly(args.detect_anomaly)
training(lp.extract(args), op.extract(args), pp.extract(args), args.test_iterations, args.save_iterations, args.checkpoint_iterations, args.start_checkpoint, args.debug_from)

初始化

以下是 training() 这个方法中初始化训练的代码和对应的注释说明

# 如果指定了 sparse_adam 加速器, 检查是否已经安装
if not SPARSE_ADAM_AVAILABLE and opt.optimizer_type == "sparse_adam":sys.exit(f"Trying to use sparse adam but it is not installed, please install the correct rasterizer using pip install [3dgs_accel].")
# first_iter用于记录当前是第几次迭代
first_iter = 0
# 创建本次训练的输出目录和日志记录器, 每次执行训练, 都会在 output 目录下创建一个随机目录名
tb_writer = prepare_output_and_logger(dataset)
# 初始化 Gaussian 模型
gaussians = GaussianModel(dataset.sh_degree, opt.optimizer_type)
# 初始化训练场景, 这里会载入相机参数和稀疏点云等数据
scene = Scene(dataset, gaussians)
# 初始化训练参数
gaussians.training_setup(opt)
# 如果存在检查点, 则载入
if checkpoint:(model_params, first_iter) = torch.load(checkpoint)gaussians.restore(model_params, opt)# 设置背景颜色
bg_color = [1, 1, 1] if dataset.white_background else [0, 0, 0]
background = torch.tensor(bg_color, dtype=torch.float32, device="cuda")# 初始化CUDA事件
iter_start = torch.cuda.Event(enable_timing = True)
iter_end = torch.cuda.Event(enable_timing = True)# 是否使用 sparse adam 加速器
use_sparse_adam = opt.optimizer_type == "sparse_adam" and SPARSE_ADAM_AVAILABLE 
# Get depth L1 weight scheduling function
depth_l1_weight = get_expon_lr_func(opt.depth_l1_weight_init, opt.depth_l1_weight_final, max_steps=opt.iterations)# Initialize viewpoint stack and indices
viewpoint_stack = scene.getTrainCameras().copy()
viewpoint_indices = list(range(len(viewpoint_stack)))
# Initialize exponential moving averages for logging
ema_loss_for_log = 0.0
ema_Ll1depth_for_log = 0.0# 初始化进度条
progress_bar = tqdm(range(first_iter, opt.iterations), desc="Training progress")

迭代训练

从大约73行开始, 进行迭代训练

first_iter += 1
for iteration in range(first_iter, opt.iterations + 1):

对外连工具展示渲染结果

    # 这部分处理网络连接, 对外展示当前训练的渲染结果if network_gui.conn == None:network_gui.try_connect()while network_gui.conn != None:try:net_image_bytes = None# Receive data from GUIcustom_cam, do_training, pipe.convert_SHs_python, pipe.compute_cov3D_python, keep_alive, scaling_modifer = network_gui.receive()if custom_cam != None:# Render image for GUInet_image = render(custom_cam, gaussians, pipe, background, scaling_modifier=scaling_modifer, use_trained_exp=dataset.train_test_exp, separate_sh=SPARSE_ADAM_AVAILABLE)["render"]net_image_bytes = memoryview((torch.clamp(net_image, min=0, max=1.0) * 255).byte().permute(1, 2, 0).contiguous().cpu().numpy())# Send image to GUInetwork_gui.send(net_image_bytes, dataset.source_path)if do_training and ((iteration < int(opt.iterations)) or not keep_alive):breakexcept Exception as e:network_gui.conn = None

更新学习率, 选中相机进行渲染

    # 记录迭代的开始时间iter_start.record()# 更新学习率, 底下都是调用的 get_expon_lr_func(), 一个学习率调度函数, 根据训练步数计算当前的学习率, 学习率从初始值指数衰减到最终值.gaussians.update_learning_rate(iteration)# 每1000次迭代, 球谐函数(SH, Spherical Harmonics)的阶数加1, 直到设置的最大的阶数, 默认最大为3, # 每个3D高斯点需要存储(阶数 + 1)^2 个球谐系数, 3阶时为16个系数, 每个系数有RGB 3个值所以一共48个值if iteration % 1000 == 0:gaussians.oneupSHdegree()# 当栈为空时, 复制一份训练帧的相机位姿列表并创建对应的索引列表if not viewpoint_stack:viewpoint_stack = scene.getTrainCameras().copy()viewpoint_indices = list(range(len(viewpoint_stack)))# 从中随机选取一个相机位姿rand_idx = randint(0, len(viewpoint_indices) - 1)# 从当前栈中弹出, 避免重复选取, 这样最终会按随机的顺序遍历完所有的相机位姿viewpoint_cam = viewpoint_stack.pop(rand_idx)vind = viewpoint_indices.pop(rand_idx)# 如果到了开启debug的迭代次数, 开启debugif (iteration - 1) == debug_from:pipe.debug = True# 如果设置了随机背景, 创建随机背景颜色张量bg = torch.rand((3), device="cuda") if opt.random_background else background# 用当前选中的相机视角, 渲染当前的场景render_pkg = render(viewpoint_cam, gaussians, pipe, bg, use_trained_exp=dataset.train_test_exp, separate_sh=SPARSE_ADAM_AVAILABLE)# 读出渲染结果image, viewspace_point_tensor, visibility_filter, radii = render_pkg["render"], render_pkg["viewspace_points"], render_pkg["visibility_filter"], render_pkg["radii"]# 处理摄像机视角的alpha遮罩(透明度), 将alpha遮罩数据从CPU内存转移到GPU显存, 将当前图像与alpha遮罩进行逐像素相乘, # alpha值为1时保留原像素, alpha值为0时使像素完全透明if viewpoint_cam.alpha_mask is not None:alpha_mask = viewpoint_cam.alpha_mask.cuda()image *= alpha_mask

计算损失

    # 从viewpoint_cam对象中获取原始图像数据, 使用.cuda()方法将数据从CPU内存转移到GPU显存, # 调用L1损失函数, 计算渲染结果与原图gt_image之间的像素级绝对差平均值gt_image = viewpoint_cam.original_image.cuda()Ll1 = l1_loss(image, gt_image)# 计算两个图像之间的结构相似性指数(SSIM), 如果 fused_ssim 可用则使用 fused_ssim, 否则使用普通的ssim# Calculate SSIM using fused implementation if availableif FUSED_SSIM_AVAILABLE:# 用unsqueeze(0)来增加一个维度,fused_ssim需要批量输入ssim_value = fused_ssim(image.unsqueeze(0), gt_image.unsqueeze(0))else:ssim_value = ssim(image, gt_image)# 结合L1损失和SSIM损失计算混合损失, (1.0 - ssim_value) 将SSIM相似度转换为损失值, 因为SSIM值越大损失越小loss = (1.0 - opt.lambda_dssim) * Ll1 + opt.lambda_dssim * (1.0 - ssim_value)# Depth regularization 深度正则化, 引入单目深度估计作为弱监督信号改善几何一致性, 缓解漂浮物伪影, 增强遮挡区域的重建效果Ll1depth_pure = 0.0if depth_l1_weight(iteration) > 0 and viewpoint_cam.depth_reliable:# 从渲染结果中获取逆向深度图(1/depth)invDepth = render_pkg["depth"]# 获取单目深度估计的逆向深度图并转移到GPUmono_invdepth = viewpoint_cam.invdepthmap.cuda()# 深度有效区域的掩码(标记可靠区域)depth_mask = viewpoint_cam.depth_mask.cuda()# 计算带掩码的L1损失 = 绝对差(渲染深度 - 单目深度) * 掩码 → 取均值Ll1depth_pure = torch.abs((invDepth  - mono_invdepth) * depth_mask).mean()# 应用动态权重系数(可能随迭代次数衰减)Ll1depth = depth_l1_weight(iteration) * Ll1depth_pure # 将加权后的深度损失加入总损失loss += Ll1depth# 将Tensor转换为Python数值用于记录Ll1depth = Ll1depth.item()else:Ll1depth = 0

反向计算梯度并优化

    # 执行反向传播算法, 自动计算所有可训练参数关于loss的梯度loss.backward()# 记录迭代结束时间iter_end.record()# End iteration timing# torch.no_grad() 临时关闭梯度计算的上下文管理器with torch.no_grad():# Progress barema_loss_for_log = 0.4 * loss.item() + 0.6 * ema_loss_for_logema_Ll1depth_for_log = 0.4 * Ll1depth + 0.6 * ema_Ll1depth_for_logif iteration % 10 == 0:progress_bar.set_postfix({"Loss": f"{ema_loss_for_log:.{7}f}", "Depth Loss": f"{ema_Ll1depth_for_log:.{7}f}"})progress_bar.update(10)if iteration == opt.iterations:progress_bar.close()# 输出日志, 当迭代次数为 testing_iterations 时(默认为7000和30000), 会做一次整体评估, 间隔5取5个样本, 取一部分相机视角计算L1和SSIM损失, iter_start.elapsed_time(iter_end) 计算耗时training_report(tb_writer, iteration, Ll1, loss, l1_loss, iter_start.elapsed_time(iter_end), testing_iterations, scene, render, (pipe, background, 1., SPARSE_ADAM_AVAILABLE, None, dataset.train_test_exp), dataset.train_test_exp)# 当迭代次数为 saving_iterations(默认为7000和30000)时,保存if (iteration in saving_iterations):print("\n[ITER {}] Saving Gaussians".format(iteration))# 里面会调用 gaussians.save_ply() 保存ply文件scene.save(iteration)# 当迭代次数小于致密化结束的右边界时if iteration < opt.densify_until_iter:# 可见性半径更新, 记录每个高斯点在所有视角下的最大可见半径, 用于后续剪枝判断. visibility_filter过滤出当前视角可见的高斯点# Keep track of max radii in image-space for pruninggaussians.max_radii2D[visibility_filter] = torch.max(gaussians.max_radii2D[visibility_filter], radii[visibility_filter])# 累积视空间位置梯度统计量, 用于后续判断哪些高斯点需要分裂(高梯度区域)或克隆(高位置变化区域)gaussians.add_densification_stats(viewspace_point_tensor, visibility_filter)# 当迭代次数大于致密化开始的左边界, 并且满足致密化间隔时, 进行致密化与修剪处理if iteration > opt.densify_from_iter and iteration % opt.densification_interval == 0:# 如果迭代次数小于不透明度重置间隔(3000)则返回20作为2D尺寸限制, 否则不限制size_threshold = 20 if iteration > opt.opacity_reset_interval else None# 致密化与修剪gaussians.densify_and_prune(opt.densify_grad_threshold, 0.005, scene.cameras_extent, size_threshold, radii)# 定期(默认3000一次)重置不透明度, 恢复被错误剪枝的高斯点, 调整新生成高斯的可见性, 适配白背景场景的特殊初始化if iteration % opt.opacity_reset_interval == 0 or (dataset.white_background and iteration == opt.densify_from_iter):gaussians.reset_opacity()# Optimizer阶段, 反向优化模型参数if iteration < opt.iterations:gaussians.exposure_optimizer.step()gaussians.exposure_optimizer.zero_grad(set_to_none = True)if use_sparse_adam:visible = radii > 0gaussians.optimizer.step(visible, radii.shape[0])gaussians.optimizer.zero_grad(set_to_none = True)else:gaussians.optimizer.step()gaussians.optimizer.zero_grad(set_to_none = True)# 到达预设的checkpoint, 默认为7000和30000, 保存当前的训练进度if (iteration in checkpoint_iterations):print("\n[ITER {}] Saving Checkpoint".format(iteration))torch.save((gaussians.capture(), iteration), scene.model_path + "/chkpnt" + str(iteration) + ".pth")

如果有错误请留言指出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【黑马程序员uniapp】项目配置、请求函数封装

黑马程序员前端项目uniapp小兔鲜儿微信小程序项目视频教程&#xff0c;基于Vue3TsPiniauni-app的最新组合技术栈开发的电商业务全流程_哔哩哔哩_bilibili 参考 有代码&#xff0c;还有app、h5页面、小程序的演示 小兔鲜儿-vue3ts-uniapp-一套代码多端部署: 小兔鲜儿-vue3ts-un…

前端使用 preview 插件预览docx文件

目录 前言一 引入插件二 JS 处理 前言 前端使用 preview 插件预览docx文件 一 引入插件 建议下载至本地&#xff0c;静态引入&#xff0c;核心的文件已打包&#xff08;前端使用 preview 插件预览docx文件&#xff09;&#xff0c;在文章目录处下载至本地&#xff0c;复制在项…

如何在运动中保护好半月板?

文章目录 引言I 半月板的作用稳定作用缓冲作用润滑作用II 在跳绳运动中保护好半月板III 半月板损伤自测IV 半月板“杀手”半月板损伤必须满足四个因素:消耗品引言 膝盖是连接大腿骨和小腿骨的地方,在两部分骨头的连接处,垫着两片半月形的纤维软骨板,这就是半月板。半月板分…

安科瑞防逆流方案落地内蒙古中高绿能光伏项目,筑牢北疆绿电安全防线

一、项目概况 内蒙古阿拉善中高绿能能源分布式光伏项目&#xff0c;位于内蒙古乌斯太镇&#xff0c;装机容量为7MW&#xff0c;采用自发自用、余电不上网模式。 用户配电站为35kV用户站&#xff0c;采用两路电源单母线分段系统。本项目共设置12台35/0.4kV变压器&#xff0c;在…

1.3 fs模块详解

fs 模块详解 Node.js 的 fs 模块提供了与文件系统交互的能力&#xff0c;是服务器端编程的核心模块之一。它支持同步、异步&#xff08;回调式&#xff09;和 Promise 三种 API 风格&#xff0c;可满足不同场景的需求。 1. 模块引入 const fs require(fs); // 回调…

LeetCode 70 爬楼梯(Java)

爬楼梯问题&#xff1a;动态规划与斐波那契的巧妙结合 问题描述 假设你正在爬楼梯&#xff0c;需要爬 n 阶才能到达楼顶。每次你可以爬 1 或 2 个台阶。求有多少种不同的方法可以爬到楼顶&#xff1f; 示例&#xff1a; n 2 → 输出 2&#xff08;1阶1阶 或 2阶&#xff0…

【学习分享】shell基础-参数传递

参数传递 我们可以在执行 Shell 脚本时&#xff0c;向脚本传递参数&#xff0c;脚本内获取参数的格式为 $n&#xff0c;n 代表一个数字&#xff0c;1 为执行脚本的第一个参数&#xff0c;2 为执行脚本的第二个参数。 例如可以使用 $1、$2 等来引用传递给脚本的参数&#xff0…

Fluence推出“Pointless计划”:五种方式参与RWA算力资产新时代

2025年6月1日&#xff0c;去中心化算力平台 Fluence 正式宣布启动“Pointless 计划”——这是其《Fluence Vision 2026》战略中四项核心举措之一&#xff0c;旨在通过贡献驱动的积分体系&#xff0c;激励更广泛的社区参与&#xff0c;为用户带来现实世界资产&#xff08;RWA&am…

Excel数据分析:基础

在现代办公环境中&#xff0c;Excel 是一款不可或缺的工具&#xff0c;它是 Microsoft&#xff08;微软&#xff09;开发的电子表格软件&#xff0c;用于处理和分析结构化数据。市场上还有其他类似的软件&#xff0c;如 Google Sheets 和 Apple Numbers&#xff0c;但 Excel 以…

12V降5V12A大功率WD5030A,充电器、便携式设备、网络及工业领域的理想选择

WD5030A 高效单片同步降压型直流 / 直流转换器 一、芯片核心概述 WD5030A 是一款高性能同步降压型 DC/DC 转换器&#xff0c;采用 平均电流模式控制架构&#xff08;带频率抖动功能&#xff09;&#xff0c;具备以下核心优势&#xff1a; 精准电流控制&#xff1a;快速响应负…

企业级AI迈入黄金时代,企业该如何向AI“蝶变”?

科技云报到原创。 近日&#xff0c;微软&#xff08;MSFT.US&#xff09;在最新全员大会上高调展示企业级AI业务进展&#xff0c;其中与巴克莱银行达成的10万份Copilot许可证交易成为焦点。 微软首席商务官贾德森阿尔索夫在会上披露&#xff0c;这家英国金融巨头已签约采购相…

Java编程课(一)

Java编程课 一、java简介二、Java基础语法2.1 环境搭建2.2 使用Intellij IDEA新建java项目2.3 Java运行介绍2.4 参数说明2.5 Java基础语法2.6 注释2.7 变量和常量一、java简介 Java是一种广泛使用的高级编程语言,最初由Sun Microsystems于1995年发布。它被设计为具有简单、可…

【Java Web】速通Tomcat

参考笔记:JavaWeb 速通Tomcat_tomcat部署java项目-CSDN博客 目录 一、Tomcat服务 1. 下载和安装 2. 启动Tomcat服务 3. 启动Tomcat服务的注意事项 4. 关闭Tomcat服务 二、Tomcat的目录结构 1. bin 🌟 2. conf 🌟 3. lib 4. logs 5. temp 6. webapps 7. work 三、Web项目…

Mysql 身份认证绕过漏洞 CVE-2012-2122

前言&#xff1a;CVE-2012-2122 是一个影响 MySQL 和 MariaDB 的身份验证漏洞&#xff0c;存在于特定版本中 vulhub/mysql/CVE-2012-2122/README.zh-cn.md at master vulhub/vulhubhttps://github.com/vulhub/vulhub/blob/master/mysql/CVE-2012-2122/README.zh-cn.md 任务一…

Win10停更,Win11不好用?现在Mac电脑比Win11电脑更便宜

最近不少朋友在换电脑前都犯了难。 以前大家最常说的一句是&#xff1a;“Mac太贵了&#xff0c;还是买Windows吧。”但现在不一样了&#xff0c;国补教育优惠下来&#xff0c;新款M4芯片的Mac mini的入门价已经降到了3000元左右&#xff0c;曾经的价格壁垒&#xff0c;已经不…

C#中Struct与IntPtr转换:实用扩展方法

C#中Struct与IntPtr转换&#xff1a;实用扩展方法 在 C# 编程的世界里&#xff0c;我们常常会遇到需要与非托管代码交互&#xff0c;或者进行一些底层内存操作的场景。这时&#xff0c;IntPtr类型就显得尤为重要&#xff0c;它可以表示一个指针或句柄&#xff0c;用来指向非托…

手机归属地查询接口如何用Java调用?

一、什么是手机归属地查询接口&#xff1f; 是一种便捷、高效的工具&#xff0c;操作简单&#xff0c;请求速度快。它不仅能够提高用户填写地址的效率&#xff0c;还能帮助企业更好地了解客户需求&#xff0c;制定个性化的营销策略&#xff0c;降低风险。随着移动互联网的发展…

43、视图解析-Thymeleaf初体验

43、视图解析-Thymeleaf初体验 “43、视图解析-Thymeleaf初体验”通常是指在学习Spring Boot框架时&#xff0c;关于如何使用Thymeleaf模板引擎进行视图解析的入门课程或章节。以下是对该主题的详细介绍&#xff1a; #### Thymeleaf简介 - **定义**&#xff1a;Thymeleaf是一个…

Day 40训练

Day 40 训练 PyTorch 图像数据训练与测试的规范写法单通道图像的规范训练流程数据预处理与加载模型定义训练与测试函数封装模型训练执行 彩色图像的扩展应用数据预处理调整模型结构调整 关键要点总结 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封…

杰理可视化SDK--系统死机异常调试

杰理可视化SDK--系统死机异常调试 系统异常原因杰理SDK异常调试准备工作杰理SDK系统异常定位异常代码示例1异常代码示例2 在使用杰理可视化SDK进行软件开发时&#xff0c;往往会遇到一些系统异常问题&#xff0c;系统异常是指芯片在运行代码时&#xff0c;由于软件或硬件状态出…