由PyRetri浅谈基于深度学习的图像检索

前言

最近发现face++开源了一个图像检索和行人重识别的基于深度学习的软件包,最近一段时间也一直在接触图像检索相关的东西。故借此机会,对里面涉及的一些常用的方法模块进行一个简单的介绍总结,便于日后回顾。

PyRetri是什么?

PyRetri是由旷视开源的第一个基于深度学习的无监督图像检索库,它是基于PyTorch进行开发的python库。

paper: https://128.84.21.199/abs/2005.02154

GitHub: https://github.com/PyRetri/PyRetri

PyRetri将基于深度学习的无监督CBIR分为了三个重要的部分:特征提取(feature extraction)、索引(indexing)和评估(evaluation)。其主要的整体框架图如下:

PyRetri

图像检索常用公开数据集

图像检索并不是一个新兴的研究方向,现阶段针对不同场景以及需求的图像检索都或多或少有数据集公开。强烈推荐一个总结了常用的计算机视觉数据集列表的网站:Yet Another Computer Vision Index To Datasets (YACVID)

下面简单介绍一些常见的图像检索数据集概况,针对实例检索,往往很多场景也都会使用大规模的分类数据集。

收集方式: 通过关键字在Flickr上查询下载图片 每一个query对应good、OK、junk list文件,作为ground truth

规模: total :5062 query: 55 (11*5)

特点: 除提供.jpg格式图片外,还提供sift描述符的压缩二进制文件等

paper: Object retrieval with large vocabularies and fast spatial matching(CVPR 2007)

收集方式: 与Oxford5K相同(查询关键字不同)

规模: total :6412 query: 55(11*5)

特点: 仅提供.jpg格式图片

paper: Lost in Quantization: Improving Particular Object Retrieval in Large Scale Image Databases(CVPR 2008)

收集方式: 基于Oxford5K

规模: query 50->70

特点: 修复原来数据标注问题 增加数据 标签细化

paper: Revisiting Oxford and Paris: Large-Scale Image Retrieval Benchmarking(CVPR 2018)

收集方式: 基于Paris6K

规模: query 50->70

特点: 修复原来数据标注问题 增加数据 标签细化

paper: Revisiting Oxford and Paris: Large-Scale Image Retrieval Benchmarking(CVPR 2018)

规模: total :200万张图片 3万个独特地标

特点: kaggle 比赛

paper: Large-Scale Image Retrieval with Attentive Deep Local Features( ICCV 2017)

收集方式: 摄影社区众包进行实例标注

规模: total:超过500万张图像 超过20万个不同的地标

特点: kaggle 比赛

paper: Detect-to-Retrieve: Efficient Regional Aggregation for Image Search (CVPR 2019)

收集方式: web 图片和3D模型(点云)

规模: total :45180

特点: 25个地标 ;45,180个数据库图像(每个地标1.4K-2K);10,000个正面query用于评估(每个标志400个);3D模型中约270万个3D点(每个地标29K-223K); 约有5800万个SIFT

paper: 3D Visual Phrases for Landmark Recognition". in Proc. of the 25th IEEE Conference on Computer Vision and Pattern Recognition (CVPR 2012)

收集方式: 数据集是从Flickr和Panoramio收集的地标图像。图像具有“自然”分布,数据集非常具有挑战性,因为存在重复和近似重复,以及大量不相关的图像,例如派对,宠物等的照片

规模: total :501,356

特点: 提供79个地标建筑的94303张图片clustering ground truth

paper: Discovering Details and Scene Structure with Hierarchical Iconoid Shift(ICCV 2013)

收集方式: ZuBuD+ 是对ZuBuD的扩展,主要是增加了测试图片 评估方式:TOP5

规模: total :1005 training data/1005 test_balance data

特点: 每个图像有201个建筑物,每个视图有五个视图,提供训练数据、相同数量的测试数据、ground truth file 和 用于评估的python程序

paper: A location-aware embedding technique for accurate landmark recognition(2017)

收集方式:是 CUB-200数据集 的扩展

规模:total : 11788, 200类别

特点:每个图片都有注释 15 Part Locations, 312 Binary Attributes, 1 Bounding Box 图片与ImageNet重叠 常用于细粒度分类、检索等

