Files
icassp2027-experiment/docs/local_feature_baseline.md
2026-08-28 10:44:13 +08:00

201 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# B1:局部视觉+近期混合音频,普通池化融合
这是用于判断“原 Omni readout 是否丢掉有用的局部 AV 线索”的基线,
不是已证明的改进,也不是 multi-speaker attention 模型。
保留原 `[T,4,2048]` 特征;不重新提取全局 Thinker,不改标签和 AP 协议。
若要比较**不输入 Thinker 的纯 A / V / AV**,使用
[EasyCom encoder-only 实验](easycom_encoder_only.md)。本页的 `audio/visual/av`
仍包含全局 G,不能将两种设置混为一组。
## 固定实验定义
| 分支 | 冻结特征 | 更新/池化 | 训练时处理 |
|---|---|---|---|
| 原全局特征 | Thinker last4,`[T,4,2048]` | 原来的 8s memory、5Hz | 原 concat-MLP,8192→2048 |
| 局部视觉 V | 每个人脸裁剪,Qwen vision encoder + merger,2048D | 原视频固定 2FPS;先空间 mean,再逐人等权 mean,再最近 1s 有效帧等权 mean | 可训练 LN→Linear(2048,256)→GELU |
| 近期音频 A | Qwen audio encoder + 原生 output projection,2048D | 每 200ms 重编码 `[max(0,t-1),t)` 混合音频;仅池化有效输出 tokens | 同上 |
Qwen 3B 的 **2048 是编码器原生输出维度,256 只是新增 head 的可选投影宽度**。
特征文件永远保存原始维度,`--local-projection-dim 0` 可不降维。
正常 AV 组合为 `[2048 global,256 V,256 A,2 validity]`,共 2562D,
接 LN→Linear(2562,1024)→GELU→Dropout→Linear(1024,30),输出仍为 `[10,3]`。
单模态只附加对应一路与一个有效标志;不传 `--local-feature-root` 时保留原 head 参数结构。
局部视觉没有运动编码、ASD 分数、speaker-aware attention、身份 embedding 或图网络。
参与者 ID 仅保存供审计,不输入 head。人数多的帧不会因 token 多而得到更大权重。
没有有效框时视觉为零、valid=False;音频静音仍 valid=True。
缺少音轨、损坏的媒体或缺少应有的标注文件会报错,不会悄悄冒充“空输入”。
时间约定是 `[t-1,t)`:例如 t=1.0s,视觉取 0.0、0.5s,排除 1.0s 的帧。
音频先截断再做 mel 和编码,不对完整视频的音频编码后切片。
这保证相对于 readout 时刻的输入因果性,但不保证编码器在窗口内部逐 token 因果。
实现依据:[Qwen 3B 配置](https://huggingface.co/Qwen/Qwen2.5-Omni-3B/blob/main/config.json)、
[Transformers 4.52.3 编码器 API](https://github.com/huggingface/transformers/blob/v4.52.3/src/transformers/models/qwen2_5_omni/modeling_qwen2_5_omni.py)、
[4.57.6 编码器 API](https://github.com/huggingface/transformers/blob/v4.57.6/src/transformers/models/qwen2_5_omni/modeling_qwen2_5_omni.py)。
脚本加载 Thinker checkpoint 后释放语言解码器,仅将冻结的视觉/音频塔移到 GPU;没有 Thinker forward。
局部提取已适配 **Transformers 4.52.3 和 4.57.6**,现有 L20 / ms-swift 容器若是 4.57.6,
无需降级,也不要为这一步重新安装整个 `requirements.txt`。旧脚本只接受精确的 4.52.3,
因此即使接口存在也会在加载模型前主动报错;更新脚本后重跑原 smoke 命令即可。
两个版本都校验原生 token 输出形状与权重完整性,并显式使用 `use_fast=False` 固定图像预处理路径。
未知版本仍在媒体解码前报错;`--preview-only` 不受编码器版本检查影响。
该兼容范围只针对本局部提取脚本,不代表 Thinker KV replay 或 ROMA 的全部接口可互换。
输出的 `config.transformers` 记录实际运行版本,不再写死 4.52.3。
同一局部特征实验的 train/test sessions 应统一版本、权重和提取配置;加载器会拒绝混用版本。
接口兼容不等于不同版本的浮点特征逐位相同,已有全局 Omni 特征无需重提。
## 1. EasyCom:先检查框,再跑一个 4s smoke test
在已有项目依赖的 GPU 机器运行,以下路径沿用之前实验;raw 数据根按实际挂载修改。
不需要先重新编码 2FPS 视频,直接读取原 `Main/Video_Compressed`。
```bash
export CUDA_VISIBLE_DEVICES=0
MODEL=/data1/yfzx/wangx/Models/Qwen2.5-Omni-3B
DATA_ROOT=/data1/yfzx/wangx/Datasets/easycom/extracted_easycom
export FEATURE_ROOT=/data1/yfzx/wangx/Datasets/easycom/omni_features/open_stream_2fps_8s_last4_stack
export TARGET_ROOT=/data1/yfzx/wangx/Datasets/easycom/easycom_target_frame
export LOCAL_ROOT=/data1/yfzx/wangx/Datasets/easycom/omni_features/local_qwen_1s
LOCAL_ARGS=(
--dataset easycom --data-root "$DATA_ROOT"
--feature-root "$FEATURE_ROOT" --target-root "$TARGET_ROOT"
--model-id "$MODEL" --device cuda:0 --dtype bfloat16 --flash-attention-2
--video-fps 2 --visual-window-seconds 1 --audio-window-seconds 1
--box-kind face --box-coordinate-space encoded
)
# 不加载模型;输出四张带框图片,请先检查框是否真在人脸上。
python extract_turn_local_features.py "${LOCAL_ARGS[@]}" \
--samples 1 --max-steps 20 --preview-only --output-root features/local_preview
# 4s / 20 states;此文件明确标记 partial,不能误拿去训练整段。
python extract_turn_local_features.py "${LOCAL_ARGS[@]}" \
--samples 1 --max-steps 20 --output-root features/local_smoke
```
EasyCom face 标注首帧是 1,head 首帧是 0;按**原始解码帧号**取框。
视频裁去左侧身份 banner,保留右侧 1920 像素;这份 EasyCom 标注的框坐标包含 banner。
EasyCom 默认使用 `encoded`,先减去当前源视频的裁剪起点,再裁框:
`x_camera = x_annotation - (source_width - 1920)`。例如已核对的 Session_4 原视频宽度
为 2124,应减去 204;不同视频可能不同,不能统一硬编码。y 也按实际裁剪起点转换。
`camera` 仅用于已经手动换算到裁后图像的标注;Ego4D 无此 banner 裁剪,默认仍为 `camera`。
之前示例中的 `camera` 参数会导致这份原始 EasyCom 标注统一右偏,现已更正。
若当前 shell 的 `LOCAL_ARGS` 还含旧参数,请修改它,或将 `--box-coordinate-space encoded`
放在 `"${LOCAL_ARGS[@]}"` **后面**覆盖旧值。仅更新代码不会覆盖显式传入的旧参数。
预览会打印 source/cropped 尺寸与实际扣除偏移,同时在预览目录生成 `boxes.json`,
记录原框、裁后框与源帧号。必须检查框确实落在人脸上,不能只看最终特征维度。
官方帧号与 banner 说明见 [EasyCom 数据说明](https://github.com/facebookresearch/EasyComDataset#notes-and-known-issues)。
若已经用错误坐标提取局部视觉特征,必须重新从图像提取,不能靠修改 `.pt` 中的框坐标修复。
推荐输出到新目录(例如 `local_qwen_1s_encoded`),并把训练的 `LOCAL_ROOT` 指向新目录。
原全局 Omni 特征和 baseline 无需重算;旧局部 V/AV 训练结果不可与修正后的特征混用。
```bash
python - <<'PY'
import torch
p = torch.load('features/local_smoke/Session_1.pt', map_location='cpu', weights_only=False)
print('partial:', p['partial'])
print('transformers:', p['config']['transformers'])
print('box coordinates:', p['config']['box_coordinate_space'])
for key in ('visual', 'audio'):
x = p[key]
print(key, tuple(x.shape), x.dtype, 'finite:', bool(torch.isfinite(x).all()),
'valid:', p[key + '_valid'].float().mean().item())
print('person features:', tuple(p['persons']['features'].shape))
print('times:', p['times'][0].item(), p['times'][-1].item())
PY
```
3B 预期 V/A 各 `[20,2048]`,时间 0.2–4.0s;没有人脸的时刻允许 V 无效。
本机已验证 CPU 对齐/融合/合成数据训练推理,真实 checkpoint + GPU 提取仍需此 smoke test。
另有不下载权重的小型随机 Qwen 编码器 API 和本地 checkpoint 加载测试,
在 4.52.3 / 4.57.6 上均适用;checkpoint 加载测试需要 `accelerate`。
可先检查本机编码器接口,再跑完整回归:
```bash
python -m unittest -v test_local_turn_features.TinyQwenEncoderTests
python -m unittest -v test_easycom_head_objectives test_local_turn_features
```
## 2. 提取全部 12 sessions(只做一次)
```bash
python extract_turn_local_features.py "${LOCAL_ARGS[@]}" \
--samples 1 2 3 4 5 6 7 8 9 10 11 12 --output-root "$LOCAL_ROOT"
```
默认前台顺序执行,显示视觉帧进度和音频 state 进度;模型只加载一次。
已有文件默认会报错;恢复时可只列尚未完成的 sessions,或增加 `--reuse-existing`
逐文件验证完整性、时间对齐和提取配置后跳过已完成文件。
`--overwrite` 是明确重算,不会重用不兼容文件。不要覆盖原始 feature root。
`persons` 还保留每帧变长的逐人特征、frame_offsets、框、帧号和时间,供后续 B2 复用。
若在不同挂载路径提取同一 checkpoint,使用相同 `--encoder-id`;不同权重不可共用这个 ID。
## 3. 四组对照:4 epochs、无 dev、相同种子和 loss
| 名称 | 输入 |
|---|---|
| baseline | 原 Omni last4 |
| audio | 原 Omni + 近期音频 |
| visual | 原 Omni + 局部视觉 |
| av | 原 Omni + 两路局部特征(B1) |
```bash
# 先做 seed0 四组;默认前台跑完训练后立即评测 Session 3/2/1。
CUDA_VISIBLE_DEVICES=0 bash run_easycom_local_baselines.sh all 0
# 再补 seed1/2;不会覆盖 seed0。
CUDA_VISIBLE_DEVICES=0 bash run_easycom_local_baselines.sh all 1 2
# 也可把互不重复的组合分配到不同卡,不要复用相同 variant/seed 输出路径。
# CUDA_VISIBLE_DEVICES=1 bash run_easycom_local_baselines.sh audio 0
python summarize_local_baselines.py --protocol causal_valid_only
python summarize_local_baselines.py --protocol egospeak_full_timeline
```
脚本使用训练 Session 4–12、epoch=4、batch=512、lr=7e-5、wd=5e-5、warmup=1.6 epochs、
warmup_factor=0.3、dropout=0.2、LSTR-MCE、不加 transition objective。
`FEATURE_ROOT/TARGET_ROOT/LOCAL_ROOT` 用上面 export 的值,也可自行覆盖。
结果分别在 `checkpoints/easycom_local_b1` 和 `results/easycom_local_b1`。
如要不降维,设置 `LOCAL_PROJECTION_DIM=0`,并换 `CHECKPOINT_ROOT/RESULT_ROOT`。
手动训练只需在原命令增加:
```bash
--local-feature-root "$LOCAL_ROOT" --local-modalities av --local-projection-dim 256
```
手动推理增加 `--local-feature-root "$LOCAL_ROOT"`;模态组合从 checkpoint 读取。
Loader 会检查 sample、时间、label_indices、维度、valid mask 和 encoder/pooling 配置。
训练与评测必须用同一种 AP 协议作比较;不要混用 full-timeline 与 causal-valid 的表格。
## Ego4D 接口
完整命令见 [Ego4D 四组局部增强实验](ego4d_local_baselines.md):新增 manifest 批量提取、
续跑校验、`run_ego4d_local_baselines.py` 四组运行入口和匹配 seed 的增量汇总。
提取脚本同样支持 `--dataset ego4d`:输入根为 `/data/wangx/Datasets/ego4d`,
读取 `v1/clips/<uid>.mp4`、`v1/annotations/av_train.json` 与 `av_val.json`,
`--samples` 传已有 Omni `.pt` 的 clip UID。`--target-root` 指向 `ego4d_target_frame`。
可用 `--ego4d-annotations` 指定其他导出路径。先用 `--preview-only --max-steps 20` 检查框。
读取的是与 clip_uid 对应的可见人物 tracks;不使用 wearer 框、voice_segments、讲话标签,
不对缺失帧用未来框插值。载入大 JSON 需要相应主机内存。
train/infer 使用原 `--dataset ego4d --manifest ...` 加上述 local 参数;
EasyCom 的运行 shell 只管理 EasyCom,不能用于 Ego4D 的 split。
## 如何解读实验
先看每种子 paired 的 0.2s wearer AP 与 10-horizon mean mAP,不能提前承诺提升。
若只有 A 有效,先支持“近期声音补充”的解释;V/AV 的额外收益才支持局部视觉线索有价值。
这几组 head 参数量不同,结果不是纯架构公平性证明;下一步应做同参数量全局头/打乱局部输入控制。
这一版使用提供的框(部分身份标注经人工修正),不是在线 detector 的端到端测试。
论文必须披露这个协议,后续补在线检测框实验;不得把 close-mic、讲话标签或测试集调优引入输入。
提供框读取即使只取当前帧,也不代表框生成器本身是在线因果的。
2FPS 静态脸特征不能代表完整唇动建模;若 V 无提升,不宜直接断言多人视觉无用。