> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 图像数据异常值检测
- URL: https://www.hubwiz.com/blog/outlier-detection-on-image-data/
- Published: 2025-01-04T00:37:38.000Z
- Updated: 2025-01-04T00:37:38.000Z
- Description: 在这篇文章中，我将研究为什么基于深度学习的方法在某些模态的异常值检测中效果很好。
- Author: admin
- Tags: APPLICATION, OUTLIER DETECTION, AUTOENCODER, PYOD, ALIBI-DETECT, PYTORCH, MNIST, #Import 2026-10-14 14:41

在过去的几年中，深度学习方法已被证明对许多机器学习问题极为有效，并且毫不奇怪，这包括了几个异常值检测领域。事实上，对于许多数据形式，包括图像、视频和音频，除了基于深度学习的方法之外，实际上没有其他可行的异常值检测方法。

但与此同时，对于表格和时间序列数据，更传统的异常值检测方法仍然往往更可取。这很有趣，因为深度学习往往是解决许多问题的非常有效的方法（深度学习已经能够解决许多使用任何其他方法都无法解决的问题），但表格数据尤其难以应用基于深度学习的方法，至少在某种程度上无法与更成熟的异常值检测方法相媲美。

在这篇文章（以及下一篇更侧重于表格数据的自监督学习的文章）中，我将研究为什么基于深度学习的方法在某些模态的异常值检测中效果很好（具体查看图像数据，但同样的想法也适用于视频、音频和其他类型的数据），以及为什么它对表格数据有局限性。

此外，我将介绍几个仍然需要认真研究深度学习进行表格数据异常值检测的原因。一是该领域发展迅速，取得了很大进展，未来几年我们很可能会看到表格数据异常值检测方面取得一些最大的进展。

另一个原因是，虽然更传统的方法（包括基于 z 分数、四分位距、直方图等统计测试，以及经典的机器学习技术，如隔离森林、k-最近邻、局部离群因子 (LOF) 和 ECOD）往往更受欢迎，但也有一些例外，即使在今天，基于深度学习的方法也可能是表格离群值检测的最佳选择。我们也会看看这些。

本文继续关于离群值检测的系列文章，涵盖子空间的使用、PCA、距离度量学习、共享最近邻、频繁模式离群值因子、计数离群值检测器和掺杂。

## 1、使用图像数据进行异常值检测

如前所述，对于某些数据模式（包括图像数据），目前除了基于深度学习的方法之外，没有其他可行的异常值检测方法，因此我们将首先研究基于深度学习的图像数据异常值检测。

对于本文，我假设你对神经网络和嵌入的概念相当熟悉。如果不熟悉，我建议你阅读网上的许多入门文章，并快速掌握它们。不幸的是，我无法在这里提供这些信息，但是一旦你对神经网络和嵌入有了很好的了解，就应该能够理解本文的其余部分。

有许多这样的方法，但都以某种方式涉及深度神经网络，并且大多数通过生成嵌入来表示图像。

一些最常见的基于深度学习的异常值检测技术基于自动编码器、变分自动编码器 (VAE) 和生成对抗网络 (GAN)。我将在本文中介绍几种异常值检测方法，但自动编码器、VAE 和 GAN 是不错的起点。

这些都是较老的、成熟的理念，也是异常值检测中一个常见主题的例子：工具或技术通常是为了一个目的而开发的，后来被发现对异常值检测有效。许多其他示例包括聚类、频繁项集、马尔可夫模型、空间填充曲线和关联规则。

鉴于篇幅限制，我将在本文中仅介绍自动编码器，但将尝试在以后的文章中介绍 VAE、GAN 和其他一些方法。自动编码器是一种神经网络，最初实际上是为压缩数据而设计的（其他一些压缩算法有时也用于异常值检测）。

与聚类、频繁项集、关联规则、马尔可夫模型等一样，其理念是：我们可以使用某种类型的模型来对数据进行建模，然后创建数据中主要模式的简明摘要。例如，我们可以通过描述聚类（如果数据聚类良好）、数据中的频繁项集、特征之间的线性关系等来对数据进行建模。使用自动编码器，我们用原始数据的压缩向量表示对数据进行建模。

这些模型通常能够很好地表示数据中的典型项（假设模型很好-构造），但通常无法很好地对异常值进行建模，因此可用于帮助识别异常值。例如，对于聚类（即，当使用一组聚类来建模数据时），异常值是与聚类不太吻合的记录。对于频繁项集，异常值是包含少量频繁项集的记录。对于自动编码器，异常值是压缩效果不佳的记录。

如果模型是深度神经网络的形式，则它们具有能够表示几乎任何类型的数据（包括图像）的优势。因此，自动编码器（以及其他深度神经网络，如 VAE 和 GAN）对于图像数据的异常值检测非常重要。

