论文Re-ranking Person Re-identification with k-reciprocal Encoding(person re-id的re-ranking)

本文深入解析了行人重识别任务中的k-相互最近邻rerank方法,该方法旨在提高正样本在排序列表中的排名。通过对原始距离计算马氏距离,确定k-nearest neighbors,并通过k-相互最近邻扩展和Jaccard距离优化排名。源码分析展示了如何实现这一过程,包括计算k-相互最近邻、V矩阵、Jaccard距离和最终距离的融合。
摘要由CSDN通过智能技术生成

1.基础数学知识

欧氏距离:

余弦距离:

马氏距离:

(1)方差:

(2)协方差

        协方差:标准差与方差是描述一维数据的,当存在多维数据时,我们通常需要知道每个维数的变量中间是否存在关联。协方差就是衡量多维数据集中,变量之间相关性的统计量。比如一个人身高和体重的关系:如果两个变量之间的协方差为正值,则这两个变量之间存在正相关,即你变大,同时我也变大。

 例子:X,Y反向运动

 (2)协方差矩阵:

        X为是以 n个随机变数组成的列向量:

                                                 

则其协方差矩阵为:

 (4)马氏距离

2 k-reciprocal re-ranking论文简介

看之前手上最好有一份行人重识别的代码,并有rerank模块和源码。

Re-ranking Person Re-identification with k-reciprocal Encoding》论文地址:http://arxiv.org/pdf/1701.08398

        在行人重识别的任务中,对模型的输出,我们对结果相似的进行排序(距离近的靠前)。但是结果中经常会出现噪声。

        如下图中最上面的一行为一些正负样本(好像不是排序的结果,原文中说:P1, P2, P3 and P4 are four true matches to the probe, but all of them are not included in the top-4 ranks,some false matches (N1- N6) receive high ranks ),与probe相同身份的正样本为P1-P4,负样本N1-N6。

         当两张图片被称为k nearest neighbors时,他们互相在对方的top-k 的rank中,在下图的样本中,每个样本对应的两竖列(10张图)为它的nearest neighbors,可以看到蓝色为probe,绿色为身份相同的正样本,蓝色probe在全部正样本的neighbors中出现了,且没有在负样本中出现。

        基于上述的considerations,提出了一种REID的rerank的方法‘k-reciprocal encoding method’。

(1)问题的定义:

 给出一个probe,它和gallery中的每一个样本的距离,可以用马氏距离来计算,见公式(1),其中xp是p的feature,xgi是gi的feature,M是协方差矩阵(协方差矩阵是半正定的positive semidefinite matrix)。

 

         原始的initial ranking list为L(p,G),我们的目标就是去提升正样本在rank list中的排名。

(2)k-reciprocal Nearest Neighbors

定义N,N是由rank list前k个samples组成的,|*|表示数量=k。

 

The k-reciprocal nearest neighbors  ℛ(p, k)可以定义为公式(3),gi在N(p,k)中 且p在N(gi,k)中,即对应了中心思想“他们互相在对方的top-k 的rank中”。

