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次方图像
基于之前的一些定义,交并集的计算可以转换为公式(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
最后一步:
即按权重相加
输出前进行裁剪