自监督(一)

自监督(一)

自监督学习是可以看做是一种特殊的无监督学习,因为它利用了未标记的数据,关键思想是让模型无需手动标签即可学习数据表示。一旦模型学会了如何表示数据,那么它就可以用较少量的标记数据用于下游任务,以达到与没有自监督学习的模型相似或更好的性能。

自监督学习主要的步骤如下:

  1. 基于对数据的理解,以编程方式从未标记的数据生成输入数据和标签
  2. 预训练:使用上一步中的数据/标签训练模型(代理任务)
  3. Fine-tune:使用预训练的模型作为初始权重来训练感兴趣的任务(迁移学习)

MoCo

论文标题:Momentum Contrast for Unsupervised Visual Representation Learning

论文地址:https://arxiv.org/pdf/1911.05722.pdf

论文代码:https://github.com/facebookresearch/moco

简介和背景知识

MoCo于2019年11月13在 CVPR发表,并获得 CVPR2020最佳论文提名,它是用一种对比学习的方式进行自监督训练的模型。MoCo是第一个在很多主流的机器视觉领域上(比如分类、检测、分割、人体关键点检测等),都超越了有监督预训练模型的无监督模型,从某种程度上证明了无监督学习在机器视觉领域,也能达到很好的效果。MoCo取自论文标题的前两个词动量对比Momentum Contrast

动量从数学上可以理解成一种加权平均:
y t = m ∗ y t − 1 + ( 1 − m ) ∗ x t y_t = m*y_{t-1} + (1-m)*x_t yt=myt1+(1m)xt
上式中,yt和yt-1是当前时刻和前一时刻的输出,xt是当前时刻的输入,m就是动量。前时刻的输出,不仅依赖于当前时刻的输入,还依赖于前一时刻的输出。m越大,当前时刻的输入xt对结果yt就越小。MoCo利用了动量的这种特性,从而缓慢地更新一个编码器,让中间学习的字典中的特征尽可能地保持一致。被广泛使用在深度学习模型训练权重更新的EMA模型权重更新也是运用到了动量原理。这个更新方式也被使用到一些知识蒸馏策略中,并取得了不错的效果。

对比学习 是自监督训练的一种方法,常常被使用在自监督训练的代理任务(pretext task)中,希望相似数据(图片)最终学到的特征是相似的,在特征空间中,特征向量尽量靠近;反之还希望不同的数据学到的特征向量,尽量远离。

代理任务 通过设计合适的代理任务使原本无标注的数据自动产生标签,例如个体判别(Instance Discrimination):对输入网络的同一批图做两种不同的数据增强,同一张图片的两种增强出自同一张图片,相似度一般较高,那么他们在特征空间中的特征向量应该靠近,这对图片对就是正样本,而不同图片的数据增强,因为来自不同图片,相似度一般较低,那么他们在特征空间中的特征向量应该远离,这对图片对就是负样本,主要无标注数据就自动形成的标签,从而可以通过监督训练的方式学习到图片中有效的特征,得到一个较好的预训练权重。

代理任务的设计可以十分灵活,甚至是天马行空,只要能找到一种方式去定义正负样本,剩下的都是一些比较标准化的流程。因此对比学习这种方式十分灵活,并且在大量的实验中验证了它的有效性。

文章摘要

我们在机器视觉领域提出了一种新的无监督学习方法——MoCoMoCo虽然是基于对比学习的,但是本文是从另外一个角度来看对比学习,即把对比学习看作是一个字典查询任务。具体来说,我们构建了一个动态的字典,这个字典有两个特性:队列特性和移动平均编码(moving-averaged encoder)这两点在下文模型结构中会具体说明。得益于这两个特性使得我们的字典可以非常大,并且特征一致性非常好,从而便于进行对比学习。最终,MoCo作为一个自监督的预训练模型,能够在7个下游任务(分割、检测等)上 ,超越之前的有监督的训练模型 ,填平了CV领域中,无监督训练和有监督训练之间的坑——自监督。

引言

