fix(ceus): 降低长视频内存峰值,快照开跑即写,build 核对 CEUS 运行与交付包
- 内存:prepare_segmentation 改为流式解码,只留分屏和配准的抽样帧,绿框逐帧在两半上找; 分割那一半由 SegmentationInput.target_frames() 到用时再解码,segment_video 边读边写 jpg; MedSAM2 init_state 的读帧换成直接在存放设备上建张量(不改 MedSAM2 源码,结果逐位相同); 传播结果写进预分配数组。帧数最多的一段视频常驻内存峰值 11.2 GiB → 2.3 GiB,分辨率最大的 一段原代码被 OOM 杀掉、现为 4.2 GiB;两段的掩码和全部产物与改前逐字节相同,33 段视频的 第 1–2 步结果与改前一致,29 例配准与 review.csv 一致。 - 快照:ceus_quant 开跑时写 snapshot/(git、ceus.yaml、review.csv、uv.lock、segment.json), 配置和核对表从快照副本读;收尾由新函数 runs.finish_snapshot 只新建 finished.json。 build 与 ceus_align_review 同样改用 finish_snapshot 记结束时间。 - build:summary.json 加 video_sha256;装 CEUS 结果时核对快照里的 delivery_dir、逐例核对 视频 SHA-256 和推注时间,不同就整次停下;新字段做类型检查,不进 tic.json。 来源:主会话任务(问题 1–3 及补充的 delivery_dir 比对)。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import csv
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from collections.abc import Callable, Mapping, Sequence
|
||||
from collections.abc import Callable, Iterable, Mapping
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
@@ -88,11 +88,12 @@ class _CountingSegment:
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
frames: Sequence[npt.NDArray[np.uint8]],
|
||||
frames: Iterable[npt.NDArray[np.uint8]],
|
||||
prompt_masks: Mapping[int, npt.NDArray[np.bool_]],
|
||||
cfg: object,
|
||||
) -> npt.NDArray[np.bool_]:
|
||||
self.calls += 1
|
||||
frames = list(frames)
|
||||
height, width = frames[0].shape[:2]
|
||||
masks = np.zeros((len(frames), height, width), dtype=bool)
|
||||
x1, y1, x2, y2 = LESION
|
||||
@@ -170,7 +171,11 @@ def test_run_writes_outputs_and_failed_case(
|
||||
assert (snapshot / "align_review.csv").read_bytes() == (tmp_path / "review.csv").read_bytes()
|
||||
segment_record = json.loads((snapshot / "segment.json").read_text(encoding="utf-8"))
|
||||
assert segment_record["medsam2_commit"] == "c" * 40
|
||||
assert segment_record["cuda_peak_memory_bytes"] is None # device 是 cpu
|
||||
assert segment_record["checkpoint_sha256"] == hashlib.sha256(b"fake weights").hexdigest()
|
||||
finished = json.loads((snapshot / "finished.json").read_text(encoding="utf-8"))
|
||||
assert finished["cuda_peak_memory_bytes"] is None # device 是 cpu
|
||||
assert finished["segment_cache_hits"] == 0
|
||||
assert "finished_at" in finished
|
||||
assert (snapshot / "snapshot.json").is_file()
|
||||
|
||||
out = capsys.readouterr().out
|
||||
@@ -178,6 +183,37 @@ def test_run_writes_outputs_and_failed_case(
|
||||
assert "U001" not in out and "U002" not in out
|
||||
|
||||
|
||||
def test_snapshot_written_before_cases_run(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
setup: tuple[ModuleType, _CountingSegment, dict[str, Any], Callable[[str], None]],
|
||||
) -> None:
|
||||
"""跑病例时快照已在 .partial 里;运行中途改了 ceus.yaml 和 review.csv,快照仍是开跑时那份。"""
|
||||
module, segment, document, next_run_id = setup
|
||||
config = _write_config(tmp_path, document, "ceus.yaml")
|
||||
review = Path(document["paths"]["align_review"])
|
||||
original_config, original_review = config.read_bytes(), review.read_bytes()
|
||||
snapshot = tmp_path / "runs" / "run-a.partial" / "snapshot"
|
||||
seen: list[bool] = []
|
||||
|
||||
def editing_segment(*args: Any) -> npt.NDArray[np.bool_]:
|
||||
seen.append(all((snapshot / name).is_file() for name in ("snapshot.json", "segment.json")))
|
||||
config.write_text("# 运行中途改了\n", encoding="utf-8")
|
||||
review.write_text("改了\n", encoding="utf-8")
|
||||
return segment(*args)
|
||||
|
||||
monkeypatch.setattr(module, "segment_video", editing_segment)
|
||||
next_run_id("run-a")
|
||||
module.main(["--config", str(config)])
|
||||
|
||||
assert seen == [True]
|
||||
done = tmp_path / "runs" / "run-a" / "snapshot"
|
||||
assert (done / "ceus.yaml").read_bytes() == original_config
|
||||
assert (done / "align_review.csv").read_bytes() == original_review
|
||||
snapshot_json = json.loads((done / "snapshot.json").read_text(encoding="utf-8"))
|
||||
assert "finished_at" not in snapshot_json and "started_at" in snapshot_json
|
||||
|
||||
|
||||
def test_build_reads_real_run_layout(
|
||||
tmp_path: Path,
|
||||
setup: tuple[ModuleType, _CountingSegment, dict[str, Any], Callable[[str], None]],
|
||||
|
||||
Reference in New Issue
Block a user