Composable Evidence States Research

第一阶段 MVP:在全合成符号世界中生成图像事实与文本事实/规则,训练 Evidence Extractor 将两种模态映射到可比较的 latent space。Composer 暂不在本阶段实现。

数据方案 A:全合成符号世界

每张图包含两个带颜色和形状的对象以及一条空间关系。manifest 保存完整结构化标注, 同时生成两类文本状态:

  • FACT:例如 obj1 is red,结构为 [FACT, subject, predicate, object, -, -]。
  • RULE:例如 Every red object belongs to class alpha,结构为 [RULE, variable, antecedent predicate, antecedent object, consequent predicate, consequent object]。
  • NULL:表示未被事实或规则占用的 slot。

FACT 与 RULE 共享 subject、predicate、object 分类头,但类型头明确区分二者;RULE 还有独立的 consequent predicate/object 训练目标。视觉分支只产生 FACT,文本分支同时 产生 FACT 和 RULE。因此实现不会把“事实”和“规则”当成完全相同的对象。

test_ood 使用训练集中明确排除的颜色–形状组合,并同时改变规则映射,用来检查组合泛化。

统一 latent space 的两个实验方案

方案 1(当前默认):共享分类头 + 跨模态对比学习

视觉和文本 extractor 使用相同的标签空间与同构分类头。Hungarian set matching 处理无序 slots;对比损失只把语义完全相同的视觉 FACT 与文本 FACT 拉近。RULE 保持为文本独有的 有类型状态,不参与错误的视觉规则对齐。

配置:configs/mvp.yaml,其中 prototype.enabled: false。

方案 2(已实现、默认关闭):Canonical Fact Prototype

CanonicalFactPrototype 将类型、主语、前件谓词/宾语以及后件谓词/宾语的可学习 embedding 组合成规范原型,并用 cosine loss 把 matched latent state 拉向该原型。类型 embedding 与 RULE 后件字段确保 FACT 和 RULE 的原型结构不同。

使用完整的 configs/prototype.yaml 即可单独训练和比较:

uv run python train_extractor.py --config configs/prototype.yaml --backbone tiny

运行

uv sync --extra dev
uv run python -m data.build_splits --config configs/mvp.yaml --small
uv run python train_extractor.py --config configs/mvp.yaml --backbone tiny --epochs 2
uv run python -m eval.evaluate_slots --config configs/mvp.yaml \
  --checkpoint artifacts/tiny/best.pt --backbone tiny
uv run pytest

正式 GPU 实验可把 --small 去掉并改用冻结 CLIP:

uv sync --extra dev --extra hf
uv run python -m data.build_splits --config configs/mvp.yaml
uv run python train_extractor.py --config configs/mvp.yaml --backbone clip

正式 2B 级训练

正式实验使用 google/siglip2-giant-opt-patch16-384:约 2B 总参数、384 分辨率的 原生图文双塔。backbone 使用 BF16 并冻结,四层 512 维 Evidence Extractor 参与训练。 configs/siglip2_giant.yaml 定义 30,000/3,000/3,000/3,000 数据划分与 10 epochs。

Gitea 的 Formal GPU training workflow 会:

  • 校验预先放在 /root/autodl-tmp/ces-models/siglip2-giant 的固定版本模型;
  • 在 /root/autodl-tmp/ces-data/synthetic-v1 复用完整数据集;
  • 每个 epoch 保存可恢复的 last.pt,并按验证集保存 best.pt;
  • 将运行配置、硬件、分项 loss、每轮耗时、所有验证指标和最终 benchmark 放在 Actions 的 Details step;
  • 将结果持久化到 /root/autodl-tmp/ces-artifacts/siglip2-giant;
  • 最后用六项研究门槛决定 workflow 是否通过。

为避免付费 GPU 等待 7.53 GB 权重下载,先在网络更稳定的无 GPU 服务器上执行:

python3 -m venv ces-model-download
source ces-model-download/bin/activate
python -m pip install -U "huggingface_hub[hf_xet]"
HF_ENDPOINT=https://hf-mirror.com hf download \
  google/siglip2-giant-opt-patch16-384 \
  --revision a713301b217d38485fb2204c808367d10bc3cc40 \
  --local-dir /data/ces-models/siglip2-giant
du -sh /data/ces-models/siglip2-giant

然后从该服务器断点续传到 AutoDL(替换 SSH 主机和端口):

ssh -p <AUTODL_SSH_PORT> root@<AUTODL_SSH_HOST> \
  'mkdir -p /root/autodl-tmp/ces-models/siglip2-giant'
rsync -ahP --partial --append-verify \
  -e 'ssh -p <AUTODL_SSH_PORT>' \
  /data/ces-models/siglip2-giant/ \
  root@<AUTODL_SSH_HOST>:/root/autodl-tmp/ces-models/siglip2-giant/

