无监督动量对比学习(Unsupervised Momentum Contrastive Learning, 简称 MoCo) 是一种用于无监督表征学习的对比学习方法。MoCo 的目标是通过对比正负样本对,学习一个编码器,生成能够区分不同实例的有用特征表示,而不需要显式的标签信息。
1. 核心思想
MoCo通过构造正负样本对,让模型学会区分正样本(相似样本)和负样本(不相似样本)。它使用一个主编码器(encoder)和一个动量编码器(momentum encoder)来构造稳定的对比学习任务。
对比学习的目标是:拉近正样本的特征向量,让同一实例的增强样本的特征尽可能接近;推远负样本的特征向量,让不同实例的特征尽可能远离。这种对比关系通过对比损失函数(如InfoNCE)实现。
2. MoCo 的机制
MoCo 提出了一种动态更新的机制,解决了对比学习中负样本选择困难和特征不稳定的问题。以下是 MoCo 的关键模块:
(1)正负样本对的构建
- 正样本对:从同一实例生成的两种增强样本。例如,图像经过不同的数据增强(如旋转、裁剪、模糊等)后生成两个视图。这两种增强样本在语义上相同,应该具有相似的特征。
- 负样本对:来自不同实例的样本对。它们的特征应该尽可能不同。
(2)主编码器和动量编码器
- 主编码器( f q f_q fq):一个标准的神经网络(如 ResNet),通过反向传播进行更新。
- 动量编码器( f k f_k fk):主编码器的副本,参数通过动量更新,而不是直接反向传播。动量更新公式: θ k = m ⋅ θ k + ( 1 − m ) ⋅ θ q , \theta_k = m \cdot \theta_k + (1 - m) \cdot \theta_q, θk=m⋅θk+(1−m)⋅θq, 其中, θ k \theta_k θk 是动量编码器的参数, θ q \theta_q θq 是主编码器的参数, m ∈ [ 0 , 1 ) m \in [0, 1) m∈[0,1) 是动量系数(一般设为 0.999)。动量编码器的参数更新更平滑,能够保持特征的稳定性,避免了训练过程中特征漂移的问题。
为什么动量编码器需要使用动量更新参数,用来避免特征漂移,而主编码器却不用考虑抑制特征漂移呢?
动量编码器需要使用动量更新参数来抑制特征漂移,原因在于:
- 动量编码器负责生成负样本队列的特征,这些特征需要保持分布稳定,以避免对比学习失效。
- 动量更新通过平滑地调整动量编码器参数,保证了负样本特征的时间一致性。
主编码器不需要考虑抑制特征漂移,因为:
- 主编码器的特征是实时生成的,只用于当前批次的训练。
- 主编码器的快速更新反映了最新的优化目标,能够保证模型的学习效率。
两者的不同设计目标决定了它们采用不同的参数更新策略。动量编码器和主编码器通过分工合作,共同实现高效稳定的对比学习。
(3)负样本队列
MoCo 通过维护一个动态更新的 负样本队列(Queue),存储了多个来自不同实例的负样本特征。
随着训练的进行,新的负样本会加入队列,旧的样本会被移出队列。动量编码器生成的负样本特征与主编码器生成的特征具有一致性,避免了特征漂移问题。
(4)对比损失
MoCo 使用 InfoNCE 损失来优化特征表示: L = − log exp ( q ⋅ k + / τ ) ∑ i = 1 N exp ( q ⋅ k i / τ ) , \mathcal{L} = - \log \frac{\exp(q \cdot k_+ / \tau)}{\sum_{i=1}^N \exp(q \cdot k_i / \tau)}, L=−log∑i=1Nexp(q⋅ki/τ)exp(q⋅k+/τ), 其中, q q q 是主编码器生成的查询特征, k + k_+ k+ 是动量编码器生成的正样本特征, k i k_i ki 是动量编码器生成的所有负样本特征, τ \tau τ 是温度超参数,用于调节分布的平滑程度。
3. 负样本队列详解
对比学习依赖于大量的正负样本对,但训练过程中负样本的数量是一个关键问题,如果负样本数量不足,模型难以学到鲁棒的特征。在小批量训练中,负样本的数量受限于批量大小,限制了学习效果。例如,批量大小为 B B B,那么负样本数量最多为 B − 1 B-1 B−1。
MoCo 通过设计负样本队列,解决了负样本数量不足的问题。负样本队列是一种存储机制,保存了最近多个实例的特征向量(由动量编码器生成)。队列中的这些特征用作对比学习中的负样本。
负样本队列是一个固定大小的结构(例如包含 65,536 个特征向量),存储的是动量编码器生成的特征向量。每个特征向量表示一个样本的表征,这些表征可以用于计算与正样本的对比损失。当一个新的样本被编码后,其特征会被加入队列,同时最早的一个样本会被移出队列(先进先出)。
MoCo 负样本队列的实际工作流程:
- 初始化队列:随机初始化一个固定大小的队列(例如大小为 65,536),用来存储负样本特征。
- 批次训练:主编码器和动量编码器分别对当前批次的数据进行编码,主编码器生成查询特征 q q q,动量编码器生成正样本特征 k + k_+ k+,队列中的所有特征作为负样本 k i k_i ki。
- 计算对比损失:通过主编码器生成的查询特征 q q q、动量编码器生成的正样本 k + k_+ k+ 和队列中的负样本 k i k_i ki,计算对比损失(InfoNCE 损失): L = − log exp ( q ⋅ k + / τ ) exp ( q ⋅ k + / τ ) + ∑ i = 1 N exp ( q ⋅ k i / τ ) , \mathcal{L} = -\log \frac{\exp(q \cdot k_+ / \tau)}{\exp(q \cdot k_+ / \tau) + \sum_{i=1}^N \exp(q \cdot k_i / \tau)}, L=−logexp(q⋅k+/τ)+∑i=1Nexp(q⋅ki/τ)exp(q⋅k+/τ), 其中, k + k_+ k+ 是当前批次中正样本的特征, k i k_i ki 是从队列中取出的负样本, N N N 是队列中负样本的数量。损失的优化会拉近 q q q 和 k + k_+ k+ 的特征距离,同时推远 q q q 和 k i k_i ki 的特征距离。
- 更新负样本队列:当前批次的正样本特征 k + k_+ k+ 会加入队列,替换最旧的负样本特征(FIFO 机制)。假设队列的容量为 K K K,当前批次大小为 B B B,将 B B B 个新的 k + k_+ k+ 加入队列,队列中最旧的 B B B 个特征会被移除。
- 动量编码器参数更新:动量编码器的参数通过主编码器的参数以指数移动平均(EMA)的方式更新: θ k = m ⋅ θ k + ( 1 − m ) ⋅ θ q , \theta_k = m \cdot \theta_k + (1 - m) \cdot \theta_q, θk=m⋅θk+(1−m)⋅θq, 其中, θ k \theta_k θk 是动量编码器的参数, θ q \theta_q θq 是主编码器的参数, m m m 是动量系数(通常设为 0.999)。
- 主编码器参数更新:主编码器的参数通过反向传播和梯度下降更新,以优化对比损失。