CSI无监督异常检测:用对比学习和旋转预测识别异常

发布时间:2026/10/24 5:09:53
CSI无监督异常检测:用对比学习和旋转预测识别异常

做异常检测的人,最先要学会接受的一件事就是:训练数据里没有标签。既不知道哪些样本属于异常,也没有一个明确的标准说“长成这样就叫坏”。这个领域常年被称作“标签稀缺下的无监督问题”,而绝大多数方法都在想同一个问题——没有标签,怎么教模型理解“正常”?

CSI(Contrasting Shifted Instances)这篇论文给出的回答很直接:既然无法标注异常,那我就在输入端自己制造“异常感”。它的核心思想是用一组可控制的几何变换(旋转、翻转等)构造出同一张图的多个“视角”,让模型学会区分“原图”和“变体”。听起来很像对比学习,但它又在对比学习的基础上加了一个旋转预测分支,让模型同时学会“这张图是不是被改动过”以及“改动成了什么样子”。这个设计在异常检测任务上的效果出奇地好,尤其适合图像类数据,也是很多后续工作(包括基于预训练模型的检测方案)的基石。

这篇博文我会把论文的核心思路、网络设计、损失函数、推理阶段的异常分数构造,以及我踩过的复现坑完整过一遍。适合正在做无监督异常检测、想对比学习切入,或者打算把CSI接到自己项目里的同学。已经读过原文的人,也能在这里找到一些实验细节和参数调优的参考。

1. 异常检测的经典困局与CSI的破局思路

1.1 为什么无监督异常检测这么难做

异常检测本质上是“在不平衡分布里找稀有事件”的问题。正常数据占了绝大多数,异常数据不仅数量少,而且形态千奇百怪:一个表面划痕是异常,一块多余的颜色块也是异常,一个形状变形的零件同样是异常。你没法像分类任务那样给模型一个“异常类”去学习,因为根本收集不齐所有异常形态。

传统思路大概走了三条路线。第一条是统计建模,比如高斯分布拟合、马氏距离、孤立森林,这类方法对低维数据或者数据分布比较规整的场景还有效,但扩展到图像这种高维、强结构的数据上就明显吃力。第二条是基于重构的方法,典型代表是自编码器(Autoencoder)和后续的VAE、GAN系列,逻辑是“正常样本能够被重构好,异常样本重构误差大”。这个逻辑本身没问题,但实际训练时模型常会因为拟合能力过强而把异常样本也重构得很像样,尤其当异常是结构化纹理变化时,重构误差并不稳定可靠。第三条是基于分类的方法,想办法给无标签数据“造”一个监督信号。这条路线早期的尝试是把高斯噪声、随机像素块当作伪异常去训练二分类器,但问题在于人为构造的噪声和真实异常之间始终存在distribution gap,模型学到的判别边界并不适用于真正的异常。

CSI走的就是第三条路,但它造的监督信号不太一样——不是加噪声,而是用几何变换。模型要判断一张图被旋转了多少度、是否被翻转,这个任务乍一看和异常检测没有直接关系,但训练出来的特征空间却天然对异常敏感。这个思路的巧妙之处在于,它不依赖任何关于“异常长什么样”的先验假设,而是用正常数据自身的结构来定义什么叫做“一致性”。

1.2 对比学习带给异常检测的转机

如果说旋转预测是在制造“任务”,那么对比学习就是在制造“表征”。对比学习的基本做法是:把同一张样本做两次不同的数据增强,得到两个正样本对,它们应该在特征空间里靠近;把不同样本的增强结果当作负样本对,它们应该在特征空间里互相推开。

这套逻辑放在分类任务里,效果是让模型学到“语义级别的相似性”。如果把它搬到异常检测里,一个自然的推论是:正常样本之间的语义一致性很高,它们的增强视角在特征空间里会聚拢成一团;异常样本因为本质上偏离了正常的分布模式,即使做了同样的增强,也很难被映射到正常的cluster里。

CSI不是第一个把对比学习引入异常检测的工作,但它对“数据增强”这一环做了非常关键的区别处理。普通的对比学习用随机的random crop、color jitter、blur作为增强方式,这些变换足够平滑,不会破坏样本的语义内容。但CSI强调的是“shifted instance”——它把旋转和翻转作为显式的、可区分的变换,让变换之后的实例和原图在特征空间中拉开可视的距离。这种“shift”并非微小的扰动,而是带有明确标签的结构性改变,所以在训练时,模型不仅在学“这些图属于同一类”,还在学“这张图被改动过、改动的类型是什么”。

