[论文笔记]PCB_RPP_reID分析

2020-07-22  本文已影响0人  祁晏晏

论文和思路理解

行人重识别(Person Re-ID)【四】:论文笔记——Beyond Part Models: Person Retrieval with Refined Part Pooling 这篇写的很棒

简单地梳理一下主要思想

PCB(Part-based Convolutional Baseline)

image

这是个基础baseline,backbone使用的是resnet50, 到global average pooling之前完全一样。

  1. 假定输入是H,经过backbone之后得到三维的Tensor T
  2. 定义T中每个channel axis为column vectors, 即f
  3. 将T分成P个horizontal stripes(水平块),对p个horizontal stripes做Average pooling得到p个局部特征g
  4. 用1*1 conv降低g的维度到256维,为新的局部特征h
  5. 每个h经过一个全+分类器进行训练(分类器实质上是p个n分类的softmax, n为训练集的ID数目)

特点:减少降采样

训练:loss为交叉熵损失函数的sum(p个分类器,p个loss)

测试:串联向量g和h作为特征表示

Part内部信息不一致

这部分类似triplet loss或者像cornernet中的embedding

同一个part中的f应相似,不同part间应有差异

因此我们需要训练PCB到收敛后,测量f和g的相似程度(余弦距离),找到离每一个f最近的part,根据f的距离情况进一步改进

image

RPP(Rifined Part Pooling)

目标:解决上一模块说的要改进的东西,通过f和与其最相似的part来对齐所有f,让f回到自己属于的part去

具体做法:

  1. 测量f与每一个part的相似程度:S(f \leftrightarrow P_i)

  2. f根据S重采样到最相似的part上

  3. 重复1、2直到收敛

  4. 用p个classifier去预测 S(f \leftrightarrow P_i)的值,预测公式为
    S(f \leftrightarrow P_i) = softmax(w_i^Tf) = \frac{exp(w_i^Tf)}{\sum_{j=1}^{n}exp(w_j^Tf)}

PCB部分的第三步即可用上述RPP取代,即RPP后,得到每个part对应的attention map权值,后续的降采样等步骤一致。

如何训练RPP中calssifier的权重

这个权重就是上一部分第4步中的w

  1. 将图像等分,训练PCB至收敛
  2. 将T后面的Average pooling替换为一个p分类的part classifier
  3. 固定PCB其它层的参数,只训练part classifier至收敛
  4. 放开全部参数,fine tune

代码阅读

看了好几份代码,在最后检测部分都有点含糊。找到一份好像有点写清楚了,打算分析一下。

test.py

代码逻辑

代码知识

image

源码分析

# ---------------------- Evaluation ----------------------
# query:待查询输入,gallery:候选行人框
# query和gallery的features, labels, cams均为已知信息,features是通过网络提取到的特征
def evaluate(query_features, query_labels, query_cams, gallery_features, gallery_labels, gallery_cams):
    """Evaluate the CMC and mAP
    Arguments:
        query_features {np.ndarray of size NxC} -- Features of probe images
        query_labels {np.ndarray of query size N} -- Labels of probe images
        query_cams {np.ndarray of query size N} -- Cameras of probe images
        gallery_features {np.ndarray of size N'xC} -- Features of gallery images
        gallery_labels {np.ndarray of gallery size N'} -- Lables of gallery images
        gallery_cams {np.ndarray of gallery size N'} -- Cameras of gallery images
    Returns:
        (torch.IntTensor, float) -- CMC list, mAP
    """

    CMC = torch.IntTensor(len(gallery_labels)).zero_()
    AP = 0
    sorted_index_list, sorted_y_true_list, junk_index_list = [], [], []

    for i in range(len(query_labels)):
        # 对每一个要查询的图像,获取一下信息
        query_feature = query_features[i]
        query_label = query_labels[i]
        query_cam = query_cams[i]

        # Prediction score
        # 对要查询的图像获取一个score,但这个score为什么能这么获取没搞明白
        score = np.dot(gallery_features, query_feature)
        
        # 去gallery_labels里找和query_label相对应的数据索引(即找到这些数据在哪儿)
        match_query_index = np.argwhere(gallery_labels == query_label)
        # 去gallery_cams里找和query_cam相对应的数据索引(即找到这些数据在哪儿)
        same_camera_index = np.argwhere(gallery_cams == query_cam)

        # Positive index is the matched indexs at different camera i.e. the desired result
        # 在match_query_index中但不在same_camera_index中的已排序的唯一值
        positive_index = np.setdiff1d(
            match_query_index, same_camera_index, assume_unique=True)

        # Junk index is the indexs at the same camera or the unlabeled image
        # Junk index是同一camera下同一label的照片或未标注的图像,不要的索引
        junk_index = np.append(
            np.argwhere(gallery_labels == -1),
            np.intersect1d(match_query_index, same_camera_index))  # .flatten()

        index = np.arange(len(gallery_labels))
        # Remove all the junk indexs
        # 从gallery中去掉junk index的元素
        sufficient_index = np.setdiff1d(index, junk_index)

        # compute AP
        # y_true返回一个结果,判断sufficient_index中的每一个元素是否在positive_index中
        y_true = np.in1d(sufficient_index, positive_index)
        # y_score是根据特征算到的结果
        y_score = score[sufficient_index]
        # 用库函数算AP
        AP += average_precision_score(y_true, y_score)

        # Compute CMC
        # Sort the sufficient index by their scores, from large to small
        # sorted_index: y_score从大到小排序后得到的索引
        sorted_index = np.argsort(y_score)[::-1]
        # 与sorted_index排序一致的正负样本真实情况 
        sorted_y_true = y_true[sorted_index]
        # 得知sorted_y_true中哪些是正样本
        match_index = np.argwhere(sorted_y_true == True)
        
        # 累计CMC的值
        if match_index.size > 0:
            first_match_index = match_index.flatten()[0]
            CMC[first_match_index:] += 1

        # keep with junk index, for using the index to show the img from dataloader
        all_sorted_index = np.argsort(score)[::-1]
        all_y_true = np.in1d(index, match_query_index)
        all_sorted_y_true = all_y_true[all_sorted_index]

        sorted_index_list.append(all_sorted_index)
        sorted_y_true_list.append(all_sorted_y_true)
        junk_index_list.append(junk_index)

    CMC = CMC.float()
    CMC = CMC / len(query_labels) * 100  # average CMC
    mAP = AP / len(query_labels) * 100

    return CMC, mAP, (sorted_index_list, sorted_y_true_list, junk_index_list)
上一篇下一篇

猜你喜欢

热点阅读