第四章:特征工程入门——时域特征提取、频域特征提取、特征选择方法
各位同行,大家好。这一章我们聊聊特征工程。
说实话,在风电运维这个行当里摸爬滚打这么多年,我越来越觉得:数据挖掘的成败,七分在特征,三分在模型。你模型再花哨,喂进去的是垃圾特征,出来的只能是垃圾结果。我自己就吃过这个亏——有一回做齿轮箱故障诊断,直接拿原始振动信号扔进神经网络,折腾了两周,准确率死活上不去。后来老老实实做特征提取,半天就搞定了。
好,咱们直接进入正题。
4.1 时域特征提取:从波形里“抠”信息
时域特征,说白了就是直接从信号波形上算出来的统计量。它最直观,也最常用。你想想看,一个振动传感器贴在轴承座上,采集到的是一串随时间变化的数值——这就是时域信号。
常用的时域特征有哪些?
我习惯把它们分成两类:有量纲特征和无量纲特征。
| 特征类别 | 特征名称 | 计算公式 | 物理意义 |
|---|---|---|---|
| 有量纲特征 | 均值 | μ = (1/N) Σxᵢ | 信号直流分量,反映趋势 |
| 均方根值 | RMS = √[(1/N) Σxᵢ²] | 信号能量,我最常用的指标 | |
| 峰值 | Xpeak = max|xᵢ| | 冲击强度,轴承故障时明显增大 | |
| 方差 | σ² = (1/N) Σ(xᵢ-μ)² | 信号波动程度 | |
| 无量纲特征 | 峭度 | K = (1/N) Σ[(xᵢ-μ)/σ]⁴ | 冲击性指标,正常值≈3 |
| 偏度 | S = (1/N) Σ[(xᵢ-μ)/σ]³ | 分布不对称性 | |
| 峰值因子 | C = Xpeak / 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['variance'] = np.var(signal)
features['kurtosis'] = stats.kurtosis(signal)
features['skewness'] = stats.skew(signal)
features['crest_factor'] = features['peak'] / features['rms']
return features
# 示例:处理一段振动数据
vibration_data = np.random.randn(1000) # 模拟数据
time_features = extract_time_features(vibration_data)
print(time_features)
4.2 频域特征提取:换个角度看问题
时域特征虽然直观,但有些故障在时域里根本看不出来。举个例子,齿轮的啮合频率故障——时域波形可能只是微微抖动,但一转到频域,故障频率处的幅值会明显凸起。
频域分析的核心工具:傅里叶变换
嗯,这里要注意。FFT(快速傅里叶变换)是基础,但直接用FFT得到的频谱有512条、1024条谱线,你不可能把每条谱线都当特征。那样做的话,特征维度比样本还多,模型直接过拟合。
我通常的做法是:提取频域统计特征。
- 重心频率:频谱的能量重心位置,反映信号主频带
- 频率方差:频谱的分散程度
- 均方频率:各频率分量的加权平均
- 特定频带能量:比如0-100Hz、100-200Hz等频段的能量占比
举个例子,做齿轮箱故障诊断时,我习惯把频谱分成三个频带:
| 频带 | 范围 | 对应故障 |
|---|---|---|
| 低频带 | 0-500 Hz | 轴不平衡、不对中 |
| 中频带 | 500-2000 Hz | 齿轮啮合故障 |
| 高频带 | 2000-5000 Hz | 轴承早期故障 |
import numpy as np
from scipy.fft import fft, fftfreq
def extract_frequency_features(signal, fs=1000):
n = len(signal)
# 加汉宁窗
window = np.hanning(n)
signal_windowed = signal * window
# FFT
spectrum = fft(signal_windowed)
magnitude = np.abs(spectrum[:n//2])
freqs = fftfreq(n, 1/fs)[:n//2]
features = {}
# 重心频率
features['centroid'] = np.sum(freqs * magnitude) / np.sum(magnitude)
# 频率方差
features['freq_variance'] = np.sum((freqs - features['centroid'])**2 * magnitude) / np.sum(magnitude)
# 特定频带能量
low_band = magnitude[(freqs >= 0) & (freqs < 500)]
features['low_band_energy'] = np.sum(low_band**2)
return features
4.3 特征选择方法:去粗取精
特征提取完了,你可能得到几十个甚至上百个特征。但别急着全扔进模型——特征太多不是好事。我见过有人提取了200多个特征,结果模型训练时间长了10倍,准确率反而下降了。这就是“维度灾难”。
特征选择,就是帮你从一堆特征里挑出最有用的那几个。
常用的三类方法:
- 过滤法(Filter):先算特征和目标变量的相关性,再排序筛选。速度快,适合大规模数据。
- 包裹法(Wrapper):把特征子集扔进模型,看效果好坏。准确率高,但计算量大。
- 嵌入法(Embedded):在模型训练过程中自动选择特征。比如Lasso回归、决策树特征重要性。
我个人最常用的是随机森林特征重要性(嵌入法)。为什么?因为它既考虑了特征与目标的关系,又考虑了特征之间的交互作用,而且实现起来特别方便。
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
# 假设X是特征矩阵,y是标签
X = pd.DataFrame(...) # 你的特征数据
y = pd.Series(...) # 故障标签
# 训练随机森林
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X, y)
# 获取特征重要性
importance = rf.feature_importances_
feature_names = X.columns
# 排序并展示前10个重要特征
sorted_idx = np.argsort(importance)[::-1]
print("Top 10 重要特征:")
for i in range(10):
print(f"{feature_names[sorted_idx[i]]}: {importance[sorted_idx[i]]:.4f}")
4.4 本章知识体系总览
说了这么多,咱们用一张图把整个流程串起来。这张图是我自己画的知识框架,你一看就明白特征工程在数据挖掘中的位置了。
这张图把咱们这章的内容串起来了。从原始数据出发,经过时域和频域两条路径提取特征,再通过特征选择去粗取精,最终得到高质量的特征集,喂给故障诊断模型。
公众号:蓝海数据掘金营,微信deep3321