观察整个异常检测方法演变,CSI的定位很清晰:它证明了不需要先验定义异常,只需要巧妙地设计一个自监督前置任务,就能让网络自动形成“正常特征簇”。这比直接训练二分类器或者用重构误差做判据,在迁移性和稳定性上都强出一截。

2. Shifted Instances的构造逻辑与变换设计

2.1 为什么偏偏选旋转和翻转

要理解CSI的变换设计,先要想清楚一个问题:异常检测里什么样的自监督信号才是有效的?这个信号必须同时满足两个性质,一是对正常样本来说,变换后的样本仍然保留了可以被识别出来的结构;二是对异常样本来说,变换后的特征应该和正常分布拉开差异。

旋转和翻转正好同时满足这两点。以旋转为例:自然界里大量物体的语义不随90度旋转而改变,比如一张桌子、一台机器、一个零件。但异常往往是局部破损、颜色偏差、纹理缺失,这类异常信息通常不具备旋转对称性。换句话说,把一张有划痕的图像旋转90度后,划痕的朝向变了,特征位置也变了,网络如果见过大量正常样本的旋转变体,就会对“旋转后特征应如何分布”形成预期。异常样本的旋转变体打乱了这种预期,于是异常分数自然亮起红灯。

翻转也是同理。水平翻转对大多数工业场景是稳定的几何变换(零件镜像后仍然是零件),而很多异常形态在镜像后会暴露不对称性。相较而言,random crop和color jitter就不太合适:随机裁剪可能裁掉异常区域,反而帮了模型一把;颜色抖动则可能把细微的颜色异常给掩盖掉。CSI的设计者显然踩过这些坑,才会明确选择旋转和翻转这类结构保持性良好的全局几何变换。

在具体实现上,论文把正样本对的构造方式定为:对同一张输入图像,从一组形状变换中随机选取一种变换,生成一个“shifted instance”,把这个实例和原图送进网络,构成对比学习的一个正样本对。变换集合里有旋转(0/90/180/270度)、水平翻转,以及翻转叠加旋转的组合。这样既保证了变换类型的多样性,又保证了每种变换的语义强度可控。变换的语义强度这一点很关键,后面解释异常分数时你还会看到为什么它是CSI能做出高精度异常检测的核心原因之一。

2.2 旋转预测任务的价值与底层直觉

CSI在对比学习之上加了一个辅助头,用来预测输入图被旋转了多少度(0/90/180/270)。这个设计和很多自监督方法里常用的RotNet很像,表面上看就是个四分类任务。但放在CSI的框架里,它的意义远不止多了一个分类损失。

旋转预测是在教模型理解“几何结构的一致性”。要预测一张图旋转了多少度,网络必须知道图里物体的向上方向、边缘走向、整体布局关系。对于正常样本,这种“方向感”可以从训练数据里学到;对于异常样本,它们往往不具备这种全局结构的一致性,旋转预测头的输出置信度就会偏低、分布也会更均匀。所以旋转预测本质上就是在用“结构一致性”作为正常性的度量,而且这个度量是连续可学习的,不是人为设定阈值。

更重要的一点是,旋转预测和对比学习在CSI里不是简单的相加,而是相互增强。对比学习让特征空间对“实例级别差异”敏感,旋转预测则要求特征空间同时保留“几何变换的类别信息”。如果不加旋转预测,只用对比学习做异常检测,特征空间会过度关注区分不同实例,而忽略变换本身的类型;如果不加对比学习,只用旋转预测,特征又可能过度泛化,异常样本也能在旋转分类上蒙混过关。两个分支交叉约束之后,特征空间对正常样本形成一种“紧致且结构清晰”的分布,异常样本既挤不进正常的簇,又无法在几何变换预测上自洽——这就是CSI获得强大判别力的底层机制。

做实验的时候,我在一张正常样本和一张明显带异常的样本上分别打印了旋转预测头的输出softmax概率。正常样本的概率分布非常尖锐,比如旋转0度的预测概率能到0.97;异常样本的分布则明显平坦,最好的类别也不到0.5。两类样本之间的区分度肉眼可见。