paper: Wah C., Branson S., Welinder P., Perona P., Belongie S. “The Caltech-UCSD Birds-200-2011 Dataset.” Computation & Neural Systems Technical Report, CNS-TR-2011-001.

规模:total :15620 67个室内场景 train set :67*80 test set :67*20

特点:图片有annotation (实例分割) LabelMe格式

paper: A. Quattoni, and A.Torralba. Recognizing Indoor Scenes. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2009.

规模:101个类别,每类40-80张

特点:通用的分类的数据集(有annotation),注:往往分类数据集也会用来实例检索

paper: Learning generative visual models from few training examples: an incremental Bayesian approach tested on 101 object categories. CVPR 2004

行人重识别常用数据集

行人重识别(person re-id)是图像检索的子领域,近几年从各大计算机视觉会议的论文收录情况也可以看出,行人重识别已经成为了计算机视觉领域的研究热点。无论是从社会意义还是从商业角度上来看,行人重识别都具有一定的研究意义。因为PyRetri提到了其也适用于行人重识别任务,那么我们也简单的介绍一下行人重识别常用的数据集。

针对行人重识别数据集,推荐网站: awesome-reid-dataset

  • Market-1501

    Market-1501 数据集最早是 2015 年 公布的, 是至今使用广泛的大规模行人重识别数据集之一。该数据集使用 6 个摄像头(5 个高分辨率,一个低分辨率)在清华大学超市前收集的。为了保证跨摄像头搜索,不同摄像头之间存在重叠,并且保证每一个被标注的行人至少来自两个摄像头。更具体地,数据集包括 1501 个行人共 32668 张图片。网上所提供的公开数据集已将 32668张图片分为了训练集和测试集,其中测试集文件夹中包含 19732 张图片,训练集文件夹中包含 12936 张图片。另外将每个摄像头随机选出的一张 ID 行人共3368 张图片(750 个 ID,每个 ID 至多 6 张)作为待选集单独分开。
    paper: Scalable Person Re-identification: A Benchmark (ICCV 2016)

  • CUHK01

    CUHK01 数据集由香港中文大学研究团队公开, 相关信息首次发表于2012 年的亚洲计算机视觉会议上。该数据集采自一对不相交的相机(即具有两个摄像视角)在大学校园收集的。一个视角主要捕获行人的正面和背面,另一个视角主要捕获行人的侧面。该数据集共收集了 971 个行人的 3884 张图片,每个行人包含来自两个视角的共4 张图片。由于采集环境和设备的关系以及采用手工裁剪标注的形式致使CUHK01 数据集行人图片的质量相对较好。
    paper: Human re-identification with transferred metric learning (ACCV 2012)

  • CUHK03

    2014 年起深度学习进入行人重识别领域,为解决之前数据集太小无法满足深度学习训练的问题,香港中文大学的 Li 等人建立了较大规模的行人重识别数据集,名为 CUHK03,此前,他们已经建立了 CUHK01 和 CUHK02 数据集。CUHK03数据集由 5对不同的监控视角拍摄,包含 1360个行人的 13164张图片。此外, CUHK03 数据集还具有以下几个特点: 1) CUHK03 除了手工裁剪的图片以外,还有使用检测算法进行检测裁剪的图片。这使得 CUHK03 的数据集更接近于真实的环境,错位、遮挡、部位缺失都是常见的情况。 2) CUHK03 的样本是通过复杂的交叉视图混合变换采集的。 3)由于数据集中的图像获取是经过了几个月的时间,导致不同图片间存在着光照的不同。这些都使得 CUHK03 数据集上的行人重识别任务更具有挑战性。
    paper: DeepReID: Deep Filter Pairing Neural Network for Person Re-identification (CVPR 2014)

  • DukeMTMC-reID

    DukeMTMC数据集是一个大规模的多目标多摄像机行人跟踪数据集。数据集包含了 85 分钟的高分辨率视频,收集了来自8 个不同的摄像头, 超过 2700 个不同行人数据信息。基于该原始数据集,两个行人重识别扩展数据集 DukeMTMC-reID 和 DukeMTMC4ReID 分别被创建。其主要区别在于行人边界框的生成方式,前者采用的是直接手工标注,后者则采用 Doppia 作为检测器进行目标检测标注。DukeMTMC-reID作为DukeMTMC 数据集的行人重识别子集,共有36,411 张图像(原始视频中每 120 帧采样一张)。数据集中共包括 1812 个行人,其中 1404 个行人在至少两个摄像头以上出现,余下的 408 个行人只出现在单一 摄像头下。故在将该数据集按照 Market-1501 数据集格式组织时,随机采样 702个行人图片作为训练集,另外 702 个行人图片作为测试集。 查询集(query) 中为 1404 个行人 ID 每个摄像头下的一张图片,剩下的所有图片作为搜索库(gallery),其中也包括 408 个行人的图片作为干扰信息项。最终 DukeMTMC-reID 的数据构成训练集 16522 张、查询集 2228 张图片、 以及搜素库 17661 张图片。其命名规则与 Market-1501 数据集图片命名规则相同。
    paper: Performance Measures and a Data Set for Multi-target,Multi-camera Tracking (ECCV 2016)