GPT和BERT已经证明了无监督的表征学习在NLP领域是非常成功的,但是在视觉领域,无监督学习效果差很多,作者认为可能是二者的原始信号空间不同。

  1. 在NLP任务中,原始信号空间是离散的(都是一些含有不同语义的单词或者词根),信号本来就拉得比较开,容易建立tokenize(将单词映射成向量)的字典。这样无监督学习容易建模,且模型容易优化。
  2. CV中,视觉信号都是在一个连续且高维的空间里,不想单词那样信息和语义浓缩的那么好,不够简洁,这样就不容易建立一个这样的字典,也就不容易进行无监督学习。

MoCo作者把所有的对比学习的方法归纳成为了一个动态字典的问题,字典的key就是各个样本,字典的value就是编码之后的特征(后面直接以k0表示第一个样本的编码特征)。我们先编码好锚点的特征,当做query;其它所有样本特征当做字典中不同的key,那么那对比学习就转化成为了一个字典查询的问题了。

作者认为,一个好的字典应该有两个特性:

  1. 字典足够大。字典越大,key越多,所能表示的视觉信息、视觉特征就越丰富 ,这样拿query去做对比学习的时候,才越能学到图片的特征。相反,如果字典很小,模型很容易通过学习一些捷径来区分正负样本,这样在碰到大量的真实数据时,泛化就会特别差(我的理解是,字典中只有猫和狗,狗都是黑色,猫都是黄色。模型简单的判断图片中物体是否是黄色,来区分猫和狗,而不是真的学到了猫和狗的特征)。
  2. 编码的特征尽量保持一致性。字典里的key都应该用相同或者说相似的编码器去编码得到,否则模型在查找query时,可以简单的通过找到和它使用相同或者相似编码器的key,而不是真的和它含有相同语义信息的key(变相引入两一个捷径)。

本文提出刘动量对比 (MoCo) 作为构建大型且一致的字典以进行具有对比损失的无监督学习的一种方式。以前的对比学习如下图(a)(b)所示,都至少被上述所说的两个方面中的一个所限制(要么一致性不好,要么字典不够大)。本文最大的贡献,就是使用队列以及动量编码器来进行对比学习,如下图(c)所示,解决了这个问题。

在这里插入图片描述

具体来说:

  1. key(编码特征)并不需要梯度更新,而是通过更新编码器,新的编码器使输出的key更新。

  2. queue :整个队列里面的元素都是字典,队首输入当前batch的编码特征,队尾弹出最旧的batch特征。每次移除的是最老的那些key,从一致性的角度来说 ,有利于对比学习。并且使用队列还有以下好处:

    1)可以重复使用那些已经编码好的key,而这些key是从之前的那些mini-batch中得到的。

    2)可以把的batch size的大小和队列的大小直接分开了,所以最后这个队列的大小,也就是字典的大小可以设的非常大,因为它大部分的元素都不是每个iteration都需要更新的。

    3)在字典里计算loss而不是整个数据集上计算loss,使用队列的数据结构,可以让维护这个字典的计算开销非常小。

  3. momentum encoder 动量编码器,如果只有当前batch的key是从当前的编码器得到特征,其它的key都是另外时刻的编码器输出的特征,这样就无法保证字典中key的一致性。所以作者又提出了动量编码器,即编码器参数通过动量的方式(如公式1所示)进行更新。MoCo中编码器的动量m=0.999。初始化的编码器来自于query的编码器,之后每次更新,只有**1%**的参数会从query的编码器参数里拿过来更新,所以这个编码器参数更新的非常缓慢。从而保证了字典中所有的key都是由相似的编码器抽取得到的,尽最大可能地保持了他们的一致性。避免了直接更新编码器k的所有参数,会导致编码器更新过快,降低了这个队列中所有key的特征的一致性的问题。

  4. 动态字典:字典中的key都是随机取样的,而且key的编码器在训练的过程中也是在不停的改变。

算法概述

损失函数

