ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CEEMD+BP神经网络实现轴承故障诊断:从振动信号到工程落地

CEEMD+BP神经网络实现轴承故障诊断:从振动信号到工程落地 简介面向机械设备故障诊断与深度学习应用人群这份PDF文献以滚动轴承为对象提出结合互补集合经验模态分解与反向传播神经网络的诊断方案。分解方法用于原始信号降噪和重构配合峭度指标、峰值指数、重心频率、均方频率等时频域特征作为网络输入反向传播网络通过多层前馈结构实现非线性映射并借助大数据环境下七个维度特征分析提高故障识别可靠性。资源包仅含一个PDF文件大小约253KB内容包含完整的算法原理、网络结构与参数讨论、实验验证及参考文献便于直接阅读或用于课题参考。目前已有一百四十二人学习下载适合机械专业学生、故障诊断研究人员以及想了解信号处理与神经网络结合应用的读者。1. 轴承故障诊断为什么绕不开CEEMD从振动信号里挖出早期故障的这条路做设备预测性维护的工程师应该都有这种经历现场采回来的振动信号频谱图上杂峰一片边带被噪声糊死轴承内圈点蚀的故障特征频率明明算出来了就是不敢在报告里画圈。滚动轴承早期故障信号能量占比极低直接拿原始波形喂分类器BP神经网络很容易被正常振动分量带偏。CEEMD互补集合经验模态分解就是用来解决这个问题的它把非线性、非平稳的振动信号自适应地拆成一组固有模态函数IMF把微弱故障冲击从强背景振动里分离出来再从IMF里提取特征交给BP神经网络学习。后端接上大数据量的样本管理和批量训练就构成了一个完整的轴承故障诊断落地链路。这套方案适合两类人做设备健康管理的一线工程师以及做故障诊断研究方向、需要快速搭出基线模型的学生。它不一定是最花哨的方案但是最容易被复现、能见效果、能在现场顶住压力的方案。2. 先用CEEMD把振动信号拆开对照EMD谈参数与代码实现2.1 为什么是CEEMD模态混叠和EEMD残留噪声这笔账先看基础。经验模态分解EMD的思路是把信号按频率从高到低逐层剥开剥出来的每个IMF要求局部对称、极值点数和过零点数相等或至多差一。但EMD有个老毛病叫模态混叠同一个IMF里同时出现时间尺度差异很大的分量比如故障冲击的高频振荡和转轴转频的低频调制挤在一起分不干净。原因在于信号里偶尔出现的间歇性成分改变了极值点的分布包络拟合跟着出错。EEMD的解决办法是给原始信号加白噪声让噪声填充整个频带把间歇性成分顶开然后多次集成平均。代价是白噪声在平均之后不会完全消失重构后的IMF里残留噪声水平跟集成次数成反比要加到上千次才压得下去计算成本直线上升。而且EEMD每次加的都是独立噪声两次分解的噪声项不会互补结果天然带随机性。CEEMDComplementary EEMD的核心改动就是成对加噪。对每个集成试次先产生一组白噪声序列然后生成两个混合信号原始信号加这组噪声、原始信号减这组噪声分别做EMD。最后把所有IMF按序数分组求平均。因为正负噪声在平均过程里是成对抵消的残留噪声大幅减小集成次数比EEMD少一个量级也不怕。代价是分解次数翻倍但计算时间仍远小于EEMD跑到千次集成。工程实现上CEEMD的输入参数只有两个最要紧噪声幅值比例Nstd和集成次数trials正负对。这两个参数直接决定分解质量。2.2 PyEMD跑CEEMD的最小代码与参数解读Python里做CEEMD最常用的是PyEMD库它把EMD、EEMD、CEEMDAN的接口都封装好了。注意一点PyEMD没有直接提供名为CEEMD的类传统互补CEEMD的常见做法是用EEMD类并手动构造正负噪声对或者直接用CEEMDAN类做替代后者在论文里经常被混称为CEEMD。这里两版都给出第一版是传统CEEMD的完整实现可靠且可控。import numpy as np from PyEMD import EMD def ceemd_decompose(signal, trials200, nstd0.2, max_imf8): 互补集合经验模态分解CEEMD signal: 一维振动信号建议长度 2048 trials: 正负噪声对数越大残留越小 nstd: 噪声标准差占原始信号标准差的比例 max_imf: 最大IMF层数残差也算一层 n len(signal) # 按序号累加IMF imfs_sum {} counts {} for i in range(trials): # 每个试次只生成一组噪声正负两路共用 noise np.random.randn(n) * nstd * np.std(signal) for sign in (1.0, -1.0): mixed signal sign * noise emd EMD() emd.max_imf max_imf # emd返回结果最后一行是残差和IMF一起按层计数 imfs emd.emd(mixed) for j, imf in enumerate(imfs): if j not in imfs_sum: imfs_sum[j] np.zeros(n) counts[j] 0 imfs_sum[j] imf counts[j] 1 # 平均后按层序返回最后一个通常是趋势项 return [imfs_sum[j] / counts[j] for j in sorted(imfs_sum)]这段代码里最值得注意的有三点。第一噪声的标准差按原始信号标准差的比例计算而不是固定绝对值这样对幅值不同的振动信号都能自适应。第二正负号在一层循环里生成保证每一试次的噪声是严格互补的这是CEEMD能抵消残留噪声的根本原因。第三counts按IMF序数分别计数不同试次分解出的IMF层数可能不一致分层平均前必须判断当前试次有没有这一层否则靠索引对齐会把不同频段的分量平均在一起属于隐性翻车点。参数方面的经验值trials取200到500之间低于80时模态混叠压不下去高于500之后改善有限但耗时线性增长nstd取0.1到0.3太小起不到扰动极值点的作用太大噪声本身会变成主导分量max_imf一般设8到10超过10层之后多数是低频趋势项物理意义已经很弱。如果你不想手写这个循环PyEMD的CEEMDAN类一行就能调用它就是Yeh在2010年提出的自适应噪声版本论文里写成CEEMD的情况很常见import numpy as np from PyEMD import CEEMDAN # 加载数据每行为一段振动窗口 data np.load(vibration_windows.npy) # shape: (n_samples, n_points) # 只对第一个窗口演示 signal data[0] ceemdan CEEMDAN(trials300, epsilon0.005) imfs ceemdan(signal) print(f分解出 {len(imfs)} 层IMF信号长度 {len(signal)})CEEMDAN的关键参数是epsilon它控制每层附加的自适应噪声幅值默认0.005通常就够不需要像传统CEEMD那样手动调Nstd。trials同样取300上下。用CEEMDAN的好处是收敛更快、需要调的参数更少缺点是你的论文里如果写CEEMD而代码用的是CEEMDAN审稿人可能会较真。2.3 从一堆IMF里挑出敏感分量相关系数与频段核对分解出来的IMF不是每一层都值得做特征。轴承故障冲击主要集中在中高频带而转频和倍频集中在低频带两者在IMF层间是有分界的。直接拿全部IMF做特征会把转速波动这类与故障无关的信息带进分类器增加过拟合风险。常见做法是两层过滤。第一层用相关系数筛选对每个IMF算它与原始振动信号的皮尔逊相关系数经验阈值是0.3低于这个值的IMF认为与原始信号相关性弱大概率是噪声主导层。第二层看频谱对筛选出的IMF做FFT核对包络谱里有没有目标故障特征频率及其倍频这一步是物理校验比纯统计阈值可靠。import numpy as np def select_sensitive_imfs(signal, imfs, corr_threshold0.3): 按相关系数筛IMF返回保留的IMF列表 selected [] for imf in imfs: corr np.corrcoef(imf, signal)[0, 1] if abs(corr) corr_threshold: selected.append(imf) # 同时打印相关系数方便人工复核 print(各IMF与原信号的相关系数:, [round(abs(np.corrcoef(imf, signal)[0, 1]), 3) for imf in imfs]) return selected # 重构敏感分量把保留的IMF按序号相加 selected_imfs select_sensitive_imfs(signal, imfs, 0.3) reconstructed np.sum(selected_imfs, axis0)这一步的输出有两个用途。如果需要做可视化诊断把重构信号拿去画包络谱故障特征频率会更干净如果需要喂BP神经网络重构信号比单个IMF稳定特征值的方差更小。我一般建议先重构再提特征因为单个IMF的幅值会受噪声干扰而多个敏感IMF叠加之后真实振动分量的比重被强化了。corr_threshold不建议卡得太高。轴承早期微弱故障对应的IMF层和相关度本来就低卡到0.5会把有效信息丢掉卡到0.2又会把噪声层放进来。我用0.3居多然后人工看两到三个样本的包络谱确认。3. 把IMF加工成BP能消化的特征时频熵组合与大数据落盘3.1 特征清单时域、频域、熵特征各取什么CEEMD解决的是信号怎么拆的问题BP解决的是特征怎么分类的问题中间还差一步把重构信号变成一组数值特征。轴承故障诊断里特征讲究组合单靠峭度或均方根很容易在某种工况下失效。时域特征里最常用的是峰值、均方根值、峭度、峰值因子。峰值反映冲击强度均方根反映振动能量峭度对早期点蚀和剥落这类瞬态冲击非常敏感峰值因子是峰值与均方根的比值用来区分冲击型故障和磨损型故障。这组特征的计算成本低适合处理大数据量。频域特征里取重心频率和均方频率。重心频率反映信号频谱能量的集中位置轴承故障后高频分量增加重心频率会向高频偏移。它比单个频率幅值稳定受转速波动影响小。熵特征在早期故障诊断里格外有用。排列熵计算量小对信号突变敏感适合捕捉周期信号里的随机冲击样本熵衡量时间序列的自相似程度故障状态下信号的规律性被破坏样本熵会明显变化。这两类熵特征配合时域特征能覆盖能量变了、冲击多了、规律乱了三个维度。最终的典型特征列表示例如下类别特征名物理含义故障敏感性时域峰值最大冲击幅值点蚀、剥落时域均方根振动能量磨损、松动时域峭度冲击概率密度陡峭程度早期微弱故障时域峰值因子冲击与能量之比区分冲击/磨损频域重心频率频谱能量集中位置高频分量增加频域均方频率频谱二阶矩频带展宽熵排列熵信号规律性随机冲击熵样本熵自相似程度故障不稳定性3.2 特征计算代码从重构信号到特征矩阵实际的工程做法是滑窗处理。一段连续振动信号按固定窗口长度切片每个窗口做一次CEEMD分解和特征计算一个窗口就是一个训练样本。窗口长度推荐2048或4096个采样点太短时频分辨率不足分解出的IMF不稳定太长时一个窗口里可能混入多个工况特征平均化后故障特征被稀释。import numpy as np from entropy import permutation_entropy, sample_entropy def extract_features_from_signal(signal): 从单段信号建议已重构计算一组特征 返回一维numpy数组 rms np.sqrt(np.mean(signal ** 2)) peak np.max(np.abs(signal)) kurtosis np.mean((signal - np.mean(signal)) ** 4) / (np.std(signal) ** 4 1e-12) crest_factor peak / (rms 1e-12) # 频域特征 spectrum np.fft.rfft(signal) power np.abs(spectrum) ** 2 freqs np.fft.rfftfreq(len(signal), d1.0) # 这里d按采样周期实际值给 if np.sum(power) 1e-12: spectral_centroid np.sum(freqs * power) / np.sum(power) else: spectral_centroid 0.0 # 熵特征 pe permutation_entropy(signal, order3, delay1) se sample_entropy(signal, order2, metricchebyshev) return np.array([peak, rms, kurtosis, crest_factor, spectral_centroid, pe, se]) def build_feature_matrix(windows, labels): windows: (n_samples, n_points) 振动窗口数组 labels: (n_samples,) 故障类别标签 feature_list [] for win in windows: # 实际工程中先把窗口做CEEMD再重构这里直接以输入信号为准 feature_list.append(extract_features_from_signal(win)) X np.vstack(feature_list) y np.asarray(labels) return X, y代码里需要注意几处细节。峭度计算里的1e-12是为了防止纯直流信号除以零加了之后对正常信号的峭度值影响可以忽略。频谱的d参数必须改成实际采样周期的倒数否则重心频率算出来的单位是错的。排列熵的order取3比较稳妥order大了计算量指数增长且对噪声更敏感样本熵的order取2metric用切比雪夫距离这是最常见配置。这段代码产出的X矩阵就是后面BP神经网络的输入。每一列是一个特征一行是一个窗口样本。隐私一点说特征矩阵的质量直接决定模型上限CEEMD分解再漂亮特征选错了也是白搭。3.3 样本量大时的落盘与预处理Parquet与分布式思路轴承故障诊断挂上大数据三个字通常意味着样本量从几万窗口起步上探到几百万窗口。特征矩阵的shape可能是2000000, 50这个规模用CSV存会非常难受写入慢、读取慢、占空间。行业里普遍转向Parquet列式存储。Parquet把每一列连续存放做特征筛选时只需读需要的列内存占用和IO开销都小一个量级。import pandas as pd df pd.DataFrame(X, columns[ peak, rms, kurtosis, crest_factor, spectral_centroid, permutation_entropy, sample_entropy ]) df[label] y # 按标签分区训练时只读需要的类别 df.to_parquet(bearing_features.parquet, partition_cols[label])特征计算本身是耗时的因为每个窗口都要跑CEEMD。常见做法是把窗口数组切块后并行处理单机用multiprocessing窗口量到百万级时上Dask或者Spark把振动窗口作为分布式数据集的partition每个worker上独立跑CEEMD和特征提取。这样做的收益是线性的CEEMD的各个窗口分解之间完全独立没有任何共享状态是天然的并行任务。大数据集群部署策略上考虑的就是每个worker的内存要能装得下分给它的窗口数和特征矩阵分块节点数按总窗口数除以单节点每秒处理窗口数估算。3.4 训练集划分与标准化防止特征泄漏的第一步特征泄漏是这类项目最隐蔽的坑。标准化的正确顺序和划分逻辑单拎出来值得专门说。from sklearn.model_selection import GroupShuffleSplit from sklearn.preprocessing import StandardScaler # groups标记每个窗口所属的原始样本/测点同一测点的窗口必须放在同一侧 groups window_source_ids splitter GroupShuffleSplit(n_splits1, test_size0.3, random_state42) train_idx, test_idx next(splitter.split(X, y, groups)) scaler StandardScaler() X_train_scaled scaler.fit_transform(X[train_idx]) X_test_scaled scaler.transform(X[test_idx])GroupShuffleSplit保证同一个测点的所有窗口不跨训练集和测试集否则模型实际上已经见过同一段信号的邻近窗口特征泄漏会让测试准确率虚高四到六个点。StandardScaler只能fit训练集测试集用同一组均值和方差做transform这是铁律。如果测试集单独fit分布信息就从测试集流进了模型评估过程。标准化这一步在BP之前几乎是必须的。BP神经网络对输入特征的尺度敏感峰值可能到几十排列熵只有0到1之间量级差距大会让梯度更新被大幅值特征主导隐层神经元的激活值过早饱和。特征泄漏不是危言耸听后面避坑章节里我会给一个我在现场踩中的真实翻车场景。4. 用BP神经网络做故障分类结构设计与训练参数4.1 网络结构怎么定输入维度、隐层节点与输出编码BP神经网络在轴承故障诊断里的角色是把前面提取的特征向量映射到故障类别。输入维度就是特征个数假设特征矩阵有50列输入层就是50个节点。输出维度由故障类别数决定正常、内圈故障、外圈故障、滚动体故障四类输出层就是4个节点用one-hot编码比如正常对应[1,0,0,0]。隐层节点数是BP结构设计里最玄学的部分。经验公式有两个流派一个是sqrt(m*n)m是输入维度n是输出维度另一个是(mn)/2然后再上下浮动试探。50个输入、4个输出时前者大约是14后者是27区间很宽。我的做法是先按(mn)/2取整然后跑一遍对比训练集和验证集的准确率差如果训练集高验证集低就减小隐层节点数加正则如果两边都低就增大节点数。隐层设两层比一层在特征非线性组合上更灵活但层数多了小样本容易过拟合。两隐层结构64、32在多数轴承数据集上是靠谱的起点。激活函数的选择上隐层用ReLU是主流计算快且缓解梯度消失输出层用softmax给每个类别输出一个概率。训练目标是最小化交叉熵损失本质上是最大似然估计和用MSE做分类回归相比收敛更稳。4.2 用PyTorch搭一个可复现的BP网络这里给出一个可以直接复制的PyTorch实现。PyTorch相比sklearn的MLPClassifier好处是能自己控制结构细节、断点续训、上GPU坏处是代码量多一点。考虑到工程落地的需要我建议直接上PyTorch。import torch import torch.nn as nn import torch.optim as optim class BearingBP(nn.Module): 两隐层BP网络用于轴承故障特征分类 def __init__(self, input_dim, hidden_dim64, num_classes4): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_dim, hidden_dim // 2), nn.ReLU(), nn.Linear(hidden_dim // 2, num_classes) ) def forward(self, x): return self.net(x) def train_bp_model(X_train, y_train, X_val, y_val, input_dim, num_classes4, lr1e-3, batch_size128, epochs200, patience10, random_seed42): # 固定随机种子保证可复现 torch.manual_seed(random_seed) np.random.seed(random_seed) model BearingBP(input_dim, hidden_dim64, num_classesnum_classes) # 类别权重在第五章讲这里先不传 loss_fn nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lrlr) scheduler optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, patience5, factor0.5 ) # 转成Tensor数据集 train_ds torch.utils.data.TensorDataset( torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.long) ) val_ds torch.utils.data.TensorDataset( torch.tensor(X_val, dtypetorch.float32), torch.tensor(y_val, dtypetorch.long) ) train_loader torch.utils.data.DataLoader( train_ds, batch_sizebatch_size, shuffleTrue ) val_loader torch.utils.data.DataLoader( val_ds, batch_sizebatch_size, shuffleFalse ) best_val_acc 0.0 bad_epochs 0 for epoch in range(epochs): model.train() train_loss 0.0 for xb, yb in train_loader: optimizer.zero_grad() logits model(xb) loss loss_fn(logits, yb) loss.backward() optimizer.step() train_loss loss.item() * xb.size(0) # 验证 model.eval() correct 0 total 0 val_loss 0.0 with torch.no_grad(): for xb, yb in val_loader: logits model(xb) loss loss_fn(logits, yb) val_loss loss.item() * xb.size(0) preds logits.argmax(dim1) correct (preds yb).sum().item() total yb.size(0) val_acc correct / total scheduler.step(val_loss) if val_acc best_val_acc: best_val_acc val_acc bad_epochs 0 torch.save(model.state_dict(), best_bearing_model.pt) else: bad_epochs 1 if bad_epochs patience: print(f早停于 epoch {epoch1}最佳验证准确率 {best_val_acc:.4f}) break model.load_state_dict(torch.load(best_bearing_model.pt)) return model这段代码的几个设计点值得展开说明。Dropout(0.3)放在第一隐层之后作用是随机丢弃30%的神经元输出迫使网络不依赖单一特征路径这是对抗高维特征过拟合最直接的手段。ReduceLROnPlateau在验证损失连续5个epoch不下降时把学习率减半避免后期loss在一个平台期反复震荡。patience10是早停的容忍度验证准确率连续10个epoch不破纪录就停止训练保存历史最佳模型防止最后的过拟合权重覆盖最好的结果。batch_size128适合中等规模数据。窗口数在几万级别时128是收敛速度和梯度稳定性的平衡点窗口数到百万级时调到512或1024用更大batch换训练吞吐量。学习率lr1e-3是Adam的默认推荐值如果发现训练loss下降特别慢或者震荡剧烈先把lr降到3e-4再跑一轮。4.3 训练配置参数组合、epoch与评估指标训练配置里真正影响现场效果的是三类参数样本量、特征维度、类别分布。样本量少几千窗口时隐层节点要往下调Dropout提到0.5epoch上限收缩到100因为小样本下模型很快就能拟合训练集跑太多轮纯属给过拟合送机会。特征维度高上百个特征时优先考虑在输入层后面加一个BatchNorm1d稳定分布或者做特征选择把维度压到50以内。评估指标不能只看准确率。轴承故障诊断的典型场景是故障类别分布严重不均正常样本占70%四种故障各占不到10%。这时准确率会被多数类主导模型把少数类全猜错也能有70%以上的准确率。工程上我以macro-F1为准也就是每个类别单独算F1再取平均。配合混淆矩阵看具体错在哪一类上——是内圈故障被误判成外圈还是早期故障被误判成正常。from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import matplotlib.pyplot as plt # model 为训练好的BP模型 model.eval() with torch.no_grad(): logits model(torch.tensor(X_test_scaled, dtypetorch.float32)) y_pred logits.argmax(dim1).numpy() print(classification_report(y_test, y_pred, digits3)) cm confusion_matrix(y_test, y_pred) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.show()如果classification_report里某个故障类别的recall明显低于其他类优先从数据层面补样本而不是调模型结构。这个动作背后的逻辑很简单BP神经网络的分类边界是靠数据撑起来的类别样本数差10倍以上时任何结构上的微调都盖不住样本分布的问题。5. 轴承故障诊断踩坑记录模态混叠到特征泄漏的五条血泪经验5.1 模态混叠残留分解结果里藏着另一个频段的能量现象CEEMD分解完IMF1的频谱里同时出现高频冲击带和低频转频成分IMF2里也残有同样的低频峰值各层之间频率成分没有干净分开。用重构信号做的包络谱上故障特征频率的幅值被稀释甚至淹没在边带里。原因噪声幅值Nstd设得太小集成试次太少。Nstd只有0.05时白噪声对信号极值点分布的扰动不足以重新分布间歇性成分模态混叠没有被抑制。trials小于100时正负噪声对消不完全残留噪声在平均IMF里形成新的伪分量。解决把Nstd调到0.2到0.3倍信号标准差trials调到300以上。调完参数后不要直接看分解结果画一遍各IMF的频谱确认相邻IMF的中心频率没有重叠。这一步值得花十几分钟做后面所有特征都建立在这些IMF上源头上混叠下游全部污染。5.2 端点发散IMF两端翘起来特征计算全被污染现象分解出的IMF前后两端几十个采样点出现大幅摆动幅度明显大于信号中段像把一条直线折弯了一样。窗口切割时症状更明显每个窗口的边缘都会冒出不真实的冲击。做峭度特征时两端伪冲击直接抬高了峭度值模型对正常样本也会报警。原因EMD包络拟合在端点处缺少约束三次样条在信号首尾处无法预知外部趋势包络线在端点附近产生过冲。窗口长度越短过冲区占比越大症状越明显。解决最省事的办法是在滑窗时左右各多留128个采样点CEEMD分解完成后丢弃两端各128点只用中间部分计算特征或者用镜像延拓方法把信号首尾各复制一段再分解PyEMD的EMD类支持设置扩展方式用镜像延拓后端点效应明显缓解。我在实际项目里两种方案叠加先延拓、再裁剪特征值比单靠一种稳得多。5.3 特征泄漏标准化用错地方测试集准确率虚高现象模型离线测试准确率99%模型换到独立工况或现场的在线数据上准确率只有80%出头。排查特征和模型结构都没发现问题最后回头查数据预处理才找到根子。原因StandardScaler在整个特征矩阵上先做了fit再划分训练测试集或者随机切分时同一个测点的多个窗口同时落在训练集和测试集里。前者的后果是测试集的均值和方差已经参与训练数据的标准化信息提前泄漏后者的后果是模型在训练时见过同一段信号的邻近片段学的是背答案而不是学规律。解决严格按照先划分、后标准化的顺序划分时用GroupShuffleSplit按测点分组。这个坑我在实际项目里踩过离线指标漂亮得让人上头跑到现场第一次做盲测就翻车。从那之后标准化和划分的顺序写进了团队代码评审的checklist。5.4 训练结果每次不一样BP随机性是玄学还是可管理现象同样的训练代码和数据连续跑三次验证准确率波动三到五个百分点F1波动更大。部分epoch后训练loss下降路径差异很大早停的位置也不同。原因BP的权重初始化、mini-batch采样顺序、Dropout的随机丢弃都是随机的不固定种子就不可能复现。这在实验对比里很伤A方案和B方案各差两个点但随机波动本身就是三个点结论根本站不住。解决在训练前固定torch.manual_seed和numpy.random.seed把所有随机源收敛到一个种子。更严谨的做法是取三到五个不同种子分别训练报告准确率的均值和标准差用均值做方案对比。模型比较只信这个均值不要拿单次运行的最好数字说事。5.5 故障类别不均轻微故障精准率只有六成现象正常类和严重故障类的识别准确率都在95%以上早期轻微故障刚出现的点蚀、微磨损的精准率和召回率只有60%上下。混淆矩阵显示早期故障大量被误判成正常少量被错分到其他故障类。原因早期故障样本在数量上和质量上双重吃亏。数量上故障刚出现时样本占比往往不到5%质量上早期故障冲击能量弱特征分布和正常样本高度重叠分类边界本来就不清晰。解决先给损失函数加类别权重CrossEntropyLoss的weight参数按各类别样本数的反比设置让模型对少数类的错判付出更大代价。如果加权后还不够对少数类做SMOTE过采样在特征空间里合成新样本。评估指标改用macro-F1不要让正常类的高准确率掩盖少数类的低识别率。6. 从实验室到现场变工况验证与模型可信度的最后一关6.1 换转速测试训练A工况测试B工况怎么跑很多轴承故障诊断项目在实验室数据集上表现优异一到现场就蔫。原因很简单实验室数据通常是在稳定转速、稳定负载下采的而现场的转速、负载是波动的。转速一变振动能量的频段分布整体平移同一个故障的特征值分布也跟着变。所以在交付模型之前至少要做一次变工况验证——用A转速的数据训练用B转速的数据测试。# 假设data_a、data_b是两种工况下已经做好的特征矩阵 X_train data_a_features y_train data_a_labels X_test data_b_features y_test data_b_labels # 标准化同样只fit训练工况 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) model_bp train_bp_model( X_train_scaled, y_train, X_test_scaled, y_test, input_dimX_train.shape[1], num_classes4, lr1e-3, batch_size128, epochs200, patience10 )跑出来的准确率如果明显低于同工况自测先不要急着调网络结构。优先检查CEEMD分解参数是否针对新转速重新调过——转速升高时故障特征频率整体上移原本筛选IMF时用的相关系数阈值和频段范围可能需要重新验证。其次检查特征里是否有对转速敏感的低频项比如重心频率它在不同转速下天然会漂移必要时把特征按转频归一化而不是用绝对频率值。6.2 IMF贡献度与特征可解释性知道模型在听哪个频段BP神经网络是黑匣子但在故障诊断场景里模型的可信度可以通过两个手段增强。第一个手段是IMF逐层消融只用第i层IMF提取的特征训练模型观察去掉某一层后准确率的变化以此判断模型决策主要依赖哪个频段。第二个手段是t-SNE可视化把测试集特征降维到二维平面按真实标签着色看同一故障类别的样本是否聚成一团。from sklearn.manifold import TSNE import matplotlib.pyplot as plt tsne TSNE(n_components2, perplexity30, random_state42) embeddings tsne.fit_transform(X_test_scaled) plt.figure(figsize(8, 6)) for label in np.unique(y_test): idx y_test label plt.scatter(embeddings[idx, 0], embeddings[idx, 1], s8, labelfclass {label}) plt.legend() plt.show()散点图上如果四个类别的点混成一团说明特征里有效区分信息不足这时候调BP结构是没有用的问题在特征工程环节。如果类间分界清晰但边界处有零星交叉那大概率是早期故障样本的特征重叠可以考虑在特征矩阵里加入IMF能量比特征把各层IMF的能量占比作为新列这类特征对故障频段迁移有更强的代表性。我最早做轴承诊断时也是一路踩坑过来的。第一次在某个公开数据集上把测试准确率刷到99%还挺得意结果换了转速和负载之后掉到82%被现场工程师一句话问住你到底是会诊断轴承还是会背数据那次之后我给自己定了个习惯任何模型交付前必须跑变工况验证必须跑混淆矩阵分析必须解释模型主要用哪些IMF层做决策。这三件事做完模型才敢从实验室挪到现场。希望这个流程对你也有用哪怕只帮你少踩一个坑这篇笔记就没白写。本文还有配套的精品资源点击获取
返回列表