3. 数据预处理:缺失值处理、数据标准化、降维
数据预处理这步,说实话,我见过太多人急着调模型,结果数据一塌糊涂。你想想看,异常检测模型本来就对数据分布敏感,要是原始数据里全是洞、量纲不统一、特征冗余,那训练出来的模型基本是个摆设。
我自己做工业异常检测项目时,有次因为没处理好缺失值,模型在线上连续误报,差点背锅。从那以后我每回跑模型前,都会老老实实把预处理走一遍。
3.1 缺失值处理
异常检测里缺失值是个双刃剑。处理得好,能保留信息;处理不好,会引入偏差。我个人的习惯是,先区分缺失机制——是完全随机缺失(MCAR)、随机缺失(MAR),还是非随机缺失(MNAR)。不过实际项目中,你往往没时间去深究,那就按数据量来定策略。
| 缺失比例 | 常用方法 | 我的经验评级 |
|---|---|---|
| < 5% | 直接删除含缺失的行 | 安全,但心疼数据 |
| 5% – 20% | 均值/中位数/众数填充 | 快速,但会压缩方差 |
| 20% – 50% | KNN插补 / 多重插补 | 保有分布特征,计算量大 |
| > 50% | 构建缺失指示器 + 单独建模 | 慎用,容易过拟合 |
这里有个小坑: 异常检测里用均值填充,会把异常样本拉向正常中心,导致漏检。我记得有一次,一个传感器故障导致的缺失值,被均值填平后,异常模式直接被淹没了。后来我改用KNN插补,才把那个模式找回来。
我在电力异常检测项目里,直接把“是否缺失”作为一个二元特征加进去,模型反而更灵敏。
3.2 数据标准化
异常检测模型大多依赖距离或密度,比如 kNN、LOF、孤立森林。如果特征量纲不统一,一个特征就能主导距离。你不希望“年龄”和“收入”在模型里因为数值范围不同,让你误认为“收入”比“年龄”重要100倍吧?
常用的标准化方法有3种:
- Z-score:减去均值,除以标准差。适合数据近似正态分布。
- Min-Max:缩放到[0,1]。适合边界明确、无极端值的情况。
- RobustScaler:用中位数和四分位距。对异常值鲁棒,我偏爱这个。
# 我自己常用的预处理代码片段
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler() # 不容易被离群点带偏
X_scaled = scaler.fit_transform(X)
有一次我在金融交易数据上做异常检测,交易金额特征有少数几笔上千万的,而大部分是几百块。Z-score处理后,正常交易全部挤在-0.2到0.2之间,那几笔异常反而被压成了1.5左右,根本无法区分。换成RobustScaler后,正常和异常的差距就出来了。
3.3 降维
降维在异常检测里有两个作用:一是减少噪声和冗余,让算法聚焦真正的异常模式;二是解决“维数灾难”——高维空间下所有点都稀疏,距离度量失去意义。
我常用的降维路线:
| 方法 | 适用场景 | 注意点 |
|---|---|---|
| PCA(主成分分析) | 线性相关较强,降维后保留方差 | 对异常值敏感,先标准化 |
| t-SNE / UMAP | 可视化探索,看异常聚类情况 | 计算慢,不能用于在线推理 |
| Autoencoder | 非线性结构,重构误差做异常分 | 需要大量正常样本 |
| 特征选择(方差过滤、互信息) | 特征极少时,保留最有用的 | 防止丢弃微弱但关键的异常信号 |
我个人建议,先做一次方差过滤,去掉方差接近0的特征(比如传感器读数一直不变,那这个通道基本没信息)。然后跑PCA,看累计解释方差曲线,保留95%方差所需的主成分数。别一口气降到二维,信息损失太多。
3.4 本章知识结构
整张图梳理了数据预处理的三个必经阶段。你看,从原始数据出发,缺失值处理、标准化、降维这三条路最终汇聚到干净的数据集。每一环都可能有坑,但只要你按图上的策略走一遍,基本能稳住。
Pipeline 串联,这样交叉验证时不会发生数据泄露。你想想看,如果你先把所有数据做了标准化,再切分训练测试集,那测试集信息就提前暴露了——这是很多人忽略的错误。
降维这块,我还想补充一点:不要为了降维而降维。如果原始特征只有十几个,而且业务可解释性很强(比如温度、压力、转速),我宁愿保留全部特征,让模型自己学习重要性。降维的收益在高维(比如 >50 维)时才明显。我曾经在基因组数据上跑异常检测,2000多个特征,PCA降维到50后,孤立森林的 AUC 提升超过15%。