在本文中,采取InfoNCE作为对比损失来训练模型。InfoNCE是噪声对比估计(Noise-contrastive Estimation,NCE)损失的一个简单的变体。NCE将超级多分类转为二分类——数据类别data sample和噪声类别noisy sample,以此解决了类别多的问题。但是MoCo作者们认为如果只把问题看作是一个二分类(只有数据样本和噪声样本)的话,可能对模型学习不是很友好,毕竟在那么多的噪声样本中,大家很有可能不是一个类,所以还是把它看成一个多分类的问题比较合理。InfoNCE Loss的数学表达式如下:
L q = − l o g e x p ( q ∗ k + / τ ) ∑ i = 0 K e x p ( q ∗ k + / τ ) L_q=-log\frac{exp(q*k_+/\tau)}{\sum_{i=0}^Kexp(q*k_+/\tau)} Lq=logi=0Kexp(qk+/τ)expqk+/τ)
式子中,τ是一个超参数。如果去掉τ,整个式子其实就是交叉熵损失函数(cross entropy loss ),在后面的伪代码中,也是基于cross entropy loss实现。

分子表示q和正样本做计算,分母其实是k个负样本上做累加和,因为是从0到k,所以是k+1个样本,也就指的是字典里所有的key。

伪代码

下面给出文章伪代码

f_k.params = f_q.params # 初始化
for x in loader: # 输入一个图像序列x,包含N张图,没有标签
    x_q = aug(x) # 用于查询的图(数据增强得到)
    x_k = aug(x) # 模板图(数据增强得到),自监督就体现在这里,只有图x和x的数据增强才被归为一类
    q = f_q.forward(x_q) # 提取查询特征,输出NxC
    k = f_k.forward(x_k) # 提取模板特征,输出NxC
    # 不使用梯度更新f_k的参数,这是因为文章假设用于提取模板的表示应该是稳定的,不应立即更新
    k = k.detach() 
    # 这里bmm是分批矩阵乘法
    l_pos = bmm(q.view(N,1,C), k.view(N,C,1)) # 输出Nx1,也就是自己与自己的增强图的特征的匹配度
    l_neg = mm(q.view(N,C), queue.view(C,K)) # 输出Nxk,自己与上一批次所有图的匹配度(全不匹配)
    logits = cat([l_pos, l_neg], dim=1) # 输出Nx(1+k)
    labels = zeros(N) # 因为每对样本0位置对应的正好是自己正样本(l_pos)对所在的索引号,所以labels初始化N个0就可以
    # NCE损失函数,就是为了保证自己与自己衍生的匹配度输出越大越好,否则越小越好
    loss = CrossEntropyLoss(logits/t, labels) 
    loss.backward()
    update(f_q.params) # f_q使用梯度立即更新
    # 由于假设模板特征的表示方法是稳定的,因此它更新得更慢,这里使用动量法更新,相当于做了个滤波。
    f_k.params = m*f_k.params+(1-m)*f_q.params 
    enqueue(queue, k) # 为了生成反例,所以引入了队列
    dequeue(queue)

本人理解:labels = zeros(N),这一行非常巧妙,因为logits = cat([l_pos, l_neg], dim=1),使得每张图片的正样本对(l_pos)的位置一直处在logits第一个维度索引位0点位置,并且pytorch的CrossEntropyLoss的接口target只需要给出对应类别在logits中的索引号,而不需要转1-hot编码,所以给出对应的target为0,而一个mini-batch有N对图片,所以给出N个0作为target即可。

实验及结论

实验

实验结果也是对比了之前end to end还有memory bank方法,随着negative sample的增加,moco的优势愈发明显,结如下图所示:

在这里插入图片描述
作者也对比了其他的自监督方法,展示Moco性能的优越性.并且作者将moco的预训练方式迁移到目标检测、关键点检测,姿态识别、实例分割以及语义分割等下游任务中,均取得不错的效果,结果如下图所示。
在这里插入图片描述
在这里插入图片描述
这里只列出了部分实验结果,实验细节及其他详细结果请阅读原文。

结论

moco的预训练方式能够容易迁移到目标检测、关键点检测,姿态识别、实例分割以及语义分割等下游任务中,并取得不错的效果。

参考

【1】https://arxiv.org/pdf/1911.05722.pdf
【2】跟着李沐学AI,MoCo论文精读
【3】李沐论文精读系列三:MoCo、对比学习综述(MoCov1/v2/v3、SimCLR v1/v2、DINO等)

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值