SPQ:自监督量化的无监督图像检索算法

Self-supervised Product Quantization for Deep Unsupervised Image Retrieval

基于自监督量化的无监督图像检索

提出了一种新的无监督学习方案:交叉量化对比学习,如下图所示:

在这里插入图片描述

Fig1 cross quantized contrastive learning

( t , t ′ ∼ T ) (t,t'\sim \mathcal{T}) (t,tT)为取自集合 T \mathcal{T} T的图形变换(数据增强)。

普通的对比学习对特征向量进行投影后计算相似度,SPQ中提出的量化对比学习采用量化方案对特征向量编码后,使用编码之后的结果与原特征向量进行对比学习,并设法使二者之间相似度最小。


整体架构

整个算法流程图如下所示:

在这里插入图片描述

Fig2 Pipeline of SPQ

交叉相似度使用原始特征 x ^ \hat{x} x^与经过量化的特征 z ^ \hat{z} z^计算

具体来说,可以将整个任务描述为:学习一个映射 R : x ↦ b \mathcal{R}:x \mapsto b R:xb R \mathcal{R} R定义为整个系统, x x x为一个包含了 N N N张训练样本的数据集 X = { x n } n = 1 N \mathcal{X}=\lbrace{x_n}\rbrace^{N}_{n=1} X={xn}n=1N中的一张图像, b ^ \hat{b} b^是一个 B B B位的二值编码 b ^ ∈ { 0 , 1 } B \hat{b} \in \lbrace{0,1}\rbrace^B b^{0,1}B
如图2中所示,SPQ系统( R \mathcal{R} R)含有一个基于CNN的特征提取器 F ( x ; θ F ) \mathcal{F}(x;\theta_{\mathcal{F}}) F(x;θF),这个CNN网络输出一个D维的特征向量: x ∈ R D x\in R^D xRD。为了加速图像检索数度,SPQ使用一个包含 M M M个码本(codebooks)的量化器 Q ( x ^ ; θ Q ) \mathcal{Q}(\hat{x};\theta_\mathcal{Q}) Q(x^;θQ),其中 { C 1 , . . . , C M } ⊂ Q \lbrace{C_1,...,C_M}\rbrace \subset \mathcal{Q} {C1,...,CM}Q,每个 C i C_i Ci包含有 K K K个码字 c ∈ R D / M c \in R^{D/M} cRD/M,因此第m个码本可以描述为 C m = { c m 1 , . . . , c m K } C_m=\lbrace{c_{m1},...,c_{mK}}\rbrace Cm={cm1,...,cmK}。深度特征空间为分为若干子空间的笛卡尔乘积,对应子空间的每个码本都表现出数据集 X \mathcal{X} X的几个不同的特征。码本中的每个码字都指向分割的深度描述符的其中一个聚类中心,目的是容纳一个频繁出现的局部模式(local pattern)。
在量化过程中,图像之间相似的特征通过分配给相同的码字来实现共享,而可区分的特征则属于不同的码字。


自监督训练

在SPQ训练体系中, θ F \theta_{\mathcal{F}} θF θ Q \theta_{\mathcal{Q}} θQ被定义为特征提取器 F \mathcal{F} F与量化器 Q \mathcal{Q} Q的训练参数。由于硬分配量化方案(hard assignment quantization)是很难计算的,所以SPQ采用了软量化器 q m ( . ) q_m(.) qm(.)定义如下:
z n m = ∑ k K exp ⁡ ( − ∣ ∣ x n m − c m k ∣ ∣ 2 2 / τ q ) ∑ k ′ K exp ⁡ ( − ∣ ∣ x n m − c m k ′ ∣ ∣ 2 2 / τ q ) c m k z_{nm}=\sum^K_k\frac{\exp(-||x_{nm}-c_{mk}||^2_2/\tau_q)}{\sum^K_{k'}\exp(-||x_nm-c_{mk'}||^2_2/\tau_q)}c_{mk} znm=kKkKexp(xnmcmk22/τq)exp(xnmcmk22/τq)cmk
其中, τ q \tau_q τq是非负的温度系数,用于控制softmax的输出尺度,而 ∣ ∣ . ∣ ∣ 2 2 ||.||^2_2 .22定义为欧几里德距离,用以衡量两个输入之间的相似性。在这种形式下,每个子量化描述符 z n m = q m ( x n m ; τ q , C m ) z_{nm}=q_m(x_{nm};\tau_q,C_m) znm=qm(xnm;τq,Cm)可以被视为码本 C m C_m Cm中的码字的指数加权和。需要注意,这个操作相当于码本中的所有码字都被用来近似量化输出,其中与输出最接近的码字贡献最大。
与之前的深度量化(deep PQ)方案不同的是,SPQ中排除了内部归一化操作(intra-normalization)。在将子量化描述符连接成整体量化描述符 z ^ \hat{z} z^的过程中,内部归一化可以最小化视觉特征的影响,但是由于SPQ的训练中完全无人工监督,这有助于发现不同的特征。在SPQ中,算法致力于捕捉主要的视觉特征,而不是试图平衡每个码本的影响。
为了同时训练深度描述符和码字,SPQ使用了交叉量化对比学习(cross quantized contrastive learning)模式,尝试对各种视图(转换图像)的深度描述符和乘积量化描述符进行对比。如图2所示,如果视图源自同一幅图像,则深度描述符和量化描述符被视为相关的,反之则视为不相关的。同时,为了增强码字的泛化能力,深度描述符和自身的量化描述符之间的相关性被忽略了,这是因为当子向量与最接近的码字一致性最大化时,其他码字的贡献就减小了。
在一个大小为 N B 的 N_B的 NBmini-batch中,我们对其随机应用两次数据增强,以生成新的大小为 2 N B 2N_B 2NB的数据视图。将源自同一幅图像的视图 ( x i ~ , x j ~ ) (\tilde{x_i}, \tilde{x_j}) (xi~,xj~)视为相关的,而其他 2 ( N B − 1 ) 2(N_B-1) 2(NB1)幅视图视为无关的。基于这个假设,SPQ设计了一个交叉量化损失函数以学习相关的图像对 ( i , j ) (i,j) (i,j)
l ( i , j ) = − log ⁡ exp ⁡ ( S ( i , j ) / τ c q c ) ∑ n = 1 N B 1 n ′ ≠ j exp ⁡ ( S ( i , n ′ ) / τ c q c ) l_(i,j)=-\log \frac{\exp(\mathcal{S}(i,j)/\tau_{cqc})}{\sum_{n=1}^{N_B}\mathbb{1}_{n'\neq j}\exp(\mathcal{S(i,n')}/\tau_{cqc})} l(i,j)=logn=1NB1n=jexp(S(i,n)/τcqc)exp(S(i,j)/τcqc)
其中, S ( i , j ) \mathcal{S}(i,j) S(i,j)定义了 x i ^ \hat{x_i} xi^ z j ^ \hat{z_j} zj^之间的余弦相似度,使用余弦相似度来度量是为了避免 x i ^ \hat{x_i} xi^ z j ^ \hat{z_j} zj^之间的范数偏差。

