3DGS加速&压缩指标评测方法、高斯数量变化曲线绘制——Training Time、FPS、Gaussian Number、Peak Memory

3DGS加速&压缩指标评测方法、高斯数量变化曲线绘制------Training Time、FPS、Gaussian Number、Peak Memory

前言

  • 本文旨在为3DGS加速&压缩方向初学者提供一个全面的指标评测方法。这篇文章将以3DGS官方代码为例,说明如何集成各种评测指标。该方法也可应用到各种基于3DGS改进的代码框架中。
  • 基于3DGS代码的完整指标评测和可视化代码请参考笔者的Github仓库:gaussian-splatting-metric
  • 同时,如果想了解更多关于3DGS的加速压缩新工作,请关注笔者的Github仓库:Awesome-3DGS-Compress-Accelerate

一、3DGS加速&压缩指标评测

这部分涉及到的加速&压缩指标主要包括:

  • 训练时间(Training time);
  • 渲染帧率(FPS);
  • 高斯数量(Gaussian Number);
  • GPU峰值显存占用(Peak Memory);

1.1 训练时间(Training time)

训练时间指标最直观的统计方法是使用3DGS代码中tqdm进度条来显示训练时间,但该方法统计的时间不够精确。因此这里提供一种更精确的3DGS训练时间指标评测方法,直观统计训练秒数。

这部分代码修改对应 train.py 文件。

1️⃣ 在循环迭代前,初始化统计变量

其中 iter_startiter_end 为3DGS自带变量,但该变量并未统计致密化等操作的时间,因此新增 optim_startoptim_end以及 total_time 变量。

python 复制代码
iter_start = torch.cuda.Event(enable_timing = True)
iter_end = torch.cuda.Event(enable_timing = True)
## new add
optim_start = torch.cuda.Event(enable_timing=True)
optim_end = torch.cuda.Event(enable_timing=True)
total_time = 0.0

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

2️⃣ 统计每次迭代时间iter_time

在每次迭代循环开始和梯度反向传播结束时,调用record()方法记录时间,并通过iter_start.elapsed_time(iter_end)获取本轮迭代时间iter_time

python 复制代码
for iteration in range(first_iter, opt.iterations + 1):
	......
	iter_start.record()
	gaussians.update_learning_rate(iteration)
	......
	loss.backward()
	iter_end.record()
	
	iter_time = iter_start.elapsed_time(iter_end)

3️⃣ 统计致密化等操作时间optim_time并累加总时间total_time

在致密化开始前和相应操作结束时,调用record()方法记录时间,并通过optim_start.elapsed_time(optim_end)获取本轮迭代时间optim_time。最后,累加总时间total_time

python 复制代码
for iteration in range(first_iter, opt.iterations + 1):
	......
	optim_start.record()
	# Densification
	if iteration < opt.densify_until_iter:
		......
	# record time
	optim_end.record()
	torch.cuda.synchronize()
	optim_time = optim_start.elapsed_time(optim_end)
	total_time += (iter_time + optim_time) / 1e3

4️⃣ 全部迭代结束,打印训练时间

python 复制代码
def training(......):
	......
	# location:function end
	print(f"Training time: {total_time}")

1.2 渲染帧率(FPS)

这部分评测对应 render.py 文件,用于测试渲染图像时的平均帧率(FPS)。只需要修改def render_set函数即可。

