☰
从零搭建指代视频目标分割(RVOS)系统:多模态对齐与时间一致性实践
2026/10/17 14:33:47 网站建设 项目流程

做视频理解相关的项目时,有一个方向每次想起来都会让人又爱又恨:Referring Video Object Segmentation,简称 RVOS。翻译成人话就是“指代视频目标分割”。给你一段视频,再给你一句自然语言描述,比如“第二个进球后跑到场地左侧抱头蹲下的红队球员”,模型要把这句话指向的那个对象,在整段视频的所有帧里,一个像素不差地分割出来,还得保持同一身份。

这个任务听起来只是“VOS 加了一句文本”,但真正动手做就会发现,它把视觉、语言、时序三个难题全揉在了一起。目标外观在变、镜头在动、目标会被遮挡甚至短暂离场,而唯一的“锚点”只是一句话。干过视频分割的人都知道这里面的坑有多深。

这篇文我想从一个普通开发者的视角,把从零研究 RVOS 的完整链路梳理一遍。核心概念是什么、模型架构怎么搭、有哪些必须搞懂的数据与指标、训练时会撞上哪些墙、以及我踩坑之后摸索出的解决办法。适合有一定深度学习基础、想做视频理解或多模态方向的朋友,也欢迎纯新手把本文当作一个“带路地图”。

1. 先把概念讲清楚:RVOS 到底难在哪

1.1 从 VOS 到 RVOS:多了一个“所指”维度

传统 Video Object Segmentation 通常解决两类问题。半监督 VOS 给你第一帧的目标 mask,让你把后续帧的目标都抠出来。无监督 VOS 则依赖模型自己从运动、显著性等线索里找目标。这两类任务有一个共同特点:目标身份由“第一帧的 mask”或者“谁最突出”来确定,模型不需要理解语言。

而 RVOS 把目标定义完全交给了自然语言。同一个视频里可能有五六个人、七八辆车,模型必须准确理解“穿红色卫衣背双肩包的男人”和“站在商店门口打电话的人”到底是不是同一个。这要求模型同时具备细粒度视觉感知、语言理解、跨模态对齐和时间跟踪能力。

多出的这个“所指”维度,让任务难度直接上一个台阶。语言描述本身就有歧义,比如“左边的那个”,不同帧里左边可能是不同的人,甚至目标转身后“左边”就不再适用。这个描述在时间维度上要动态解译,模型不能只在第一帧做一次对齐,然后闷头跟踪,因为描述与视觉内容的关系随时间会变。

1.2 三种典型场景里,为什么需要 RVOS

顺着刚说的特性往下想,RVOS 的实际价值其实很好理解。视频编辑里最常见的需求是“帮我单独抠出某个人物”,但只靠人物检测做不到像素级、贯穿全片段的分割。RVOS 把定位与分割统一了,一句“戴白色帽子的女生”就能驱动批量出片和动态打码。

自动驾驶和机器人领域也很有用。视觉系统不能光检测“行人”,还要能理解“站在斑马线中间回头看的那个行人”。当指令来自自然语言时,模型相当于在完成一个“视觉锚定”动作,把语言意图映射到连续的像素和时间序列上。

内容检索场景同样吃这套能力。海量视频里搜“穿蓝色球衣控球的后卫”,传统方案靠标签与人工打点,而 RVOS 直接输出目标的位置掩码,检索结果可以直接用于裁剪、跟踪和统计。这不是一个实验室里自嗨的任务,它确实能落地到很多真实需求上。

1.3 拆开看,挑战其实是四座山

第一座山是语言与视觉的对齐。文本是高度抽象的,视频是连续且冗余的,词和像素之间没有固定映射。第二座山是时间一致性。模型不能每帧独立猜目标,那样会出现身份跳变,必须建立跨帧的记忆或传播机制。第三座山是遮挡与长时消失。目标被汽车挡住、走进镜头外再回来,模型需要靠描述和上下文把身份接续上。第四座山是像素级分割的精度,语言定位通常给一个框就够了,但分割要精细到轮廓,这对特征分辨率提出了很高要求。

