论文阅读:AAAI 2020 Relation Network for Person Re-identification 论文翻译

Relation Network for Person Re-identification
Hyunjong Park, Bumsub Ham∗
School of Electrical and Electronic Engineering, Yonsei University
[email protected], [email protected]
code: https://github.com/cvlab-yonsei/RRID
Abstract:
行人的重新识别(reID)的目的是从通常由多个相机捕获的一组图像中检索到感兴趣的人的图像。最近的reID方法表明,利用描述身体部位的局部特征,以及人图像本身的全局特征,提供健壮的特征表示,即使在缺少身体部位的情况下。然而,直接使用单个部分级别的特征,而不考虑身体部位之间的关系,混淆了在相应部位具有相似属性的不同人的身份差异。为了解决这个问题,我们提出了一种新的行人reID关系网络,它考虑了单个身体部位与其他部位之间的关系。我们的模型使一个单一的部分级特征也包含了身体其他部位的部分信息,支持它更具鉴别性。我们还引入了一种全局对比池(GCP)方法来获得一个人的图像的全局特征。我们建议使用GCP的差异contrastive特征来补充传统的最大和平均池化技术。我们证明,我们的模型在Market1501、DukeMTMC-reID和CUHK03数据集上优于最先进的模型,证明了我们的方法在鉴别人表征上的有效性。
Introduction
人重新识别(reID)是计算机视觉中的基本任务之一,目的是从一组由多个摄像机捕捉到的一组行人图像中检索特定的人。近年来,由于行人检测等广泛的应用,它受到了广泛的关注和多人跟踪。这个问题非常具有挑战性,因为行人有不同的属性(如衣服、性别、头发),而且它们的照片是在不同的条件下拍摄的,如照明、遮挡、背景杂乱和相机类型。在过去的十年中,卷积神经网络(CNNs)的显著进步允许去获得人的表征。对这些变化的因素很稳健,特别是对人类的姿势,此外,它们还支持学习指标来计算人物特征的相似性。
使用cnn的人的reID方法通常侧重于提取一个人的图像的全局特征。以获得一个紧凑的描述符的有效检索。然而,这给出了一个有限的表示,因为全局特征可能不能解释类内的变化(例如,人类的姿态,遮挡,背景杂乱)。为了解决这个问题,基于部分的方法已经被提出。它们从身体部位(如手臂、腿、躯干)中提取局部特征,通常与一个人的图像本身的整体特征一起,并聚合它们,以形成一个有效的人的reID。为了利用身体部位,这些方法从现成的姿态估计器中提取姿态特征计算注意力图以考虑感兴趣的鉴别区域。或将人的图像分割成水平网格。Part-level特征比全局特征提供了比全局特征更好的人表示,但是聚合单个局部特征,例如,通过不考虑身体部位之间的关系而连接它们,仅限于有区别地代表一个人的身份。特别是,这并不能区分图像之间在相应部分中具有相似属性的不同人的身份,因为基于部分的方法是独立计算相应的部分级特征的相似性的。
在本文中,我们建议使每个部分级特征包含了身体其他部位的信息,以获得有效人reID的鉴别人表征。为此(To this end,),我们引入了一种利用身体部位的one-vs.-rest关系的新的关系模块。它解释accounts for了身体单个部位与其他部分之间的关系,因此每个部分级别的特征都包含了相应部分本身和身体其他部位的信息,支持其更具鉴别性(supporting it to be more discriminative.)。正如在我们的实验中可以看到的,考虑到身体部位之间的关系提供了更好的part级特征优于当前的基于part的方法。我们观察到,1)直接使用全局平均池技术和最大池技术(GAP和GMP)来获得人图像的全局特征并不能提供性能增益,2)GMP比GAP提供更好的结果。在此基础上,我们还提出了一种全局对比池(GCP)方法,以获得更好的特征表示,该方法自适应地聚合了整个部分级特征的GAP和GMP结果。具体来说,它利用池结果之间的差异,并以残余方式提取最大的合并特征。在标准基准上的实验结果,包括Market1501,DukeMTMC-reID(Ristani等。和 CUHK03(Li等。,展示了我们的方法在个人reID方面的优势。为了鼓励比较和未来的工作,我们的代码和模型可以在网上获得:https://cvlab-yonsei.github.io/projects/RRID/。
本文的主要贡献可总结如下:1)我们引入了一个基于部分的人reID的关系网络,以获得判别的局部特征。2)我们提出了一种新的池化方法,利用对比特征GCP来提取人图像的全局特征。3)我们实现了一种新的技术状态,远远优于其他基于部分的reID方法。
Related Work
Person reID:
最近提出了几种基于cnn的人reID方法。他们通常将reID任务表示为一个多分类问题,其中同一身份的人的图像属于同一类别。分类损失鼓励将相同身份的图像在特征空间中聚集在附近。其他reID方法还使用不同身份的人图像进行训练,并通过排名损失强制具有相同身份的人图像的特征距离小于具有不同身份的人图像的特征距离。有许多尝试获得鉴别特征表示,例如,利用生成对抗网络(GANs)来提取识别相关特征,使用属性提供补充信息(Lin等。,或利用身体部位提取不同的人的特征。
Part-based方法增强了身体各部位的鉴别能力。我们将它们分为三类:第一种方法使用姿态估计器来提取姿态图。这需要一个带有地标注释的额外数据来训练姿态估计器,而reID的检索精度在很大程度上取决于估计器的性能。第二种方法利用注意力map隐式地利用身体部位。这可以在不需要辅助监控信号(即姿态注释)的情况下实现。它提供了一个对背景杂乱鲁棒的特征表示,重点关注感兴趣的区域,但参与的区域可能不包含有区别性的身体部分。第三种方法还利用身体部位隐式地将人的图像分割成多个尺度的水平网格。它假设人的图片,由现成的物体探测器定位。对于特定的网格,通常具有相同的身体部位(例如,人体图像下部的腿)。然而,当探测器没有紧密地定位这些人时,这是有问题的。我们的方法属于第三类。与其他方法相比,我们在考虑身体部位之间的关系时聚合局部特征,而不是直接利用它们。此外,我们引入了一种GCP方法来获得一个人的图像的全局特征,提供有区别的行人表征。
Relation network.
利用关系推理,对于许多需要有能力推理不同实体(如对象、演员、场景元素)之间的依赖关系的任务非常重要。许多工作已经被提出来支持以关系为中心的计算,包括交互网络和门控图序列网络。关系网络是一种已成功适应计算机视觉问题的代表性方法,包括视觉问题回答,物体检测,行动识别和少样本学习。该关系网络背后的基本思想是考虑所有的实体对,并整合所有这些关系,例如,来回答这个问题,或将感兴趣的对象标记。受这项工作的激励,我们利用身体部位的关系来获得基于part的行人reID的更好的行人表征。不同的是,我们利用单个身体部位和其他部位之间的关系,而不是考虑所有的部位对。这鼓励每个部分级特征也合并身体其他部位的信息,使其更具区别性,同时为行人reID 保留紧凑的特征表示。
Our Approach
我们如图所示。1.我们框架的概述。我们从一个人的图像中提取了一个大小为H×W×C的特征图,其中H、W、C分别为高度、宽度和通道的数量。所得到的特征图被平均划分为6个水平网格。然后将GMP应用于每个特征图,获得大小为1×1×C的部分级特征。我们通过两个模块提供这些特征,以提取新的局部和全局的人表示:one vs rest模块和GCP。第一模块通过考虑各个身体部分与其他部分之间的关系,使每个部分级特征更有区别,并输出1×1×c的局部关系特征。第二模块提供了代表人图像本身的1×1×c的全局对比特征。我们沿着信道维数将全局对比关系特征和局部关系特征连接起来,并使用的特性大小1×1×7c作为reID的行人表示。我们使用交叉熵和三元组损失端到端训练模型,with triplets of anchor、正负行人图像,其中锚图像与正图像具有相同的身份,而与负图像具有不相同的身份。在测试时,我们提取人的图像的特征,并计算它们之间的欧氏距离来确定人的身份。
在这里插入图片描述

图1:对我们的框架的概述。所提出的reID模型主要由三部分组成:我们首先将GMP应用于主干网络中特征图的单个水平切片,从而提取部分水平特征。然后,我们将局部特征输入到单独的模块中,一个one-vs.-rest关系模块和GCP,它们分别给出局部关系特征和全局对比特征。
Relation networks for part-based reID
Part-level features.
我们利用了一个ResNet-50,受过ImageNet分类的训练。作为我们的骨干网络,从输入的人图像中提取初始特征图。具体来说,根据(Sun等人2018b)的工作,我们从ResNet-50架构中删除GAP层和全连接层,并将最后一个卷积层的步幅设置为1。与其他基于部件的reID方法类似(Sun等人2018b;Fu等人2019),我们将初始特征图划分为多个大小为H/6×W×C的水平网格. 我们将GMP应用于每个GMP,并获得大小为1×1×的部分级特征 C.
One-vs.-rest relational module.
从水平网格中提取部分级特征允许隐式地利用身体部分来进行不同的人的表示。现有的reID方法独立使用这些本地特性用于人员检索。它们以特定的顺序连接所有的局部特征,考虑到人的图像之间粗糙的几何对应。虽然这给了一个结构性的行人表示 对几何变化和遮挡的鲁棒性,但局部特征只覆盖图像的一小部分,更重要的是,它们没有解释身体部位之间的关系。也就是说,单个部分是孤立的,不与其他部分交流,这会分散在相应部分具有相似属性的不同人之间相似性的计算。为了缓解这个问题,我们建议利用身体部位之间的关系来表征。具体来说,我们引入了一种新的关系网络(图。2(a))利用身体部位的one-vs-rest关系,使每个部分级别的特征能够包含相应部分本身和其他身体部位的信息。
具体地说,我们用pi表示(i=1,…,6)大小为1×1×c的每个部分级特征。我们对所有部分级的特征进行平均池化,除了特定部分pi,收集来自身体其他部位的信息如下:在这里插入图片描述
。然后,我们分别为每个pi和ri添加一个1×1的卷积层,分别给出大小为1×1×c的特征图¯pi和¯ri。该关系网络连接特征¯pi和¯ri,并为每个pi输出一个局部关系特征qi。我们在图中描述了。2(a)提取局部关系特征q1的一个例子。在这里,我们假设特征图qi包含了原始的一个¯pi本身和其他身体部位的信息。因此,我们使用了一个跳过连接。将¯pi和¯ri的关系信息转移到¯pi,(transfer to 就是叠加 再把¯pi和¯ri融合后与残差的¯pi相连)如下所示:
在这里插入图片描述

其中Rp是一个由1×1卷积、批处理归一化和ReLU层组成的子网络。我们用T来表示一个特征的串联。残余Rp(T(¯pi,¯ri))支持部分级特征¯pi,使其对遮挡更具鉴别性和鲁棒性。我们可以利用所有特征¯pi之间的成对关系,类似于(一篇论文)。,但这需要很大的计算成本,并大幅增加特征的维数。相比之下,我们的one-vs.-rest关系模块以线性时间计算特征qi,并保留了一个紧凑的特征表示。
在这里插入图片描述

图2:(a)one-vs.rest关系模块和(b)GCP。关系模块给出了每个pi的局部关系特征qi。在这里,我们展示了一个提取特征q1的过程。其他关系的局部特征也有类似的计算。GCP同时输出一个考虑所有部分级特征的全局对比特征q0。我们不共享所有部分级特征的卷积层的权重参数。有关详细信息,请参见文本。
在这里插入图片描述

图3:各种池化方法的图示:(a)GAP;(b)GMP;©GAP+GMP;(d)GCP。
GCP:
为了表示一个完整的人的图像,以前的reID方法使用GAP,GMP。,或两者兼而有之。GAP覆盖了人体图像的全身各部分(图。3(a)),但它很容易被背景杂乱和遮挡所分心。GMP克服了这个问题,聚集了对reID有用的最具鉴别部分的特征,同时丢弃背景杂乱(图。3(b))。然而,这并不包含来自整个身体部位的信息。一种同时利用GAP和GMP的混合方法。可能表现更好,但也受到背景杂波的影响(图。3©)。已经证明,GMP比GAP更有效,这也将在我们的实验中再次得到验证。在此基础上,我们提出了一种基于GMP的新型GCP方法,从全身部位提取全局特征图。2(b))。而不是将GAP或GMP应用于输入行人图像的初始特征图,我们首先对所有part级别的特征执行平均和最大池化。我们分别用pavg和pmax来表示由平均和最大池得到的特征图。请注意,pavg和pmax对背景杂波是鲁棒性的,因为我们使用GMP方法来获得初始的部分级特征(图。 1).也就是说,我们从每个水平区域的最具区别的部分聚合特征。特别是,pmax对应于GMP相对于来自主干网络的初始特征图的结果。然后,我们通过从pavg中减去pmax来计算一个对比特征pcont,即它们之间的差异。它收集了来自身体部位的大多数区别信息(例如,图中的绿色框。3(d)),除了pmax(例如,图中的红色框。3(d))。我们添加瓶颈层,将pcont和pmax的通道数从C减少到C,分别用¯pcont和¯pmax表示,最后将对比特征¯pcont的互补信息转移到¯pmax(图。3(d))。在形式上,我们得到了输入图像的全局对比特征q0,如下:
在这里插入图片描述

