ARTICLE DETAIL

资讯详情

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

CNN-SVM轴承故障诊断:小样本下可解释分类实战

CNN-SVM轴承故障诊断:小样本下可解释分类实战 简介这份资源面向工业设备健康监测方向的学习者与算法实践者围绕轴承故障诊断这一典型场景提供支持向量机与卷积神经网络两条技术路线的完整实现素材。包内共3201个文件以3200张jpg灰度图像和1个py脚本为主压缩包约4.1MB图像数据可用于CNN的输入与特征提取实验脚本则承担模型构建、训练与验证等核心逻辑便于对照理解传统机器学习与深度学习方法在同一任务上的差异。资源已有1739人学习下载说明其在故障诊断入门与课程设计场景中具备一定参考价值。读者可借助这批图像样本与代码完成数据预处理、SVM分类器训练、CNN网络搭建及两种方法诊断结果的对比分析进而掌握从振动信号到故障识别的完整流程为设备维护与健康管理相关课题提供可复用的实验基础。1. cnn-svm轴承故障诊断把振动信号变成可解释的分类结果产线上电机轴承刚出现早期点蚀时振动信号里那点周期性冲击往往被噪声淹没阈值报警要么漏报要么天天误报。cnn-svm轴承故障诊断这套组合就是冲着这个场景来的先用 CNN 从原始振动或时频图里自动学特征再把 CNN 提取的高维特征喂给 SVM 做小样本分类。它解决的不是能不能分类而是样本少、工况变、还要能解释这三件事。适合手里有振动数据、会点 Python、但被传统特征工程和深度模型调参反复折磨的从业者。下面按我实际落地的顺序把选型、实现、参数和坑一次讲清。2. 为什么不是纯 CNN 也不是纯 SVM两条路各自的边界2.1 纯 CNN 在小样本轴承数据上的翻车点轴承故障诊断的公开数据集比如 CWRU单工况下每类样本也就几百条划分训练集后往往只剩一两百。纯 CNN 参数量动辄几十万这种数据量下过拟合几乎是必然。我最早用 1D-CNN 直接端到端分类训练集准确率 99%换到另一台电机测的验证集直接掉到 70% 出头。原因不玄学卷积核学到的判别特征和特定转速、负载强绑定工况一变特征分布就漂了。另一个问题是可解释性。产线工程师不认黑匣子说这是内圈故障他们要看到特征空间里类间距离、支持向量的分布。纯 CNN 的 softmax 输出给不了这个。所以纯 CNN 适合数据量上万、工况固定的场景轴承这种小样本多工况的活它单独扛不住。2.2 SVM 在轴承诊断里的真实优势与短板SVM 的优势在小样本和高维结构风险最小化泛化误差有界核函数能把非线性可分问题映射到高维。轴承故障的类间边界本来就不是线性的RBF 核很合适。我用人工特征时域峭度、频域包络谱峰值加 SVMCWRU 上跨负载能到 85% 左右比纯 CNN 稳。短板也明显特征得人工设计。峭度对早期冲击敏感但对转速敏感包络谱峰值要选对共振频带选错频带整个特征就废了。而且人工特征维度低信息损失大遇到复合故障内圈外圈同时就分不开。所以 SVM 缺的是自动且判别性强的特征这正好是 CNN 能补的。2.3 CNN 提特征 SVM 分类的分工逻辑把两者串起来的分工是CNN 当特征提取器不接 softmax 分类头取最后一个卷积池化层或全连接层的输出作为特征向量SVM 拿这个向量做分类。这样 CNN 只负责学什么样的振动模式有区分度不负责最终决策过拟合压力小很多SVM 负责在小样本特征空间里找最大间隔超平面泛化稳。常见做法是 CNN 先在训练集上做一次预训练可以带分类头辅助收敛然后去掉分类头把中间层输出抽出来用这些特征重新训练 SVM。也有端到端联合训练的但实现复杂我一般用两阶段好调、好排查。3. 从振动信号到 CNN 输入数据预处理的四个关键动作3.1 振动信号分段与样本增强原始振动是长时序得切成固定长度样本。CWRU 驱动端数据采样率 12kHz我一般切 1024 点一段重叠 50%。重叠是为了增加样本量小样本场景下这一步很值。切完做 Z-score 归一化按通道减均值除标准差别用全局统计量用每段自己的避免工况差异被归一化抹掉。import numpy as np def segment_signal(signal, window1024, overlap0.5): step int(window * (1 - overlap)) segments [] for start in range(0, len(signal) - window 1, step): seg signal[start:start window] # 每段独立 Z-score保留工况内的相对幅值关系 seg (seg - seg.mean()) / (seg.std() 1e-8) segments.append(seg) return np.array(segments)window 取 1024 是经验值轴承故障特征频率通常在几百 Hz 到几 kHz1024 点在 12kHz 下覆盖约 85ms够包含几个冲击周期。overlap 0.5 是样本量和独立性的折中再高会导致训练集和验证集泄漏切分时要按时间段切不能随机打乱后再切。3.2 时频图还是原始时序输入形式怎么选CNN 输入有两种主流直接 1D 原始时序或转成 2D 时频图STFT、CWT。1D 网络浅、参数少、训练快适合样本少2D 时频图信息密度高但需要更多数据且 STFT 窗长选不好会丢时间或频率分辨率。我的选择标准样本总数低于 2000 用 1D高于 2000 且算力够用 2D CWT。CWT 比 STFT 更适合冲击类故障因为它在低频有好的频率分辨率、高频有好的时间分辨率正好匹配轴承冲击的瞬态特性。import pywt import numpy as np def cwt_image(segment, scalesnp.arange(1, 64)): # 用 Morlet 小波做连续小波变换得到尺度-时间图 coeffs, _ pywt.cwt(segment, scales, morl) # 取模值并归一化到 0-1作为 CNN 的 2D 输入 img np.abs(coeffs) img (img - img.min()) / (img.max() - img.min() 1e-8) return imgscales 范围决定覆盖的频带1 到 64 在 12kHz 下大致覆盖 187Hz 到 12kHz够用。morl 是复 Morlet对冲击响应好。如果换成 2D 输入CNN 第一层卷积核要改成 2D后面结构不变。3.3 标签与工况划分别让数据泄漏毁掉验证CWRU 有 0/1/2/3 马力四种负载。很多人随机划分样本结果同一段信号切出的相邻样本一个进训练一个进验证验证准确率虚高十几个点。正确做法是按负载划分比如 0/1/2 马力训练3 马力验证模拟跨工况。或者按时间段划分前 70% 训练后 30% 验证。标签用 one-hot 还是整数都行SVM 用整数标签。类别不平衡时正常样本远多于故障SVM 设 class_weightbalancedCNN 训练时用加权交叉熵。3.4 训练集/验证集/测试集的划分比例小样本场景我一般 6:2:2。训练集训 CNN 和 SVM验证集调超参测试集只在最后跑一次。别用测试集反复调参那是自欺欺人。如果样本实在少每类不到 100用 5 折交叉验证报告平均准确率和标准差标准差比均值更能说明模型稳不稳。4. 搭 CNN 特征提取器结构、训练与特征导出4.1 1D-CNN 的最小可用结构1D 输入下我用三层卷积加全局平均池化结构简单、参数少、不容易过拟合。每层卷积后接 BN 和 ReLU池化用最大池化。最后全局平均池化输出一个固定长度向量这就是给 SVM 的特征。import torch import torch.nn as nn class CNNFeatureExtractor(nn.Module): def __init__(self, in_channels1, feat_dim64): super().__init__() self.conv1 nn.Sequential( nn.Conv1d(in_channels, 16, kernel_size7, padding3), nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2)) self.conv2 nn.Sequential( nn.Conv1d(16, 32, kernel_size5, padding2), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2)) self.conv3 nn.Sequential( nn.Conv1d(32, feat_dim, kernel_size3, padding1), nn.BatchNorm1d(feat_dim), nn.ReLU()) # 全局平均池化把时间维压掉输出 feat_dim 维特征 self.gap nn.AdaptiveAvgPool1d(1) def forward(self, x): x self.conv1(x) x self.conv2(x) x self.conv3(x) x self.gap(x).squeeze(-1) return xkernel_size 从 7 到 5 到 3 递减是为了先抓大范围冲击模式再抓细节。feat_dim 取 64 是特征维度和 SVM 训练成本的折中取 128 也行但 SVM 训练会慢。BN 在小 batch 下不稳定batch_size 别低于 32。4.2 带分类头的预训练与损失函数选择光靠特征提取器没有监督信号得加个分类头预训练。分类头就是一层全连接加 softmax训练完丢掉。class CNNClassifier(nn.Module): def __init__(self, extractor, num_classes): super().__init__() self.extractor extractor self.head nn.Linear(64, num_classes) def forward(self, x): feat self.extractor(x) return self.head(feat), feat # 训练时用交叉熵类别不平衡加权重 criterion nn.CrossEntropyLoss(weighttorch.tensor([1.0, 3.0, 3.0, 3.0]))weight 里正常类给 1故障类给 3是经验值按实际类别比例调。优化器用 Adamlr 1e-3训练 50 到 100 epoch早停看验证集损失。别用太大 lrBN 加 Adam 容易震荡。4.3 特征导出与维度确认训练完把 extractor 单独拿出来对训练集和验证集抽特征。注意要设 eval 模式关掉 dropout 和 BN 的训练行为。extractor.eval() def extract_features(model, loader): feats, labels [], [] with torch.no_grad(): for x, y in loader: f model(x) feats.append(f.numpy()) labels.append(y.numpy()) return np.concatenate(feats), np.concatenate(labels) X_train, y_train extract_features(extractor, train_loader) X_val, y_val extract_features(extractor, val_loader) print(X_train.shape) # 应为 (样本数, 64)抽完特征检查维度如果 feat_dim 是 64X_train 第二维必须是 64。维度不对说明 forward 里 squeeze 或池化写错了。特征最好再做一次标准化SVM 对特征尺度敏感。5. SVM 分类与调参核函数、C 和 gamma 怎么定5.1 RBF 核 vs 线性核先看特征是否线性可分CNN 抽出的特征已经经过非线性变换类间边界可能接近线性。我一般先用线性核跑一版如果验证准确率和 RBF 差 3 个点以内就用线性核训练快、可解释性好能看权重。差得多再上 RBF。from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # 先标准化再 SVMRBF 核对尺度敏感 clf_linear make_pipeline(StandardScaler(), SVC(kernellinear, C1.0)) clf_rbf make_pipeline(StandardScaler(), SVC(kernelrbf, C10.0, gammascale))gammascale 是 sklearn 默认等于 1/(n_features * X.var())比手写 gamma 稳。C 控制间隔和误分类的权衡C 大偏向拟合训练集C 小偏向泛化。5.2 网格搜索的实用范围与交叉验证别用大范围网格浪费时间。C 取 [0.1, 1, 10, 100]gamma 取 [0.001, 0.01, 0.1, 1]5 折交叉验证。from sklearn.model_selection import GridSearchCV param_grid {svc__C: [0.1, 1, 10, 100], svc__gamma: [0.001, 0.01, 0.1, 1]} grid GridSearchCV(clf_rbf, param_grid, cv5, scoringaccuracy, n_jobs-1) grid.fit(X_train, y_train) print(grid.best_params_, grid.best_score_)n_jobs-1 用满 CPU。best_score_ 是交叉验证均值如果它和验证集准确率差很多说明数据划分有问题或过拟合。我一般要求两者差距在 5 个点以内。5.3 用混淆矩阵定位易混类别准确率会骗人混淆矩阵不会。内圈故障和外圈故障在特征空间里可能挨得近尤其早期故障。from sklearn.metrics import confusion_matrix, classification_report y_pred grid.predict(X_val) print(confusion_matrix(y_val, y_pred)) print(classification_report(y_val, y_pred, digits4))看混淆矩阵哪两类互相错分多如果内圈和外圈混说明 CNN 特征对冲击位置不敏感可以加宽第一层卷积核或增加 CWT 输入的频率分辨率。如果正常和故障混说明特征对早期故障不敏感检查预处理有没有把微弱冲击归一化掉。6. 避坑与排查五个我踩过的真实坑6.1 验证准确率虚高测试集崩盘现象验证集 95%换测试集 60%。原因随机划分样本导致相邻段泄漏或归一化用了全局统计量把测试集信息泄进训练。解决按负载或时间段划分归一化只用训练集统计量测试集复用训练集的均值和标准差。6.2 SVM 训练报错特征维度不一致现象fit 时报 X 维度不匹配。原因CNN 抽特征时有的 batch 用了 train 模式BN 行为不同导致输出维度或分布不一致或 extractor 没设 eval。解决抽特征前 extractor.eval()且用 torch.no_grad()确保所有样本走同一套 BN 统计量。6.3 RBF 核训练极慢或内存爆现象几万样本时 SVM 训练卡死。原因RBF 核 SVM 训练复杂度 O(n²) 到 O(n³)样本多了扛不住。解决先降特征维度PCA 到 32 维或用 LinearSVC 替代或对样本下采样。轴承小样本一般不会遇到但特征维度别设太大。6.4 跨工况准确率掉 20 个点现象同工况 98%跨工况 75%。原因CNN 学到了工况相关特征转速、负载不是故障本质特征。解决训练时做工况增强加不同转速的样本或用领域自适应方法如 MMD 损失对齐不同工况的特征分布。简单做法是归一化时用每段自己的统计量减少工况幅值差异。6.5 复合故障分不开现象单故障准内圈外圈复合故障全错。原因训练集没有复合故障样本SVM 只能分见过的类。解决要么补复合故障数据要么把任务改成多标签每个故障独立一个 SVM别硬做多分类。7. 进阶用 t-SNE 验证 CNN 特征到底学没学到东西调完参准确率还行但你怎么知道 CNN 抽的特征真的把不同故障分开了而不是靠某个偶然的幅值差异我习惯用 t-SNE 把 CNN 特征降到 2D 画出来看类簇是否分离。这一步比准确率更能说明问题也是给产线工程师解释模型为什么可信的好材料。from sklearn.manifold import TSNE import matplotlib.pyplot as plt # 对验证集特征做 t-SNEperplexity 一般取 5-50 tsne TSNE(n_components2, perplexity30, random_state42) X_2d tsne.fit_transform(X_val) plt.figure(figsize(8, 6)) for cls in np.unique(y_val): idx y_val cls plt.scatter(X_2d[idx, 0], X_2d[idx, 1], labelfclass {cls}, s10) plt.legend() plt.title(t-SNE of CNN features) plt.show()perplexity 取 30 是常用值样本少时调到 5 到 10。如果类簇重叠严重但 SVM 准确率还高说明 SVM 在过拟合得回头查数据泄漏。如果类簇分得开但准确率低说明 SVM 参数没调好。两个指标要一起看。再进一步可以对比 CNN 特征和人工特征的 t-SNE 图。我做过一次对比人工特征下内圈和外圈簇有重叠CNN 特征下分开了这就是 CNN 提特征价值的直接证据。把这个图放进汇报材料比堆准确率数字管用。还有个实用技巧SVM 的支持向量本身就是难分样本把支持向量对应的原始振动段拿出来看往往能发现一些标注可疑或传感器异常的样本。我靠这招揪出过几条被误标为正常的早期故障数据重新标注后模型跨工况准确率提了 6 个点。这个习惯我一直保留着模型指标只是入口支持向量和 t-SNE 图才是理解数据的窗口。希望帮到你。本文还有配套的精品资源点击获取
返回列表