特征提取 feature extraction

图片预处理方法

无论是图像检索还是其他计算机视觉相关的任务都会多多少少涉及到图片预处理相关的方法,同样的图片预处理对于图像相关任务也有这重要的作用,往往简单的进行图片增强就可以提高网络的performance。因为由PyRetri而起,这边也主要介绍一下,PyRetri提供的预处理方法以及探索一下其在代码实现上有什么可以借鉴的亮点之处。

  • 缩放

    1)DirectResize:

    功能:按指定大小resize

    参数:目标size(w,h)\ 插值方式

    2)PadResize:

    功能:指定长边的长度,短边通过填充到相同大小resize

    参数:长边长度\padding的像素值\插值方式

    3)ShorterResize

    功能:指定短边的长度,并保持原来图像宽高比进行resize

    参数:短边长度\插值方式

  • 裁剪

    1)CenterCrop:

    功能:从图片中心按指定大小进行裁剪

    参数:指定的图片大小

    2)TenCrop:

    功能:按指定大小将原图进行上下左右中心裁剪,并进行翻转(默认水平)得到10张图片

    参数:指定的图片大小

  • 翻转

    TwoFlip:

    功能:返回原图和水平翻转后的图片

    参数:图片

  • 其他

    1)ToTensor:

    功能:将图片转为Tensor

    注:PIL的图片size是(w,h) 转为的tensor是(c,h,w)

    2)ToCaffeTensor:

    功能:将图片转为适应在Caffe中预训练的模型

    注:在PyTorch中图片的组织方式是PIL——RGB,而在Caffe中图片的组织方式是OpenCV——BGR

    3)Normalize:

    功能:使用特定的均值和方差来normalize tensor,减均值除方差

    参数:指定的均值和方差

上述是PyRetri中提供的图片预处理的方法,简单看了代码基本上在torchvision.transforms的基础上进行的二次开发。从更大的层面看,在计算机视觉中的图片增强预处理方法远远不止上述所述。常见的数据增强方法概括上来说,包括:翻转、旋转、缩放、裁剪、平移、加噪声、随机擦除、颜色亮度对比度方面进行变化等等,更一般地,针对增强还可以采用一些生成方法来进行图片生成。

基础网络Backbone

在很多计算机视觉任务中都会使用源于分类的基本网络骨架,在此基础上再进行一些特定任务的扩展与延伸。发展至今,经典网络从最开始的AlexNet、VGG等到现在的通过NAS涌现的一些网络(例如EfficientNet),可选择的网络很多。对于基于深度学习的图像检索,在特征提取部分往往也是采用在数据集上训练好的经典网络,然后抽取某层的输出(最开始是全连接层、现在一般是中间层特征然后在进行融合)。PyRetri库中提供了VGG16和ResNet50(后续应该会增加更多),在其进行的性能试验中也是通过采用在不同的数据上面训练的这两个模型的比较。

经典网络介绍: https://blog.csdn.net/u014448054/article/details/102612195

PyRetri 提供的MODEL ZOO: https://github.com/PyRetri/PyRetri/blob/master/docs/MODEL_ZOO.md

