无异常训练集的信号数据无监督异常检测模型选型及参数咨询
解决训练集无异常样本时的PyOD算法参数问题
嘿,这个问题我之前碰到过不少新手朋友问,确实PyOD里的KNN、Isolation Forest这类算法默认假设训练集里存在异常样本,所以contamination参数要求必须大于0,刚好和你训练集全正常的情况冲突。不过没关系,针对你这种仅用正常样本训练的异常检测场景,我们有专门的思路和方案,一步步来:
第一步:先处理3D信号数据的维度问题
你的数据是(1700, 64, 100)——1700个样本,每个样本是64通道×100时间步的信号,但PyOD的大部分算法只接受2D输入(样本数×特征数)。所以首先要把3D数据转成2D,推荐两种方法:
- 统计特征提取:对每个通道的100个时间步提取时域/统计特征,比如均值、方差、最大值、最小值、标准差、偏度、峰度。这样每个样本会变成
64×7=448维的特征向量,最终得到(1700, 448)的2D数据。 - 降维/特征压缩:用自编码器(AutoEncoder)、PCA等方法把每个
(64,100)的样本压缩成固定长度的特征向量,适合不想手动设计特征的场景。
第二步:选择适配无异常训练集的模型
方法1:用One-Class SVM(专门针对仅正常样本的场景)
Sklearn里的OneClassSVM不需要指定异常比例,专门为只有正常样本的一类分类设计,完全适配你的情况:
from sklearn.svm import OneClassSVM from sklearn.preprocessing import StandardScaler import numpy as np # 假设X_train_2d是你转好的2D训练数据 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train_2d) # nu参数:控制支持向量的比例,可近似设置为你预期的异常比例(比如0.01) oc_svm = OneClassSVM(nu=0.01, kernel='rbf') oc_svm.fit(X_train_scaled) # 验证集预测:1表示正常,-1表示异常 X_val_scaled = scaler.transform(X_val_2d) y_pred = oc_svm.predict(X_val_scaled) # 可以把-1转成1(异常),1转成0(正常)方便后续评估 y_pred = np.where(y_pred == -1, 1, 0)
方法2:用自编码器(AutoEncoder)基于重构误差检测异常
自编码器通过学习正常样本的重构模式,异常样本的重构误差会远高于正常样本,完全不需要训练集里有异常:
from tensorflow.keras.models import Model from tensorflow.keras.layers import Input, Dense import numpy as np # 构建自编码器 input_dim = X_train_2d.shape[1] encoding_dim = 32 # 可根据需求调整 input_layer = Input(shape=(input_dim,)) encoder = Dense(encoding_dim, activation='relu')(input_layer) decoder = Dense(input_dim, activation='sigmoid')(encoder) autoencoder = Model(inputs=input_layer, outputs=decoder) autoencoder.compile(optimizer='adam', loss='mse') # 仅用正常样本训练 autoencoder.fit(X_train_scaled, X_train_scaled, epochs=50, batch_size=32, validation_split=0.1) # 计算验证集重构误差 X_val_recon = autoencoder.predict(X_val_scaled) recon_error = np.mean(np.square(X_val_scaled - X_val_recon), axis=1) # 用验证集的正常样本确定阈值(比如取正常样本误差的99分位数) normal_recon_error = recon_error[y_val == 0] # y_val是验证集真实标签,0=正常,1=异常 threshold = np.percentile(normal_recon_error, 99) # 预测:误差超过阈值即为异常 y_pred = (recon_error > threshold).astype(int)
方法3:绕开PyOD的contamination参数限制
如果你还是想用PyOD里的KNN/Isolation Forest,其实可以先设置一个极小的contamination值(比如0.001),训练后不用默认阈值,而是在验证集上手动确定最优阈值:
from pyod.models.iforest import IForest from sklearn.metrics import precision_recall_curve import numpy as np # 设置极小的contamination,训练过程其实不依赖这个参数,只是避免报错 iforest = IForest(contamination=0.001, random_state=42) iforest.fit(X_train_2d) # 获取验证集的异常得分 val_scores = iforest.decision_function(X_val_2d) # 根据验证集真实标签找最优阈值(比如用F1分数最高的阈值) precision, recall, thresholds = precision_recall_curve(y_val, val_scores) f1_scores = 2 * (precision * recall) / (precision + recall) best_threshold = thresholds[np.argmax(f1_scores)] # 用最优阈值预测 y_pred = (val_scores > best_threshold).astype(int)
额外新手建议
- 信号数据可以试试频域特征(比如FFT提取频谱、小波变换),有时比时域特征的异常检测效果更好;
- 先从One-Class SVM+手动提取统计特征开始上手,流程简单,容易理解;
- 可以把多个模型的得分融合(比如SVM的异常得分+自编码器的重构误差),进一步提升检测性能。
内容的提问来源于stack exchange,提问作者Jinter
相关产品推荐
相关产品推荐