这几座山叠在一起,比单独的检测、分割或者跟踪都要难。很多模型给人一种“在某几帧效果好”的错觉,但放到完整视频里跑一遍,就会暴露稳定性的问题。早年我们实验室里跑基线模型时,看到第一帧分得准就以为成了,后来发现目标消失两秒再出现就直接跟丢,那种挫败感特别真实。

2. 核心组件拆解:视觉、语言、时序怎么拧在一起

2.1 双编码器:视频流和文本流缺一不可

主流 RVOS 模型基本都走“双编码器 + 融合 + 解码”的路子。视觉编码器负责把每一帧图像变成高维特征,为了捕捉运动信息,通常还会输入相邻帧的差分或光流,让模型知道哪些区域在动,哪些区域因为场景变化而改变。文本编码器则将描述转换成特征向量。

视觉编码器的选型会显著影响分割精度。早期模型爱用 ResNet 这类卷积网络叠加 FPN 结构,好处是能输出多尺度特征,便于分割小目标。后来大家转向视频 Transformer 或者预训练的视觉语言基础模型,因为这类模型天然能把“图像区域”和“语言词元”放进同一特征空间,等于给对齐铺好了一层底子。

文本编码器的选择同样关键。如果你用的是 CLIP 文本分支,那么描述会被编码成一组词元特征和一句整句特征。词元级别特征用来做细粒度匹配,比如对象“红色帽子”可以跟图像里的局部区域呼应;整句特征则用来约束全局,避免模型把局部特征误解成别的对象。这两个特征不能只留一个,我们在实践里发现只保留整句特征会让模型丢失很多细节语义。

2.2 融合模块:最简单的拼接往往最难训

拿到两种模态特征以后,怎么融合是 RVOS 的灵魂。最笨的办法是把文本特征复制到每个空间位置,和视觉特征直接在通道维度上拼接,然后扔给卷积网络。这种“早期拼接”的问题很明显:文本特征只有一句,拼到每个像素上相当于给每个位置加了一堆相同的通道,模型需要自己学会“这句话在哪些位置才起作用”,学习负担非常大,收敛也慢。

更聪明的办法是交叉注意力。让视觉特征作为 Query,文本特征作为 Key/Value,模型能在一句话里自动找到跟当前像素相关的词元。这种机制的好处是每个像素可以关注不同词,例如一个人的上半身关注“红衣服”,腿部关注“黑色短裤”。后面再做一次自注意力,把不同位置的匹配结果整合起来,避免分割结果出现“脑袋认了、身体没认”的割裂情况。

除了注意力,还有一类做法是把语言当作“条件”注入到解码器里,这就是所谓的语言引导掩码生成。代表思路是用文本特征生成一组可学习的对象查询,类似检测里的 object query。每个查询负责一个可能存在的目标,然后查询跟视频特征交互、迭代更新,最终解码出 mask。这类设计天然适合做多目标,也方便在时间维度上把同一查询传给下一帧。

2.3 时间传播:别让模型每帧从零开始

如果模型只在每一帧独立做分割,那么即使单帧效果再好,整个视频也会有抖动和错位。时间信息的处理大致分三种流派。

第一种是帧间注意力。把前后帧的特征做一个 transformer 的自注意力,让当前帧特征包含前后帧的上下文。这种方式适合做短时序建模,但无法记忆很久以前的目标信息,目标离场太久就可能丢。

第二种是基于记忆库的传播。可以维护一个滚动队列,把先前帧预测的目标特征存入记忆,当前帧通过跨注意力去记忆里查询相似对象。这种方式更贴近半监督 VOS 的思路,特别适合长视频,对目标被遮挡后重新出现比较友好。

第三种也是最简单但很有效的,就是把视频切成长度为 T 的片段,一次性把 T 帧同时输入模型做时空建模。模型不仅能看到运动,还能在空间和时间两个维度上做联合注意力。缺点是显存消耗大,片段长度受限。

我在初期做尝试时,为了省显存,只用了两帧输入。结果模型在目标快速移动时经常把上下文搞混,表现特别飘。后来老老实实把片段长度加到 6 帧,配合时间下采样和梯度累积,效果才稳定下来。

2.4 解码与分割:从特征回归到像素 Mask