其中Rg是一个由1×1卷积、批处理归一化和ReLU层组成的子网络。全局特征q0基于¯pmax,并聚合了来自对比特征¯pcont(参考¯pmax)的互补信息。因此,它继承了GMP的优点,如在覆盖整个身体部位时对背景杂乱的鲁棒性。我们将(2)中的全局对比特征q0和局部关系中的qi(i=1,…,6)连接起来,并将其作为reID的人表示。
Training loss.
我们利用行人图像的ground-truth识别标签来学习人的表征。为了训练我们的模型,我们使用交叉熵和三联体损失,由参数λ平衡如下:

在这里插入图片描述

其中,我们分别用和Ltriplet and Lce三联体和交叉熵损失表示。交叉熵的损失(分类用)是定义为:
在这里插入图片描述

其中,我们分别用N和yn表示minibatch和 ground-truth识别标签中的图像数量。yˆin是行人表征中每个特征qi的预测标识标签,定义为
在这里插入图片描述

K is the number of identification labels, and wik is classifier for the feature qi and the label k. K是身份标签的数量,wik是特征qi和标签K的分类器。我们对分类器使用了一个全连接的层。为了提高排名性能,我们使用了batch-hard triplet loss,表述如下:
在这里插入图片描述

其中,NK是小批中的标识数量,NM是小批中每个标识标签的图像数量(N=NKNM)。α是控制特征空间中正对和负对之间距离的边距参数。我们分别用qAi、j、qPi、j、qNi、j表示锚、正、负图像的人表示,其中i、j对应识别和图像索引。
Extension to different numbers of grids.
到目前为止,我们使用全局和局部特征来描述我们的模型,即T(q0……q6),对于一个人的表示,下面用qp6表示。在不失去一般性的情况下,我们可以使用不同数量的水平网格来表示人,来考虑多个尺度的不同部分。如qP2和qP4,它们将初始特征图分别划分为2个和4个水平区域。因此,我们将qP2、qP4和qP6的特征,即T(qP2、qP4、qP6)连接起来,并将其作为有效reID的最终人表示。请注意,qP2、qP4和qP6包含不同的局部关系特征,因此具有不同的全局对比特征。还要注意,这些特性共享相同的且一样参数的主干网络。
Experimental Results
Implementation details
Dataset.
我们在以下数据集上测试了我们的方法,并将其性能与最新的性能进行了比较。1)Market1501数据集(其中包含了由6台相机拍摄的1501个身份的32,668张个人图像。我们使用由(Zheng提供的训练/测试分割等。2015年),其中包括12,936张751个身份用于训练的图像、3368张查询和197,32个750个身份用于测试的图库图像。2)CUHK03数据集(Li等。2014年)提供了两台相机观察到的14097张1467张1467张身份图像,并提供了两种类型的人图像:通过DPM方法手动标记和检测图像(Felzenszwalb等人。2008)。在训练/测试分开后(Zhong等。2017a),我们将其分为7365张767个身份的训练图像,1400个查询和5332个700个身份的图库图像。3)DukeMTMCreID(Ristani等。2016年)提供16522张702个身份的训练图像,2228张查询和17661张702个身份的图库图像。
在这里插入图片描述