python 复制代码
def render_set(model_path, name, iteration, views, gaussians, pipeline, background, train_test_exp, separate_sh):
    render_path = os.path.join(model_path, name, "ours_{}".format(iteration), "renders")
    gts_path = os.path.join(model_path, name, "ours_{}".format(iteration), "gt")

    # new add
    total_time = 0.0

    makedirs(render_path, exist_ok=True)
    makedirs(gts_path, exist_ok=True)

    for idx, view in enumerate(tqdm(views, desc="Rendering progress")):
        rendering = render(view, gaussians, pipeline, background, use_trained_exp=train_test_exp, separate_sh=separate_sh)["render"]
        gt = view.original_image[0:3, :, :]

        if args.train_test_exp:
            rendering = rendering[..., rendering.shape[-1] // 2:]
            gt = gt[..., gt.shape[-1] // 2:]

        torchvision.utils.save_image(rendering, os.path.join(render_path, '{0:05d}'.format(idx) + ".png"))
        torchvision.utils.save_image(gt, os.path.join(gts_path, '{0:05d}'.format(idx) + ".png"))
    
    # new add
    num_frames = len(views)
    avg_time = total_time / num_frames if num_frames > 0 else 0
    fps = 1.0 / avg_time if avg_time > 0 else 0
    print(f"[{name}] Rendered {num_frames} frames in {total_time:.2f} seconds. Average FPS: {fps:.2f}")

1.3 高斯数量(Gaussian Number)

这部分很简单,只需要在训练迭代循环结束后打印高斯数量即可。

python 复制代码
def training(......):
	......
	# location:function end
	print(f"Gaussian number after train: {gaussians._xyz.shape[0]}")

1.4 GPU峰值显存占用(Peak Memory)

主要借助Pytorch自带统计工具完成。首先,在train.py文件中各种库导入代码下面插入torch.cuda.reset_peak_memory_stats(),避免之前的内存占用造成影响。

python 复制代码
import torch
...
# new add
torch.cuda.reset_peak_memory_stats()

其次,在train.py文件中主函数if __name__ == "__main__"下面打印训练时的峰值内存统计。这里torch.cuda.max_memory_allocated()代表理论要分配的峰值内存,而torch.cuda.max_memory_reserved()代表实际使用的峰值内存。因此,选torch.cuda.max_memory_reserved()更能反映实际的峰值内存指标。代码如下:

python 复制代码
if __name__ == "__main__":
	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, 
	        args.websockets
	    )
	# new add
    print("Peak memory allocated:", torch.cuda.max_memory_allocated() / 1024**2, "MB")
    print("Peak memory reserved:", torch.cuda.max_memory_reserved() / 1024**2, "MB")
    # All done
    print("\nTraining complete.")

二、3DGS加速&压缩指标评测示例

这里就以3DGS训练Train场景为例,展示前述加速&压缩指标评测结果。

python 复制代码
python train.py -m output/train-metric -s ./dataset/train --eval
python render.py -m output/train-metric --skip_train
python metrics.py -m output-train_metric

运行上述命令,得到如下结果。整理可得各项指标:

Method Scene Training Time #Gaussians Peak Memory FPS PSNR SSIM LPIPS
3DGS Train 852.4s 1089691 6.814 GB 133.9 22.178 0.821 0.195


三、3DGS训练过程高斯数量变化曲线

3.1 每轮迭代高斯数量统计

1️⃣ 初始化统计变量

python 复制代码
def training(dataset, opt, pipe, testing_iterations, saving_iterations, checkpoint_iterations, checkpoint, debug_from):
    # csv file setup
    scene_name = os.path.basename(os.path.normpath(dataset.source_path))
    csv_filename = f"3dgs_{scene_name}_gaussians.csv" # {method}_{scene}.csv
    gaussian_count_history = []

2️⃣ 记录每次训练迭代高斯数量

python 复制代码
for iteration in range(first_iter, opt.iterations + 1):
	......
	# record per-iteration gaussian number
	current_count = gaussians.get_xyz.shape[0]
	gaussian_count_history.append([iteration, current_count])

3️⃣ 保存csv结果

python 复制代码
# --- save gaussian numbers result to CSV ---
output_path = os.path.join(scene.model_path, csv_filename)
with open(output_path, mode='w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(["iteration", "gaussian_count"]) # title
    writer.writerows(gaussian_count_history)         # data

print(f"Gaussian count history saved to: {output_path}")

3.2 高斯数量变化曲线绘制

读取csv文件并绘图。相应的绘图函数如下:

python 复制代码
import pandas as pd
import matplotlib.pyplot as plt
import argparse
import os

def get_label(csv_path):
    filename = os.path.basename(csv_path)
    target_suffix = "_gaussians.csv"
    
    if filename.endswith(target_suffix):
        label = filename[:-len(target_suffix)]
    else:
        label = "curve1"
        
    return label
def plot_curve(csv_path):
    # Existence check
    if not os.path.exists(csv_path):
        print(f"Error: File '{csv_path}' not found.")
        return

    # Read data
    print(f"Reading data from {csv_path}...")
    try:
        df = pd.read_csv(csv_path)
    except Exception as e:
        print(f"Error: Could not read CSV. {e}")
        return

    # Validate required columns
    if 'iteration' not in df.columns or 'gaussian_count' not in df.columns:
        print("Error: CSV must contain 'iteration' and 'gaussian_count' columns.")
        return

    plt.style.use('seaborn-v0_8-muted') # or use 'ggplot'
    fig, ax = plt.subplots(figsize=(12, 7))

    # Draw the curve
    scene_name = os.path.basename(csv_path).split('_3dgs')[0]
    curve_label = get_label(csv_path)
    ax.plot(df['iteration'], df['gaussian_count'], 
            color='#1f77b4', linewidth=2, label=curve_label)
    ax.set_title(f'Gaussian Growth Curve', fontsize=16, pad=20)
    ax.set_xlabel('Iterations', fontsize=12)
    ax.set_ylabel('Gaussian Count', fontsize=12)
    
    # Format big numbers on y-axis
    ax.ticklabel_format(style='plain', axis='y')
    ax.get_yaxis().set_major_formatter(
        plt.FuncFormatter(lambda x, loc: "{:,}".format(int(x))))

    ax.grid(True, linestyle='--', alpha=0.6)
    ax.legend(loc='upper left')

    output_png = csv_path.replace('.csv', '_curve.png')
    
    # Save image
    plt.tight_layout()
    plt.savefig(output_png, dpi=300)
    print(f"Successfully saved plot to: {output_png}")

if __name__ == "__main__": 
    parser = argparse.ArgumentParser(description="Plot Gaussian count curve from a 3DGS training CSV.")
    parser.add_argument("--csv_path", type=str, required=True, help="Path to the 3dgs_gaussians.csv file")
    
    args = parser.parse_args()

    plot_curve(args.csv_path)

读取示例的csv文件并绘图得到以下曲线:

写在最后

相关推荐
li星野2 小时前
OpenCV4X学习—图像平滑、几何变换
图像处理·学习·计算机视觉
暴风游侠2 小时前
金融经济学笔记
笔记·金融
Yu_Lijing2 小时前
基于C++的《Head First设计模式》笔记——模版方法模式
笔记·设计模式
时光慢煮2 小时前
基于 Flutter × OpenHarmony 开发的去除空行 / 多余空格工具实战
flutter·华为·开源·openharmony
安小牛2 小时前
Apache License 2.0的中文介绍及其许可使用
笔记·apache
友思特 智能感知2 小时前
友思特案例 | 金属行业视觉检测案例一:彩涂钢板卷对卷检测
人工智能·计算机视觉·视觉检测·缺陷检测
航Hang*2 小时前
Photoshop 图形与图像处理技术——第9章:实践训练4——图层和蒙版
图像处理·笔记·ui·photoshop·期末·复习
摇滚侠3 小时前
尚硅谷 Java 零基础全套视频教程,System、Runtime、BigDecimal、BigInteger、Random,笔记 151
java·开发语言·笔记
传说故事3 小时前
【论文自动阅读】How Much 3D Do Video Foundation Models Encode?
人工智能·深度学习·3d