
在路测数据里做检索最头疼的不是模型跑不动而是你根本不知道“该让模型学什么”。几十万段行车视频堆在硬盘里你想把“雨天路口发生危险变道”的场景捞出来靠人工翻会翻到怀疑人生靠普通的动作识别模型又容易把视觉上相似、驾驶语义完全不同的片段混在一起。最近读到的“TraVEL: Trajectory-Guided Video Embedding Learning for Driving-Video Retrieval”这篇工作正好是围绕“行车视频检索”这个话题展开的。本文不打算做复读机式的论文翻译而是结合标题含义、任务建模和 PyTorch 复现思路把它拆开来讲为什么行车视频检索不能照搬通用视频检索方案轨迹为什么能当引导信号以及如果你想在项目里验证这类思路代码框架大致要怎么写。需要先说明的是这篇论文的完整细节与实验数据请以原文为准我这里给的代码都是“理解用”的演示框架不是论文源码的逐行复刻。1. 行车视频检索到底在解决什么问题1.1 视频检索的朴素定义视频检索这个说法听起来很宽泛其实可以落到一个非常具体的需求上给一段查询视频或者一个查询条件希望系统从候选视频库里返回语义上最匹配的那一批视频片段。这个过程通常分成两步把所有候选视频编码成向量存进向量索引。对查询视频做同样的编码再在索引里做最近邻搜索。所以这项工作的核心挑战就变成如何把一段高维、时序、多变的行车视频压缩成一个“能表达语义”的紧凑向量。压缩得好检索就准压缩得不好库里其他东西越多检索结果就越离谱。对于普通视频我们只需要理解动作和物体比如“一个人在跑步”“一群人在跳舞”。但行车视频有它的特殊性画面里的语义不只是“有什么物体”还要加上“车在怎么动”“驾驶员处于什么状态”“接下来可能发生什么”。同样的路口画面直行通过和加速抢黄灯是完全不同的驾驶事件同样的跟车画面前车急刹和正常减速也需要区分。这种动态语义很难只用静态图像特征描述。1.2 行车驾驶场景带来的检索难点行车视频和通用视频相比有几个很明显的干扰因素场景外观变化大同一个工区或者同一条路白天、晚上、雨天、雾天拍出来的像素分布完全不同。相机运动与自车运动耦合你看到的每一帧画面都同时包含了“自车在动”这个隐藏条件所以视觉特征很容易把车辆自身运动当成前景内容。语义类别粒度细掉头、变道、压线、逆行、礼让行人这些类别之间的差异往往不在物体的“样子”上而在车辆轨迹和交互关系上。数据量大且难标注路测数据天然带有传感器信息比如 GPS、速度、转向角这些信息字段不经过人工标注也能拿到。如果直接把通用视频预训练模型拿来提取特征它很容易被外观层面的相似性欺骗。比如两段完全不同的驾驶行为只要都在同一条路上拍画面高度相似特征向量就会很接近反过来相同驾驶行为在不同光线环境下特征距离也可能被拉远。1.3 TraVEL 给出的核心动机这篇文章的标题里有两个关键短语Trajectory-Guided轨迹引导和 Driving-Video Retrieval行车视频检索。通俗地理解作者想表达的意思可能是既然行车视频里天然同步记录着车辆自身的运动轨迹那为什么不把轨迹当成一个可靠的跨模态引导信号用它来帮助视频编码器学到更稳定的驾驶语义这和很多跨模态对比学习的工作在逻辑上有近似之处。不同点在于这里用来“引导”的模态不是文本也不是语音而是行车记录里原本就有、不需要额外标注的轨迹序列。这种自我监督的信号对海量行车数据来说几乎零成本。如果沿着这个思路往下走模型要学到的核心能力就是把视频片段和对应的轨迹放进同一个嵌入空间让正样本对互相靠近负样本对互相远离。这样一来视频嵌入表达的不再只是“画面里有什么”而是“车辆在一个什么样的动态过程中”。2. 核心概念拆解轨迹、嵌入与检索闭环2.1 轨迹信号应该怎么理解在自动驾驶或者辅助驾驶领域轨迹Trajectory通常指的是自车在一段时间内的位置变化序列。从数据文件角度看它可能是这样一组字段时间戳 t0、t1、t2……经度、纬度坐标或者在局部地图坐标系下的 x、y 坐标速度值包括纵向速度和横向速度前轮转角或方向盘转角横向加速度、纵向加速度等一条轨迹序列就是车辆在某个时间窗口里“如何运动”的数值化表达。它比画面更抽象但恰恰因为抽象所以不容易受光照、遮挡等外在因素干扰。一个很简单的例子车辆在大雾天左转画面可能模糊不清但 GPS 位置序列反映出来的转向轨迹没变速度曲线也不会骗人。工程平台上这些数据往往来自惯性测量单元、轮速计、高精定位系统或者组合导航终端。也就是说真实路采数据里轨迹不是需要人工标注的标签而是随手可取的传感器数据。2.2 为什么轨迹可以当引导信号在训练阶段视频片段和轨迹数据是天然对齐的。一条轨迹对应的是同一时间窗内摄像头拍下的画面。这两种模态虽然信息形态完全不同但它们描述的是同一个物理事实。我们可以把“车辆动线一致”当作一种自监督信号如果某段视频和某段轨迹发生在同一个时间窗就让它们的嵌入表达靠近如果它们来自不同的驾驶片段就让它们的嵌入表达远离。这样做能带来一个额外好处模型不再只依赖视觉表观去区分不同驾驶事件。假设两段视频都是“前方有障碍物”一段是车道内绕行一段是停车等待。只看画面不好区分但绕行伴随明显的横向位移停车等待则是速度迅速降为 0。轨迹特征能够把这两个驾驶行为清晰地切开而轨迹引导的视频编码器就有机会学到这种隐藏差异。2.3 视频嵌入与检索闭环这里说的“嵌入”不用想得很玄它就是模型最终输出的一个向量通常是一个固定维度的浮点数数组比如 256 维或者 512 维。视频检索系统的闭环由四个环节组成编码把视频和查询条件编码成向量。建库把大量候选视频向量写入向量数据库比如 Milvus、Faiss。检索用查询向量的最近邻结果返回候选片段。重排可选地再用更精细的特征对结果做二次排序。TraVEL 从标题上看重点改造的是第一步如何训练出一个更鲁棒的“行车视频编码器”。如果嵌入本身质量不行后面建库和检索做得再好也白搭。3. 数据形态与任务建模思路3.1 正对、负对怎么构造首先定义一个基本样例一个样例 一个时间窗内的视频片段 同一时间窗内的轨迹序列。这里为了方便理解我们可以给一个更直观的元数据组织方式字段类型说明video_pathstr视频片段路径frame_start_msint片段起始时间frame_end_msint片段结束时间traj_pathstr轨迹文件路径clip_idstr片段唯一标识scene_typestr可选的场景标签非必须在构造训练数据时一条 clip_id 对应的视频和轨迹天然构成一对正样本。随机抽取其他 clip_id 的视频或者轨迹作为负样本也就是不匹配的样本对。如果项目里已经有额外的驾驶行为标签还可以把负样本构建成“难负样本”优先选择同场景但不同驾驶行为的片段这样能让模型学到更细粒度差异。3.2 模型整体的数据处理流程图在我们给出的概念框架里训练数据处理可以拆成这么几条线视频线视频片段 - 抽帧 - 图像特征序列 - 视频特征向量。轨迹线轨迹坐标和车速序列 - 归一化 - 轨迹向量。对齐线把两个向量映射到公共嵌入空间计算相似度或距离。从最终任务来看系统的输入输出关系是输入在公共嵌入空间中查询视频通过同一条视频编码流程得到向量而后通过与全库向量相似度排序返回 top-k 结果。对于一个数据样本来说最核心的处理逻辑是视频特征 vh264 视频 - 均匀采样 N 帧 - 图像编码器 - 池化 - v 轨迹特征 t坐标/车速 - 轨迹编码器 - 池化 - t 约束目标sim(v, t_positive) 尽量大sim(v, t_negative) 尽量小这里 N 的取值会影响时序信息保留程度。如果 N 太小比如只取 4 帧很多短暂交互会消失如果 N 太大训练显存又吃不消。部分论文常用 8 到 32 帧这个区间做平衡具体数值你需要根据显存和数据集情况调整。3.3 检索任务使用的评价口径行车视频检索的评价指标与通用视频检索是一致的通常看这几个指标含义使用场景RecallK正确结果出现在前 K 个结果的概率最常用PrecisionK前 K 个结果里正确结果占比结果精确性mAP对所有查询的平均精度均值整体排序质量NDCGK考虑了排序位置的指标带相关性等级时使用论文如果重点强调“视频到视频检索”那基本上用 Recall5、Recall10 就能判断模型好不好。4. 核心模块设计思路与损失函数4.1 特征提取模块怎么分工在概念框架中视频特征提取器和轨迹特征提取器可以是两个独立网络也可以是一个双塔结构。视频塔负责把视觉信息压缩成高级语义向量轨迹塔负责把运动学信息压缩成轨迹向量。既然两种输入的维度差异很大一般不会直接共享参数。在视频塔内部常见做法是先抽帧再用一个 2D 或 3D CNN 主干网络提取每帧特征。如果你希望保留时序建模能力可以在 CNN 后面接一层时序 Transformer 或者 BiGRU。轨迹塔则相对轻量因为它输入的本质是时间序列所以可以用多层 MLP 或者小型自注意力网络。在实现层面有一个细节值得强调因为视频和轨迹的数据来自同一时间窗所以你最好在预处理阶段就把它们裁剪成同样的时间范围。否则视频内容是 10 秒轨迹却只有 4 秒正样本对齐就是错位的。4.2 轨迹如何引导视觉表征这是整个方法里最有意思的地方。普通双塔模型只是简单地把两个模态的特征拉到同一个空间但“引导”这个词暗示了更强的交互关系。可以这样理解引导视觉特征在编码过程中需要在轨迹信息的辅助下进行特征选择。比如模型学到某个视觉区域和车辆转弯轨迹高度相关当轨迹信息提示“前方有弯道”时视觉编码器就会更关注车道线、对向车辆这些和过弯有关的区域当轨迹信息提示“车速持续为零”时视觉编码器可以更关注车前障碍物和行人区域。在实现这类交互时最简单的方式是特征级门控或注意力融合。视觉特征作为主干轨迹特征在后期改变视觉特征的通道权重。用 PyTorch 伪代码来表达这个交互关系可以采用下面的方式import torch import torch.nn as nn import torch.nn.functional as F class TrajectoryGuidedFusion(nn.Module): 概念实现用轨迹特征对视频特征做通道注意力引导 视频特征 shape: (B, T, C) 或 (B, C, T) 轨迹特征 shape: (B, D) def __init__(self, video_dim: int, traj_dim: int): super().__init__() hidden_dim 512 self.gate nn.Sequential( nn.Linear(traj_dim, hidden_dim), nn.ReLU(inplaceTrue), nn.Linear(hidden_dim, video_dim), ) def forward(self, video_feat: torch.Tensor, traj_feat: torch.Tensor) - torch.Tensor: # 对时序维度做平均池化得到视频全局特征用于生成通道权重 if video_feat.dim() 3: video_pooled video_feat.mean(dim1) # (B, C) gate_weight torch.sigmoid(self.gate(traj_feat)) # (B, C) video_out video_feat * gate_weight.unsqueeze(1) # 通道加权 else: video_pooled video_feat.mean(dim-1) gate_weight torch.sigmoid(self.gate(traj_feat)) video_out video_feat * gate_weight.unsqueeze(-1) return video_out这个实现非常浅显但它足够说明问题轨迹向量并没有直接替代视觉特征而是告诉视觉编码器“哪些通道上的语义更值得记住”。更高级的实现自然可以换成 Transformer 的 cross-attention原理一脉相承。4.3 对比损失怎么构建为了把视频嵌入和轨迹嵌入拉进同一个空间通常采用 InfoNCE 类的对比损失。这里有一个常见的设计点是做视频到轨迹的单向对比还是视频与轨迹的双向对比。普遍做法是双向对比让两个方向的检索都能成立。对比损失的核心思想是归一化后的余弦相似度配合温度系数import torch import torch.nn as nn import torch.nn.functional as F def contrastive_loss(video_emb: torch.Tensor, traj_emb: torch.Tensor, temperature: float 0.07) - torch.Tensor: video_emb: (B, D) 已经归一化 traj_emb: (B, D) 已经归一化 B 为 batch size # 批次内的余弦相似度矩阵 logits video_emb traj_emb.T / temperature # (B, B) labels torch.arange(logits.size(0), devicelogits.device) # 双向 InfoNCE loss_v2t F.cross_entropy(logits, labels) # 视频去找轨迹 loss_t2v F.cross_entropy(logits.T, labels) # 轨迹去找视频 loss (loss_v2t loss_t2v) / 2.0 return loss从这个代码能看到一个需要特别注意的问题批次内随机采样的负样本如果太容易区分模型学到的判别力可能不足。因为路采数据里大量片段都在相似的直路上画面区别不大随机负样本很容易被模型忽略。为了提升鲁棒性工程上经常要加一个“难负样本挖掘”模块比如把同一条路不同批次的片段拉来当负样本或者用当前模型分数最高的错误样本重新进入训练。4.4 可选的轨迹预测任务除了对比损失行车视频检索或预训练框架里还可能加入一个辅助任务从视频特征中预测轨迹的相关属性比如速度档位、转向角、道路曲率。这个任务相当于一个正则化手段防止模型只抓住画面表观特征。这种多任务思路在工程实现上也很直接。在视频塔的顶层接一个小型预测头输出速度和转向角。用 MSE 损失去监督它。之所以有效是因为速度和转向角本身就是轨迹的低维表达强迫视频特征去解码这两类信息等于变相让模型提取了自动驾驶决策所关心的关键线索。5. 给一个最小可跑的复现框架为了不让你觉得前面的概念都在空中飘下面给出一套简化版的行车视频与轨迹双塔检索项目骨架。代码是面向理解的不是论文官方源码你需要根据自己的数据集结构调整路径和参数。5.1 数据集结构假设先约定一个简单的文件组织方式drive_data/ ├── videos/ │ ├── clip_000001.mp4 │ ├── clip_000002.mp4 ├── trajectories/ │ ├── clip_000001.csv │ ├── clip_000002.csv └── train_list.csvtrain_list.csv 只需要三列就能跑起来video_path,traj_path,clip_id drive_data/videos/clip_000001.mp4,drive_data/trajectories/clip_000001.csv,clip_000001 drive_data/videos/clip_000002.mp4,drive_data/trajectories/clip_000002.csv,clip_0000025.2 自定义 Dataset首先要处理的任务是从视频文件里均匀采样帧同时把轨迹文件读取成轨迹向量。很多新手会犯一个错误在__getitem__里每次都调用cv2.VideoCapture打开同一个视频。训练阶段有很多 epoch这样做会让数据加载慢到无法容忍。正确做法是先把视频解压成帧索引或者缓存成内存张量至少也要用decord这类高效的视频解码库。下面给出一个相对完整的 Dataset 示例import os import cv2 import numpy as np import pandas as pd import torch from torch.utils.data import Dataset from decord import VideoReader, cpu class DriveClipDataset(Dataset): 简化版用于轨迹引导视频检索的数据集封装。 每条样本包含视频片段路径和轨迹 CSV 路径。 def __init__(self, meta_csv: str, num_frames: int 8, traj_len: int 64): self.meta pd.read_csv(meta_csv) self.num_frames num_frames self.traj_len traj_len def __len__(self): return len(self.meta) def _read_video_frames(self, video_path: str) - torch.Tensor: vr VideoReader(video_path, ctxcpu(0)) total len(vr) frame_ids np.linspace(0, total - 1, self.num_frames).astype(int) frames vr.get_batch(frame_ids).asnumpy() # (N,H,W,C) frames torch.from_numpy(frames).permute(0, 3, 1, 2).float() / 255.0 return frames # (N,3,H,W) def _read_trajectory(self, traj_path: str) - torch.Tensor: df pd.read_csv(traj_path) # 假设轨迹 CSV 包含 x、y、v 三列 traj df[[x, y, v]].values traj traj[:self.traj_len] pad_len self.traj_len - len(traj) if pad_len 0: traj np.pad(traj, ((0, pad_len), (0, 0)), modeedge) return torch.from_numpy(traj).float() # (T,3) def __getitem__(self, idx: int): row self.meta.iloc[idx] video self._read_video_frames(row[video_path]) # (N,3,H,W) traj self._read_trajectory(row[traj_path]) # (T,3) return { video: video, traj: traj, clip_id: row[clip_id], }在这个代码里我们默认轨迹 CSV 有x、y、v三列。如果你手里的原始数据是经纬度那需要先把经纬度投影到局部平面坐标再做归一化不能直接把经纬度整数喂给网络数值尺度差异太大会让训练非常不稳定。5.3 构建双塔编码器接下来是双塔模型。视频塔使用一个轻量 3D CNN 或者 2D CNN 池化轨迹塔使用一维卷积或 GRU然后在融合模块里做轨迹引导。一种实现路线是import torch import torch.nn as nn import torchvision.models as models class VideoEncoder(nn.Module): def __init__(self, embed_dim: int 256): super().__init__() backbone models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) self.backbone nn.Sequential(*list(backbone.children())[:-2]) self.pool nn.AdaptiveAvgPool2d((1, 1)) self.fc nn.Sequential( nn.Linear(512, embed_dim), nn.ReLU(inplaceTrue), nn.Linear(embed_dim, embed_dim), ) def forward(self, video): # video: (B,N,3,H,W) B, N, C, H, W video.shape video video.view(B * N, C, H, W) feat self.backbone(video) # (B*N,512,7,7) feat self.pool(feat).view(B, N, -1) # (B,N,512) feat feat.mean(dim1) # (B,512) return self.fc(feat) class TrajEncoder(nn.Module): def __init__(self, input_dim: int 3, embed_dim: int 256): super().__init__() self.mlp nn.Sequential( nn.Conv1d(input_dim, 64, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv1d(64, 128, kernel_size3, padding1), nn.ReLU(inplaceTrue), ) self.fc nn.Sequential( nn.Linear(128, embed_dim), nn.ReLU(inplaceTrue), nn.Linear(embed_dim, embed_dim), ) def forward(self, traj): # traj: (B,T,3) x traj.permute(0, 2, 1) # (B,3,T) x self.mlp(x) # (B,128,T) x x.mean(dim-1) # (B,128) return self.fc(x) class DualTowerModel(nn.Module): def __init__(self, embed_dim: int 256): super().__init__() self.video_encoder VideoEncoder(embed_dim) self.traj_encoder TrajEncoder(input_dim3, embed_dimembed_dim) self.l2norm nn.functional.normalize def encode_video(self, video): v self.video_encoder(video) return self.l2norm(v, dim-1) def encode_traj(self, traj): t self.traj_encoder(traj) return self.l2norm(t, dim-1)如果你的显存只够跑很小的 batch建议把 ResNet18 换成更浅的骨干网络比如 ResNet10 或者 MobileNetV2。对于检索问题来说特征表达够用就行模型太大反而容易过拟合到某一条线路的场景上。5.4 训练主循环训练主循环比较常规用对比损失做反向传播。下面给出一个最小训练循环示例import torch import torch.optim as optim from torch.utils.data import DataLoader device torch.device(cuda if torch.cuda.is_available() else cpu) model DualTowerModel(embed_dim128).to(device) optimizer optim.AdamW(model.parameters(), lr1e-4) dataset DriveClipDataset(train_list.csv, num_frames8, traj_len64) loader DataLoader(dataset, batch_size32, shuffleTrue, num_workers4, drop_lastTrue) epochs 10 for epoch in range(epochs): total_loss 0.0 for batch in loader: video batch[video].to(device) traj batch[traj].to(device) v_emb model.encode_video(video) t_emb model.encode_traj(traj) loss contrastive_loss(v_emb, t_emb, temperature0.07) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() print(fepoch {epoch 1}, avg loss: {total_loss / max(len(loader), 1):.4f})实现细节层面有两个坑需要特别提醒batch_size 如果很大对比矩阵(B,B)会占用显存必要时要考虑用一个内存库memory bank保存历史特征。温度系数是个敏感参数temperature 太小训练会不稳定temperature 太大梯度又会变得很平缓。0.07 只是初始值实际数据分布变化后需要调整。5.5 用 Faiss 做索引和检索训练完成后把候选库里的视频全部用model.encode_video()提取特征然后写入 Faiss 索引。使用 Faiss 的 IndexFlatIP 是最简单的余弦相似度检索方式不过如果你的库超过百万级建议换成 IVF 或 HNSW 索引结构。import faiss import numpy as np def build_index(vectors: np.ndarray) - faiss.IndexFlatIP: vectors: (N, D) 且为 L2 归一化后的 float32 返回索引对象 dim vectors.shape[1] index faiss.IndexFlatIP(dim) # 内积等价于余弦相似度需要预先归一化 index.add(vectors) return index def retrieve(index: faiss.IndexFlatIP, query_vec: np.ndarray, topk: int 10): query_vec query_vec.reshape(1, -1).astype(np.float32) faiss.normalize_L2(query_vec) scores, ids index.search(query_vec, topk) return scores, ids这段代码的作用是把模型输出的特征向量变成真正可用的检索系统。如果没有安装 faiss可以用pip install faiss-cpu安装 CPU 版本项目原型阶段完全够用。6. 怎么检验这个方案在你的数据上有效很多论文复现或项目验证容易踩到一个误区跑通流程就算完成不对比基线也不做消融实验。对于一个检索类项目我建议至少做下面三组对比。第一组可视化样例对比。从检索库里随机挑几个查询视频把 top-5 结果可视化出来用肉眼看召回结果是否符合“驾驶语义”。这一步虽然不量化但能最快暴露问题比如模型是不是只靠场景相似度召回是不是分不清左转和右转。第二组量化指标对比。固定数据集切分分别用三种方案提取视频特征然后比较 Recall1、Recall5、Recall10方案特征说明Recall1Recall5基线 A仅视频塔随机初始化或分类预训练不高需要你自己测需要你自己测基线 B仅视频塔使用对比学习但无轨迹引导需要你自己测需要你自己测方案 C视频 轨迹引导的联合训练需要你自己测需要你自己测只有方案 C 在这三项指标上全面优于方案 B才能证明“轨迹引导”真的带来了收益。第三组困难集测试。自己构造一个小的困难集比如 200 段雾天、强逆光、夜晚的视频专门测试模型的鲁棒性。如果模型在困难集上表现崩塌说明它仍然依赖显式表观线索轨迹引导的强度还不够。7. 常见问题与排查方向这里梳理几个我在类似项目里遇到的典型问题以及对应的排查顺序供你参考。问题现象可能原因解决思路训练 loss 下降很快但检索指标很差模型只会区分 batch 内随机负样本增加难负样本挖掘或引入额外的场景分类辅助任务视频与轨迹对齐错位视频裁剪窗口和轨迹起点不一致检查两个模态的起止时间是否统一轨迹插值到帧时间戳速度或坐标尺度差异过大loss 震荡没有做归一化计算 z-score 归一化速度除以最大值位置做局部坐标化模型把不同车道的同向行驶片段聚在一起轨迹特征权重太小提高轨迹分支在融合模块中的比例或增加预测轨迹的辅助损失检索结果全是同一条道路的画面训练集里场景偏置太严重按道路 id 或路线 id 划分数据集避免同路段的视频同时出现在训练和测试里同一视频重复出现导致指标虚高数据漏切或切分不严谨按视频源文件去重确保不存在片段重叠模型训练很慢视频解码耗时严重不要每次读原视频预处理阶段裁剪好短片或直接抽帧成图片序列如果你发现自己复现出来的结果远不如论文先不要急着调 loss。优先确认的事应该是数据切分和你用来对比的检索集是否合理。很多开源的视频理解任务都特别强调“同一视频片段不能同时出现在训练集和测试集”行车数据也是一样的道理。8. 从论文标题出发的工程落地建议如果把 TraVEL 当成一个研究方向来看它真正启发工程实践的地方在于强调“多模态自发对齐信号”的价值。行车视频场景里除了轨迹其实还有很多天然对齐的信号比如 CAN 总线数据、毫米波雷达点云、4D 毫米波雷达目标列表甚至导航地图的 lane-level 路径。这些信号都满足两个特点采集成本低附带物理语义强。如果我们能用类似的方式把这些信号作为引导模态设计预训练任务模型学习到的特征往往会比纯视觉监督更稳定。从落地角度我建议你在自己的项目里做三步走第一步先把轨迹视频检索系统跑通验证数据链路和指标评估流程。这个阶段哪怕只用一个最简单的随机负样本对比损失也比没有基线要好得多。第二步加入难负样本挖掘与场景分类辅助任务。这一步能显著提升模型的语义细分能力因为行车数据中真正的难点从来不是“区分车和行人”而是“区分两种极其相近的跟车行为”。第三步如果模型已经稳定收敛且检索指标基本可用再考虑更复杂的 Transformer 跨模态融合或轨迹预测辅助分支。这样做可以避免你一开始就把问题复杂化最后却搞不清是哪个模块在起作用。另外说一句关于“视频检索 vs 视频召回”的细节。在真实工程中视频检索往往不是终点它前面连接数据管理平台后面连接人工审核或者多模态大模型精排。TraVEL 这类方法负责的是粗召回阶段它关心的是“会不会把正确的片段排在候选池前几百名”而不是“能不能精确输出每一条目标框”。所以在做工程时不要太追求极端精确率先保证召回率足够高再让精排模型去完成后续筛除。9. 下一步怎么深入学习如果你对这篇论文中的方法感兴趣顺着这几个关键词去深入研究会比较高效传统视频检索与特征学习方法方面可以看 contrastive learning、Video Retrieval、Self-supervised learning 等内容可以快速建立基础体系。驾驶场景表征学习方面搜索 driving video understanding、ego-motion representation、trajectory forecasting 相关论文。轨迹引导的思想和自车运动预测方向有交集。轨迹编码方面LSTM、GRU、Transformer 在时间序列建模上的差异值得实践一轮。把轨迹从坐标序列编码成特征向量的方法直接决定引导质量。检索系统工程方面建议了解一下 Faiss、Milvus、CNNs 特征提取 pipeline 的完整流程以及 ANN 检索的基本原理。毕竟论文可以只训练嵌入模型但工程必须把检索索引和在线服务一起做好。如果你对照原文去读建议带着这样一个问题看实验部分作者用了哪些负样本构造策略、在哪个数据子集上提升最明显。这两个信息比读懂模型结构更有价值。因为从方法设计来说只要你知道“用轨迹做引导”这个大方向很容易想到若干种实现变体但只有实验能告诉你特定困难场景下哪种设计真正有效。学习这类论文时不要只看标题后就开始到处找源码先自己做一次“从任务到损失再到关键模块”的结构化拆解再对照正文修正理解偏差吸收效率会高很多。