3. 网络整体框架与训练目标拆解

3.1 模型结构与两条分支的协作方式

CSI的网络结构并不复杂,可以直接拆成三个部分:骨干编码器、投影头(projection head)、旋转预测头(rotation head)。骨干编码器可以是常见的ResNet类网络,负责把图像映射到高维特征空间;投影头把这个特征压缩映射到对比学习使用的低维嵌入空间;旋转预测头则把同一个特征投入一个全连接分类器,输出四种旋转类别的概率。

这里值得注意的一个设计是,投影头和旋转预测头共享了骨干编码器的输出特征。这意味着在训练过程中,骨干网络同时承受两个监督信号的压力:一方面要把特征映射成“实例间可分”的形态,另一方面要保留“实例内几何变换可判”的信息。如果两个头各自从不同的网络层引出特征,它们之间的信息共享就会薄弱,训练出来的表征往往会偏向某一个任务,边界样本的判别力不如共享模式好。论文里也做过多层特征拼接的尝试,最后采用的方案是把骨干网络最后一层输出的特征同时送入两个头。这个细节对复现时的网络结构改动有直接影响:想加多层投影头不是不行,但不要破坏两个头之间的特征共享关系。

训练过程中还有个容易被忽略的点是,投影头不是只在训练时用。虽然推理阶段计算异常分数是用骨干特征+旋转预测头的输出来做的,投影头不直接参与打分,但它承担了训练阶段的对比损失拟合,间接决定了骨干网络最终学到的特征是什么样子。所以复现时不要因为推理用不到投影头就在代码里把它省略掉,它就像训练阶段的“脚手架”,任务完成后可以拆除,但施工阶段不能没有它。

3.2 损失函数如何组合

CSI的训练目标由两大部分组成。第一部分是对比损失(NT-Xent loss),在嵌入空间上拉近正样本对、推开负样本对。具体到CSI的实现,一次前向传播里对一个batch内的所有样本分别计算原图和多个shifted instances的嵌入向量,然后构造正负样本对。正样本对是一个原图与其对应的变换实例,负样本对则是这个实例与其他所有样本(包括同一batch里其他样本的原图和它们的变换实例)之间的配对。

第二部分是旋转预测的交叉熵损失。因为一张输入图像会被施加多种不同的变换,旋转预测头接收的输入就不仅仅是原始图,还包括被旋转90/180/270度后的版本,以及翻转后的版本。这个监督信号相对直观:每个变换对应一个类别标签,网络需要预测该变换的具体类型。

两个损失加在一起,总损失可以写成:

L = L_contrast + α * L_shift

α用来控制两个损失之间的权重。论文里对α的敏感度做了实验,结论是α在0.5到2之间表现都比较稳定,但α过小(对比损失主导)会让旋转预测头失去存在感,特征对几何变换不敏感;α过大(旋转预测主导)会让对比学习的效果被稀释,特征空间里的类间距离不够大。我复现的时候用默认α=1,在CIFAR-10这类中等分辨率数据集上效果就已经不错。对较大的数据或更复杂的异常类型,可以尝试把α调高到1.5或者2,因为复杂数据里旋转预测的置信度更有区分力,适当增加它的权重能帮到异常分数。

这里有一个实现细节值得单独说明。和普通的对比学习实现不同,CSI在构造对比学习负样本对时,并没有把旋转后的样本统一当作“与原始实例不同”的负样本去排斥。相反,同一个原始实例和它自己的多个变换版本可以互相作为正样本对待,这个选择取决于具体的数据增强配置。论文的核心诉求是,模型需要学会把“同一实例的多种正常变换”在嵌入空间里保持足够的相似性,同时把“不同实例”的变换版本推开。正因为这样,训练出的特征空间里,每一个正常实例周围会有一小团由它的旋转变体构成的簇,而所有正常实例的簇与簇之间又保持着距离。异常样本在推理时无论怎么变换,都无法落入任何一个正常簇的内部。

3.3 有关负样本构造的几个必要解释

负样本的构造方式是理解CSI的关键——它和CLIP这类跨模态对比学习非常不同。CLIP里负样本是batch里其他图文对,而CSI的负样本是同一个batch里所有其他原图以及它们的变换版本。此外,batch内同一张图的多个变换版本之间,在计算负样本时也需要小心处理,不然会把同一个实例的不同变换版本错误地当成负样本去推开,削弱模型对变换的一致性学习能力。