特征融合Aggregation

深度学习最早应用到图像检索的特征提取中时,最常使用的是全连接层输出的特征描述。然而选用上层的语义层其实是不利于object retrieval,因为上层的语义层丢失了object的空间信息,并且多篇论文都从实验的角度说明了选取中间层的特征更利于object retrieval。

实际上,在选取中间层来表达特征的过程中,我们可以去掉全连接层,从而使得我们可以摆脱掉输入图像尺寸约束(比如224*224)的约束,而保持原图大小的输入。

通常,图像分辨率越大,对于分类、检测等图像任务是越有利的。因而,从这一方面讲,选取上层的全连接层作为特征,并不利于我们的object retrieval任务。一种可能的猜想是,上层全连接层的语义特征,应该更适合做全局的相似。

虽然中间层更适合于做object retrieval,但是在选用中间层的feature map作为raw feature的时候,我们面临的一个主要问题是:如何将3d的tensor转成一个有效的向量特征表示? 这就涉及到了一些特征融合处理的方式,在深度学习中,一般会采用池化的方式进行特征融合,接下来会详细地介绍一些常用的特征融合方法。

  • 最大池化 max pooling

    MAX pooling指的是对于每一个channel(假设有N个channel),将该channel的feature map的像素值选取其中最大值作为该channel的代表,从而得到一个N维向量表示。

  • 求和池化 sum pooling

    SUM pooling指的是对于每一个channel(假设有N个channel),将该channel的feature map的所有像素值求和,这样每一个channel得到一个实数值,N个channel最终会得到一个长度为N的向量。

  • 全局平均池化 global average pooling

    AVG pooling指的是对于每一个channel(假设有N个channel),将**该channel的feature map的所有像素值求和在除以像素个数求平均值,**这样每一个channel得到一个实数值,N个channel最终会得到一个长度为N的向量。

# feature.shape (b,c,h,w)
feature = feature.mean(dim=3).mean(dim=2)
  • SPoC:sum-pooled convolutional

    这种特征融合方式在sum pooling的基础上面考虑到了ROI的思想。具体地,针对深度特征向量,假定坐标点的空间位置与权重服从二维高斯分布,中心点为 μ \mu μ,方差 σ \sigma σ为中心点距离最近边界的距离的三分之一。据此,通过特征点的空间坐标求出权重矩阵,在进行加权求和来进行特征融合。

# SPoC核心代码
h, w = feature.shape[2:]
sigma = min(h, w) / 2.0 / 3.0
x = torch.Tensor(range(w))
y = torch.Tensor(range(h))[:, None]
spatial_weight = torch.exp(-((x - (w - 1) / 2.0) ** 2 + (y - (h - 1) / 2.0) ** 2) / 2.0 / (sigma ** 2)) #求权重
feature = (feature * spatial_weight).sum(dim=(2, 3)) #加权求和
  • CROW pooling : Cross-dimensional Weighting 跨维加权池化
    CROW pooling 也是基于ROI的思想提供的池化方法,通过构建Spatial权重和Channel权重,其能够在一定程度上加大感兴趣区域的权重,降低非物体区域的权重。

    1)针对Spatial Weight,考虑到通过卷积滤波,响应强的地方一般都是物体的边缘等,因而将多个通道相加求和后,那些非零且响应大的区域,也一般都是物体所在的区域,因而我们可以将它作为feature map的权重。计算公式如下:
    S i j = ( S i j ′ ( ∑ m , n S m , n ′ a ) 1 a ) 1 b S_{ij}=(\frac{S'_{ij}}{(\sum_{m,n}S_{m,n}^{'a})^{\frac{1}{a}}})^{\frac{1}{b}} Sij=((m,nSm,na)a1Sij)b1

    2)针对Channel Weight,借用了IDF权重的思想。比如某一个channel,其feature map每个像素值都是非零的,且都比较大,从视觉上看上去,白色区域占据了整个feature map,我们可以想到,这个channel的feature map是不利于我们去定位物体的区域的,因此我们需要降低这个channel的权重,而对于白色区域占feature map面积很小的channel,我们认为它对于定位物体包含有很大的信息,因此应该加大这种channel的权重。简单的说,计算每一个通道的非零数据的比例,然后对所有通道非零的比例求和,某个通道非零数据比例除以总非零数据比例的log就是Channel Weight,和IDF思想不谋而合。

