ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

遮挡视频行人重识别:从时序注意力聚合到PyQt5演示系统

遮挡视频行人重识别:从时序注意力聚合到PyQt5演示系统 简介这是一份面向深度学习毕业设计或课程设计的遮挡视频行人重识别系统源码包包含完整python源码与图形界面适合需要完成行人重识别项目或理解遮挡场景下识别流程的学生与开发者。系统覆盖视频导入、帧提取、行人检测、特征提取、遮挡检测与特征增强、行人匹配等端到端环节其中遮挡处理引入注意力机制突出未遮挡区域特征有助于降低遮挡对匹配结果的影响。资源共744个文件以715张行人图像和16个源码文件为主图像可用于模型训练与识别结果可视化源码涵盖界面、检测与重识别等模块另有若干配置、说明与字体等辅助文件压缩包仅10.94兆字节结构清晰。目前已有196人学习下载。通过该资源可快速复现基于深度学习的行人重识别流程了解图形界面如何串联视频输入、预处理与匹配展示同时也可作为毕业设计或课程设计的代码支撑与实验参考。1. 遮挡视频行人重识别毕设题目拆开看难点不在“识别”而在“遮挡”做行人重识别ReID毕设的同学十有八九先跑单帧图像模型而这道题偏偏给你“视频”和“遮挡”两个限定词。视频不是多给了几帧数据而是把问题从“认一张脸/一身衣服”升级成“从一段不完整、有遮挡的轨迹里认出同一个人”。这套源码里真正值钱的部分是把 ResNet 骨干、时序注意力聚合、三元组损失和 PyQt5 界面串成了一条可演示、可答辩、可复现的完整链路。适合两类人一类是拿它当毕设骨架换数据集、换骨干网络就能改出自己课题另一类是刚入行做安防视频检索的工程师需要一套能直接跑的基线系统。全文会按“模型怎么选 → 数据怎么喂 → GUI 怎么接 → 坑在哪”的顺序拆最后给到把遮挡场景单独做成实验的进阶方法。2. 视频 ReID 的主体架构为什么用“时序聚合”而不是“单帧硬扛”2.1 遮挡让单帧模型失效的三个瞬间以及视频带来的转机单帧 ReID 在遮挡下经常翻车典型场景有三种行人被路牌、树木、车辆局部挡住时全身特征被截断多个人交错行走时目标被他人身影盖住低帧率摄像头下关键帧恰好拍到遮挡最严重的瞬间。这三个瞬间的共同点是当前帧的外观信息不可靠但前后帧里大概率存在该目标的完整或半完整外观。视频ReID的核心逻辑不是“把每帧都认一遍再投票”而是让模型先学会判断“哪些帧、哪些区域是可信的”再根据可信度聚合特征。常见做法是双流设计空间流用 CNN 提取每帧的外观特征时间流用时序注意力或 RNN 建模帧间关系。但毕设规模的项目用纯 3D CNN 或 Transformer 不划算收敛慢、显存需求高答辩时解释成本也大。更稳妥的是“CNN 骨干 时序池化 遮挡感知注意力”的组合——结构简单、可解释性强、改造成本低而且和本标题里“遮挡”这个限定词贴合得最紧。2.2 骨干网络选择ResNet50 是默认答案但最后一个 stride 必须改常见的视频 ReID 骨干有 ResNet50、ResNet101、ViT-Base。ResNet50 是这套源码的首选原因不是它最聪明而是它开箱即用ImageNet 预训练权重随处可得PyTorch 官方 torchvision 直接加载训练速度在单卡 2080Ti 上也能接受。ResNet101 对小数据集容易过拟合ViT 则需要更长训练周期都不适合作为毕设默认配置。关键修改在 ResNet50 的最后一个 stage。torchvision 默认的 resnet50 在 conv5_x 层使用 stride2 下采样最后输出特征图是 7×7对行人这种细长目标来说空间分辨率太低。我一般会做两处改动把 layer4 的 stride 改成 1保住 14×14 的分辨率同时把该层的下采样捷径分支也改成 stride1保证残差连接能对齐。这两步直接决定特征能不能留住“被遮挡边缘”的空间细节。import torchvision.models as models def build_backbone(pretrainedTrue): backbone models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1 if pretrained else None) # 修改 layer4 的下采样策略保留更高空间分辨率 backbone.layer4[0].conv1.stride (1, 1) backbone.layer4[0].conv2.stride (1, 1) # 下采样捷径分支同步调整 backbone.layer4[0].downsample[0].stride (1, 1) return backbone这段代码的逻辑是layer4[0]是 conv5_x 中的 Bottleneck 第一层默认 stride2 会让特征图缩小一半。改成 stride1 后整张特征图从 7×7 变成 14×14行人被遮挡部分的边缘响应更容易保留。downsample[0]是残差捷径里的 1×1 卷积它的 stride 必须和主分支一致否则维度对不齐会直接报错或静默丢失信息。注意这个改动会让层4的感受野略微变小但对行人级别目标的影响可以忽略。2.3 时序聚合Temporal Attention Pooling 比平均池化多一个可解释维度拿到每帧特征后要把 T 帧聚合成一个视频级描述。最朴素的做法是 Temporal Average PoolingTAP把所有帧的特征取平均。但平均池化有一个致命问题被遮挡帧和清晰帧被同等对待遮挡帧里的错误特征会稀释正确特征。升级做法是 Temporal Attention Pooling用一个轻量网络为每帧学一个权重模型自动学会压低遮挡帧的贡献。这套源码用的就是注意力池化。具体实现是先对每帧的全局特征做一个小型全连接映射得到标量分数再用 softmax 归一化成帧权重最后按权重加权求和。这里有个细节值得注意softmax 的 temperature 参数可以调。默认 temperature1 时帧间权重比较均匀调低到 0.5 时权重分布更尖锐模型会更“激进”地抛弃遮挡帧但也可能误伤一些信息量中等但有用的帧。对遮挡视频数据集我建议从 1.0 起步等训练稳定后下调到 0.8 左右观察 Rank-1 指标的变化再决定。import torch import torch.nn as nn class TemporalAttentionPooling(nn.Module): def __init__(self, feat_dim, temperature1.0): super().__init__() self.temperature temperature self.attn nn.Sequential( nn.Linear(feat_dim, 256), nn.ReLU(inplaceTrue), nn.Linear(256, 1) ) def forward(self, frame_feats): # frame_feats: [B, T, C] scores self.attn(frame_feats).squeeze(-1) # [B, T] weights torch.softmax(scores / self.temperature, dim1) # [B, T] video_feat torch.sum(frame_feats * weights.unsqueeze(-1), dim1) return video_feat, weights代码说明feat_dim是骨干输出的特征维度ResNet50 改了最后一层后通常是 2048。temperature是前面说的锐化参数。forward里先把[B, T, C]的特征逐帧打分softmax 沿时间维归一化再用广播乘法加权求和。返回的weights是额外的“可解释输出”——把每帧权重打印出来你能直观看到模型是否把高权重分配给了清晰帧这正是做系统演示时的加分项。注意scores / self.temperature必须在 softmax 之前做放进 softmax 里效果完全不同。2.4 损失函数组合三元组损失管“区分度”交叉熵管“分类”视频 ReID 的标准损失组合是 Triplet Loss Cross Entropy。Triplet 负责拉近同一 ID 的视频特征、推开不同 ID 的特征CE 负责把特征映射到 ID 分类空间。这套源码里 Triplet 用的是 Batch Hard 版本——在一个 batch 内对每个锚点样本选最难的正样本和最难负样本组成三元组。Batch Hard 的优点是收敛快缺点是容易受难样本噪声影响尤其在遮挡场景里某个被严重遮挡的样本可能被误选为“最难正样本”导致梯度方向扭曲。一个实用的缓解办法是给困难样本设置阈值。源码里用margin0.3的 TripletMarginLoss配合mining函数返回的索引来做过滤。我个人习惯在训练初始阶段用随机采样Soft Margin跑完 10 个 epoch 再切到 Batch Hard。PyTorch 的实现可以直接用nn.TripletMarginLoss它对距离的计算封装得比较完整。要注意的是距离度量默认是欧氏距离而 ReID 评测一般用余弦相似度所以训练时也可以把distance_weighted改成余弦距离实现但这样收敛速度会慢一些建议先用欧氏。criterion_triplet nn.TripletMarginLoss(margin0.3, p2) criterion_ce nn.CrossEntropyLoss() for epoch in range(epochs): for batch in train_loader: video_feats, labels model(batch) # video_feats: [B, C] anchors, positives, negatives hard_mining(video_feats, labels) loss criterion_triplet(anchors, positives, negatives) \ 0.5 * criterion_ce(classifier(video_feats), labels)参数说明p2表示 L2 距离。0.5是 CE 损失的权重系数这是一个经验值——权重调大会让模型偏向分类特征的可区分性下降调太小则分类分支不起作用ID 特征不够稳定。我调试时先用 1.0 跑通再降到 0.5Rank-1 通常会有 1-2 个百分点的提升。hard_mining 函数接收特征和标签返回每个锚点对应的最难正负样本索引这部分在训练循环里可以单独抽出来做成一个工具函数方便调试。3. 数据与评测闭环从训练到 Rank-1 指标避开“刷错榜”的自我感动3.1 数据集怎么选Market1501 和 DukeMTMC 的差异以及自制遮挡集的价值视频 ReID 常用公开数据集是 MARS、DukeMTMC-VideoReID但遮挡视频场景没有专门的公开大基准常见做法是用 MARS 站岗同时切一段 Market1501 数据手动加遮挡块来做交叉验证。这套源码的训练入口已经封装成标准的 VideoDataset 形式换数据集只改一个配置文件的目录路径和采样帧数即可。MARS 和 DukeMTMC-VideoReID 的数据组织都是“每个行人的多段轨迹”但 DukeMTMC-VideoReID 的场景遮挡更密集——校园里树荫、建筑阴影、行人交错更多训练出的模型在遮挡下鲁棒性更好但场景单一、泛化到广场/地铁场景会掉点。如果你只有 Market1501单帧数据建议用自制遮挡策略对每张图随机叠加模拟遮挡块再把连续若干帧组合成伪视频序列来训练。这个策略是低成本替换视频数据的常见做法在毕设答辩里可以理直气壮地说“用数据增强模拟视频时序遮挡”。3.2 视频帧采样clip sampling 的三种模式与参数视频帧采样决定了模型每个 batch 能看到多少时间上下文。常见做法是固定采样 T4 或 T8 帧。T4 收敛快T8 能更好捕捉“遮挡前后帧互补”的特性但显存占用翻倍、训练时间延长。这套源码默认 T4理由很实际——单卡 11GB 显存下T4 可以开 batch size 32T8 只能开到 16而 ReID 的 Triplet Loss 对 batch size 非常敏感小 batch 会让难样本挖掘失效。采样模式有随机采样每个 epoch 换一组帧、均匀采样固定间隔抽帧、密集采样连续抽帧。对遮挡场景我强烈建议用随机采样——它天然构成一种时序数据增强让模型不至于记住某个轨迹的固定帧组合。class VideoDataset(torch.utils.data.Dataset): def __init__(self, tracklet_paths, labels, T4, samplingrandom, transformNone): self.tracklet_paths tracklet_paths self.labels labels self.T T self.sampling sampling self.transform transform def __getitem__(self, idx): frames load_frames(self.tracklet_paths[idx]) if self.sampling random: # 随机选出 T 帧保证乱序且可能重复防止模型依赖时序位置 indices sorted(random.sample(range(len(frames)), min(self.T, len(frames)))) elif self.sampling uniform: step len(frames) / self.T indices [min(int(i * step), len(frames) - 1) for i in range(self.T)] elif self.sampling dense: start random.randint(0, len(frames) - self.T) indices list(range(start, start self.T)) clip [frames[i] for i in indices] if self.transform: clip [self.transform(frame) for frame in clip] return torch.stack(clip), self.label_list[idx]参数说明random.sample返回的是不重复随机索引排序后时间顺序不保证连续这样可以打乱帧间顺序逼迫模型学习“内容”而不是“位置”。uniform适合轨迹较长的测试集dense适合检测模型预测出的连续片段。注意当轨迹帧数小于 T 时dense模式会 index out of range所以代码里要先用len(frames)做长度判断常见处理是对不足 T 帧的轨迹做重复采样补齐。这个边界坑在第 5 章还会提到。3.3 训练输出的三张表Rank-1、mAP、CMC 曲线到底给答辩看什么视频 ReID 的评估指标是 Rank-k 和 mAP。Rank-1 是“检索最匹配结果是否正确”mAP 是“正确匹配在所有匹配结果里的排名质量”。答辩时这两个数字都要报但要区分讲法Rank-1 说明系统“能不能找到人”mAP 说明系统“把这人排得多靠前”。CMC 曲线则展示整个排序分布。这里的坑在于很多人把 Market1501 的评测代码直接拿来跑视频数据集忽略了 query 和 gallery 的划分标准。视频 ReID 的 query 是一段轨迹gallery 是另一段轨迹同一个 ID 的两段轨迹不能有重叠帧。如果你没做轨迹层级切分而是把单帧当作独立样本评测Rank-1 会虚高 10% 以上。源码里必须确认query.txt和gallery.txt是轨迹级划分、且轨迹来源互相排斥。python evaluate.py --query_list data/mars/query.txt \ --gallery_list data/mars/gallery.txt \ --model_path checkpoints/occluded_reid.pth \ --T 8 --batch_size 64这段命令的作用是启动评测流程。evaluate.py内部会把 query 和 gallery 的视频分别过模型得到特征再去计算相似度矩阵。T 8和训练时的T 4可以不同——评测时为了结果稳定用更多帧聚合特征通常能提升 Rank-1。如果你发现评测结果比训练时的验证结果差很多先检查是不是评测时忘了开torch.no_grad()再检查 frame 采样是否和训练分布不一致。3.4 一个最少可行的遮挡实验设计为了让“遮挡”这个关键词在论文和答辩里真正立住建议用以下三步做一个简单但有力的自对比实验第一步用公开数据集原始样本训练一个 baseline第二步用加遮挡版的数据集训练同一个模型第三步用加遮挡的测试集评测两个模型。如果加了遮挡训练的模型在遮挡测试集上 Rank-1 掉点明显更少你就有了一组可以写进论文的对比数据。遮挡块的模拟可以用 OpenCV 画矩形块或高斯噪声块注意遮挡比例控制在 20%-40% 之间太大变成“目标丢失”太小和噪声没区别。import cv2 import numpy as np def add_occlusion(image, ratio0.3): h, w, _ image.shape block_h, block_w int(h * ratio), int(w * ratio) x1 np.random.randint(0, w - block_w) y1 np.random.randint(0, h - block_h) occluded image.copy() # 用随机灰度块模拟遮挡 occluded[y1:y1block_h, x1:x1block_w] np.random.randint(0, 255, (block_h, block_w, 3)) return occluded这段代码是自制遮挡辅助实验的核心ratio控制遮挡面积占比x1, y1随机决定遮挡位置。注意遮挡块的形状是矩形真实场景中的遮挡物形状不规则所以实验报告里要注明“模拟遮挡采用矩形块是对真实遮挡的近似”然后补充一句“后续可替换为语义分割掩码来精确模拟”。这个诚实又严谨的表述在答辩时比强行吹效果更可信。4. GUI 界面落地PyQt5 把“训练好的模型”变成“能演示的系统”4.1 为什么选 PyQt5三个理由和两个替代方案毕设系统的 GUI 通常在三套方案里选Tkinter、PyQt5、Streamlit。Tkinter 虽然轻量但做出来的界面“一看就是课设”PyQt5 的控件成熟、视频播放部件好用、打包成 exe 也方便Streamlit 只适合网页演示不能和本地摄像头采集衔接紧。这套源码的 GUI 层用的是 PyQt5配合 OpenCV 做视频帧读取。注意PyQt5 和 OpenCV 的坐标、颜色通道格式不同——OpenCV 读出来是 BGR 顺序PyQt5 的 QImage 默认是 RGB转之前必须先cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)否则画面上人像会变成“蓝脸红唇”配色。4.2 用 PyQt5 把视频推理包进界面关键信号槽设计GUI 的骨架是“左边播放视频、右边显示识别结果和检索列表”。视频播放线程需要和 UI 主线程通信直接用while True读帧会卡死界面所以要用 QThread 发出信号让主线程刷新画面。核心逻辑是子线程按固定间隔读取并推理一帧然后发信号给主线程更新画面主线程不参与耗时操作。from PyQt5.QtCore import QThread, pyqtSignal import cv2 import numpy as np class VideoInferenceThread(QThread): frame_ready pyqtSignal(np.ndarray, np.ndarray, list) def __init__(self, video_path, model, transform, device): super().__init__() self.video_path video_path self.model model self.transform transform self.device device self.is_running True def run(self): cap cv2.VideoCapture(self.video_path) while self.is_running: ret, frame cap.read() if not ret: break # 模型推理和可视化标记放在工作线程里做 with torch.no_grad(): feats self.model.inference_frame(frame) result_frame, pred_list draw_results(frame, feats) self.frame_ready.emit(frame, result_frame, pred_list) cap.release()这段代码的关键是frame_ready信号携带三个对象原始帧、标注后的结果帧、预测 ID 列表。主线程的槽函数接收后把结果帧放进界面的 QLabel把预测列表刷新到表格控件里。推理和画框都在子线程进行主线程只需要负责刷新这样才不会产生界面“未响应”。注意is_running标志位要在窗口关闭事件里置为 False不然关闭界面后后台线程还在跑视频进程关不掉——这是 PyQt5 GUI 最常见的“关不掉程序”原因。4.3 界面布局的三种交互设计上传视频、实时摄像头、检索相似片段一个演示级的 ReID 系统通常要包含三种交互入口才好在答辩现场应对老师的随机提问。第一种是上传本地视频做离线推理这是主力演示第二种是调用摄像头实时识别适合展示系统对真实场景的响应第三种是“选中视频中的某个人去另一个视频里检索他的片段”这是体现 ReID 系统核心价值的功能。# 主窗口里挂接三个入口 btn_upload QPushButton(上传视频, self) btn_upload.clicked.connect(self.upload_video) btn_camera QPushButton(打开摄像头, self) btn_camera.clicked.connect(self.open_camera) btn_search QPushButton(检索相似行人, self) btn_search.clicked.connect(self.search_similar) self.video_player QLabel() self.result_list QTableWidget() self.query_photo QLabel()代码本身很简单但背后的业务逻辑要注意摄像头推理用的是同一套模型但输入格式不同摄像头画面是单帧如果你的模型是专门对 T 帧序列训练的单帧输入时要做帧缓存——把上一帧的结果缓存、当前帧组合成 2 帧或者 4 帧的滑动窗口再喂模型否则效果会明显下降。检索相似行人的实现是把当前选中的目标框特征和库里所有轨迹特征做余弦比对取 Top-K 返回这部分在 GUI 里展示时注意要显示相似度百分比而不是只显示一个 ID 数字让老师一眼能看懂。4.4 GUI 模型加载避免每次启动都重新初始化两次PyTorch 模型加载有一个常见玄学问题模型在 GPU 上训练、在 CPU 上演示加载权重时会报 “Missing key(s) in state_dict”。这是因为训练时如果用了nn.DataParallel权重文件里的 key 会带module.前缀而单卡推理时模型没有这个前缀。常见解决办法是加载时做兼容处理def load_model_with_legacy(model, ckpt_path, device): state_dict torch.load(ckpt_path, map_locationdevice) new_state_dict {} for k, v in state_dict.items(): # 去掉 DataParallel 带来的 module. 前缀 if k.startswith(module.): k k.replace(module., , 1) new_state_dict[k] v model.load_state_dict(new_state_dict, strictTrue) return model这段代码的意义在于不管训练时用没用 DataParallel加载到推理环境时都能对齐 key。另一个常见问题是map_location没指定导致在无 GPU 的机器上加载时报 CUDA 不可用必须要写。用这段代码可以避免 80% 的“我明明训练好了但 GUI 跑不起来”的血泪事故。5. 遮挡视频 ReID 的五个经典踩坑记录从训练崩溃到 GUI 黑屏5.1 训练时 loss 变成 NaNBN 层和难样本挖掘的隐形冲突现象损失函数前几个 epoch 正常跑到第 8-10 个 epoch 突然变成 NaN之后一直回不来。 原因视频 ReID 批次里同一个 ID 通常有多段轨迹Batch Hard 挖掘时容易把来自同一 ID 的不同轨迹当成“负样本”导致标签错乱、梯度爆炸。此外batch_size较小而T较大时BN 层的统计量不稳定也可能触发 NaN。 解决先检查 hard mining 函数确保负样本的 ID 和锚点严格不同再把 TripletMarginLoss 的margin调小到 0.2 或者改用nn.SmoothL1Loss替换掉边界部分作为过渡最后检查学习率一般视频 ReID 初始学习率在 3e-4 左右超过 1e-3 非常容易 NaN。排查时可以打印每个 batch 的 loss 和梯度范数看到梯度突增就锁定了问题。5.2 训练指标正常、评测掉一大截轨迹切分错误导致的列表污染现象训练时验证精度很高Rank-1 到 85% 以上但跑公开评测脚本却只有 60%。 原因query 和 gallery 的轨迹切分含了重叠帧或者同 ID 的重复轨迹。评测集合里如果存在完全相同的帧模型相当于“开卷考试”分数虚高。这个坑在视频 ReID 里特别隐蔽因为单帧评测代码一跑就报失败很多人干脆用单帧 base 的评测脚本结果 lable 不匹配。 解决最直接的办法是在评估前统计一下 query 和 gallery 的轨迹 ID 是否有交集。如果有按轨迹 ID 重新分配保证一个 ID 的 query 段不出现在 gallery 里。这条血泪经验几乎每个做过视频 ReID 的人都踩过我也不例外。5.3 遮挡块增强反而让 Rank-1 下降遮挡比例和位置没对齐行人结构现象加了遮挡增强后模型在遮挡测试集上的指标不升反降。 原因矩形遮挡块随机放在任何位置如果遮挡落在背景上模型看到的只是“背景被涂了一块”没有学到“行人的某部分被遮住”这个语义如果遮挡比例超过 40%模型学到的反而是噪声。 解决把遮挡块限制在行人框的中心区域并且遮挡比例控制在 0.2-0.4。可以用检测框让遮挡块落在行人区域内而不是全图随机。具体做法是在数据加载时用 ground truth 的 bounding box 作为遮挡候选区。另外遮挡块的灰度和纹理不要用均匀色块用真实背景裁剪的 patch 去覆盖效果会更好。5.4 GPU 推理慢GUI 每秒只刷两三帧TensorRT 转换和帧间隔优化现象GUI 里视频播放卡顿肉眼看着不连续推理线程 CPU/GPU 占用高。 原因直接拿 PyTorch 模型逐帧推理没有利用 GPU 的 batch 能力。视频是连续的逐帧推理浪费了算力而且 PyTorch 的 eager 模式有大量 kernel 调度开销。 解决两个方向第一把帧变成 batch 推理——设置 buffer 为 4 帧攒够 4 帧一次性推理然后在 GUI 里分帧显示这样吞吐量能提升 3-4 倍第二用 TensorRT 把 CNN 模型转为 FP16 engine。如果不想引入 TensorRT 的编译烦恼至少要把torch.no_grad()包住整个推理循环并且固定 CUDA 上下文不要每帧都重新分配显存。GUI 展示时还可以把推理和显示分离推理线程只输出特征向量画框操作由主线程完成减少 OpenCV 图像拷贝的耗时。5.5 界面加载权重后第一帧预测结果总是错模型推理模式和训练模式不一致现象GUI 跑起来第一帧识别效果很差但是跑几帧之后就正常了或者一直很差。 原因模型加载后处于trainingTrue状态Dropout 和 BN 层仍在使用训练时的随机行为导致输出特征不稳定。很多人加载权重后忘记调用.eval()。 解决在推理循环开始前强制设置model.eval()并关闭梯度。如果加载的是训练状态保存的整个 checkpoint 而不是 state_dict还要确认优化器状态没有影响前向推理。另一个导致第一帧错误的原因是 BN 的 running_mean 未更新尤其在换数据集测试时建议在加载权重后跑 50 个 dummy 样本做一次前向让 BN 层统计量稳定下来再正式推理。6. 进阶把遮挡视频 ReID 从“能跑”做到“能写进论文的可解释实验”当你能稳定跑通上述全部流程ReID 这一步就算真正落地了。下一步值得花时间的是“遮挡可解释性”实验这是答辩时最能拉开层次的展示。具体做法是将时序注意力权重可视化在 GUI 里把刚才TemporalAttentionPooling返回的weights数值同步显示在界面表格里当视频播放到行人被遮挡的帧你可以直观地看到权重数值下跌——这就构成了一条“模型确实在响应遮挡”的证据链。再配合遮挡热力图用 Grad-CAM 对最后卷积层做响应图叠加把热力图覆盖在原始帧上老师一眼就能看出模型的关注区域是否绕开了遮挡物。另一条进阶路径是跨数据集验证。用你在 MARS 上训练好的模型直接去测 DukeMTMC-VideoReID 的测试集记录 Rank-1 掉点幅度。你会得到一个真实的技术结论“模型在场景迁移下会掉多少”这个数字比任何吹捧都有说服力。同时也可以在训练时引入跨数据集联合训练——把两个数据集的 ID 映射到统一空间用共享的 ID 分类头做联合优化这种方法在我的实践中能减少跨场景掉点约 8%。但注意联合训练时两个数据集的 ID 数量差距过大要做类别平衡采样否则大 ID 数量的数据集会主导分类头。最后一个技巧是学习率调度的细节。视频 ReID 在遮挡条件下更容易在小学习率中后期获得收益我习惯用 Warmup-Cosine 调度前 5 个 epoch 线性升到 3e-4之后 cosine 衰减到 1e-6。这样后期的微调过程对“难样本”特征空间的打磨非常有效。做实验时可以把最后的余弦阶段从 20 个 epoch 延长到 40 个 epochRank-1 通常还有 0.5-1 个百分点的提升。这套经验是我在多次连调失败后总结出来的固定习惯——先保证流程稳定再做精致的实验设置。希望帮到你。本文还有配套的精品资源点击获取
返回列表