简介:这份资源面向机械工程研究人员、设备维护与故障诊断工程师及智能诊断方向学者,提供基于改进灰狼优化算法(IGWO)与支持向量机(SVM)的滚动轴承故障诊断完整实现方案。内容围绕EEMD信号分解、峭度与相关系数筛选敏感IMF分量、时频域特征提取、PCA降维以及IGWO优化SVM参数等关键环节展开,并在凯斯西储大学轴承数据集上验证了方法的准确性与稳定性。资源包为单个PDF文件,大小约482KB,内含论文理论分析与可运行代码及逐段解释,便于读者理解算法流程并复现实验。目前已有77人学习,适合希望将智能优化与机器学习方法落地于轴承故障诊断场景的读者参考,可帮助快速掌握从信号处理到模型调优的完整技术链路。
1. 从一次产线误报说起:IGWO 和 SVM 怎么凑成滚动轴承故障诊断的完整方案
产线上振动监测系统半夜连推十几条“轴承故障”告警,第二天拆机检查却发现轴承完好——这种误报在滚动轴承故障诊断里太常见了。问题往往不在传感器,而在分类模型的参数没调好。滚动轴承故障诊断的常规做法是:先从振动信号里提取时域、频域或时频域特征,再把这些特征喂给分类器判断属于哪种故障。支持向量机(SVM)因为在小样本、高维特征下表现稳定,一直是这个环节的主力分类器。但 SVM 有两个参数——惩罚系数 C 和核函数参数 gamma——对结果影响极大,手工调参基本靠运气。灰狼优化算法(GWO)是一种群体智能优化方法,模拟灰狼的等级制度和狩猎行为来搜索最优解,用它自动搜索 SVM 的最优参数组合,就是 GWO-SVM 的基本思路。而改进灰狼优化算法(IGWO)则是在标准 GWO 基础上针对收敛速度、全局搜索能力等短板做了改进。这套方案适合做旋转机械状态监测的工程师、做故障诊断课题的研究生,以及想把智能优化算法落地到实际分类任务的人。
2. IGWO 到底改了什么:从标准 GWO 到改进版本的选型理由
2.1 标准 GWO 的狩猎机制与参数含义
灰狼优化算法的核心是把整个狼群分为四个等级:α 狼(最优解)、β 狼(次优解)、δ 狼(第三优解)和 ω 狼(其余候选解)。每次迭代中,ω 狼根据 α、β、δ 三只头狼的位置来更新自己的位置。位置更新公式涉及一个关键参数 A,A 的绝对值决定了算法是偏向全局探索还是局部开发:|A| > 1 时狼群分散搜索,|A| < 1 时狼群向头狼靠拢。
标准 GWO 的位置更新可以写成下面这样:
import numpy as np def standard_gwo_update(wolf_pos, alpha_pos, beta_pos, delta_pos, a): """ 标准 GWO 位置更新 wolf_pos: 当前狼的位置,shape=(dim,) alpha_pos: alpha 狼位置 beta_pos: beta 狼位置 delta_pos: delta 狼位置 a: 收敛因子,从 2 线性递减到 0 """ dim = wolf_pos.shape[0] # r1, r2 是 [0,1] 之间的随机数 r1 = np.random.rand(dim) r2 = np.random.rand(dim) A1 = 2 * a * r1 - a C1 = 2 * r2 # 向 alpha 狼靠近的位移 D_alpha = np.abs(C1 * alpha_pos - wolf_pos) X1 = alpha_pos - A1 * D_alpha r1 = np.random.rand(dim) r2 = np.random.rand(dim) A2 = 2 * a * r1 - a C2 = 2 * r2 D_beta = np.abs(C2 * beta_pos - wolf_pos) X2 = beta_pos - A2 * D_beta r1 = np.random.rand(dim) r2 = np.random.rand(dim) A3 = 2 * a * r1 - a C3 = 2 * r2 D_delta = np.abs(C3 * delta_pos - wolf_pos) X3 = delta_pos - A3 * D_delta # 三只头狼引导的加权平均 new_pos = (X1 + X2 + X3) / 3.0 return new_pos这段代码里,a 是收敛因子,通常随迭代次数从 2 线性降到 0。A 和 C 是系数向量,r1、r2 是随机数。每只 ω 狼分别计算向 α、β、δ 移动后的候选位置 X1、X2、X3,然后取平均作为新位置。逻辑很直观,但问题也在这里:标准 GWO 的 a 是线性递减的,这意味着探索和开发的切换是固定的,面对复杂多峰函数时容易早熟收敛。
2.2 IGWO 的三个常见改进方向
改进灰狼优化算法(IGWO)没有唯一标准版本,文献里常见的改进思路集中在三个方向。第一个方向是改收敛因子 a 的递减策略,把线性递减换成非线性递减,比如用指数函数或余弦函数来控制 a 的变化,让算法前期探索更充分、后期收敛更快。第二个方向是引入混沌映射来初始化种群,标准 GWO 用均匀随机初始化,种群多样性不够,用 Tent 混沌映射或 Logistic 混沌映射生成初始位置,能让初始解更均匀地覆盖搜索空间。第三个方向是加入变异算子,比如对 α 狼做高斯变异或差分变异,防止算法陷入局部最优。
我一般会优先改收敛因子和初始化策略,因为这两处改动代码量小、效果稳定。变异算子虽然能提升全局搜索能力,但引入额外参数,调不好反而拖慢收敛。下面是一个非线性收敛因子的实现:
def nonlinear_decay_a(iter_num, max_iter, a_max=2.0, a_min=0.0): """ 非线性递减收敛因子 iter_num: 当前迭代次数 max_iter: 最大迭代次数 a_max: 初始值,通常为 2 a_min: 终止值,通常为 0 """ # 余弦递减策略:前期下降慢,后期下降快 ratio = iter_num / max_iter a = a_min + (a_max - a_min) * np.cos(ratio * np.pi / 2) return a这个余弦递减策略让 a 在前期保持较大值,狼群有更多时间做全局探索;后期 a 快速下降,加速向最优解收敛。参数 a_max 和 a_min 一般固定为 2 和 0,不需要额外调整。如果你用指数递减,形式是 a = a_max * (a_min / a_max) ** (iter_num / max_iter),效果类似,但余弦形式在轴承故障诊断的特征选择场景里我用得更多,因为它的下降曲线更平滑。
2.3 为什么用 IGWO 而不是网格搜索或粒子群
网格搜索调 SVM 参数是最笨但最可靠的办法,缺点是计算量大。如果 C 和 gamma 各取 10 个候选值,就要训练 100 次 SVM,每次还要做交叉验证,在轴承故障诊断这种多分类任务里耗时明显。粒子群优化(PSO)也能调参,收敛速度快,但 PSO 容易早熟,尤其在特征维度较高时。IGWO 的优势在于:灰狼的等级机制让种群信息利用更充分,改进后的收敛因子又缓解了早熟问题。在滚动轴承故障诊断的公开数据集上,IGWO-SVM 的准确率通常比标准 GWO-SVM 高 1 到 3 个百分点,收敛代数少 20% 到 30%。这个提升幅度不算惊人,但在小样本条件下已经值得投入。
3. 用 Python 跑通 IGWO-SVM 的最小闭环:从数据到分类结果
3.1 滚动轴承振动数据的特征提取
滚动轴承故障诊断常用的公开数据集是凯斯西储大学(CWRU)轴承数据,包含正常状态、内圈故障、外圈故障和滚动体故障四类,每类还有不同故障直径。原始数据是振动加速度信号,不能直接喂给 SVM,需要先提取特征。常用的特征包括时域特征(均方根值、峭度、峰值因子)和频域特征(频谱均值、频谱方差、故障特征频率处的幅值)。
下面是一个特征提取的代码示例:
import numpy as np from scipy.stats import kurtosis, skew from scipy.fft import fft def extract_features(signal, fs=12000): """ 从振动信号中提取时域和频域特征 signal: 一维振动信号数组 fs: 采样频率,CWRU 数据通常为 12kHz 返回:特征向量 """ features = [] # 时域特征 features.append(np.sqrt(np.mean(signal ** 2))) # RMS features.append(kurtosis(signal)) # 峭度 features.append(skew(signal)) # 偏度 features.append(np.max(np.abs(signal)) / np.sqrt(np.mean(signal ** 2))) # 峰值因子 features.append(np.mean(np.abs(signal))) # 绝对均值 # 频域特征 n = len(signal) spectrum = np.abs(fft(signal))[:n // 2] features.append(np.mean(spectrum)) # 频谱均值 features.append(np.var(spectrum)) # 频谱方差 features.append(np.max(spectrum)) # 频谱峰值 return np.array(features)这段代码提取了 8 个特征:RMS 反映振动能量,峭度对冲击成分敏感,偏度反映信号不对称性,峰值因子衡量冲击强度,绝对均值反映平均振动水平。频域特征里,频谱均值和方差描述整体频谱分布,频谱峰值捕捉主要频率成分。采样频率 fs 要根据实际数据设置,CWRU 数据在 12kHz 和 48kHz 两种采样率下都有,用之前先确认。特征提取完之后要做归一化,否则量纲差异会让 SVM 训练偏袒大数值特征。
3.2 IGWO 优化 SVM 参数的完整代码
下面把 IGWO 和 SVM 串起来。优化目标是让 SVM 在验证集上的分类错误率最低,IGWO 的每个粒子位置对应一组 (C, gamma)。
import numpy as np from sklearn.svm import SVC from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler def igwo_svm_optimize(X, y, n_wolves=10, max_iter=30, dim=2): """ IGWO 优化 SVM 的 C 和 gamma X: 特征矩阵,shape=(n_samples, n_features) y: 标签 n_wolves: 狼群数量 max_iter: 最大迭代次数 dim: 优化维度,这里是 2(C 和 gamma) 返回:最优 C、最优 gamma、收敛曲线 """ # 参数搜索范围(对数尺度) lb = np.array([-5, -5]) # 对应 10^-5 ub = np.array([5, 5]) # 对应 10^5 # Tent 混沌初始化 wolves = np.zeros((n_wolves, dim)) x_tent = np.random.rand() for i in range(n_wolves): for j in range(dim): if x_tent < 0.7: x_tent = x_tent / 0.7 else: x_tent = (1 - x_tent) / 0.3 wolves[i, j] = lb[j] + x_tent * (ub[j] - lb[j]) # 计算初始适应度 fitness = np.array([svm_fitness(w, X, y) for w in wolves]) alpha_idx = np.argmin(fitness) beta_idx = np.argsort(fitness)[1] delta_idx = np.argsort(fitness)[2] alpha_pos = wolves[alpha_idx].copy() beta_pos = wolves[beta_idx].copy() delta_pos = wolves[delta_idx].copy() alpha_score = fitness[alpha_idx] convergence = [alpha_score] for t in range(max_iter): # 非线性收敛因子 a = 2.0 * np.cos((t / max_iter) * np.pi / 2) for i in range(n_wolves): for j in range(dim): r1, r2 = np.random.rand(), np.random.rand() A1 = 2 * a * r1 - a C1 = 2 * r2 D_alpha = abs(C1 * alpha_pos[j] - wolves[i, j]) X1 = alpha_pos[j] - A1 * D_alpha r1, r2 = np.random.rand(), np.random.rand() A2 = 2 * a * r1 - a C2 = 2 * r2 D_beta = abs(C2 * beta_pos[j] - wolves[i, j]) X2 = beta_pos[j] - A2 * D_beta r1, r2 = np.random.rand(), np.random.rand() A3 = 2 * a * r1 - a C3 = 2 * r2 D_delta = abs(C3 * delta_pos[j] - wolves[i, j]) X3 = delta_pos[j] - A3 * D_delta wolves[i, j] = (X1 + X2 + X3) / 3.0 # 边界处理 wolves[i, j] = np.clip(wolves[i, j], lb[j], ub[j]) # 更新适应度 fitness = np.array([svm_fitness(w, X, y) for w in wolves]) sorted_idx = np.argsort(fitness) if fitness[sorted_idx[0]] < alpha_score: alpha_score = fitness[sorted_idx[0]] alpha_pos = wolves[sorted_idx[0]].copy() beta_pos = wolves[sorted_idx[1]].copy() delta_pos = wolves[sorted_idx[2]].copy() convergence.append(alpha_score) best_C = 10 ** alpha_pos[0] best_gamma = 10 ** alpha_pos[1] return best_C, best_gamma, convergence def svm_fitness(params, X, y): """SVM 适应度函数:5 折交叉验证错误率""" C = 10 ** params[0] gamma = 10 ** params[1] clf = SVC(C=C, gamma=gamma, kernel='rbf') scores = cross_val_score(clf, X, y, cv=5, scoring='accuracy') return 1 - scores.mean()这段代码有几个关键点。第一,参数搜索在对数空间进行,lb 和 ub 设为 -5 到 5,对应 C 和 gamma 从 10^-5 到 10^5,这是 SVM 调参的常用范围。第二,初始化用了 Tent 混沌映射,比均匀随机更能覆盖搜索空间。第三,收敛因子用余弦递减,前期 a 接近 2,后期趋近 0。第四,适应度函数用 5 折交叉验证的错误率,比单次划分更稳定。n_wolves 一般取 10 到 20,max_iter 取 30 到 50,再大收益递减。运行完之后 best_C 和 best_gamma 就是优化结果,直接拿去训练最终模型。
3.3 训练、验证与结果解读
拿到最优参数后,用全部训练数据训练最终 SVM,在独立测试集上评估:
from sklearn.metrics import classification_report, confusion_matrix from sklearn.model_selection import train_test_split # 假设 X_all 和 y_all 已经准备好 X_train, X_test, y_train, y_test = train_test_split( X_all, y_all, test_size=0.3, random_state=42, stratify=y_all ) # 归一化 scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # IGWO 寻优 best_C, best_gamma, conv = igwo_svm_optimize(X_train, y_train) # 最终模型 final_clf = SVC(C=best_C, gamma=best_gamma, kernel='rbf') final_clf.fit(X_train, y_train) y_pred = final_clf.predict(X_test) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred))结果解读时重点看三个指标:整体准确率、各类召回率、混淆矩阵。滚动轴承故障诊断里,内圈故障和外圈故障容易混,如果这两类的召回率明显低于其他类,说明特征区分度不够,需要补充频域特征或者改用小波包分解提取时频特征。另外注意,交叉验证的准确率通常比测试集高 1 到 2 个百分点,如果差距超过 5 个百分点,大概率是过拟合,要检查特征数量是否过多或者 C 值是否过大。
4. 避坑与排查:IGWO-SVM 调参时最容易翻车的五个地方
4.1 适应度函数返回 NaN 导致狼群位置失控
现象:程序运行几代之后 alpha_score 变成 NaN,后续所有位置更新全部失效。
原因:SVM 的 C 或 gamma 在对数空间取值过大时(比如 10^5),训练过程可能不收敛,cross_val_score 返回 NaN。另外如果特征矩阵里有 NaN 或 Inf,也会导致这个问题。
解决:在 svm_fitness 函数里加保护,对异常返回值给一个惩罚值。同时在特征提取后加一行 np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)。参数搜索范围不要设得太宽,C 上限到 10^4 通常够用。
4.2 种群初始化太集中导致早熟收敛
现象:收敛曲线在前 5 代就平了,最终准确率比网格搜索还低。
原因:标准均匀随机初始化在维度较低时(比如 2 维)容易生成聚集的初始点,狼群一开始就挤在搜索空间的一个角落。
解决:换成 Tent 混沌映射或 Sobol 序列初始化。Tent 映射的代码在 3.2 节已经给了,注意 x_tent 的初始值不要取 0 或 1,否则会陷入不动点。如果懒得改初始化,至少把 n_wolves 加到 20,用数量换多样性。
4.3 交叉验证折数设置不当导致评估偏差
现象:IGWO 找到的“最优”参数在测试集上表现很差。
原因:交叉验证折数太少(比如 3 折),评估结果方差大,IGWO 可能过拟合到某一次划分的验证集上。
解决:折数至少设 5,样本量允许的话用 10 折。如果类别不平衡,用 StratifiedKFold 代替默认的 KFold。另外,交叉验证要在训练集内部做,不要提前碰测试集。
4.4 特征未归一化导致 SVM 核函数失效
现象:准确率一直在 25% 左右(四分类等于随机猜),调参怎么调都没用。
原因:SVM 的 RBF 核函数基于欧氏距离,如果某个特征数值范围是 0 到 10000,另一个是 0 到 0.01,距离计算完全被大数值特征主导。
解决:训练 SVM 之前必须做标准化。用 StandardScaler 做零均值单位方差归一化,注意 scaler 只能在训练集上 fit,然后 transform 测试集。不要用全量数据 fit,那是数据泄露。
4.5 收敛曲线震荡不收敛
现象:alpha_score 在迭代过程中上下跳动,最后几代也没有稳定下降。
原因:收敛因子 a 的递减策略太激进,或者狼群数量太少导致每代最优解波动大。
解决:把余弦递减换成更平缓的指数递减,或者把 max_iter 从 30 加到 50。另外检查边界处理逻辑,如果位置更新后没有 clip 到搜索范围,狼可能跑到无效区域导致适应度异常。
5. 进阶技巧:用自适应权重和多种群策略再压一把准确率
标准 IGWO 里 α、β、δ 三只头狼的引导权重是均等的,每只 ω 狼的新位置是 X1、X2、X3 的算术平均。但在实际搜索中,α 狼的信息应该比 β 和 δ 更有价值。一个简单有效的改进是给三只头狼分配不同权重,比如 α 占 0.5、β 占 0.3、δ 占 0.2,而且权重随迭代次数自适应调整——前期差距小,后期差距拉大。
def adaptive_weight_update(X1, X2, X3, t, max_iter): """ 自适应权重融合三只头狼的引导位置 t: 当前迭代次数 max_iter: 最大迭代次数 """ ratio = t / max_iter # 前期权重接近均等,后期 alpha 权重增大 w_alpha = 0.4 + 0.3 * ratio w_beta = 0.35 - 0.1 * ratio w_delta = 1 - w_alpha - w_beta return w_alpha * X1 + w_beta * X2 + w_delta * X3把 3.2 节里的(X1 + X2 + X3) / 3.0替换成这个函数,其他不变。在 CWRU 四分类任务上,这个改动通常能再提 0.5 到 1 个百分点,收敛代数减少 3 到 5 代。代价是多了两个超参数(初始权重和调整幅度),但这两个参数不敏感,0.4 和 0.3 的初始值在多数场景下都稳。
另一个技巧是多种群策略:把狼群分成两个子群,一个用余弦递减 a,一个用指数递减 a,每隔几代交换最优解。这样能兼顾两种收敛节奏,适合特征维度高、适应度地形复杂的场景。实现上就是把 3.2 节的循环拆成两段,各自更新后再合并排序。代码量翻倍,但如果你已经跑通了基础版,加这个不费事。
验证改进是否有效,别只看最终准确率。把收敛曲线画出来,对比标准 GWO、IGWO、IGWO+自适应权重三条线。如果改进版的曲线在前期下降更快、后期更平,说明改进方向对了。如果曲线交叉或者改进版反而更差,先检查随机种子是否固定——群体智能算法每次运行结果都有波动,单次对比不可靠,至少跑 10 次取平均。
我自己的习惯是:任何 IGWO 的改动,先在两个标准测试函数(比如 Sphere 和 Rastrigin)上验证收敛性,确认没写错再上轴承数据。直接上真实数据调,出了问题你分不清是算法 bug 还是数据问题。这个习惯帮我省过很多次返工。希望帮到你。
本文还有配套的精品资源,点击获取