拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CNN-LSTM-Attention轴承故障诊断Matlab实现与源码解析

简介这是一套面向机械设备故障诊断研究者的Matlab完整源码包采用CNN-LSTM-Attention混合模型对滚动轴承故障进行分类识别。基于西储大学轴承数据集输入数据已经过预处理可直接运行main.m完成模型训练与评估并借助配套绘图脚本输出混淆矩阵等结果适合正在学习深度学习故障诊断方法或需要快速搭建复现实验的工程师、研究生使用。资源包共包含3个文件其中2个为.m源码文件主程序和结果可视化脚本1个为.mat格式的预处理数据文件整体压缩包大小仅84KB结构简洁便于快速下载与本地验证。目前已有47人学习下载特别适合关注CNN、LSTM及注意力机制在时序信号分类中应用的读者。代码由经验丰富的Matlab算法开发者整理对数据读取、网络搭建、训练与可视化等流程做了清晰串联拿到后既能直接运行体验效果也可对照源码理解混合模型的设计思路进一步迁移到其他轴承或设备故障诊断场景中。1. 轴承故障诊断里的CNN-LSTM-Attention拿到这份Matlab源码先看什么做滚动轴承故障诊断时振动信号里真正有用的信息往往只占很小一段CNN能抓局部冲击形状LSTM能记住冲击会不会周期性重复Attention则告诉模型该重点看哪几个时间片段。这个基于Matlab整理的CNN-LSTM-Attention故障诊断项目压缩包里带了完整源码加data.mat数据用的是西储大学公开轴承数据集数据已经按故障诊断格式处理好main.m把训练、测试、出图串完适合正在做课程设计、毕业设计或者横向项目初版baseline的人。项目由机器学习之心整理作者对时序和故障诊断的Matlab实现经验比较丰富。拿到压缩包先别急着运行第一件事是打开data.mat确认数据维度和标签分布如果样本组织方式没看懂后面调注意力层权重时很容易被维度报错耽误一下午。2. 西储大学数据预处理data.mat怎么切出能训练的一维样本2.1 data.mat里的变量结构拿到手先分清四个变量解压后的几个文件里最核心的就是data.mat。用Matlab加载后先执行whos看作者在数据里存了哪些变量clear; close all; clc; load(data.mat); whos % 常见结构不同版本命名略有差异 % XTrain: 1×N 的 cell 数组每个 cell 是 1×1024 的振动信号行向量 % YTrain: N×1 的 categorical 数组记录每一段信号对应的故障类别 % XTest : 1×M 的 cell 数组测试样本 % YTest : M×1 的 categorical 数组这段代码本身不涉及计算重点是识别数据组织方式。有两个点必须注意一是XTrain如果是cell而不是普通矩阵说明作者已经按序列格式切好了样本这是为了配合后续的sequenceInputLayer二是YTrain的categories顺序是不是固定的因为后面混淆矩阵的横纵坐标都按这个顺序排。如果手上的data.mat只有一个allData和一个allLabels说明数据还没切好直接用下一小节的滑动窗口函数自己处理也可以。2.2 滑动窗口切片西储数据要滑多久才是有效样本原始西储大学数据集给的是几十万点的连续信号直接丢进网络既慢又学不到故障特征因为信号里正常段占比太高。工业上处理振动数据常用滑动窗口切片每段取1024个点窗口之间可以重叠。重叠率越高样本数越多但训练集和测试集之间的相似性也会增加评估结果会偏乐观这一点在写论文时容易被审稿人追问。function [X, Y] slidingWindow(signal, label, winLen, stride) % signal : 1×L 的振动信号行向量 % label : 当前这段信号对应的故障类别categorical 或 string % winLen : 窗口长度取 1024对应约 0.085 秒的 12kHz 采样数据 % stride : 滑动步长步长winLen 时无重叠步长winLen/2 时重叠 50% L length(signal); numWindows floor((L - winLen) / stride) 1; X cell(numWindows, 1); Y repmat(label, numWindows, 1); for i 1:numWindows idx (i - 1) * stride 1; X{i} signal(idx : idx winLen - 1); end end这个函数的逻辑很直接先根据信号长度、窗口长度、步长算出能切出多少个窗口然后每滑一次截取一段winLen长度的信号放进cell。公式里用floor向下取整尾部不足一个窗口的数据直接丢弃这段数据量很小对故障特征分布影响可忽略。窗口长度1024的选取有两个理由从频率分辨率看1024个点做FFT能分辨到约11.7Hz足够区分滚动轴承内圈、外圈、滚动体的常见故障特征频率从网络结构看1024正好是2的整数次幂后续每经过一次池化长度减半计算友好。步长如果设置到512样本量翻倍但相邻样本高度重复模型很快就会过拟合所以不是越小越好。2.3 训练集、验证集、测试集划分与标准化顺序数据切完后不能直接训练要做三层划分。测试集必须从头到尾独立验证集用来做early stopping观察训练集只负责更新权重。比例用8:1:1比较常见关键是标准化必须先基于训练集求均值和标准差再把同一组统计量套到验证集和测试集上否则相当于让模型提前看到了测试集的分布信息属于数据泄漏。% 先从XTrain中随机抽10%做验证剩下90%做训练 rng(42); idx randperm(numel(XTrain)); numVal round(0.1 * numel(XTrain)); valIdx idx(1:numVal); trainIdx idx(numVal1:end); XValidation XTrain(valIdx); YValidation YTrain(valIdx); XTrain XTrain(trainIdx); YTrain YTrain(trainIdx); % 基于训练集全体样本计算均值方差这里用vertcat拼接成矩阵 allTrain vertcat(XTrain{:}); mu mean(allTrain, all); sigma std(allTrain, 0, all); % 训练、验证、测试都用同一组mu/sigma for i 1:numel(XTrain) XTrain{i} (XTrain{i} - mu) / sigma; end for i 1:numel(XValidation) XValidation{i} (XValidation{i} - mu) / sigma; end for i 1:numel(XTest) XTest{i} (XTest{i} - mu) / sigma; end这段代码有四个关键点。rng(42)固定随机种子保证每次运行划分完全一致便于复现实验randperm做无放回随机采样避免随机抽样的过程破坏样本独立性mean(allTrain,all)对训练集所有样本拼成的长向量求全局均值而不是对单个样本分别处理标准化后的数值范围大致落在[-3,3]之间卷积核的初始化对量级更敏感LSTM的tanh和sigmoid门也不会因为输入幅值过大而饱和。如果在测试集上效果明显差于训练集优先检查标准化时是不是把训练集和测试集混在一起计算了mu和sigma。3. Matlab里搭建CNN-LSTM-Attention各层维度与注意力实现细节3.1 为什么一维卷积适合振动信号二维卷积在这里是浪费故障诊断里有人先把信号转成时频谱再用二维CNN效果也不错但代价是频谱图分辨率需要人工调推理阶段还要额外做一次短时傅里叶变换。这个项目直接用一维卷积在原始振动信号上提取冲击形状。一维卷积核本质是一组可学习的带通滤波器卷积核长度16意味着覆盖16个采样点对应约1.3ms的时间窗刚好能框住轴承局部损伤引发的短时冲击。多个卷积核并行相当于同时用多组不同频带特性的滤波器对信号扫描。这张表是这个项目最常用的网络结构配置不算臃肿能跑通也够做对比实验层Matlab层函数参数设置输出维度说明输入层sequenceInputLayer每个时间步1个通道1×1024×1第一层卷积convolution1dLayer16个核核长16Padding same16×1024×1归一化batchNormalizationLayer无16×1024×1激活reluLayer无16×1024×1池化maxPooling1dLayer池化核2步长216×512×1第二层卷积convolution1dLayer32个核核长8Padding same32×512×1归一化激活batchNormalizationLayer reluLayer无32×512×1LSTMlstmLayer隐层128输出所有时间步128×512×1注意力attentionLayer自定义打分函数见3.2128×1×1全连接fullyConnectedLayer节点数等于类别数10×1输出softmaxLayer classificationLayer无10×1这个结构的意图是把1024个原始采样点逐步压缩第一层池化后时间步从1024降到512每个时间步携带16个特征通道LSTM再把32个通道的局部特征聚合成128维隐状态序列。Attention层不改变隐状态维度只把512个时间步压缩成1个向量。这一步替代了常见的“取LSTM最后时间步”因为最后一个时间步只代表序列末尾状态而故障冲击可能出现在序列中段直接取尾部就丢掉了最关键的信息。3.2 Attention核心逻辑打分、归一化、加权求和注意力层在这个项目里是自定义的加法注意力也叫Bahdanau注意力。思路是给LSTM每个时间步的输出打一个分数再用softmax把分数转成权重最后对所有时间步做加权平均。打分不是直接算一个标量而是先通过tanh降维层提炼隐状态特征再用线性层压缩到1维这样模型能学到“什么样的隐状态值得重点关注”。% attentionLayer.m 的内部核心计算简化自完整类定义 % H : [128, seqLen, batch]LSTM 输出seqLen 是时间步数 % W1 : [64, 128]打分第一组权重 % W2 : [1, 64]打分第二组权重 % 以下代码假设 W1 和 W2 已经作为层属性完成初始化 score tanh(W1 * H); % [64, seqLen, batch] logits W2 * score; % [1, seqLen, batch] e exp(logits - max(logits, [], 2)); % 减去最大值防止exp溢出 alpha e ./ sum(e, 2); % [1, seqLen, batch]权重和为1 Z sum(H .* alpha, 2); % [128, 1, batch]这段代码的维度流转是理解整层的关键。H乘以W1后每个时间步的128维隐状态被映射到64维相当于提取“可被打分”的中间表示W2再把64维压成1维得到每个时间步的原始分数。exp前减去max是为了数值稳定因为当logits超过700时exp结果会变成inf这是Matlab里很常见的坑。最后H和alpha逐元素相乘再沿时间步维度累加等价于每个时间步的隐状态乘以自己的注意力权重权重越大的时间步对输出Z的贡献越大。训练过程中W1和W2作为可学习参数自动更新源码里把这套逻辑封装进自定义层所以trainNetwork能正常对它们求梯度。3.3 组装完整网络sequenceInputLayer的位置决定维度怎么走Matlab里组装网络有两种方式一种是直接用layers数组堆叠适合线性结构另一种是用layerGraph适合有并行分支或跨层连接的结构。这个项目是线性结构直接堆叠即可但有两个容易报维度错误的地方。第一LSTM必须设OutputMode为sequence否则只输出最后一个隐状态注意力层拿不到完整时间步第二自定义注意力层的输出必须明确是单个时间步否则后续fullyConnectedLayer会认为输入仍是序列数据。inputSize 1024; numClasses 10; hiddenSize 128; layers [ sequenceInputLayer(1, Name, input) convolution1dLayer(16, 16, Padding, same, Name, conv1) batchNormalizationLayer(Name, bn1) reluLayer(Name, relu1) maxPooling1dLayer(2, Stride, 2, Name, pool1) convolution1dLayer(8, 32, Padding, same, Name, conv2) batchNormalizationLayer(Name, bn2) reluLayer(Name, relu2) lstmLayer(hiddenSize, OutputMode, sequence, Name, lstm) attentionLayer(Name, attention) fullyConnectedLayer(numClasses, Name, fc) softmaxLayer(Name, softmax) classificationLayer(Name, output) ]; % 打印网络结构确认各层激活维度 analyzeNetwork(layers)这里每一层都显式写了Name原因有两个一是自定义attentionLayer连接时需要知道前后层的名字方便在layerGraph里addLayers和connectLayers时定位二是analyzeNetwork报错时能直接定位到具体层。sequenceInputLayer的通道数设成1表示每个时间步是一个标量1×1024的振动样本会被看成1024个时间步、每步1个特征。第一层卷积核长16、核数16对应3.1节的设计意图maxPooling1dLayer把时间步从1024降到512能有效缩小LSTM内部矩阵运算量。运行analyzeNetwork后如果看到lstm层输出维度是[128, 512, batchSize]说明维度正确如果显示[128, 1, batchSize]说明OutputMode误设成了last改成sequence再跑。4. 跑通main.m训练选项、混淆矩阵与评估指标4.1 训练选项里最影响效果的四个参数main.m里训练部分用的是adam优化器这几乎是序列模型的标准选择。adam对学习率不敏感即使初始学习率没设好也能在几十个epoch内收敛到可用范围和SGD相比adam结合了梯度的一阶矩和二阶矩估计在振动信号这种非平稳输入上表现更稳。options trainingOptions(adam, ... MiniBatchSize, 64, ... MaxEpochs, 30, ... InitialLearnRate, 1e-3, ... LearnRateSchedule, piecewise, ... LearnRateDropPeriod, 10, ... LearnRateDropFactor, 0.5, ... ValidationData, {XValidation, YValidation}, ... ValidationFrequency, 20, ... Shuffle, every-epoch, ... Plots, training-progress, ... Verbose, false);MiniBatchSize默认是128但振动样本长度1024加LSTM反向传播显存占用明显偏高设为64能降低显存压力同时梯度更新更频繁收敛曲线更平滑。MaxEpochs设30对西储这类规模的数据集一般够用如果训练损失还在下降就继续加大。InitialLearnRate从0.001起步是adam的常见起点过大会在头几个batch出现loss变成NaN过小则收敛到高准确率会拖到50轮以后。LearnRateDropPeriod和LearnRateDropFactor组合起来表示每10轮学习率乘0.5训练后期用小步长精细调整权重这对注意力层里的W1和W2尤其重要因为它们初始值比较小学习率太大容易震荡。4.2 zjyanseplotConfMat.m自定义配色混淆矩阵的绘图逻辑混淆矩阵是故障诊断报告里必须出现的图Matlab内置的confusionchart交互性虽好但导出图片的配色偏浅放在论文里对比度不够。这个文件名拆开看是“自定义颜色-plot-ConfMat”博主用text逐格写数值配合自定义颜色映射比默认图表更有辨识度。核心逻辑是先用confusionmat得到矩阵再用imagesc绘图最后双重循环在每个格子中心写数字。function zjyanseplotConfMat(confMat, classNames) % confMat : confusionmat 输出的方阵第i行第j列表示真实类别i被预测成类别j的样本数 % classNames: 类别名称顺序必须和YTrain中categories顺序一致 numClass size(confMat, 1); figure(Color, w); imagesc(confMat); colormap(parula); % parula自带深蓝到黄的渐变适合数值对比 for i 1:numClass for j 1:numClass if confMat(i, j) max(confMat(:)) / 2 textColor w; else textColor k; end text(j, i, num2str(confMat(i, j)), ... HorizontalAlignment, center, ... FontSize, 12, Color, textColor); end end set(gca, XTick, 1:numClass, YTick, 1:numClass, ... XTickLabel, classNames, YTickLabel, classNames, ... TickLabelInterpreter, none); xlabel(预测类别); ylabel(真实类别); axis square; endtextColor的判断是个小技巧如果当前格子的数值超过整个矩阵最大值的一半说明它大概率是主对角线上的高频格子深色背景下用白色数字更容易看清低频错分格子背景颜色浅用黑色更合适。colormap选parula而不是默认jet因为jet的绿色和青色区域在灰度打印时难以区分parula的亮度变化更平滑转成灰度图后依然能看出数值梯度。如果类别名是中文需要额外设置字体否则坐标轴标签在Windows下会显示成方框。4.3 从混淆矩阵计算精确率、召回率和F1混淆矩阵只能定性看哪些类别互相混淆要定量对比不同网络结构精确率和F1比准确率更有说服力。准确率在类别不平衡时会被多数类主导而轴承故障诊断里内圈故障和滚动体故障的样本数量往往不是严格均衡。YPred classify(net, XTest, MiniBatchSize, 64); confMat confusionmat(YTest, YPred); % 类别级指标 precision diag(confMat) ./ sum(confMat, 1); recall diag(confMat) ./ sum(confMat, 2); F1 2 * precision .* recall ./ (precision recall); % 宏观平均 macroF1 mean(F1, omitnan); fprintf(宏观F1 %.4f\n, macroF1); % 每类单独打印 classNames categories(YTest); for i 1:numel(classNames) fprintf(%-8s precision%.3f recall%.3f F1%.3f\n, ... classNames{i}, precision(i), recall(i), F1(i)); endprecision的分母sum(confMat,1)是列和代表模型把所有测试样本预测成第i类的总数recall的分母sum(confMat,2)是第i类真实样本总数。某一类完全没有被预测到时precision的分母变成0结果会算成NaNmean里加omitnan就是为了防止宏观F1因此失效。观察输出时不要只盯宏观F1如果某类F1明显低于其他类先看这个类在测试集里的样本占比再回看注意力可视化通常能从振动波形相似度上找到原因。5. 注意力可视化验证与跨数据集迁移的三个关键点5.1 先排除两个环境坑再谈模型效果Matlab深度学习工具箱对GPU版本约束严格跑之前先执行gpuDevice看看可用显存低于4GB的话MiniBatchSize从64降到32否则LSTM反向传播很容易内存不足。另外convolution1dLayer这个函数名在不同版本里不统一R2021a之前的版本可能找不到这个函数报错时先确认工具箱版本必要时换成旧版命名。5.2 把Attention权重画到原始信号上直接看模型注意到了哪里这个技巧最能说明注意力层是否“在干活”。前提是在attentionLayer.m的属性区加一个LastAttention字段并在predict方法的最后把算好的alpha缓存进去。测试阶段前向传播时直接从这个属性取最近一次的注意力权重。% 取测试集中第一个内圈故障样本 x XTest{find(YTest 内圈故障, 1)}; % 假设注意力层在网络的第10层索引可通过 analyzeNetwork(layers) 确认 attLayer net.Layers(10); alpha attLayer.LastAttention; figure(Color, w); yyaxis left plot(x, b); ylabel(振动幅值); yyaxis right plot(alpha, r, LineWidth, 1.5); ylabel(注意力权重); xlabel(采样点 / 时间步); title(注意力权重与原始振动信号的对应关系);画出图后如果发现注意力权重均匀分布在整段序列上没有明显尖峰说明LSTM输出的时间步之间信息冗余度高注意力层差不多退化成了均值池化。这时可以把LSTM隐层从128加到256或者把打分函数的中间维度从64提到128增加注意力层表达能力。如果权重尖峰出现在冲击段之前说明模型学到了冲击出现的周期性规律这是很可靠的可解释性证据写论文时可以直接截图。5.3 换一个数据集时只改三个地方这套源码迁移到齿轮箱或电机轴承等其他机械数据集时不需要改网络结构只需要处理三处。第一处是数据生成阶段窗口长度的选取要跟着采样率走1024点只适用于12kHz采样如果新数据集采样率是48kHz同样0.085秒需要4096点。第二处是分类数量fullyConnectedLayer的节点数和混淆矩阵的classNames需要同步更新。第三处是InitialLearnRate新数据集信噪比如果更低学习率从0.0005起步更稳妥否则前几个epoch的梯度噪声太大注意力层的W1和W2可能学不到有效特征。改完这三处直接重跑main.m就能得到新数据集上的第一版评估结果后续再根据F1决定要不要调整网络宽度。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门