简介:双向长短期记忆神经网络的故障诊断与分类预测完整源码,面向机械故障诊断、轴承状态监测领域的研究者与工程师。数据采用西储大学轴承诊断数据经特征提取后的样本,基于Matlab2023环境构建,涵盖数据导入、BiLSTM网络搭建、训练及结果可视化全流程。压缩包共6个文件,包含3个m脚本,分别为主程序、自定义翻转层与混淆矩阵绘图函数,以及1个mat格式数据集和2张效果图片,整体大小189KB,结构紧凑便于查看。已有132人学习下载,适合刚接触循环神经网络的初学者进行复现与二次开发。通过完整源码可深入理解BiLSTM对时序信号双向建模的特点,掌握前向与后向信息融合的分类思路;同时,源码预留了数据接口,可直接替换为自己采集的故障数据,迁移至其他诊断任务,是入门深度学习方法在故障诊断领域应用的实用参考。
1. BiLSTM故障诊断,不先处理数据形态就白搭
轴承座上的加速度传感器采到一万赫兹以上的振动数据,大部分工程师习惯直接拉一个1×N向量丢给分类器。但BiLSTM吃的是序列,不是向量。每个输入时间步是一个多通道观测,整个样本是一段固定窗长的时序片段。窗太长,几百个时间步后正反向记忆互相稀释;窗太短,冲击间隔跨不出窗口,正向和反向支路看到的是同一个脉冲的残肢而非全貌。在Matlab里做BiLSTM故障诊断/分类预测,难点不在网络堆多深,而在数据怎么切成观测×通道×时间戳的cell数组,以及训练参数是否匹配序列特性。下面给出一套能在Matlab完整跑通的源码思路,从层配置到调参再到结果验证,按工程顺序往下捋。
2. BiLSTM双向结构在Matlab中的建模选项拆解
2.1 双向读取在故障诊断里的实际作用
LSTM的单向读取是“只记得过去”,BiLSTM增加了一条按时间逆序扫描的支路。针对旋转机械,外圈故障冲击后激起的共振衰减波形,单LSTM要依赖遗忘门决定保留多远的冲击痕迹,而BiLSTM的正向支路保留事件前背景,反向支路从未来看到冲击,双支路在每一时间步沿特征维度拼接。拼接后隐藏维度翻倍,表达能力提升,代价是计算量和参数量同步增大。
故障分类预测场景里,前期建议隐藏单元先设64,不要盲目翻到128。样本每类只有几百条时,128个隐藏单元几乎必然过拟合。放在实际信号里,转频30Hz、采样率25.6kHz时,一个冲击周期约853个点,64个神经元在一个方向上的记忆容量已经足够编码包络变化,再增大隐藏单元,训练曲线会漂亮,但验证集波动也随之增大。
2.2 bilstmLayer 常用属性
Deep Learning Toolbox直接把双向结构封装为bilstmLayer,层对象的关键属性和场景如下:
| 属性 | 常用值 | 说明 |
|---|---|---|
| NumHiddenUnits | 32 / 64 / 128 | 每方向的隐藏维度,常取2的幂 |
| OutputMode | 'last' / 'sequence' | 故障分类用 'last',剩余寿命预测用 'sequence' |
| InputWeightsInitializer | 'he' 或 'orthogonal' | 输入权重初始化,绝大多数情况保持默认 |
| RecurrentWeightsInitializer | 'orthogonal' | 循环权重保持正交,降低梯度收缩风险 |
| BiasInitializer | 'ones' 或 'zeros' | 手动调的场景少,默认即可 |
真正需要动的一般只有OutputMode。如果训练早期loss直接变成NaN,优先检查学习率和数据归一化,而不是折腾初始化器。
2.3 内置双向与手动翻转拼接的差别
部分老代码用两个单向LSTM模拟双向:一个处理原始序列,另一个处理翻转后的序列,再用concatenationLayer拼接。这个办法的缺陷在于翻转发生在进入网络之前,两个支路的梯度流和时序末端对齐完全靠手工维护,反向支路的时间步错位会直接影响序列末端的分类输出。
内置bilstmLayer在层内部同时维护正向和反向两个隐藏状态,共享同一条损失函数,拼接点在每个时间步上对称。验证过几次手动拼接方案后,我在项目里都改为直接调用bilstmLayer,只要工具箱版本支持,就不要手写拼接。
2.4 Matlab最小BiLSTM分类网络骨架
inputSize = 1; % 单通道振动加速度信号 numClasses = 4; % 正常、内圈、外圈、滚动体 layers = [ sequenceInputLayer(inputSize, 'Name', 'input') bilstmLayer(64, 'OutputMode', 'last', 'Name', 'bilstm') dropoutLayer(0.2, 'Name', 'drop') fullyConnectedLayer(numClasses, 'Name', 'fc') softmaxLayer('Name', 'softmax') classificationLayer('Name', 'output') ];sequenceInputLayer只声明通道数量,每个观测是1×T的矩阵,T是单个样本的时间点数。bilstmLayer的OutputMode设为'last',代表只在序列末尾把双向信息压成一个向量,供后续全连接层做分类;若改成'sequence',输出是每个时间步的特征,分类场景还要自己再做一次时间维压缩,参数利用率偏低。dropout放在全连接之前,训练时随机丢弃20%的单元,防止分类头过拟合。这段骨架不包含数据预处理,能直接通过layerGraph检查,但离训练还有一步距离,下一章把数据闭环补齐。
3. 轴承故障分类预测的Matlab完整源码与训练闭环
3.1 输入数据组织方式:矩阵还是cell
trainNetwork对序列数据的输入有固定要求:X必须是列方向排列的cell数组,每个单元是C×T矩阵,C为测点通道数,T为该样本的时间点数。网络不认一个N×T的普通矩阵,那种写法会被解释成N个独立观测,每个观测只有1个时间步,BiLSTM的双向能力完全没有发挥空间。
常见的错误是把多个样本横向拼接成一个长向量,再在网络内部手动切回。这会把样本边界上的伪冲击引入序列,训练时表现尚可,换到新数据立刻劣化。输入组织方式的对照如下:
| 输入形态 | 正确做法 | 常见出错点 |
|---|---|---|
| 单通道振动信号 | X{i} = 1×T | 错误拼成大矩阵喂养 |
| 多测点同步采集 | X{i} = C×T | 通道维与时间维颠倒 |
| 变长样本 | 每个cell长度不同 | 未处理填充影响双向读取 |
3.2 训练前的数据切片与标签构造
用滑动窗口截取原始振动信号是工程里最常见的做法。窗口长度要覆盖至少两个冲击周期。转速1500转/分时转频为25Hz,1024点窗口在25.6kHz采样率下约40ms,包含一个完整转频周期;用50%重叠的滑动步长,样本量翻倍,但相邻窗口相关性也会变高。验证集必须按原始文件划分,不能随机切cell,否则相邻窗口泄漏进验证集,训练曲线异常漂亮,换到新采集数据就崩。
fs = 25600; % 采样率,按采集卡实际值修改 winLen = 1024; % 窗口长度 step = 512; % 滑动步长,窗口重叠率50% dataPath = 'E:/bearing/'; % 数据目录,按类别分子文件夹 classes = ["normal", "inner", "outer", "ball"]; X = {}; Y = []; for ci = 1:numel(classes) files = dir(fullfile(dataPath, classes(ci), '*.mat')); for fi = 1:numel(files) raw = load(fullfile(files(fi).folder, files(fi).name)); sig = raw.signal(:)'; % 强制转成行向量 sig = (sig - mean(sig)) / std(sig); % 每个样本独立z-score归一化 starts = 1:step:numel(sig)-winLen; for si = 1:numel(starts) seg = sig(starts(si):starts(si)+winLen-1); X{end+1,1} = seg; % 1×winLen矩阵 Y(end+1,1) = classes(ci); % 类别标签 end end end Y = categorical(Y);每个样本独立做z-score归一化,而不是按全局统计量,原因是不同工况下振动幅值差异显著,按全局归一化会让网络把幅值当分类依据,换一台设备或转速后准确率直线下降。step控制样本重叠率,50%重叠能有效增加样本量,但重叠过高时训练集和验证集的独立性变差,模型误差的估计会偏乐观。
3.3 训练选项设置与模型保存
numClasses = numel(classes); layers = [ sequenceInputLayer(1, 'Name', 'input') bilstmLayer(64, 'OutputMode', 'last', 'Name', 'bilstm') dropoutLayer(0.2, 'Name', 'drop') fullyConnectedLayer(numClasses, 'Name', 'fc') softmaxLayer('Name', 'softmax') classificationLayer('Name', 'output') ]; options = trainingOptions('adam', ... 'InitialLearnRate', 0.001, ... 'MiniBatchSize', 32, ... 'MaxEpochs', 30, ... 'Shuffle', 'every-epoch', ... 'Verbose', true, ... 'Plots', 'training-progress'); net = trainNetwork(XTrain, YTrain, layers, options); save('bilstm_bearing_model.mat', 'net', 'fs', 'classes');训练选项里,Shuffle设为every-epoch,保证每个epoch的batch组合不同,避免模型记住固定batch顺序。MiniBatchSize=32对单通道1024点序列非常友好,显存占用不高。训练完成后把网络、采样率和类别列表一并保存,后续做预测时直接load。
3.4 训练中的失败信号与排查顺序
训练过程出现以下几类信号,按顺序排查:
- loss变成NaN,先看训练数据里有没有NaN或Inf,其次把InitialLearnRate降到0.0005。
- 验证准确率一直不动,检查XTest的cell方向是否一致,以及序列长度是否被意外转置。
- 训练准确率高、验证低,优先怀疑相邻窗口泄漏,把切片重叠率降到25%以下再验证。
- GPU内存不足时,把MiniBatchSize降到16,不要优先削减序列长度,序列太短会让双向信息不完整。
4. 五类必调参数:稳定性和精度怎么平衡
4.1 参数推荐区间
BiLSTM的训练对参数组合比CNN更敏感,原因在于梯度要沿时间维双向传播,学习率过大时反向支路的梯度很容易积累并爆炸。下面是一组经过多个轴承数据集验证的起点参数:
| 参数 | 推荐起点 | 调整方向 |
|---|---|---|
| InitialLearnRate | 0.001(Adam) | 验证loss震荡时降为0.0005 |
| MiniBatchSize | 32 | 显存不足或小样本降到16 |
| MaxEpochs | 30 | 曲线未收敛再加到50,配合早停 |
| SequenceLength | 1024 | 按冲击周期调整,至少2个周期 |
| L2Regularization | 1e-4 | 过拟合时增到1e-2 |
4.2 学习率、批大小和收敛行为
学习率0.001搭配Adam是稳妥起手式。若训练集loss下降快但验证集抖动大,先不调网络结构,把学习率降到0.0005并配合更大的MiniBatchSize,减小参数更新方差。MiniBatchSize对BiLSTM的影响比CNN更直接:每个batch里的序列长度不一致时,Matlab会对齐到batch内最长样本,填充0的比例随batch加大而上升,反向支路会读到大量填充位置,所以小batch反而训练更稳定。
MaxEpochs不是越大越好。BiLSTM在故障诊断数据上通常20到40轮就接近饱和,继续训练只会让验证集波动更大。我一般用ValidationData配合ValidationFrequency检查,而不是盲目拉长epoch。
4.3 序列截断与0填充的坑
Matlab对变长序列自动填充0,填充0对BiLSTM的影响远大于单LSTM。反向支路从序列末尾开始读取,填充的0会被当作未来信息吸收进隐藏状态,且'last'输出模式最终读取的位置正好是正序末尾,如果该位置落在填充区,输出向量就掺杂了大量无意义信息。
工程上最省事的方法是统一窗口长度。先统计所有样本的长度分布,把低于中位数长度80%的样本丢弃,超过中位数长度120%的直接截断到中位数。比手动padding再调整Mask省心得多。若原始数据本身就是等长的分段记录,则不存在这个问题。
4.4 小样本下的正则化组合
故障诊断项目经常遇到某一类故障样本只有几十条的情况。此时把dropout、L2正则化和梯度裁剪组合使用:
options = trainingOptions('adam', ... 'InitialLearnRate', 0.0005, ... 'MiniBatchSize', 16, ... 'MaxEpochs', 40, ... 'L2Regularization', 5e-3, ... 'GradientThreshold', 2, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 10, ... 'OutputNetwork', 'best-validation');GradientThreshold=2是BiLSTM训练里的保命参数,梯度范数超过2时直接裁剪,防止双向梯度累加导致的loss突跳。OutputNetwork设为best-validation,会保留验证集最优的权重,避免最后一轮过拟合权重覆盖更优结果。dropout层已在网络骨架中固定为0.2,L2正则设为5e-3,两者同时生效时不要把L2加得过大,否则网络退化到欠拟合,训练集准确率都上不去。
5. 用混淆矩阵和t-SNE验证BiLSTM分类预测的可靠性
5.1 confusionchart看错分方向
准确率只能回答“对多少”,回答不了“错在哪类”。Matlab里用confusionchart一步得到完整混淆矩阵:
YPred = classify(net, XTest); figure; cm = confusionchart(YTest, YPred); cm.RowSummary = 'row-normalized'; cm.ColumnSummary = 'column-normalized';行代表真实类别,列代表预测类别。RowSummary显示每行归一化比例,对应召回率;ColumnSummary对应查准率。内圈故障和外圈故障在传统特征里经常混淆,如果看混淆矩阵发现这两类互相错分,说明BiLSTM捕捉到的是相似的调制模式,此时可以增大窗口长度,让正反向支路看到更多冲击周期,而不是盲目加隐藏单元。
5.2 t-SNE看特征是否真正可分
混淆矩阵只能验证最终输出,还想看BiLSTM提取的特征空间是否可分,可在drop层引出中间特征做t-SNE降维:
feats = activations(net, XTest, 'drop', 'OutputAs', 'rows'); proj = tsne(feats, 'Perplexity', 30); figure; gscatter(proj(:,1), proj(:,2), YTest);t-SNE投影中同一故障类别的点聚成一团、不同类别之间有明显间隙,说明双向特征已经分离;若类别交叠严重但准确率尚可,说明全连接层强行压出了决策边界,换到新数据容易退化。Perplexity=30适用于中等样本量,样本总量小于200时降到15。特征提取层选'drop'而不是'fc',因为fc层已经做了线性加权,类别信息被压缩成接近one-hot的形态,t-SNE看不出原始特征质量。
5.3 多轮训练的多数投票
小样本下BiLSTM对随机种子敏感,单次训练的准确率可能波动3到5个百分点。需要可靠分类预测时,保存5份不同随机种子的模型,预测阶段做softmax分数累加后取最大值对应类别:
votes = zeros(numel(YTest), numClasses); for i = 1:5 rng(i); nets{i} = trainNetwork(XTrain, YTrain, layers, options); votes = votes + predict(nets{i}, XTest); end [~, idx] = max(votes, [], 2); YPredEnsemble = categories(idx);多数投票的集成效果比单模型稳定得多,且实现成本低。5次训练之间的分类准确率如果差异超过8个百分点,说明数据或参数仍有问题,先回去查窗口长度和样本重叠率,不要继续堆模型数量。工程上我更愿意保留这5份不同随机种子的模型,在预测阶段用votes矩阵完成一次在线集成投票,比单模型调用更稳。
本文还有配套的精品资源,点击获取