表1:与最先进的个人reID进行定量比较。我们测量了Market1501、 CUHK03。和DukeMTMC-reID数据集上的mAP(%)和rank-1的精度(%)。我们分别使用qP6和T(qP2、qP4、qP6)的模型表示后缀“-S”和“-F”
Training.
我们将所有图像的大小调整为384×128以进行训练。我们将特征通道C的数量设置为2048,c设置为256。结果表明,qP6和T(qP2、qP4、qP6)分别有1792-和3840维的特征。我们通过水平翻转和随机擦除来增强训练数据集。我们使用随机梯度下降(SGD)作为优化量,动量为0.9,权重衰减为5e-4。我们用80个epochs ,N为64来训练我们的模型,我们随机的选择16个身份,并为每个身份采样4人的图像(NK=16,NM=4)。主干网络和其他部分的学习速率最初分别设置为1e-3和1e-2,直到40个epoches除以10以每20个epochs。我们根据经验将权重参数λ设置为2,并将其固定到所有实验中。所有的网络都使用PyTorch进行端到端训练(Paszke等。2017)。使用两个NVIDIATitan Market1501、DukeMTMC-reID和DukeMTMC-reID数据集训练我们的模型大约需要6个、3个和8个小时。
Comparison with the state of the art
Quantitative results.定量结果。
我们在表中比较了我们的模型与SOTA,包括基于part的行人reID方法。我们测量了Market1501、 CUHK03。和DukeMTMC-reID数据集上的mAP(%)和rank-1的精度(%)。我们报告单个查询的reID结果,以进行公平的比较。我们使用qP6和T(qP2、qP4、qP6)分别表示后缀“-S”和“-F”。做为最终的行人表示。表显示,除MGN外,Ours-S在所有数据集上的mAP和rank1精度都优于最先进的reID方法。这证明了我们的one-vs.-rest关系模块和GCP的有效性。此外,Ours-S使用了1792维特征,允许一个高效的人检索,同时提供最先进的结果。Ours-F在所有数据集Map方面再次给出了最好的结果。我们对market 1501的mAP为88.9%,rank-1准确率为95.2%,在CUHK03数据上检测/标记图像的MAP为69.6%/75.6%,Rank-1为74.4%/77.9%。DukeMTMC-reID的mAP为78.6%,1准确率为89.7%。Our-F的rank-1准确性略低于MGN 在 Market1501。但Our-F在其他数据集上显著优于MGN。请注意,CUHK03和DukeMTMC-reID数据集中的人物图像更难检索,因为它们通常具有较大的姿态变化、背景杂波、遮挡和令人困惑的属性。
Qualitative results.定性结果。
图4显示了行人检索结果与SOTA的视觉比较。在Market1501数据集上。我们显示了查询图像的前5个检索结果。所有比较的结果均来自作者提供的官方模型。我们可以看到,我们的方法检索到正确的人的图像,特别是它对属性变化(如自行车)和背景杂乱(如草)是鲁棒的。其他基于part的方法,包括AlignedReID和PCB,试图匹配图像之间的局部特征进行检索。例如,他们专注于在查询图像中寻找自行车或草的对应关系,给出许多假阳性的结果。请注意,图库集包含了许多骑自行车的人的图像,但它们的身份与查询中的人不同。
在这里插入图片描述

