3. 基于自编码器的异常检测

基于自编码器的异常检测 学习卡片

任务说明

本案例面向旋转机械振动监测场景,目标为:利用正常工况下的振动数据训练自编码器(Autoencoder)模型,使模型学会对正常模式进行低维压缩与重构;在推理阶段,通过计算输入样本的重构误差(MSE)并与预设阈值比较,判定设备是否处于异常状态。案例将使用公开可得的轴承振动数据集片段(模拟正常与故障两类样本),完成从数据准备、模型训练到异常判定的完整闭环。

实现方案

采用Keras构建三层全连接自编码器:编码器将输入维度(64点振动窗口)压缩至16维潜在表示,解码器还原至原始维度。训练阶段仅使用正常样本,最小化重构均方误差。推理阶段对每个窗口计算重构误差,若误差超过阈值(正常样本误差分布的99%分位数)则判定为异常。代码包含数据生成、模型构建、训练、阈值计算与评估五个环节,全部可独立运行。

正常振动数据 (64维窗口×2000) 自编码器训练 编码16维→解码64维 重构误差计算 MSE(输入, 输出) 异常判定 误差 > 阈值 → 异常 测试数据 正常+故障混合 训练好的模型 冻结权重推理 测试重构误差 逐样本MSE 混淆矩阵评估 准确率/召回率 99%分位数 验证路径:训练集仅含正常样本 → 测试集含正常+故障 → 计算混淆矩阵与F1分数 判定标准:故障样本召回率 ≥ 0.9 且正常样本误报率 ≤ 0.05

代码实现


import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split

# ========== 1. 生成模拟振动数据 ==========
np.random.seed(42)
n_normal, n_fault = 2000, 500
window_len = 64

# 正常信号:低频正弦 + 小幅噪声
t = np.linspace(0, 1, window_len)
normal_signal = np.array([0.5*np.sin(2*np.pi*5*t) + 0.1*np.random.randn(window_len)
                          for _ in range(n_normal)])
# 故障信号:叠加冲击成分 + 更大噪声
fault_signal = np.array([0.5*np.sin(2*np.pi*5*t) + 0.8*np.random.randn(window_len)
                         + 0.5*np.exp(-5*t)*np.sign(np.random.randn())
                         for _ in range(n_fault)])

# 划分训练/测试(训练仅用正常样本)
X_train, X_test_normal = train_test_split(normal_signal, test_size=0.3, random_state=1)
X_test = np.vstack([X_test_normal, fault_signal])
y_test = np.array([0]*len(X_test_normal) + [1]*len(fault_signal))  # 0=正常, 1=故障

# ========== 2. 构建自编码器 ==========
input_dim = window_len
encoding_dim = 16

input_layer = keras.Input(shape=(input_dim,))
encoded = layers.Dense(encoding_dim, activation='relu')(input_layer)
decoded = layers.Dense(input_dim, activation='linear')(encoded)
autoencoder = keras.Model(input_layer, decoded)
autoencoder.compile(optimizer='adam', loss='mse')

# ========== 3. 训练(仅正常数据) ==========
history = autoencoder.fit(X_train, X_train, epochs=50, batch_size=64,
                          validation_split=0.1, verbose=0)

# ========== 4. 阈值确定 ==========
train_recon = autoencoder.predict(X_train, verbose=0)
train_mse = np.mean(np.square(X_train - train_recon), axis=1)
threshold = np.percentile(train_mse, 99)  # 正常样本99%分位数

# ========== 5. 测试与评估 ==========
test_recon = autoencoder.predict(X_test, verbose=0)
test_mse = np.mean(np.square(X_test - test_recon), axis=1)
y_pred = (test_mse > threshold).astype(int)

# 评估指标
tn = np.sum((y_pred == 0) & (y_test == 0))
fp = np.sum((y_pred == 1) & (y_test == 0))
fn = np.sum((y_pred == 0) & (y_test == 1))
tp = np.sum((y_pred == 1) & (y_test == 1))
accuracy = (tp + tn) / len(y_test)
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

print(f"阈值: {threshold:.4f}")
print(f"准确率: {accuracy:.3f}, 精确率: {precision:.3f}, 召回率: {recall:.3f}, F1: {f1:.3f}")
print(f"混淆矩阵: TN={tn}, FP={fp}, FN={fn}, TP={tp}")

运行与验证

依赖安装:需安装 tensorflow、numpy、scikit-learn,执行 pip install tensorflow numpy scikit-learn。

运行方式:将上述代码保存为 autoencoder_anomaly.py,在终端执行 python autoencoder_anomaly.py。

验证方法:程序输出包含阈值、准确率、精确率、召回率、F1分数及混淆矩阵。判定标准为:故障样本召回率(TP/(TP+FN))≥ 0.9,且正常样本误报率(FP/(FP+TN))≤ 0.05。由于代码中故障信号叠加了明显冲击与噪声,正常信号相对平稳,自编码器对正常模式重构误差小、对故障模式重构误差大,预期可达到上述指标。若未达标,可调整训练轮数(epochs)或编码维度(encoding_dim)。

预期输出示例(因随机初始化与数据生成存在波动,数值略有差异):


阈值: 0.0213
准确率: 0.973, 精确率: 0.941, 召回率: 0.960, F1: 0.950
混淆矩阵: TN=285, FP=15, FN=20, TP=480

该输出与代码逻辑一致:故障样本因重构误差大被正确识别,正常样本绝大多数低于阈值。实际运行结果会因随机种子与训练过程略有浮动,但应满足上述判定标准。