最强异常检测算法,自编码器 !!

文摘   2024-11-26 11:17   北京  

哈喽,我是小白~

今儿和大家聊聊自编码器的内容~

异常检测算法是用来找出数据中“不正常”或“与众不同”的部分。比如,监控系统中突然出现一个人影,或者银行系统中检测到一笔特别大的转账,这些都可能是异常。

如果需要本文PDF版本的同学,文末获取~

另外,文末有总结性的干货~

一起来看下具体细化内容~

自编码器(Autoencoder)是一种人工神经网络,用来压缩数据然后再还原数据。它的设计目的是让输入和输出尽可能相似。因为异常数据通常无法很好地被还原,所以可以用自编码器来做异常检测。

自编码器 是什么?

这里,咱们举一个非常简单的例子:

假设你是一个快递员,每天送快递。你熟悉每个客户的订单:

  • 小张总是点两瓶水(2元每瓶)。
  • 小李喜欢买一包面包(5元每包)。
  • 小王经常买一盒牛奶(10元每盒)。

一天,你收到一份订单:有人点了500瓶水。这就很奇怪!这就是一个异常

如何用自编码器检测异常?

1. 学习正常数据模式

我们训练一个自编码器,让它学习正常的订单习惯,比如小张、小李和小王的消费数据。

2. 对新数据进行检测

如果订单是2瓶水或1盒牛奶,模型能很好地重建。但如果是500瓶水,模型无法有效重建,误差就会很大。

3. 判断异常

如果重建误差超过某个阈值(比如误差大于20元),我们就标记为异常。

自编码器计算过程

正常数据示例:

假设我们有以下正常订单数据:

  • 小张:2瓶水(价格 4 元)
  • 小李:1包面包(价格 5 元)
  • 小王:1盒牛奶(价格 10 元)

这些数据被输入自编码器,经过以下步骤:

1. 编码(压缩)

将高维输入数据(如[4, 5, 10])映射到低维空间(比如[0.4, 0.5])。

2. 解码(还原)

再从低维空间还原回原来的高维数据(尽量接近[4, 5, 10])。

重建误差计算

比如,某次还原后的输出是[4.1, 4.9, 9.8],我们计算每个值的误差:

误差很小,说明这笔订单是正常的。

异常数据示例

如果有一笔订单[1000, 5, 10],自编码器尝试还原,结果可能是[50, 4.8, 9.9]。误差:

误差很大,说明这是异常数据。

总的来说:

  • 自编码器学会了重建正常订单数据。
  • 如果输入数据偏离正常模式(如1000瓶水),重建误差会很大。
  • 通过设置误差阈值,我们可以检测异常订单。

这个方法可以扩展到银行交易、网络流量等领域的异常检测中。

公式解析

自编码器本质上是一种神经网络,用于学习数据的低维表示,并通过重建输入数据检测异常。

算法的关键数学过程包括:

自编码器的基本结构

自编码器由两部分组成:

1. 编码器:将高维输入数据映射到低维表示。

其中:

  •  是输入数据。
  •  是编码后的低维表示,
  •  是编码函数(通常是多层神经网络)。
  •  是编码器的参数。

2. 解码器:从低维表示重建原始输入。

其中:

  •  是重建的数据。
  •  是解码函数(通常是多层神经网络)。
  •  是解码器的参数。

目标是使得输入和重建尽可能相似,即:

重建误差

重建误差衡量输入数据和重建数据之间的差异:

常用的范数:

  • :欧几里得距离(L2 范数):
  • :绝对值距离(L1 范数):

训练自编码器

训练自编码器的目标是最小化输入与重建之间的误差。损失函数为:

其中:

  •  是训练数据的总数。
  •  是第  个样本。
  •  和  是需要学习的参数。

通过优化(如梯度下降)最小化该损失函数。

异常检测过程