图4:对Market1501数据集上的人reID结果的定性比较。我们显示了AlignedReID的前5个检索结果(左排名1,右排名5)。检索到的带有绿色和红色方框的图像分别是正确和不正确的结果。
Discussion
Ablation study.
我们在表中显示了对我们的模型的不同组成部分的消融分析。我们比较了我们的模型的几个变体在mAP和rank1精度方面的性能。从前三行中,我们可以看到,同时使用全局和局部特性可以提高检索性能,这证实了在基于part 的reID方法中的发现。第四行显示,GMP比GAP提供了更好的结果,因为GMP避免了背景混乱。下一行的结果展示了使用我们的关系模块获得的局部特征的效果。例如,对于Market1501数据集,mAP和rank1的精度分别提高了3%和1%,这是非常显著的。从第五行和第八行,我们比较了根据池化方法对全局特征的检索性能,可以看到GCP在mAP和rank-1精度方面优于GMP、GAP和GMP+GAP。例如,与GMP+GAP相比,我们的GCP将market 1501数据集上的mAP从86.6%提高到88.0%。最后四行表明,利用多个尺度的part级特性是很重要的,并且使用所有组件的性能最好。
在这里插入图片描述

表2:不同网络架构的定量比较。我们测量了market 1501,cuhk 03和DukeMTMC-reID数据集上的mAP(%)和rank 1的精度(%)。粗体中的数字表示表现最好,下划线中的数字表示表现第二好。GF:全局功能;LF:本地功能;RM:One-vs.-rest关系模块;Ext。:对多个尺度的扩展。
我们如图所示。5(a)使用关系模块的检索结果。我们可以看到,从关系模块中获得的行人表征成功地区分了不同身份(如性别)的相同属性(如紫色衬衫),并且对遮挡(例如,被袋子或自行车遮挡的人)具有鲁棒性。我们在图中进行了比较。5(b)不同池化方法的检索结果。我们再次证实,GAP对背景杂乱并不稳健,而GMP只看到最具区别的区域(例如,自行车),而不是人。我们观察到,GCP在保持GMP的优势的同时缓解了这些问题。
在这里插入图片描述