CQC Loss的python实现实际如下:

class CQCLoss(T.nn.Module):
    def __init__(self, device, batch_size, tau_cqc):
        super(CQCLoss, self).__init__()
        self.batch_size = batch_size
        self.tau_cqc = tau_cqc
        self.device = device
        self.COSSIM = T.nn.CosineSimilarity(dim=-1)
        self.CE = T.nn.CrossEntropyLoss(reduction="sum")
        # 定义一个minibatch中相关的图像对的mask
        self.get_corr_mask = self._get_correlated_mask().type(T.bool)

    def _get_correlated_mask(self):
        """定义一个minibatch中相关的图像对的mask
        由主对角线和两条副对角线构成
        副对角线的位置与batchsize相关

        Returns:
            一个minibatch中相关的图像对的mask
        """
        diag = np.eye(2 * self.batch_size)
        l1 = np.eye((2 * self.batch_size), 2 * self.batch_size, k=-self.batch_size)
        l2 = np.eye((2 * self.batch_size), 2 * self.batch_size, k=self.batch_size)
        mask = T.from_numpy((diag + l1 + l2))
        mask = (1 - mask).type(T.bool)
        return mask.to(self.device)

    def forward(self, Xa, Xb, Za, Zb):
        """
        Xa与Xb是同一个batch的图像经过不同数据增强生成的新batch
        Za与Zb是Xa与Xb对应的量化向量
        """
        XaZb = T.cat([Xa, Zb], dim=0)
        XbZa = T.cat([Xb, Za], dim=0)

        Cossim_ab = self.COSSIM(XaZb.unsqueeze(1), XaZb.unsqueeze(0))
        Rab = T.diag(Cossim_ab, self.batch_size)
        Lab = T.diag(Cossim_ab, -self.batch_size)
        Pos_ab = T.cat([Rab, Lab]).view(2 * self.batch_size, 1)
        Neg_ab = Cossim_ab[self.get_corr_mask].view(2 * self.batch_size, -1)
        
        # 计算向量之间的相关性矩阵
        Cossim_ba = self.COSSIM(XbZa.unsqueeze(1), XbZa.unsqueeze(0))
        Rba = T.diag(Cossim_ba, self.batch_size)
        Lba = T.diag(Cossim_ba, -self.batch_size)
        # 寻找所有的相关图像对作为正例    
        Pos_ba = T.cat([Rba, Lba]).view(2 * self.batch_size, 1)
        # 去除所有相关的图像对即为不相关的负例
        Neg_ba = Cossim_ba[self.get_corr_mask].view(2 * self.batch_size, -1)
        # 将所有的正例放在首位
        logits_ab = T.cat((Pos_ab, Neg_ab), dim=1)
        logits_ab /= self.tau_cqc

        logits_ba = T.cat((Pos_ba, Neg_ba), dim=1)
        logits_ba /= self.tau_cqc
        # 定义标签(坐标0为正例)
        labels = T.zeros(2 * self.batch_size).to(self.device).long()

        loss = self.CE(logits_ab, labels) + self.CE(logits_ba, labels)
        return loss / (2 * self.batch_size)