许多异常值检测器也是使用一种称为自监督学习 (SSL) 的技术构建的。这些技术在异常值检测中的应用可能不如自动编码器、VAE 和 GAN 那么广泛，但它们非常有趣，值得一看，至少值得快速了解一下。我将在下面介绍这些内容，但首先我将介绍使用图像数据进行异常值检测的一些动机。

## 2、使用图像数据进行异常值检测的动机

一种应用是自动驾驶汽车。汽车将配备多个摄像头，每个摄像头都会检测一个或多个物体。然后，系统将预测图像中出现的每个物体是什么。这些系统面临的一个问题是，当摄像头检测到物体时，系统会预测该物体的类型，但可能会预测错误。此外，它可能会预测错误，但置信度很高；神经网络可能特别倾向于对最佳匹配表现出很高的置信度，即使是错误的，这使得很难从分类器本身确定系统是否应该对检测到的物体更加谨慎。当看到的物体与用于训练系统的任何训练示例不同时，这种情况最容易发生。

为了解决这个问题，异常值检测系统可以与图像分类系统并行运行，当以这种方式使用时，它们通常专门寻找似乎超出训练数据分布范围的项目，称为分布外数据 OOD。

也就是说，任何视觉分类系统都是针对一些可能非常大但有限的对象集进行训练的。对于自动驾驶汽车，这可能包括交通信号灯、停车标志、其他汽车、公共汽车、摩托车、行人、狗、消防栓等（模型将被训练以识别这些类别中的每一个，并在每个类别的许多实例上进行训练）。但是，无论系统被训练识别多少类型的项目，在路上可能会遇到其他类型的（分布外）物体，因此确定系统何时遇到未识别的物体非常重要。

这实际上是使用图像数据进行异常值检测的一个共同主题：我们经常对识别不寻常的物体感兴趣，而不是不寻常的图像。也就是说，诸如不寻常的光照、色彩、摄影角度、模糊和图像本身的其他属性等因素通常不那么有趣。通常，背景也会分散我们对识别不寻常物品的主要目标的注意力。也有例外，但这很常见，我们真正感兴趣的是图片中显示的主要物体（或少数相关物体）的性质。

自动驾驶汽车对物体进行错误分类可能是一个相当严重的问题——车辆可能会得出结论，一个新物体（例如它在训练期间没有看到的车辆类型）是完全不同的物体类型，很可能是视觉上与训练期间看到的任何物体类型最接近的匹配。例如，它可能预测新车辆是广告牌、电线杆或其他不动的物体。但是，如果并行运行的异常检测器识别出这个物体是不寻常的（并且很可能超出分布范围，OOD），整个系统可以采取更保守和谨慎的方法处理该物体，并且可以激活任何相关的故障安全机制。

图像数据异常检测的另一个常见用途是医学成像，其中图像中出现的任何异常都可能令人担忧，值得进一步研究。同样，我们对图像本身的异常属性不感兴趣——只对图像中的任何对象是否是 OOD 感兴趣：与训练期间看到的任何东西都不一样（或在训练期间很少看到），因此很少见，可能是一个问题。

其他示例是检测安全摄像头或监控工业过程的摄像头中出现异常物体的位置。同样，任何异常都可能值得注意。

对于自动驾驶汽车，检测 OOD 物体可能使团队能够增强其训练数据。对于医学成像或工业过程，任何异常通常都有可能成为问题。而且，与汽车一样，只要知道我们检测到了 OOD 物体ct 允许系统更加保守，不假设分类预测是正确的。

由于检测图像中的 OOD 对象是视觉中异常值检测的关键，因此通常进行的训练和测试都与此有关。通常使用图像数据，异常值检测系统会在一个数据集合中的图像上进行训练，并使用另一个类似的数据集进行测试，假设图像足够不同，可以被视为来自不同的分布（并包含不同类型的对象）。然后，这将测试检测 OOD 数据的能力。

例如，可以使用一组涵盖 100 种鸟类的图像进行训练，并使用另一组鸟类图像进行测试。我们通常假设，如果使用不同的图像来源，则第二组中的任何图像至少会略有不同，并且可以假设为超出分布，尽管也可以使用标签来更好地限定这一点：如果训练集包含欧洲绿雀，并且测试集也包含，则可以合理地认为这些不是 OOD。

## 3、自动编码器

为了开始更具体地了解如何使用神经网络进行异常值检测，我们首先介绍一种最实用和最直接的方法，即自动编码器。在 Python 中的异常值检测中有更全面的介绍，以及不同软件包中可用的 VAE、GAN 及其变体的介绍，但本文将介绍至少一种执行异常值检测的方法。

如前所述，自动编码器是一种神经网络，传统上用作压缩工具，尽管它们也被发现可用于异常值检测。自动编码器接收输入并学习以尽可能少的损失对其进行压缩，以便可以将其重建为接近原始数据。对于表格数据，自动编码器一次给出一行，输入神经元对应于表格的列。对于图像数据，它们一次给出一张图像，输入神经元对应于图片的像素（尽管图像也可以以嵌入格式给出）。