# CROW Pool 核心代码
# Spatial weight 计算
spatial_weight = feature.sum(dim=1, keepdims=True)
z = (spatial_weight ** spatial_a).sum(dim=(2, 3), keepdims=True)
z = z ** (1.0 / spatial_a)
spatial_weight = (spatial_weight / z) ** (1.0 / spatial_b)

# Channel weight 计算
c, w, h = feature.shape[1:]
nonzeros = (feature!=0).float().sum(dim=(2, 3)) / 1.0 / (w * h) + 1e-6
channel_weight = torch.log(nonzeros.sum(dim=1, keepdims=True) / nonzeros)

# 联合计算融合特征
feature = feature * spatial_weight
feature = feature.sum(dim=(2, 3))
feature = feature * channel_weight
  • MOP Pooling : Multi-Scale Orderless Pooling 多尺度无序池化

    此pooling方法未在PyRetri中实现,但是在介绍R-MAC之前有必要简单的了解一下这种方法。其算法主要过程如下:

    对图片分为三级尺度,将输入图片resize到256*256:

    1)将256*256大小的图片输入到网络中,在全连接层后得到1*4096D向量

    2)在256*256的图片上以大小128*128,步长为32进行块采样,得到5*5个128*128的patch,对每一个patch,上采样到256*256并输入网络中,得到1*4096D向量

    3)在256*256的图片上以大小64*64,步长为32进行块采样,得到7*7个64*64的patch,对每一个patch,上采样到256*256并输入网络中,得到1*4096D向量

    4)对所得到的向量进行PCA降维至500D

    5)将降维后的向量使用VLAD进行融合,VLAD聚类中心为100,融合后的特征为500*100D

    6)对融合后的特征进行PCA降维至4096

  • R-MAC: Regional Maximum activation of convolutions
    和MOP有些相似地,R-MAC也考虑到了图片的局部区域特征,而不同于在图片上进行块采样的方式,R-MAC采用在feature map进行滑动窗口的方式来体现Local feature的思想,此外不同于使用VLAD,R-MAC则处理得更简单,直接将local特征相加得到最终的global特征。

R-MAC

图中x代表滑窗中心,以上述三种方式在feature map上进行滑窗采样,共20个local feature,每一个子滑窗,采用max pooling的方式进行区域融合。20个local feature和一个global feature(整个feature map + max pooling) 进行求和,得到最后的特征向量。另外,也可以将20个local feature求和合并并与global feature串联拼接。

