1、论文:Invariance Matters: Exemplar Memory for Domain Adaptive Person Re-identification
2、代码: https://github.com/zhunzhong07/ECN
无监督的域自适应(UAD):源域的训练集有标签,目标域的训练集无标签,在目标域的测试集进行验证。
由于行人的镜头是容易确认的,论文中使用了目标域的行人镜头信息。
文章的网络框架:
Exemplar Memory:利用Exemplar Memory保存训练样本的所有特征。
本文提出了三种不变形:
Exemplar-invariance (样本不变性):每个图像之间都是不同的,将每个图像认为是单独的一类,增大了所有图像之间的差距。
Camera-invariance(相机不变性):同一类别由多个相机获得,经过stargan对相机风格进行转化,他们仍具有相同的标签。
Neighborhood-invariance(近邻不变性):由于样本不变性增大了样本之间的差距。对于每一个样本,寻找K个最近邻的图像,认定为具有相同标签。
风格转化图像:以market为例,该数据库有6个摄像机拍摄,对每一幅图像产生对应的另外5种镜头的风格。该数据库训练集有12936张图像,使用starGAN总共产生了64680张图像。
代码实现解析:
网络中的第一个分支简单使用监督学习进行优化;
网络中的第二个分支首先初始化所有的特征为0,存储在Exemplar Memory中。
目标域的数据获取方式:随机在目标域中选取batch个索引。获得对应索引的图像镜头A。以market为例,在6个镜头中随机选取一个镜头,如果该镜头正好是A,则获得该图像,如若该镜头不是A,则获取对应产生的假图像。最后获得的batch中既包含了真图像,也包含了生成的假图像。
对batch计算损失:
公式(3)计算每幅图像属于12936中的每一类的概率。K的维度是4096 * 12936;f()获得的维度是4096 * 1。然后利用公式(4)求交叉熵损失:
经过几个epoch之后开始约束近邻不变:
获得的k个近邻图像如果标签与目标图像标签一直,权重为1,否则为1/k,获得对应权重的代码:
def smooth_hot(self, inputs, targets, k=6):
# Sort
_, index_sorted = torch.sort(inputs, dim=1, descending=True)
ones_mat = torch.ones(targets.size(0), k).to(self.device)
targets = torch.unsqueeze(targets, 1)
targets_onehot = torch.zeros(inputs.size()).to(self.device)
weights = F.softmax(ones_mat, dim=1)
targets_onehot.scatter_(1, index_sorted[:, 0:k], ones_mat * weights)
targets_onehot.scatter_(1, targets, float(1))
return targets_onehot
然后使用交叉熵计算损失,对应代码为:
def smooth_loss(self, inputs, targets):
targets = self.smooth_hot(inputs.detach().clone(), targets.detach().clone(), self.knn)
outputs = F.log_softmax(inputs, dim=1)
loss = - (targets * outputs)
loss = loss.sum(dim=1)
loss = loss.mean(dim=0)
return loss
Exemplar Memory 中的每个特征更新方式:
即每次只更新batch中每个图像对应的索引,对应的更新代码:`
def backward(self, grad_outputs):
inputs, targets = self.saved_tensors
grad_inputs = None
if self.needs_input_grad[0]:
grad_inputs = grad_outputs.mm(self.em)
for x, y in zip(inputs, targets):
self.em[y] = self.alpha * self.em[y] + (1. - self.alpha) * x
self.em[y] /= self.em[y].norm()
return grad_inputs, None