哈喽,我是小白~
今儿和大家聊聊自编码器的内容~
异常检测算法是用来找出数据中“不正常”或“与众不同”的部分。比如,监控系统中突然出现一个人影,或者银行系统中检测到一笔特别大的转账,这些都可能是异常。
如果需要本文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. 统计方法:
计算训练数据的重建误差均值 和标准差 ,设定阈值为:
其中 是一个调节参数(如 表示 95% 的置信区间)。
2. 百分位法:
使用训练数据误差的某个百分位(如 95%)作为阈值。
数学推理
为什么重建误差可以用于异常检测?
自编码器被训练在正常数据上,因此:
对于正常数据, 的特征模式被学习到,重建误差较小:
对于异常数据,自编码器未见过类似模式,难以准确重建,导致较大的误差:
为什么使用低维表示?
低维表示 可以逼近正常数据的主要特征(主成分),但无法很好地表示异常数据。异常数据的重建过程中,信息丢失较多,导致误差增大。
示例推导
假设我们有以下一维数据:
训练样本: 新样本:
自编码器的结构:
编码器:。 解码器:。
对训练数据的重建:
,,,误差 。 ,,,误差 。 ,,,误差 。
训练数据的误差均值为 。
对新样本 :
编码器:。 解码器:。 重建误差:
如果增加更复杂的数据结构,可以通过神经网络提高自编码器的表现力,处理多维和复杂分布。
优缺点和适用场景
优点
1. 无需监督学习:自编码器只需要正常数据进行训练,无需标注异常样本,适用于大多数无法明确标注异常的场景。
2. 强大的非线性建模能力:使用深度学习的结构,自编码器可以捕捉复杂的非线性关系,比传统的线性方法(如 PCA)表现更好。
3. 适用于高维数据:自编码器能有效降维并提取高维数据的主要特征。
4. 可扩展性好:通过增加网络层数或节点数,可以轻松扩展到更复杂的数据。
5. 灵活性高:可结合不同的变种(如变分自编码器、稀疏自编码器、卷积自编码器等)来适应特定场景。
缺点
1. 对训练数据的依赖性强:如果训练数据不包含所有可能的正常模式,模型可能误判异常或漏检异常。
2. 计算成本较高:深度网络的训练和推断可能需要更多的计算资源,特别是对于大规模数据。
3. 不适合实时性要求高的任务:重建过程的计算复杂度较高,可能无法满足实时性需求。
4. 阈值选择困难:如何选择适合的异常判别阈值需要仔细调试,尤其是当异常分布复杂时。
5. 对噪声敏感:如果训练数据中含有噪声,模型可能会错误学习噪声特征,从而影响检测性能。
适用场景
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=[30, 0.5, 1.0], # 平均值
cov=[[10, 2, 1], [2, 0.5, 0.2], [1, 0.2, 0.3]], # 协方差
size=1000 # 样本数量
)
# 异常数据:设备故障模拟
anomaly_data = np.random.multivariate_normal(
mean=[80, 5, 10], # 故障时的特征
cov=[[20, 4, 2], [4, 1, 0.5], [2, 0.5, 0.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=(16, 8))
# 图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:重建误差分布
直方图展示了重建误差的分布:
正常数据的误差值集中且较小。 异常数据的误差值分布较宽且偏大。 阈值用虚线标记,区分正常和异常数据。
最后