下图提供了自动编码器的示例。这是一种特定形式的神经网络，其设计目的不是预测单独的目标，而是重现给予自动编码器的输入。我们可以看到，网络的输入元素（网络最左边的神经元，以橙色显示）与输出元素（网络最右边的神经元，以绿色显示）一样多，但在两者之间，各层的神经元较少。中间层的神经元最少；这一层代表每个对象的嵌入（也称为瓶颈或潜在表示）。

![](https://www.hubwiz.com/blog/content/images/2025/01/image-55.png)

具有 8 个输入神经元和中间层 2 个神经元的自动编码器

中间层的大小是我们尝试压缩所有数据的大小，以便可以在后续层中重新创建（或几乎重新创建）。创建的嵌入本质上是一个简洁的浮点数向量，可以表示每个项目。

自动编码器有两个主要部分：网络的第一层称为编码器。这些层将数据逐渐缩小到更少的神经元，直到到达网络中间。网络的第二部分称为解码器：一组与编码器层对称的层，它们采用每个输入的压缩形式并尝试将其尽可能接近地重建为原始形式。

如果我们能够训练一个重建误差较低的自动编码器（网络的输出往往与输入非常接近），那么如果某些记录具有较高的重建误差，它们就是异常值——它们不遵循允许压缩的数据的一般模式。

压缩是可能的，因为表格数据中的特征之间、文本中的单词之间、图像中的概念之间等通常存在一些关系。当项目是典型的时，它们遵循这些模式，并且压缩可以非常有效（损失最小）。当项目不典型时，它们不遵循这些模式，并且不能在没有更严重损失的情况下进行压缩。

层的数量和大小是一个建模决策。数据包含的模式越多（特征之间的常规关联），我们就越能压缩数据，这意味着我们可以在中间层使用的神经元越少。这通常需要一些实验，但我们希望设置网络的大小，以便大多数记录可以构建得非常少，但有一些错误。

如果大多数记录可以零错误地重新创建，则网络可能容量太大——中间层能够完全描述正在通过的对象。我们希望任何不寻常的记录都有更大的重建误差，但也能够将其与典型记录的中等误差进行比较；很难如果几乎所有其他记录的误差都是 0.0，则衡量记录的重构误差有多不寻常。如果发生这种情况，我们知道我们需要缩减模型的容量（减少神经元的数量），直到不再可能。事实上，这可能是调整自动编码器的一种实用方法——例如，从中间层的许多神经元开始，然后逐渐调整参数，直到获得所需的结果。

通过这种方式，自动编码器能够为每个对象创建一个嵌入（项目的压缩形式），但我们通常不会在此自动编码器之外使用嵌入；异常值分数通常完全基于重构误差。

但情况并非总是如此。在中间层创建的嵌入是对象的合法表示，可用于异常值检测。下图显示了一个示例，其中我们使用两个神经元作为中间层，这允许将潜在空间绘制为散点图。 x 维度表示一个神经元中出现的值，y 维度表示另一个神经元中出现的值。每个点表示一个对象的嵌入（可能是图像、声音片段、文档或表格行）。

然后可以在潜在空间上使用任何标准异常值检测器（例如 KNN、孤立森林、凸包、马哈拉诺比斯距离等）。这提供了一个异常值检测系统，如果仅限于二维或三维，则在某种程度上是可解释的，但是，与主成分分析 (PCA) 和其他降维方法一样，潜在空间本身是不可解释的。

![](https://www.hubwiz.com/blog/content/images/2025/01/image-56.png)

中间层有两个神经元的自动编码器创建的潜在空间示例。每个点代表一个项目。在这个空间中可视化异常值很简单，尽管 2D 空间本身是不可理解的。

假设我们使用重构误差来识别异常值，为了计算误差，可以使用任何距离度量来测量输入向量和输出向量之间的距离。通常使用余弦、欧几里得或曼哈顿距离，其他一些距离也相当常见。在大多数情况下，最好在执行异常值检测之前对数据进行标准化，这样既可以让神经网络更好地拟合，也可以更公平地测量重构误差。考虑到这一点，每个记录的异常值分数可以计算为重构误差除以中值重构误差（对于某些参考数据集）。

另一种可能更稳健的方法是不使用单个误差度量进行重构，而是使用多个。这使我们能够有效地使用自动编码器为每条记录生成一组特征（每个特征都与重构误差的测量有关），并将其传递给标准异常值检测工具，该工具将找到由一个或多个重构误差度量给出的异常大值的记录。

一般来说，自动编码器可以成为一种有效的方法来定位数据中的异常值，即使在存在许多特征且异常值很复杂的情况下也是如此——例如表格数据，跨越许多特征。自动编码器面临的一个挑战是，它们确实需要设置架构（网络层数和每层神经元数量），以及与网络相关的许多参数（激活方法、学习率、辍学率等），这可能很难做到。

任何基于神经网络的模型都必然比其他模型更难调整。自动编码器的另一个限制是，它们可能不适用于所有类型的异常值检测。例如，对于图像数据，它们将在像素级别测量重建（至少如果像素用作输入），这可能并不总是相关的。

有趣的是，GAN 在这方面表现更好。将 GAN 应用于异常值检测的一般方法在某些方面是相似的，但稍微复杂一些。不过，这里的主要思想是，这种深度网络可以有效地用于异常值检测，并且它们对任何数据模态的工作方式都类似，尽管不同的检测器会标记不同类型的异常值，这些异常值可能比其他异常值更有趣或更不有趣。

## 4、图像数据的自监督学习

如前所述，自监督学习 (SSL) 是另一种使用图像数据（以及所有其他类型的数据）进行异常值检测的技术，也值得一看。

如果您习惯在其他情况下使用深度学习，那么您可能已经熟悉 SSL。它是深度学习的大多数领域的标准，包括大型神经网络最终用于分类、回归、生成或其他任务的地方。而且，如果您对大型语言模型很熟悉，那么您可能熟悉在一段文本中屏蔽单词并训练神经网络来猜测屏蔽单词的想法，这是一种 SSL 形式。

在处理图像时，这个想法是，我们经常有一个非常大量图像，或者可以轻松在线获取大量图像。实际上，我们通常只会使用经过自我监督训练的基础模型，但原则上我们可以自己做，无论如何，我们在这里描述的是创建基础模型的团队所做的事情。

一旦我们拥有大量图像，这些图像几乎肯定是未标记的，这意味着它们不能立即用于训练模型（训练模型需要定义一些损失函数，这需要为每个项目提供一个基本事实标签）。我们需要以某种方式为每幅图像分配标签。一种方法是手动标记数据，但这种方法成本高昂、耗时且容易出错。也可以使用自我监督学习，而且大多数情况下这更为实用。

使用 SSL，我们找到了一种安排数据的方法，使其可以以某种方式自动标记。如上所述，掩蔽就是其中一种方式，在训练大型语言模型时非常常见，同样的掩蔽技术也可用于图像数据。对于图像，我们可以掩蔽图像的某个区域（如下方的马克杯图像），而不是掩蔽单词，然后训练神经网络来猜测被掩蔽区域的内容。

![](https://www.hubwiz.com/blog/content/images/2025/01/image-57.png)

马克杯图像，其中 3 个区域被掩蔽

对于图像数据，还可以使用其他几种自监督学习技术。

总的来说，它们的工作原理是创建所谓的代理任务或借口任务。也就是说，我们训练一个模型来预测某些东西（例如图像中缺失的区域），借口是我们感兴趣的，但实际上我们的目标实际上是训练一个理解图像的神经网络。我们也可以说，任务是这个目标的代理。

这很重要，因为没有专门训练异常值检测的方法；代理任务是必要的。利用这些，我们可以创建一个对图像有良好总体理解的基础模型（理解程度足够好，能够执行代理任务）。与语言的基础模型非常相似，这些模型可以进行微调以用于其他任务。这可以包括分类、回归和其他此类任务，也可以包括异常值检测。

也就是说，以这种方式进行训练（使用自监督学习创建标签，并在代理任务上进行训练以预测该标签）可以创建一个强大的基础模型——为了执行代理任务（例如，估计图像被遮罩区域的内容），它需要对所处理的图像类型有很强的理解。这也意味着，它可能被很好地设置为识别图像中的异常。

SSL 用于异常值检测的技巧是识别好的代理任务，这使我们能够创建我们正在建模的域的良好表示，并使我们能够可靠地识别我们拥有的数据中的任何有意义的异常。

对于图像数据，有很多机会定义有用的借口任务。我们拥有许多其他模式所不具备的巨大优势：如果我们有一张物体的图片，并且我们以任何方式扭曲了该图像，它仍然是同一物体的图像。而且，正如所指出的，我们感兴趣的往往是物体，而不是图片。这使我们能够对图像执行许多操作，这些操作可以支持（即使是间接的）我们最终的异常值检测目标。

其中一些包括：旋转图像、调整颜色、裁剪和拉伸，以及其他此类图像扰动。执行这些转换后，图像可能看起来完全不同，在像素级别，它完全不同，但显示的对象是相同的。

这至少开辟了几种异常值检测方法。一种是利用这些转换为图像创建嵌入，并将异常值识别为具有不寻常嵌入的异常值。另一种是更直接地使用转换。我将在下一节中描述这两种方法。

## 5、创建嵌入并使用特征建模

有很多方法可以为图像创建嵌入，这可能对异常值检测有用。我将在这里描述一种称为对比学习的方法。

这利用了这样一个事实：同一幅图像的扰动版本将代表同一对象，因此应该具有相似的嵌入。鉴于此，我们可以训练一个神经网络，给定同一幅图像的两个或更多个变体，给出这些相似的嵌入，同时为不同的图像分配不同的嵌入。这鼓励神经网络专注于每幅图像中的主要对象而不是图像本身，并对颜色、方向、大小等的变化具有鲁棒性。

但是，对比学习只是为图像创建嵌入的一种方法，许多其他方法，包括任何自监督方法，可能最适合任何给定的异常值检测任务。

一旦我们有了图像的嵌入，我们可以识别具有不寻常嵌入的对象，这些嵌入将与大多数其他嵌入相差甚远。为此，我们可以使用嵌入空间中图像之间的欧几里得、余弦或其他距离测量。

## 6、直接使用借口任务

更直接地使用扰动来识别异常值也很有趣且非常有效。例如，考虑旋转图像。

给定一个图像，我们可以将其旋转 0、90、180 和 270 度，这样就有了同一张图像的四个版本。然后，我们可以训练一个神经网络来预测给定任何图像，它是旋转了 0、90、180 还是 270 度。与上面的一些示例一样（其中离群值可能是不能很好地融入聚类、不包含频繁项目模式、压缩效果不佳等的项目），这里的离群值是神经网络无法很好地预测图像每个版本旋转程度的图像。

![](https://www.hubwiz.com/blog/content/images/2025/01/image-58.png)

旋转 0、90、180 和 270 度的马克杯图像

对于典型图像，当我们将图像的四种变体通过网络时（假设网络经过良好训练），它将倾向于正确预测每个图像的旋转，但对于非典型图像，它将无法准确预测，或者对预测的置信度较低。

相同的通用方法可以用于其他扰动，包括翻转图像、放大、拉伸等 - 在这些示例中，模型预测图像的翻转方式、图像的比例或拉伸方式。

其中一些也可以用于其他模态。例如，掩蔽几乎可以用于任何模态。但有些方法并不普遍适用；例如，翻转可能对音频数据无效。

## 7、使用图像数据进行异常值检测的技术

我将在这里回顾一些最常见的选项：

- 自动编码器、变分自动编码器和生成对抗网络。这些方法已经很成熟，很可能是最常见的异常值检测方法。
- 特征建模 - 在这里为每个对象创建嵌入，并在嵌入上使用标准异常值检测（例如，孤立森林、局部异常值因子 (LOF)、k-最近邻 (KNN) 或类似算法）。正如下一篇文章中讨论的那样，为支持分类或回归问题而创建的嵌入通常在这种情况下效果不佳，但我们稍后会介绍一些与创建更适合异常值检测的嵌入相关的研究。
- 直接使用借口任务。例如，预测图像的旋转、拉伸、缩放等。这是一种有趣的方法，可能是最有用的异常值检测方法之一。
- 置信度分数——在这里，我们考虑使用分类器的情况，并且与所有类别相关的置信度都很低。如果训练分类器识别 100 种鸟类，那么当呈现新图像时，它将为这 100 种鸟类中的每一种生成一个概率。如果所有这些的概率都很低，则该对象在某种程度上是不寻常的，很可能不在分布范围内。如前所述，分类器即使在错误的情况下也经常提供高置信度，因此这种方法并不总是可靠的，但即使在不可靠的情况下，当返回低置信度分数时，系统可以利用这一点并识别出图像在某些方面是不寻常的。

## 8、具有图像数据的神经网络

有了图像数据，我们就可以充分利用深度神经网络，它可以创建非常复杂的数据模型：我们可以访问大量数据，可以使用自动编码器、VAE 和 GAN 等工具，而且自监督学习也非常可行。

深度神经网络的一个重要特性是它们可以发展到非常大的规模，这使得它们可以利用额外的数据并创建更复杂的模型。

这与更传统的异常值检测模型不同，例如频繁模式异常值因子 (FPOF)、关联规则、k-最近邻、孤立森林、LOF、半径等：当它们在额外数据上进行训练时，它们可能会开发出稍微更准确的正常数据模型，但它们往往会在一段时间后趋于平稳，超过某个点后，使用额外数据进行训练的回报会大大减少。另一方面，深度学习模型倾向于继续利用更多数据，即使已经使用了大量数据。

不过，我们应该注意，尽管在图像异常值检测方面取得了很大进展，但它仍是一个尚未解决的问题。它比其他模式的主观性要小得多，至少在它被定义为严格处理分布外的数据时是这样（尽管当对象真实时，它仍然有些模糊）lly 与训练期间看到的物体属于不同的类型 — 例如，鸟类，如果松鸦和蓝松鸦是不同的类别）。图像数据很难处理，异常值检测仍然是一个具有挑战性的领域。

## 9、基于深度学习的异常值检测工具

有几种工具可用于基于深度学习的异常值检测。我们将在这里和下一篇文章中介绍其中的三种，它们是 PyOD、DeepOD 和 Alibi-Detect。

[PyOD](https://github.com/yzhao062/pyod?ref=hubwiz.com)，我在之前的一些文章中介绍过，它可能是目前用于 Python 表格数据异常值检测的最全面的工具。它包含几个标准的异常值检测器（孤立森林、局部异常值因子、核密度估计 (KDE)、基于直方图的异常值检测 (HBOS)、高斯混合模型 (GMM) 和其他几个），以及许多基于深度学习的模型，这些模型基于自动编码器、变异自动编码器、GANS 及其变体。

[DeepOD ](https://github.com/xuhongzuo/DeepOD?ref=hubwiz.com)为表格和时间序列数据提供异常值检测。我将在下一篇文章中对此进行更深入的研究。

[Alibi-Detec](https://github.com/SeldonIO/alibi-detect?ref=hubwiz.com)t 涵盖表格、时间序列和图像数据的异常值检测。下面显示了一个使用图像数据的示例。

当今大多数深度学习工作都基于 TensorFlow/Keras 或 PyTorch（PyTorch 的份额越来越大）。同样，大多数基于深度学习的异常值检测都使用其中之一。

至少就我的经验而言，PyOD 可能是这三个库中最直接的，但所有库都相当易于管理且有详尽的文档。

## 10、使用 PyOD 的示例

本节展示了一个使用 PyOD 的 AutoEncoder 异常值检测器对表格数据集（特别是 KDD 数据集，具有公共许可证）进行检测的示例。

在使用 PyOD 之前，需要先安装它，安装方法如下：

```
pip install pyod
```

然后，如果尚未安装 TensorFlow 或 PyTorch，则需要安装它们（取决于正在使用哪种检测器）。我为此使用了 Google colab，它已经安装了 TensorFlow 和 PyTorch。此示例使用 PyOD 的 AutoEncoder 异常值检测器，它在后台使用 PyTorch。

```
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_kddcup99
from pyod.models.auto_encoder import AutoEncoder

# Load the data
X, y = fetch_kddcup99(subset="SA", percent10=True, random_state=42, 
                      return_X_y=True, as_frame=True)

# Convert categorical columns to numeric, using one-hot encoding
cat_columns = ["protocol_type", "service", "flag"]
X = pd.get_dummies(X, columns=cat_columns)

det = AutoEncoder()
det.fit(X)
scores = det.decision_scores_
```

虽然自动编码器比 PyOD 支持的许多其他检测器更复杂（例如，HBOS 基于直方图，Cook 距离基于线性回归； （其他一些也相对简单），使用 PyOD 中的自动编码器检测器的接口也同样简单。尤其如此，在本例中，我们使用默认参数。PyOD 提供的基于 VAE 和 GAN 的检测器也是如此，它们在底层甚至比自动编码器更复杂一些，但除参数之外的 API 是相同的。

在此示例中，我们只需加载数据，将分类列转换为数字格式（这对于任何神经网络模型都是必需的），创建一个自动编码器检测器，拟合数据，并评估数据中的每个记录。

## 11、Alibi-Detect

Alibi-Detect 还支持自动编码器进行异常值检测。与 PyOD 相比，它在创建检测器时确实需要更多的编码；这可能稍微多一点工作，但也允许更大的灵活性。Alibi-Detect 的文档提供了几个示例，它们对您入门很有用。

下面的列表提供了一个示例，可以帮助解释一般的想法，但最好通读它们的文档和示例以彻底了解该过程。该列表还使用了自动编码器异常值检测器。由于 alibi-detect 可以支持图像数据，我们提供了一个使用它的示例。

使用深度神经网络可能会很慢。为此，我建议尽可能使用 GPU。例如，在 Alibi-Detect 文档中找到的一些示例或我测试过的这些示例的变体，在 Google colab 上使用 CPU 运行时可能需要大约 1 小时，但使用 T4 GPU 运行时只需大约 3 分钟。

## 12、图像数据异常值检测示例

对于这个例子，我只提供了一些可以用于任何数据集的通用代码，尽管必须调整层的尺寸以匹配使用的图像的大小。这个例子只是调用一个名为 load\_data() 的未定义方法来获取相关数据（下一个示例更详细地介绍了特定的数据集——这里我只是展示了 Alibi-Detect 使用的一般系统）。

本示例首先使用 Keras（如果你更熟悉 PyTorch，那么使用 Keras 时的想法是类似的）来创建编码器和解码器。y 自动编码器，然后将它们作为参数传递给 alibi-detect 提供的 OutlierAE 对象。

与图像数据一样，神经网络包括卷积层。这些有时也用于其他类型的数据，包括文本和时间序列，但很少用于表格。它还使用密集层。

代码假设图像为 32x32。对于其他尺寸，必须组织解码器以便它也输出此尺寸的图像。OutlierAE 类的工作原理是将输入图像与输出图像进行比较（在将输入图像通过编码器和解码器之后），因此输出图像必须具有与输入相同的大小。与使用密集层相比，使用 Conv2D 和 Conv2DTranspose 层时（如本例所示），这有点棘手。

然后我们调用 fit() 和 predict()。对于 fit()，我们指定五个时期。使用更多可能会更好，但也需要更多时间。 Alibi-detect 的 OutlierAE 使用重建误差（具体来说，是从原始图像重建的图像的均方误差）。

```
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.keras.backend.clear_session()
from tensorflow.keras.layers import Conv2D, Conv2DTranspose, \
    Dense, Layer, Reshape, InputLayer, Flatten
from alibi_detect.od import OutlierAE

# Loads the data used
train, test = load_data() 

X_train, y_train = train
X_test, y_test = test
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255

encoding_dim = 1024

# Defines the encoder portion of the AE
encoder_net = tf.keras.Sequential([ 
      InputLayer(input_shape=(32, 32, 3)),
      Conv2D(64, 4, strides=2, padding='same', activation=tf.nn.relu),
      Conv2D(128, 4, strides=2, padding='same', activation=tf.nn.relu),
      Conv2D(512, 4, strides=2, padding='same', activation=tf.nn.relu),
      Flatten(),
      Dense(encoding_dim,)])

# Defines the decoder portion of the AE
decoder_net = tf.keras.Sequential([ 
      InputLayer(input_shape=(encoding_dim,)),
      Dense(4*4*128),
      Reshape(target_shape=(4, 4, 128)),
      Conv2DTranspose(256, 4, strides=2, padding='same',
          activation=tf.nn.relu),
      Conv2DTranspose(64, 4, strides=2, padding='same', 
          activation=tf.nn.relu),
      Conv2DTranspose(3, 4, strides=2, padding='same', 
          activation='sigmoid')])

# Specifies the threshold for outlier scores
od = OutlierAE(threshold=.015,  
               encoder_net=encoder_net,  
               decoder_net=decoder_net)
od.fit(X_train, epochs=5, verbose=True)

# Makes predictions on the records
X = X_train
od_preds = od.predict(X,
                      outlier_type='instance',    
                      return_feature_score=True,  
                      return_instance_score=True)
print("Number of outliers with normal data:",  
   od_preds['data']['is_outlier'].tolist().count(1))
```

这会对训练数据中使用的行进行预测。理想情况下，没有异常值。

## 13、使用 PyTorch 的示例

由于自动编码器的创建相当简单，因此这通常是直接完成的，也可以使用 Alibi-Detect 或 PyOD 等工具完成。在此示例中，我们使用 MNIST 数据集（具有公共许可证，在本例中与 PyTorch 的 torchvision 一起分发），并使用 PyTorch 展示一个快速示例。

```
import numpy as np
import torch
from torchvision import datasets, transforms
from matplotlib import pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision.utils import make_grid

# Collect the data
train_dataset = datasets.MNIST(root='./mnist_data/', train=True, 
  transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./mnist_data/', train=False, 
  transform=transforms.ToTensor(), download=True)

# Define DataLoaders
batchSize=128
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batchSize, shuffle=True) 
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batchSize, shuffle=False)  

# Display a sample of the data
inputs, _ = next(iter(test_loader))
fig, ax = plt.subplots(nrows=1, ncols=10, figsize=(12, 4))
for i in range(10):
  ax[i].imshow(inputs[i][0])
plt.tight_layout()
plt.show()

# Define the properties of the autoencoder
num_input_pixels = 784  
num_neurons_1 = 256  
num_neurons_2 = 64   

# Define the Autoencoder
class Autoencoder(nn.Module):
    def __init__(self, x_dim, h_dim1, h_dim2):
        super(Autoencoder, self).__init__()
        
        # Encoder
        self.layer1 = nn.Linear(x_dim, h_dim1)
        self.layer2 = nn.Linear(h_dim1, h_dim2)

        # Decoder
        self.layer3 = nn.Linear(h_dim2, h_dim1)
        self.layer4 = nn.Linear(h_dim1, x_dim)

    def encoder(self, x):
        x = torch.sigmoid(self.layer1(x))
        x = torch.sigmoid(self.layer2(x))
        return x

    def decoder(self, x):
        x = torch.sigmoid(self.layer3(x))
        x = torch.sigmoid(self.layer4(x))
        return x

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

model = Autoencoder(num_input_pixels, num_neurons_1, num_neurons_2)
model.cuda()

optimizer = optim.Adam(model.parameters())
n_epoch = 20
loss_function = nn.MSELoss()

for i in range(n_epoch):
    train_loss = 0
    for batch_idx, (data, _) in enumerate(train_loader):
        data = data.cuda()        
        inputs = torch.reshape(data,(-1, 784)) 
        optimizer.zero_grad()

        # Get the result of passing the input through the network      
        recon_x = model(inputs)        

        # The loss is in terms of the difference between the input and
        # output of the model
        loss = loss_function(recon_x, inputs)        
        loss.backward()
        train_loss += loss.item()
        optimizer.step()
        
    if i % 5 == 0:    
        print(f'Epoch: {i:>3d} Average loss: {train_loss:.4f}')
print('Training complete...')
```

此示例使用 cuda，但如果没有可用的 GPU，可以将其删除。

在此示例中，我们收集数据，为训练和测试数据创建一个 DataLoader（大多数使用 PyTorch 的项目都这样做），并显示数据样本，我们在此处看到：

![](https://www.hubwiz.com/blog/content/images/2025/01/image-59.png)

来自测试数据的图像样本

数据包含手写数字。

接下来我们定义一个自动编码器，它清楚地定义了编码器和解码器。通过自动编码器的任何数据都会经过这两个。

自动编码器的训练方式与 PyTorch 中的大多数神经网络类似。我们定义一个优化器和损失函数，并在一定数量的周期（这里使用 20 个周期）内迭代数据，每次覆盖一定数量的批次中的数据（这使用 128 的批次大小，因此在给定完整数据大小的情况下，每个周期有 16 个批次）。在每个批次之后，我们根据输入和输出向量之间的差异计算损失，然后更新权重并继续。

执行以下代码，我们可以看到，对于大多数数字，重建误差非常小：

```
inputs, _ = next(iter(test_loader))

fig, ax = plt.subplots(nrows=1, ncols=10, figsize=(12, 4))
for i in range(10):
  ax[i].imshow(inputs[i][0])
plt.tight_layout()
plt.show()

inputs=inputs.cuda()
inputs=torch.reshape(inputs,(-1,784))
outputs=model(inputs)
outputs=torch.reshape(outputs,(-1,1,28,28))
outputs=outputs.detach().cpu()

fig, ax = plt.subplots(nrows=1, ncols=10, figsize=(12, 4))
for i in range(10):
  ax[i].imshow(outputs[i][0])
plt.tight_layout()
plt.show()
```

![](https://www.hubwiz.com/blog/content/images/2025/01/image-60.png)

原始测试数据

![](https://www.hubwiz.com/blog/content/images/2025/01/image-61.png)

自动编码器的输出。在大多数情况下，输出与输入非常相似。

然后，我们可以用分布外的数据进行测试，在这个例子中传递一个接近 X 的字符（因此与训练的 10 个数字中的任何一个都不一样）。

```
inputs, _ = next(iter(test_loader))

for i in range(28):
  for j in range(28):
    inputs[0][0][i][j] = 0
    if i == j:
      inputs[0][0][i][j] = 1
    if i == j+1:
      inputs[0][0][i][j] = 1
    if i == j+2:
      inputs[0][0][i][j] = 1
    if j == 27-i:
      inputs[0][0][i][j] = 1

fig, ax = plt.subplots(nrows=1, ncols=10, figsize=(12, 4))
for i in range(10):
  ax[i].imshow(inputs[i][0])
plt.tight_layout()
plt.show()

inputs=inputs.cuda()
inputs=torch.reshape(inputs,(-1,784))
outputs=model(inputs)
outputs=torch.reshape(outputs,(-1,1,28,28))
outputs=outputs.detach().cpu()

fig, ax = plt.subplots(nrows=1, ncols=10, figsize=(12, 4))
for i in range(10):
  ax[i].imshow(outputs[i][0])
plt.tight_layout()
plt.show()   
```

输出结果为：

![](https://www.hubwiz.com/blog/content/images/2025/01/image-62.png)

输入。第一个字符已被修改，包括一个分布外的字符。

![](https://www.hubwiz.com/blog/content/images/2025/01/image-63.png)

上述字符的输出。 X 的重建效果还不错，但错误比正常情况要多。

在这种情况下，我们看到 X 的重建错误并不大——它能够重新创建看起来像 X 的东西，但相对于其他字符，错误异常大。

## 14、结束语

深度学习对于包括图像数据在内的许多模态的异常值检测都是必要的，并且在其他尚未完善的领域（如表格数据）中显示出希望。然而，目前，更传统的异常值检测方法仍然倾向于对表格数据最有效。

话虽如此，现在有些情况下，基于深度学习的异常值检测可能是识别表格数据中异常的最有效方法，或者至少可以将其纳入所研究的方法中（并可能纳入更大的检测器集合中）。

使用深度学习进行异常值检测的方法有很多，未来几年我们可能会看到更多方法的发展。其中最成熟的有自动编码器、变分自动编码器和 GAN，主要的异常值检测库（包括 PyOD 和 Alibi-Detect）对这些技术提供了很好的支持。

用于异常值检测的自监督学习也显示出很大的前景。我们在这里介绍了如何将其应用于图像数据，并在下一篇文章中介绍表格数据。它也可以以某种形式应用于大多数模态。例如，对于大多数模态，通常有某种方法可以实现掩码，其中模型学习预测数据的掩码部分。例如，对于时间序列数据，模型可以学习预测时间序列内某个范围或一组范围内的掩码值。

---

原文链接：[Deep Learning for Outlier Detection on Tabular and Image Data](https://towardsdatascience.com/deep-learning-for-outlier-detection-on-tabular-and-image-data-90ae518a27b3?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处