# R-MAC实现代码
def rmac(x, L=3, eps=1e-6):
    ovr = 0.4 # desired overlap of neighboring regions
    steps = torch.Tensor([2, 3, 4, 5, 6, 7]) # possible regions for the long dimension
    W = x.size(3)
    H = x.size(2)
    w = min(W, H)
    w2 = math.floor(w/2.0 - 1)
    b = (max(H, W)-w)/(steps-1)
    (tmp, idx) = torch.min(torch.abs(((w**2 - w*b)/w**2)-ovr), 0) # steps(idx) regions for long dimension
 
    # region overplus per dimension
    Wd = 0;
    Hd = 0;
    if H < W: 
        Wd = idx.item() + 1
    elif H > W:
        Hd = idx.item() + 1
 
    v = F.max_pool2d(x, (x.size(-2), x.size(-1)))
    v = v / (torch.norm(v, p=2, dim=1, keepdim=True) + eps).expand_as(v)
 
    for l in range(1, L+1):
        wl = math.floor(2*w/(l+1))
        wl2 = math.floor(wl/2 - 1)
 
        if l+Wd == 1:
            b = 0
        else:
            b = (W-wl)/(l+Wd-1)
        cenW = torch.floor(wl2 + torch.Tensor(range(l-1+Wd+1))*b) - wl2 # center coordinates
        if l+Hd == 1:
            b = 0
        else:
            b = (H-wl)/(l+Hd-1)
        cenH = torch.floor(wl2 + torch.Tensor(range(l-1+Hd+1))*b) - wl2 # center coordinates
             
        for i_ in cenH.tolist():
            for j_ in cenW.tolist():
                if wl == 0:
                    continue
                R = x[:,:,(int(i_)+torch.Tensor(range(wl)).long()).tolist(),:]
                R = R[:,:,:,(int(j_)+torch.Tensor(range(wl)).long()).tolist()]
                vt = F.max_pool2d(R, (R.size(-2), R.size(-1)))
                vt = vt / (torch.norm(vt, p=2, dim=1, keepdim=True) + eps).expand_as(vt)
                v += vt
    return v
  • SCDA:Selective Convolutional Descriptor Aggregation

    仍是源于希望针对细粒度检索任务,能够在无监督的情况下对物体进行定位。基于对卷积层输出特征响应情况的观察。SCDA使用以下方式,进行特征融合:首先在channel方向进行求和即将H*W*C的tensor变成H*W*1大小,然后将各个值与通道方向的均值进行对比,如果大于就设为1否则为0,生成一个H*W的mask。针对这个mask可以使用BFS的方式找到最大的连通区域,近一步得到只含有最大连通域的mask。然后将原始特征与mask进行相乘,得到的结果在H*W方向求均值和最大值,将两者连接得到最终融合后的特征向量2C*1。

    具体实现代码可以看PyRetri中的scda.py

  • PWA:Part-Based Weighting Aggregation

    此融合方法来自AAAI2018的一篇无监督图像检索的文章,与一些融合方法相似,这种融合方式也是出于在无监督的情况下,可以通过融合的方式来强化各个组件的特征。具体地,在训练阶段,将feature map在channel方向按照方差进行从大到小进行排序并取前N个,得到N*H*W的probabilistic proposals。在测试阶段,针对每一张图片的输出feature C*H*W与N*H*W进行点乘求和(“加权求和”)得到N*C,近一步得到1*NC的向量,在后处理阶段可以近一步对该向量采用降维等方式降低维度。

  • GeM:Generalized-mean pooling

    GeM是更一般的池化方式,计算公式如下:
    f ( g ) = [ f 1 ( g ) … f k ( g ) … f K ( g ) ] T f^{(g)} = [f_{1}^{(g)}…f_{k}^{(g)}…f_{K}^{(g)}]^{T} f(g)=[f1(g)fk(g)fK(g)]T

    f k ( g ) = ( 1 ∣ X k ∣ ∑ x ∈ X k x p k ) 1 p k f_{k}^{(g)} = (\frac{1}{|X_{k}|}\sum_{x\in X_{k}}x^{p_{k}})^{\frac{1}{p_{k}}} fk(g)=(Xk1xXkxpk)pk1

    p k p_{k} pk趋近于正无穷时,上式相当于max pooling。当 p k p_{k} pk=1时,上式相当于avg pool,可以说Generalized-mean pooling是更普遍的池化公式,理论上在每一层上,我们可以使用不用的 p k p_{k} pk,来进行不一样的池化操作。

    实验中发现, p k p_{k} pk可以固定为一个值,并且效果也不差。

def gem(feature,p = 3,eps = 1e-6):
	feature = feature.clamp(min=eps) ** p
	h, w = feature.shape[2:]
	feature = feature.sum(dim=(2, 3)) * 1.0 / w / h
	feature = feature ** (1.0 / p)

对于Object Retrieval,在使用CNN提取特征的时候,我们所希望的是在有物体的区域进行特征提取,就像提取局部特征比如SIFT特征构BoW、VLAD、FV向量的时候,可以采用MSER、Saliency等手段将SIFT特征限制在有物体的区域。同样基于这样一种思路,在采用CNN做Object Retrieval的时候,我们有两种方式来更细化Object Retrieval的特征:一种是先做物体检测然后在检测到的物体区域里面提取CNN特征;另一种方式是我们通过某种权重自适应的方式,加大有物体区域的权重,而减小非物体区域的权重。在上述所介绍的方法中,我们也可以看到很多特征融合池化方法所提出的初衷和思想也是基于这一思路的。

索引indexing

