WDCNN与t-SNE可视化:轴承故障诊断的端到端实践

发布时间:2026/9/8 1:50:32
WDCNN与t-SNE可视化:轴承故障诊断的端到端实践 简介面向机械故障诊断与深度学习交叉领域学习者这份资源提供了基于WDCNN宽度深度卷积神经网络的轴承故障诊断完整实现并附带t-SNE可视化结果适合从事故障诊断研究、毕业设计或工业智能运维入门的学生与工程师。压缩包共13个文件包含2个Python脚本主程序与数据处理模块、10个MAT格式的轴承振动数据涵盖正常、内圈、外圈、滚动体不同损伤尺寸工况以及1张t-SNE特征可视化图整体大小8.66MB便于快速下载与复现。目前已有1275人学习使用对于想复现经典深度学习诊断模型的研究者尤为实用。通过这份资料读者可掌握WDCNN模型的数据预处理流程、网络结构与故障分类方法还能借助t-SNE图直观理解高维特征的聚类分布为后续改进模型或对比实验提供参考。 做旋转机械故障诊断的人应该都有同感拿到振动信号的第一步往往不是建模而是先纠结特征。我早期试过时域统计、频段能量、包络谱加SVM的经典方案CWRU数据上能到95%换到自己实验台的数据上直接掉到80%以下。后来我认真跑了WDCNN方案把原始振动信号直接丢进网络做端到端分类再用t-SNE可视化去看模型学到了什么整个流程清爽了很多。这篇文章我把项目完整做法写一遍包括数据怎么切、模型怎么搭、t-SNE图怎么出也把踩过的坑一并列出来。适合正在做轴承故障诊断或者设备健康管理的工程师也适合刚入门深度学习但不想在特征工程上耗太多时间的研究生。1. 项目整体思路为什么选WDCNN还要做t-SNE1.1 传统特征工程卡在哪时域特征里均值、方差、峭度、波峰因子看着简单换一个工况就不稳定频域特征虽然能反映能量分布但需要手动选择一个合适的频段包络谱更麻烦要先做带通滤波再解调带通范围选不好故障频率直接淹在噪声里。每个特征对哪种故障敏感基本靠经验试。这不是说传统方法没用而是它把“特征提取”和“分类器训练”拆成了两个独立环节特征没提好后面换什么分类器都白搭。WDCNN却把特征提取和分类放在同一个网络里优化输入就是原始振动信号输出直接是故障类别。这样至少省掉了一大半调特征的痛苦。尤其是换了新设备、新工况时传统方法要重新做特征筛选而重新训练一个端到端模型往往更省事。1.2 WDCNN的切入点宽卷积核与端到端学习WDCNN的命名重点在于“宽卷积核”。网络第一层不是常规的3×1小卷积核而是64×1的宽卷积核覆盖的时域长度足够看完整的故障冲击波形像一个可学习的低通滤波器先把高频噪声抑制掉让模型专注于故障冲击的低频包络信息。后面的卷积层再逐步变窄用3×1小卷积核去提取更抽象的模式。这种做法和人工包络解调有异曲同工之妙但包络解调的滤波器是固定设计WDCNN里的“滤波器”是跟着分类目标一起训练出来的。实际跑下来同样的数据下WDCNN基本不需要在特征工程上反复折腾网络自己会找到区分故障的模式。1.3 为什么一定要加t-SNE可视化只看准确率很难发现模型内部的问题。t-SNE在这里的作用是把网络中间层输出的高维特征向量压缩到二维平面每个样本用一个点表示按真实标签上色。如果模型学得好同标签样本会聚成簇不同标签之间分得开如果学得不好二维图就是一团乱麻哪怕准确率数字挺高也要警惕是不是数据划分或者特征提取出了问题。所以这个项目里t-SNE可视化不是一个“好看的图”而是模型诊断工具。它能在训练完成后告诉你模型到底有没有学到可分特征也能帮你定位容易混淆的故障类别。2. 网络结构拆解与关键参数选择2.1 WDCNN的完整网络骨架项目里用的WDCNN结构如下输入为长度为2048的一维振动信号输出为故障类别数。这个配置主要针对CWRU数据集中12kHz采样率的数据。层卷积核/操作通道数步长输出尺寸Conv164×1168256×16BN ReLU MaxPool2-2128×16Conv23×1321128×32BN ReLU MaxPool2-264×32Conv33×164164×64BN ReLU MaxPool2-232×64Conv43×164132×64BN ReLU MaxPool2-216×64Conv53×164116×64BN ReLU GlobalMaxPool---64Flatten Dropout---64FC全连接num_classes-num_classesConv1的padding需要配合步长确保输入长度能整除。我通常把第一个卷积的padding设为28这样2048经过步长8后得到256个位置再做一次池化变128后面的尺寸正好是2的幂。搭建的PyTorch代码如下import torch.nn as nn class WDCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 16, kernel_size64, stride8, padding28), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size3, stride1, padding1), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(64, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(64, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.AdaptiveMaxPool1d(1), ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(64, num_classes), ) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) return self.classifier(x)如果想复现直接拿这段代码起步没问题它的参数量不大训练几张显卡都能跑CPU硬扛也能训练只是慢一些。2.2 第一个卷积核为什么是64而不是3很多人第一次看到WDCNN都会问这个问题。轴承故障信号里存在周期性的冲击但采集到的加速度信号混杂着噪声、齿轮啮合频率和其他结构振动。如果第一层就用3×1小卷积核网络很容易把局部毛刺当成重要特征训练会很不稳定。64×1卷积核覆盖了64个采样点12kHz采样率下大约是5.3毫秒相当于对连续波形做了时域平滑处理能抑制高频干扰、保留故障冲击的包络信息。我试过把第一层改成32×1训练确实也能收敛但测试集准确率稳定性和最终分类效果都略差于64×1。如果数据采样率不是12kHz比如换到20kHz64个点代表的时间变短就要考虑把卷积核放大到96或128而不是机械照抄。2.3 BN、池化与Dropout的作用BatchNorm放在卷积层之后、激活函数之前作用是稳定每层特征的分布。1D卷积网络在训练初期对输入尺度非常敏感BN能显著加快收敛也允许使用稍大一点的学习率。池化层的作用是降低特征维度和计算量同时提供一定程度的平移不变性故障冲击在窗口里稍微偏移一点池化后特征不会剧烈变化。Dropout则主要用来防过拟合尤其在CWRU这类样本量不算大的数据集上特别重要。去掉Dropout时训练集准确率很快接近100%验证集却开始波动这是典型的过拟合。我一般把Dropout放在0.4到0.6之间太低了没有约束效果太高了网络学不进去。3. 数据准备与预处理最容易翻车的一步3.1 数据集怎么选怎么划分项目使用的是CWRU轴承数据公开数据集取驱动端12kHz采样率下的正常、内圈故障、外圈故障、滚动体故障四类再加上不同损伤直径的细分总共10类。公开数据的好处是方便和别人的结果对比但划分方法很容易出问题不能直接把所有窗口随机打散再分训练集和测试集。正确做法是先按原始连续振动信号的“采集文件/工况”分成训练、验证、测试三组再从各组内部切窗口。很多第一次做的人把所有窗口全部随机shuffle结果训练集和测试集来自同一条连续信号的不同片段验证指标虚高实际部署时模型根本没那么好。这个坑在故障诊断场景里非常普遍一定要按记录单元划分而不是按样本划分。3.2 窗口长度和重叠率怎么定WDCNN输入窗口长度我固定为2048。12kHz采样率下2048个点对应约0.17秒以1797rpm的转速计算大约是5个旋转周期故障特征信息足够完整。窗口再短可能截断故障脉冲周期窗口太长样本数变少训练效率也低。窗口之间的重叠率我设在50%~75%之间。重叠能够成倍扩大样本量对数据量有限的情况很友好但重叠率太高会让相邻样本高度相关造成“假重复样本”验证结果会偏乐观。我的习惯是先50%起步如果样本量不足以支撑训练再提到75%。另外滑动窗口的步长要保证窗口能完整落在原始信号内超出边界的部分直接舍弃即可。3.3 归一化这样做才不出问题每个窗口独立做z-score归一化也就是减去本窗口均值再除以本窗口标准差。不建议用全局mean和std因为测试集的统计信息会被提前引入训练过程而且不同工况下振动幅值差异很大全局归一化会让弱故障样本在幅度上吃亏。import numpy as np def sliding_window(data, length2048, overlap0.5): step int(length * (1 - overlap)) windows [] for i in range(0, len(data) - length 1, step): w data[i:i length] w (w - w.mean()) / (w.std() 1e-8) windows.append(w) return np.array(windows, dtypenp.float32)代码里加了一个1e-8防止窗口方差为0导致除零。我实测遇到过某段停机数据全为零不处理就直接出现NaN这个小量成本几乎为零但能避免大问题。3.4 样本不平衡怎么办如果某些故障类型样本偏少比如滚动体故障天然比内外圈故障难采集可以采取两种手段一是给损失函数加上类别权重对少数类样本的错误给更大惩罚二是在窗口层面做数据增强。常用的增强方式有叠加小幅高斯噪声、随机缩放0.9到1.1倍。增强后的样本参与训练标签不变整体准确率提升不大但对少数类的recall帮助明显。要避免一种做法直接复制少数类原始样本几十几百遍去硬凑数量。这样模型只会过拟合到极少数的原始窗口上稍微换一段信号性能就崩。4. 训练与评估完整流程实录4.1 训练配置和代码框架训练使用PyTorch优化器选择Adam初始学习率0.001批量大小64训练50个epoch。学习率调度器用ReduceLROnPlateau当验证损失连续5个epoch不下降时学习率乘以0.5。损失函数用交叉熵类别权重按样本数倒数计算。criterion nn.CrossEntropyLoss(weightclass_weights) optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5 ) for epoch in range(50): model.train() for xb, yb in train_loader: optimizer.zero_grad() out model(xb.unsqueeze(1)) loss criterion(out, yb) loss.backward() optimizer.step() model.eval() val_loss evaluate_loss(model, val_loader) scheduler.step(val_loss)注意输入x在进入网络前要reshape成(batch, 1, 2048)。漏掉这个维度问题很常见PyTorch里卷积网络直接会把一维信号当作没有通道维报错信息也能看懂但新手第一次遇到往往会懵一会儿。4.2 训练过程中的表现这个模型在CWRU数据上收敛很快。我实际跑的时候前5个epoch准确率就能到90%以上20个epoch之后验证集准确率基本稳定在98%以上。loss曲线会在前10个epoch快速下降之后进入平台期。如果发现验证loss先降后升、训练loss还在降就是过拟合信号这时候检查Dropout设置、是否训练太久、要不要提前停止。我的早停策略是验证损失连续8个epoch不下降就保存当前最优模型。在50个epoch内这个策略基本都能触发模型不会白跑满全部epoch。4.3 评估不能只看准确率故障诊断不是普通图像分类类别不平衡和故障漏检的代价经常被低估。我除了看整体准确率还会记录macro-F1、每类recall和混淆矩阵。下面是一个典型结果类别precisionrecallf1-score正常1.001.001.00内圈0.0070.990.990.99内圈0.0140.971.000.98外圈0.0071.000.960.98滚动体0.0070.960.950.95滚动体故障的recall略低这在物理上也说得通滚动体故障信号需要经过保持架传递到传感器能量衰减比内圈外圈更明显模型容易把它和轻微内圈故障混淆。这时候不要急着调模型先看混淆矩阵集中在哪两类再决定是补充数据还是调整损失权重。4.4 从结果反推问题有一次整体准确率到了99.2%但混淆矩阵显示外圈故障0.007和正常之间有少量混淆。查原始信号后发现那几条数据正好处于负载变化的过渡段幅值明显比其他样本低。准确率数字会掩盖局部问题必须结合混淆矩阵和原始波形一起看。项目里我习惯把预测错误的样本全部导出逐条看波形和频谱这个过程往往比调模型更有效。5. t-SNE可视化实现与调参5.1 取出中间层特征t-SNE可视化的第一步是让模型输出特征而不是分类结果。在上面WDCNN代码里features部分输出的是全局池化后的64维特征这个特征已经经过多层抽象足够用来表征一个样本。提取特征的代码可以这样写model.eval() feats [] labels [] with torch.no_grad(): for xb, yb in test_loader: f model.features(xb.unsqueeze(1)).squeeze() feats.append(f.cpu().numpy()) labels.append(yb.numpy()) X np.concatenate(feats, axis0) Y np.concatenate(labels, axis0)注意这里用验证集或测试集做可视化更有说服力不建议把训练集样本混进去否则图上的簇可能只是模型记住了训练样本。5.2 核心代码用sklearn实现t-SNE特征准备好之后用sklearn.manifold.TSNE降维。我常用的配置如下from sklearn.manifold import TSNE import matplotlib.pyplot as plt tsne TSNE(n_components2, perplexity30, learning_rate200, n_iter1000, initpca, random_state42) X_2d tsne.fit_transform(X) plt.figure(figsize(10, 8)) for i in np.unique(Y): idx Y i plt.scatter(X_2d[idx, 0], X_2d[idx, 1], s8, labelfclass {i}) plt.legend() plt.savefig(tsne_result.png, dpi200)参数里initpca很多时候比默认的random更稳定PCA初始化能让初始点分布更接近真实结构后续迭代不容易陷入局部极小。random_state一定要固定否则每次跑出的图都不一样后续分析没法对比。5.3 调参经验别让可视化骗了你t-SNE最有争议的就是参数。perplexity通常取5到50之间样本量越大可以适当调大几千个样本时30是比较稳妥的值。learning_rate太小时点会挤成一团看不出任何结构太大会让图看起来过度散开好像分得很开实际上可能只是噪声。遇到聚类效果不好时还有一个常见原因提取的特征太浅。只取第一个卷积层的输出高维特征还停留在低级波形层面t-SNE很难呈现清晰簇结构。解决方法是改取最后一层池化后的特征也就是代码里的64维向量。另外t-SNE本身是随机优化算法不同随机种子下图的形态会有差异判断标准应该是宏观簇结构是否稳定而不是每个点的精确坐标。同类样本即使不在一个紧密球体里只要相对集中、与异类有明显分离结果就基本可用。5.4 结果图怎么解读好的结果图通常长这样正常样本单独聚成一团不同故障类型各自成团同类不同损伤程度分布在相邻区域呈现一种渐进变化。如果看到两个故障类别完全重叠先别急着说模型不行去查这两类的原始波形是否本来就相似。我在实际项目中曾遇到外圈故障和滚动体故障的二维点长期叠在一起后来发现测试数据里包含了一段混合工况模型其实并没有错而是数据本身标签有歧义。t-SNE图在故障诊断里不只是展示工具更是一个数据质量检查工具。6. 常见问题与避坑经验实录6.1 训练loss不下降最常见的原因是输入数据没归一化或学习率太大。振动信号幅值可能是几个g不归一化的情况下第一层卷积的梯度很容易爆炸。解决办法是前面提到的每窗口z-score归一化同时把学习率降到0.0001试一试。如果还是不行检查最终全连接层的输出维度是否和类别数一致这类维度问题往往到最后一步才暴露。6.2 验证指标高但部署效果差绝大多数是数据划分泄漏。一定要按连续原始信号分块而不是把滑动窗口随机打散。另一个常见问题是测试集和训练集来自同一段原始信号的重复切片验证时表现自然好换到新采集数据就不行了。判断方法很简单看训练样本和测试样本有没有重叠的原始数据段。如果有重构数据划分。6.3 t-SNE每次运行结果都不一样这是正常现象。t-SNE的优化起点和度量方式决定了它不是确定性映射。处理办法是固定random_state同时至少跑3次取稳定出现的簇结构作为结论。如果不同random_state下簇结构差别很大说明特征本身的分辨力不够强应该回到模型端改进特征提取而不是继续调t-SNE参数。6.4 换设备、换采样率后模型失效这是故障诊断落地中的老问题。输入长度2048和卷积核64都是针对12kHz采样率调的如果换到20kHz要先按采样率比例重新计算窗口长度比如2048乘以20除以12约等于3413取2的幂就是4096。更根本的解法是做跨设备迁移学习先在源域数据上预训练再用目标域少量数据微调。这个方向比较深但在实际项目中比反复调参更划算。这个项目做完之后我自己留下两个习惯。一是不管模型准确率多高每次训练完都强制出一张t-SNE图图能帮我快速判断特征是不是真的可分二是在任何换工况、换设备实验前先核对数据划分方式和归一化策略再谈模型调参。WDCNN本身不是特别花哨的模型但配合合理的预处理和可视化检查在轴承故障诊断这个场景里非常能打。如果你也在复现类似项目建议先跑通这条主线再去尝试更复杂的注意力机制或者域适应算法你会更容易判断哪些改动真的有价值。本文还有配套的精品资源点击获取