
1. 先把问题拆开Video Grounding 到底在算一道什么题Video Grounding 这个词第一次出现在我的任务清单上时我下意识觉得它是个“带时间轴的搜索”——输入一段视频加一句话输出一个起止时间。真做完一轮才发现检索只是表层真正吃掉大部分工期的是边界模型能大致知道“东西在视频后半段”但它说不准是第 47 秒开始还是第 51 秒开始而这 4 秒的差距在业务侧往往就是“能不能用”的分水岭。先把这个任务的骨架说清楚后面所有选型都建立在这三要素上输入是一段长度不定的视频 V 和一条自然语言查询 Q输出是一个时间区间 [t_s, t_e]要求这个区间在语义上与 Q 描述的事件、动作、片段对应同时尽量贴合人工标注的边界。它解决的问题很朴素——视频是非结构化的、以秒为单位的时序信号而人的需求是“把提到那件事的那一小段找出来”。适合参考这篇内容的人大致分三类正在做视频理解相关功能、需要把“一句话”映射到“一段时间”的工程同学手上有一批带时间戳标注的视频数据、想验证能不能训出可用模型的研究同学以及产品侧想知道这个能力边界在哪、什么场景别硬上的同学。它和几个相邻任务特别容易被混为一谈我在需求评审上就见过把三者当一件事的情况。时序动作检测只关心“动作类别 起止”没有语言输入类别是闭集视频问答输出的是答案文本不要求定位视频摘要/高光检测输出的是“值得看的片段”没有查询条件约束。Video Grounding 的特殊性在于它是开放语义 精确边界的组合查询是自由文本所以你要处理语言的长尾表达输出是连续区间所以你要处理回归精度。这两件事叠在一起难度不是相加而是相乘。还有一个常被低估的点图像里的目标检测框是离散的、边界是“物体轮廓”人有共识而视频里的“这件事”边界是模糊的。“他打开冰箱拿了一瓶水”拿水的动作从手伸进去算还是从握住瓶子算标注员之间就能差出两秒。所以这个任务从数据那一端就带着噪声模型不是在学习一个确定函数而是在学习一个人群标注的分布中心。理解这一点后面看指标、调阈值、做验收的时候心态会完全不一样——你追的不是 100% 准确而是“和标注习惯对齐”。2. 方案选型两阶段、单阶段、集合预测我分别在什么情况下用2.1 两阶段方案候选生成加跨模态排序最稳但天花板明显两阶段的思路非常工程化先不管文本只从视频里生成一批候选时间段滑窗、基于动作性分数峰值、基于片段边界的聚类再用跨模态模型给每个候选打一个“和这句话匹配”的分数排序取 top-k。它的好处是每一段都能被解释候选是怎么来的、分数为什么高你都能拆开看。线上出问题时你可以单独检查候选生成有没有漏——如果漏了那是召回问题跟文本模型无关如果没漏但排错了那是排序问题。这种可归因性在项目早期极其宝贵。我一般用滑窗做冷启动的基线窗口长度取视频平均事件时长的 1.5 到 2 倍步长取窗口的一半。举个例子如果业务里的事件平均 40 秒那窗口就设 64 秒、步长 32 秒。一段 10 分钟600 秒的视频窗口数 (600 - 64) / 32 1 ≈ 17 个每个窗口再往后扩几个尺度0.5x、0.75x、1x、1.5x候选规模在 60 到 70 个完全可控。这套东西的检索指标通常能到一个“能交代”的水平但它的天花板也很清楚候选的粒度决定了精度的上限。如果真实片段是 7 秒而你的最小候选是 16 秒那你无论排序多准IoU 最高也就 0.4 出头永远过不了 0.5 的线。2.2 单阶段稠密预测把定位拆成“逐帧打分 边界回归”单阶段的思路是把每个时间步或者每个短片段当成一个二分类问题这一步是否落在目标区间内同时回归它到左右边界的距离。训练时对每一帧都算损失监督信号非常稠密小片段也能拿到足够的梯度。这类方案在推理时很轻一次前向拿到帧级分数曲线取峰值附近的连续段作为输出。它最香的地方是能吃到细粒度监督短片段召回明显好于纯滑窗方案。但它有个天然毛病帧级分类学的是“在不在里面”而边界附近的帧本身语义就模糊模型倾向于把分数曲线学得很平导致输出区间要么偏长把相邻内容吞进来要么被后处理的阈值切得偏短。所以这类方案必须配合一个像样的后处理先对分数曲线做平滑滑动平均或高斯核再以峰值为中心向两侧扩展到分数跌破峰值某个比例的位置最后用回归头预测的边界做一次修正。只靠分类分数取阈值我试过边界抖动非常厉害。2.3 集合预测式结构端到端很漂亮但训练期是真的磨人集合预测的思路是直接让模型输出固定数量的 (起, 止, 置信度)训练时用二分图匹配把预测和真值一一配对只对配对上的算回归损失和分类损失。它的好处是不依赖手工候选、天然去重、可以端到端。我第一次跑通的时候确实觉得优雅但后面两周基本都在跟训练稳定性较劲匹配结果在早期几个 epoch 里剧烈跳变同一个真值这轮配给 query 3、下轮配给 query 17模型一直在重新学学习率稍大就发散稍小就卡在一个“所有预测都挤在视频中间”的退化解上。后来我的处理方式是三条一是匹配成本里加一项时序位置先验让靠得近的预测更容易被匹配上减少早期抖动二是预热阶段先只训分类头让每个 query 先分化出各自负责的时间区域再打开回归损失三是给不同尺度的 query 初始化不同的参考区间不要全部从零开始学。这三条加上之后收敛曲线才变得正常。如果你团队里没有专门的人愿意花时间调训练稳定性我建议先别碰这条路线。2.4 三条路线怎么选一张对照表说清楚维度两阶段候选排序单阶段稠密预测集合预测式冷启动速度快一周内能出基线中需要调后处理慢训练稳定性成本高边界精度受候选粒度限制中上依赖回归头高短片段召回差小于最小候选就废好好可归因性强召回/排序可分离中弱推理成本高候选数 × 一次前向低一次前向低适合阶段需求验证、数据量少有稳定数据、追求性价比数据充足、追求精度上限我自己的习惯是先用两阶段跑出一个能看的数字用它来验证标注质量到底行不行。如果两阶段在验证集上连 0.5 IoU 的线都摸不到八成不是模型问题是标注一致性或者特征分辨率的问题这时候换再花哨的结构都是白搭。等确认数据没问题再决定往单阶段还是集合预测上走。3. 数据这一关标注规范、一致性校准与样本构造3.1 标注规范要写到“边界对齐什么动作”而不是“标出相关片段”我见过最省事也最坑的标注规范只有一句话“请标出与描述对应的视频片段”。结果就是标注员 A 把“他起身走向门口”从身体开始前倾算起标注员 B 从脚迈出第一步算起同一个视频两条标注差了三秒。模型学到的就是“平均偏保守”或者“平均偏激进”边界永远差一点点。后来我改的规范是这样的边界对齐到语义动作的起势与收势并给出三个具体判据——出现可辨识的动作启动信号身体位移、手部离开静止位置、声音事件起始为起点动作完成并进入静止或下一动作的过渡帧为终点如果描述里包含两个连续动作以第一个动作的起点到最后一个动作的终点为区间中间的停顿不切开。另外强制要求标注员给每条标注打一个“边界置信度”高/中/低低置信度的样本在训练时降权或者直接不进训练集。这一条改动听起来麻烦实际把标注返工率降了一大截。3.2 一致性校准先量一遍标注员之间的偏差再决定模型能到哪在正式训模型之前我会做一件很多人跳过的事抽 200 条样本让三个人独立标一遍然后两两算时序 IoU。这个数字非常能说明问题。如果平均 IoU 只有 0.6那说明人类自己的上限就在 0.6 附近你的模型能到 0.55 就已经很不错了别拿 0.8 当目标折磨自己。如果平均 IoU 有 0.85那说明任务定义清晰模型还有很大提升空间。这个校准还有一个副作用很有用能揪出“特别难标”的那批样本。通常是两类——描述里带指代“他拿起刚才那个东西”和描述里含否定“他没有站起来的那段”。前者的解法是把指代改写成具体名词后者我一般建议产品侧直接不接受因为否定的时序范围在逻辑上是不连续的模型很难学。3.3 训练样本怎么造正样本阈值、忽略区间与负样本配比如果是稠密预测方案每个时间步都要给标签。我的设法是与真值 IoU 大于 0.7 的候选片段记为正样本小于 0.3 的记为负样本中间 0.3 到 0.7 的设为“忽略”不参与损失。为什么留这个灰区因为 0.5 左右的片段语义上既不算对也不算错硬给它一个标签等于把标注噪声直接灌进梯度里。负样本也不是随便抓。纯随机采样会让模型见到的负样本大多是“明显无关”的比如完全不同的场景学到的判别力很弱。我的做法是难负样本采样优先取与真值时间上相邻但不重叠的片段IoU 在 0 到 0.3 之间以及同一视频里语义相似但对象不同的片段。这类负样本才真正逼着模型去看文本细节。正负比控制在 1:3 到 1:5 之间比较舒服比例再高容易把模型训成“保守派”分数普遍偏低。3.4 数据切分里的隐形地雷按视频切不要按片段切这条我踩过代价是两周的实验全部作废。当时的做法是把所有 (视频, 查询, 区间) 三元组打散后随机切 train/val结果验证集指标高得离谱上线后直接掉一半。原因很简单同一个视频的不同查询会同时出现在训练集和验证集里模型记住了这段视频的视觉特征等于开卷考试。正确做法是以视频为单位切分保证验证集里的视频在训练集里一帧都没出现过。如果业务场景是“同一批视频里新增查询”那还要额外准备一个“同视频不同查询”的测试子集单独看这个子集上的表现——它反映的是模型的文本泛化能力而不是视觉记忆能力。这两个数字经常差很多只看总分会被误导。4. 特征与编码视频侧怎么压文本侧怎么对齐4.1 抽帧率是第一个要算清楚的账设 1 fps 还是 2 fps不是拍脑袋定的要看你业务里最短的目标片段有多长。如果最短片段是 3 秒1 fps 只给你 3 个特征点边界回归的精度极限就在 ±0.5 秒如果要做到 ±0.2 秒抽帧率得提到 4 fps 以上。但抽帧率翻倍特征存储和前向计算也跟着翻倍。算一笔具体的账一段 600 秒的视频1 fps 得到 600 个时间步视觉特征取 512 维、float32 存储占用约 600 × 512 × 4 字节 ≈ 1.2 MB换成 2 fps 就是 2.4 MB。单看不多但如果有 50 万条视频就是 600 GB 和 1.2 TB 的差别而且这些特征要反复读、反复喂给模型IO 会变成瓶颈。我的经验是用 1 fps 做粗定位、用 4 fps 只在候选区间内做精修两级配合存储和精度都能兼顾。4.2 冻结视觉编码器还是微调先算清楚成本再谈效果微调视觉编码器通常能带来一两个点的提升但代价是特征不能预先离线算好每条样本都要过一遍视觉主干。假设主干在单卡上的吞吐是 60 帧/秒一条 600 秒的样本要 10 秒一个 batch 32 条就要 5 分钟以上一轮训练按千级样本算就是几十小时。这个成本在验证阶段基本不可接受。我的折中是主实验全程冻结视觉编码器用预提取特征训练时序头和跨模态对齐层等结构定下来、确实需要冲最后一两个点了再做一次小学习率的端到端微调并且只解冻主干的最后两三个 block。冻结方案还有个额外好处——特征可以预先算好存成内存映射文件训练时读盘速度能提升一个数量级迭代节奏完全不一样。4.3 多尺度时序特征别只用一个时间粒度单一时序粒度会同时吃亏在两头粒度太粗短片段被抹平粒度太细长片段的整体语义看不出来。我一般取三层原始帧级特征、2 倍下采样的片段级特征、8 倍下采样的段落级特征然后拼接或者用轻量的融合层加权。做融合的时候注意各层的维度要统一到同一空间比如都投到 256 维再拼否则高维那层会主导梯度。文本侧相对简单用预训练语言编码器取 token 级输出再做一个注意力池化就够了。但有个细节常被忽略文本长度分布要和训练集对齐。线上查询平均 12 个词训练集里全是被截断到 40 个词的样本模型对短查询的敏感度会下降。我的做法是在训练时按真实长度分布采样长查询降采样、短查询过采样比单纯截断有效。5. 模型结构与训练细节那些真正决定成败的参数5.1 边界回归头预测“距离”比预测“坐标”稳得多直接回归绝对时间戳第 47.3 秒在长视频上表现很差因为输出范围的量纲随视频长度剧烈变化。改成预测相对锚点的偏移量——当前时间步到左边界的秒数、到右边界的秒数并且对偏移做对数变换log(1 Δ)——训练稳定性和精度都会好一大截。对数变换的作用是把“差 0.2 秒”和“差 20 秒”的惩罚拉到同一个量级避免长片段样本主导损失。另一个必做的处理是给偏移量设上限并做截断。如果不设上限模型在遇到极端样本比如真值区间跨了整个视频时会产出巨大的梯度一次就把这一批的更新带偏。我一般把最大偏移设成 128 秒超出的截断同时在损失里对这些样本降权。5.2 损失配比分类、回归、对齐三条线要分开调我用的组合大致是三项帧级二分类用带 focal 加权的交叉熵正样本占比通常只有 5% 到 15%不加权会被负样本淹没边界回归用 L1 加广义 IoU 的组合L1 负责把位置拉近GIoU 负责整体重叠度两者比例大概 1:1跨模态对齐用对比损失让匹配的视频片段文本在特征空间靠近不匹配的推远。一维的 GIoU 计算很简单设预测区间为 [a, b]真值为 [c, d]交集 I max(0, min(b,d) − max(a,c))并集 U (b−a) (d−c) − IIoU I/U再取能同时包住两者的最小闭区间长度 C max(b,d) − min(a,c)GIoU IoU − (C − U)/C。范围在 [−1, 1]预测完全错位时是负值这个负值信号比单纯的 IoU 有用得多——它告诉模型“你不仅没重叠还错得很远”。调这三项权重的时候有个经验先固定对齐损失只调分类和回归等定位指标稳定了再打开对齐损失微调。三个一起从零开始调你很难判断指标不动是谁的问题。5.3 后处理分数平滑、峰值扩展与时序 NMS推理阶段拿到的是一条分数曲线。我的标准流程是四步。第一步做长度为 5 的滑动平均平滑抹掉单点毛刺第二步找所有局部极大值每个极大值向两侧扩展直到分数跌破峰值的 0.6 倍得到一个候选区间第三步用回归头预测的边界对这些区间做一次收缩或扩张修正修正幅度限制在原长度的 ±30% 以内防止跑飞第四步做时序 NMSIoU 阈值取 0.5 到 0.7。NMS 阈值的选择有讲究如果你要输出 top-1阈值可以设高一点0.7保留更多细节候选如果要输出 top-3 给用户选阈值要设低一点0.5否则三个结果高度重叠用户体验很差。另外滑窗方案在重叠区会产生重复预测我不用硬 NMS而是对落在窗口边缘的预测乘一个惩罚系数比如 0.8因为边缘预测的上下文不完整可靠性天然更低。这个细节对最终指标的影响比换模型结构还明显。6. 评测指标与业务对齐数字好看不等于能用6.1 Rn IoUm 到底在衡量什么最常用的指标是 R1 IoU0.5取模型置信度最高的那个预测如果它和真值的 IoU 大于等于 0.5算命中最后统计命中比例。还有 R5、IoU0.3、IoU0.7 这几档。看这几个数字的组合能判断出问题在哪IoU0.3 高但 IoU0.7 低说明粗定位没问题、边界不准该去调回归头和后处理IoU0.3 就低说明根本没找对地方问题在特征或跨模态对齐。R1 和 R5 差距大说明正确结果在候选里但排序错了那就该去调排序模型或者加对比学习。我自己看实验的第一眼永远是这两组对比比看 loss 曲线有用得多。6.2 阈值不是拍出来的是从业务成本倒推的模型输出的置信度是个连续值最终要不要返回给用户得定一个阈值。这个阈值不能看验证集上哪个点指标最高要看业务侧的错误成本。如果误报的代价是“用户多看一眼、滑走”那阈值可以放低宁可多给如果误报意味着下游流程要处理一段完全无关的视频比如自动剪辑、自动打点那阈值必须拉高宁缺毋滥。我一般的做法是画一条“阈值—精确率—召回率”的曲线然后跟业务方一起在曲线上挑点而不是自己关起门来定。这个过程还能顺带暴露一个常见误解很多人以为模型输出的是一个“正确的概率”其实它只是排序用的相对分数跨视频、跨查询都不一定可比不要拿它做绝对判断。7. 常见问题与排查清单这些坑我基本都趟过7.1 loss 一直在降指标纹丝不动这是最高频的问题。第一嫌疑是数据泄漏的反面——验证集和训练集分布不一致比如验证集里长视频占比高而模型对长视频的外推能力差。第二嫌疑是分类损失在主导因为负样本多模型很快学会“全都预测为负”或者“都在视频中间给个中庸的分数”loss 降得漂亮但分数曲线是平的。排查方法很直接把某一批的预测分数曲线画出来如果多条样本的曲线形状高度相似基本可以确诊。解法是提高 focal 的聚焦参数、加大正样本权重或者换成排序损失。第三个嫌疑容易被忽略标签本身的问题。我遇到过一次原因是标注文件里时间单位混用一部分是秒、一部分是毫秒导致部分真值区间超出视频总长。跑之前加一个断言0 ≤ t_s t_e ≤ 时长能省下好几天。7.2 边界总是差那么一两秒如果 IoU0.3 很高但 0.5 上不去先看真值区间的长度分布。短片段小于 4 秒占比高的话边界误差一两秒就是致命的。这时候的解法不是换模型而是提高时序分辨率把抽帧率从 1 fps 提到 2 或 4 fps并且在候选区间内做局部重采样。另一条路是给短片段样本单独加权让模型在短区间上多学几遍。还有一个隐蔽原因是标注本身偏保守。前面说的标注一致性校准里如果三个人标的区间都倾向于比语义动作短一点点那模型学出来的也会短。这时候可以试一个常数补偿比如两端各扩 0.3 秒在验证集上看是否整体提升如果有说明是系统性偏差值得补偿。7.3 问题速查表现象最可能的原因优先动作分数曲线全平负样本主导、正样本权重过低提高正样本权重、改用排序损失预测全在视频中部回归退化解、匹配抖动加时序位置先验、预热只训分类IoU0.3 高、0.7 低时序分辨率不足提高抽帧率、局部重采样验证集高、线上掉一半按片段切分导致泄漏改为按视频切分加同视频异查询子集短片段几乎召不回最小候选粒度太粗换稠密预测、给短片段加权输出结果高度重叠NMS 阈值过高降到 0.5加窗口边缘惩罚长视频整体偏后位置编码外推差加相对位置编码、滑窗分块处理7.4 一个反直觉的经验先修数据再修模型我统计过自己参与过的几个类似项目指标卡住的原因里数据问题的占比远高于模型结构问题。标注一致性差、切分泄漏、单位错误、时间戳越界、文本长度分布偏移——这些都不需要改一行模型代码但带来的提升往往是几个点级别的。所以我的排查顺序永远是先画 20 条样本的预测和真值对比图肉眼看看错在哪再统计错误类型分布最后才动模型。直接上去换结构等于在脏数据上做无用功。8. 工程落地长视频、切片与成本控制8.1 分钟级以上的视频必须走滑窗但边界要处理单次前向能处理的时间步长度是有限的受显存和注意力复杂度约束超过就得切。我的设法是窗口 128 秒、步长 64 秒50% 重叠。这样任何长度不超过 128 秒的目标至少完整落在某一个窗口里。重叠区会产生两份预测处理方式是保留分数高的那个同时对完全落在窗口切分点附近的预测做降权因为它们的上下文被截断了。切分带来的一个副作用是跨窗口的长目标会被切断。如果业务里有超过窗口长度的目标得额外加一个“跨窗口合并”逻辑相邻窗口边缘的预测如果时间上连续、文本相似度高就尝试合并成一个大区间。这个逻辑不难写但如果不做长目标的召回会直接掉到接近零。8.2 特征缓存与预计算把在线成本压到可接受线上服务的延迟大头往往不是模型推理而是视频解码和特征提取。我的做法是把特征提取做成离线异步任务新视频入库时触发一次抽帧和特征计算结果按视频 ID抽帧率编码器版本三元组做缓存。查询时只跑文本编码和跨模态对齐延迟能压到几十毫秒级。这里有个版本管理的坑换了视觉编码器之后旧缓存的特征和新模型的输入空间不匹配必须整批重算。所以缓存 key 里一定要带上编码器版本号否则会拿到一堆“能跑但效果莫名其妙变差”的结果排查起来非常痛苦。8.3 验收怎么做别只看平均值最后交付前的验收我坚持看三个切片维度按目标时长分桶小于 5 秒、5 到 30 秒、30 秒以上、按视频总长分桶短视频、长视频、按查询词数分桶短查询、长查询。平均值好看但某个桶特别差的情况太常见了而业务侧的真实流量分布往往正好压在差的那个桶上。我印象最深的一次是整体指标 0.62看着还行但按查询长度一切15 词以上的长查询只有 0.31。原因是训练集里长查询极少模型对复杂描述的解析能力基本没建立起来。后来补了一批长查询标注再对文本侧做了一轮针对性训练那一桶才补上来。如果当时只看总分就上线长查询那部分流量会全军覆没。我在实际做这类项目的体会是Video Grounding 的难点从来不在“模型能不能搭出来”而在“你能不能把一段模糊的人类描述和一段同样模糊的人类标注对齐到同一个坐标系里”。前三次迭代我几乎都在改标注规范、做一致性校准、修切分逻辑模型结构反而是最后才动的东西。如果你正准备开这个坑我的建议是先花一周时间把 200 条样本的标注一致性量出来把按视频切分的流程搭好把单位校验的断言写上——这三件事做完后面无论你选哪条技术路线都会顺很多。至于边界那最后一两秒的差距别急着换模型先去数一数你的数据里人和人之间本来差多少。