
【斯坦福CS231n 学习笔记9】自监督学习
学习了自监督学习相关的内容。
自监督学习的动机
传统的监督学习在处理大规模数据时,人工标注的成本极高且不可持续。因此不再直接使用人工标注的语义标签训练,而是先构造一个可以从原始数据自动生成监督信号的代理任务,让网络从这个任务上学到一个有用的表征/特征提取器,之后再将其迁移到我们真正关心的下游任务(分类/检测/分割)上去。
模型如果能够解决基于数据自身结构的伪任务,就必须理解图像的语义特征。为了学习这个语义特征,自监督学习的基本框架如下:
- 编码器:将输入图像映射为高维特征向量。
- 预训练任务:通过数据的自身属性生成标签(如旋转角度、颜色等)。
- 下游任务:在预训练好的编码器基础上,接入少量有标签数据进行微调或线性探测。
评估预训练任务的好坏
自监督评估可以拆分成几个维度:预训练任务本身做得好不好、表示质量、鲁棒性与泛化性、计算效率、以及迁移到下游任务后的表现。通常来说,验证特征提取器的有效性的主要步骤如下:
- 特征迁移:将预训练阶段的特征提取器保留下来,作为评估的骨干网络。
- 输入数据:输入少量的有标签数据,这些数据来自于目标任务,例如图像分类或目标检测。
- 网络结构调整:在特征提取器之后,连接一个新的、浅层的网络,通常是一个线性分类器。
- 训练过程:通常是通过冻结特征提取器的参数,仅仅训练最后新加的线性分类器。
最终的模型不再预测原本的预训练任务,而是预测物体的类别。如果通过这种方式训练出的分类器准确率很高,就说明特征提取器在第一阶段学习到了高质量的、具有泛化能力的语义特征。
基于图像变换的预训练任务
旋转预测
旋转预测是最直白的图像变换型预训练任务,对一张无标注图片 施加一个已知的旋转变换得到 ,然后让网络输出旋转角度 作为监督信号。这可以完全不需要物体类别标签。
该类任务的通常数据流程如下:大量 unlabeled data → 一个“特征提取器(例如 convnet)”→ 再接一个小的 FC 头去做 rotation 分类/回归。实现层面上,最常见的不是回归任意角度,而是把角度离散成若干类(例如 ,用交叉熵去做一个标准分类问题:
其中 encoder 参数是 ,分类头参数是 。
如果一个模型真的能够判断一张图应该朝向哪边是正向的,就必须捕捉到一些关于物体形状、结构、布局的稳定线索,而不仅仅是像素层面的统计。从学习机制上看,网络为了把 “90°” 和 “180°” 区分开,就会倾向于在中高层表征里编码诸如“重力方向”“部件相对位置”“典型外观结构”等信息,这些信息随后往往能迁移到分类/检测等任务。
旋转预测的预训练网络中最后用于预测角度的 FC 层,本质上只是预训练任务的特定部分,在进行下游分类时,可以将这个部分去除掉,只保留编码器作为特征提取器,然后在其后面接上一个分类头来学习真正的类别标签。
Jigsaw Puzzles
有一类轻量的上下文任务:将图像切分成 的网格 patch,并且把中心 patch 作为参照,然后随机拿到一个周围的 patch,要求网络输出这个 patch 相对于中心 patch 的位置是哪一个(上下左右及四个对角)。
Patch 来自于哪一个格子位置是确定的,因此 label 不是人工标注出来的,而是由裁剪坐标直接给出的,网络想要将某个 patch 应该在中心 patch 的具体相对位置分出来,往往不能只靠低级纹理,它会被迫去捕捉更加稳定的线索,比如物体的局部结构、边界走向、背景上下文(例如天空可能更上方、地面可能更下方这类统计规律)。
而 jigsaw puzzles 所要做的事情更上一层:将切分的 9 个 patch 全部打乱数据,然后让网络判断其正确的排列是哪一个。但是如果真正允许随意打乱,排列的数量是 ,真正将其当作分类类别去做并不现实,因此网络中只挑选出 64 个相对合理且彼此差异足够大的排列,将任务变成一个 64 类分类问题,网络输出一个长度为 64 的向量,表示当前打乱方式属于哪一类。
Inpainting
与其让模型去做旋转分类、拼图分类这种人为设计的离散标签,还有一种更直观、更接近语义理解的方式——让模型去将图像中缺失的部分补充出来。这类任务也不需要人工标注,只需要在训练时对输入图像做一个简单的 masking,将一块区域遮盖住,然后要求网络根据周围上下文把遮挡区域补全,由于保留了原图,所以补全结果可以直接和原始的缺失区域做对比来形成损失函数。
模型的结构大体如下:
对输入做 mask 之后,Encoder 将可见区域编码到特征空间,中间可以接上一些全连接层作为 bottleneck,然后 Decoder 负责将缺失区域解码出来,整体上是 autoencoder 的思路,只是训练目标不是重建整张图,而是重建被遮挡的那一部分。
重建损失的核心是输出与原图的差,但是其并不是对整张图计算,而是只在被遮挡区域计算,因此会将差值再与 mask 做一次逐元素乘法,让 loss 聚焦在缺失区域。使用公式写出来是一种很常见的形式:
其中 是二值掩码。Inpainting 的评价本身就有点模糊,因为同一个缺失块可能有很多合理的补法,当使用 MSE/L2 这一类逐像素平均意义上的重建目标时,模型为了降低期望误差,往往会输出平均化的结果,于是就会出现早期重建框架常见的发虚、模糊的现象,这是因为目标函数本身在多模态输出下会鼓励均值解。
因此还可以加上一个对抗损失来提供一个真实性/自然性的约束,迫使生成的结果落在更像真实图像分布的区域,从而在视觉上更锐利、更逼真。
Image coloring
除了上述偏向几何/结构的变换,还有一种更加语义化但是仍然可以生成自监督的任务:上色。现实中的训练数据大多数本来就是彩色图像,所以完全可以将彩色图像拆分开,将其中的一部分通道作为输入,另一部分通道作为输出,这样标签就不是人工标注的类别,而是图像自身携带的颜色信息,损失函数仍然可以被计算、反转。
为了将亮度/结构与颜色分离,常用的做法并不是在 RGB 上做回归,而是换到 LAB 色彩空间,其中 L 代表亮度,A、B 两个通道代表色度信息;因此最标准的预训练形式是给定 L,预测 A 与 B,再将三者合成得到色彩图像并预测误差。
从表征学习的角度,这个任务之所以是有效的,是因为其迫使网络在 encoder 的中高层表示里编码一些与语义/结构高度相关的因素,如果只盯着局部纹理,很多区域的颜色是多解的,但是当模型逐渐学会利用物理类别、材质、场景上下文等更稳定的线索时,其会在下游分类/检测中表现得更好。
Split-Brain Autoencoder
在静态上色的基础上,引入 Split-Brain Autoencoder。既然可以给定 L 的预测 AB,同样也可以反过来给定 AB 预测 L,于是训练两套函数互相预测对方的通道,最后将两边的结果合并成图像并且使用均方误差来训练。
单方向的 L→AB 可能让网络在某些情况下更像是在做纹理到颜色的映射,而双方向映射会更加强迫 encoder 学到同时对结构与色彩关系都敏感的中间表示,下游迁移时会使用 F1 与 F2 的特征拼接做监督任务评估。
从中也可以看出一种常见的算法抽象:将输入通道拆分为 与 ,做给定 预测 预测以及给定 预测 的双任务学习,因此其也可以迁移到 RGB-D 这类多模态传感上,核心仍然是利用通道间可预测性来构造自监督信号。
Video coloring
静态上色可以扩展到视频,因为视频里相邻的帧之间本身就存在大量对应关系,颜色在时间维度上也应当保持一致,于是让模型学会用参考帧的颜色去给后续帧上色同时是一个上色任务与一个隐式的学习对应关系/跟踪任务。
对于目标帧中的每个像素,要在参考帧里找到其对应的位置,于是先用 CNN 在两帧上提取特征,然后对每个目标像素计算它与参考帧所有像素的相似度,将这些相似度作为 attention,然后使用这组权重对参考帧的颜色做加权平均,作为目标像素的预测颜色,因为数据中本来就有真实颜色,最后仍然可以对预测颜色与真实颜色计算 loss 并反传。
如果逐帧独立上色,由于缺乏时间一致性约束,人物衣服可能会在不同帧里面颜色乱跳,而引入参考帧的注意力拷贝后,颜色会更一致,因为模型被迫在帧间建立稳定的对应关系。这种为了上色而学到的对应关系本身就可以迁移为跟踪能力,当对参考帧形成注意力图并且对每个目标像素都在参考帧中找到一个权重分布时,实际上已经在进行一种软匹配,这使得模型能够在无标签的情况下做目标/区域跟踪。
Masked Autoencoders
除了前面几种预训练任务,还有一种更加现代化,也更常被实际工程作为默认选择的自监督预训练框架:Masked Autoencoders(MAE),MAE 仍然是基于重建的,但是其把 masking 做得更加彻底,不是遮挡一小块,而是遮挡大部分比例的内容,然后逼迫模型将缺失的内容补回来,并且在大规模的训练之后,不仅能够重建遮挡区域,还能够得到质量很高的 encoder 表征,适配后续的下游任务。
MAE 沿用 ViT 的输入方式,将图像切分成非重叠的 patches,然后均匀随机采样一个很大的比例进行 mask,只将剩下可见的那一小部分送进 encoder。
- 高 masking ratio 会让任务更加具有挑战性,因为预测空间变大、歧义更多,模型不得不学到更强的结构性线索。
- Encoder 不把 mask token 当作输入,而是只编码可见 patches,这使得 encoder 的 token 数量显著减少,从而给把 encoder 做得很大腾出了计算预算。
MAE 的 Encoder 大而强,只处理未被遮挡的 patches,做线性投影、加位置编码,然后通过 Transformer blocks,因为输入的 token 很少,所以 encoder 可以很大。Encoder 单 token 的计算量可以比 decoder 高很多。因此表征学习主要依靠 encoder,而 decoder 则主要负责将任务完成即可。Decoder 会将 encoder 的输出与被遮挡位置的 shared mask tokens 合并,并且给这些token 补上位置编码,再过若干 transformer blocks,然后使用线性投影输出像素重建,这部分训练后不会被继续使用,可以与 encoder 的设计解耦。
MAE 用的是像素空间的 MSE 来度量输入图像与重建图像的差异,而且 loss 只在被遮挡的 patches 上计算,这种只监督缺失部分的设定能够避免模型将容量浪费在复制可见区域上,而是将学习压力集中在真正困难的补全上。
对于预训练好的同一个 encoder,可以用两种经典的 protocol 来进行下游迁移。
- Linear probing:冻结预训练模型,只在顶层添加一个线性层训练。
- Fine-tuning:不冻结 encoder,而是继续训练(可以全量或者部分 block),并且在上面接一层或多层头部网络,更像是适配,用于榨干模型对新任务的可迁移潜力。
Contrastive Representation Learning
前面的变换类预训练任务本质上还是对同一个对象进行学习,只是换了一种形式,同时数据集中还有大量完全不同的对象。因此可以定义一个任务,让模型把同一对象的不同变换在表示空间中拉近,把不同对象在表示空间拉远,这就是对比表征学习。
进一步地将样本组织成三种角色 reference ,positive (同一个对象的变换),negtive (其他对象,通常来自同一个 batch 或队列),并且需要一个打分函数 使得 大于 ,然后将这个偏好写成可优化的损失。在有了 score 之后,经过 softmax ,reference 对所有候选形成一个概率分布,然后用选中 positive 这一事件的负对数似然作为 loss:
这个损失也被称为 InfoNCE 损失,常见的论述认为它与互信息相关,负的 InfoNCE 可以被看作是某种互信息下界;此外,negative 样本越多,下界越紧,因此对比学习往往需要大量 negative,也就需要很大的 batch size 或其他机制来凑够足够的 negatives。
SimCLR
使用余弦相似度作为 score 函数,对每张图做两次数据增强,得到两张 view,将其输入到同一个 encoder 中得到表示,将每一对 view 都当作 positive,其他样本都当作 negative,在表示空间中使用余弦相似度计算对比损失,并且在一个 batch 内用完所有的 negative。
SimCLR 的优势在于其使用了 projection head 和 large batch size。
- Projection head:对比损失是一种强不变性的约束,它强迫模型丢弃那些对识别身份无用的信息(例如物体的具体颜色、旋转角度),但是下游任务可能恰恰需要这些信息,而 projection head 就是在提取的特征向量之后再接上一层线性/非线性投影,过滤掉所有的干扰信息,得到的投影结果用于计算对比损失,而投影前的特征则保留了更多的原始信息,在迁移到下游任务时,去掉 Projection head。
- Large batch size:因为 negatives 多了,约束就会更紧,训练也会更有效,但是与之相对应的,也会需要更多的显存和 TPU/GPU 算力支持。
MoCo
Moco 解决的问题是如何在显存不足的情况下仍然能够获取大量的 negatives。其核心做法是维护一个运行队列存放历史的 negatives,让 negative 的数量不再受 batchsize 的限制,从而以较小的 batch 获得大量的 negatives。
但是队列也带来了一个新问题,队列中的 negatives 不是当前 batch 的计算图,无法对他们进行反向传播,MoCo 将 encoder 分为 query encoder 和 key encoder,梯度只通过 query encoder;key encoder 使用一个动量系数 从 query encoder 平滑更新,保证队列中 key 的表示不会剧烈漂移,同时持续更新 FIFO 队列。
MoCo v2
MoCo v2 使用了 SimCLR 的某些技巧(如 MLP 投影头、更强的数据增强)非常有效,于是将其吸纳进来推出了 MoCo v2:
- 添加 MLP head:在 encoder 后面加上了两层全连接层。
- 数据增强:引入了 SimCLR 的模糊增强。
在小 batch size 下的表现超越了 SimCLR 在大 batch size 下的表现。
Contrastive Predictive Coding(CPC)
CPC 将对比学习与序列预测相结合,与 SimCLR 和 MoCo 这种基于图像整体增强的方法不同,CPC 的核心逻辑是,如果模型理解了当前的上下文,就应该能够预测未来。
在 CPC 之前,自监督学习中的生成式方法视图直接重建像素,但是像素空间的维度极高且包含大量高频噪声。预测每一个像素的细节极其困难且计算昂贵,而且模型容易浪费算力在无关紧要的局部纹理上,而非学习高级语义。CPC 不在不在原始像素空间做预测,而是在潜变量空间做预测。
CPC 处理的数据通常具有结构化顺序,为了便于理解,以音频为案例:
将原始序列 切分为多个重叠或连续的时间窗口,记为 ,对每一个 使用卷积神经网络进行映射,得到潜变量向量 , 这一步的目的主要是为了降维,去除原始数据中的低级噪声,提取局部特征。自回归模型 负责接收过往时刻的潜变量序列 ,使用 RNN 来聚合上下文信息,而后输出当前的上下文向量 , 也可以被看作是时刻 之前所有信息的压缩摘要,在 CPC 中希望利用 来预测未来 步的潜变量 。
CPC 创新点在于对于未来的每一步 ,训练一个特定的线性变换矩阵 ,预测值为 ,以及使用对比任务来进行训练,其中正样本是序列中真实的未来特征 ,负样本是从通过一个序列的其他位置,或者完全不同的序列中随机采样的特征 。
虽然 CPC 最初是为了序列数据而设计的,但是它也可以直接应用于图像,具体的处理方式是将图像划分为一系列重叠的 patch,定义某种顺序,而后模型读取图像的上半部分,编码为 Context ,然后尝试预测图像的下半部分各行的特征向量 。这能够迫使模型理解图像的全局结构。
DINO
DINO 仅仅依靠简单的“教师-学生”蒸馏架构,无需负样本,也能够训练出极强的视觉特征,甚至自动涌现出语义分割的能力。
在之前的主流方法都是基于对比学习,其核心在于拉近正样本,推开负样本,这极其依赖负样本的数量和质量,且需要复杂的技巧来避免模型坍塌。而 DINO 的思路就是完全抛弃负样本,改用知识蒸馏,即使没有标签,也可以构建一个“老师”网络和一个“学生”网络,让学生去模仿老师的输出,只要设计合理,这一过程就能够学习到优秀的特征。
DINO 的架构通常包含两个结构相同但参数不同的网络:
- 学生网络:通过标准的梯度下降进行更新,接收输入图像的增强视图输出特征分布,尽力去拟合教师网络的输出。
- 教师网络:不进行梯度反向传播,它的参数是学生网络参数的指数移动平均 ,这与 MoCo 中的动量编码器很相似,但是在 DINO 中,教师是由学生进化得来的,其接收的是图像的全局视图,这意味着老师看到的总是更完整、更清晰的信息。
DINO 引入了一种非对称的数据增强策略,称为 Multi-Crop,包括大尺度裁剪、包含全图信息且只有老师能看到的全局视图以及小尺寸裁剪、只包含局部细节且只有学生能看到的局部视图。学生必须学会从局部推断整体,从而建立起高级的语义认知。
既然没有负样本,DINO 使用了两个关键的技巧来通过教师网络控制输出分布来防止坍塌: - 居中:教师网络的输出在计算 Loss 前会减去一个均值 (该均值是过去 batch 中输出的滑动平均),这能够防止某个维度一直占据主导地位,确保特征分布不会坍缩成一个点。
- 锐化:在 Softmax 层使用极低的 Temperature,让教师的输出概率分布变得非常尖锐。这能够强迫学生模型做出自信的预测,防止其输出均匀分布。
在没有任何像素级监督的情况下,DINO 训练出的 ViT 最后一层的 Attention Map 能够精准地勾勒出前景物体的轮廓:
这证明了基于 DINO 的自监督学习能够比监督学习捕捉到更本质的物体结构信息。
参考如下:
https://cs231n.stanford.edu/index.html
