4. 特征工程:时域特征提取、频域特征提取、特征选择与降维
好,咱们进入第四章。这一章,说白了就是“怎么让数据说话”。
传感器采集回来的原始信号,就像一堆乱码。你直接扔给模型,它根本看不懂。我刚开始做风机诊断那会儿,就犯过这个错——把原始振动信号直接丢进神经网络,结果模型训练出来跟个傻子一样,啥也分不清。
后来我才明白,关键一步是特征工程。你得从原始信号里,把那些能反映故障本质的“特征”给提炼出来。今天咱们就聊聊,怎么干这活。
4.1 时域特征提取:看波形,找规律
时域特征,就是直接在时间轴上分析信号。你想想看,一个正常的风机轴承,它的振动波形是平稳的、有规律的。一旦出现故障,波形就会“炸毛”——出现冲击、幅值变大、分布变乱。
我个人习惯,先把下面这几个最常用的时域特征算出来:
| 特征名称 | 计算公式 | 物理意义 |
|---|---|---|
| 均值 | \(\bar{x} = \frac{1}{N}\sum_{i=1}^{N}x_i\) | 信号的中心趋势,反映静态分量 |
| 均方根值 (RMS) | \(X_{rms} = \sqrt{\frac{1}{N}\sum_{i=1}^{N}x_i^2}\) | 信号的能量大小,磨损越严重,RMS越大 |
| 峰值 | \(X_{peak} = \max(|x_i|)\) | 信号的最大冲击,对早期故障敏感 |
| 峭度 (Kurtosis) | \(K = \frac{1}{N}\sum_{i=1}^{N}(\frac{x_i-\bar{x}}{\sigma})^4\) | 信号分布的“尖锐”程度,冲击故障时峭度会飙升 |
| 峰值因子 | \(C = X_{peak} / X_{rms}\) | 冲击性故障的敏感指标 |
代码实现也很简单,用Python的numpy和scipy几行就搞定:
import numpy as np
from scipy import stats
def extract_time_features(signal):
features = {}
features['mean'] = np.mean(signal)
features['rms'] = np.sqrt(np.mean(signal**2))
features['peak'] = np.max(np.abs(signal))
features['kurtosis'] = stats.kurtosis(signal)
features['crest_factor'] = features['peak'] / features['rms']
return features
4.2 频域特征提取:听声音,找频率
时域特征能看出“有没有问题”,但很难说清“哪里有问题”。这时候就得看频域了。
频域分析,说白了就是把信号从“时间-幅值”坐标系,转换到“频率-幅值”坐标系。每个机械部件(轴承内圈、外圈、滚动体、齿轮的齿)都有自己特定的故障频率。一旦某个部件坏了,对应的频率成分就会“冒出来”。
嗯,这里要注意:做频域分析前,一定要先做傅里叶变换(FFT)。我见过有人直接把原始数据当频谱用,那肯定是不对的。
常用的频域特征包括:
- 重心频率:频谱的“重心”位置,反映信号主频带的变化
- 均方频率:频率的均方值,对高频成分敏感
- 频率方差:频谱能量的分散程度
- 特定频带能量:比如提取轴承内圈故障频率附近的能量值
我建议,在提取频域特征时,先计算一下关键部件的理论故障频率。比如轴承内圈故障频率 \(f_i = \frac{n}{2} f_r (1 + \frac{d}{D} \cos \alpha)\),然后重点看那个频率附近有没有能量抬升。
import numpy as np
from scipy.fft import fft
def extract_freq_features(signal, fs):
n = len(signal)
freqs = np.fft.fftfreq(n, 1/fs)
spectrum = np.abs(fft(signal))[:n//2]
freqs = freqs[:n//2]
# 重心频率
fc = np.sum(freqs * spectrum) / np.sum(spectrum)
# 均方频率
msf = np.sum(freqs**2 * spectrum) / np.sum(spectrum)
# 频率方差
vf = np.sum((freqs - fc)**2 * spectrum) / np.sum(spectrum)
return {'fc': fc, 'msf': msf, 'vf': vf}
4.3 特征选择与降维:别让模型“挑花了眼”
特征提取完了,你可能手里有几十个甚至上百个特征。但特征不是越多越好。你想想看,如果塞进去一堆无关特征,模型反而会“学偏”。
我曾经在一个项目里,提取了50多个特征,结果模型训练完,准确率还不如只用10个关键特征。后来一分析,很多特征之间高度相关,等于重复信息。
常用的方法有这几类:
4.3.1 过滤法
先算每个特征与目标变量(正常/故障)的相关性,然后挑出相关性高的。比如用皮尔逊相关系数、互信息等。速度快,适合初步筛选。
4.3.2 包裹法
把特征子集扔进模型,看效果。比如递归特征消除(RFE),每次去掉一个最不重要的特征,直到找到最优子集。效果好,但计算量大。
4.3.3 嵌入法
在模型训练过程中自动做特征选择。比如Lasso回归(L1正则化),它会自动把不重要的特征系数压缩到0。随机森林也能输出特征重要性排序。
4.3.4 降维:PCA
如果特征太多,又不想丢掉信息,可以用主成分分析(PCA)。它能把高维特征压缩成几个“主成分”,每个主成分都是原始特征的线性组合。
我个人习惯,先用过滤法快速筛一遍,把相关性低于0.1的特征扔掉。然后用随机森林看特征重要性,保留Top 10-15个。如果特征之间相关性太高(比如>0.9),只保留其中一个。
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest, f_classif
# 过滤法:选择与目标最相关的K个特征
selector = SelectKBest(score_func=f_classif, k=15)
X_selected = selector.fit_transform(X, y)
# 随机森林特征重要性
rf = RandomForestClassifier()
rf.fit(X, y)
importances = rf.feature_importances_
# 保留重要性大于阈值的特征
important_idx = np.where(importances > 0.02)[0]
4.4 知识体系总览
说了这么多,我把这一章的核心逻辑画成了一张图,方便你理解:
核心要点:
- 时域特征:快速判断“有没有故障”,对冲击类故障敏感
- 频域特征:定位“哪个部件故障”,需要结合理论故障频率
- 特征选择:不是越多越好,要挑“有用”的,去掉冗余的
- 降维:当特征太多时,用PCA等工具压缩维度,保留主要信息
好了,这一章的内容就到这儿。特征工程做得好,模型训练事半功倍。下一章咱们聊聊模型选型——到底用传统机器学习还是深度学习?到时候再细说。