图5:检索结果的可视化比较:(a)关系模块和(b)池化方法。我们展示了rank1名的结果。关系模块对不同身份的人的图像区分相同的属性。GCP允许聚合来自区分区域的特征,并提供对背景杂乱鲁棒的人表示,克服了GAP和GMP的缺点
One-vs.-rest relation.
我们考虑每个部分级特征¯pi与其其余特征¯ri之间的关系。为了展示关系模块是如何工作的,我们在(1)中使用组合rest特征¯ri而不是T(¯pi,¯ri)来训练一个模型。在这种情况下,我们不使用多尺度的扩展。我们在图6中可视化了Rp(¯ri)和Rp(T(¯pi,¯ri))的激活图。我们观察到,与Rp(¯ri)相比,Rp(T(¯pi,¯ri))更多关注那些属性与¯pi中属性不同的区域。这表明Rp(T(¯pi,¯ri))从其他部分提取了¯pi的互补特征,这对行人reID有帮助,但不包含在¯pi中。这也验证了仅使用¯pi的特征并不足以区分图像之间在相应部分具有相似属性的不同人的身份。Market1501上的¯ri的mAP/rank-1为84.5/93.4,低于表中使用T(¯pi,¯ri)获得的86.7/94.2。
在这里插入图片描述

图6:使用Rp(¯r3)(顶部)和Rp(T(¯p3、¯r3))(底部)的模型激活图的示例。我们还显示了每个模型的前3个检索结果。我们可以看到,与使用Rp(¯r3)相比,rest区域(被使用Rp(¯p3,¯r3)水平网格)高度激活,这表明我们的one-vs.-rest关系允许每个part级特征有效地看到其余区域。
Performance comparison of local features. 本地特性的性能比较。
我们证实了one-vs.-rest关系模块具有鉴别特征的能力。无论是否使用关系模块,我们都为每个水平区域提取了一个大小为1×1×256的局部特征。给定一个查询图像,然后我们使用qP6为行人表示使用单个局部特征来检索人的图像。我们报告了从Market1501数据集上从不同水平区域(1:上,6:下)提取的单个局部特征的mAP和rank-1精度,请在表中。从这个表中,我们可以观察到两件事:(1)关系模块极大地提高了mAP和rank-1的精度。mAP和rank-1的准确率平均提高分别为21.7%和19.6%。rank-1的准确性测量了最容易匹配的检索结果的性能,而mAP描述了检索具有相同身份的所有人图像的能力,这表明关系模块特别有利于检索具有挑战性的人图像。(2)来自第三个和最后一个水平区域的局部特征分别给出了最佳和最差的结果。这表明,通常对应于一个人的躯干的中间部分为指定一个特定的人提供了最具鉴别性的特征,而底部(例如,有时由于人的探测器的错误定位而导致的腿或背景)为人的reID提供了最不具鉴别性的特征。
在这里插入图片描述

表3:Market1501数据集上单个局部特征的定量比较。RM:One-vs.- rest关系模块。
Performance comparison of global features.全局特性的性能比较。
表比较了单个全局特征在market-1501数据集上的mAP和rank-1的精度方面的reID性能。我们只在qP6的人的表示中使用256维的全局特征来进行人的检索,但通过不同的池化方法获得。请注意,全局特征的大小比表中典型的人表示要小得多。从表中可以看出,GCP在mAP和rank-1的精度方面都给出了最好的检索结果,远远优于GAP、GMP和GAP+GMP。与表中的其他reID方法相比,GCP在特性的准确性和大小方面提供了很好的折衷方案。例如,我们的尺寸为1的×1×256的全局对比特征达到了93.4%,与PCB+RPP的93.1%相当。使用1536维特征。
在这里插入图片描述

表4:在market-1501数据集上的全球特征的定量比较。
Conclusion
我们提出了一个行人reID的关系网络,考虑了个体身体部分与其他部分之间的关系,使每个部分水平的特征更具鉴别性。我们还提出了使用对比特征去表示一个全局行人表征。我们为行人reID方法达到SOTA,大大优于其他reID方法。消融实验清楚地证明了我们的模型中每个组成的有效性。

