WDCNN轴承故障诊断实战:从原理到tSNE可视化全解析
简介面向轴承故障诊断与深度学习交叉应用场景这份资料基于WDCNN一维卷积神经网络实现故障分类并附带t-SNE降维可视化结果适合机械故障诊断方向的研究生、工程师以及深度学习初学者参考。压缩包共13个文件其中10个mat文件涵盖正常、滚动体、内圈、外圈等故障类型并包含不同损伤直径与负载工况的振动信号数据便于直接用于模型输入与复现实验2个Python脚本分别承担数据读取预处理与WDCNN模型训练、t-SNE可视化功能另含1张特征聚类效果图整体仅8.66MB轻量易用。已有1277人学习下载。通过这套代码读者可以快速掌握从原始振动信号到故障模式识别的完整流程理解WDCNN如何自动提取时域特征并通过t-SNE图观察不同故障类别在特征空间中的分布为后续开展迁移学习、模型调优或进一步对比实验打下基础。数据与代码相互配套拿到后即可对照运行尤其适合需要快速复现深度轴承诊断实验的读者。1. 基于WDCNN的轴承故障诊断这份资源能让你少走三个月弯路如果你正准备做设备故障诊断方向的深度学习实验大概率会在某个平台刷到“WDCNN轴承故障诊断 含tSNE可视化”这个资源包。它解决的是振动信号分类里的一个核心痛点直接用原始时域信号做输入不需要人工构造几十个频域特征靠一个宽卷积核的第一层把局部波形特征“咬住”后面再用小卷积核加深抽象。我当初拿到这份资源时最直观的感受是代码不是那种只能跑通Demo的教学脚本而是把数据切分、模型定义、训练验证到特征可视化串成了一条完整链路。适合谁适合刚入门深度学习故障诊断的研究生也适合想快速在轴承数据集上做基线对比的工程师。但如果你只是点一下运行等着出图大概率会在数据预处理和tSNE参数上翻车下面这几章就是按我自己复现的顺序写的。2. 原理与选型为什么WDCNN能扛住振动信号的强噪声2.1 宽卷积核的第一层从原始信号里直接抓特征WDCNN全称是Wide Deep CNN最早用在一维振动信号分类上核心设计思路是在网络第一层使用较宽的卷积核。常见CNN第一层卷积核尺寸在3x3或5x5而WDCNN第一层往往取64甚至128个采样点长度这一层不降采样直接对原始信号滑动卷积。这么做的好处很直接振动信号里故障冲击往往持续几十个采样点窄核只能看到局部一两个点容易把冲击当成噪声宽核则能包住整个冲击片段相当于在第一层就做了一个可学习的带通滤波。# WDCNN第一层宽卷积核定义PyTorch风格 self.conv1 nn.Conv1d( in_channels1, out_channels16, kernel_size64, # 宽卷积核覆盖64个采样点 stride8, # 步长较大减少后续计算量 padding32 # 保证输出长度与输入一致或按公式控制 )这段代码里kernel_size是核心参数。我一般会先看输入信号长度如果每段样本是1024点kernel_size取64相当于覆盖1/16的信号长度能捕获一个完整的冲击周期如果原始数据采样率是12kHz64点大约对应5.3ms而常见轴承内圈故障特征频率在100Hz到几百Hz冲击周期远超这个窗口所以宽核不是越大越好要和信号特性匹配。stride8会导致第一层输出长度约为输入/8后续层再用小卷积核继续压缩。padding的值要根据是否有后续全连接层来定如果后面接全局池化padding可以随意点如果接Flatten则需要精算输出长度。2.2 训练流程与资源包文件结构拿到手先做两件事这套资源的典型流程分四段数据切分、模型训练、测试评估、tSNE可视化。网络主体由四个卷积块加一个全连接层组成每个卷积块包含卷积、批归一化、ReLU和池化。池化层用最大池化比较多也有的版本用平均池化来保留整体能量。批归一化在这种振动信号任务里几乎是必须的因为不同工况下振动幅值差异很大不归一化前面几层的梯度很容易爆炸。拿到资源包后我建议先看两个文件模型结构定义文件和数据读取文件。不要先跑训练而是确认数据集的路径格式是否与你手头的匹配。很多资源自带的数据集是某一所高校采集的公开轴承数据包含正常、内圈故障、外圈故障、滚动体故障四类每类又有不同故障直径。资源包里的标签文件往往是一个Excel或CSV第一列是文件名第二列是类别编号。如果你用的是自己的数据就需要把标签重新映射成从0开始的整数否则CrossEntropyLoss会直接报错。# 标签映射的常见处理把原始类别字符串转为连续整数 label_map {normal: 0, inner: 1, outer: 2, ball: 3} labels [label_map[x] for x in raw_labels]多数数据集默认类别是平衡的但有些公开数据里滚动体故障样本只有几十个而正常样本有上千个。遇到这种不平衡先别急着上过采样可以按“每一类使用相同数量样本”的原则切训练集或者采用加权采样。资源包里如果默认用了随机采样你需要自己加一个WeightedRandomSampler否则模型会把正常样本学得特别好故障样本几乎分不出来。2.3 资源包文件结构眼见为实的清单一个靠谱的WDCNN资源包目录里通常有这些data文件夹原始数据或切好的npy、models文件夹网络定义、train.py、test.py、tsne.py、requirements.txt以及一个记录超参数的yaml或json文件。如果只有model.py和train.py没有tsne脚本那说明tSNE部分需要你自己补。这份资源最值钱的地方就是tSNE那一块它输出的是测试集在最后一个全连接层前的特征降维结果而不是模型输出。文件不多但每个文件的坑不少。train.py里的数据划分方式决定了你最终报的准确率是否有说服力。我见过一种常见的错误写法先对整个数据集做标准化再划分训练集和测试集这会造成数据泄漏。正确做法是先划分后标准化且标准化参数只能从训练集统计得出。资源包里的代码如果没注意这一点你在复现时一定要改过来。3. 从零跑通训练数据读取、标签生成与超参数设置3.1 环境与依赖版本固定比新功能更重要这个资源包常见是基于PyTorch或TensorFlow写的。如果是PyTorch版本建议用Python 3.8到3.10PyTorch 1.10以上但不要盲目上2.0以上因为某些老代码里用了torch.Tensor构造函数直接传list的方式新版虽然也能跑但有时候会提示警告。CUDA版本如果不是统一环境最容易碰到的是科学计算库冲突。# 推荐用conda创建独立环境避免污染已有环境 conda create -n wdcnn python3.9 conda activate wdcnn pip install torch1.13.1 torchvision0.14.1 pip install numpy scikit-learn matplotlib pandas安装完成后先运行一个最简单的数据加载测试确认能正常读取npy文件。我习惯先把完整训练脚本里所有数据预处理代码抽出来单独跑一遍看输出的张量shape是否和模型输入对齐。资源包里的输入通常是一维信号shape是(batch_size, 1, 1024)但有些版本会写成(batch_size, 1024)然后靠模型里的unsqueeze处理这两种写法在数据加载时完全不一样直接套用容易报维度错误。3.2 数据读取和归一化的可复用代码振动信号的读取不是简单的np.load就完事还要检查传感器通道、采样率文件以及单位。很多公开数据集的原始信号是.mat格式里面包含DE驱动端加速度和FE风扇端加速度等多个通道做故障诊断通常只用驱动端加速度信号。切样本时我一般把每个样本长度固定为1024点对应采样率下约0.085秒这个长度对大多数轴承故障特征频率已经足够分辨。# 从连续振动信号中切样本并做z-score归一化 def make_samples(signal, window_size1024, step512, normaliseTrue): samples [] for i in range(0, len(signal) - window_size, step): seg signal[i:iwindow_size] if normalise: mu seg.mean() std seg.std() if std 1e-6: std 1e-6 # 防止静默信号除零 seg (seg - mu) / std samples.append(seg.reshape(1, -1)) return np.array(samples)这里step是步长。如果step等于window_size样本之间没有重叠样本量少但独立性好如果step小于window_size样本重叠样本量增大但要小心测试集中与训练集来自同一段连续信号的样本会造成信息泄漏。我习惯用step512相当于50%重叠但必须在切分训练集和测试集时保证来自同一段原始信号的样本全部属于同一个集合否则验证准确率会虚高到离谱。3.3 超参数怎么设学习率、批大小和epochs的边界WDCNN这类模型不算深参数总量多在百万级不需要太大的batch size。以1024点输入、batch size64为例一个epoch大约几百个batch训练时间完全可以接受。学习率初始值我一般用0.001或0.0005优化器用Adam但Adam对学习率比较敏感初始值太大的话前几个batch的loss会从3.0涨到5.0看起来像炸了太小的话loss下降得像乌龟爬。# 一种稳妥的学习率设置用warmup 逐步下降 from torch.optim import Adam from torch.optim.lr_scheduler import CosineAnnealingLR optimizer Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_max50, eta_min1e-5)T_max设为总epochs的一半比较合理让学习率从1e-3余弦下降到1e-5后半段模型在低学习率下精细化收敛。如果你发现训练准确率先到99%然后掉到95%往往是学习率在后期下降太快模型在最优解附近振荡过大。这类问题不要调大学习率而是把eta_min提高到1e-4试试。3.4 快速验证训练到一半也能判断有没有跑偏训练脚本打印的每个epoch平均loss如果前三个epoch里loss从0.7降到0.3以下说明数据没有喂错。如果loss一直停在某个值附近比如2.3那大概率是类别标签没有对齐。另一个快速验证方法是训练完第一个epoch后用测试集跑一次如果准确率接近随机水平四类就是25%左右不要急着加大epoch数先检查数据归一化和标签顺序。# 训练中每隔几个epoch强制输出训练和验证准确率 python train.py --epochs 50 --log_interval 5很多资源包的train.py里没有这个log_interval参数你需要自己改。把验证集准确率打印出来是判断过拟合最直接的办法训练准确率涨到100%、验证准确率卡在70%说明模型记住了训练集。这时优先做法是增大样本切分步长、减少重叠而不是盲目加dropout因为重叠造成的泄漏比过拟合更难治。4. tSNE可视化把特征空间从黑匣子变成能说的故事4.1 tSNE的困惑度设置别让可视化骗了你tSNE是故障诊断论文里最常见的可视化手段目的是把高维特征映射到二维或三维平面让人直观看到同类样本是否聚在一起、不同类之间是否分开。这本身没有问题但tSNE有一个超参数叫perplexity困惑度它控制着每个点在降维时考虑多少个近邻。取值太小比如2或5每个点只看周围极少的邻居结果会散成一团取值太大比如100或200所有点会被拉成一坨看不出类别边界。# 使用sklearn的TSNE嗯保持perplexity在30到50之间 from sklearn.manifold import TSNE from sklearn.preprocessing import StandardScaler feat features_numpy # shape: (N, 512) feat_scaled StandardScaler().fit_transform(feat) tsne TSNE(n_components2, perplexity30, n_iter1000, initpca, random_state42) result tsne.fit_transform(feat_scaled)当样本数量少于200时perplexity不能设到50因为近邻数量超过样本数会报错或结果不稳定。我通常按样本量的平方根来取perplexity值比如300个测试样本就取17800个样本取28。另外initpca比随机初始化稳定跑出来的点分布不会每次都不一样。4.2 提取中间层特征并降维的完整代码tSNE可视化不是直接把模型输出拿来画图而是提取模型在最后一个全连接层之前的特征。也就是说你需要把模型截断让前向传播输出特征向量而不是分类logits。# 注册forward hook提取最后一层池化后的特征 features {} def hook_fn(module, input, output): features[feat] output.detach().cpu().numpy() model.pool3.register_forward_hook(hook_fn) # 在测试集上跑一遍收集所有特征 model.eval() all_feats [] all_labels [] with torch.no_grad(): for x, y in test_loader: out model(x) # 触发hook all_feats.append(features[feat].reshape(x.size(0), -1)) all_labels.append(y.numpy()) feats np.concatenate(all_feats, axis0)这里选择哪个层作为特征层很关键。资源包里如果默认挂的是最后一层卷积后的输出那特征维度可能是(样本数, 64, 16)需要reshape成二维。另一种做法是直接用模型里一个叫fc_input的线性层之前的输出。我比较喜欢挂在最后一个池化层之后因为这时特征已经高度抽象但还没有被全连接层压缩到分类维度保留的区分性信息最多。4.3 从可视化图里读出的三个结论画完tSNE散点图不要只看“分开了”就完事。第一看类间距离正常类和内圈故障如果完全重叠说明模型没有学到有效特征或者数据切分时两类信号本身相似度太高。第二看类内聚集同一类样本如果分成几个小团可能是样本来自不同工况或不同负载模型只学会了工况差异而不是故障差异。第三看异常点那些游离在类别边缘的点往往是切样时落在了信号瞬态段或噪声尖峰上。我常做的做法是把这些异常点对应的原始信号打印出来用matplotlib快速画一下时域波形十次有九次能看到明显的非周期冲击说明切样本的起点没对齐。# 画出tSNE结果并给每类不同颜色 import matplotlib.pyplot as plt plt.figure(figsize(8, 6)) for cls in range(4): idx np.where(labels cls)[0] plt.scatter(tsne_result[idx, 0], tsne_result[idx, 1], s10, alpha0.7, labelfClass {cls}) plt.legend() plt.savefig(tsne_result.png, dpi200)这个图保存下来基本就是论文里的Figure 1。但要注意tSNE的坐标轴没有绝对意义不能说某个类在x方向偏移代表某种物理量。解释时只能说“类别在特征空间上分离”不能说“故障特征在特征方向上增大”。5. 避坑指南我复现这个资源时踩过的五个坑5.1 样本切割重叠率过高导致数据泄漏现象训练准确率正常验证准确率高达99.8%测试准确率反而只有85%。原因切样本时step设为64样本窗口1024点相邻样本重叠率高达93%同一个故障冲击出现在大量样本里训练集和测试集如果来自同一段连续信号就等于同一个事件被两边同时看到。解决把step设为512以上并确保训练集和测试集在切割原始信号前就按互不重叠的片段划分比如前60%的信号只进训练集后40%只进测试集。5.2 归一化用错了维度精度直接掉两个点现象loss下降很快但测试集准确率始终在80%上下波动。原因对整段连续信号一次性做z-score归一化后再切样本导致每个窗口的均值和标准差包含未来信息而且静默段和冲击段的幅值被压到了相似范围。解决改成对每个单独窗口做归一化也就是先切样本后归一化并且把均值和标准差限制在窗口内。这样更符合“实时诊断”的场景因为实际部署时你只能拿到当前窗口的数据。5.3 最后一层全连接维度不匹配报错却查不出原因现象修改了输入长度或池化类型后训练一跑就报mat1 and mat2 shapes cannot be multiplied。原因全连接层输入维度写死了例如模型默认输入是1024点第一个卷积核stride8经过两次池化后特征维度是64*64但你改成2048点后特征维度翻倍最后全连接层期望的输入维度对不上。解决不要手算在定义模型的forward里加一个print(feat.size())跑到全连接层之前打印实际维度然后把全连接层的in_features改掉。5.4 tSNE每次运行结果都不一样被质疑“造假”现象同一批代码同一批测试集昨天跑的可视化图和今天跑的形状完全不同。原因tSNE的优化是随机初始化的而且求解过程是非凸优化不同的随机种子会收敛到不同的局部最优布局。解决固定random_state42同时把init改为pca这样虽然每次降维结果仍有微小差异但大局不会变。报告图时一定要在方法里写明随机种子否则审稿人很容易提出质疑。5.5 训练集和测试集来自同一工况评估虚高现象模型在你自己的数据上跑出99%准确率但换到另一个负载或转速下的数据准确率直接跳水到60%。原因资源包默认只在一个工况下切分数据模型可能学会了工况的特征而不是故障的特征。解决用多工况数据做跨域验证比如用0负载数据训练用1负载数据测试。如果你的数据只有单一工况至少要用更严格的留出法比如把某一段连续采集的信号整体留出作为测试集而不是随机打乱后切分。6. 进阶技巧给WDCNN加上自适应学习率与特征对比验证WDCNN的基础版本用固定学习率也能跑但在实际项目里我习惯给它加上余弦退火学习率再配合特征层对比验证能在不改变网络结构的条件下把测试准确率稳定提升1到2个百分点。做法分两步第一步是训练过程中用CosineAnnealingLR替换固定学习率让模型在中期大步探索、后期小步收敛这比手动设衰减节点要省心第二步是脚本里同时提取多个中间层的特征分别做tSNE然后对比哪个层对故障类型区分得最好。# 训练完一个epoch后更新学习率 for epoch in range(epochs): train_one_epoch(model, train_loader, optimizer) scheduler.step() lr optimizer.param_groups[0][lr] print(fEpoch {epoch}: lr{lr:.6f})学习率从1e-3降到1e-5的过程中我发现训练集准确率曲线会出现几次小跳变这是正常的。真正要注意的是验证集准确率峰值出现的位置如果峰值出现在第40个epoch之后说明模型其实需要更多epoch但过早的余弦下降会削弱后期学习能力这时可以把T_max调大一点或者把eta_min抬到1e-4。另外特征层对比也是排查模型过拟合的一个手段如果最后一层卷积的tSNE图比中间层更乱说明中间层学到的特征被最后一层池化破坏了这时可以检查是不是用了过大的池化核比如窗口为8的最大池化会把冲击尖峰直接削平。从那以后我每次拿到类似资源包都强制自己先看数据切分和特征可视化脚本再动手改模型结构这套习惯帮我避开了不少隐性问题希望帮到你。本文还有配套的精品资源点击获取