(注释: ∧  且

 

 可能存在一些特殊情况,有些正样本可能不存在k-nearest neighbors的集合中,即也不包含在R(p,k)中,因此要用公式(4)把R扩展成R*,过程可理解为下图

(3)Jaccard Distance

        本部分使用ℛ*(p, k)重新进行rank list的排序。使用的是Jaccard Distance。先验知识是:如果两个图像相似,则他们的k-reciprocal nearest neighbors set   ℛ*(p, k)有很大的重叠部分

(注: |*|为计算统计数量)

(4)性能的优化

上述方法有三个缺点:

(1)耗时,全样本的交并集

(2)没看懂

(3)需要原始距离和Jaccard距离相结合

为了克服上述缺点,需要对jaccard进行改造:

首先将k-reciprocal Nearest Neighbors set 转换为V

针对一个p,可以将其neighbor set根据公式(6)转换为一个N维的Vp,但此时把每个存在于ℛ*(p, k)中的对象看为平等的关系,公式(6)完全没有用到距离。

 因此对公式(6)进行改造提出公式(7),根据p和neighbor之间的距离d(p,gi),重新计算V的值.通过这种方式,the hard weighting (0 or 1) is converted into soft weighting,with closer neighbors assigned(赋值) larger weights while farther neighbors smaller weights。

注:e-x次方图像

e的-x次方图像怎么画

基于之前的一些定义,交并集的计算可以转换为公式(8、9)???,对两个V进行element-based的max和min操作,然后再L1 norm.

至此,公式5可以重写为 纯向量的计算

(5)本地query的扩展:???没看懂

(6)最终的距离

       

        将Jaccard距离dj 和原始距离d按权重相加。第四章中不同的数据集使用了不同的λ权重的配置。

源码的解析

一部分参考了:详细解读行人重识别的k-reciprocal Encoding(k个相互近邻编码方法) re-ranking方法及其实现代码解读_ZJE-CSDN博客

源码:

"""
官方源码地址: https://github.com/zhunzhong07/person-re-ranking

Created on Mon Jun 26 14:46:56 2017
@author: luohao
Modified by Houjing Huang, 2017-12-22.
- This version accepts distance matrix instead of raw features.
- The difference of `/` division between python 2 and 3 is handled.
- numpy.float16 is replaced by numpy.float32 for numerical precision.

CVPR2017 paper:Zhong Z, Zheng L, Cao D, et al. Re-ranking Person Re-identification with k-reciprocal Encoding[J]. 2017.
url:http://openaccess.thecvf.com/content_cvpr_2017/papers/Zhong_Re-Ranking_Person_Re-Identification_CVPR_2017_paper.pdf
Matlab version: https://github.com/zhunzhong07/person-re-ranking

API
q_g_dist: query-gallery distance matrix, numpy array, shape [num_query, num_gallery]
q_q_dist: query-query distance matrix, numpy array, shape [num_query, num_query]
g_g_dist: gallery-gallery distance matrix, numpy array, shape [num_gallery, num_gallery]
k1, k2, lambda_value: parameters, the original paper is (k1=20, k2=6, lambda_value=0.3)
Returns:
  final_dist: re-ranked distance, numpy array, shape [num_query, num_gallery]
"""

import numpy as np


def re_rank(q_g_dist, q_q_dist, g_g_dist, k1=3, k2=2, lambda_value=0.3):

    # The following naming, e.g. gallery_num, is different from outer scope.
    # Don't care about it.

    original_dist = np.concatenate(
      [np.concatenate([q_q_dist, q_g_dist], axis=1),
       np.concatenate([q_g_dist.T, g_g_dist], axis=1)],
      axis=0) #3个矩阵的聚合,表示的是,全部图片各自与其他图片的距离。
    original_dist = np.power(original_dist, 2).astype(np.float32) #平方
    original_dist = 1. * original_dist / np.max(original_dist, axis=0)
    original_dist = np.transpose(original_dist)  #np.transpose转置



    V = np.zeros_like(original_dist).astype(np.float32)
    initial_rank = np.argsort(original_dist).astype(np.int32)

    query_num = q_g_dist.shape[0]
    gallery_num = q_g_dist.shape[0] + q_g_dist.shape[1]
    all_num = gallery_num

    for i in range(all_num):
        # k-reciprocal neighbors
        forward_k_neigh_index = initial_rank[i,:k1+1]  #取每一行,也就是每个query对应的排名前k个的index
        backward_k_neigh_index = initial_rank[forward_k_neigh_index,:k1+1]
        nw=np.where(backward_k_neigh_index == i)
        fi = nw[0]
        k_reciprocal_index = forward_k_neigh_index[fi]
        k_reciprocal_expansion_index = k_reciprocal_index
        for j in range(len(k_reciprocal_index)):
            candidate = k_reciprocal_index[j]
            candidate_forward_k_neigh_index = initial_rank[candidate,:int(np.around(k1/2.))+1]
            candidate_backward_k_neigh_index = initial_rank[candidate_forward_k_neigh_index,:int(np.around(k1/2.))+1]
            fi_candidate = np.where(candidate_backward_k_neigh_index == candidate)[0]
            candidate_k_reciprocal_index = candidate_forward_k_neigh_index[fi_candidate]
            if len(np.intersect1d(candidate_k_reciprocal_index,k_reciprocal_index))> 2./3*len(candidate_k_reciprocal_index): #numpy.intersect1d: Return the sorted, unique values that are in both of the input arrays.
                k_reciprocal_expansion_index = np.append(k_reciprocal_expansion_index,candidate_k_reciprocal_index)  #np.append(a)向a中添加b

        k_reciprocal_expansion_index = np.unique(k_reciprocal_expansion_index)
        weight = np.exp(-original_dist[i,k_reciprocal_expansion_index])
        V[i,k_reciprocal_expansion_index] = 1.*weight/np.sum(weight)
    original_dist = original_dist[:query_num,]
    if k2 != 1:
        V_qe = np.zeros_like(V,dtype=np.float32)
        for i in range(all_num):
            V__=V[initial_rank[i, :k2], :]
            V_qe[i,:] = np.mean(V__,axis=0)
        V = V_qe
        del V_qe
    del initial_rank
    invIndex = []   #找出V_qe矩阵每一列中不为0元素的行索引号。
    for i in range(gallery_num):
        invIndex.append(np.where(V[:,i] != 0)[0]) #找出V_qe矩阵每一列中不为0元素的行索引号。并把该行索引号加入到invIndex中。

    jaccard_dist = np.zeros_like(original_dist,dtype = np.float32)  #此时的original_dist已经是q*(q+g)大小了


    for i in range(query_num):#这个循环里面定义了indNonZero,indNonZero是以行为单位遍历V_qe中不为0的列,返回改行不为0元素的列索引。
        temp_min = np.zeros(shape=[1,gallery_num],dtype=np.float32)#gallery_num=all_num
        indNonZero = np.where(V[i,:] != 0)[0]
        indImages = []
        indImages = [invIndex[ind] for ind in indNonZero]
        for j in range(len(indNonZero)):
            print("-=-=-=-=-=-")
            print(i,j)
            print(";;",indImages)
            print(";;;",indNonZero)


            temp_min[0,indImages[j]] = temp_min[0,indImages[j]]+ np.minimum(V[i,indNonZero[j]],V[indImages[j],indNonZero[j]])
        jaccard_dist[i] = 1-temp_min/(2.-temp_min)

    final_dist = jaccard_dist*(1-lambda_value) + original_dist*lambda_value
    del original_dist
    del V
    del jaccard_dist
    final_dist = final_dist[:query_num,query_num:]
    return final_dist

逐步分析:

输入参数:

q_g_dist: query-gallery 距离矩阵, numpy array, shape [num_query, num_gallery]
q_q_dist: query-query 距离矩阵, numpy array, shape [num_query, num_query]
g_g_dist: gallery-gallery 距离矩阵, numpy array, shape [num_gallery, num_gallery]
k1, k2, lambda_value: parameters, the original paper is (k1=20, k2=6, lambda_value=0.3在market1501测试集上的设置)

        三个距离矩阵可以是欧氏距离计算得出的,可以是cos距离,区别不大应该

三个距离矩阵的聚合

 如果在market1501的测试集下,original_dist应该如下图

 

由于数据量太大了,我们使用一个很小的测试集便于调试

query如下:

gallery如下:

函数参数也调的小一点,k1=3,k2=2

 拿一个训练的差不多的模型跑一下,得到的original_dist:(蓝色线划分了一下区域),此时矩阵是对称的

 

先对每个数做平方np.power()

然后做归一化到[0,1],找出每一列的最大值,然后将这一列的每一个元素除以最大值。

最后做一个转置(不知道为啥转置。。??)

得到的矩阵为:

 注:axis=0

 

 

 初始化矩阵V(全零填充),对应论文中的

然后将原始的距离矩阵转换为rank排序,np.argsort不指定axis时默认为-1,即最后一个维度,original_dist是二维矩阵,最后一个维度即axis=1,在 ‘行’ 方向上进行排序。

得到的矩阵为:

然后就到了一个大的for循环,生成k-reciprocal neighbors以及对V进行赋值

先看前几行 

forward_k_niegh_index: initilal rank第i行的前k1个,此时K1=3,则取下面这些范围

 i=0时,forward_k_neigh_index=【0,4,2,3】

则backward_k_neigh_index为:

  即

 

 在列方向找包含i的,np.where:有查询的使用方法,返回符合条件的位置的索引,返回二维数组【0】是列方向,【1】是行方向的索引

nw的数值如下:

取nw【0】,得到fi=【0,1,2,3】

然后

 这两句为论文核心,把互为neibor的取出来

然后再是一个for子循环

 做的是这部分工作,即把R拓展成R*

遍历k_reciprocal_index

 这两句就是取 R(q,1/2k)

 

 对应:

 然后对所有结果去重

 对V进行赋值

 注:代码中 对weight进行了归一化

此时V中的数据如下:0代表不同的

 第一个大的for循环结束。。。。。。。。。。。。。。

 对origin_dist进行裁剪,只保留Query对应的地方

 然后

对应

 

 相似的向量的特征共享??

在initial_rank中找与i相似的前k2的V的值

在看一下initial_rank,取i(i=0时)的前k2(k2=2)个,得到  =0,4

 取V[[0,4],:]

 得到V__

然后np.mean求均值,赋值给V[i,:]

 之后

一个小的for循环,找出V矩阵每一!!!列!!!中不为0元素的行索引号。

 此时v中

接下来计算jaccard距离

初始化矩阵,大小为q*(q+g)

 定义了三个核心的变量

invIndex:V中每一列不为0的索引

indNonZero:V中每一行不为零的索引

indImages:V中不为零的行的索引,对应的列的不为零的索引

 然后一个小的子循环

 对应论文中

 

 最后dj(p,gi)

 其中做了个优化,max使用(2-min)替代了

原理是:

V做过归一化,即每一行加起来和为1:

 举个栗子,假设两个向量如下:

 

 两元素之和-min=max

最后一步:

 即按权重相加

 

 输出前进行裁剪

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值