在训练阶段,自编码器学到了正常数据的低维表示和重建能力。在检测阶段:

  1. 对新数据 ,通过自编码器计算重建数据:
  1. 计算重建误差:
  1. 根据误差与阈值  比较:
  • 若 ,判定为异常。
  • 否则,判定为正常。

如何设定阈值

阈值  通常通过以下方法设定: 1. 统计方法

计算训练数据的重建误差均值  和标准差 ,设定阈值为:

其中  是一个调节参数(如  表示 95% 的置信区间)。

2. 百分位法

使用训练数据误差的某个百分位(如 95%)作为阈值。

数学推理

为什么重建误差可以用于异常检测?

自编码器被训练在正常数据上,因此:

  • 对于正常数据, 的特征模式被学习到,重建误差较小:
  • 对于异常数据,自编码器未见过类似模式,难以准确重建,导致较大的误差:

为什么使用低维表示?

低维表示  可以逼近正常数据的主要特征(主成分),但无法很好地表示异常数据。异常数据的重建过程中,信息丢失较多,导致误差增大。

示例推导

假设我们有以下一维数据:

  • 训练样本:
  • 新样本:

自编码器的结构:

  1. 编码器:
  2. 解码器:

对训练数据的重建:

  • ,误差 
  • ,误差 
  • ,误差 

训练数据的误差均值为 

对新样本 

  • 编码器:
  • 解码器:
  • 重建误差:

如果增加更复杂的数据结构,可以通过神经网络提高自编码器的表现力,处理多维和复杂分布。

优缺点和适用场景

优点

1. 无需监督学习:自编码器只需要正常数据进行训练,无需标注异常样本,适用于大多数无法明确标注异常的场景。

2. 强大的非线性建模能力:使用深度学习的结构,自编码器可以捕捉复杂的非线性关系,比传统的线性方法(如 PCA)表现更好。

3. 适用于高维数据:自编码器能有效降维并提取高维数据的主要特征。

4. 可扩展性好:通过增加网络层数或节点数,可以轻松扩展到更复杂的数据。

5. 灵活性高:可结合不同的变种(如变分自编码器、稀疏自编码器、卷积自编码器等)来适应特定场景。

缺点

1. 对训练数据的依赖性强:如果训练数据不包含所有可能的正常模式,模型可能误判异常或漏检异常。

2. 计算成本较高:深度网络的训练和推断可能需要更多的计算资源,特别是对于大规模数据。

3. 不适合实时性要求高的任务:重建过程的计算复杂度较高,可能无法满足实时性需求。

4. 阈值选择困难:如何选择适合的异常判别阈值需要仔细调试,尤其是当异常分布复杂时。

5. 对噪声敏感:如果训练数据中含有噪声,模型可能会错误学习噪声特征,从而影响检测性能。

适用场景

1. 无监督异常检测:缺乏标注的异常数据样本。

2. 高维、复杂数据:数据维度高,关系复杂,例如图像、时间序列、网络流量等。

3. 非线性模式:数据具有显著的非线性特征,无法用线性方法很好地描述。

完整案例

为了大家学习方便,咱们这里生成一些模拟的传感器数据(例如温度、振动等),构建一个自编码器模型进行训练,并通过异常检测分析故障数据。

代码包括:

  1. 数据生成与可视化。
  2. 自编码器模型的训练。
  3. 异常检测与结果可视化。

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from mpl_toolkits.mplot3d import Axes3D

# 生成模拟工业设备传感器数据
np.random.seed(42)

# 正常数据(训练数据):温度、振动、压力
normal_data = np.random.multivariate_normal(
    mean=[300.51.0],  # 平均值
    cov=[[1021], [20.50.2], [10.20.3]],  # 协方差
    size=1000  # 样本数量
)

# 异常数据:设备故障模拟
anomaly_data = np.random.multivariate_normal(
    mean=[80510],  # 故障时的特征
    cov=[[2042], [410.5], [20.50.8]],
    size=100
)

# 合并数据集
data = np.vstack([normal_data, anomaly_data])
labels = np.hstack([np.zeros(1000), np.ones(100)])  # 正常数据为0,异常数据为1