PyRetri中并没有针对大规模数据的向量索引的建立,有时间会根据milvus或faiss来分析:基于树的搜索算法;基于哈希的空间划分法;向量量化的编码算法;基于图的搜索方法等。

维度处理 Dimension Process

  • L2正则化
    L2范数归一化放在维度处理这边多少有些不合适,但是其确实是图像检索中常用到的对特征向量进行处理的一种方式。其计算方式就是特征向量除以其自身的L2范数,经过L2范数归一化后,一组向量的欧式距离和它们的余弦相似度可以等价。
def l2n(x, eps=1e-6):
	return x / (torch.norm(x, p=2, dim=1, keepdim=True) + eps).expand_as(x)
  • PCA: Principal Component Analysis 主成成分分析
    PCA是一种常用的数据分析方法。PCA通过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分量,常用于高维数据的降维。涉及到的数学知识点: 向量表示,内积,基变换,协方差,特征值,特征向量,协方差矩阵对角化。

    PCA过程:1.输入矩阵X行零均值化 2.求出协方差矩阵 3.求出协方差矩阵的特征值及对应的特征向量 (求解特征方程的解)4.将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵U 5.X’=UX即为降维到k维后的数据。

    代码实现上,可以直接使用sklearn.decomposition中的PCA进行处理。PyRetri中还提到了part-PCA的概念,就是不是针对整个图片feature map来进行pca而是将feature map分成几个part,对每个part进行PCA操作再将处理后的特征拼接在一起。

    写给自己:PCA是无监督的方法,但是注意了解训练的含义。

  • SVD:Singular Value Decomposition 奇异值分解
    推荐阅读:奇异值分解(SVD)原理与在降维中的应用
    同样的代码实现可使用sklearn.decomposition中的SVD进行处理。

特征增强 Feature Enhance

PyRetri中介绍的特征增强的方法源自文章:Three things everyone should know to improve object retrieval

主要思想就是将特征中的每个值都用它自身的值和K近邻点的值得加权求和来代替。

距离度量 Distance Metric

谈到距离的度量离不开数据格式,在检索中常出现的数据格式就是浮点型和二值型,而针对浮点型数据距离计算有欧式距离、余弦相似度等,而对于二值型数据常采用Jaccard距离、Hamming距离等。

  • 欧式距离(L2)
    欧氏距离计算的是两点之间最短的直线距离。欧氏距离的计算公式为:
    d ( a , b ) = d ( b , a ) = ∑ i = 1 n ( b i − a i ) 2 d(a,b) = d(b,a) = \sqrt{\sum_{i=1}^{n}(b_{i}-a_{i})^{2}} d(a,b)=d(b,a)=i=1n(biai)2
    其中 a = (a1, a2,…, an) 和 b = (b1, b2,…, bn) 是 n 维欧氏空间中的两个点。欧氏距离是最常用的距离计算方式之一,应用广泛,适合数据完整,数据量纲统一的场景。

  • 内积 (IP)
    内积计算两条向量之间的夹角余弦,并返回相应的点积。内积距离的计算公式为:
    c o s θ = A ⋅ B ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ B ∣ ∣ cos\theta = \frac{A\cdot B}{||A||\cdot||B||} cosθ=ABAB
    假设有 A 和 B 两条向量,则 ||A|| 与 ||B|| 分别代表 A 和 B 归一化后的值。cosθ 代表 A 与 B 之间的余弦夹角。内积更适合计算向量的方向而不是大小。

    注:在向量归一化之后,内积与余弦相似度等价。

  • 杰卡德距离
    杰卡德相似系数计算数据集之间的相似度,计算方式为:数据集交集的个数和并集个数的比值。计算公式可以表示为:
    J ( A , B ) = ∣ A ∩ B ∣ ∣ A ∣ + ∣ B ∣ − ∣ A ∩ B ∣ J(A,B) = \frac{|A\cap B|}{|A|+|B|-|A\cap B|} J(A,B)=A+BABAB
    杰卡德距离是用来衡量两个数据集差异性的一种指标,被定义为 1 减去杰卡德相似系数。对于二值变量,杰卡德距离等价于谷本系数。
    d j ( A , B ) = 1 − J ( A , B ) = ∣ A ∪ B ∣ − ∣ A ∩ B ∣ ∣ A ∪ B ∣ d_{j}(A,B) = 1-J(A,B)=\frac{|A\cup B|-|A\cap B|}{|A\cup B|} dj(A,B)=1J(A,B)=ABABAB
    杰卡德距离适合字符串相似性度量。

  • 汉明距离
    汉明距离计算二进制字符串之间的距离。两个等长字符串之间的汉明距离定义为将其中一个变为另外一个所需要作的最小替换次数。

    比如,假设有两条字符串 1101 1001 和 1001 1101。比较时,如果字符相同用 0 表示,如果字符不同则用 1 表示。

    11011001 ⊕ 10011101 = 01000100
    所以以上两条字符串之间的汉明距离为 2。

