github代码地址:https://github.com/blue-blue272/VideoReID-TCLNet
作者团队:中科院&国科大
摘要:本文提出了一种时序互补学习(Temporal Complementary Learning)网络,该网络提取连续视频帧的互补特征以进行视频中的行人重识别。首先,介绍了一个时序显著性擦除(TSE)模块,其中包括显著性擦除操作和一系列有序学习器。 具体地,对于视频的特定帧,显著性擦除操作通过擦除由先前帧激活的部分来驱动特定学习者挖掘新的和互补的部分。 这样就可以发现连续帧的各种视觉特征,并最终形成目标身份的整体特征。 此外,时间显著性增强(TSB)模块被设计为在视频帧之间传播显著性信息,以增强显著性。 通过有效地减轻由于TSE擦除操作引起的信息丢失,它是对TSE的补充。大量的实验表明,我们的方法与最新技术相比具有出色的性能。
TSE模块:
如图2 (a)所示,TSE对每一帧迭代执行两种操作:用显著擦除操作(SEO)对前一帧发现的部分进行反向擦除,并学习特定的学习者发现新的部分以提取互补特征。
SEO根据挖掘出的f1的判别部分,删除框架I2的feature map F2。然后将擦除后的特征图输入到学习者L2中。当学习者L1所参与的部分被移除后,学习者第二顺位的部分自然会被驱使去发现新的区分部分来识别目标人。该结构采用了递归的结构,TSE首先应用SEO将之前帧发现的所有部分擦除,形成擦除后的特征图Fn,然后使用其特定的学习器Ln挖掘新的部分,得到特征向量Fn,可以表示为:
我还是比较感兴趣作者是如何进行擦除这个操作的:
“为了使TSE端到端可训练,我们采用了一种门机制来删除特征映射。特别地,我们在融合的相关映射上应用了一个softmax层,从这个层中我们使用来擦除所选的块以获得一个门映射”
看了一下它的代码:
def erase_feature(self, x, masks, soft_masks):
"""erasing the x with the masks, the softmasks for gradient back-propagation.
"""
b, c, h, w = x.size() #b, c, h, w
soft_masks = soft_masks - (1 - masks) * 1e8 #TSE第一层的时候初始化soft_masks和masks是m = torch.ones(b, h, w)
#第二层是masks, soft_masks = self.block_binarization(f)
soft_masks = F.softmax(soft_masks.view(b, h * w) , 1)
inputs = x * masks.unsqueeze(1)
res = torch.bmm(x.view(b, c, h * w), soft_masks.unsqueeze(-1))
outputs = inputs + self.conv_erase(res.unsqueeze(-1))
return outputs
TSB模块:
虽然TSE可以提取输入段帧的互补特征,但显著性擦除操作不可避免地导致最显著部分的信息丢失。为了解决这个问题,我们提出了一个时间显著性提升模块。由于之前的擦除操作,中间的所有特征图高质量的框架通常集中在最突出的部分。TSB被提出在中间帧级特征图之间传播最显著的信息。这样,最显著的特征能充分捕捉所有帧内的视觉线索,表现出很强的辨别能力。
类似于一个注意力模块,在代码中看到是有query和memory作为输入。
TSB的输入和输出如下,得到特征的空间增强:
def forward(self, x):
b, c, t, h, w = x.size() #z torch.Size([2, 1024, 2, 16, 8])
inputs = x
query = x.view(b, c, t, -1).mean(-1)
query = query.permute(0, 2, 1) #torch.Size([2, 1024, 2]) #torch.Size([2, 2, 1024])
memory = self.pool(x) #torch.Size([2, 1024, 2, 17, 9])
if self.patch_size % 2 == 0:
memory = memory[:, :, :, :-1, :-1] #torch.Size([2, 1024, 2, 16, 8])
memory = memory.contiguous().view(b, 1, c, t * h * w) #torch.Size([2, 1, 1024, 256])
query = F.normalize(query, p=2, dim=2, eps=1e-12) #torch.Size([2, 2, 1024])
memory = F.normalize(memory, p=2, dim=2, eps=1e-12)
f = torch.matmul(query.unsqueeze(2), memory) * 5 #
f = f.view(b, t, t, h * w)
# mask the self-enhance
mask = torch.eye(t).type(x.dtype) #torch.Size([2, 2, 2, 128])
if self.use_gpu: mask = mask.cuda()
mask = mask.view(1, t, t, 1) #torch.Size([1, 2, 2, 1])
f = (f - mask * 1e8).view(b, t, t * h * w) #torch.Size([2, 2, 256])
f = F.softmax(f, dim=-1) #
y = x.view(b, c, t * h * w) #torch.Size([2, 1024, 256])
y = torch.matmul(f, y.permute(0, 2, 1)) #torch.Size([2, 2, 1024])
y = self.W(y.view(b * t, c, 1, 1)) #torch.Size([4, 1024, 1, 1])
y = y.view(b, t, c, 1, 1)
y = y.permute(0, 2, 1, 3, 4)
z = y + inputs
print("z",z.size()) #z torch.Size([2, 1024, 2, 16, 8])
return z
整体架构:
集成了TSE和TSB模块的时序互补学习网络(TCLNet)体系结构如图4所示。我们的网络是在ImageNet上预先训练好的resnet50上建立的。ResNet-50由四个连续的阶段组成,即, stepe1-4,分别包含3、4、6、3个残差块。我们采用前三个阶段(e1 - 3阶段)作为骨干网络,最后一个阶段(4阶段)作为TSE的学习者。TSB可以插入主干到任何阶段,而TSE可以添加到主干的末端。为了降低网络复杂度,TSE的N个学习者对前两个残块使用相同的参数,对最后一个残块使用自己的参数。