Add EasyCom encoder-only comparison workflow
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"""Run raw encoder-only Ego4D heads: four epochs, no dev, manifest splits.
|
||||
"""Run raw encoder-only heads: four epochs, no dev, fixed benchmark splits.
|
||||
|
||||
This is not a capacity/context-matched Thinker ablation: native encoder vectors
|
||||
and validity flags are concatenated, so the existing MLP adapts to input width.
|
||||
This entry defaults to Ego4D; run_easycom_encoder_only.py shares the workflow.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -15,32 +16,44 @@ import sys
|
||||
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from easycom_head import load_ego4d_manifest_split, load_session_data
|
||||
from easycom_head import DATASET_NAMES, load_session_data
|
||||
from local_turn_features import LOCAL_FORMAT, MODALITIES
|
||||
from prepare_encoder_only_features import DEFAULT_DATA_ROOT, PACKING, encoder_only_mode
|
||||
from prepare_encoder_only_features import (
|
||||
DEFAULT_DATA_ROOTS, PACKING, encoder_only_mode, encoder_sample_split, sample_unit,
|
||||
)
|
||||
|
||||
|
||||
REPO = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def parse_args(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--data-root", type=Path, default=DEFAULT_DATA_ROOT)
|
||||
def parse_args(argv=None, *, dataset="ego4d"):
|
||||
if dataset not in DATASET_NAMES:
|
||||
raise ValueError(f"Unknown dataset: {dataset}")
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"{dataset}: raw encoder-only A/V/AV heads; four epochs, no dev, fixed train/test splits."
|
||||
)
|
||||
parser.add_argument("--data-root", type=Path, default=DEFAULT_DATA_ROOTS[dataset])
|
||||
parser.add_argument("--manifest", type=Path)
|
||||
parser.add_argument("--target-root", type=Path)
|
||||
parser.add_argument("--feature-root", type=Path, required=True, help="Prepared encoder-only inputs, not Thinker/local sidecars")
|
||||
parser.add_argument("--modalities", choices=tuple(MODALITIES), default="av")
|
||||
parser.add_argument("--checkpoint-root", type=Path, default=Path("checkpoints/ego4d_encoder_only_1s"))
|
||||
parser.add_argument("--result-root", type=Path, default=Path("results/ego4d_encoder_only_1s"))
|
||||
parser.add_argument("--checkpoint-root", type=Path, default=Path(f"checkpoints/{dataset}_encoder_only_1s"))
|
||||
parser.add_argument("--result-root", type=Path, default=Path(f"results/{dataset}_encoder_only_1s"))
|
||||
parser.add_argument("--seeds", type=int, nargs="+", default=[0, 1, 2])
|
||||
parser.add_argument("--device", default="cuda:0")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Validate all train/test inputs and print commands without training")
|
||||
parser.add_argument("--no-progress", action="store_true")
|
||||
args = parser.parse_args(argv)
|
||||
args.manifest = args.manifest or args.data_root / "ego4d_egospeak.tsv"
|
||||
args.target_root = args.target_root or args.data_root / "ego4d_target_frame"
|
||||
args.dataset = dataset
|
||||
if dataset == "ego4d":
|
||||
args.manifest = args.manifest or args.data_root / "ego4d_egospeak.tsv"
|
||||
elif args.manifest is not None:
|
||||
parser.error("EasyCom uses train sessions 4..12 and test sessions 3/2/1, not --manifest")
|
||||
args.target_root = args.target_root or args.data_root / f"{dataset}_target_frame"
|
||||
for key in ("data_root", "manifest", "target_root", "feature_root", "checkpoint_root", "result_root"):
|
||||
setattr(args, key, getattr(args, key).expanduser().resolve())
|
||||
value = getattr(args, key)
|
||||
if value is not None:
|
||||
setattr(args, key, value.expanduser().resolve())
|
||||
if len(set(args.seeds)) != len(args.seeds) or any(seed < 0 for seed in args.seeds):
|
||||
parser.error("Seeds must be unique and nonnegative")
|
||||
return args
|
||||
@@ -52,19 +65,18 @@ def run_paths(args, seed):
|
||||
|
||||
|
||||
def preflight(args):
|
||||
names = load_ego4d_manifest_split(args.manifest, "all")
|
||||
train = load_ego4d_manifest_split(args.manifest, "train")
|
||||
test = load_ego4d_manifest_split(args.manifest, "test")
|
||||
names, train, test = encoder_sample_split(args.dataset, args.manifest)
|
||||
unit = sample_unit(args.dataset)
|
||||
marker = args.feature_root / "complete.txt"
|
||||
if not marker.is_file() or marker.read_text().strip() != f"{len(names)} clips":
|
||||
if not marker.is_file() or marker.read_text().strip() != f"{len(names)} {unit}s":
|
||||
raise ValueError(f"Missing/incomplete encoder-only export: {marker}")
|
||||
for seed in args.seeds:
|
||||
checkpoint, result = run_paths(args, seed)
|
||||
if checkpoint.exists() or checkpoint.with_suffix(".pth.json").exists() or result.exists():
|
||||
raise FileExistsError(f"Refusing to overwrite {checkpoint} or {result}; choose unfinished seeds/new output roots")
|
||||
expected, total, width = None, 0, None
|
||||
for name in tqdm(names, desc="encoder-only inputs", unit="clip", disable=args.no_progress):
|
||||
data = load_session_data(args.feature_root, args.target_root, name, dataset="ego4d",
|
||||
for name in tqdm(names, desc="encoder-only inputs", unit=unit, disable=args.no_progress):
|
||||
data = load_session_data(args.feature_root, args.target_root, name, dataset=args.dataset,
|
||||
expected_mode=encoder_only_mode(args.modalities))
|
||||
config = data.feature_metadata.get("config") or {}
|
||||
contract = config.get("encoder_contract") or {}
|
||||
@@ -83,20 +95,21 @@ def preflight(args):
|
||||
if data.states.ndim != 2 or data.hidden_size != width or len(data.states) <= 10:
|
||||
raise ValueError(f"{name}: invalid native encoder-vector/validity-bit shape")
|
||||
if expected is not None and config != expected:
|
||||
raise ValueError(f"{name}: encoder export contract differs across clips")
|
||||
raise ValueError(f"{name}: encoder export contract differs across {unit}s")
|
||||
expected = config
|
||||
total += len(data.states)
|
||||
del data
|
||||
print(f"Encoder-only {args.modalities}: {len(train)} train / {len(test)} test; "
|
||||
print(f"{args.dataset} encoder-only {args.modalities}: {len(train)} train / {len(test)} test; "
|
||||
f"{total} states, input width={width}, MLP hidden width={width // 2}; epochs=4, dev=none", flush=True)
|
||||
print("No Thinker features or local-fusion branch. Native concatenation changes head capacity; "
|
||||
"context remains the saved encoder windows.", flush=True)
|
||||
|
||||
|
||||
def experiment_commands(args):
|
||||
common = ["--dataset", "ego4d", "--manifest", str(args.manifest),
|
||||
"--feature-root", str(args.feature_root), "--target-root", str(args.target_root),
|
||||
common = ["--dataset", args.dataset, "--feature-root", str(args.feature_root), "--target-root", str(args.target_root),
|
||||
"--expected-mode", encoder_only_mode(args.modalities), "--device", args.device]
|
||||
if args.manifest is not None:
|
||||
common += ["--manifest", str(args.manifest)]
|
||||
if args.no_progress:
|
||||
common.append("--no-progress")
|
||||
for seed in args.seeds:
|
||||
@@ -108,11 +121,15 @@ def experiment_commands(args):
|
||||
"--transition-objective", "none", "--seed", str(seed)]
|
||||
infer = [sys.executable, str(REPO / "infer_turn_head.py"), *common,
|
||||
"--checkpoint", str(checkpoint), "--output-dir", str(result)]
|
||||
if args.dataset == "easycom":
|
||||
_, train_sessions, test_sessions = encoder_sample_split("easycom")
|
||||
train += ["--sessions", *train_sessions]
|
||||
infer += ["--sessions", *test_sessions]
|
||||
yield train, infer
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
args = parse_args(argv)
|
||||
def main(argv=None, *, dataset="ego4d"):
|
||||
args = parse_args(argv, dataset=dataset)
|
||||
preflight(args)
|
||||
env = os.environ.copy()
|
||||
for name in ("OMP_NUM_THREADS", "MKL_NUM_THREADS", "OPENBLAS_NUM_THREADS"):
|
||||
@@ -123,7 +140,7 @@ def main(argv=None):
|
||||
if not args.dry_run:
|
||||
subprocess.run(command, cwd=REPO, env=env, check=True)
|
||||
summary = [sys.executable, str(REPO / "summarize_local_baselines.py"),
|
||||
"--root", str(args.result_root), "--dataset", "ego4d"]
|
||||
"--root", str(args.result_root), "--dataset", args.dataset]
|
||||
print(shlex.join(summary), flush=True)
|
||||
if not args.dry_run:
|
||||
subprocess.run(summary, cwd=REPO, env=env, check=True)
|
||||
|
||||
Reference in New Issue
Block a user