融合后的特征要变成精细的 mask,一般会经过一个解码器。常见做法是用 FPN 风格的自上而下结构,把高层语义特征逐层上采样,与低层细节特征拼接,最终通过 sigmoid 输出的单通道掩码。掩码分辨率通常和输入分辨率一致,或按比例缩小后再上采样到原尺寸。

解码器里最被忽视的往往是“多尺度监督”。只用最终输出做损失,中间层没有约束,容易导致小目标细节丢失。我的做法是在多级特征上分别接轻量分割头,各级输出分别计算损失,再把不同尺度的预测合并。虽然训练稍慢一些,但对小目标和轮廓质量的提升非常明显。

一个值得注意的点是,mask 输出不需要非要按视频帧逐帧单独算。可以借鉴 Video Instance Segmentation 的做法,输出时用 mask 时序一致性损失,让相邻帧的 mask 变化不过于剧烈。这样最后呈现出来的分割结果不会有明显的“闪烁感”。

3. 数据与评估:你看到的分数可能没你想的那么美

3.1 数据集怎么选,决定了问题边界

RVOS 领域常用的数据集有一批经典名字,比如 A2D、J-HMDB、Ref-DAVIS、Refer-YouTube-VOS。其中 Ref-DAVIS 是从 DAVIS 数据集扩展来的,标注质量高,视频长度较短,适合做学术验证和 Debug。Refer-YouTube-VOS 是从 YouTube-VOS 扩展来的,视频更长、场景更杂、目标更多,更接近真实应用,但对模型的稳定性和显存要求也高得多。

A2D 和 J-HMDB 是老牌动作视频数据,标注相对简单,现在主要用来做预训练或者跨数据集验证。如果你刚开始接触 RVOS,我建议优先在 Ref-DAVIS 上跑通流程。数据集规模小,迭代快,能很快感受到模型改进的方向。等你把框架调顺,再迁移到 Refer-YouTube-VOS 上进行大训练,这样可以少走很多弯路。

文本标注是 RVOS 数据集里最有趣也最麻烦的部分。同一目标可以有多种描述方式,比如“这个穿黑衣服的人”“正在跑步的人”“画面右侧那个人”。训练时如果只用一种文本,模型会过拟合到固定句式。扩充文本或引入模板增强是必须的操作,即使是简单的一句“左边第一个人”也能给模型带来不一样的监督信号。

3.2 指标 J&F:一维分数远不能表达整体表现

RVOS 最常用的指标之一是 region similarity J 和 contour fidelity F,平均后得到 J&F。J 本质上是预测 mask 与真实 mask 的交并比,衡量区域重合度。F 则计算预测边界与真实边界的 F1 分数,衡量轮廓贴边程度。两者各有所长,但要小心:J 高并不意味着轮廓干净,而 F 高也不代表区域重合够多,所以报告分数时一定要看组合。

除 J&F 外,还有 mIoU、CorLoc、video-level metric 等一系列指标。CorLoc 用于评估定位准确率,只看预测框是否框中了目标,不看像素精度。Video-level metric 则要求模型在整个视频上保持同一目标身份,如果中途跟丢就视为整体失败。很多论文会挑着报对自己有利的指标,作为工程人员你最好把 J&F、video-level 都看看,才能判断模型到底行不行。

另一个容易被忽视的坑是“分辨率对分数的影响”。相同模型在 480p 和 720p 下跑,J&F 会有明显差异,但你不能说模型变差了,只能说输入变了。做工程对比时必须在同一输入尺寸、同一后处理链路上跑,否则比较毫无意义。我在做消融实验时吃过这个亏,两版代码在输入 Resize 上差了几十像素,分数差了快两个点,害我排查了一整天。

3.3 数据预处理细节:比想象中更影响上限

视频读取要统一帧率。Refer-YouTube-VOS 里不同视频帧率差异很大,如果不做统一采样,模型会误把时间间隔当目标运动的一部分。通常会按固定帧数均匀采样整段视频,或者按固定 fps 抽帧,再在训练时随机裁剪一段连续镜头。

文本预处理也值得花时间。统一小写、去除多余标点、英文语料里的缩写扩展,这些小操作就能提升零点几个点。更重要的是一致性问题:训练时的文本风格要和测试时一致。比如训练时全是“a person wearing a red shirt”,测试时给“穿红衣服的人”,那跨语言或跨风格的效果肯定打折扣。必要时可以用文本增强模拟多样性。

