从 SAM2 到 GT-free 多模型选择:视频目标分割实验开发笔记
写在前面
这次实验最初只是一个标准的视频目标分割任务:给定视频首帧的目标掩码,把同一个目标逐帧追踪并分割出来。项目做到后半程,问题却逐渐从“如何得到更精细的掩码”变成了另一个更关键的问题:
在多个外观相似的实例之间,模型是否一直盯着最初指定的那个对象?
这个转变直接改变了后续的研究路线。我先后跑通了 SAM2.1-large、SAM2Long、TCP 时序后处理与 SAM3,又从简单投票一路尝试到手工 GT-free 选择器和机器学习选择器。最终得到的主要结论是:
核心结论在这组 VOS 实验中,模型间的主要差距不是“掩码边界差几个像素”,而是少数分歧点之后“是否继续跟对了目标”。因此,相比像素级融合,更有效的方向是以视频或物体为单位进行轨迹选择。
项目代码:
一页看完实验结果
单模型 Benchmark
以下成绩来自 25 个测试视频,指标为 CodaBench 报告的 J&F_new:
| 模型 | J&F | 观察 |
|---|---|---|
| SAM2-tiny | 57.47 | 最小模型 |
| SAM2.1-large + SAM2Long + TCP | 58.39 | TCP 未带来正收益 |
| SAM2.1-large + SAM2Long | 58.52 | 长记忆树整体低于 baseline |
| SAM2.1-large | 58.90 | 大模型 baseline |
| SAM3 | 60.59 | 最强单模型 |
最反直觉的结果是:SAM2Long 的整体成绩低于普通 SAM2.1-large。继续拆解到单个物体后才发现,长记忆并非普遍无效,而是呈现明显的“双刃剑”特征——部分视频提升 30 分以上,少数困难视频却直接损失 60 分以上。
多模型选择
| 方法 | J&F | 相对 SAM3 | 是否可在推理时复现 |
|---|---|---|---|
| 始终使用 SAM3 | 60.59 | - | 是 |
| 多数投票 | 61.39 | +0.80 | 是 |
| GT-free v5 | 61.33 | +0.74 | 是 |
| GT-free v8 | 62.75 | +2.16 | 是 |
| 视频级 oracle | 65.20 | +4.61 | 否 |
| 物体级 oracle | 67.42 | +6.83 | 否 |
这里必须区分两类结果:
- GT-free v8 只使用推理时可取得的掩码、轨迹和模型间一致性特征,可以自动运行;
- oracle 使用了平台返回的真实成绩来选择最优模型,相当于“看过答案后再选”,只能用于估计多模型选择的理论空间,不能包装成可部署算法。
报告截稿时,视频级 oracle 提交取得 J&F=65.2,对应 submission ID 812523(2026-06-25),在当时榜单中位列第 1,领先第 2 名约 2.7 分。这个名次是一次有用的上限实验,但不属于自动选择器的实际能力。
任务与数据
视频目标分割(Video Object Segmentation,VOS)的输入不是一句类别文本,而是首帧中指定目标的像素级掩码。模型必须在后续视频中持续输出同一实例的掩码。
任务看似只是把单图分割沿时间轴展开,实际还要处理:
- 目标运动、尺度变化与运动模糊;
- 遮挡、出画与重新出现;
- 相机运动与光照变化;
- 多个相似实例交叉时的身份保持;
- 多目标掩码之间的互斥和覆盖关系。
测试集采用 MOSE 风格、DAVIS 式调色板标注:
dataset/├── JPEGImages/│ └── {video_id}/│ ├── 00000.jpg│ ├── 00001.jpg│ └── ...└── Annotations/ └── {video_id}/ └── 00000.png其中首帧标注是 P 模式调色板 PNG,像素值 0 表示背景,1, 2, ... 表示不同物体。测试集共有:
| 数据 | 规模 |
|---|---|
| 视频数 | 25 |
| 每段帧数 | 约 81-126 |
| 标注物体数 | 共 64 个 |
| 单物体视频 | 14 个 |
| 单视频最多物体数 | 7 个 |
| 自拍图片 | 5 张,4096×3072 |
| 自拍视频 | 2 段,均不少于 5 秒 |
需要注意,“标注物体数”和“画面中的物体数”不是一回事。例如鸟群视频里可能出现几十只飞鸟,但评测只要求追踪首帧指定的几只。这正是 identity switch 最容易发生的场景。
评价指标与监督约束
平台使用 VOS 标准指标 J&F:
其中 是预测掩码, 是真实掩码。 是边界 F-measure,用于评价轮廓对齐程度,最终分数为:
这次实验有一个决定后续方法设计的硬约束:
没有逐帧 GT测试集只提供首帧标注。平台导出的
score/*.txt只有每个物体整段聚合后的 J&F,没有逐帧真值或逐帧得分。因此,任何可以在真实推理阶段运行的自动选择器,都不能把 GT、平台分数或未来帧真实标签当作输入。
这意味着我能知道“某条完整轨迹最后得了多少分”,却不知道“究竟从哪一帧开始跟错”。监督信号既稀疏,又只有 64 个物体,后面的 ML 实验会明显受到这一点限制。
Baseline:先把完整工程管线跑通
标准 SAM2 推理流程如下:
1. 首帧提示
从调色板 PNG 中读取所有非零 id,并为每个物体构造二值掩码:
annotation = np.array(Image.open(first_mask_path))object_ids = [x for x in np.unique(annotation) if x != 0]
for object_id in object_ids: binary_mask = annotation == object_id predictor.add_new_mask( inference_state=state, frame_idx=0, obj_id=int(object_id), mask=binary_mask, )2. 全序列传播
init_state 负责载入视频帧,propagate_in_video 返回每一帧、每个目标的 mask logits。这里真正需要关心的不只是“有没有输出”,还包括:
- 帧名与原图是否一一对应;
- 输出尺寸是否与输入严格一致;
- 多物体传播时 id 是否稳定;
- 目标暂时消失时是否应该输出空掩码;
- 多个掩码重叠时采用什么覆盖规则。
3. 多物体冲突裁决
实验中采用“小 id 先画,大 id 后覆盖”的确定性规则,首帧则直接用 GT 覆盖,保证条件帧不被模型输出污染:
label_map = np.zeros((height, width), dtype=np.uint8)
for object_id in sorted(object_masks): label_map[object_masks[object_id]] = object_id
if frame_idx == 0: label_map = first_frame_annotation.copy()这个规则不一定是最优的,但它可重复、容易检查,而且不会因为字典遍历顺序不同而生成不同提交。
4. 提交格式是隐藏的工程难点
输出文件不仅要“看起来像一张分割图”,还必须同时满足:
P模式 PNG;- 沿用首帧标注的调色板;
- 文件名、帧数、分辨率全部与输入一致;
- 每个像素保存的是物体 id,而不是 RGB 颜色;
- 文件夹层级完全符合平台要求。
output = Image.fromarray(label_map, mode="P")output.putpalette(first_frame_palette)output.save(output_path)如果把可视化后的 RGB 彩图直接当作提交,肉眼看上去可能完全正常,平台评分却会异常。对这类 Benchmark,格式验证和模型推理同样重要。
三条改进路线
SAM2Long:用记忆树缓解错误累积
SAM2Long 在 SAM2 上引入 memory tree,维护多条候选记忆路径,观察完整段视频后再回溯选择路径,试图缓解单路记忆中的误差累积。
实验采用官方 VOS 推荐参数:
num_pathway = 3iou_thre = 0.1uncertainty = 2它有一个结构性限制:记忆树的路径评分按单物体、单状态设计,因此多目标视频必须逐物体单独传播。
这会带来两项代价:
- 推理次数随物体数线性增长,本实验 64 个物体需要 64 次独立传播;
- 失去 SAM2 多物体联合传播时潜在的跨物体互斥约束。
一开始我怀疑第二点是 SAM2Long 崩溃的主因,但单物体视频 ukt9w7oi 上同样出现了灾难性失败。这个反例直接推翻了“主要是互斥约束丢失”的猜测。逐帧动画进一步显示,真正的问题是:记忆树在早期选错路径后,长记忆把错误身份稳定地固化了下来。
TCP:只处理掩码,不处理身份
TCP(Temporal Consistency Postprocessing)是一个基于 scipy 的纯后处理模块:
- 空间维度:填补小孔洞;
- 空间维度:删除面积小于 64 px 的孤立碎块;
- 时间维度:移除孤立帧闪烁;
- 时间维度:补齐 1 帧短遮挡;
- 可选:3 帧多数投票。
对应开关包括:
ENABLE_TCPPP_FILL_HOLESPP_MIN_AREAPP_FLICKERPP_TEMPORAL_VOTETCP 可以让边界更干净、短期轨迹更平滑,却不能把“跟到另一只鸟”的掩码重新变回正确目标。只要错误掩码本身连续、平滑、形状合理,后处理甚至看不出它是错的。这也是它最终没有提升 Benchmark 的原因。
SAM3:批量注册所有物体
SAM3 通过 HuggingFace transformers 与 ModelScope 权重完成推理,使用 Sam3TrackerVideoModel / Sam3TrackerVideoProcessor 的视频接口。
一个关键实现细节是:首帧必须一次性批量注册全部物体。若按 object 逐次注册,条件帧上可能出现部分物体没有 mask memory,随后传播报错:
maskmem_features ... cannot be emptySAM3 原生支持多物体联合传播,在本数据集上取得 J&F=60.59,成为最强单模型,也因此被选为 GT-free 选择器的默认模型。
研究转折:长记忆为什么既能大涨,也能崩盘?
把平均分拆到具体视频与物体后,SAM2Long 的双刃剑特征非常明显:
| 类型 | 视频 / 物体 | SAM2.1-large | SAM2Long | 差值 |
|---|---|---|---|---|
| 长记忆大涨 | 0d0030a7 obj5 | 51.49 | 87.48 | +36.0 |
| 长记忆大涨 | 1ef6cb7b obj3 | 63.08 | 94.44 | +31.4 |
| 长记忆大涨 | 0d0030a7 obj1 | 26.52 | 56.59 | +30.1 |
| 长记忆大涨 | sp9uf9in obj1 | 67.71 | 87.64 | +19.9 |
| 长记忆崩盘 | 429d96d4 obj6 | 83.03 | 64.76 | -18.3 |
| 长记忆崩盘 | 0u8fy7u2 obj1 | 76.07 | 15.82 | -60.3 |
| 长记忆崩盘 | ukt9w7oi obj1 | 69.16 | 0.91 | -68.3 |
可以把机制理解成:
在目标稳定、运动连续、干扰物少的视频中,长期记忆能抵抗短期噪声;在小目标、弱提示、密集相似实例的场景中,它也会同样坚定地记住一个错误对象。
差异主要发生在“身份”,而不是“质量”
进一步对比不同模型的逐帧掩码后,我得到三个重要观察:
- 在 64 个物体中,约 50 个物体上 SAM2Long 与 SAM2.1-large 的逐帧互 IoU 超过 0.9,面积比中位数接近 1.00,二者几乎没有可融合的差异;
- 真正拉开分数的是约 10-14 个物体,差异不是边界厚薄,而是整段轨迹指向了不同实例;
- 不同模型跟错的物体并不完全重合,因此存在按物体选择模型的提升空间。
由此得到两条工程结论:
- 融合只能“选”,不适合“糊”:不同实例的掩码做像素平均,会得到一个同时覆盖多个对象的错误结果;
- 多数投票不是免费午餐:三个同源 SAM2 模型可能一起犯错,把少数但正确的 SAM3 投掉。
实验中的多数投票只从 60.59 提升到 61.39,距离 oracle 仍有明显差距。集成的关键不是模型数量,而是错误模式是否足够独立。
多模型选择框架
整体框架不是逐像素融合,而是对每个 (video, object) 从候选模型中选出一条完整轨迹:
Oracle:先测量“选择”这件事值不值得做
在设计自动选择器前,我先使用平台返回的 per-object 分数构造 oracle:
- 视频级 oracle:每个视频选成绩最高的模型;
- 物体级 oracle:每个物体选成绩最高的模型。
视频级 oracle 达到 65.2,物体级 oracle 达到 67.42,显著高于 SAM3 的 60.59。视频级混合中模型使用次数为:
SAM3 ×13SAM2-tiny ×5SAM2.1-large ×4SAM2Long ×2SAM2Long + TCP ×1这个分布说明没有一个模型在所有视频上都最好,多模型之间确实存在互补性。Oracle 的意义不是“刷榜方案”,而是回答一个研究问题:如果自动选择器足够好,理论上还有多少空间?
GT-free 选择器:从不合物理的轨迹中找错误
自动选择器不能使用 GT,因此我把问题反过来:
无法直接证明哪条轨迹是对的,能否先检测哪条轨迹明显不合理?
对每个 (video, object, model),从逐帧质心、面积、bbox 与 active 状态中提取六类特征:
| 特征 | 捕捉的异常 | 权重 |
|---|---|---|
flash | 从边缘离开后在画面中央幻觉式闪现 | 4 |
far_reappear | 在画面内部消失后于远处重现 | 2 |
teleport | 单帧位移超过画面对角线的 30% | 1 |
blob | 只持续不超过 2 帧的孤立碎掩码 | 0.5 |
disengage | 在画面内部凭空消失并持续缺席 | 8 |
isolation | 轨迹活跃但明显偏离其它模型的紧密共识 | 8 |
综合惩罚定义为:
disengage 的边界条件非常重要:目标从画面边缘正常离开时,不应该因为输出空掩码而受罚;只有目标在画面内部凭空消失才被视作“放弃跟踪”。这个区分能把以下两种情况分开:
yjvoxabg:目标真的飞出边缘,输出空掩码是正确行为;9mv2g80y:目标仍在画面内部,模型却停止跟踪。
Stage 1:检测 SAM3 自己在“幻觉”
默认信任最强单模型 SAM3,取 penalty 最低的其它模型作为挑战者。只有同时满足以下高置信条件才允许切换:
- 所有模型并非都很脏,即最小 penalty 不超过 5;
- SAM3 自身出现中央
flash; - 挑战者 penalty 不超过 1;
- 挑战者相对 SAM3 的 penalty 优势至少为 3。
这是一种“保守默认”策略:宁愿错过一些潜在提升,也不要因为一个看似干净、实际跟错身份的轨迹而频繁切换。
Stage 2:检测 SAM3 安静地跟错对象
有一类错误不会产生闪烁:SAM3 全程平滑,但盯着错误实例;与此同时,三个 SAM2 模型形成一致轨迹。为此,v8 新增第二道机制:
- 用并查集按质心一致度对非 SAM3 模型聚类;
- 要求共识簇大小
csize == 3; - 共识簇的 disengage 指标
cdis < 0.55; - 共识轨迹活跃度
cact > 0.4; - SAM3 与共识的面积比
arat >= 0.75; - SAM3 与共识的一致度
s3ag < 0.65,确认它确实处于孤立状态。
五道门全部通过后,才切换到共识簇中 penalty 最低的模型。
Stage 1 和 Stage 2 不能简单合并,因为两种错误的几何表现相反:
- Stage 1 中,SAM3 的轨迹明显闪烁,错误是“吵闹的”;
- Stage 2 中,SAM3 平滑地跟错,错误是“安静的”。
v8 共触发 4 次切换,4 次全部净增益;报告中选取的 7 个陷阱物体没有误触,最终达到 62.75。
关于泛化能力v8 的阈值是在包含这些物体的 64-object 数据上调出的,因此 +2.16 带有 in-sample 乐观成分。它是当前实验中效果最好的可复现规则,不应被描述成已经通过独立测试集证明的通用算法。
ML 选择器:一次有价值的负结果
人工规则可解释,但阈值依赖经验。于是我又尝试用机器学习直接预测每个模型的得分,输入特征包括:
- 面积、质心、bbox 与缺席区间;
- 模型之间的互 IoU;
- disagreement window 的占比与长度;
- 分歧窗口前后的面积、位置和一致性变化;
- 共识簇、孤立度和 unphysical penalty。
为了避免同一视频中的多个物体同时出现在训练集和验证集,评估采用 Leave-One-Video-Out CV:
| 方法 | 留一视频 CV J&F | 命中最优模型 |
|---|---|---|
| 始终使用 SAM3 | 60.59 | - |
| Ridge 回归 | 56.20 | 22% |
| GBM 回归 | 58.60 | 22% |
| GBM + 保守门控 | 61.30 | - |
裸 ML 甚至低于 SAM3。只有规定“预测优势超过 18 分才允许切换”,把模型压回极保守状态,才能回到 61.30。
特征重要度最高的是分歧窗口占比 w_frac=0.495。但它更像“这个物体有多难”的代理,而不是“哪个模型正确”的信号。模型学会了何时不该信任 SAM3,却没有足够信息学会应该切换到谁。一旦放宽门控,错误切换会迅速吞掉全部增益。
这不是“机器学习无效”,而是一个关于数据与监督的结果:
- 只有 64 个带标签物体,样本量过小;
- 标签只有整段聚合 J&F,没有逐帧监督;
- 正确身份和错误身份可能具有同样平滑、合理的几何轨迹;
- 同源 SAM 模型的错误高度相关,候选多样性不足。
在当前已尝试的几何/掩码特征与监督条件下,多条路线最终都落在约 61-63 的区间。这个区间只是观察到的结果,不是经过证明的硬上限。
两个关键失败案例
ukt9w7oi:弱提示、小目标与密集行人
首帧目标位于街景画面中部偏上,区域很小,周围有大量行人和相似衣着,伴随强透视、遮挡和运动模糊。
SAM2Long 从最初的路径选择阶段就锁定了错误对象,之后长记忆持续巩固错误身份:
SAM2.1-large: 69.16SAM2Long: 0.91差值: -68.3这个案例说明,长记忆无法自动弥补低信息量提示。只要第一步身份选择错了,记得越牢,整段错误越稳定。
可能的工程应对包括:
- 检测“小首帧掩码 + 高 distractor 密度”,自动回退到 SAM2 联合传播或 SAM3;
- 使用点、框和 mask 的组合提示;
- 在早期帧保留多个身份候选,而不是立即提交到单一路径;
- 引入独立的 re-identification 表征验证候选身份。
0u8fy7u2:鸟群中的 identity switch
画面中有大量快速运动的同类飞鸟。SAM2Long 在约第 11 帧后平滑地切换到另一只鸟:
SAM2.1-large: 76.07SAM2Long: 15.82差值: -60.3它的质心移动连续、步长合理、面积也没有明显异常,从几何上看完全“合法”。这正是 GT-free 几何选择器最难处理的错误:轨迹没有坏,只是对象身份错了。
我尝试过把候选掩码区域与首帧目标做 HSV 直方图匹配,但鸟的姿态、视角与光照变化太大,同一只鸟在不同帧的外观差异甚至可能大于不同鸟之间的差异,简单颜色特征反而引入误判。
这个案例提示,突破方向可能不再是给 SAM2 输出加更多规则,而是引入 DINO 类语义表征、显式 re-identification,或 Cutie 等身份保持机制不同的 VOS 模型。
自拍实验暴露的概念缺口
除了标准数据集,我还设计了两段自拍视频:
- 叠书后抽走前书:测试模型是否理解先后顺序与遮挡关系;
- 硬币叠放:测试模型是否理解空间叠放次序。
模型在简单平移阶段能够稳定传播,但发生遮挡或层叠后容易混淆目标。这类失败和 Benchmark 中的 identity switch 相互补充:
- Benchmark 失败更像实例身份保持问题;
- 自拍失败进一步暴露出概念与语义推理的缺失。
SAM2 的 memory propagation 很擅长传播像素证据,却不等价于理解物体永久性、遮挡顺序或“应该继续追踪谁”。
工程环境与复现记录
实验在三类算力环境上运行:
| 平台 | GPU | 用途 |
|---|---|---|
| Kaggle | Tesla T4 ×2,各 16GB | 全量推理与对照实验 |
| 本地工作站 | RTX 4060,8GB | baseline 与自拍数据 |
| 腾讯 Cloud Studio | A100 | SAM3 与长时实验补充 |
主要环境:
Python >= 3.10PyTorch 2.x + CUDAsam2transformers >= 5.12acceleratesupervisionopencv-pythonnumpyscipypandasmatplotlibWindows 环境安装 SAM2 时,为跳过非必要 CUDA 扩展编译,需要设置:
SAM2_BUILD_CUDA=0SAM2_BUILD_ALLOW_ERRORS=1仓库的主要结构为:
notebooks/├── sam2_assignment.ipynb├── sam2_assignment_comparison.ipynb└── sam3_assignment.ipynb
scripts/├── selector/ # v5/v8、ML 选择器与诊断脚本└── figures/ # 模型对比动画和指标表
结果/ # 各模型逐帧输出和混合提交score/ # per-object 成绩与汇总建议按以下顺序复现:
- 准备
dataset/JPEGImages与只含首帧的dataset/Annotations; - 先用 SAM2.1-large 跑通一个视频,逐项验证文件名、尺寸、模式与调色板;
- 将
MAX_VIDEOS=None,生成完整 baseline; - 分别运行 SAM2Long、TCP 与 SAM3,保留各模型逐帧输出;
- 用
scripts/selector提取轨迹特征并生成混合提交; - 提交 CodaBench,严格区分自动结果与依赖平台反馈的 oracle 结果。
这次实验真正学到的东西
1. 平均分会掩盖机制
SAM2Long 的 58.52 看起来只是比 baseline 低 0.38,但拆到具体物体后,真实现象是 +36 与 -68 同时存在。只看平均值,很容易得出“长记忆没用”这种错误结论。
2. 先验证失败类型,再决定优化层级
如果错误是边界毛刺,形态学与时序后处理有用;如果错误是实例身份切换,继续调孔洞填充、连通域阈值或投票窗口几乎不会解决问题。优化方法必须和失败层级匹配。
3. 负结果也能确定研究边界
TCP 无增益、HSV re-identification 失效、裸 ML 低于 baseline,这些结果共同说明:纯几何与浅层外观特征不足以稳定判断身份。它们不是“没做出来”的附注,而是后续为什么需要语义表征、更强监督和异构模型的重要证据。
4. 集成收益来自错误独立性
三个同源模型不一定比一个强模型更可靠。如果它们共享主干、记忆机制和失效模式,多数投票只会放大相关错误。下一步更值得做的不是继续堆叠 SAM 变体,而是引入身份机制不同的模型。
下一步计划
如果继续推进这项研究,我会优先尝试:
- 逐帧监督:取得逐帧 J&F 或自行构造小规模逐帧标注,学习分歧发生的具体时间点;
- 语义外观特征:使用 DINO 等表征替代 HSV 直方图,比较首帧目标与候选轨迹的语义一致性;
- 光流与点跟踪:加入光流一致性、点跟踪覆盖率和遮挡恢复质量;
- 异构模型集成:加入 Cutie 等非 SAM 系 VOS 模型,提高错误模式独立性;
- 早期多假设追踪:在不确定阶段保留多个候选身份,避免长记忆过早固化单一路径;
- 独立验证集:把阈值搜索与最终评估完全分离,重新测量 v8 的真实泛化增益。
最终,我更愿意把这个项目理解为一次关于“模型为什么会跟错”的实验,而不只是一次分割模型跑分。掩码是否精细当然重要,但在视频里,首先必须保证这张掩码仍然属于最初指定的那个对象。身份正确,边界优化才有意义;身份错误,再平滑的轨迹也只是稳定地追错。
部分信息可能已经过时









