# EasyCom:先跑 encoder-only A / V / AV 对照 复用已经提取的 Qwen 局部编码器特征,不输入 Thinker hidden states,不重提音频或视频, 不创建软链接。原 B1 的 `audio / visual / av` 分别是 **G+A / G+V / G+A+V**; 这里的新结果命名为 `encoder_audio / encoder_visual / encoder_av`,分别只有 **A / V / AV**。 固定训练 Session **4–12(升序,与原 B1 一致)**,测试 **3/2/1**;4 epochs、无 dev, 评测最后一轮。沿用原来的 5Hz 行索引、十个 horizon、LSTR-MCE 和 AP 协议,不改变标签。 已收到的 seed 0 结果与完整日志见 [2026-08-28 实验记录](../results/easycom_encoder_only_1s_4ep_2026-08-28/README.md)。 其中完整曲线为 `egospeak_full_timeline`,此前主表为 `causal_valid_only`,两者分开记录。 ## 1. 确定已经验证过的局部特征目录 用户已确认本次使用的实际目录是 `/data1/yfzx/wangx/Datasets/easycom/omni_features/local_qwen_1s_encoded_tf4576`, 不是早期默认的 `local_qwen_1s`;以下 checkpoint 读取方法可用于再次核对。 在 L20 机器更新本次代码后执行。下面从你之前跑完的 `av_seed0.pth` 读取实际路径, 避免猜错目录或混入 bbox 修正前的旧视觉特征;checkpoint 仅用于找路径,不作为新模型输入。 ```bash cd /data1/yfzx/wangx/Code/icassp2027-experiment DATA_ROOT=/data1/yfzx/wangx/Datasets/easycom LOCAL_ROOT=$(python - <<'PY' from pathlib import Path import torch checkpoint = Path("checkpoints/easycom_local_b1_encoded_tf4576/av_seed0.pth") p = torch.load(checkpoint, map_location="cpu", weights_only=False) value = p["training_config"]["local_feature_root"] if not value: raise ValueError("This checkpoint has no local-feature root") root = Path(value).expanduser().resolve() if not root.is_dir(): raise FileNotFoundError(root) print(root) PY ) && printf 'LOCAL_ROOT=%s\n' "$LOCAL_ROOT" ``` 如果 checkpoint 已移动,修改上述路径;也可以直接将 `LOCAL_ROOT` 设成以前 `easycom_local_b1_encoded_tf4576` 实验实际使用的局部特征目录。 必须包含完整的 `Session_1.pt` 到 `Session_12.pt`,不能用 `local_smoke_encoded`。 下面命令对空变量和缺文件会报错,不会自动重提特征或换成 Thinker 输入。 ## 2. 先做 seed 0,三组前台顺序运行 单张空闲 L20 足够先跑这组小 head,无需启动八卡提取或安装新的依赖。 下面先 A、再 AV、最后 V;使用同一批冻结特征,仅改变输入模态。 ```bash ( set -euo pipefail : "${DATA_ROOT:?Set DATA_ROOT first}" : "${LOCAL_ROOT:?Set the verified local-feature directory first}" export CUDA_VISIBLE_DEVICES=0 export OMP_NUM_THREADS=4 MKL_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 for modality in audio av visual do ENCODER_ROOT="${DATA_ROOT}/omni_features/encoder_only_${modality}_1s" python prepare_encoder_only_features.py \ --dataset easycom \ --data-root "$DATA_ROOT" \ --local-feature-root "$LOCAL_ROOT" \ --modalities "$modality" \ --output-root "$ENCODER_ROOT" python run_easycom_encoder_only.py \ --data-root "$DATA_ROOT" \ --feature-root "$ENCODER_ROOT" \ --modalities "$modality" \ --seeds 0 done ) ``` 两条 Python 命令都支持 `--dry-run`。整理的 dry-run 只验证源文件,不生成训练输入; 训练的 dry-run 则需要已完成的导出目录,只打印训练/评测命令,不启动训练。 整理在 CPU 上完成:读取原生向量+有效性标志,校验全部 12 sessions、时间/索引、 配置一致性和 target 长度,逐文件保存,最后写出 `complete.txt`。 默认 target 是 `$DATA_ROOT/easycom_target_frame`,可用 `--target-root` 覆盖。 不使用 Ego4D manifest。新入口和 `--dataset easycom` 不影响已有 Ego4D 命令。 输出: - 特征:`omni_features/encoder_only_audio_1s`、`encoder_only_av_1s`、`encoder_only_visual_1s`。 - 模型:`checkpoints/easycom_encoder_only_1s/encoder__seed0.pth`。 - 评测:`results/easycom_encoder_only_1s/encoder__seed0/metrics.json` 和 `pred/`。 导出目录、checkpoint 或结果目录已存在时**拒绝覆盖**。不要重新跑整个循环来补 seeds; 保留已完成的导出,只运行下面的训练入口。 若某次导出中断、没有完成标记,请使用新的 `--output-root`,不要手动补标记。 训练中断后换新的 `--checkpoint-root` / `--result-root`;不会自动恢复半成品训练。 ## 3. 打印结果,随后补 seed 1/2 ```bash python summarize_local_baselines.py \ --root results/easycom_encoder_only_1s \ --dataset easycom \ --protocol causal_valid_only ``` 请贴出三行 `encoder_audio_seed0`、`encoder_av_seed0`、`encoder_visual_seed0`。 原来的 81.72257 / 82.02661 / 73.47412 是 **G+A / G+A+V / G+V** 的 0.2s mAP, 不能标为 encoder-only。比较时使用相同 seed 和同一 `causal_valid_only` 协议。 第一轮确认能跑通后,补齐三个种子(不重新导出): ```bash ( set -euo pipefail : "${DATA_ROOT:?Set DATA_ROOT first}" export CUDA_VISIBLE_DEVICES=0 export OMP_NUM_THREADS=4 MKL_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 for modality in audio av visual do python run_easycom_encoder_only.py \ --data-root "$DATA_ROOT" \ --feature-root "${DATA_ROOT}/omni_features/encoder_only_${modality}_1s" \ --modalities "$modality" \ --seeds 1 2 done ) ``` ## 实验能回答什么 它测的是“Qwen 编码器近期 readout 本身的预测能力”,不是严格相同容量的 Thinker 消融。 3B 的 A/V 原生各 2048D;带有效性位后单模态 2049D、AV 4098D, 顺序是 `[visual, visual_valid, audio, audio_valid]`。不用 B1 的额外 256D 分支投影; 沿用原 MLP 类,隐藏宽度为输入维度整除 2,因此单模态 1024、AV 2049。 窗口、视觉 crop/视野和 head 参数量与原 Thinker 基线不同,应一起报告。 batch=512、lr=7e-5、weight_decay=5e-5、warmup_epochs=1.6、warmup_factor=0.3、 dropout=0.2、transition_objective=none。目录名 `1s` 不覆盖源文件的窗口配置, 真实窗口保存在导出 `config.encoder_contract.config`;时间含义完全沿用源特征。 本地 CPU 回归包括全部 12 sessions 的三模态导出、精确数值/时间对齐、缺文件和 partial 拒绝、 固定 split 顺序、无 Thinker 依赖,以及三个模态各 4 epochs 的合成数据训练/推理。 真实 EasyCom 精度和 L20 耗时由上述实验验证,不由这些测试推断。 ```bash python -m unittest -q test_easycom_encoder_only test_encoder_only_workflow ```