Python实现马氏距离异常检测预测模型及分类逻辑修正
马氏距离单类分类器(适配LDA特征)修正实现方案
核心问题排查
- 分类器未遵循sklearn估计器接口规范,训练阶段从C0类计算得到的分布参数没有作为实例属性持久化,预测阶段无法调用训练好的分布信息
- 预测流程缺失核心的马氏距离计算环节,
predict_proba直接返回输入数据,判定逻辑完全失效,导致准确率虚高为100% - 标签映射规则不清晰,没有按照单类异常检测的逻辑划分正常/异常边界,无法正确识别假阴性样本
各方法正确实现逻辑
fit方法(训练阶段)
仅使用正常类C0的LDA特征作为训练输入,执行以下操作:
- 校验输入特征格式为二维矩阵,记录特征维度
- 计算C0类样本的特征均值向量,存储为实例属性
self.mean_ - 计算C0类样本的协方差矩阵,做正定校验后求逆,分别存储为
self.cov_、self.inv_cov_(可添加极小单位矩阵扰动避免协方差矩阵奇异) - 基于训练集马氏距离的分布确定异常阈值:可选择卡方分布对应置信度的分位点,或训练集样本马氏距离的经验分位点,存储为
self.threshold_ - 方法末尾返回
self,符合sklearn接口要求
内部马氏距离计算方法
实现私有方法_calc_mahalanobis供预测阶段调用:
- 校验输入样本的特征维度和训练阶段一致
- 批量计算每个输入样本到C0类分布的马氏距离,返回一维距离数组
predict方法(预测阶段)
- 调用内部距离计算方法,得到所有输入样本的马氏距离
- 判定规则:马氏距离 ≤ 阈值的样本判定为正常类C0(标签设为0),马氏距离 > 阈值的样本判定为异常类(标签设为1,对应C1-C4)
- 返回和输入样本长度一致的标签数组
predict_proba方法(概率输出)
禁止直接返回输入数据,按以下逻辑实现:
- 基于样本的马氏距离转换为类别概率:距离越近,属于正常类C0的概率越高;距离越远,属于异常类的概率越高
- 返回形状为
(n_samples, 2)的数组,第一列为异常类概率,第二列为正常类概率,匹配sklearn接口规范
可直接复用的实现代码
import numpy as np from sklearn.base import BaseEstimator, ClassifierMixin from scipy.stats import chi2 class MahalanobisOneclassClassifier(BaseEstimator, ClassifierMixin): def __init__(self, confidence=0.95): self.confidence = confidence def fit(self, X, y=None): # 存储C0类分布参数 n_samples, n_features = X.shape self.mean_ = np.mean(X, axis=0) # 加1e-6单位矩阵扰动保证协方差正定 self.cov_ = np.cov(X, rowvar=False) + 1e-6 * np.eye(n_features) self.inv_cov_ = np.linalg.inv(self.cov_) # 卡方分布计算阈值:自由度等于特征数 self.threshold_ = np.sqrt(chi2.ppf(self.confidence, df=n_features)) return self def _calc_mahalanobis(self, X): # 批量计算马氏距离 diff = X - self.mean_ left_term = np.dot(diff, self.inv_cov_) dist_sq = np.sum(left_term * diff, axis=1) return np.sqrt(dist_sq) def predict(self, X): dist = self._calc_mahalanobis(X) # 大于阈值判为异常(1),否则为正常(0) return (dist > self.threshold_).astype(int) def predict_proba(self, X): dist = self._calc_mahalanobis(X) # 距离转换为正常类概率,用sigmoid形式保证概率在0-1区间 prob_normal = 1 / (1 + np.exp(dist - self.threshold_)) prob_abnormal = 1 - prob_normal return np.column_stack([prob_abnormal, prob_normal])
评估注意事项
- 标签映射统一:评估前将测试集的C0类样本标记为0(正类,正常),所有C1-C4类样本标记为1(负类,异常)
- 假阴性样本无需手动标记:真实标签为1(C1-C4类)但预测标签为0(距离低于阈值被误判为正常)的样本,会自动被sklearn的
confusion_matrix、classification_report识别统计 - 禁止数据泄露:预测阶段不得重新计算协方差、均值等分布参数,必须全程使用fit阶段在C0训练集上得到的参数
- 结果校验:训练完成后在C0训练集上做预测,异常样本占比应接近设置的显著性水平(如confidence=0.95时异常占比约5%),不会出现100%准确率的异常结果
内容的提问来源于stack exchange,提问作者WDpad159
相关产品推荐
相关产品推荐

