
简介这是一份面向深度学习初学者和故障诊断入门者的实操资源内容围绕数据预处理、模型搭建、模型训练三大环节展开完整演示了基于卷积神经网络等方法的故障识别流程也涉及传感器数据清洗、特征工程、训练调参与评估优化等细节。压缩包共57个文件包括40个mat数据文件、10个png可视化图、3个Python脚本、2个已训练h5模型和2份markdown说明文档整体大小46.13MB目录按数据、代码、结果等模块划分便于按步骤查阅。已有904人学习下载。读者可对照代码和说明复现完整流程理解CNN建模范式、优化器选择、正则化处理及准确率/召回率等指标含义并利用提供的数据集和模型进行二次开发是快速上手深度学习故障诊断的实用入门资料。 深度学习这几年在工业智能运维领域算是彻底扎下根了尤其是故障诊断这一块从原来以信号处理和专家特征为主的传统路子慢慢转向了数据驱动端到端学习的模式。经常有朋友问我说想入门深度学习故障诊断但不知道从哪下手——网上的资料要么偏理论讲一堆数学推导要么就是纯调包跑完一个demo也不知道每一步在干什么。我个人的建议是别一上来就啃 Transformer、图神经网络这些花活先把一条最经典、最完整的链路走通数据预处理 - 模型搭建 - 模型训练 - 结果评估。这条链路里踩过的坑、积累的经验基本能覆盖你后续做任何复杂工况诊断的底层逻辑。这篇文章我就拿滚动轴承故障诊断这个最典型的场景配合CWRU凯斯西储大学公开数据集把整个流程完整拆解一遍。不只是给代码更重要的是讲清楚每一步背后的为什么以及在实操中那些文档里不会写的细节。文章里的代码基于PyTorch框架硬件只要普通CPU就能跑通但如果你有GPU体验会更好。1. 为什么深度学习能直接做故障诊断从提特征到学特征的转变在讲具体实现之前我得先花点篇幅把这件事说透深度学习到底在故障诊断里解决了什么问题传统故障诊断的思路可以简单概括成信号处理人工特征分类器三步走。比如对轴承振动信号先做FFT快速傅里叶变换看频谱再提取均方根值、峰值因子、峭度这些时域统计量或者是包络谱里特定频带的能量然后丢给SVM、随机森林这类机器学习分类器去识别。这套方法不是不能用直到今天很多工业现场还在跑但它有个绕不开的痛点特征工程极度依赖专家经验。不同转速、不同负载、不同故障类型你提取的特征可能完全失效需要重新摸索。深度学习做的事情本质上是把特征提取这个最费专家的环节也交给网络自己去学。你把原始振动信号或者简单预处理后的信号直接喂给模型卷积层会自动从数据里学习到那些对区分故障类型有判别力的特征模式比如冲击成分的周期性、特定频带的能量分布等。拿CNN卷积神经网络来说它对振动信号的处理逻辑其实和图像识别有异曲同工之妙图像是二维像素网格卷积核去提取局部纹理特征振动信号是一维时序波形一维卷积核去提取局部波形形态特征。一维卷积在轴承故障诊断里特别好用的原因在于故障冲击在时域波形上表现为周期性的衰减振荡这种局部形态特征恰好是一维卷积核最擅长捕捉的模式。有人可能会问既然深度学习这么强为什么工业落地还是有很多阻力我的体会是问题大多不在模型本身而在数据质量和工程落地环节。实验室数据干净、工况单一测试集和训练集分布一致准确率轻松99%。但现场数据有负载波动、转速波动、强噪声干扰、标签缺失等各种情况模型很容易水土不服。所以入门阶段你就要养成一个习惯不要只看测试集准确率要关注模型的泛化能力和鲁棒性。这也是我后面在数据预处理和训练部分反复强调的东西。2. 数据预处理这一步没做好后面全都白搭数据预处理在故障诊断里的重要性怎么说都不为过。很多人把精力都放在调模型结构上结果发现准确率上不去回头查才发现是数据划分泄漏了、数据没做标准化、或者滑窗截取的时候把同一段数据既放训练集又放测试集了。这些坑每一个我都踩过下面逐个说清楚。2.1 CWRU数据集怎么下载和理解CWRU轴承数据中心数据集算是故障诊断领域的MNIST几乎所有论文都会在上面做验证。它的实验台结构大致是电机驱动轴带动轴承旋转通过电火花加工在轴承上人为制造单点故障损伤直径有0.007英寸、0.014英寸、0.021英寸等几种然后用加速度传感器采集振动信号。关于CWRU数据集有几点你需要特别留意采样频率驱动端加速度计数据有12kHz和48kHz两种采样率可选12kHz用的最多。工况划分包含四种负载0、1、2、3马力对应转速大概在1730、1750、1772、1797 r/min左右。入门阶段你可以先只用一种负载比如0负载跑通流程后续再加不同负载做跨工况泛化实验。故障位置有滚动体故障B、内圈故障IR、外圈故障OR、正常N四类。外圈故障还要注意是几点钟方向比如6:00不同方向的振动传递路径不同对诊断结果会有影响。文件格式.mat文件用scipy.io.loadmat读取即可。2.2 滑窗采样把长信号切成样本原始振动数据是一段很长的连续信号比如几秒甚至几十秒你不可能把整段信号作为一条样本送进网络——太长了而且计算量巨大。标准的做法是滑窗采样用一个固定长度的窗口比如1024个点或2048个点按照一定的步长在原始信号上滑动每滑一次就截取一段作为一个样本。窗口长度我建议从1024或2048起步。理论上讲一个窗口至少应该包含几个完整的故障冲击周期才能让模型学到周期性特征。轴承故障特征频率通常在几十赫兹到几百赫兹之间12kHz采样率下2048个点对应约0.17秒的时间长度足够覆盖多个冲击周期了。采样步长的选择上如果步长等于窗口长度那就是无重叠切分相邻样本完全独立步长小于窗口长度则是重叠切分样本量可以翻好几倍。我推荐用重叠率75%甚至更高来做数据增强即步长取窗口长度的四分之一。但这里有个陷阱我必须提醒你重叠切分会造成相邻样本高度相似如果你在划分训练集和测试集时不小心把它们分到了两边那评估结果会虚高得离谱——重度数据泄漏。下面我会专门讲这个问题。2.3 训练集/测试集划分最容易泄漏的地方这是入门者最容易犯、影响最严重的一个错误。假设你把一段10秒钟的信号切成了1000个样本然后直接 random split 成训练集800个、测试集200个。由于样本之间有重叠训练集里某个样本的绝大部分波形内容可能和测试集里某个样本几乎一模一样——模型在训练时已经背过这段波形了测试时当然认得。最终准确率可能飙到99.5%但模型真正接触到一个全新的、没有重叠的数据段时性能会大幅下降。正确的做法是按时间段划分先把原始连续信号按时间顺序分成相邻的几大段比如前70%的时间段拿来切训练样本后30%的时间段拿来切测试样本。这样训练集和测试集在时间上完全不重叠即便做滑窗重叠也不会出现信息泄漏。这是模拟真实场景的基本功——现场故障诊断要面对的是未来的数据你训练时的信息不能提前剧透给测试环节。2.4 标准化要不要做怎么做深度学习中数据标准化基本是标配。振动信号的幅值范围可能在不同工况、不同传感器下有差异如果不做标准化模型训练时某些特征尺度大的维度会主导梯度更新导致收敛变慢或陷入局部最优。具体做法有两种Z-score标准化对每个样本减去均值除以标准差。这种做法在故障诊断里比较常用因为振动信号本质上是零均值的周期性信号减去均值不会损失有效信息。Min-Max归一化把幅值映射到[0,1]区间。这种做法在有明显冲击成分的信号上容易被少数极大值点主导我个人对振动信号更推荐Z-score。注意统计量只能用训练集的均值方差测试集要和训练集用同一套统计量去变换不能各自算各自的。这个细节我见过很多次被忽略直接导致数据分布不一致模型评估失真。3. 模型搭建一维CNN是入门首选但别只盯着结构创新3.1 一维CNN为什么适合处理振动信号振动信号本质是时序波形处理这类数据最自然的思路是RNN/LSTM这类循环神经网络。但在我实际对比测试下来一维CNN在绝大多数轴承故障诊断任务上效果比LSTM好而且训练速度快得多。原因在于LSTM更适合捕捉长距离时序依赖而故障冲击特征在短时窗内就已经表现得非常明显了一维卷积层可以并行计算在提取局部波形特征上效率极高。CNN处理一维信号的原理细节上一维卷积核沿着时间轴滑动每次都和一小段波形做加权求和可以理解成一组可学习的滤波模板在训练过程中逐步调整自己的频率响应特性最终那些能有效激活特定故障模式的模板会保留下来。多个卷积层叠加起来低层提取细粒度波形形态高层组合出更抽象的模式这跟传统方法里先滤波再提包络特征的想法是不谋而合的只不过CNN把这一整套滤波器的设计和组合全部端到端地学出来了。3.2 一个简洁实用的CNN结构我给的示例结构不算复杂但经过大量实验验证在CWRU上效果很稳定import torch import torch.nn as nn class FaultDiagnosisCNN(nn.Module): def __init__(self, num_classes4): super().__init__() self.features nn.Sequential( # 第一层1024点输入40个卷积核卷积核大小为16步长4 nn.Conv1d(1, 16, kernel_size32, stride4, padding15), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), # 第二层40 - 80 个卷积核 nn.Conv1d(16, 32, kernel_size16, stride2, padding7), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), # 第三层80 - 160 个卷积核 nn.Conv1d(32, 64, kernel_size8, stride2, padding3), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1), # 全局平均池化 ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(64, 64), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(64, num_classes) ) def forward(self, x): return self.classifier(self.features(x))这个结构的设计逻辑每层逐渐减半特征长度、翻倍通道数这是图像CNN里经典的设计范式——空间分辨率下降但特征丰富度上升在不显著增加计算量的条件下让网络表达能力更强。BatchNorm放在激活之前先对卷积输出做归一化再激活能有效缓解训练过程中内部协变量偏移问题你可以把它理解成每个卷积层后面加了自动校准让数据分布一直在合理范围内流向下层。我在实验里发现不加BN的版本在CWRU上准确率大约会掉2-4个百分点而且训练波动明显更大。AdaptiveAvgPool1d(1)不管前面的特征图长度是多少都压成1个值每个通道这样全连接层的输入维度就和输入信号长度解耦了。以后你换不同窗口长度512、2048、4096时不用改模型结构这个技巧在实战中非常实用。Dropout(0.5)全连接层是参数最多的地方最容易过拟合Dropout按50%概率随机丢弃神经元等价于训练了多个子网络的集成。你可能会问这个模型算不算创新坦率说不算。但入门阶段追求的不是结构创新而是把标准组件在故障诊断场景下用对、用稳。后面你如果想深入研究可以尝试引入注意力机制、残差连接、多尺度卷积等思路但要先确保基本功扎实。4. 模型训练超参数、损失函数和训练流程的细节4.1 训练配置的完整代码数据准备好、模型写好后训练代码这块的逻辑其实比较固定。但有几个地方我想特别说明import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # 假设 train_x, train_y, test_x, test_y 已经准备好了 # 转换数据类型深度学习框架默认用float32 train_x_tensor torch.FloatTensor(train_x) # shape: [N, 1, 1024] train_y_tensor torch.LongTensor(train_y) test_x_tensor torch.FloatTensor(test_x) test_y_tensor torch.LongTensor(test_y) # 构建DataLoader train_dataset TensorDataset(train_x_tensor, train_y_tensor) test_dataset TensorDataset(test_x_tensor, test_y_tensor) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers0) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse, num_workers0) # 初始化模型、损失函数、优化器 device torch.device(cuda if torch.cuda.is_available() else cpu) model FaultDiagnosisCNN(num_classes4).to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max100, eta_min1e-5) # 训练循环 num_epochs 100 best_acc 0.0 for epoch in range(num_epochs): model.train() running_loss 0.0 correct 0 total 0 for inputs, labels in train_loader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() * inputs.size(0) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() train_acc correct / total train_loss running_loss / len(train_dataset) # 每个epoch做一次测试集验证 model.eval() test_correct 0 test_total 0 with torch.no_grad(): for inputs, labels in test_loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) _, predicted torch.max(outputs, 1) test_total labels.size(0) test_correct (predicted labels).sum().item() test_acc test_correct / test_total scheduler.step() # 保存最优模型 if test_acc best_acc: best_acc test_acc torch.save(model.state_dict(), best_model.pth) if (epoch 1) % 10 0: print(fEpoch [{epoch1}/{num_epochs}], Loss: {train_loss:.4f}, fTrain Acc: {train_acc:.4f}, Test Acc: {test_acc:.4f}) print(fBest Test Accuracy: {best_acc:.4f})4.2 损失函数为什么用交叉熵故障诊断本质上是个多分类任务交叉熵损失函数是这个场景下的标准选择。它做了两件事一是通过softmax把模型输出的logits转换成概率分布二是衡量预测概率分布和真实标签one-hot分布之间的差异。交叉熵数值越小说明模型对真实类别的预测概率越接近1。和均方误差MSE做损失函数相比交叉熵在分类任务上有显著优势MSE配合softmax容易出现梯度饱和即模型输出越接近0或1时梯度越接近0导致训练早期收敛极慢交叉熵在所有输出区间内都能提供足够的梯度信号训练更稳定。我在实验里实测同样的模型结构用MSE做损失函数的收敛速度大约慢两倍最终准确率还低几个点。4.3 优化器和学习率Adam是万金油但别忽视weight_decayAdam优化器在大多数深度学习任务里都是开箱即用的选择它结合了动量和自适应学习率的优点对学习率的选择没有SGD那么敏感。入门阶段直接用Adamlr设为1e-3基本不会出大问题。但这里有个容易被忽视的参数weight_decayL2正则化系数。它做的事是对过大的权重参数施加惩罚防止模型过于依赖某些特征维度从而抑制过拟合。在故障诊断这种样本量不是特别大的场景下weight_decay设为1e-4到1e-3之间能明显提升测试集上的稳定性。具体多合适建议你跑一组对比实验自己观察一下。再说学习率调度。我上面用了余弦退火CosineAnnealingLR它的思路是让学习率从初始值按余弦曲线周期性地降到较低值然后再恢复。这样做的好处是训练初期学习率较大快速跨过陡峭区域后期学习率降下来在损失曲面底部精细搜索。对比固定学习率余弦退火在CWRU数据上一般能让最终准确率再提升1-2个百分点而且对初始学习率的选择更鲁棒。4.4 训练中怎么判断模型是不是正常训练过程中我一般会同时盯着四个指标训练损失、训练准确率、测试准确率、以及训练和测试的差距。它们之间的配合关系能告诉你很多信息训练损失持续下降测试准确率也同步上升健康状态继续跑。训练损失下降但测试准确率停滞甚至下降过拟合的信号可以加大weight_decay、增加Dropout比例或增加训练样本量适当增大滑窗重叠率。训练损失从一开始就不怎么降可能是学习率太大或数据有问题先换个更小的lr试试。训练准确率高但测试准确率低数据泄漏或过拟合的概率比较大优先检查数据划分逻辑。我自己踩过的一个大坑是为了赶实验进度把训练epoch设得特别大比如300轮结果模型在150轮时已经过拟合了后面150轮纯粹是在死记硬背。后来我基本都会开启早停Early Stopping连续20个epoch测试集准确率没有提升就停止训练并恢复到best模型。上面的代码里我保存了最优checkpoint就是为这个准备的。5. 结果评估准确率之外还要会看混淆矩阵5.1 为什么只用准确率不够训练结束后大多数人看一眼测试集准确率超过95%就觉得可以交差了。但在故障诊断这个场景下准确率远远不够。原因很简单不同故障类型的经济代价完全不同。如果模型把正常样本误判成故障产线顶多停机检查一下、虚惊一场但如果把内圈故障误判成正常那可能导致设备带病运行到彻底损坏引发非计划停机甚至安全事故。所以你需要看得更细。混淆矩阵Confusion Matrix是最直观的诊断工具行是真实类别列是预测类别。它能把模型在每一类上的表现拆开看。比如在CWRU四分类任务中你可能发现模型对滚动体故障B和正常N区分得很好但在内圈故障IR和外圈故障OR之间存在一些混淆——这其实有物理背景因为内外圈故障特征在早期阶段信号形态差异本来就不大。5.2 混淆矩阵示例代码import numpy as np from sklearn.metrics import confusion_matrix, classification_report import matplotlib.pyplot as plt def evaluate_model(model, test_loader, device): model.eval() all_preds [] all_labels [] with torch.no_grad(): for inputs, labels in test_loader: inputs inputs.to(device) outputs model(inputs) _, predicted torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.numpy()) # 计算混淆矩阵 cm confusion_matrix(all_labels, all_preds) print(Confusion Matrix:) print(cm) print(\nClassification Report:) print(classification_report(all_labels, all_preds, target_names[Normal, Ball, Inner, Outer])) # 可视化混淆矩阵 fig, ax plt.subplots(figsize(8, 6)) im ax.imshow(cm, interpolationnearest, cmapplt.cm.Blues) ax.figure.colorbar(im, axax) classes [Normal, Ball, Inner, Outer] ax.set(xticksnp.arange(cm.shape[1]), yticksnp.arange(cm.shape[0]), xticklabelsclasses, yticklabelsclasses, ylabelTrue Label, xlabelPredicted Label) # 在单元格中标注数字 thresh cm.max() / 2. for i in range(cm.shape[0]): for j in range(cm.shape[1]): ax.text(j, i, format(cm[i, j], d), hacenter, vacenter, colorwhite if cm[i, j] thresh else black) fig.tight_layout() plt.savefig(confusion_matrix.png, dpi150) plt.show()5.3 运行结果怎么解读以我自己跑的一组实验为例CWRU四分类0负载2048窗口长度75%重叠测试集准确率大约在98.7%左右。混淆矩阵大致看下来Normal类几乎没有误判Ball类有极少数样本被误判为InnerInner和Outer之间有个别混。这个结果在入门实验里已经算不错了但实战中如果出现类似某两类频繁互相混淆的情况我的排查思路是这样的先看原始信号波形这两类故障的时域波形特征到底差异在哪是不是冲击间隔、幅值包络形态比较接近再看频域特征做一下包络谱看看特征频率及其谐波是否确实容易混淆。最后决定怎么改进如果数据本身可分性差可以考虑换更长的窗口包含更多冲击周期、加注意力机制强化关键频段或者增加通道数让网络捕捉更细的差异。我一直强调深度学习故障诊断不是模型跑完就结束你要能解释模型为什么误判才能在实际部署中建立对它的信任。6. 进阶方向与常见问题速查到这里一条从数据到模型到评估的完整链路已经走通了。但我知道很多人接下来还会遇到各种问题这里我把高频问题和我自己的解决思路整理一下希望能帮你少走一些弯路。6.1 换数据集后效果暴跌怎么办CWRU上跑到98%换到自己的实验台数据准确率掉到80%这种情况太常见了。核心原因有三个数据分布差异CWRU是实验室环境、恒定转速、电火花人工故障现场数据有转速波动、负载波动、环境噪声模型没见过这种分布。样本量不足现场故障样本永远是稀缺的可能一种故障只有几分钟数据切出来也就几十个样本。标签噪声现场标签依赖人工标注存在漏标、错标。我的建议是第一用迁移学习的思路把在CWRU上预训练的模型当作特征提取器只微调最后一两层这样能保留通用的振动特征提取能力第二引入数据增强对原始信号做时间偏移、加噪声、幅值缩放提升模型的泛化能力第三如果样本实在太多类又太少可以考虑用元学习或者小样本学习的方法但这属于进阶方向入门阶段先把基本功打牢。6.2 要不要用更复杂的模型经常看到有人一上来就想用Transformer、图神经网络、Informer这些高级货。我的看法是在CWRU这类公开数据集上简单CNN往往已经能达到98%的准确率复杂模型带来的提升极其有限但训练成本和调参难度却大幅上升。除非你的实验目标是验证某种新方法的有效性或者你的数据复杂到简单模型确实无法收敛否则我不建议入门阶段在模型结构上过度投入。更值得投入的方向是把数据工程做好——更合理的数据划分、更充分的增强策略、更贴近真实场景的实验设计。真要追求模型效果先从残差连接、注意力机制这些小而稳的改动入手而不是直接上大而全的架构。6.3 epoch过拟合问题应该怎么设置在CWRU上100个epoch配合CosineAnnealingLR一般足够收敛甚至开始过拟合了。但你自己的数据可能需要更少的epoch因为样本量小或者更多的epoch因为数据复杂度高。一个实操办法是先用训练损失连续5个epoch不再下降作为停止训练的条件打印出最佳checkpoint对应的测试结果如果你发现最佳结果是出现在最后一个epoch那就说明训练量不够可以考虑加大epoch上限。6.4 硬件要求真那么高吗很多人被深度学习一定要GPU这个印象劝退。实际上CWRU这种小型数据集配合轻量CNNCPU上用纯训练也就在几分钟到十几分钟内跑完一个epoch完全可接受。我的建议是入门阶段CPU完全够用先把流程跑通、把原理吃透等你真的要跑大规模数据或者大模型了再考虑云GPU平台或者本地显卡。工具从来不是瓶颈认知才是。我至今还记得自己第一次在CPU上把CNN跑通、看到测试准确率跳到97%以上的那个瞬间。回过头来看入门深度学习故障诊断真正宝贵的不是那几个点的准确率数字而是你掌握了这套数据-模型-训练-评估闭环方法论它会在你面对完全陌生的设备、陌生的数据时给你一条清晰的解决问题的路径。希望这篇分享能给你一些可落地的参考。本文还有配套的精品资源点击获取