代码中相关性矩阵Cossim_ab是一个 2 N × 2 N 2N\times 2N 2N×2N大小的方阵, N N N为batchsize,由拼接向量 X a Z b X_aZ_b XaZb与自身进行余弦相似度计算得到。因此其主对角线和batchsize位置的两条副对角线是同一个样本计算相似度得到的值,其他位置为不同图像计算相似度得到的值。
[ X a 1 ∗ X a 1 … X a 1 ∗ X a n X a 1 ∗ Z b 1 … X a 1 ∗ Z b n ⋮ ⋱ ⋮ ⋮ ⋱ ⋮ X a n ∗ X a 1 … X a n ∗ X a n X a n ∗ Z b 1 … X a n ∗ Z b n Z b 1 ∗ X a 1 … Z b 1 ∗ X a n Z b 1 ∗ Z b 1 … Z b 1 ∗ Z b n ⋮ ⋱ ⋮ ⋮ ⋱ ⋮ Z b n ∗ X a 1 … Z b n ∗ X a n Z b n ∗ Z b 1 … Z b n ∗ Z b n ] \begin{bmatrix} X_{a_1}*X_{a_1}& \dots &X_{a_1}*X_{a_n} & X_{a_1}*Z_{b_1} & \dots & X_{a_1}*Z_{b_n}\\ \vdots&\ddots & \vdots & \vdots & \ddots & \vdots\\ X_{a_n}*X_{a_1} & \dots & X_{a_n}*X_{a_n} & X_{a_n}*Z_{b_1} & \dots & X_{a_n}*Z_{b_n}\\ Z_{b_1}*X_{a_1} & \dots & Z_{b_1}*X_{a_n} & Z_{b_1}*Z_{b_1} & \dots & Z_{b_1} * Z_{b_n} \\ \vdots & \ddots & \vdots & \vdots & \ddots & \vdots \\ Z_{b_n}*X_{a_1} & \dots & Z_{b_n}*X_{a_n} & Z_{b_n}*Z_{b_1} & \dots & Z_{b_n}*Z_{b_n} \end{bmatrix} Xa1Xa1XanXa1Zb1Xa1ZbnXa1Xa1XanXanXanZb1XanZbnXanXa1Zb1XanZb1Zb1Zb1ZbnZb1Xa1ZbnXanZbnZb1ZbnZbnZbn
因此同一个图像对的样本相似度在矩阵中的位置如下,分别为主对角线与 n = N n=N n=N的两条副对角线:
[ X a 1 ∗ X a 1 … X a 1 ∗ Z b 1 … ⋮ ⋱ ⋮ ⋮ ⋱ ⋮ … X a n ∗ X a n … X a n ∗ Z b n Z b 1 ∗ X a 1 … Z b 1 ∗ Z b 1 … ⋮ ⋱ ⋮ ⋮ ⋱ ⋮ … Z b n ∗ X a n … Z b n ∗ Z b n ] \begin{bmatrix} X_{a_1}*X_{a_1}& \dots & & X_{a_1}*Z_{b_1} & \dots & \\ \vdots&\ddots & \vdots & \vdots & \ddots & \vdots\\ & \dots & X_{a_n}*X_{a_n} & & \dots & X_{a_n}*Z_{b_n}\\ Z_{b_1}*X_{a_1} & \dots & & Z_{b_1}*Z_{b_1} & \dots & \\ \vdots & \ddots & \vdots & \vdots & \ddots & \vdots \\ & \dots & Z_{b_n}*X_{a_n} & & \dots & Z_{b_n}*Z_{b_n} \end{bmatrix} Xa1Xa1Zb1Xa1XanXanZbnXanXa1Zb1Zb1Zb1XanZbnZbnZbn

参考文献

[1] Self-supervised Product Quantization for Deep Unsupervised Image Retrieval
[2] Github官方代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值