最后是掩码标注的一致性问题。RVOS 数据集里,同一个目标在不同帧的掩码通常由不同标注员标注,边缘会有噪声。训练时把 mask 做轻微腐蚀或膨胀,或者对 label 做 one-hot 平滑,都能让模型不那么在意噪声。直接硬怼原图 label 容易让模型学到“边缘锯齿”这种不想要的特征。

4. 从零搭建一个可用基线:代码思路与关键实现

4.1 整体框架与数据加载设计

我会用一种非常通用的设计来讲,方便你迁移到自己熟悉的框架里。整体可以分成四块:视觉编码器、文本编码器、多模态融合器、掩码头。视觉编码器可以用一个预训练的视觉 Transformer,输入是连续的 T 帧图像,输出每一帧的多层特征。文本编码器则直接加载预训练的文本模型,输出词元特征和句子特征。

数据加载器的关键在“样本构造”。每次采样是一个三元组:视频片段、文本描述、每一帧的 mask。训练时视频片段通常是随机裁剪的连续帧序列,但测试时往往需要整段推理或者滑窗推理。在构造批次时会遇到一个麻烦:不同样本的 mask 类别数不同,有的只有一个目标,有的可能有两个目标,而语言描述次数也可能变化,因此需要 pad 到统一长度。

一个常见的数据增强是“同视频多句描述”。一个视频虽然有一个标准描述,但可以衍生出很多同义表达,比如“左边那个戴帽子的男人”“正在和店员交谈的顾客”。这种增强成本很低,对模型鲁棒性却很有帮助。

4.2 模型前向:用伪代码看清信息流

下面是一套直接可上手的伪代码式结构。我用 PyTorch 风格写,但去掉了实际 API 细节,重点是让你看清各模块之间如何传递信息:

# 输入 clip_frames: [B, T, H, W, 3] # 视频片段 text: [B, L] # 文本 token ids gt_masks: [B, T, num_queries, H, W] # 1. 文本编码,padding mask 需要保留 text_features, text_emb = text_encoder(text) # text_features: [B, L, C](词元级) # text_emb: [B, C](句子级) # 2. 视觉编码,把时空维度合并再拆开 video_feats = visual_encoder(clip_frames) # [B, T, C, Hi, Wi] Hi, Wi = video_feats.shape[-2:] # 3. 重建空间网格,进行逐帧交叉注意力 v_pixels = video_feats.flatten(2).transpose(1, 2) # [B, T*Hi*Wi, C] # 每个像素位置都与文本词元做 cross-attention v_fused = cross_attention(v_pixels, text_features, text_features) v_fused = self_attention(v_fused) v_fused = v_fused.reshape(B, T, Hi, Wi, C) # 4. 生成语言引导查询(或者直接用固定 query 加文本条件) queries = query_embeddings.unsqueeze(0).expand(B, num_queries, -1) queries = conditional_update(queries, text_emb) # 5. 查询与视频特征迭代交互,掩码头解码出 mask for i in range(decode_layers): queries = decoder_layer(queries, v_fused[i]) # 每层 attention mask_logits = mask_head(queries, multi_scale_feats[i]) # 6. 多尺度损失 loss = bce_loss(mask_logits, gt_masks) + dice_loss(mask_logits, gt_masks)

这里面最容易出问题的就是num_queries的设定。如果只预测一个目标,就设 1 个 query;但现实场景往往有多个相同类别目标,文本帮你指定了特定的“那一个”,查询则需要多个候选。设少了找不到目标,设多了会造成重复预测与混乱。我的经验是固定 5-10 个 query,训练时通过二分匹配给每个 query 分配一个目标,没匹配上的 query 负责输出空 mask。

4.3 损失函数与训练策略的搭配

训练一个 RVOS 模型,损失函数建议由两个以上组成。BCE 是最基本的像素分类损失,但它对前景背景样本不均衡不够友好。Dice loss 直接优化区域重叠,对小目标尤其友善,可以缓解“背景淹没前景”的问题。如果你还希望 mask 边缘更干净,可以再加一个可选的边界损失,不过要谨慎,边界损失权重太高容易让模型忽略区域完整性。

