
简介这份资源面向深度学习与工业设备故障诊断方向的学习者和研究者围绕基于WDCNN宽卷积核深度卷积神经网络的轴承故障诊断任务展开适合具备一定Python与深度学习基础、希望将卷积网络落地到振动信号分类场景的读者。压缩包共13个文件以10个mat数据文件为核心涵盖正常、内圈、外圈、滚动体等多种故障类型与不同损伤尺寸的轴承振动样本另含2个py脚本分别负责数据处理与模型训练主流程并附1张tsne可视化结果图整体约8.66MB结构紧凑、开箱即用。目前已有1276人学习下载说明该方案在同类任务中具有较好的参考价值。读者可借此完整复现从原始振动信号到WDCNN分类的流程理解宽卷积核在轴承故障特征提取中的作用同时通过tsne降维图直观观察不同故障类别的特征分布与聚类效果为论文实验、课程设计或工程诊断方案提供可借鉴的实现思路与数据基础。1. 从一段振动信号说起WDCNN 轴承故障诊断资源能跑出什么手里拿到一段轴承振动信号采样率 12kHz几千个采样点里混着正常、内圈故障、外圈故障、滚动体故障几种状态肉眼根本看不出区别。这时候大多数人会先画个时域波形再做个 FFT 看频谱但频谱里的边频带和调制成分往往被噪声盖住靠人工判读既慢又不稳定。这份「基于 WDCNN 的轴承故障诊断含 t-SNE 可视化」资源解决的正是这个场景把原始振动信号直接喂给一维卷积网络让模型自己学特征最后用 t-SNE 把高维特征压到二维平面上直观看到不同故障类别到底分没分开。它适合两类人一类是刚接触深度学习做故障诊断的学生或初级算法工程师想找一个能跑通、能改、能可视化的完整流程另一类是做设备健康管理但被传统特征工程折磨的从业者想看看端到端方案到底靠不靠谱。资源本身不依赖复杂环境核心就是 PyTorch 或 TensorFlow 加几个科学计算库数据用公开轴承数据集即可。下面按「数据怎么进、模型怎么搭、结果怎么看、坑怎么避」的顺序拆开讲。2. WDCNN 模型结构与数据预处理把振动信号切成网络能吃的形状2.1 为什么选 WDCNN 而不是普通 CNN普通二维 CNN 处理图像很顺手但振动信号是一维时序强行转成二维谱图会引入额外变换误差还会让网络参数量暴涨。WDCNN 的核心思路是「宽卷积核 多层小卷积核」第一层用很大的卷积核常见宽度 64 或 128直接对原始信号做卷积目的是抑制高频噪声并保留低频冲击成分后面接几层 3×1 或 5×1 的小卷积核逐步提取更抽象的特征。这个结构在轴承故障诊断里被反复验证过对变工况和噪声有一定鲁棒性。我一般会先确认数据维度输入是(batch, 1, signal_length)其中signal_length通常取 1024 或 2048 个采样点。如果原始信号很长就滑动切窗窗长和步长决定样本数量。标签是故障类别索引比如 0 正常、1 内圈、2 外圈、3 滚动体。2.2 数据加载与归一化代码下面这段是数据预处理和 Dataset 构建的常见写法用 PyTorch 实现import numpy as np import torch from torch.utils.data import Dataset, DataLoader class BearingDataset(Dataset): def __init__(self, signals, labels, window_size1024, stride512): signals: shape (num_samples, signal_length) 原始振动信号 labels: shape (num_samples,) 故障类别 window_size: 每个样本的采样点数 stride: 滑动步长 self.windows [] self.labels [] for sig, lab in zip(signals, labels): # 滑动切窗避免样本过少 for start in range(0, len(sig) - window_size 1, stride): window sig[start:start window_size] # 按窗做 z-score 归一化消除量纲影响 window (window - np.mean(window)) / (np.std(window) 1e-8) self.windows.append(window) self.labels.append(lab) self.windows np.array(self.windows, dtypenp.float32) self.labels np.array(self.labels, dtypenp.int64) def __len__(self): return len(self.labels) def __getitem__(self, idx): # 增加通道维度 (1, window_size) x torch.tensor(self.windows[idx]).unsqueeze(0) y torch.tensor(self.labels[idx]) return x, y # 假设 signals 和 labels 已经加载好 dataset BearingDataset(signals, labels, window_size1024, stride512) loader DataLoader(dataset, batch_size64, shuffleTrue)逻辑说明切窗是为了扩充样本量步长小于窗长能保证相邻窗有重叠减少信息丢失。归一化按每个窗单独做而不是全局做因为不同工况下信号幅值差异很大全局归一化会让某些工况的样本被淹没。参数上window_size取 1024 是经验值对应 12kHz 采样率下约 85ms 的数据足够覆盖一个冲击周期stride取窗长的一半是常见做法如果样本太少可以减小到 256。2.3 WDCNN 网络定义与关键参数import torch.nn as nn import torch.nn.functional as F class WDCNN(nn.Module): def __init__(self, num_classes4): super(WDCNN, self).__init__() # 第一层宽卷积核抑制高频噪声 self.conv1 nn.Conv1d(1, 16, kernel_size64, stride8, padding28) self.bn1 nn.BatchNorm1d(16) self.pool1 nn.MaxPool1d(kernel_size2, stride2) # 后续小卷积核堆叠 self.conv2 nn.Conv1d(16, 32, kernel_size3, padding1) self.bn2 nn.BatchNorm1d(32) self.pool2 nn.MaxPool1d(kernel_size2, stride2) self.conv3 nn.Conv1d(32, 64, kernel_size3, padding1) self.bn3 nn.BatchNorm1d(64) self.pool3 nn.MaxPool1d(kernel_size2, stride2) self.conv4 nn.Conv1d(64, 64, kernel_size3, padding1) self.bn4 nn.BatchNorm1d(64) self.pool4 nn.MaxPool1d(kernel_size2, stride2) self.flatten nn.Flatten() # 全连接层输入维度需根据信号长度计算 self.fc nn.Linear(64 * 7, 100) self.fc_out nn.Linear(100, num_classes) def forward(self, x): x F.relu(self.bn1(self.conv1(x))) x self.pool1(x) x F.relu(self.bn2(self.conv2(x))) x self.pool2(x) x F.relu(self.bn3(self.conv3(x))) x self.pool3(x) x F.relu(self.bn4(self.conv4(x))) x self.pool4(x) x self.flatten(x) x F.relu(self.fc(x)) x self.fc_out(x) return x逻辑说明第一层kernel_size64, stride8是 WDCNN 的标志性设计大核加下采样能快速压缩数据量同时保留冲击成分。padding28是为了让输出长度不至于缩得太快具体数值要按输入长度算。后面每层卷积后接 BatchNorm 和 ReLUBatchNorm 在故障诊断里很关键能缓解不同负载下的分布偏移。全连接层的输入维度64 * 7是假设输入长度 1024 经过四次池化后的结果实际用的时候要打印一下x.shape确认这个数对不上是新手最常见的翻车点。3. 训练流程与 t-SNE 可视化把高维特征摊开看类别分没分开3.1 训练循环与学习率设置训练部分没有太多玄学但有几个参数值得注意。优化器用 Adam学习率初始设 1e-3如果损失震荡明显就降到 1e-4。批大小 64 或 128 都行太小会让 BatchNorm 不稳定。训练轮数一般 50 到 100 轮足够因为 WDCNN 参数量不大收敛很快。import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model WDCNN(num_classes4).to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3) for epoch in range(80): model.train() total_loss 0 for x, y in loader: x, y x.to(device), y.to(device) optimizer.zero_grad() logits model(x) loss criterion(logits, y) loss.backward() optimizer.step() total_loss loss.item() # 每 10 轮打印一次平均损失 if (epoch 1) % 10 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.4f})逻辑说明optimizer.zero_grad()必须放在前向传播之前否则梯度会累加。损失用交叉熵因为是多分类。如果发现训练损失降得很快但验证损失反弹说明过拟合了可以加 Dropout 或者减少全连接层神经元数量。我一般会在全连接层后面加nn.Dropout(0.5)但 WDCNN 原论文里没加所以看情况。3.2 t-SNE 可视化的实现细节t-SNE 的作用是把网络某一层的输出通常是全连接层之前的特征降到二维然后按类别上色。这里的关键是「取哪一层」取最后一层卷积后的特征还是取全连接层的输出常见做法是取全连接层fc的输出因为维度适中100 维t-SNE 跑起来快而且语义信息更集中。from sklearn.manifold import TSNE import matplotlib.pyplot as plt def extract_features(model, loader, device): model.eval() features [] labels [] with torch.no_grad(): for x, y in loader: x x.to(device) # 取全连接层后的特征 feat model.flatten(model.pool4(model.bn4(model.conv4( model.pool3(model.bn3(model.conv3( model.pool2(model.bn2(model.conv2( model.pool1(model.bn1(model.conv1(x))) ))) ))) )))) feat model.fc(feat) features.append(feat.cpu().numpy()) labels.append(y.numpy()) return np.concatenate(features), np.concatenate(labels) # 提取特征 feats, labs extract_features(model, loader, device) # t-SNE 降维perplexity 一般取 30太大容易糊成一团 tsne TSNE(n_components2, perplexity30, random_state42) feats_2d tsne.fit_transform(feats) # 绘图 plt.figure(figsize(8, 6)) for cls in np.unique(labs): idx labs cls plt.scatter(feats_2d[idx, 0], feats_2d[idx, 1], labelfClass {cls}, s10) plt.legend() plt.title(t-SNE Visualization of WDCNN Features) plt.savefig(tsne_result.png, dpi150)逻辑说明perplexity是 t-SNE 最重要的参数一般取 5 到 50数据点少就取小一点。random_state固定住是为了结果可复现不然每次跑出来的图都不一样没法对比。如果图上类别混在一起说明模型没学好要么是训练不够要么是数据本身有问题。如果类别分得很开但同一类内部散得太厉害可能是特征维度太高可以试试取池化后的特征而不是全连接层。3.3 评估指标与混淆矩阵光看 t-SNE 不够还得有量化指标。准确率、召回率、F1 值都要看尤其是类别不平衡的时候。混淆矩阵能看出哪两类容易混。from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns model.eval() all_preds [] all_labels [] with torch.no_grad(): for x, y in loader: x x.to(device) logits model(x) preds torch.argmax(logits, dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(y.numpy()) print(classification_report(all_labels, all_preds)) cm confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.savefig(confusion_matrix.png, dpi150)逻辑说明classification_report会输出每个类别的精确率、召回率和 F1 值重点看召回率低的类别那通常是难分的故障类型。混淆矩阵用热力图展示对角线越深越好。如果内圈和外圈混得厉害可能是切窗长度不够没能覆盖完整的冲击衰减过程。4. 避坑与排查轴承故障诊断里最容易翻车的五个地方4.1 现象训练准确率很高但 t-SNE 图上类别完全混在一起原因特征提取时取错了层。如果取的是最后一层卷积后的特征维度可能只有几十但空间信息已经被池化丢得差不多了t-SNE 压出来自然乱。解决改取全连接层fc的输出或者取fc之前的展平特征维度在 100 到 500 之间比较合适。4.2 现象模型在训练集上准确率 99%测试集只有 60%原因数据泄露。滑动切窗时如果先切窗再划分训练集和测试集同一个原始信号切出来的窗会同时出现在两边模型等于见过测试数据。解决先按原始信号文件划分训练集和测试集再分别切窗。这个坑我踩过不止一次血泪经验。4.3 现象损失变成 NaN训练直接崩掉原因学习率太大或者归一化没做对。如果某个窗的标准差接近零除以它就会产生巨大值。解决归一化时加一个极小值1e-8学习率从 1e-4 开始试不要一上来就 1e-2。4.4 现象t-SNE 跑得特别慢几分钟出不来图原因样本量太大。t-SNE 的时间复杂度是 O(n²)几万个点跑起来很吃力。解决从每个类别里随机采样 200 到 500 个点总共两三千个点就够了图上的分布趋势不会变。4.5 现象换一个工况的数据准确率断崖式下跌原因模型过拟合了当前工况的分布。WDCNN 虽然有一定鲁棒性但没做域适应的话跨工况性能有限。解决训练时加入不同工况的数据做数据增强或者在损失函数里加一个域对齐项。如果只是做 demo至少要在报告里说明这个局限性别把单一工况的结果吹成通用。5. 进阶技巧用混淆矩阵反推切窗参数怎么调最后一章说一个我常用的技巧不要凭感觉调切窗长度和步长用混淆矩阵反推。具体做法是固定模型结构只改window_size和stride跑几组对比实验看哪组参数下混淆矩阵的对角线最干净。窗长步长准确率内圈-外圈混淆数5122560.874210245120.9411204810240.9315从表里能看出来窗长 1024 时内圈和外圈混得最少。原因是内圈故障的冲击间隔和外圈故障的冲击间隔不同窗长太短覆盖不到一个完整周期太长又会引入相邻周期的干扰。这个表不用跑很多组三组就能看出趋势。还有一个技巧是看 t-SNE 图上同一类别的聚集程度。如果某一类散得特别开说明这类样本内部差异大可能是负载变化导致的。这时候可以在数据预处理阶段按负载做分层采样让每个 batch 里都有不同负载的样本BatchNorm 的效果会更好。从那以后我每次做故障诊断项目都会先跑一遍 t-SNE 看原始数据的分布再跑一遍模型特征的分布两张图对比着看心里才有底。希望帮到你。本文还有配套的精品资源点击获取