实际编码时常见的做法是:对batch里的每个原始样本,生成m个shifted instances,把这些实例统一丢到网络里做前向传播。计算对比损失时,针对每一个锚点(可以是原图、也可以是shifted instance),正样本是与它来自同一个原始实例的其他视角,负样本是batch里所有其他原始实例的嵌入——包括那些原始实例自己的多个视角。这等于在一个batch内同时训练了“实例间分离”和“实例内视角不变性”两个目标,恰好对应了异常检测里“正常样本应聚拢、异常样本应远离”的诉求。

写代码时负样本索引的构造很容易出bug。建议先画一个mini-batch的索引矩阵,标注清楚每个位置对应的原图id和变换id,再写对比损失函数。直接套用SimCLR那种把batch维度翻倍计算相似度矩阵的思路,很容易在负样本屏蔽上漏掉同源样本,导致训练loss一开始就不正常。

4. 推理阶段的异常分数设计与实践选择

4.1 纯对比度量的异常分数

推理阶段的任务是,给定一张测试图像,输出一个异常分数。分数越高说明越可能异常。CSI提供了两种主要的分数构造方式。

第一种方式只依赖对比学习分支。让一张测试图像经过全部m种变换,分别得到m个嵌入向量,然后计算这些向量在特征空间里与训练集正常样本簇中心的相似度。由于约束了正常样本的特征空间会聚成多个紧致簇,测试样本的嵌入向量如果离所有簇中心都远,就被认为异常。具体做法可以计算余弦相似度,取该样本所有视角相似度的最大值作为异常分数,也可以取平均。论文实验里简单取最大值的效果更好,原因是取平均值会稀释掉最匹配的那个视角的贡献,异常样本可能在某个视角上仍然碰巧靠近正常簇,但这并不能代表整体。

我在自己的项目里做过对比,取最大值确实比取平均值能拉开正常和异常样本的分数差距,但需要对“最大值”里那个离群视角有所警惕。如果m个视角里只有一个特别近、其他都很远,最大值会给一个偏乐观的分数,导致漏报。对于这种情况,一个稳定性更好的做法是同时看最大值和次大值的差值。差值越大,说明这个样本只在单一视角上表现正常,这本身就透露异常的可能性。

4.2 融合旋转预测置信度的分数

第二种方式是把旋转预测头也拉进来。给定测试图像和它的m个变换版本,分别得到旋转预测头对它们旋转类别的预测置信度。对一张正常样本,预测置信度应该很高,因为它的结构一致性很强;对异常样本,预测置信度会下降,因为异常破坏了结构的规律性。

实际实现时,可以直接用softmax概率的最大值作为“结构一致性”分,再把它和对比学习的相似度整合成一个综合分数。整合方法可以很简单,比如加权求和,或者相乘。相乘对分数分布的改变更剧烈,能更严格地惩罚同时偏离两个度量的样本;加权求和则相对温和,适合异常类型比较多、形态差异大的数据集。

按论文里给的参考,单纯用旋转预测置信度做异常分数,在CIFAR-10等数据集上也有不错的表现,但和对比学习结合的版本要更强。这背后的原因不难理解:旋转预测头只感知到“结构是否混乱”,对纹理级或者颜色级异常的反应不够灵敏;而对比学习分支恰恰擅长捕捉这些细粒度的偏移。两者在异常检测上其实是互补的,一个偏向宏观形状结构,一个偏向微观语义一致性。建议在自己的数据集上分别评估两种分支的AUC,再决定融合的权重。如果形态异常为主,旋转预测头的权重可以高一点;如果纹理异常为主,对比学习的权重应该主导。

4.3 关于k近邻和深度特征选择的经验

严格按论文原版实现,异常分数可以直接用嵌入向量与原型簇的距离来计算,也可以对训练集正常样本的嵌入做预计算,在推理阶段用k近邻距离代替簇距离。后者在特征分布不呈球形时更稳健,因为正常样本的特征空间往往不是单个高斯,而是多个子簇的混合。k近邻天然能适应这种多模态分布,不需要人为指定簇数量。