梯度计算上,视觉主干尽量用可学习的低学习率,文本编码器前几个 epoch 可以冻结。等模型把基本对齐关系学出来以后,再逐渐解冻文本编码器,这样可以有效防止训练初期灾难性遗忘。整个训练过程可以分成预热、联合训练、微调三个阶段,分别对应 1e-4、1e-4 到 5e-5、1e-5 量级的学习率。

一个隐藏的比较有用的技巧是“视频级负样本”。随机把同一视频里的描述换到另一段视频上,构造“这个描述不属于这段视频”的样本。模型需要输出空 mask。这么做能让模型学会“区分对象而不乱分割”,对抑制无语义区域的误分割帮助很大。

5. 训练过程中的坑与对抗手册

5.1 模型为什么会“只认出颜色,认不出人”

这是我在做 RVOS 时遇到的最典型的智商下降现象。模型听到“穿蓝色衣服的人”,就把所有带蓝色的区域都激活,包括背景里的蓝色汽车。这是典型的多模态对齐不充分、文本描述过度权重到名词而不是整体对象。解决办法有两种思路。

第一种是给模型更明确的“对象性”先验。比如在视觉特征旁边额外加一个前景概率图,让模型知道哪些区域更像“完整对象”而不是色块。这可以用一个轻量显著性分支生成。第二种是损失函数上做文章,空间上增加一个紧凑性约束,让 mask 尽量是连通域,避免零星小块四处乱跳。

我在项目里常用一个简单但有效的手段:在训练时把同一文本的目标描述换成不完全一致的词,比如“蓝色衣服”“身穿蓝色上衣的人”“背景里的红衣人”,迫使模型去理解语义组合而不是简单查颜色。这项数据增强之后,蓝色误检问题降低了差不多一半。

5.2 长视频上目标消失再出现,怎么接上身份

短片段训练时模型几乎不会遇到目标消失超过几秒的情况,所以推理时只要目标一离场,再回来模型就视为新目标。解决办法是训练阶段就做长时序采样和记忆机制。

一种做法是把视频分成多个片段,每个片段独立预测,但查询向量会跨片段传递。上一个片段最后预测出的 query 特征,作为下一个片段初始查询的额外输入。这样即使目标中间没出现,query 依然携带“目标长什么样、穿什么颜色”的记忆。

另一种做法是在训练中主动构造遮挡。对连续帧的 mask 做随机抠除,让模型在部分帧里看不到目标,但要求它仍然输出合理 mask。这个增强手段虽然看起来粗暴,实测下来对长时记忆提升很明显,模型不会因为目标消失就完全失去锚点。

5.3 显存不够时的突围方案

很多做 RVOS 的同学会被显存劝退。视频模型动辄 T×H×W×C,动不动就是 16GB 显存打底。我给出几档可操作的降配方案。

第一档是降低输入分辨率。很多人一上来就 720p,其实 256×448 分辨率已经能验证大部分结构问题。第二档是减少片段长度,从 8 帧降到 4 帧,显存压力几乎减半。第三档是梯度累积,通过多次小批次累积梯度来模拟大批次,虽然训练变慢但稳定。最后还可以用混合精度训练,把显存占用再压掉四分之一。

如果这些都不够,那就得靠“帧稀疏采样”和“关键帧重建”了。全局低频采样做目标定位,局部高频采样做精细分割,这种两阶段思路可以很大程度缓解显存和时间的双重压力。工程上甚至可以先把所有帧“浓缩”出少量代表性帧,再在这些关键帧上细抠 mask。

5.4 文本描述极端多样化,模型扛不住

用户给的文本不会像训练集那么规整。有长有短、有口语化表达、甚至有错别字。而模型一旦遇到训练时没见过的句式就会明显退化。提高泛化能力有几个我能确认有效的办法。

一是文本增强,不只是换词,还可以改句式,比如“那个穿白衬衫站在门口的人”改成“站在门口,穿着白衬衫的那位”。二是用更强的预训练文本模型,让文本编码器本身就带有丰富的语言先验,即使没见过某些句子也能泛化。三是在融合时不要过度依赖句法,而是尽量让词元特征与视觉对齐。