如果两台服务器不能直连,使用阿里云 OSS 作为中转;目标目录保持不变。workflow 只做完整文件校验,不再从 Hugging Face 下载,因此模型传好后再手动触发正式训练。

手动等价命令为:

python -m data.build_splits --config configs/siglip2_giant.yaml --reuse-existing
python train_extractor.py --config configs/siglip2_giant.yaml --backbone siglip2
python -m eval.benchmark_extractor \
  --config configs/siglip2_giant.yaml \
  --checkpoint /root/autodl-tmp/ces-artifacts/siglip2-giant/best.pt \
  --backbone siglip2 \
  --output /root/autodl-tmp/ces-artifacts/siglip2-giant/benchmark.json \
  --enforce-gates

每次训练后的独立验收

以下命令运行统一 benchmark,写出 JSON,并在任一指标未达标时返回非零退出码:

uv run python -m eval.benchmark_extractor \
  --config configs/mvp.yaml \
  --checkpoint artifacts/clip/best.pt \
  --backbone clip \
  --output artifacts/clip/benchmark.json \
  --enforce-gates
指标 门槛 定义
Text fact/rule F1 0.98 IID 文本中 typed FACT 与 typed RULE 的六字段 exact-match micro F1
Visual fact F1 0.90 IID 图像 FACT 的四个有效字段 exact-match micro F1
Cross-modal Recall@1 0.80 文本 FACT 检索全部视觉 FACT,最近邻标签完全相同
Hard-negative Recall@1 0.75 候选仅含正确项与恰好错一个基础字段的视觉 FACT
OOD fact F1 0.80 held-out 颜色–形状组合上的视觉 FACT exact-match F1
NULL slot accuracy 0.95 permutation matching 后,应为 NULL 的空槽中预测为 NULL 的比例

CI 的单 epoch smoke run 会执行 benchmark 来验证整条评测管线,但不会使用 --enforce-gates,因为 smoke 模型并不代表正式实验是否达标。正式训练完成后使用上述强制命令。

AutoDL Pro 临时 Runner

项目代码始终只在本地 Git 工作区和 Gitea 中维护。阿里云服务器是持久化的算力控制器与 模型/产物中转站,AutoDL Pro 实例只是临时 Gitea Actions 执行节点。控制器不会把项目源码 复制到任何服务器;ephemeral runner 接受任务后,workflow 才从 Gitea checkout 指定 commit。

ops/autodl_pro_controller.py 实现以下生命周期:

  1. 调用 AutoDL Pro API 创建一张 PRO6000-96G;
  2. 等待 SSH 就绪,将模型数据和经过 SHA-256 固定的 act_runner 传入实例;
  3. 注册只带 ces-gpu-formal:host 专用标签的一次性 runner;
  4. 可选地通过 Gitea API dispatch gpu-train.yaml,也可保留为手动触发;
  5. 等待 runner 完成一项任务并退出,将 checkpoint、benchmark 和 runner 日志回传阿里云;
  6. 无论成功或失败都调用 API 关机;默认不释放实例,避免未经确认删除系统盘。

先把示例配置复制到阿里云控制器的受限目录并填写私有镜像 UUID:

install -d -m 700 /root/.config/ces-controller/secrets
install -m 600 ops/autodl-pro.example.json /root/.config/ces-controller/config.json

AutoDL 开发者 Token、Gitea runner 注册 Token,以及可选的 Gitea API Token 必须分别放在 配置指定的 root-only 文件中,不能写进配置、仓库、命令行或 AutoDL start_command。控制器 还需要一把专用于 AutoDL 的 SSH 私钥;将其公钥预先添加到 AutoDL 账号的 SSH 公钥设置。

首次运行先检查不会产生费用的执行计划:

python3 ops/autodl_pro_controller.py plan \
  --config /root/.config/ces-controller/config.json

确认镜像、GPU、系统盘扩容和是否自动 dispatch 后,再在 tmux 或 systemd 中运行付费流程:

python3 ops/autodl_pro_controller.py run \
  --config /root/.config/ces-controller/config.json \
  --confirm-create

run 会真实创建按量计费实例。release_after_run 默认是 false;只有确认回传产物完整并愿意 删除该 Pro 实例时才应改为 true。控制器只负责调度与数据搬运,训练步骤仍完全由 .gitea/workflows/gpu-train.yaml 定义。

正式 Runner 故意不注册 ubuntu-latest、ces-ci 或通用 gpu 标签。ephemeral runner 只会 执行一个任务;使用通用标签可能让它先领取普通 CI 或 smoke job,随后退出,导致正式训练继续 排队。普通测试 Runner 与临时正式训练 Runner 因而使用互不重叠的标签。

S
Description
No description provided
Readme 236 KiB
Languages
Python 100%