Add frozen Thinker cache-path control

This commit is contained in:
Xin Wang
2026-08-24 01:07:42 +08:00
parent c5e2e044a8
commit 3cc0c3da85
2 changed files with 69 additions and 15 deletions
+32
View File
@@ -1072,6 +1072,38 @@ and `valid/`, and writes the aggregate metrics to `metrics.json`. Add
Treat Sessions 1, 2, and 3 as the official test set: inspect their metrics only Treat Sessions 1, 2, and 3 as the official test set: inspect their metrics only
after the training recipe and hyperparameters have been fixed. after the training recipe and hyperparameters have been fixed.
To isolate whether a score change comes from LoRA or from the dense-cache
reconstruction path, run the same evaluator without `--adapter-checkpoint` and
load the original frozen 8-second head:
```bash
FROZEN_HEAD=checkpoints/easycom_open_stream_2fps_8s_head_seed0.pth
CUDA_VISIBLE_DEVICES=0,1,2 \
OMP_NUM_THREADS=1 \
MKL_NUM_THREADS=1 \
OPENBLAS_NUM_THREADS=1 \
PYTORCH_ALLOC_CONF=expandable_segments:True \
TORCH_NCCL_ASYNC_ERROR_HANDLING=1 \
torchrun --standalone --nproc_per_node=3 eval_omni_turn_lora.py \
--model-id /data1/yfzx/wangx/Models/Qwen2.5-Omni-3B \
--cache-dir /data1/yfzx/wangx/Datasets/easycom/omni_lora_cache_dense_8s/Session_3 \
--cache-dir /data1/yfzx/wangx/Datasets/easycom/omni_lora_cache_dense_8s/Session_2 \
--cache-dir /data1/yfzx/wangx/Datasets/easycom/omni_lora_cache_dense_8s/Session_1 \
--head-checkpoint "${FROZEN_HEAD}" \
--output-dir results/easycom_frozen_8s_dense_cache_control \
--batch-size 2 \
--block-cache-size 32 \
--dtype bfloat16 \
--flash-attention-2 \
--ddp-timeout-minutes 60
```
This control should approach the previously reported frozen 8-second score of
58.51937 full-timeline mean mAP. A material mismatch indicates a representation
difference between dense-cache reconstruction and the earlier open-stream
feature files; a close match isolates the regression to LoRA/head training.
For a speed-only alternative, `train_omni_turn_lora.py` supports a two-GPU For a speed-only alternative, `train_omni_turn_lora.py` supports a two-GPU
producer-consumer benchmark. The producer GPU runs the frozen AV towers and producer-consumer benchmark. The producer GPU runs the frozen AV towers and
places current-tail embeddings in a bounded CPU queue while the consumer GPU places current-tail embeddings in a bounded CPU queue while the consumer GPU
+37 -15
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Evaluate Thinker LoRA + turn head from dense Omni AV caches. """Evaluate a frozen or LoRA-adapted Thinker from dense Omni AV caches.
The script accepts one dense cache directory per EasyCom session. With The script accepts one dense cache directory per EasyCom session. With
``torchrun``, complete sessions are assigned round-robin to ranks; rank zero ``torchrun``, complete sessions are assigned round-robin to ranks; rank zero
@@ -55,7 +55,14 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
required=True, required=True,
help="Dense cache directory from cache_omni_av_pieces.py; repeat per session", help="Dense cache directory from cache_omni_av_pieces.py; repeat per session",
) )
parser.add_argument("--adapter-checkpoint", type=Path, required=True) parser.add_argument(
"--adapter-checkpoint",
type=Path,
help=(
"PEFT adapter directory. Omit it to evaluate the frozen base "
"Thinker as a cache-path control."
),
)
parser.add_argument("--head-checkpoint", type=Path, required=True) parser.add_argument("--head-checkpoint", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True) parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--batch-size", type=int, default=2) parser.add_argument("--batch-size", type=int, default=2)
@@ -267,18 +274,30 @@ def _run(args: argparse.Namespace, run: DistributedRun) -> None:
base_model, processor = _load_base_model(args, run) base_model, processor = _load_base_model(args, run)
base_model.requires_grad_(False) base_model.requires_grad_(False)
try: adapter_path = None
from peft import PeftModel if args.adapter_checkpoint is None:
except ImportError as exc: language_model = base_model.model.eval()
raise RuntimeError("PEFT is required to evaluate the LoRA adapter") from exc model_variant = "frozen_base_thinker"
adapter_path = args.adapter_checkpoint.expanduser().resolve() print(
if not (adapter_path / "adapter_config.json").is_file(): f"[rank {run.rank}] frozen control: no LoRA adapter loaded",
raise FileNotFoundError(f"Missing adapter config: {adapter_path}") flush=True,
language_model = PeftModel.from_pretrained( )
base_model.model, else:
str(adapter_path), try:
is_trainable=False, from peft import PeftModel
).eval() except ImportError as exc:
raise RuntimeError(
"PEFT is required to evaluate the LoRA adapter"
) from exc
adapter_path = args.adapter_checkpoint.expanduser().resolve()
if not (adapter_path / "adapter_config.json").is_file():
raise FileNotFoundError(f"Missing adapter config: {adapter_path}")
language_model = PeftModel.from_pretrained(
base_model.model,
str(adapter_path),
is_trainable=False,
).eval()
model_variant = "thinker_lora"
language_model.config.use_cache = False language_model.config.use_cache = False
model_dtype = next(language_model.parameters()).dtype model_dtype = next(language_model.parameters()).dtype
head, head_payload = _load_head(args.head_checkpoint, run.device) head, head_payload = _load_head(args.head_checkpoint, run.device)
@@ -336,7 +355,10 @@ def _run(args: argparse.Namespace, run: DistributedRun) -> None:
"classes": list(CLASS_NAMES), "classes": list(CLASS_NAMES),
"prediction_layout": "[target_time, class, future_horizon]", "prediction_layout": "[target_time, class, future_horizon]",
"model_id": args.model_id, "model_id": args.model_id,
"adapter_checkpoint": str(adapter_path), "model_variant": model_variant,
"adapter_checkpoint": (
None if adapter_path is None else str(adapter_path)
),
"head_checkpoint": str(args.head_checkpoint.expanduser().resolve()), "head_checkpoint": str(args.head_checkpoint.expanduser().resolve()),
"head_training_step": head_payload.get("training_step"), "head_training_step": head_payload.get("training_step"),
"cache_dirs": [str(item.cache_dir) for item in sessions], "cache_dirs": [str(item.cache_dir) for item in sessions],