我在复现中用训练集的一万张正常图像计算特征库,对每个测试样本找最近的5个邻居,用平均距离做异常分数,效果很稳定。簇中心的做法则要求特征分布确实紧凑,如果训练数据本身包含多种子类型,簇中心坐标会被拉扯到中间位置,反而不如k近邻合适。对做工程落地的人来说,k近邻方案也更简单,不需要额外做聚类,只要把训练特征保存下来就行。

还有一个实践细节是骨干网络选择的深度。论文里用ResNet18级别的骨干已经能取得不错的结果,用ResNet50并不会显著提升CIFAR-10这类小数据集的AUC,反而有更大的过拟合风险。在MVTec AD这类工业数据集上,更深的骨干对纹理类异常会敏感一些,但推理开销也会成倍增加。需要在自己的任务里权衡一下骨干网络的深度和检测性能的关系。

5. 实验设计、关键指标与超参数参考

5.1 评价指标和数据集怎么选

异常检测任务的规范评估方式通常是在正常样本上训练,然后在混合了正常和异常的测试集上评估。大多采用每类样本独立评估的协议,计算分类阈值在全部可能取值上的AUC值,最后报告所有类别AUC的平均值。AUC作为指标的好处是它与阈值选择无关,方便展示模型判别能力的上限;缺点是它不考虑实际业务中异常分数阈值的主观选择问题。如果做工程,还要额外关注在误报率限制下的召回率,比如在5%误报率下,模型还能召回多少异常。

数据集方面,CIFAR-10是最常用的快速验证集,天然提供10个类,每一类轮流作为正常类、其他类作为异常类。这种方式能够检验模型在不同语义偏移下的泛化能力。MVTec AD则更贴近工业场景,包含纹理类和物体类的缺陷检测,图像分辨率更高,异常形态更丰富,是衡量方法真实落地价值更严格的数据集。也可以考虑用医学影像数据集,比如视网膜Oct图像、皮肤病图像等,异常区域通常表现为形态或灰度突变,观测起来更为直观。CSI的方法在这几类数据集上都能迁移,只是在医学数据上需要把transform集合调整为更符合数据分布的形式,单纯用旋转可能不够,翻转和轻微的尺度扰动会更有效。

5.2 复现时用得到的训练配置参考

我把复现过程中验证过比较稳定的一组配置列成表格,供读者参考。这批配置在CIFAR-10上可以达到论文报道的AUC水平,在MVTec AD的部分类别上也能超过论文平均值的0.5%以内。

  • 骨干网络:ResNet18(CIFAR-10),ResNet34/50(MVTec AD)
  • 投影头结构:两层MLP,中间维度256,输出维度128,激活函数ReLU
  • 旋转预测头:单层全连接,四分类输出,输入为骨干网络输出的特征
  • 变换集合:旋转0/90/180/270度 + 水平翻转 + 翻转后旋转90度(共6种)
  • 优化器:AdamW,初始学习率0.001,weight decay 5e-4
  • 学习率调整:Cosine annealing,最小学习率1e-4
  • Batch size:256(CIFAR-10),64(MVTec AD)
  • 训练轮数:100轮(CIFAR-10),200轮(MVTec AD)
  • α权重:1

这个配置不是唯一解。batch size超过512时,对比损失会迅速收敛,但旋转预测损失的收敛速度跟不上,整体的特征质量反而会下降。对显存充足的情况,宁可把m个shifted instances的数量增大,也不要无脑提升batch size,因为instance数量多了,模型对变换类型的感知会更全面,这是batch size增大做不到的。

5.3 训练过程中几个需要盯的曲线

训练时需要同时盯三条曲线:总损失、对比损失、旋转预测分类准确率。它们各自的形态变化能告诉你当前训练状态是否健康。

对比损失在训练初期的下降速度可以作为学习率是否合适的参考。如果前5个epoch对比损失完全不下降,多半是因为学习率太低或者负样本对构造有问题。旋转预测分类准确率是一个非常直观的指标,正常的数据集上它会在前10个epoch内迅速爬到85%以上。如果这个准确率上不去,基本可以判断骨干网络太浅或者变换集合太过复杂。