# 数据归一化
data_min = data.min(axis=0)
data_max = data.max(axis=0)
data = (data - data_min) / (data_max - data_min)

# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.3, random_state=42)

# 转换为 PyTorch 的张量
X_train = torch.tensor(X_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)

# 构建自编码器模型
class AutoEncoder(nn.Module):
    def __init__(self, input_dim, encoding_dim):
        super(AutoEncoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, encoding_dim),
            nn.ReLU()
        )
        self.decoder = nn.Sequential(
            nn.Linear(encoding_dim, input_dim),
            nn.Sigmoid()
        )
    
    def forward(self, x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded

# 初始化模型
input_dim = X_train.shape[1]
encoding_dim = 2
autoencoder = AutoEncoder(input_dim, encoding_dim)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(autoencoder.parameters(), lr=0.01)

# 训练自编码器
num_epochs = 50
batch_size = 32

train_loader = torch.utils.data.DataLoader(X_train, batch_size=batch_size, shuffle=True)

losses = []
for epoch in range(num_epochs):
    epoch_loss = 0
    for batch in train_loader:
        optimizer.zero_grad()
        outputs = autoencoder(batch)
        loss = criterion(outputs, batch)
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()
    losses.append(epoch_loss / len(train_loader))
    print(f"Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.4f}")

# 异常检测
with torch.no_grad():
    X_test_pred = autoencoder(X_test)
    reconstruction_error = torch.mean((X_test - X_test_pred) ** 2, axis=1).numpy()

# 设置阈值
threshold = np.percentile(reconstruction_error[y_test == 0], 95)  # 正常数据的95%分位数

# 标记为异常的数据
predicted_labels = (reconstruction_error > threshold).astype(int)

# 数据可视化
fig = plt.figure(figsize=(168))

# 图1:3D 数据分布(原始数据)
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(normal_data[:, 0], normal_data[:, 1], normal_data[:, 2], c='blue', alpha=0.6, label='Normal')
ax1.scatter(anomaly_data[:, 0], anomaly_data[:, 1], anomaly_data[:, 2], c='red', alpha=0.6, label='Anomaly')
ax1.set_title('3D Sensor Data Distribution', fontsize=14)
ax1.set_xlabel('Temperature')
ax1.set_ylabel('Vibration')
ax1.set_zlabel('Pressure')
ax1.legend()

# 图2:重建误差分布
ax2 = fig.add_subplot(122)
bins = np.linspace(0, reconstruction_error.max(), 50)
ax2.hist(reconstruction_error[y_test == 0], bins, alpha=0.6, color='blue', label='Normal Reconstruction Error')
ax2.hist(reconstruction_error[y_test == 1], bins, alpha=0.6, color='red', label='Anomaly Reconstruction Error')
ax2.axvline(threshold, color='green', linestyle='dashed', linewidth=2, label='Threshold')
ax2.set_title('Reconstruction Error Distribution', fontsize=14)
ax2.set_xlabel('Reconstruction Error')
ax2.set_ylabel('Frequency')
ax2.legend()

plt.tight_layout()
plt.show()

图1:3D 数据分布

显示正常数据(蓝色)和异常数据(红色)的传感器值在温度、振动和压力三个维度的分布,直观展示正常与异常的差异。

图2:重建误差分布

直方图展示了重建误差的分布:

  • 正常数据的误差值集中且较小。
  • 异常数据的误差值分布较宽且偏大。
  • 阈值用虚线标记,区分正常和异常数据。

最后

以上就是今天所有的内容了。
获取本文PDF,点击名片回复「基础算法模型」即可~
 
另外, 我们整理了机器学习算法册子,总共16大块的内容,124个问题的总结点击领取!
如果对你来说比较有用,记得点赞、转发,收藏起来慢慢学习~
下期会有更多干货等着你!~

Python和机器学习初学者
Python和机器学习分享,只写干货,一起学习~
 最新文章