我个人强烈建议你在最终应用层加一层“语言改写模型”。把用户短句标准化成模型容易理解的模板格式,比如自动补全主体和属性。这一层虽然多了一个模型,但对整体效果稳定性帮助极大,而且工程上也不难实现。

6. 常见问题速查与调试实录

6.1 问题速查表

现象常见原因处理思路
第一帧分割不准确语言对齐不足,文本特征与视觉没融合好增加交叉注意力层数,或换更强的文本编码器
目标跟踪中断,身份跳变帧间依赖建模不足,query 没有跨帧传递引入记忆队列或跨片段 query 传递
分割边缘粗糙低层细节特征未参与计算加 FPN 多尺度融合,多级损失监督
背景误激活大量色块把颜色词当全局线索,忽略对象性学习前景图先验,增加紧凑性损失
小目标几乎分不出来下采样倍数太高提高输入分辨率,加入小目标重采样增强
长视频推理速度慢每帧重复计算视觉特征关键帧稀疏化,两阶段定位+细化
训练损失下降但指标不动文本与 mask 未对齐,指标对分割边缘敏感验证数据划分是否合理,检查 mask 对齐代码
重复预测多个目标query 数量过多,无匹配机制加 query 匹配成本,强制空 mask

这张表不是我编出来的,基本都是训练中真撞过的问题。遇到指标诡异时,先按表排查前三四条,多数时候能救回来。

6.2 一个典型的定位失败调试实录

某开发者在测试时发现,模型对“右二戴黄帽的人”这句话总是分出左侧路人。我们的直觉以为语言理解有 bug,但查代码发现其实是文本 tokenizer 处理时把数字“二”丢失了。文本输入变成“右 戴黄帽的人”,模型只能靠帽子找。修好 tokenizer 和文本归一化之后,效果立刻恢复正常。

这个例子想提醒你:RVOS 是一个多模态系统,任何一个环节出了小问题都会表现为整体性能下降。遇到问题不要只盯着模型结构,建议排查的顺序是:数据->文本预处理->输入采样->融合模块->损失函数->后处理。

6.3 后处理与推理细节:上线前的最后一道关

模型输出 mask 后不能直接当作最终结果。很多人忽略了一致性后处理,导致离线评测分数高、线上实际表现却很碎。我建议至少做三件事。

第一,时序中值滤波。对同一目标连续帧的 mask 做一个小窗口的中值取并,能有效去除闪烁。第二,形态学操作。先腐蚀再膨胀可以让 mask 边缘和标注风格对齐,特别是模型训练参考的数据集本身做了形态学平滑时。第三,按照面积和置信度过滤热点。那些面积忽大忽小、置信度忽高忽低的团块大概率是噪声,直接删掉即可。

后处理不能完全弥补模型缺陷,但它可以把 90 分推到 93 分,而且成本极低。做工程不是只看模型论文,上线前花一天时间调后处理,回报率非常高。

7. 一些扩展想法:RVOS 还能往哪里走

把基础流程打赢之后,再往上走有很多有意思的方向。比如把 RVOS 和视觉语言大模型结合,用更强的文本理解和常识推理能力去理解复杂描述,比如“之前进球后庆祝的那个人”这类时间指代。模型如果连“之前”都能理解,就可以真正做到“对话式视频分割”。

另一个方向是高效推理。视频模型天生慢,在线应用很难接受逐帧分割。通过关键帧检测、稀疏采样和轻量跟踪器组合,有可能在保持精度的同时达到实时级别。这种现实约束在工程里往往比刷一个学术分数更有价值,也更能体现系统的完整竞争力。

还有自监督与少样本方向。RVOS 依赖像素级时序标注,标注成本极高。如果能用大规模未标注视频,辅以自动生成的文本描述来做预训练,再在少量标注上微调,就能大幅降低应用门槛。这块还在快速演进中,但前景明确。

最后想分享一个我从这套框架里得到的最重要体会:单模态模型可以靠堆参数进步,多模态模型拼的是对齐质量,而视频多模态模型拼的则是在对齐基础上能否维持时间一致性。你把这三者的平衡点找好,RVOS 项目基本就成功了一大半。别追求一步到位,先跑通小数据上的完整链路,再逐步加复杂模块。这样每一步的退步或进步,你都能准确归因,而不是盲目调参。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询