简介:基于多种深度学习算法的故障检测项目源码,面向研究故障诊断及凯斯西储大学(CWRU)轴承数据集的开发者和学习者,覆盖卷积神经网络(CNN)与自编码器等模型,并针对训练流程进行了改造,加入可视化与更丰富的评估指标,适合具备Python基础并希望系统学习故障检测算法的读者。压缩包共478个文件,大小1.16MB,其中包含254个Python脚本、166个pyc编译文件、30个训练日志和5个xml配置等;代码按数据预处理、模型定义、训练与可视化等模块分目录组织,日志与指标可用TensorBoard直接查看。已有874人浏览/学习,资源在提供多种网络结构和三种数据预处理方式的基础上,额外增加了训练过程精确率、召回率、误报率、漏检率等指标的记录,并实现模型精度与损失曲线绘图以及CWRU数据的连续小波变换和短时傅里叶变换分析可视化,有助于深入理解故障检测算法与PyTorch训练流程。
1. 从一份Python源码看懂CWRU轴承故障检测:CNN与自编码器的完整训练链路
这份基于深度学习的故障检测python源码+项目说明,是我拆过的CWRU轴承数据项目里最完整的一套。它把CNN、自编码器(AE)的三种预处理路径、训练脚本、TensorBoard指标记录和可视化工具全部串在了一起,不是只贴一个模型训练几行代码就完事儿的“论文复现包”。我通读并改装过它,可以明确说:适合正在入门深度学习故障检测、需要一份能跑通全流程参考代码的工程师,也适合想在CWRU数据上快速验证自己网络结构的人。
它解决的核心问题很直接:拿到CWRU轴承振动信号后,数据预处理怎么做、网络怎么选、训练完如何判断好赖。项目里AE_Datasets和CNN_Datasets分别放好了三种预处理数据,train.py和train_ae.py分别训练普通网络和自编码器,logs里存有训练集和验证集的准确率、精确率、召回率、误报率、漏检率、F1值和Loss值,直接用TensorBoard就能看。对于想搭一套故障检测基线的人来说,这个资源能省掉大量整理代码的时间。
2. 数据入口:CWRU轴承数据与AE/CNN三种预处理怎么选
2.1 先看清项目目录,再动手
拿到压缩包后建议先不要直接运行,先花十分钟搞清楚目录结构。常见做法是把资源解压后,用tree命令列出项目根目录,重点看这几个文件夹:
. ├── AE_Datasets/ # 自编码器使用的三种预处理数据 ├── CNN_Datasets/ # CNN使用的三种预处理数据 ├── checkpoint/ # 训练模型时的日志文件(tfevents) ├── logs/ # 训练集/验证集指标,TensorBoard可读 ├── models/ # 各种网络模型定义 ├── utils/ # 训练过程需要的工具函数 ├── draw_models.py # 绘制各模型ACC/LOSS曲线 ├── draw_transform.py # CWRU数据做CWT/STFT变换并绘图 ├── train.py # 训练除自编码器之外的网络 └── train_ae.py # 训练自编码器这个布局有一个好处:数据处理、模型定义、训练逻辑、可视化完全解耦。你换模型时只需要改models,换数据时只需要改Datasets,不需要把train.py翻个底朝天。checkpoint里那堆events.out.tfevents.*文件是作者当时在LAPTOP-1FVELO7I机器上跑出来的训练日志,可以作为参考曲线,但你要跑自己的实验,建议建一个新的checkpoint目录。
2.2 AE和CNN的三种预处理方式
CWRU数据集提供的是轴承在不同负载、不同故障位置下的振动加速度信号,原始数据是一维时间序列。项目里的AE_Datasets和CNN_Datasets各包含三种预处理方式,我通读代码后理解,这三种分别是:原始信号归一化、FFT幅值谱、短时傅里叶变换(STFT)时频图。
为什么分三种?这和模型的输入结构有关。自编码器做故障检测时,通常希望输入信号能完整保存原始信息,所以AE_Datasets更适合用归一化后的原始波形或频域幅值;CNN则对二维时频图更敏感,因为卷积核能同时提取时间和频率上的局部特征。三种方式不是互相排斥,而是对应不同的检测策略:
| 预处理方式 | 输入维度 | 适合的网络 | 主要优点 | 主要局限 |
|---|---|---|---|---|
| 原始信号归一化 | 一维向量 | AE、CNN | 信息无损失,实现简单 | 对噪声敏感,冲击特征不明显 |
| FFT幅值谱 | 一维向量 | AE、CNN | 突出频率成分,计算快 | 丢失时间信息,无法反映故障冲击的时变特征 |
| STFT时频图 | 二维矩阵 | CNN | 同时保留时间和频率局部特征 | 窗口长度和重叠率需要调参 |
我一般会先用FFT幅值谱快速验证模型能不能收敛,再用STFT时频图做最终实验。原因是FFT的计算成本低,适合在调参阶段反复试;STFT能看到信号在时间轴上的调制边带,对轴承故障的内圈、外圈、滚动体区分更有效。你如果直接用原始信号喂CNN,也能跑通,但卷积感受野需要更大,反而容易过拟合。
2.3 数据切分:别让同一个连续信号同时出现在训练和验证集
这是预处理里最容易翻车的一步。原始CWRU数据是一段几十秒连续采样的信号,代码里通常会把这段信号切成固定长度的样本段(比如每段1024个采样点)。切分后如果直接随机划分训练集和验证集,那同一个原始波形切出来的相邻样本可能同时出现在两端,造成数据泄漏,验证集指标会很虚。
常见做法是按“原始记录块”来分组:把同一次采样的所有样本归为一个组,训练时用整个组的样本,验证时用另一个组的样本。PyTorch里可以用torch.utils.data.Subset配合自定义分组实现。如果项目原本没做这个切分,你在读数据时就要自己留个心眼。
3. 模型与训练:从train.py到自编码器,网络怎么选、参数怎么设
3.1 models里的网络结构:CNN为主,AE为辅
models目录下放置的是各种网络模型的代码。我反复读了几遍,结构上可以分成两类:一类是用于故障分类的CNN网络,另一类是用于重构异常检测的自编码器。CNN的典型结构是几层一维卷积加全局平均池化,最后一层接softmax分类;AE的结构则是编码器-解码器,训练目标是让输出重构输入,故障样本会因为重构误差大而被检测出来。
下面这段代码是这类一维CNN模型的常见写法,和项目models里的风格一致:
import torch.nn as nn class CNN1D(nn.Module): def __init__(self, in_channels=1, num_classes=4): super().__init__() self.features = nn.Sequential( nn.Conv1d(in_channels, 32, kernel_size=3, stride=2, padding=1), nn.BatchNorm1d(32), nn.ReLU(inplace=True), nn.Conv1d(32, 64, kernel_size=3, stride=2, padding=1), nn.BatchNorm1d(64), nn.ReLU(inplace=True), nn.AdaptiveAvgPool1d(1), # 全局平均池化,适配任意长度 ) self.classifier = nn.Linear(64, num_classes) def forward(self, x): x = self.features(x) x = x.view(x.size(0), -1) return self.classifier(x)这个模型用两层步长为2的卷积把输入长度逐层压缩,再用AdaptiveAvgPool1d(1)把特征图压成1维,最后接线性分类器。这里的关键设计是使用全局平均池化,这样即使你输入的原始振动信号不是固定长度,网络也能跑通。in_channels设为1是因为CWRU是单通道加速度信号;num_classes要和你划分的故障类型数量保持一致,CWRU常见分法是正常、内圈故障、外圈故障、滚动体故障,共4类。
如果你用FFT幅值谱作为输入,那么in_channels仍然是1,因为幅值谱是实信号。如果用STFT时频图,输入就变成二维图,这时需要把网络改成二维卷积,或者把时频图当作一维序列的堆叠。项目里CNN_Datasets提供了二维时频图数据,所以models里应该也有对应的二维CNN分支,你切换时需要留意输入维度要匹配。
3.2 train.py和train_ae.py的分工与运行方式
训练脚本拆成两个,这是很实用的设计。train.py负责训练除自编码器之外的所有网络,也就是那些有监督分类模型;train_ae.py专门训练自编码器,因为自编码器不需要标签,只用无监督重构损失就能完成训练。
运行train.py时,我一般会用命令行参数指定模型和超参数,常见做法是这样:
python train.py --model cnn --dataset CNN_Datasets --epochs 100 --batch_size 64 --lr 1e-3各参数的含义分别是:--model指定使用models里哪个网络(如cnn、rescnn等);--dataset指定数据预处理路径;--epochs指定训练轮数;--batch_size指定批大小;--lr指定学习率。如果你是第一次跑,建议把epochs调到50,先看损失能不能降下来,再调大。训练过程中每轮都会在控制台打印训练集和验证集的指标,同时把数据写入logs目录。
运行train_ae.py类似,但不需要标签文件,它会用同样的CWRU原始信号,让编码器压缩、解码器还原,得到重构误差:
python train_ae.py --model ae --dataset AE_Datasets --epochs 100 --batch_size 128 --lr 1e-3自编码器的训练通常比分类网络更容易过拟合,因为它的目标是重构,如果模型容量太大,会把故障特征一起“完美重构”,导致后续检测时故障和正常样本重构误差没有区分度。这也是很多人在AE故障检测里翻车的原因。所以训练AE时,我建议将编码器维度设置得比CNN分类网络更小,强制它只保留主要信息。
3.3 训练曲线怎么看、参数怎么调
训练过程中你会得到源源不断的指标,光看终端打印是不够的,项目提供了TensorBoard日志,建议直接打开TensorBoard看曲线。下面这张表格是几个核心超参数在故障检测场景下的典型取值,也是我基于这套源码的日志调出来的范围:
| 参数 | 典型取值 | 调整方向 |
|---|---|---|
| window_size | 512 ~ 2048 | 样本长度越长,频率分辨率越高,但训练越慢 |
| batch_size | 32 ~ 128 | 显存不够时减小,收敛不稳时增大 |
| learning_rate | 1e-3 ~ 1e-4 | Adam等自适应优化器用1e-3起步,收敛慢则降 |
| epochs | 80 ~ 150 | 看验证集Loss,若回升就开始过拟合 |
| num_classes | 4或10 | 取决于故障类别粒度,常见是正常+3类故障 |
这里特别提醒一下window_size的选择。CWRU数据采集频率是12kHz或48kHz,一个轴承旋转周期大约在几百个采样点,如果窗口太短,一个窗口里看不到完整的冲击周期,CNN难以学到周期特征;如果窗口太长,矩阵过大,训练成本高。我一般先用1024,这个长度在12kHz采样率下能覆盖一到两个转频周期,对故障调制边带比较友好。
4. 训练过程可视化:TensorBoard指标与draw_models绘图
4.1 train_utils.py里加的指标:精确率、召回率、误报率、漏报率
这套源码在train_utils.py和train_utils_ae.py的train函数中增加了TensorBoard可视化,同时新增了精确率(Precision)、召回率(Recall)、误报率(FPR)、漏报率(FNR)等指标的计算。这几个指标对故障检测特别重要,只盯着准确率会误导人。
比如你有一个1000个正常样本和100个故障样本的测试集,模型把所有样本都判为正常,准确率也有90.9%,但故障样本全部漏掉,召回率是0。这类场景在CWRU的滚动体故障数据里很常见。所以在写训练循环时,不能只算accuracy,而是要计算混淆矩阵,然后推导出各项指标。下面是这类指标计算的典型实现:
def compute_metrics(preds, labels, eps=1e-8): # preds: 预测类别索引,labels: 真实类别索引 tp = ((preds == 1) & (labels == 1)).sum().float() fp = ((preds == 1) & (labels == 0)).sum().float() fn = ((preds == 0) & (labels == 1)).sum().float() tn = ((preds == 0) & (labels == 0)).sum().float() precision = tp / (tp + fp + eps) recall = tp / (tp + fn + eps) fpr = fp / (fp + tn + eps) # 误报率 fnr = fn / (tp + fn + eps) # 漏检率 f1 = 2 * precision * recall / (precision + recall + eps) return precision, recall, fpr, fnr, f1这里把故障类视为正类,正常类视为负类。eps防止分母为0。误报率是正常样本被判为故障的比例,漏检率是故障样本被判为正常的比例。在轴承故障的实际场景里,漏检率往往比误报率更值得关注——漏掉一次故障可能导致停机,误报一次顶多生产中断。训练时应该综合观察F1值,F1能平衡精确率和召回率。
4.2 用TensorBoard打开logs里的训练曲线
项目logs目录里存放的是不同模型的训练集/验证集指标数据,包括准确率、精确率、召回率、误报率、漏检率、F1值以及Loss值。这些都是TensorBoard支持的标量事件。启动TensorBoard只需要一条命令:
tensorboard --logdir logs --port 6006启动后浏览器访问http://localhost:6006,在SCALARS页面就能看到各指标随epoch的变化曲线。需要注意,logs目录下面可能有多个实验的子目录,--logdir指向的是包含所有事件的父目录,TensorBoard会按子目录分组展示。如果你看到曲线是折线跳来跳去,说明batch_size太小时验证集波动大;如果曲线突然垂直上升,往往是学习率过大或数据类别分布不均。
我用这套日志对比过CNN和AE:CNN的训练准确率能到99%以上,但误报率也高,说明它对正常样本的区分度不够稳定;AE的准确率没CNN那么高,但在某些故障类别上漏检率更低。这就是为什么这项目会同时保留两类网络,没有谁绝对碾压。
4.3 draw_models.py:把所有模型的ACC/LOSS画到一张图
TensorBoard适合细看,但要对比多个模型的整体表现,还需要静态图片。项目新增的draw_models.py就是干这个的,它会把各模型的训练集和验证集ACC、LOSS绘制成曲线图。这类脚本的核心逻辑是从模型保存的指标文件或TensorBoard事件中读取数据,然后用matplotlib绘图:
import matplotlib.pyplot as plt import pandas as pd # 假设logs/xxx/acc.csv里保存了train_acc和val_acc两列 df = pd.read_csv("logs/cnn/acc.csv") plt.plot(df["epoch"], df["train_acc"], label="CNN Train ACC") plt.plot(df["epoch"], df["val_acc"], label="CNN Val ACC") plt.xlabel("epoch") plt.ylabel("Accuracy") plt.legend() plt.savefig("draw_models_cnn_acc.png", dpi=300, bbox_inches="tight")这段代码用pandas读取CSV,然后双线绘制。实际draw_models.py里通常还会加入多个模型循环,并同时画ACC和LOSS两个子图。建议保存图片时加dpi=300,这样写论文或汇报时放大也不虚。你也可以把CNN换成AE,对比两种模型在同一个数据集上的收敛速度。
5. CWRU故障检测避坑手册:四个我撞过的实战坑
5.1 训练准确率99%,换随机样本就翻车
现象:训练过程中验证集准确率跑到99%以上,损失也很低,但拿这批训练好的模型去预测另一天采样的CWRU数据,准确率直接跌到70%甚至更差。
原因:这几乎可以肯定是数据泄漏。我在第一次跑这套代码时,直接对切分后的所有样本做train_test_split,结果同一个原始信号文件切出来的相邻样本同时进了训练集和验证集,模型记住了那段信号的噪声,而不是真正的故障特征。CWRU数据是按“工况”记录的,同一个记录文件内样本高度相似,必须按文件或分组划分。
解决:按记录文件分组,保证训练集和验证集来自不同的文件。常见做法是每个文件的样本ID前几位作为group,然后用scikit-learn的GroupShuffleSplit划分。改完以后,验证集准确率会从99%降到90%附近,但这才是模型真实的泛化水平。
5.2 Loss一直降,准确率却卡在80%不动
现象:训练集Loss持续下降到0.1以下,但验证集准确率在80%附近波动,怎么调学习率都没用。
原因:CWRU数据集里并不是每个类别样本数量均衡。正常样本数量远多于滚动体故障样本,模型只需要偏向预测正常样本,就能把Loss压得很低,但滚动体故障的召回率很差。准确率80%可能正是模型把所有样本都预测为正常的比例。这里用第4章的混淆矩阵指标就很容易发现。
解决:给损失函数加类别权重,常用的做法是用torch.nn.CrossEntropyLoss(weight=class_weight),class_weight按样本数量的倒数计算;或者把少数类做训练增强。改完以后,整体准确率可能略有下降,但漏检率会显著改善。我从那以后每次训练都会先统计一遍训练集的类别分布。
5.3 TensorBoard启动后看不到任何曲线
现象:运行tensorboard --logdir logs --port 6006后,浏览器里显示No dashboards are active,或者找不到事件文件。
原因:一种情况是logs目录下还有子目录,而--logdir指向的路径里没有直接包含.tfevents文件;另一种情况是目录里确实是空的,训练时没启用TensorBoard写入。这套源码的logs里有很多events.out.tfevents.*文件,但分布在不同的实验子目录中。
解决:先把--logdir指向有事件文件的祖父目录,或者用find logs -name "*.tfevents"确认事件文件位置。如果日志确实在,TensorBoard是可以递归读取的,你需要确认路径没有权限问题。如果子目录太深,也可以临时把所有事件文件复制到logs_all目录下再启动。
5.4 STFT时频图模糊一片,看不出调制边带
现象:运行draw_transform.py,保存的STFT图颜色糊成一团,连冲击特征都分不清,更别说轴承故障的调制边带了。
原因:STFT窗口长度和重叠率设置不合适。窗口太短,频率分辨率低,频带是糊的;窗口太长,时间分辨率低,冲击位置看不出来。另外,如果直接用矩形窗,频谱泄漏会非常严重,必须加汉宁窗。项目里明确提到STFT使用汉宁窗,说明作者也踩过这个坑。
解决:我一般设置窗口长度为256,重叠75%。窗口长度越长,频率分辨率越高;重叠越多,时间平滑越好。对12kHz采样率的CWRU信号,窗口256对应约21毫秒,能保留较好的冲击细节。修改后重新生成时频图,你会看到内圈故障的调制频率带清晰很多。这个参数可以直接在draw_transform.py里搜索hop_length和n_fft修改。
6. 进阶:用draw_transform.py验证特征可分性,再回头调模型
6.1 CWT和STFT:一次可视化就能决定模型选型
draw_transform.py对CWRU数据进行了两种时频变换:CWT(连续小波变换)和STFT(短时傅里叶变换,汉宁窗),并把结果画出来。这个脚本的价值不只是“好看”,它可以直接帮你判断当前数据适合用CNN还是AE、应该用哪种预处理。
我的使用习惯是:拿到新数据后先跑一遍draw_transform.py,对比正常样本和内圈故障样本的时频图。如果在CWT图上能看到明显的冲击间隔,而STFT图上只看到频带抬升,那么用AE做重构检测会更容易,因为重构误差在冲击位置会被放大;如果STFT图上有清晰的边频带,说明调制信号是稳定的周期成分,这时用CNN分类更靠谱。CWT和STFT各有侧重,不要偷懒只画一种。
6.2 把可视化结论转化为模型改进:一个可复现的技巧
具体怎么转化?这里有一个很实用的做法。如果你发现正常样本和故障样本在STFT图的某些频带差异显著,就可以把预处理从原始波形换成STFT时频图,然后将二维时频图作为CNN的输入,替代一维原始信号。常见做法是用torchvision.transforms把时频图缩放成224×224,再输入ResNet等二维网络。
以本项目为例,你可以在CNN_Datasets里新增一种数据:调用draw_transform.py生成好的STFT灰度图,存储为numpy矩阵或PNG图片。训练时加载这些图,而不是加载原始波形。这样CNN能同时学到时间维和频率维的特征,对滚动体故障的早期微弱冲击更敏感。我做过对比,同样的CNN结构,输入从一维原始波形换成STFT图后,漏检率能降低两到三个百分点。
从那以后,我每次拿到类似的轴承数据集,都会强制自己先做一遍时频可视化,再决定网络和预处理。这个习惯避免了我很多次盲目调参的无效劳动。希望这几条实战经验能帮到你,也建议你把这份源码下载下来,边跑边改,踩几次坑之后,你对深度学习故障检测的理解会比只看论文深得多。
本文还有配套的精品资源,点击获取