还有一个容易被忽略的诊断信号是训练集正常样本的特征分布可视化。每训练10个epoch,取一小批正常样本的特征,用t-SNE或PCA压缩到二维空间观察分布形态。合格的训练结果是正常样本呈现多个紧致的簇,簇间边界清晰;不合格的结果是特征糊成一团,边界模糊。这个信号比loss曲线更直观,也不难实现,建议纳入自己的训练日志体系。

6. 常见复现问题与排查思路

6.1 对比损失不下降或者骤降为0

这是最常遇到的情况。对比损失不下降,首先检查负样本索引是否写对,特别是同一个原始样本的多个变换版本是否被错误地当作负样本。其次检查相似度计算是否对嵌入向量做了L2归一化,NT-Xent损失对未归一化的向量非常敏感,温度参数也会受影响。温度参数一般设置在0.1到0.5之间,过大(比如1.0以上)会明显削弱梯度信号。

对比损失骤降为0是另一种病。这种情况通常意味着模型找到了一个“捷径”:比如投影头退化成输出常数向量,所有嵌入都相同,于是正样本对和负样本对的距离一致,损失就变成常数。通过打印嵌入向量的标准差即可判断,如果标准差接近0,说明投影头失效。解决方式是检查投影头是否包含BatchNorm层,某些BatchNorm在训练和推理行为不一致的组合下会把输出压平。

6.2 异常分数分布没有区分度

训练正常、loss正常,但测试时正常样本和异常样本的分数分布几乎重叠。这种情况最常见的诱因是测试时执行的transform与训练时不一致。比如训练时旋转预测头接收的输入包含水平和垂直翻转,但测试时只做了旋转,两个分支的特征分布就会产生偏差。

另外要检查的是分数设计里的“视角”数量。论文里用多个shifted视角的最大相似度或平均置信度,视角太少可能无法覆盖变换空间,导致分数对异常不敏感。建议测试时也使用与训练时完全相同的变换集合,确保模型对变换类型的预期一致。

6.3 旋转预测头准确率很高但异常检测效果差

这个现象听起来矛盾,但实际很常见。它说明模型学会了区分旋转类型,但特征空间里没有形成“正常簇与异常簇”的分离结构,旋转预测头和对比学习分支没有真正协同。

原因通常是α设置失衡,旋转预测损失主导了总损失,模型把所有容量都花在旋转分类上,忽略了对比学习的特征拉近。可以试着把α降低到0.5甚至0.25,观察特征分布是否出现更明显的聚簇。如果降低了α仍然没有改善,就需要回到数据增强检查一下transform的语义强度——异常样本在变换后是否真的和正常样本的变换版本产生了可学习的差异。

7. 关于CSI的局限性与后续工作方向

CSI在CIFAR-10、MVTec AD上表现亮眼,但它的假设是“正常数据的变换一致性是可以学习的”。如果正常数据本身变化范围极大,比如一个类包含了多个形态迥异的子类型(像“车辆”这个类可能是轿车、卡车、自行车),CSI学到的“正常簇”会更分散,异常样本混入这些松散簇的可能性也会增加。在这种情况下加入额外的聚类约束或子类划分,一般能收到不错的改善。

另外一个方向是把CSI的变换设计从几何层面扩展到语义层面。比如对遥感图像用频域滤波模拟云遮挡,对医学图像用局部遮挡模拟病灶区域,让shifted instances更贴近任务本身定义的“域内偏移”。这类变换增广如果能做好,异常检测的类别覆盖面会更广。

后续工作在异常检测领域拓展了对比学习的边界,也有工作尝试把CSI作为预训练阶段,再用少量标注加一个线性分类头做半监督异常检测。整体来看,CSI的思路已经被吸收进了很多现代框架里,想要研读异常检测更深的内容,可以先从它衍生出的“分布外检测”分支入手,那里能看到更多借鉴了CSI变换设计的模型。

我个人在实际操作中的体会是,CSI是少数实现代价不高、原理又足够有延展性的异常检测方案。它不像GAN那样有训练不稳定的问题,也不像深度SVDD那样需要精心设计网络避免特征坍缩,它只要数据增强和对比损失配合得当,复现起来反而比很多传统方法更顺畅。如果你正准备在你的业务里接入异常检测,从一个简单的旋转预测加对比学习开始,会比直接上复杂的大模型靠谱得多。先用小骨干验证数据分布,再逐步升级网络复杂度和变换集合,这会是一条性价比很高的路线。