1. 车辆重识别到底在解决什么问题车辆重识别Vehicle Re-Identification简称Vehicle ReID这个方向说白了就是让系统在换了一个完全不同的摄像头之后还能认出同一辆车。你想想城市里路口那么多每个摄像头拍到的角度、光照、遮挡情况都不一样一辆车从A路口开到B路口可能被拍到的就是完全不同的两面——A路口拍到车头B路口拍到车尾中间还隔着好几秒甚至几十秒的时间差。人眼去比对都费劲更别说让机器自动完成了。这个技术最直接的应用场景就是智能交通和安防领域的车辆追踪。比如某辆车在某个区域出现了异常行为需要快速定位它的行驶轨迹靠人工一个个摄像头回放根本不现实。再比如停车场找车、高速公路收费稽核、套牌车识别这些场景背后都需要Vehicle ReID技术做支撑。但Vehicle ReID有一个特别棘手的问题车辆之间的视觉差异太小了。同款同色的车满大街都是你光看外观根本分不出来。而且同一辆车在不同摄像头下的外观变化又特别大角度、光照、分辨率、遮挡每一个因素都在增加识别难度。这就导致了一个尴尬的局面——模型在训练集上表现很好一换到新场景就崩了。这篇论文笔记要聊的就是怎么通过多域学习和身份挖掘来解决这个问题。核心思路不复杂既然单一数据域训练出来的模型泛化能力差那就把多个不同数据域的信息融合起来一起学既然标注数据不够用那就想办法从无标注数据里挖出有用的身份信息来。下面我会把这篇论文的核心设计思路、关键技术细节、实操要点和踩坑经验都拆开来讲尽量让做这个方向的朋友能直接抄作业。2. 多域学习与身份挖掘的整体设计思路2.1 为什么单域训练在Vehicle ReID里行不通先说说为什么非要搞多域学习。Vehicle ReID的数据集通常是在特定场景下采集的比如某个城市的卡口摄像头、某个停车场的出入口摄像头。这些数据集的域特征非常明显——摄像头型号固定、光照条件类似、拍摄角度有限。模型在这种数据上训练很容易学到一些“捷径特征”。什么叫捷径特征举个例子模型可能发现训练集里所有红色轿车都是同一个ID于是它就把颜色作为主要判别依据。但到了实际场景中红色轿车成千上万这个特征就完全失效了。更麻烦的是不同数据域之间的分布差异可能非常大。A数据集是白天高速公路场景B数据集是夜间城市道路场景C数据集是地下停车场场景。你在A上训练的模型直接拿到B上用性能下降个百分之二三十都是正常的。这就是所谓的域偏移Domain Shift问题。多域学习的思路就是与其在一个域上死磕不如把多个域的数据放在一起训练让模型学会提取那些跨域不变的鲁棒特征。颜色会变、光照会变、角度会变但车辆的一些结构性特征——比如车灯形状、格栅纹理、车标位置——是相对稳定的。多域学习就是逼着模型去关注这些稳定特征而不是被那些容易变化的表面特征带偏。2.2 身份挖掘要解决的是什么痛点再来说身份挖掘。Vehicle ReID的标注成本非常高。你要给每辆车分配一个ID还得保证同一辆车在不同摄像头下的图片都被正确标注为同一个ID。这个工作量有多大一个中等规模的数据集几万张图片、几千个ID人工标注可能要花几个月。而且标注过程中还容易出错同一辆车因为角度差异太大被标成了两个ID或者两辆同款车被标成了同一个ID这些噪声标注对模型训练的伤害非常大。身份挖掘的核心思想是利用大量无标注或弱标注的车辆图片通过聚类、相似度计算等手段自动发现潜在的身份信息把这些信息作为额外的监督信号来增强模型。这样做的好处是显而易见的——标注成本降低了数据利用率提高了模型的泛化能力也有可能跟着提升。但身份挖掘也有自己的坑。聚类算法对特征质量非常敏感如果初始特征不好聚类结果就会一团糟用这种噪声标签去训练模型只会越训越差。所以身份挖掘通常需要和模型训练交替进行——先用现有模型提取特征做聚类用聚类结果训练模型再用更新后的模型重新提取特征做聚类如此迭代。这个过程中怎么控制噪声、怎么设计聚类策略、怎么平衡不同域的贡献都是需要仔细考虑的问题。2.3 多域学习和身份挖掘怎么协同工作把多域学习和身份挖掘放在一个框架里逻辑上是互补的。多域学习提供了更丰富的特征表示基础让模型在不同域上都能提取到有判别力的特征身份挖掘则利用这些特征去发现新的身份信息反过来增强模型的判别能力。具体来说整个框架可以分成两个阶段。第一阶段是多域预训练把多个数据域的标注数据混合在一起训练一个基础模型。这个阶段的目标是让模型学会提取跨域通用的特征表示。第二阶段是身份挖掘和微调用预训练模型对无标注数据进行特征提取和聚类把聚类得到的伪标签作为监督信号和原始标注数据一起训练。这个阶段可以迭代多轮每轮都用更新后的模型重新做聚类。这种设计的巧妙之处在于它没有把多域学习和身份挖掘割裂开来而是让它们形成了一个正向循环。多域学习让特征更鲁棒鲁棒的特征让聚类更准确准确的聚类又让模型学到更多身份信息模型变强之后特征又更鲁棒。当然这个循环要转起来每个环节都得处理好不然很容易变成负向循环。3. 核心细节解析与实操要点3.1 多域数据集的构建与采样策略多域学习的第一步是搞定数据。Vehicle ReID领域有几个常用的公开数据集每个数据集都有自己的域特征。比如有的数据集是城市道路卡口场景图片分辨率较高车辆角度以正面和背面为主有的数据集是停车场场景角度更丰富但光照条件复杂还有的数据集是高速公路场景车辆速度快运动模糊比较严重。构建多域训练集的时候不能简单地把所有数据混在一起就完事了。不同数据集的ID空间是不重叠的A数据集里的ID 001和B数据集里的ID 001完全是两辆车。如果直接混合训练模型会懵——同一个ID标签对应了不同的车这还怎么学所以通常的做法是给每个数据集的ID加上域前缀比如A_001、B_001这样模型就知道它们不是同一个身份。采样策略也很关键。如果某个数据集特别大另一个特别小直接混合会导致模型偏向大数据集。常见的做法是按域采样每个batch里保证每个域都有一定比例的样本。具体比例可以根据数据集大小和重要性来调整但一般来说不要让任何一个域的样本占比低于20%否则那个域的特征就学不充分。还有一个细节是图像尺寸的归一化。不同数据集的图片分辨率可能差很多有的256x256有的512x512。训练之前需要统一resize到相同尺寸但resize的方式也有讲究。直接拉伸会改变车辆的宽高比影响结构特征的提取。比较稳妥的做法是保持宽高比做padding或者用随机裁剪的方式增强鲁棒性。3.2 网络 backbone 的选择与改造Vehicle ReID的backbone选择目前主流是用ResNet系列或者Vision Transformer系列。ResNet50是经典选择计算量适中特征提取能力也够用。如果追求更高性能可以上ResNet101或者ResNeSt。Transformer类的方法最近两年在ReID领域也很火比如ViT、Swin Transformer它们的长距离依赖建模能力对车辆这种结构化目标很有帮助但计算量会大不少。选好backbone之后通常要做一些改造。最典型的是去掉最后的全连接分类层换成ReID专用的embedding层。embedding的维度一般设为2048或者1024太小了信息压缩太狠太大了容易过拟合。另外很多工作会在backbone后面加一个BNNeck结构就是在embedding层后面接一个BatchNorm层然后把BN之前的特征用于三元组损失BN之后的特征用于分类损失。这个设计能缓解分类损失和三元组损失之间的优化冲突实测下来对性能提升挺明显的。还有一个常用的改造是加注意力模块。车辆有一些关键区域比如车灯、车标、格栅、后视镜这些区域的判别力比车身颜色、背景这些强得多。可以加空间注意力或者通道注意力让模型自动关注这些关键区域。不过注意力模块也不是越多越好加多了容易过拟合而且推理速度会下降。一般加一两个轻量级的注意力模块就够了。3.3 损失函数的设计与权重平衡Vehicle ReID的损失函数通常是多损失联合训练最常见的是ID分类损失加三元组损失。ID分类损失用的是交叉熵把每个ID当成一个类别让模型学会区分不同的身份。三元组损失则是让同一ID的样本在特征空间里靠得更近不同ID的样本离得更远。三元组损失的采样策略非常关键。随机采样的话大部分三元组都是easy tripletloss接近0对模型没有提升。所以要搞hard mining专门挑那些难分的三元组。但hard mining也不能太激进如果只挑最难的训练容易不稳定甚至崩溃。比较稳妥的做法是batch hard sampling在每个batch里为每个anchor挑一个最远的正样本和最近的一个负样本这样既保证了难度又不会太极端。多域学习的时候损失函数还要考虑域之间的平衡。如果某个域的loss特别大梯度就会偏向那个域其他域学不好。可以给每个域的loss加一个权重权重可以根据域的大小或者难度来调整。还有一种做法是搞域对抗训练加一个域分类器让特征提取器学到的特征无法区分来自哪个域这样能增强跨域的泛化能力。不过域对抗训练调参比较麻烦搞不好会损害判别能力需要谨慎使用。3.4 身份挖掘中的聚类算法选型身份挖掘的核心是聚类。常用的聚类算法有DBSCAN、层次聚类、K-means等。DBSCAN的好处是不需要预先指定簇的数量而且能识别噪声点适合ReID这种簇数量不确定的场景。但DBSCAN对参数eps和min_samples很敏感eps稍微变一点聚类结果可能就差很多。层次聚类不需要指定簇数量但计算复杂度高数据量大的时候跑不动。K-means需要指定K值但速度快适合大规模数据。实际操作中比较常用的方案是先用DBSCAN做初始聚类把噪声点过滤掉然后再用层次聚类或者K-means做精细聚类。还有一种做法是用近似最近邻搜索构建一个kNN图然后在图上做聚类这样能利用样本之间的局部关系对噪声更鲁棒。聚类的阈值设定是个经验活。阈值太松不同ID的样本被聚到一起产生假阳性阈值太紧同一个ID的样本被拆开产生假阴性。这两种错误对训练的伤害都很大。通常的做法是先用一个较松的阈值做聚类然后通过人工检查或者统计方法过滤掉那些置信度低的簇。也可以搞多阈值聚类不同阈值下的聚类结果互相验证只保留那些在多个阈值下都稳定的簇。4. 实操过程与核心环节实现4.1 数据准备与预处理流程数据准备是整个流程的基础这一步没做好后面怎么调都白搭。假设我们手头有三个域的标注数据分别记为D1、D2、D3还有一个无标注数据域Du。第一步是把所有标注数据整理成统一的格式每张图片对应一个图片路径ID域标签的三元组。ID要加域前缀避免冲突域标签用于后续的采样和损失加权。图片预处理包括resize、归一化和数据增强。resize统一到256x128或者384x192前者是经典设置后者分辨率更高但计算量更大。归一化用ImageNet的均值和标准差就行这是常规操作。数据增强方面随机水平翻转、随机裁剪、随机擦除是标配。颜色抖动要慎用因为颜色在Vehicle ReID里有时候是判别特征抖动太厉害可能把有用信息也抖没了。可以加一点轻微的亮度对比度扰动但幅度不要太大。对于无标注数据Du预处理流程类似但不需要ID标签。这些数据主要用于后续的身份挖掘阶段。需要注意的是无标注数据的域分布最好和标注数据有一定重叠否则聚类出来的身份信息和标注数据的ID空间对不上挖掘出来的信息就用不上。4.2 多域预训练模型的搭建与训练预训练阶段的目标是得到一个跨域通用的特征提取器。网络结构用ResNet50加BNNeckembedding维度设为2048。损失函数用ID损失加三元组损失权重比设为1:1。优化器用Adam初始学习率3e-4weight decay设为5e-4。学习率调度用余弦退火训练120个epoch。训练时的batch采样要按域分层。假设batch size是64三个域各采样16张剩下16张从最大的域里补。每个域内部的采样要保证每个ID至少出现4张图片这样才能构成有效的三元组。如果某个域里有些ID的图片数量少于4张要么丢弃这些ID要么通过数据增强扩充。训练过程中要监控每个域的loss和准确率。如果某个域的loss一直降不下去可能是那个域的数据太难或者域权重设得不合理。可以适当调高那个域的损失权重或者对那个域的数据做更强的增强。反过来如果某个域的准确率远高于其他域说明模型偏向那个域了要调低它的权重。预训练完成后用验证集评估模型的跨域性能。重点看模型在未见过的域上的表现这才是多域学习效果的真正体现。如果跨域性能不理想可能需要调整域采样比例、损失权重或者网络结构。4.3 身份挖掘的迭代流程实现身份挖掘是一个迭代过程每一轮包括特征提取、聚类、伪标签生成和模型微调四个步骤。特征提取阶段用当前模型对无标注数据Du提取embedding特征。注意要用BNNeck之后的特征因为这部分特征经过了BN归一化分布更稳定适合做相似度计算。提取完特征后做L2归一化然后用余弦相似度构建相似度矩阵。聚类阶段先用DBSCAN做初始聚类。eps设为0.5到0.6之间min_samples设为4。这个参数不是固定的要根据数据规模和特征分布来调。聚完类之后统计每个簇的大小把那些特别小的簇比如只有2-3个样本的标记为噪声不参与后续训练。对于剩下的簇计算簇内平均相似度和簇间最大相似度如果簇间相似度太高说明这两个簇可能是同一个ID需要合并。伪标签生成阶段给每个簇分配一个伪ID格式可以是Du_001、Du_002这样。伪ID的置信度可以用簇内平均相似度来衡量置信度低的样本在训练时给更小的损失权重。模型微调阶段把标注数据和伪标注数据混合在一起训练。标注数据的损失权重设为1.0伪标注数据的权重设为0.5或者根据置信度动态调整。微调的学习率要设小一点比如1e-4避免把预训练学到的知识冲掉。微调epoch数不用太多20-30个epoch就够了。整个迭代过程通常跑3-5轮。每轮结束后重新评估模型性能如果性能不再提升就停止。实测下来第一轮迭代的提升最明显后面几轮提升幅度会逐渐减小但一般不会出现性能下降的情况除非聚类质量太差。4.4 推理阶段的特征提取与匹配推理阶段就是把训练好的模型部署到实际场景中。给定一张查询图片提取它的embedding特征然后和底库中所有图片的特征计算余弦相似度按相似度从高到低排序返回Top-K结果。这里有几个实操细节需要注意。第一查询图片和底库图片的预处理要完全一致resize方式、归一化参数都不能有差异否则特征分布会对不上。第二底库特征可以提前提取好存起来用FAISS或者类似的向量检索库做近似最近邻搜索这样能支持大规模底库的实时查询。第三相似度阈值要根据实际场景调整阈值太高会漏掉正确结果太低会引入太多误报。可以先用验证集画一个ROC曲线根据业务需求选择合适的阈值。还有一个容易被忽略的点是摄像头之间的差异。如果查询图片来自摄像头A底库图片来自摄像头B两个摄像头的成像风格可能不一样直接算相似度会有偏差。可以在推理前做一个域适应把查询图片的风格向底库域靠拢或者用域不变特征来做匹配。不过这个操作会增加推理复杂度需要根据实际效果来决定是否采用。5. 常见问题与排查技巧实录5.1 训练不收敛或loss震荡怎么办这是最常见的问题之一。首先检查学习率是不是太大了ReID任务的学习率一般不超过3e-4如果用了1e-3甚至更高很容易震荡。其次检查batch sampling是不是有问题如果某个batch里某个ID只有1张图片三元组损失就没法算会导致loss异常。再检查数据预处理看看有没有图片损坏或者标注错误的情况一张标错的图片就足以让训练跑偏。如果这些都没问题可以试试warmup策略。前5个epoch用很小的学习率线性增长到初始值让模型先适应一下数据分布然后再正常训练。另外梯度裁剪也很有用把梯度范数限制在1.0或者5.0以内能有效防止梯度爆炸。还有一个可能的原因是域之间的冲突太严重。如果两个域的数据分布差异极大模型可能在两个域之间反复横跳loss自然就震荡了。这时候可以试试分阶段训练先在一个域上预训练再逐步加入其他域的数据让模型有个适应过程。5.2 跨域性能下降严重怎么排查跨域性能下降是Vehicle ReID的常态但如果下降幅度超过30%那就说明有问题了。第一步是可视化特征分布用t-SNE把源域和目标域的特征画出来看看两个域的特征是不是完全分开了。如果分得很开说明域偏移很严重需要加强域适应。第二步是检查数据预处理。不同域的图片分辨率、光照条件、拍摄角度可能差异很大如果预处理没有针对性地处理这些差异模型很难学到域不变特征。可以试试对目标域的数据做直方图均衡化或者颜色迁移让它的风格向源域靠拢。第三步是调整损失函数。如果用了域对抗损失检查一下域分类器的梯度反转系数是不是设得太大了。梯度反转系数太大域分类器太强会损害特征判别能力太小又起不到域适应的作用。一般从0.1开始调逐步增加到1.0。如果以上方法都不行可以考虑用元学习或者联邦学习的思路让模型学会“快速适应新域”。不过这些方法实现复杂度高需要更多调参经验。5.3 聚类结果质量差的优化方法聚类质量差通常表现为两种一种是簇太多同一个ID被拆成好几个簇另一种是簇太大不同ID被混在一起。前者说明特征判别力不够后者说明特征太粗糙。如果是簇太多可以试试降低DBSCAN的eps值或者用层次聚类替代DBSCAN。另外可以在聚类前对特征做降维用PCA降到256维或者128维去掉噪声维度聚类效果往往会好一些。如果是簇太大说明特征区分度不够。可以试试用更强的backbone或者在特征后面加一个度量学习模块专门优化特征空间的类间距离。还有一个技巧是聚类前先做一次kNN过滤对于每个样本只保留它的k个最近邻然后在kNN图上做聚类这样能避免远距离样本的干扰。聚类参数不要一次调到位可以搞一个参数搜索在验证集上评估不同参数下的聚类质量选最好的那个。评估指标可以用轮廓系数或者Calinski-Harabasz指数这两个指标不需要真实标签就能算。5.4 伪标签噪声对训练的负面影响怎么控制伪标签噪声是身份挖掘绕不开的问题。控制噪声的方法有几个。第一是置信度过滤只保留那些置信度高的伪标签置信度低的样本要么丢弃要么给很小的损失权重。置信度可以用簇内平均相似度、簇的紧密度等指标来衡量。第二是一致性检查同一个样本在不同轮次的聚类中应该被分到同一个簇如果某样本的簇分配在不同轮次间跳来跳去说明它的特征不稳定这种样本的伪标签不可靠应该过滤掉。第三是损失函数设计用标签平滑或者对称损失来降低噪声标签的影响。标签平滑把硬标签变成软标签让模型不要对伪标签过于自信。对称损失则是从噪声鲁棒性的角度设计的对噪声标签有天然的抵抗能力。第四是人工抽检虽然费时费力但对于关键应用场景抽检一批伪标签的准确性还是很有必要的。如果发现某个簇的伪标签明显有问题可以把这个簇整个丢掉或者重新聚类。5.5 常见问题速查表问题现象可能原因排查方法解决思路训练loss不下降学习率过大、数据标注错误、batch采样不合理检查学习率、抽查标注、打印batch内ID分布降低学习率、修正标注、调整采样策略loss震荡剧烈学习率过大、域冲突严重、梯度爆炸监控梯度范数、可视化域特征分布加warmup、梯度裁剪、分阶段训练跨域性能下降大域偏移严重、预处理不一致、域适应不足t-SNE可视化、检查预处理流程加强域适应、统一预处理、调整域权重聚类簇太多特征判别力不足、eps太小计算轮廓系数、可视化特征分布降维、调大eps、换聚类算法聚类簇太大特征区分度不够、eps太大检查簇内相似度、簇间相似度换更强backbone、调小eps、加度量学习伪标签噪声大聚类质量差、置信度阈值太低抽检伪标签、计算置信度分布提高置信度阈值、一致性过滤、标签平滑推理速度慢底库太大、特征维度太高测推理耗时、检查检索方式用FAISS、降维、量化特征6. 实操心得与避坑经验6.1 数据比模型更重要做了这么久Vehicle ReID我最大的体会是数据质量对最终性能的影响远比模型结构大。你花一周时间调网络结构可能提升1-2个点但你把数据清洗一遍把标错的、模糊的、遮挡严重的图片处理掉性能可能直接涨5个点。所以如果时间有限优先搞数据别一上来就折腾模型。多域学习的时候每个域的数据质量都要检查。有些公开数据集虽然大但里面有不少噪声图片比如车牌被遮挡、车辆只露出一小部分、图片严重模糊。这些图片在单域训练时可能影响不大但在多域训练时会干扰域不变特征的学习。建议对每个域的数据都做一轮清洗把质量太差的图片剔除掉。6.2 域权重不是拍脑袋定的多域学习中每个域的损失权重怎么设很多人是凭感觉。其实可以用一个简单的策略根据域的大小和难度来定。域越大权重可以适当调低因为大域本身样本多梯度贡献已经很大了域越小权重可以适当调高保证小域的特征也能学好。难度方面如果某个域的验证准确率一直偏低说明这个域难学可以适当增加它的权重。但权重也不能调得太极端。如果某个域的权重是另一个域的10倍那模型基本就只学那个域了多域学习就退化成单域学习了。一般来说域之间的权重比不要超过3:1。6.3 聚类迭代不是越多越好身份挖掘的迭代轮数很多人觉得越多越好其实不是。第一轮迭代通常提升最大因为初始模型的特征已经有一定判别力聚类能挖出不少有用的身份信息。第二轮提升会小一些第三轮可能就基本没提升了。如果继续迭代反而可能因为聚类噪声累积导致性能下降。我的经验是迭代3轮左右就够了。每轮结束后在验证集上评估如果连续两轮性能没有提升就停止迭代。另外每轮迭代后要重新检查聚类质量如果发现聚类质量明显下降说明模型可能过拟合到噪声标签了这时候要么停止迭代要么降低伪标签的权重。6.4 推理阶段的阈值要动态调整推理阶段的相似度阈值很多人设一个固定值就不管了。但实际场景中不同摄像头、不同时间段、不同天气条件下的图片质量差异很大固定阈值很难适应所有情况。比较稳妥的做法是动态调整阈值比如根据查询图片的质量分数来调——质量高的图片阈值可以设高一点质量低的图片阈值设低一点。还有一种做法是用Top-K结果的自适应阈值比如取Top-10结果的平均相似度作为基准然后根据这个基准来筛选最终结果。这样能避免因为整体相似度偏低而漏掉正确结果。6.5 别忘了做消融实验多域学习和身份挖掘涉及很多组件每个组件到底有没有用一定要做消融实验来验证。比如去掉多域学习只用单域训练性能下降多少去掉身份挖掘只用标注数据训练性能下降多少不同的聚类算法、不同的损失权重、不同的backbone对性能的影响分别是多少消融实验不仅是为了写论文更是为了在实际应用中做取舍。如果某个组件带来的提升很小但计算成本很高那在实际部署时就可以考虑去掉它。我见过不少人堆了一堆模块性能是涨了一点但推理速度慢了一倍实际落地时根本用不了。6.6 注意过拟合的早期信号Vehicle ReID的数据集通常不大过拟合是很容易发生的。过拟合的早期信号包括训练loss持续下降但验证loss开始上升、训练准确率远高于验证准确率、模型在训练集上的特征分布过于紧凑。一旦发现这些信号就要及时采取措施比如加正则化、加dropout、减小模型容量、增加数据增强。多域学习虽然数据量大了但过拟合的风险依然存在因为不同域之间的分布差异可能导致模型在某些域上过拟合。建议在每个域上都留一个验证集分别监控每个域的过拟合情况。如果某个域过拟合严重可以针对那个域做更强的数据增强或者更早停止训练。6.7 工程落地时的性能优化最后聊聊工程落地。学术研究追求的是精度工程落地还要考虑速度、内存、部署成本。如果底库有几十万甚至上百万张图片特征检索就是瓶颈。用FAISS的IVF索引可以把检索速度提升几十倍但会损失一点精度。如果对精度要求极高可以用HNSW索引速度快精度也高但内存占用大。模型量化也是常用的优化手段。把FP32的模型量化成FP16或者INT8推理速度能提升2-4倍精度损失通常在1个点以内。对于Vehicle ReID这种对精度不是极端敏感的任务量化是完全可接受的。还有一个容易被忽略的点是预处理的速度。图片解码、resize、归一化这些操作如果不用GPU加速可能比模型推理还慢。可以用NVIDIA的DALI库做GPU加速的预处理或者用OpenCV的GPU版本能显著提升整体吞吐量。
阅读完成 · 觉得有帮助?