重排序 Re-Rank

在大规模索引中,最简单的重排序,会使用原始的未经压缩和索引过的特征在返回的小范围结果集的重新搜索排序一遍。 另外常用的重排序方法还有扩展查询等。

  • QE:query expansion 扩展查询
    扩展查询的思想很简单,在图像检索中就是将待查图片的特征和搜索出来的topk的图片特征取均值,再次进行一次查询。

  • K-reciprocal:
    写在开头,这种re-rank方法在大规模的数据集上是时间上面的巨大牺牲。
    首先解释什么是k-reciprocal nearest neighbors,简单地说要满足两个图片都在对方的knn列表中。为了应对光照、光照、姿态、视角等一系列变化,正样本可能会被排除到k-nn列表外情况,论文中定义了一个更鲁棒的k-rnn的集合。约束条件如下:

    R ∗ ( p , k ) ← R ( p , k ) ∩ R ( q , 1 2 k R^{*}(p,k) \leftarrow R(p,k)\cap R(q,\frac{1}{2}k R(p,k)R(p,k)R(q,21k

    s . t . ∣ R ( p , k ) ∩ R ( q , 1 2 k ) ∣ ≥ 2 3 ∣ R ( q , 1 2 k ) , ∀ q ∈ R ( p , k ) s.t. |R(p,k)\cap R(q,\frac{1}{2}k)|\geq \frac{2}{3}|R(q,\frac{1}{2}k),\forall q \in R(p,k) s.t.R(p,k)R(q,21k)32R(q,21k),qR(p,k)

    作者认为,假如两张图片相似,那么它们的k-rnn集合会重叠,即会有重复的样本。重复的样本越多,这两张图片就越相似。那么很自然地就想到用Jaccard Distance度量它们k-rnn集合的相似度。但是这样距离度量有一些缺点,比如说耗时间、将近邻样本中的每个都认为同等重要、鲁棒性不高。基于上述考虑,文章中将Jaccard距离进行了改造,将集合比较稳妥转化为了向量计算问题。具体说明和复杂度分析可以参考原论文。

    具体实现代码可以看PyRetri里面的k-reciprocal.py 也可以直接看论文的github。

评估 Evaluation

在图像检索中常用到的就是CMC曲线和mAP这两个评估指标,PyRetri中实现了评估的代码以及可视化的代码。

  • CMC曲线
    CMC曲线衡量了识别系统的性能,反映了目标行人在候选列表每个位置出现的概率。假设查询集中共有M张图像,则一共执行M次查询。对查询图像q,为了提高训练难度,在测试集中去除与q来自相同摄像视角的同一行人的图像。取测试集中与𝑞距离最近的图像,则与其相同标签的图片在候选列表中的位置 r q r_{q} rq,即为查询图像𝑞的查询结果。

  • mAP
    以查准率为纵轴,召回率为横轴作图,就得到了“查准率-召回率曲线”,简称“P-R 曲线”,而P-R曲线下的面积,就是查询样本q的AP值(Average Percision) A P q AP_{q} APq。而mAP则为查询集中所有图像的AP值的均值。

针对评估方法的详细介绍,推荐阅读:
ReID任务中的CMC和mAP

总结

PyRetri中提供很多方法,并且用户可以直接进行使用,支持自动配置。后面有时间会写一些关于大型图像检索上面所要面对的问题以及索引的一些方法。

  • 11
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值