biptv3 / code /pointcept_framework /measure_all.py
YYYYYYUUU's picture
Add core reproduction code (binarization layers, PTv3, superpoint ops, min-repro pack)
7b95dc2 verified
Raw
History Blame Contribute Delete
7.59 kB
# measure_all.py (加写log 'w', 增大num_points测真实, 加Bi mode调试)
import torch
import time
import numpy as np
import numpy.dtypes
import numpy
import matplotlib.pyplot as plt
from pointcept.engines.defaults import default_config_parser, default_setup
from pointcept.datasets import build_dataset, point_collate_fn
from pointcept.models import build_model
from pointcept.models.quantization.quant_utils import convert_ptv3_to_bi_ptv3
from pointcept.utils.config import DictAction
torch.serialization.add_safe_globals([numpy.dtypes.Float64DType, numpy.dtype, numpy.core.multiarray.scalar])
def flops_counter_hook(module, input, output):
flops = 0
if isinstance(module, torch.nn.Linear):
batch_size = input[0].size(0)
in_features = module.in_features
out_features = module.out_features
flops = batch_size * in_features * out_features * 2
elif isinstance(module, torch.nn.Conv1d):
batch_size = input[0].size(0)
out_channels = module.out_channels
in_channels = module.in_channels
kernel_size = module.kernel_size[0]
output_size = output.size(2)
flops = batch_size * out_channels * in_channels * kernel_size * output_size * 2
return flops
def measure_for_model(config_file, weight_path, quantize=False):
print("="*40)
print(f"Measuring for: {config_file.split('/')[-1]}, Weight: {weight_path}, Quantize: {quantize}")
print("="*40)
cfg = default_config_parser(config_file, None)
cfg = default_setup(cfg)
model = build_model(cfg.model)
if quantize:
print("INFO: Converting to Bi-PTV3...")
model = convert_ptv3_to_bi_ptv3(model)
print(f"INFO: Loading weights from {weight_path}...")
weight = torch.load(weight_path, map_location="cpu", weights_only=True)
if "state_dict" in weight:
weight = weight["state_dict"]
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in weight.items():
name = k[7:] if k.startswith('module.') else k
new_state_dict[name] = v
model.load_state_dict(new_state_dict, strict=False)
model = model.cuda()
model.eval()
print("INFO: Preparing one sample for testing...")
dataset = build_dataset(cfg.data.val)
input_dict = dataset[0]
num_points = 81920 # 正常大小测真实
if 'coord' in input_dict:
input_dict['coord'] = input_dict['coord'][:num_points]
if 'feat' in input_dict:
input_dict['feat'] = input_dict['feat'][:num_points]
if 'segment' in input_dict:
input_dict['segment'] = input_dict['segment'][:num_points]
if 'origin_segment' in input_dict:
input_dict['origin_segment'] = input_dict['origin_segment'][:num_points]
if 'grid_coord' in input_dict:
input_dict['grid_coord'] = input_dict['grid_coord'][:num_points]
if 'inverse' in input_dict:
input_dict['inverse'] = input_dict['inverse'][:num_points]
if 'offset' in input_dict:
input_dict['offset'] = torch.tensor([num_points], dtype=torch.int)
input_dict = point_collate_fn([input_dict])
for key in input_dict:
if isinstance(input_dict[key], torch.Tensor):
input_dict[key] = input_dict[key].cuda()
total_flops = 0
hooks = []
def add_hooks(m):
if isinstance(m, (torch.nn.Linear, torch.nn.Conv1d)):
def hook_fn(module, input, output):
nonlocal total_flops
flops_added = flops_counter_hook(module, input, output)
total_flops += flops_added
print(f"DEBUG: Layer {module.__class__.__name__} FLOPs added: {flops_added}")
hooks.append(m.register_forward_hook(hook_fn))
model.apply(add_hooks)
print("DEBUG: Starting model forward for FLOPs calculation...")
with torch.no_grad():
_ = model(input_dict)
print("DEBUG: Model forward complete, total FLOPs calculated.")
for h in hooks:
h.remove()
flops_g = total_flops / 1e9
num_warmup = 100
num_runs = 500
print(f"INFO: Warming up for {num_warmup} iterations...")
with torch.no_grad():
for _ in range(num_warmup):
_ = model(input_dict)
torch.cuda.synchronize()
timings = []
peak_memory = 0
print(f"INFO: Running measurement for {num_runs} iterations...")
with torch.no_grad():
for _ in range(num_runs):
torch.cuda.synchronize()
start_time = time.perf_counter()
_ = model(input_dict)
current_memory = torch.cuda.max_memory_allocated() / 1024**2 # MB
peak_memory = max(peak_memory, current_memory)
torch.cuda.reset_peak_memory_stats()
torch.cuda.synchronize()
end_time = time.perf_counter()
timings.append((end_time - start_time) * 1000)
avg_latency = np.mean(timings)
std_latency = np.std(timings)
print(f"Total FLOPs: {flops_g:.3f} GFLOPs")
print(f"Average Latency: {avg_latency:.3f} ms")
print(f"Peak Memory: {peak_memory:.2f} MB")
return flops_g, avg_latency, peak_memory
def generate_plot(fp32_flops, fp32_latency, fp32_memory, short_flops, short_latency, short_memory, long_flops, long_latency, long_memory):
models = ['FP32 PTV3', 'Bi-PTV3 Short QAT', 'Bi-PTV3 Long QAT']
flops = [fp32_flops, short_flops, long_flops]
latency = [fp32_latency, short_latency, long_latency]
memory = [fp32_memory, short_memory, long_memory]
fig, axs = plt.subplots(1, 3, figsize=(18, 5))
# (a) Latency
axs[0].bar(models, latency, color=['blue', 'orange', 'orange'])
axs[0].set_title('Time Cost (ms)')
axs[0].set_ylabel('ms')
# (b) Memory
axs[1].bar(models, memory, color=['blue', 'orange', 'orange'])
axs[1].set_title('Storage Usage (MB)')
axs[1].set_ylabel('MB')
# (c) FLOPs vs Latency
axs[2].scatter(latency, flops, color=['blue', 'orange', 'orange'])
for i, txt in enumerate(models):
axs[2].annotate(txt, (latency[i], flops[i]))
axs[2].set_title('FLOPs (G) vs Time Cost (ms)')
axs[2].set_xlabel('Time Cost (ms)')
axs[2].set_ylabel('FLOPs (G)')
plt.savefig('performance_showdown.png')
print("Generated plot: performance_showdown.png")
def measure_all():
models = [
("FP32 Baseline", "configs/s3dis/semseg-pt-v3m1-0-base.py", "exp/fp32_baseline/model/model_last.pth", False),
("Bi-PTV3 Short QAT", "configs/s3dis/semseg-pt-v3m1-0-base_qat.py", "exp/bi_ptv3_qat_sprint_run/model/model_last.pth", True),
("Bi-PTV3 Long QAT", "configs/s3dis/semseg-pt-v3m1-0-base_qat_long.py", "exp/bi_ptv3_qat_long_run/model/model_best.pth", True)
]
results = []
log_str = "Results Table:\n| Model | FLOPs (G) | Latency (ms) | Memory (MB) |\n|-------|-----------|--------------|-------------|\n"
for name, config, weight, quantize in models:
flops, latency, memory = measure_for_model(config, weight, quantize)
results.append((name, flops, latency, memory))
log_str += f"| {name} | {flops:.3f} | {latency:.3f} | {memory:.2f} |\n"
# 写log文件
with open('measure_results.log', 'w') as f:
f.write(log_str)
print("Results saved to measure_results.log")
# 生成图
generate_plot(results[0][1], results[0][2], results[0][3], results[1][1], results[1][2], results[1][3], results[2][1], results[2][2], results[2][3])
if __name__ == "__main__":
measure_all()