本文的创新点:
1.我们引入了一个基于部分的person reID的关系网络来获得判别的局部特征。它考虑了身体各个部位与身体其他部位之间的关系,使得每个part-level feature都包含了相应部位本身以及身体其他部位的信息,使其更具识别性
2.我们提出了一种新的利用对比度特征的池化方法,即GCP,来提取一个人图像的全局特征。具体来说,它利用池化结果之间的差异,并提取互补信息,以残差学习的方式最大池化特征。
在这里插入图片描述
网络整体如上图,使用ResNet50作为特征提取的backbone网络,然后将特征图按照H分为6个部分,分别使用GMP提取到part-level的特征,最后分别使用One-vs.-rest relational module和GCP提取到局部关系特征和全局对比度特征。
在这里插入图片描述
1)关系网络结构如上图(a),具体来说,以p1为基准,先用相加求均值的方法聚合p2-p6的特征得到r1,分别用两个卷积对p1和r1进行降维,再对结果进行拼接,融合。最后使用残差的结构,将p1降维的结果加上去得到最终的局部关系特征q1 。其余的q2-q6也是同上操作。
2)GCP如上图(b),先对p1-p6的part level 特征进行 avg和max pooling操作得到 pmax, pavg。再使用pmax-pavg得到对比度特征pcont,剩余的操作同1)中相同。

其他博客参考:
https://www.jianshu.com/p/3e934bbef897
代码结束参考:
https://blog.csdn.net/weixin_44498393/article/details/113551968?spm=1001.2101.3001.6650.10&utm_medium=distribute.pc_relevant.none-task-blog-2defaultCTRLISTdefault-10.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2defaultCTRLISTdefault-10.no_search_link
也可以参考 唐宇迪博士行人重识别课程里面 讲解的也很好。 我是TB 10来块钱买的视频课。
在这里插入图片描述
在这里插入图片描述
相互关联的博客:
https://www.cnblogs.com/orangecyh/p/13176955.html
主要是介绍三篇2020年 论文的主要思想:
Relation-Aware Global Attention for Person Re-identification(CVPR2020)
Multi-Granularity Reference-Aided Attentive Feature Aggregation for Video-based Person Re-identification(CVPR2020)
Relation Network for Person Re-identification(AAAI2020)

部分代码解读:
在Train.py文件中:

 #分类任务 cross是交叉熵 计算  最后把分类损失加在一起
        C_loss_local = torch.sum(
            torch.stack([cross_entropy_loss(output, labels) for output in logits_local_list]), dim=0)

        C_loss_local_rest = torch.sum(
            torch.stack([cross_entropy_loss(output, labels) for output in logits_local_rest_list]), dim=0)

        C_loss_rest = torch.sum(
            torch.stack([cross_entropy_loss(output, labels) for output in logits_rest_list]), dim=0)

        C_loss_global = torch.sum(
            torch.stack([cross_entropy_loss(output, labels) for output in logits_global_list]), dim=0)

        C_loss = C_loss_local_rest + C_loss_global + C_loss_local + C_loss_rest

        loss = T_loss + 2 * C_loss

在 Relation_final_ver_last_scale_lager_lossers.py文件中:

    def forward(self, x):
        
        criterion = nn.CrossEntropyLoss()
        #print(x.shape) # 8 3 384 128 
        feat = self.base(x) # 8 3 2048 24 8
        #print(feat.shape) 
        assert (feat.size(2) % self.num_stripes == 0)
        stripe_h_6 = int(feat.size(2) / self.num_stripes) # 4
        stripe_h_4 = int(feat.size(2) / 4) # 6
        stripe_h_2 = int(feat.size(2) / 2) # 12
        local_6_feat_list = []  #local feature with 6
        local_4_feat_list = []
        local_2_feat_list = []
        final_feat_list = []
        logits_list = []
        rest_6_feat_list = []
        rest_4_feat_list = []
        rest_2_feat_list = []
        logits_local_rest_list = [] #left image local and list feature together
        logits_local_list = []
        logits_rest_list = []
        logits_global_list = []  #right image
        
        
        for i in range(self.num_stripes): # 得到6块中每一个的特征
            local_6_feat = F.max_pool2d(
                feat[:, :, i * stripe_h_6: (i + 1) * stripe_h_6, :], # 每一块是4*w   :代表全要  遍历i=0 然后大小变成0-4 分成六块
                (stripe_h_6, feat.size(-1))) #pool成1*1的  最后是有个maxpool的操作
            #print(local_6_feat.shape) #8 2048 1 1
            
            local_6_feat_list.append(local_6_feat) # 按顺序得到每一块的特征



