Add shared-AV multiwindow Qwen feature extraction
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# EasyCom:一次共享 AV 提取,多种独立 context cap
|
||||
|
||||
状态:实现和 CPU 合成测试已通过;**未跑真实 Qwen GPU smoke、全量提取或训练**。
|
||||
入口:[extract_easycom_multiwindow.py](../extract_easycom_multiwindow.py);
|
||||
缓存实现:[streaming_turn_multiwindow.py](../streaming_turn_multiwindow.py)。
|
||||
既有 [streaming_turn_model_directly.py](../streaming_turn_model_directly.py)
|
||||
单窗口命令不变,也可直接传入 `--memory-seconds-list`。
|
||||
|
||||
## 实验契约
|
||||
|
||||
默认导出 2/4/6/8/10/12 s;2 s native block、5 Hz 截止时间、2 FPS 因果视觉网格、
|
||||
observed partial tail;默认保存 last4 stack `[T,4,D]`。必须使用现有 Thinker replay
|
||||
要求的 `transformers==4.52.3`,不能用局部 encoder 对 4.57.6 的兼容性替代它。
|
||||
|
||||
- 一次 session reader,共享每个截止点的 preprocessing、AV block/tail embeddings。
|
||||
模型在所选 sessions 间只加载一次,所有缓存在 session 边界清空。
|
||||
- **每个 cap 有独立 Thinker KV、滚动重建和 readout**。旧历史移出后,用缓存的
|
||||
encoder embeddings 从空 Thinker KV 重放留下的完整 blocks。
|
||||
不裁剪长窗口 KV 或 pooled state 来假装短窗口。
|
||||
- 同一尾块仍随 200 ms 截止点增长并重编码;不是整个视频只编码一次。
|
||||
六窗口生产 AV preparation 为 `T` 次,不是 `6T` 次;Thinker 成本仍按窗口增加。
|
||||
`--verify-every` 额外从原媒体独立重放,不计入生产调用计数。
|
||||
- Cap 是**最大呈现预算**,不是每行精确固定长度的滑窗。稳态 span 为
|
||||
`cap - 2s + 当前 tail phase`,phase 在 0.2--2.0 s;startup 少于 cap。
|
||||
例如 cutoff=20.6 s:cap=2/4/8/12 s 分别读取
|
||||
`[20,20.6)` / `[18,20.6)` / `[14,20.6)` / `[10,20.6)`。
|
||||
局部 A 都是 `[19.6,20.6)`;0.2 s horizon 都指向 20.8 s。
|
||||
- Sessions 4--12 训练、1--3 测试。当前测试已多次查看,曲线如实标 exploratory;
|
||||
不按测试涨点选择窗口、训练 epoch 或 early stopping。
|
||||
- 同一份 1 s local sidecar 可复用,但其 dense rows、times、label indices、源媒体和
|
||||
encoder contract 必须一致;加载失败不得 padding/clamp。
|
||||
- 联合提取 wall time/显存不是单窗口流式效率。后者需单独运行所选 cap,测整个流水线。
|
||||
|
||||
## 1. 先跑 Session 4 的 24 s GPU smoke
|
||||
|
||||
从仓库根目录执行,路径自行设置,不使用某台机器的固定挂载。
|
||||
24 s 会触发最大 12 s cap 的滚动重建;4 s smoke 不足以覆盖全部窗口。
|
||||
|
||||
```bash
|
||||
MODEL_ID=/path/to/Qwen2.5-Omni-3B
|
||||
VIDEO_ROOT=/path/to/easycom/Main/Video_Compressed
|
||||
TARGET_ROOT=/path/to/EasyCom/target_perframe
|
||||
MULTI_ROOT=/path/to/omni_features/qwen_multiwindow_2fps_last4_stack
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 python3 -u extract_easycom_multiwindow.py \
|
||||
--model-id "$MODEL_ID" \
|
||||
--easycom-video-root "$VIDEO_ROOT" \
|
||||
--sessions Session_4 \
|
||||
--memory-seconds-list 2 4 6 8 10 12 \
|
||||
--end 24 --verify-every 11 \
|
||||
--flash-attention-2 \
|
||||
--output-root features/easycom_multiwindow_smoke
|
||||
```
|
||||
|
||||
也可用 `--dataset-root /path/to/easycom` 代替 `--easycom-video-root`。
|
||||
`--media-only` 只检验媒体/decoder、不加载模型、不写 features。
|
||||
`--end` 是 bounded smoke,故即便给了 target root 也不附加整段 target;输出标记
|
||||
`partial_extraction=true`,不能与整段 target/local sidecar 混用。
|
||||
|
||||
检查 shape、时间、freshness、预算、commit schedule 和独立 replay:
|
||||
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
import torch
|
||||
from local_turn_features import reference_alignment
|
||||
|
||||
root = Path("features/easycom_multiwindow_smoke")
|
||||
for cap in (2, 4, 6, 8, 10, 12):
|
||||
p = torch.load(root / f"context_{cap}s/Session_4.pt", map_location="cpu", weights_only=False)
|
||||
times, indices = reference_alignment(p)
|
||||
d = p["extraction_diagnostics"]
|
||||
assert p["states"].ndim == 3 and p["states"].shape[:2] == (120, 4)
|
||||
assert torch.isfinite(p["states"]).all()
|
||||
assert torch.equal(times, d["state_source_times"])
|
||||
assert d["state_age_seconds"].eq(0).all()
|
||||
assert (d["actual_context_seconds"] <= cap + 1e-6).all()
|
||||
assert d["payload_commit_mask"].nonzero().flatten().tolist() == list(range(9,120,10))
|
||||
valid = indices[:, None] + torch.arange(1,11)[None, :] < len(times)
|
||||
assert int(valid.all(dim=1).sum()) == p["valid_10_horizon_steps"] == 110
|
||||
checks = [c for c in p["verification"] if c["kind"] == "cache_replay"]
|
||||
assert len(checks) == 10
|
||||
print(cap, tuple(p["states"].shape), "shared AV calls=", d["production_processor_calls_shared"],
|
||||
"max_abs=", max(c["max_abs"] for c in checks),
|
||||
"min_cosine=", min(c["cosine"] for c in checks))
|
||||
PY
|
||||
```
|
||||
|
||||
Same-segmentation `cache_replay` 应 exact/effectively exact;显著差异先排查,不开始全量。
|
||||
若要核对既有单窗口生产路径,再运行相同媒体/参数的 `--memory-seconds 8 --end 24`
|
||||
到另一目录,逐行比较其 states 与 `context_8s/Session_4.pt`。
|
||||
CPU 测试已覆盖 >12 s 的滚动重建、可变 KV、从头 replay、future-only 合成输入改动、
|
||||
跨调用 reset、last4/mean、CLI 单窗口兼容及 head/local 加载成功和失败路径;
|
||||
这不能替代真实模型/媒体的因果 GPU 审计。
|
||||
|
||||
## 2. Smoke 审计通过后,一条命令提取所有 sessions
|
||||
|
||||
去掉 `--end`;完整目标长度和原始 20 FPS 视频帧数必须匹配。
|
||||
默认 sessions 为 1--12,也可以先只提取训练侧。验证关闭以避免额外 reference 开销。
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 python3 -u extract_easycom_multiwindow.py \
|
||||
--model-id "$MODEL_ID" \
|
||||
--easycom-video-root "$VIDEO_ROOT" \
|
||||
--target-root "$TARGET_ROOT" \
|
||||
--memory-seconds-list 2 4 6 8 10 12 \
|
||||
--flash-attention-2 \
|
||||
--output-root "$MULTI_ROOT"
|
||||
```
|
||||
|
||||
输出结构:每个 `context_2s/` ... `context_12s/` 含独立 `Session_1.pt` ...
|
||||
`Session_12.pt`。每个 payload 仍是 `mode=open-stream-replay`,保留 model、source、
|
||||
target、readout 和 sampling provenance,并记录 actual history range、phase boundary
|
||||
和共享生产计数。`extraction_summary.json` 每完成一个 session 原子更新,记录本次调用的
|
||||
sessions、输出 shape、cap、运行 commit/dirty 状态、依赖版本及联合耗时。
|
||||
|
||||
默认拒绝覆盖任一已有 bank。中断后检查文件;可用 `--sessions` 选择未完成 sessions,
|
||||
或换新输出目录。**不实现 checkpoint resume**;显式 `--overwrite` 才会重跑/替换。
|
||||
Summary 只描述本次调用,不能将上次与本次不同配置的输出默认为同一个实验。
|
||||
联合保存每个文件原子替换,但六个文件不是跨文件事务;异常后仍需核查是否齐全。
|
||||
|
||||
## 3. 每种 cap 独立训练 G 或 G+A head
|
||||
|
||||
复用 [run_easycom_local_baselines.sh](../run_easycom_local_baselines.sh) 的固定四 epoch
|
||||
recipe;不要复用 8 s head 到其他 cap 当作训练对照。
|
||||
`audio` 表示 **G+A**,不是纯 audio-only。`LOCAL_ROOT` 指向已验证的 1 s local sidecar。
|
||||
|
||||
```bash
|
||||
export TARGET_ROOT
|
||||
export LOCAL_ROOT=/path/to/omni_features/local_qwen_1s
|
||||
for cap in 2 4 6 8 10 12; do
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
FEATURE_ROOT="$MULTI_ROOT/context_${cap}s" \
|
||||
CHECKPOINT_ROOT="checkpoints/easycom_context_${cap}s_2026-09-17" \
|
||||
RESULT_ROOT="results/easycom_context_${cap}s_2026-09-17" \
|
||||
bash run_easycom_local_baselines.sh audio 0 1 2
|
||||
done
|
||||
```
|
||||
|
||||
要检验 local A 是否缓解 context 敏感性,在相同 roots 下另外运行 `baseline 0 1 2`
|
||||
(G-only);不要在已生成的 `audio_seedN` 路径重复启动。局部 V 是独立条件追加对照。
|
||||
现有 driver 训练 4--12 后立即推理 1--3,因此开始整组运行前冻结分析方案。
|
||||
报告完整预指定六-cap 曲线、dense 三类 AP/mAP、raw wearer endpoint onset/offset AP、
|
||||
prevalence 和 common valid pairs;semantic ENTRY/YIELD 仍等标注审计。
|
||||
|
||||
CPU 回归命令:
|
||||
|
||||
```bash
|
||||
python3 -m unittest test_streaming_multiwindow test_streaming_chunk_ablation \
|
||||
test_easycom_head_objectives test_local_turn_features test_encoder_only_workflow
|
||||
```
|
||||
Reference in New Issue
Block a user