Add SafeTensors consolidation stage for zero-copy tensor loading

Bundle all per-image modalities (depth, edge, chm, segm) into a single
.safetensors file for fast training DataLoader reads (~0.1ms zero-copy mmap
vs ~5ms for 4x PNG). Adds consolidate stage after main pipeline stages,
save_safetensors/cleanup_npy config flags, resume support, and 10 new tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
pikaliov
2026-04-17 16:53:29 +03:00
parent 686db62c25
commit f3cb18ac4d
7 changed files with 330 additions and 32 deletions

View File

@@ -40,7 +40,8 @@ from src.augmentor.inference import (
)
from src.augmentor.io_utils import (
save_depth_async, save_chmv2_async, save_edges_async,
save_segmentation_async, setup_logging, shutdown_io_pool,
save_segmentation_async, consolidate_safetensors,
setup_logging, shutdown_io_pool,
)
from src.augmentor.models import (
load_depth_model, load_chmv2_model, load_segmentation_model, unload_model,
@@ -57,6 +58,7 @@ _STAGE_EMOJI = {
"segmentation": "🗺️",
"chmv2": "🦕",
"concat": "🧩",
"consolidate": "📦",
}
@@ -313,6 +315,24 @@ def run_segmentation_stage(
unload_model(model)
def run_consolidate_stage(
records: list[ImageRecord],
cleanup_npy: bool = False,
) -> None:
"""📦 Bundle per-image .npy/.png modalities into .safetensors files."""
total = len(records)
written = 0
pbar = tqdm(records, desc="📦 consolidate → safetensors", unit="img",
colour="magenta",
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}]")
for r in pbar:
ok = consolidate_safetensors(r.output_dir, r.stem, cleanup_npy=cleanup_npy)
if ok:
written += 1
pbar.set_postfix(written=f"{written}/{total}")
logger.info("📦 Consolidated %d / %d images to .safetensors.", written, total)
# ---------------------------------------------------------------------------
# Pipeline orchestration
# ---------------------------------------------------------------------------
@@ -408,9 +428,25 @@ def run_pipeline(
log_vram_snapshot(f"after {stage}")
log_ram_snapshot(f"after {stage}")
# SafeTensors consolidation: bundle all modalities per image.
if pipeline_conf.save_safetensors:
pending_st, skipped_st = filter_completed(all_records, "consolidate")
logger.info("📦 [consolidate] %d pending, %d skipped.", len(pending_st), skipped_st)
if pending_st:
t0 = time.perf_counter()
run_consolidate_stage(pending_st, cleanup_npy=pipeline_conf.cleanup_npy)
elapsed_st = time.perf_counter() - t0
stage_times["consolidate"] = elapsed_st
stage_counts["consolidate"] = len(pending_st)
logger.info("✅ [consolidate] Completed in %.1f s (%d images).",
elapsed_st, len(pending_st))
else:
stage_times["consolidate"] = 0.0
stage_counts["consolidate"] = 0
# Manifest.
manifest = {
"pipeline_version": "3.2.0-dual-resolution",
"pipeline_version": "3.3.0-safetensors",
"image_size_db": input_conf.image_size,
"image_size_query": input_conf.query_image_size,
"profile": hw_conf.profile_name,
@@ -420,6 +456,7 @@ def run_pipeline(
"segmentation": models_conf.seg_model_type,
"chmv2": models_conf.chmv2_model_id,
},
"save_safetensors": pipeline_conf.save_safetensors,
"seg_prompts": seg_conf.prompts,
"total_images": len(all_records),
"stages": {