#上面操作实现了 吧特征图进行划分 最后存在list这个数组中 接下来 先进行gcp操作
        
            
        global_6_max_feat = F.max_pool2d(feat, (feat.size(2), feat.size(3))) # 8 2048 1 1,全局特征  h w的维度多大
        #print(global_6_max_feat.shape)
        global_6_rest_feat = (local_6_feat_list[0] + local_6_feat_list[1] + local_6_feat_list[2]#局部与全局的差异 
                              + local_6_feat_list[3] + local_6_feat_list[4] + local_6_feat_list[5] - global_6_max_feat)/5
        #print(global_6_rest_feat.shape) # 8 2048 1 1
        global_6_max_feat = self.global_6_max_conv_list[0](global_6_max_feat) # 8 256 1 1
        #print(global_6_max_feat.shape)
        global_6_rest_feat = self.global_6_rest_conv_list[0](global_6_rest_feat) # 8 256 1 1
        #print(global_6_rest_feat.shape)
        global_6_max_rest_feat = self.global_6_pooling_conv_list[0](torch.cat((global_6_max_feat, global_6_rest_feat), 1))
        #print(global_6_max_rest_feat.shape) # 8 256 1 1
        global_6_feat = (global_6_max_feat + global_6_max_rest_feat).squeeze(3).squeeze(2)
        #print(global_6_feat.shape) # 论文中Global contrastive feature Figure2(b)   16*256


        #接着去做左边的操作  遍历  local是0时候  i+1 就是1  local是1时候 i+1 最后是 6%6 =0
        for i in range(self.num_stripes): #对于每块特征,除去自己之后其他的特征组合在一起
            
            rest_6_feat_list.append((local_6_feat_list[(i+1)%self.num_stripes]#论文公式1处的ri 
                                   + local_6_feat_list[(i+2)%self.num_stripes]
                                   + local_6_feat_list[(i+3)%self.num_stripes] 
                                   + local_6_feat_list[(i+4)%self.num_stripes]
                                   + local_6_feat_list[(i+5)%self.num_stripes])/5)  #最后除以5
            
        for i in range(4):
            local_4_feat = F.max_pool2d(
                feat[:, :, i * stripe_h_4: (i + 1) * stripe_h_4, :],
                (stripe_h_4, feat.size(-1)))
            #print(local_4_feat.shape)
            
            local_4_feat_list.append(local_4_feat)
        
        
            
        global_4_max_feat = F.max_pool2d(feat, (feat.size(2), feat.size(3)))
        #print(global_4_max_feat.shape) # 8 2048 1 1
        global_4_rest_feat = (local_4_feat_list[0] + local_4_feat_list[1] + local_4_feat_list[2] 
                              + local_4_feat_list[3] - global_4_max_feat)/3
        #print(global_4_rest_feat.shape)                     
        global_4_max_feat = self.global_4_max_conv_list[0](global_4_max_feat) # 8 256 1 1
        #print(global_4_max_feat.shape)
        global_4_rest_feat = self.global_4_rest_conv_list[0](global_4_rest_feat)  # 8 256 1 1
        #print(global_4_rest_feat.shape)
        global_4_max_rest_feat = self.global_4_pooling_conv_list[0](torch.cat((global_4_max_feat, global_4_rest_feat), 1))
        #print(global_4_max_rest_feat.shape) # 8 256 1 1
        global_4_feat = (global_4_max_feat + global_4_max_rest_feat).squeeze(3).squeeze(2)
        #print(global_4_feat.shape) # 依旧是16 256
        for i in range(4): #假如是4块的时候
            
            rest_4_feat_list.append((local_4_feat_list[(i+1)%4] 
                                   + local_4_feat_list[(i+2)%4]
                                   + local_4_feat_list[(i+3)%4])/3)
            
        for i in range(2):
            local_2_feat = F.max_pool2d(
                feat[:, :, i * stripe_h_2: (i + 1) * stripe_h_2, :],
                (stripe_h_2, feat.size(-1)))
            #print(local_2_feat.shape)
            local_2_feat_list.append(local_2_feat)
        
        
            
        global_2_max_feat = F.max_pool2d(feat, (feat.size(2), feat.size(3)))
        #print(global_2_max_feat.shape)
        global_2_rest_feat = (local_2_feat_list[0] + local_2_feat_list[1] - global_2_max_feat)
        #print(global_2_rest_feat.shape)
        global_2_max_feat = self.global_2_max_conv_list[0](global_2_max_feat)
        #print(global_2_max_feat.shape)
        global_2_rest_feat = self.global_2_rest_conv_list[0](global_2_rest_feat)
        #print(global_2_rest_feat.shape)
        global_2_max_rest_feat = self.global_2_pooling_conv_list[0](torch.cat((global_2_max_feat, global_2_rest_feat), 1))
        #print(global_2_max_rest_feat.shape)
        global_2_feat = (global_2_max_feat + global_2_max_rest_feat).squeeze(3).squeeze(2)
        #print(global_2_feat.shape)
        for i in range(2):
            
            rest_2_feat_list.append((local_2_feat_list[(i+1)%2]))

     #之前相当于分别把642 三种情况的 考虑一边了  接下来是进行左图的剩下操作。
        for i in range(self.num_stripes):

            local_6_feat = self.local_6_conv_list[i](local_6_feat_list[i]).squeeze(3).squeeze(2)#pi  #先取的p1 第一个
            #print(local_6_feat.shape)  16*256大小的
            input_rest_6_feat = self.rest_6_conv_list[i](rest_6_feat_list[i]).squeeze(3).squeeze(2)#ri
            #print(input_rest_6_feat.shape)  16*256大小的
            input_local_rest_6_feat = torch.cat((local_6_feat, input_rest_6_feat), 1).unsqueeze(2).unsqueeze(3)
            #print(input_local_rest_6_feat.shape) # 8 512 1 1
            local_rest_6_feat = self.relation_6_conv_list[i](input_local_rest_6_feat)
            #print(local_rest_6_feat.shape) # 8 256 1 1
            local_rest_6_feat = (local_rest_6_feat 
                               + local_6_feat.unsqueeze(2).unsqueeze(3)).squeeze(3).squeeze(2)
            #print(local_rest_6_feat.shape)# 16 256
            final_feat_list.append(local_rest_6_feat)  #存起来
            

            
            if self.num_classes > 0:
                logits_local_rest_list.append(self.fc_local_rest_6_list[i](local_rest_6_feat)) #当前local和rest的 共i份 全部都fc操作 分类结果
                logits_local_list.append(self.fc_local_6_list[i](local_6_feat))# 当前local的分类结果 也就是对输入的123456 进行监督
                logits_rest_list.append(self.fc_rest_6_list[i](input_rest_6_feat))# 当前rest的分类结果
        #print(np.array(logits_local_rest_list).shape)
        #只在最后做监督的话  怕有个阶段不好
                
        for i in range(4):
            
            local_4_feat = self.local_4_conv_list[i](local_4_feat_list[i]).squeeze(3).squeeze(2)
            #print(local_4_feat.shape)
            input_rest_4_feat = self.rest_4_conv_list[i](rest_4_feat_list[i]).squeeze(3).squeeze(2)
            #print(input_rest_4_feat.shape)
            input_local_rest_4_feat = torch.cat((local_4_feat, input_rest_4_feat), 1).unsqueeze(2).unsqueeze(3)
            #print(input_local_rest_4_feat.shape)
            local_rest_4_feat = self.relation_4_conv_list[i](input_local_rest_4_feat)
            #print(local_rest_4_feat.shape)
            local_rest_4_feat = (local_rest_4_feat 
                               + local_4_feat.unsqueeze(2).unsqueeze(3)).squeeze(3).squeeze(2)
            #print(local_rest_4_feat.shape)
            final_feat_list.append(local_rest_4_feat)

            
            if self.num_classes > 0:
                logits_local_rest_list.append(self.fc_local_rest_4_list[i](local_rest_4_feat))
                logits_local_list.append(self.fc_local_4_list[i](local_4_feat))
                logits_rest_list.append(self.fc_rest_4_list[i](input_rest_4_feat))
        #print(np.array(logits_local_rest_list).shape)  append操作  6+4 一共10
                
        for i in range(2):

            local_2_feat = self.local_2_conv_list[i](local_2_feat_list[i]).squeeze(3).squeeze(2)
            #print(local_2_feat.shape)
            input_rest_2_feat = self.rest_2_conv_list[i](rest_2_feat_list[i]).squeeze(3).squeeze(2)
            #print(input_rest_2_feat.shape)
            input_local_rest_2_feat = torch.cat((local_2_feat, input_rest_2_feat), 1).unsqueeze(2).unsqueeze(3)
            #print(input_local_rest_2_feat.shape)
            local_rest_2_feat = self.relation_2_conv_list[i](input_local_rest_2_feat)
            #print(local_rest_2_feat.shape)
            local_rest_2_feat = (local_rest_2_feat 
                               + local_2_feat.unsqueeze(2).unsqueeze(3)).squeeze(3).squeeze(2)
            #print(local_rest_2_feat.shape)
            final_feat_list.append(local_rest_2_feat)
            

            
            if self.num_classes > 0:
                logits_local_rest_list.append(self.fc_local_rest_2_list[i](local_rest_2_feat))
                logits_local_list.append(self.fc_local_2_list[i](local_2_feat))
                logits_rest_list.append(self.fc_rest_2_list[i](input_rest_2_feat))                
        #print(np.array(logits_local_rest_list).shape)   结果是12
                
                
         #把右边的值拿到  加入进去
            
        final_feat_list.append(global_6_feat)
        final_feat_list.append(global_4_feat)
        final_feat_list.append(global_2_feat)
        #print(np.array(logits_local_rest_list).shape)

        if self.num_classes > 0:
            #有多少种损失:9个  左边三个阶段 每个阶段分642 情况 3*3
            logits_global_list.append(self.fc_global_6_list[0](global_6_feat))
            logits_global_list.append(self.fc_global_max_6_list[0](global_6_max_feat.squeeze(3).squeeze(2)))
            logits_global_list.append(self.fc_global_rest_6_list[0](global_6_rest_feat.squeeze(3).squeeze(2)))
            logits_global_list.append(self.fc_global_4_list[0](global_4_feat))
            logits_global_list.append(self.fc_global_max_4_list[0](global_4_max_feat.squeeze(3).squeeze(2)))
            logits_global_list.append(self.fc_global_rest_4_list[0](global_4_rest_feat.squeeze(3).squeeze(2)))
            logits_global_list.append(self.fc_global_2_list[0](global_2_feat))
            logits_global_list.append(self.fc_global_max_2_list[0](global_2_max_feat.squeeze(3).squeeze(2)))
            logits_global_list.append(self.fc_global_rest_2_list[0](global_2_rest_feat.squeeze(3).squeeze(2)))
            #print(np.array(logits_global_list).shape)      9
            return final_feat_list, logits_local_rest_list, logits_local_list, logits_rest_list, logits_global_list
        #9分类损失  12个特征 添加的
        
        return final_feat_list
    

猜你喜欢

转载自blog.csdn.net/zqx951102/article/details/120989728