搞设备故障诊断这些年,我发现自己越来越“保守”了。看到新模型第一反应不是“好酷”,而是“它到底在解决什么实际问题”。很多人一上来就堆 LSTM、加注意力、上 Transformer,结果在公开数据集上刷出漂亮指标,一碰到现场有噪声的振动信号,立刻原形毕露。今天想聊的是一套我调试过很多次的组合路线:狮群优化算法(LSO)+ K-means + Transformer + GRU,专门用来处理非平稳、强噪声背景下的故障诊断任务。它不是什么天外来客般的全新架构,而是把几个成熟模块按正确逻辑串成一条流水线,让每个模块都干自己最擅长的事。这篇文章适合三类人:正在做故障诊断方向的研究生、想换思路的算法工程师,以及准备把这套方法写成论文但不知道从哪下手的同学。我会把每个模块为什么存在、怎么调、踩过哪些坑,一条条说清楚。
注意,我默认你已经在 Matlab 环境下跑过深度学习代码,至少知道什么是训练集和损失函数。如果还不太熟,也能看懂整体思路,只是动手时需要先补一下基础。
1. 先别急着调代码:这套组合里的每一步到底在解决什么问题
1.1 故障信号的老三难
我接触过不少真实设备数据,包括轴承、齿轮箱、电机等,最头疼的不是模型不行,而是信号本身就带着三个毛病。
第一,非平稳性。设备转速和负载不会稳定不变,哪怕是同一类型的故障,在不同工况下表现出的频域特征也会有差异。早期故障尤其如此——特征很微弱,经常被工况波动直接淹没。
第二,强噪声。传感器本身有底噪,现场还有电磁干扰和相邻设备的振动耦合。很多算法在公开数据集上表现优异,换到现场数据就崩溃,主要原因就是没有处理好噪声。
第三,样本不均衡。正常工况的样本很容易拿到,故障样本却非常稀缺,尤其是有标签的故障样本。这就导致模型容易把大多数测试样本都判成“正常”,准确率虚高,但实际没任何价值。
这三件事叠加在一起,意味着单纯换一个“更强的分类器”并不能解决问题。你需要先把信号中的有效信息找出来,再做时序建模,最后还要防止在样本不均衡上翻车。我用的这套 LSO-Kmeans-Transformer-GRU 组合,本质上就是分别针对这三个问题给出的对策。
1.2 四个模块的分工
先说结论,这套方法可以拆成三个层次:
- 优化层:狮群优化算法(LSO)。负责搜索模型超参数。故障诊断模型的超参数特别多,学习率、Transformer 头数、GRU 隐藏单元数、K-means 的聚类数等,手动调会调到怀疑人生。
- 预处理层:K-means。承担数据净化和结构感知。它不是拿来给样本“贴标签”的,而是用来剔除离群样本、缓解类别不平衡,同时找到数据内部隐含的结构关系。
- 特征提取层:Transformer + GRU。Transformer 抓长程依赖,GRU 抓时序动态变化,两者串联后完成从原始信号到故障类别的映射。
这样拆开后,每个模块的职责都变得非常单一。这比堆一个超大模型更好调试,也更容易解释清楚。
1.3 为什么是这四件套,而不是“全家桶”
有人可能会问:直接用 Transformer 不就行了?为什么还要加 K-means 和 GRU?
单纯的 Transformer 确实能捕捉长程特征,但它的训练需要大量数据,而故障诊断领域恰恰是“小样本”场景。直接用 Transformer 容易过拟合,尤其当故障样本只有几百条时,注意力矩阵很容易记住噪声而不是规律。
GRU 则负责把 Transformer 输出序列在时间维度上进一步压缩。实际故障信号里,相邻时间点的状态变化很有价值。GRU 用更少的参数量(相比 LSTM)完成了这个任务。
K-means 的价值在外围,很多人会忽略。它可以提前发现样本中的离群点,也能在训练前为不同类别找到自然分组。这些信息注入后的模型,泛化能力有明显提升。
至于为什么优化算法选 LSO 而不是粒子群或遗传算法,我后面专门讲。这里先记住一个观点:组合方法的价值,不在于“模块多”,而在于“每个模块都承担了明确职责”。
2. 狮群优化算法:LSO 的实现细节与参数调优
2.1 狮群行为模型:三个角色的分工
狮群优化算法的灵感来自狮子群体的协作捕猎行为。我在第一次接触时觉得很意外,因为自然界的狮子分工非常明确:狮王负责守卫领地,母狮负责协作捕猎,幼狮负责学习和探索。
算法把搜索个体也分成这三类角色:
- 狮王:当前最优个体,数量一般为 1。它的搜索策略是小步精细搜索,保护已有优势。这相当于粒子群算法里的全局最优解,但搜索幅度更小,避免震荡。
- 母狮:中间层个体,数量最多。它们负责大范围协作搜索,位置更新时会参考狮王位置,也会参考群体中其他优秀个体的位置,保证搜索的高效性。
- 幼狮:少量个体,有两种行为模式。一部分围绕狮王进行局部学习,另一部分被驱逐到搜索空间边缘进行探索式搜索,用来防止陷入局部最优。
这种角色分工让 LSO 同时具备局部开发能力和全局探索能力。相比粒子群算法容易早熟、遗传算法收敛慢的问题,LSO 在中等规模的超参搜索问题上表现很稳定。
2.2 位置更新公式与参数设定
具体实现时,我会用以下这套伪代码逻辑,它是基于常见 LSO 改进实现的简化版本:
初始化狮子群体(位置 = 超参数向量) 设定角色比例:狮王 1 个,幼狮若干,其余为母狮 while 迭代次数 t <= 最大迭代次数: 计算每个个体的适应度(例如验证集上的 F1 分数) 按适应度排序,选最优为狮王 更新狮王位置: x_new = x_best + gamma * (x_best - x) + sigma * randn(D) 更新母狮位置: x_new = x + alpha1 * (x_best - x) + alpha2 * (x_good - x) 更新幼狮位置: 学习型幼狮:x_new = x + beta1 * (x_best - x) 驱逐型幼狮:x_new = x - beta2 * (x_rand - x) 边界处理,防止超参数越界 记录全局最优这里有几点参数上的经验:
gamma是狮王精细搜索的步长系数,建议取 0.1 左右,太小会导致收敛慢,太大会让搜索变成随机游走。alpha1和alpha2分别控制母狮向狮王和群体均值学习的强度,常用范围在 0.5~1.0 之间。beta1和beta2用于幼狮,前者是学习速率,后者是驱逐距离,通常取 0.5 和 1.2 左右。- 种群规模建议设在 20~30,迭代次数 30~50。这个规模对深度学习训练来说已经不小了,因为每次适应度评估都要完整训练一遍模型。
2.3 Matlab 实现时的三个注意点
第一,务必向量化个体更新。写 for 循环逐个体更新在种群只有 20 个时也能跑,但如果你把 LSO 和深度学习训练耦合在一起,每次评估都要几分钟,循环效率影响就很大了。
第二,适应度函数别只盯着准确率。故障诊断中样本不均衡是常态,我建议用验证集上的 F1-score 或 G-mean 作为 LSO 的适应度函数,而不是准确率。否则算法会把所有样本都判成多数类,适应度虚高,实际诊断能力却差。
第三,保存收敛曲线。画出最优适应度随迭代次数的变化曲线,可以帮助判断 LSO 是否早熟收敛。如果曲线在 5 次迭代内就完全平坦,很可能是种群多样性不足,可以适当增加幼狮的驱逐型比例。
3. K-means 的真实角色:不是聚类,而是数据净化器
3.1 训练数据里的“脏样本”比噪声更麻烦
故障诊断任务中,训练数据是分批从现场采集的。现场数据有个特点:工况切换、传感器瞬时扰动、人为误操作,都会产生一批与故障无关的异常样本。
这些样本既不是“正常”也不是“故障”,它们是“脏”的。如果让模型强行学习这些样本,分类边界会被带偏,测试时遇到真实信号反而拿不准。
K-means 在这里的作用,是把样本投影到特征空间后,快速发现那些“孤岛”样本——即距离所有簇中心都很远的点。这类样本往往是异常记录或标签错误,直接剔除,比让模型硬学要安全得多。
3.2 K-means 参与训练的三种方式
我在不同项目里试过三种用法,各有适用场景。
方式一:剔除离群样本。对训练集做标准化后跑 K-means,统计每个样本到最近簇中心的距离,取距离分布的上 5% 作为离群值删掉。适合处理标签噪声和数据记录错误。
方式二:簇中心作为附加特征。把每个样本所属簇的中心向量拼接进原始特征或作为模型输入的额外通道。这种方式等于给模型提供了数据的“结构先验”,在某些数据集上能提升 2~3 个百分点的准确率。
方式三:按簇分层采样。当某个故障类别样本极少时,优先保留该类别所在簇的全部样本,再从其他簇中抽样。这相当于一种朴素的过采样策略,简便有效。
3.3 K 值怎么选:肘部法则 + LSO 联调
K-means 最麻烦的地方是 K 值不好定。我个人的经验是不要单独用肘部法则,因为故障数据本身不是很规整的簇状分布,SSE 曲线的“肘点”经常不明显。
更靠谱的做法是分两步:
- 先用肘部法则或轮廓系数筛出一个候选范围,通常我会把 K 定在 3~8。
- 再把 K 作为 LSO 要优化的超参数之一,和 Transformer、GRU 的超参数一起搜索,以验证集 F1 为准。
这样 K 值就不是拍脑袋定的,而是和数据、模型联合调出来的。也是标题里 LSO 和 K-means 能联动的关键原因。
3.4 千万别踩的坑:数据泄漏
这是这类方法里最容易犯、也最致命的一个问题。
K-means 的拟合过程(包括标准化参数计算)必须在“训练集划分之后”进行,严格只用训练数据。测试集和验证集只能在训练完成后用同样的聚类参数做变换或标记,绝不能参与 K-means 的拟合。
我见过有人把全量数据跑了一遍 K-means,再划分训练测试集,结果测试集里“隐藏”了训练集的数据分布信息,指标虚高到离谱,一上现场就崩。这不是模型的问题,是方法上的信息泄漏。
4. Transformer-GRU 时序故障特征提取层
4.1 Transformer 编码器在故障诊断里的定位
先说明一下,我这里用的是 Transformer 的编码器部分,不加解码器。故障诊断是分类任务,不需要生成序列,所以只取编码特征。
Transformer 的核心是自注意力机制。它会计算序列中每个位置和其他所有位置的关联权重。对故障信号来说,这意味着在某一时刻出现的异常冲击,可以与几十个时间步之前的状态变化产生关联。
位置编码也值得一说。Transformer 本身没有时间顺序概念,必须给输入打上位置标记。Matlab 里你可以手动拼接位置编码矩阵,或者直接使用内置层。实际效果上,正余弦位置编码在故障诊断里完全够用。
Transformer 层数建议控制在 2~4 层。故障诊断样本量通常不大,层数太深容易过拟合,而且训练时间大幅增加。
4.2 GRU 为什么够用
GRU 是简化版的 LSTM,只有更新门和重置门。它比 LSTM 参数少,训练更快,在很多中等长度序列任务里效果反而更稳定。
在设备故障诊断中,信号的时序长度往往在几百到几千个采样点之间。GRU 可以逐步把 Transformer 输出序列压缩成一个固定维度的特征向量,再交给全连接分类层。
重置门决定当前候选状态要不要保留上一时刻的信息,更新门决定上一状态有多少被带入当前状态。这种门控结构让 GRU 能有效记住故障发生的“上下文”,而不是只看当前时刻的瞬态值。
4.3 两者怎么拼接出来更稳
我实测过三种拼接方式,经验如下:
- 串联结构:输入序列 → Transformer 编码器 → GRU → 全连接分类。最常用,逻辑清晰,Transformer 先做全局建模,GRU 再做时序压缩,推荐优先尝试。
- 并联结构:输入序列分别送入 Transformer 和 GRU,得到的两个特征向量拼接后进入分类层。这种方式在长序列和短序列特征均匀分布时效果不错,但参数量更大。
- 双向 GRU 增强:如果算力允许,把 GRU 改成双向,可以同时从正反向理解时间序列,在高噪声数据上能小幅提升。
大多数场景下我会先跑串联结构,稳定之后再尝试其他变体。
4.4 Matlab 实现时的兼容问题
Matlab 中搭建 Transformer-GRU 模型,我建议两种路线根据你的版本选择:
一是使用深度学习工具箱内置的transformerLayer(较新版本才有)加gruLayer,直接搭建层图。这种方法上手快,适合标准结构。
二是用自定义训练循环加dlarray,自己写多头自注意力层。适合需要精细控制注意力头数、掩码逻辑和嵌入维度的场景。
我不推荐一上来就写完整自定义层,因为调试成本很高。先确认你的 Matlab 版本是否支持内置 Transformer,不支持的话再手写。如果两者都行,优先内置。
另外要重点注意序列填充。故障信号经过滑动窗口切分后,所有序列长度不一定一致。Matlab 的padSequence会填充NaN,而此时 Transformer 的注意力不能被填充元素污染,你需要传入有效的掩码矩阵。这个是我调试过程中最容易漏的细节。
5. 完整实验设计和 Matlab 落地流程
5.1 数据准备与序列化
我先拿一个常见的实验室滚动轴承故障数据集来举例,四类状态:正常、内圈故障、外圈故障、滚动体故障。每类状态采集若干段振动信号,采样率可以设为 12 kHz,转速 1500 rpm 左右,故障尺寸在零点几毫米级别。
采集完成后,用滑动窗口切分样本。窗口长度通常设为 1024 或 2048 个采样点,每个窗口作为一个训练样本。相邻窗口之间可以重叠 50% 来增加样本量。
划分比例建议 6:2:2,训练集、验证集、测试集严格按时间段划分,不随机打乱。原因很简单:同一时间段的数据高度相似,随机划分会让验证集和测试集沾到训练集的光,指标偏高。
5.2 特征提取与标准化
原始振动信号可以直接输入网络,但加上特征工程会更稳。我在实验中会先计算一组时域和频域特征:
时域特征包括均值、方差、峰值、峭度、偏度。频域特征包括频谱重心、频谱峭度、主频带能量占比。
这些特征的维度不高(十几维到几十维),但信息密度很大。我会把它们和原始序列的降采样版本拼接,作为 Transformer 的输入特征通道。K-means 则是直接在这些统计特征上运行。
标准化方面,建议用 Z-Score,也就是让每个特征维度均值为 0、方差为 1。注意,标准化的均值和方差一定只在训练集上计算,测试集引用同一组参数,否则又会出现数据泄漏。
5.3 超参数参考表
这是我跑完多个数据集后总结的一组默认参数,可以作为起步配置:
| 参数 | 默认值 | 推荐范围 | 说明 |
|---|---|---|---|
| LSO 种群规模 | 20 | 15~30 | 太大训练成本高 |
| LSO 迭代次数 | 40 | 20~50 | 依据训练时长调整 |
| K-means 聚类数 K | 4 | 3~8 | 由 LSO 联合搜索 |
| Transformer 层数 | 3 | 2~4 | 层数过深易过拟合 |
| 注意力头数 | 4 | 4~8 | 头数太多参数暴涨 |
| 嵌入维度 | 64 | 32~128 | 数据量大可上调 |
| GRU 隐藏单元数 | 48 | 32~64 | 太小欠拟合 |
| 学习率 | 0.001 | 0.0005~0.005 | Adam 优化器时用 |
| 批大小 | 64 | 32~128 | 显存允许时偏大为好 |
| Dropout | 0.3 | 0.2~0.5 | 防止注意力过拟合 |
5.4 训练流程与评估指标
训练时的整体流程按以下顺序推进:
- 从训练集计算 Z-Score 参数,标准化训练、验证、测试集。
- 在训练集上跑 K-means,剔除离群样本,记录簇中心和标准化参数。
- 初始化 LSO 种群,每个个体代表一组超参数。
- 每个个体用当前超参训练一次 Transformer-GRU 模型,在验证集上计算 F1 分数,作为适应度。
- LSO 迭代更新,输出最优超参。
- 用最优超参在训练集上重新训练模型,在测试集上评估。
评估指标我一般看四个:准确率、精确率、召回率、F1-score,另外一定画混淆矩阵。只看准确率在故障诊断里是不够的,内圈故障和滚动体故障经常混淆,只有看混淆矩阵才能发现模型在哪两类之间犹豫不决。
6. 实操中的高频坑:调试与排查手册
6.1 验证集指标高,测试集指标低
这是最常见的问题,通常有四个原因:
- 数据泄漏。检查 K-means 和标准化是不是都在划分之后才拟合的。
- 验证集参与过超参选择。LSO 是以验证集 F1 为目标的,所以验证集成绩天然偏高。这不算 bug,但需要明白最终可信指标是测试集。
- 随机性。深度学习训练有随机性,同一组超参跑两次结果可能差 1~2 个百分点。我建议每个配置跑 3 次取均值,不要用单次结果下结论。
- 样本不均衡。如果训练集里正常样本占 90%,模型倾向于全判正常。解决方法是按 K-means 分层采样,或者给损失函数加权重。
6.2 LSO 适应度震荡不收敛
LSO 本身是稳定算法,但在深度学习场景里会有这个问题。原因是模型训练本身有随机性:适应度函数(验证集 F1)就是个带噪声的观测值,LSO 容易在不同代之间反复跳跃。
我的解决办法有三个:
一是固定随机种子。每次训练都固定 Matlab 的随机数种子,保证同一组超参的评估结果可复现。
二是使用“精英保留机制”。每一代直接保留上一代最优个体,不参与更新。这样最优解至少不会变差。
三是降低学习率范围。把 LSO 搜索上下界收紧,例如学习率只在 0.0005~0.003 之间搜索,能减少很多无效尝试。
6.3 训练时间过长
Transformer-GRU 本身不算非常重,但如果嵌入维度和头数设置得太大,叠加 LSO 的几十次评估,训练时长会爆炸。
我的经验是先把模型规模调小,跑通流程,确认结果趋势正确后再放大。比如先用嵌入维度 32、1 层 Transformer,把整体流程控制在 30 分钟内跑完,再逐步加大。
另一个技巧是给 LSO 设定早停条件:如果连续 10 次迭代最优适应度没有提升,提前结束搜索。大部分情况下得到的解已经够用。
6.4 Transformer 层报维度错误
这个问题出现的频率非常高。错误信息一般是“维度不匹配”或“输入 size 不一致”。
核心原因通常是序列填充后的长度和位置编码矩阵不匹配,或者注意力头数不能整除嵌入维度。比如嵌入维度 64、注意力头数 6,64 不能被 6 整除,就会报错。
我建议建立一条默认规则:嵌入维度必须是注意力头数的整数倍。如果嵌入维度 64,头数选 4 或 8,别选 6 或 5。
6.5 问题排查速查表
我整理了一张常用排查表,方便大家快速对照:
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| 训练损失不下降 | 学习率过大或数据未标准化 | 降低学习率,检查 Z-Score |
| 准确率 90% 以上但召回率极低 | 类别不平衡 | 加损失权重,按簇重采样 |
| 测试集 F1 比验证集低很多 | 数据泄漏 | 检查 K-means 和标准化的拟合时机 |
| 模型对某两类故障严重混淆 | 特征区分度不够 | 增加频域特征,调整窗口长度 |
| LSO 每轮结果波动很大 | 随机种子未固定 | 固定随机种子,多轮取均值 |
| Transformer 层速度太慢 | 头数或嵌入维度太大 | 调整配置,保证整除关系 |
| GRU 输出维度和分类层不匹配 | 隐藏单元设置问题 | 检查最后时间步的输出拼接方式 |
这套流程还能怎么扩展
如果后面想继续加码,可以预留两个方向:一是把 K-means 换成能自适应确定簇数目的密度聚类算法,减少 LSO 需要搜索的参数数量;二是在 Transformer 的注意力分数上引入设备运行的工况标签作为条件约束,让模型学会区分工况变化和真实故障。
我个人做完这套东西最大的感受是:故障诊断模型能不能落地,一半取决于训练数据是否干净、划分是否严谨、验证是否可信,另一半才是模型结构本身。LSO 负责找参数,K-means 负责去脏数据,Transformer-GRU 负责建模,它们各管一段,你才不会在排查问题时一头雾水。希望这些踩坑经验能帮你少走几趟弯路。