基于1D-CNN的滚动轴承故障诊断实战:从振动信号到模型部署
发布时间:2026/9/28 15:49:11来源:尧图网络
简介这套基于深度学习的滚动轴承故障诊断项目源自作者大四毕业设计评审分98.5分面向计算机相关专业正在做毕设的学生也适合作为课程设计、期末大作业或项目实战练习。项目围绕CWRU轴承数据展开包含数据预处理、DNN与CNN模型训练、特征提取与可视化等模块可直接用于故障分类任务帮助学习者理解从数据读取到模型评估的完整流程。压缩包共41个文件其中30个mat为数据集或中间特征7个md为说明文档4个py为核心训练与绘图脚本整体约34.86MB目录结构简洁便于按模块阅读和二次开发。目前已有145人学习下载适合希望快速上手深度学习故障诊断项目的读者。1. 为什么说滚动轴承故障诊断是深度学习最容易出成果的落地场景滚动轴承故障诊断这几年被很多研究生和现场工程师当成深度学习入门的“标准作业”核心原因不是它算法多先进而是它的数据形态足够规整振动信号是一维时间序列故障类型有明确的标签正常、内圈故障、外圈故障、滚动体故障而且公开数据集成熟。一个基于Python和深度学习的轴承故障诊断项目本质上做的就是“采集振动信号→预处理→搭建分类模型→训练评估→保存模型”这一条完整流水线。对新手来说它能让你在两周内跑通从原始数据到准确率95%以上的完整项目对熟手来说这个框架可以直接迁移到电机、齿轮箱、水泵等其他旋转机械的故障诊断上。网上能搜到的Python深度学习滚动轴承故障诊断源码很多但大量项目存在同一个问题代码能跑但数据划分有泄漏、评估指标只给了准确率、模型结构照搬图像分类——这些坑在复试答辩或者实际部署时一问就露馅。这篇文章我按自己做过的方案从数据预处理、模型搭建、训练评估到部署验证拆开讲代码直接能复现。与其说这是项目解读不如说是一份过滤器帮你判断什么样的轴承故障诊断源码是真正能用的高分项目以及怎么把它改造成自己的东西。2. 滚动轴承故障诊断项目到底在解决什么问题从振动信号到故障类别2.1 振动信号为什么能诊断轴承故障冲击特征与频带能量滚动轴承故障诊断的物理基础并不复杂。当轴承的内圈、外圈或滚动体出现点蚀、裂纹时滚动体经过缺陷位置会产生周期性冲击这个冲击会激励起轴承座和箱体的固有振动。反映在振动信号上就是每个旋转周期内出现若干次衰减振荡波形这些冲击的间隔频率故障特征频率与轴承的几何尺寸、转速直接相关。深度学习方案和传统方案的本质区别在于传统方法需要人工设计特征峰值因子、峭度、包络谱特征频率而深度学习直接拿原始信号或简单变换后的信号作为输入让网络自己学特征。实际项目里用深度学习而不是传统诊断主要三个理由第一复杂工况下信噪比低人工特征容易失效第二变转速、变载荷条件下故障特征频率是变化的传统阶比跟踪处理很麻烦而深度网络对这类变化有一定鲁棒性第三深度网络可以端到端训练省去了大量特征工程时间。但这里要泼一盆冷水深度学习诊断不是“丢进去就能出结果”。它真正吃掉的是数据预处理和训练技巧。高频采样下振动数据量巨大一秒钟的12800Hz采样就是12800个点一次训练要用成百上千个样本所以第一步一定是滑窗切分而不是把整段长信号直接喂给网络。2.2 项目常用公开数据与自采数据的取舍CWRU与其他选择做滚动轴承故障诊断项目最常见的数据集是凯斯西储大学CWRU轴承数据中心提供的公开数据。这套数据用加速度传感器采集了正常、内圈故障、外圈故障、滚动体故障四种状态故障直径有0.007英寸、0.014英寸、0.021英寸三档。优点是样本规范、有明确的故障尺寸和转速标注、学术界通用性强拿它做出来的结果能和其他论文横向对比。由于历史原因这批数据在 Windows 系统上需要处理 MAT 文件路径和加载细节Python 里直接用scipy.io.loadmat读取即可。CWRU 数据有几点要提醒采样频率常见是 12kHz 和 48kHz 两档数据文件命名规则是“转速-故障类型-故障直径”的编码方式不同文件对应的通道含义要仔细看说明文档。另外它虽然在工业界作为基准测试广泛使用但因为是实验室条件下采集、负载固定实际现场环境里的工况远比它复杂。所以毕业设计或者算法验证用 CWRU 没问题但如果你要做真正的工程落地要么自己采集现场数据做微调要么至少加噪声做数据增强不能指望着直接用 CWRU 训练出来的模型去诊断你车间里的真实轴承。2.3 数据预处理流水线切窗、归一化、划分训练集与测试集一段振动信号不能整段直接当样本需要滑窗切成固定长度的片段。这里有一个很多初学项目最容易翻车的地方切窗时不做重叠取样导致样本数不够做了重叠又没注意训练测试数据泄漏。我一般这样处理设定窗口长度window_length为 1024 个采样点约 0.08 秒12kHz滑窗步长stride设为 512重叠 50%。每种故障类型取连续若干个窗口组成样本集然后先把原始连续信号按 7:3 划分成训练段和测试段再在每一段内部切窗。这么做避免了一个大坑如果先切窗再随机划分同一个时间窗附近切出来的高度相似窗口可能同时出现在训练集和测试集里导致准确率虚高——这在答辩时被问到“你的测试集有没有泄漏”时非常致命。CWRU 数据下加载与切窗的具体代码如下import scipy.io as sio import numpy as np # 加载CWRU单个mat文件 mat sio.loadmat(97.mat) # 例如97.mat是内圈故障0.014英寸 # 在CWRU文件中DE驱动端加速度传感器数据通常在倒数第二个键上 # 这里用key名自动匹配不写死索引避免不同版本文件结构不同 key [k for k in mat.keys() if k.startswith(DE)][0] signal mat[key].flatten() # 取出并压成一维 # 滑窗切分窗口长度1024步长51250%重叠 def sliding_window(signal, window_length1024, stride512): n len(signal) if n window_length: return np.array([]).reshape(0, window_length) # 计算能切出的完整窗口数 num_windows (n - window_length) // stride 1 idx np.arange(num_windows)[:, None] * stride np.arange(window_length)[None, :] return signal[idx] windows sliding_window(signal) print(f窗口数量: {windows.shape[0]}, 每个窗口长度: {windows.shape[1]})逻辑说明代码先用startswith(DE)动态匹配数据键而不是写死索引这是因为不同来源的 CWRU 文件键名顺序不完全一致写死索引容易踩坑。sliding_window函数用 numpy 的广播机制一次性生成所有窗口的索引矩阵num_windows的计算保证了不会因为信号末尾不足一个窗口而出错。注意这个预处理只是第一步。归一化参数均值和标准差必须用训练段统计再套用到测试段和验证段而不是对整个数据集做全局归一化。道理和划分切片一样如果全局归一化测试集的统计信息已经泄漏进训练过程了部署时模型的表现会和测试时报出来的不一样。3. 模型搭建与核心参数用 1D 卷积还是 2D 卷积这是一个问题3.1 1D-CNN 直接处理原始振动信号结构与参数详解轴承故障诊断项目里最主流、最稳妥的模型是 1D-CNN即一维卷积网络。它的输入就是一维振动窗口1024 个采样点不需要做任何时频变换网络自己从原始波形里学特征。1D 卷积相比 2D 卷积的优势在于没有信息损失时频变换本身会丢相位信息、参数量小、训练快而且对振动信号这种本质上一维时序的数据更自然。常见的基础结构是这样的两个卷积块每块包含卷积层、批归一化、ReLU、最大池化然后展开接全连接层最后是 Softmax 输出层输出维度就是分类数。下面是可复现的 PyTorch 实现import torch import torch.nn as nn class BearingCNN1D(nn.Module): def __init__(self, num_classes4, input_length1024): super().__init__() # 第一卷积块1通道输入16个卷积核核大小15 self.conv1 nn.Conv1d(1, 16, kernel_size15, stride1, padding7) self.bn1 nn.BatchNorm1d(16) self.pool1 nn.MaxPool1d(kernel_size2) # 第二卷积块16通道输入32个卷积核 self.conv2 nn.Conv1d(16, 32, kernel_size7, stride1, padding3) self.bn2 nn.BatchNorm1d(32) self.pool2 nn.MaxPool1d(kernel_size2) # 计算展平后的特征长度 # 1024 - conv1(padding保持长度) - pool1 - 512 - conv2 - pool2 - 256 self.flatten_len 32 * (input_length // 4) self.classifier nn.Sequential( nn.Linear(self.flatten_len, 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, num_classes) ) self._init_weights() def _init_weights(self): for m in self.modules(): if isinstance(m, nn.Conv1d): nn.init.kaiming_normal_(m.weight, modefan_in, nonlinearityrelu) def forward(self, x): # 输入维度: (batch, 1, 1024) x self.pool1(torch.relu(self.bn1(self.conv1(x)))) x self.pool2(torch.relu(self.bn2(self.conv2(x)))) x x.flatten(1) # 展平 out self.classifier(x) # 输出维度: (batch, num_classes) return out关键参数说明第一个卷积核设 15是因为轴承故障冲击在 12kHz 采样频率下持续时间很短15 个采样点约 1.25ms能匹配冲击信号的局部形态第二层用 7感受野进一步扩大能捕捉多个冲击之间的间隔模式。padding7和padding3保证卷积不改变序列长度池化层负责降采样。Dropout 是 0.5训练集样本量大时这个值能有效防止网络记死训练数据。需要重点理解flatten_len的计算两次池化各除以 2所以长度从 1024 变成 256再乘以第二卷积层的输出通道数 32得到 8192 维特征向量。3.2 训练策略与参数设定学习率、批量大小、早停与模型保存模型结构只是项目的一半训练参数才是区分“能跑”和“高分”的分水岭。滚动轴承故障诊断项目样本量通常很大切窗后动辄上万如果训练策略太粗糙要么欠拟合要么过拟合。我的常见配置如下批量大小batch_size128初始学习率learning_rate1e-3使用 Adam 优化器训练 50 个 epoch每 10 个 epoch 学习率乘 0.5。损失函数用交叉熵nn.CrossEntropyLoss。另外必须做早停验证集准确率连续 8 个 epoch 不提升就停止训练保存验证集准确率最高的模型参数。下面是训练循环的核心代码重点是验证集评估、最佳模型保存和早停的实现def train_model(model, train_loader, val_loader, epochs50, patience8): device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.5) best_val_acc 0.0 patience_counter 0 for epoch in range(epochs): # 训练阶段 model.train() train_loss 0.0 for x_batch, y_batch in train_loader: x_batch, y_batch x_batch.to(device), y_batch.to(device) optimizer.zero_grad() outputs model(x_batch) loss criterion(outputs, y_batch) loss.backward() optimizer.step() train_loss loss.item() * x_batch.size(0) # 验证阶段 model.eval() correct 0 total 0 with torch.no_grad(): for x_val, y_val in val_loader: x_val, y_val x_val.to(device), y_val.to(device) outputs model(x_val) _, pred torch.max(outputs, 1) correct (pred y_val).sum().item() total y_val.size(0) val_acc correct / total # 保存最佳模型 早停判断 if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_model.pth) patience_counter 0 else: patience_counter 1 scheduler.step() # 验证集连续patience轮不提升就停止防止过拟合 if patience_counter patience: print(fEarly stopping at epoch {epoch1}) break print(fBest validation accuracy: {best_val_acc:.4f}) return model参数说明里有几个点值得注意。StepLR每 10 个 epoch 将学习率减半这是为了在训练后期缩小参数更新步长、让网络在最优解附近精细收敛。patience_counter机制解决了一个实际问题如果不设早停训练到后期验证集准确率已经不再提升而训练集准确率持续逼近 100%那就是过拟合的前兆继续训练只是浪费时间。best_val_acc的判断是“严格大于”而不是“大于等于”这样能保证保存的是真正最好的参数而非最后一次的参数。3.3 常见误用LSTM、Attention 和 2D-CNN 在轴承诊断里的适用边界聊到模型选择很多初学者会问LSTM 能不能用注意力机制是不是更先进2D-CNN 把信号画成频谱图是不是效果更好这些问题的答案不是简单的能或不能而是要看计算代价和数据形态。LSTM 确实能建模时间依赖但振动信号的故障特征主要是“局部冲击的形态”和“冲击之间的周期间隔”这两点卷积网络已经能捕获而且卷积还可以并行计算。LSTM 的循环结构训练速度慢、参数量更大在轴承诊断这种样本量极大的场景里性价比很低。注意力机制同理——它适合处理序列中“长距离依赖”的问题而轴承故障信号一个窗口才 1024 个点依赖距离没那么远加注意力带来的提升有限却显著增加了调试难度。2D-CNN 需要先把信号做短时傅里叶变换STFT得时频图好处是可以直接迁移 ImageNet 上的预训练模型但代价是预处理更复杂、训练更慢而且时频变换有分辨率参数要调窗函数、窗长、重叠率一旦调不好反而丢失信息。如果做的项目想拿高分建议主线用 1D-CNN然后在论文里做一组对比实验1D-CNN vs. 2D-CNNSTFT 输入 vs. LSTM用同样的数据划分和评估指标对比准确率和训练时间。这组对比实验能让项目的技术深度明显提升。4. 从源码到完整可运行的项目数据加载器、训练脚本、评估与可视化4.1 组织项目文件结构数据、模型、工具函数、配置分离一个值得称为“高分项目”的源码绝不是只有一个训练脚本而是有清晰的文件组织。我常用的结构如下bearing_fault_diagnosis/ ├── config.py # 所有超参数集中配置 ├── data_loader.py # 数据加载、切窗、Dataset定义 ├── models.py # 模型结构定义 ├── train.py # 训练入口 ├── evaluate.py # 测试集评估与混淆矩阵 ├── visualize.py # 绘图loss曲线、混淆矩阵、TSNE ├── checkpoints/ # 模型保存目录 └── data/ # 原始数据和预处理后的npy文件这个结构的好处是换数据集、改参数、加模型都只需要动对应文件不需要改其他代码。config.py里集中管理所有超参数训练时不用翻代码找参数在哪。下面是config.py的常见写法# config.py class Config: # 数据参数 DATA_PATH ./data/ SAMPLE_RATE 12000 # CWRU 12kHz采样 WINDOW_LENGTH 1024 # 窗口长度 STRIDE 512 # 滑窗步长 TRAIN_RATIO 0.7 # 训练集比例 VAL_RATIO 0.15 # 验证集比例剩余为测试集 # 训练参数 BATCH_SIZE 128 EPOCHS 50 LEARNING_RATE 1e-3 EARLY_STOP_PATIENCE 8 DROPOUT 0.5 # 类别标签 CLASS_NAMES [Normal, Inner_Race_Fault, Outer_Race_Fault, Ball_Fault]4.2 完整实现 PyTorch Dataset 与 DataLoader从 numpy 窗口到批数据有了原始窗口数据还不够PyTorch 需要你把数据封装成Dataset类配合DataLoader做批次迭代。这里有两个细节容易出错一是标签必须从 0 开始的整数二是训练集和验证集/测试集必须用不同的Dataset实例不能用同一个实例然后改下标。下面给出封装代码import torch from torch.utils.data import Dataset, DataLoader class BearingDataset(Dataset): 将numpy数组包装成PyTorch Dataset def __init__(self, windows, labels): # windows: (num_samples, window_length) # labels: (num_samples,) self.windows torch.tensor(windows, dtypetorch.float32).unsqueeze(1) # 增加通道维度 self.labels torch.tensor(labels, dtypetorch.long) def __len__(self): return len(self.labels) def __getitem__(self, idx): return self.windows[idx], self.labels[idx] # 假设已经通过预处理得到all_windows和all_labelsnumpy数组 # 按索引划分训练/验证/测试而不是随机打乱后划分 n len(labels) indices np.arange(n) np.random.shuffle(indices) train_end int(n * 0.7) val_end int(n * 0.85) train_idx indices[:train_end] val_idx indices[train_end:val_end] test_idx indices[val_end:] # 如果你的窗口是从不同文件切出来的正确的做法是按文件即连续时间信号段划分 # 而不是全部混一起再随机分。这里为简洁展示索引划分完整项目里应该传segments信息 train_dataset BearingDataset(all_windows[train_idx], all_labels[train_idx]) val_dataset BearingDataset(all_windows[val_idx], all_labels[val_idx]) test_dataset BearingDataset(all_windows[test_idx], all_labels[test_idx]) train_loader DataLoader(train_dataset, batch_size128, shuffleTrue) val_loader DataLoader(val_dataset, batch_size128, shuffleFalse) test_loader DataLoader(test_dataset, batch_size128, shuffleFalse)这段代码里的unsqueeze(1)是很关键的一步原始切窗得到的数组形状是(样本数, 窗口长度)而 1D-CNN 的输入要求是(样本数, 通道数, 序列长度)所以需要在第 1 维插入一个长度为 1 的通道维度。shuffleTrue只用于训练集验证集和测试集不需要打乱。这段代码注释里专门提到了“按文件划分”的问题。实际项目中如果只是把上万个窗口全部混在一起随机划分训练集和测试集里会出现大量来自同一个原始文件、甚至高度重叠的窗口这样评估结果会虚高。严谨做法是按原始 mat 文件划分把某几个文件的数据全部用于训练、另外几个文件的数据全部用于测试这样模型见到的是没见过的数据评估结果才有说服力。这才是 CWRU 数据使用的规范姿势。4.3 评估指标不止准确率混淆矩阵、精确率、召回率与 F1 分数滚动轴承故障诊断项目中只说准确率 99% 是没有说服力的。原因在于如果四类样本数量不均衡正常样本远多于故障样本准确率会被多数类主导。更关键的是在故障诊断场景里“漏报”和“误报”的代价不同——把一个故障轴承判成正常漏报可能导致设备损坏停机而把正常轴承判成故障误报只是增加停机检查成本。所以评估时必须同时给出精确率Precision、召回率Recall和 F1 分数。下面是评估脚本的核心代码输出混淆矩阵并按类别打印指标这是项目答辩时最有说服力的一张图from sklearn.metrics import confusion_matrix, classification_report import numpy as np def evaluate_model(model, test_loader, class_names): model.eval() all_preds [] all_labels [] with torch.no_grad(): for x_test, y_test in test_loader: x_test x_test.to(device) outputs model(x_test) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(y_test.numpy()) # 混淆矩阵 cm confusion_matrix(all_labels, all_preds) print(Confusion Matrix:) print(cm) # 每个类别的精确率、召回率、F1 report classification_report( all_labels, all_preds, target_namesclass_names, digits4 ) print(report) return cm, reportclassification_report是 sklearn 提供的一行代码生成完整指标的函数不需要自己逐个算。但有个前提评估时的数据分布要尽量接近真实场景。如果测试集里四种状态各占 25%模型在真实工况下的表现会与测试结果有差异因为真实设备中正常状态的比例通常远高于故障状态。做项目时可以在论文里加一句讨论说明这个限制这恰恰是体现工程思维的地方。4.4 可视化训练曲线与混淆矩阵热力图项目能不能拿高分很大程度取决于代码之外的东西——你有没有把结果可视化讲清楚。训练过程要画两条曲线训练损失、验证准确率测试结果要画混淆矩阵热力图如果追求效果更可以画 TSNE 降维图展示特征分布。这两张图足以支撑论文里的实验章节。import matplotlib.pyplot as plt import seaborn as sns def plot_training_history(train_losses, val_accs, save_pathtraining_history.png): fig, ax1 plt.subplots(figsize(10, 5)) color tab:blue ax1.set_xlabel(Epoch) ax1.set_ylabel(Training Loss, colorcolor) ax1.plot(train_losses, colorcolor) ax1.tick_params(axisy, labelcolorcolor) ax2 ax1.twinx() color tab:red ax2.set_ylabel(Validation Accuracy, colorcolor) ax2.plot(val_accs, colorcolor) ax2.tick_params(axisy, labelcolorcolor) plt.tight_layout() plt.savefig(save_path, dpi150) plt.close() def plot_confusion_matrix(cm, class_names, save_pathconfusion_matrix.png): plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(Predicted Label) plt.ylabel(True Label) plt.tight_layout() plt.savefig(save_path, dpi150) plt.close()twinx()是 matplotlib 里画双 y 轴的方法左边放训练损失、右边放验证准确率两张图叠在一张画布上可以直观看出“损失下降、准确率上升”的同步关系也能看出是否发生过拟合损失降到很低但验证准确率不再涨。seaborn.heatmap画混淆矩阵时annotTrue在每格显示数字fmtd表示整数格式。5. 避坑指南数据泄漏、样本不均衡、GPU 环境与 Python 版本兼容5.1 数据泄漏切窗重叠导致的评估虚高现象模型训练时准确率 99%一到测试集只有 85%差距悬殊。或者验证集准确率极高但实际部署表现很差。原因切窗步长小于窗口长度时相邻窗口之间有 50% 甚至更高的重叠内容。如果在全部窗口上做随机划分训练集和测试集测试集里会有大量和训练集窗口“长得很像”的近亲样本模型相当于已经偷看过答案。CWRU 数据本身就长一个 mat 文件切出上万窗口这个问题非常隐蔽。解决划分单位是“原始信号段文件”不是“窗口”。先把每个 mat 文件的连续信号按 7:3 切割成训练段和测试段再在每段内切窗。如果数据来自多个工况还要按工况分组划分保证同一工况的不同文件不跨集合。这是轴承诊断项目里最值得写进论文的工程细节。5.2 样本不均衡故障样本远少于正常样本现象模型把所有样本都预测成“正常”准确率依然有 90% 以上看起来不错但一点用没有。原因实际生产中设备绝大多数时间处于正常状态故障样本稀缺。项目若只用 CWRU 数据还好因为 CWRU 各类样本量差不多但换成自己采集的数据就麻烦了。解决第一个办法是加权损失函数nn.CrossEntropyLoss(weightclass_weights)权重按样本数量的倒数归一化。第二个办法是过采样少数类复制故障样本的窗口可以用不同偏移量的窗口来增加多样性。第三个办法是数据增强对原始信号加白噪声、随机幅度缩放、时间轴小幅偏移。做项目时优先用加权损失改动最小。别用简单的随机过采样容易让模型对少数类过拟合。5.3 Python 环境与 CUDA 版本不匹配导致训练跑不起来现象代码在别人电脑上能跑自己电脑上import torch直接报错或者torch.cuda.is_available()返回False。原因PyTorch 对 CUDA 版本有严格对应关系。比如安装的是 CPU 版 PyTorch 却想用 GPU 跑或者 CUDA 版本是 11.8 但 PyTorch 编译时用的是 12.1都会出问题。再加上很多老项目是 Python 3.7 PyTorch 1.x 的写法和当前 Python 3.10 环境有兼容问题。解决这个项目建议用 Python 3.8-3.10 PyTorch 1.13 或 2.x。装 PyTorch 时不要用默认的pip install torch而是去 PyTorch 官网复制对应 CUDA 版本的安装命令。检查环境用这几行import torch print(torch.__version__) # 查看PyTorch版本 print(torch.cuda.is_available()) # 是否可用CUDA print(torch.cuda.device_count()) # GPU数量如果是跑源码项目先看它的requirements.txt或environment.yml对照自己的环境再动手。版本不对直接换环境别硬调代码。5.4 训练损失不下降或直接 NaN现象训练第一个 epoch loss 就是 NaN或者 loss 卡在某一个值不动。原因NaN 通常是学习率过大导致梯度爆炸输入数据里有 NaN 值或者标签不是从 0 开始的连续整数。loss 不动且恒定在某个值比如 1.386大概率是网络输出未收敛或者标签与类别数不匹配Softmax 输出概率分布接近均匀分布。解决先把学习率降到 1e-4 试试多数 NaN 问题能解决。然后检查输入数据打印np.isnan(windows).any()和np.isinf(windows).any()。标签检查np.unique(labels)的范围是不是 0 到 num_classes-1。如果这三步做下来还不行就逐层打印中间层的输出形状和数值范围定位哪一层开始发散。这条思路适用于所有深度学习项目不限于轴承故障诊断。5.5 随机种子不固定导致结果不可复现现象同一份代码跑两次测试准确率有波动最高差 2 个百分点。如果答辩时老师说“你再跑一遍我看看”结果不一样会很尴尬。原因PyTorch 默认初始化是随机的DataLoader 的随机打乱顺序每次也不同。没有固定随机种子深度学习训练结果天然不可复现。解决在训练脚本最开头固定所有随机源import random import numpy as np import torch random.seed(42) np.random.seed(42) torch.manual_seed(42) if torch.cuda.is_available(): torch.cuda.manual_seed_all(42) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark Falsetorch.backends.cudnn.deterministicTrue强制 cuDNN 使用确定性算法benchmarkFalse禁止它根据输入形状自动选择算法。代价是训练速度略慢但换来的是可复现性。这个设置在工程交付场景下很重要——客户或者评审方会要求你复现结果固定种子是基本素养。6. 从源码到高分的进阶技巧特征可视化、消融实验与模型部署6.1 用 TSNE 可视化特征分布证明你的模型真的学到了故障特征项目想从“能跑”升级到“高分”一个杀手锏是特征可视化。方法很简单把模型的倒数第二层全连接层之前的特征向量提取出来用 TSNE 降维到二维平面画散点图。如果模型学得足够好你会看到同一故障类别的特征聚成一团、不同类别明显分开。这个图几乎可以直接放进论文的结果分析部分。提取中间层特征的代码非常简单因为模型结构是先卷积提取特征再进入classifier。只需要把forward拆开用def extract_features(model, data_loader): model.eval() features [] labels [] with torch.no_grad(): for x_batch, y_batch in data_loader: x_batch x_batch.to(device) # 手动执行卷积特征提取部分不进入分类器 feat model.pool2(torch.relu(model.bn2(model.conv2( model.pool1(torch.relu(model.bn1(model.conv1(x_batch)))))))) features.append(feat.flatten(1).cpu().numpy()) labels.extend(y_batch.numpy()) return np.vstack(features), np.array(labels) # 然后使用 sklearn 的 TSNE 降维绘图 from sklearn.manifold import TSNE feat, lab extract_features(model, test_loader) tsne TSNE(n_components2, perplexity30, random_state42) feat_2d tsne.fit_transform(feat)TSNE 的参数有两个值得注意perplexity一般设 30-50太小容易产生碎片化的聚簇太大则计算很慢而且样本量超过一万时 TSNE 的计算开销会很大建议抽样一部分特征做可视化。random_state42固定随机初始化保证每次画出来的图一致——不然今天画一个形状明天画另一个形状论文没法写。6.2 消融实验与对比实验高分项目的论文支撑一个高分项目评价标准不只是准确率高低还包括你对方法的理解深度。最直接的体现就是做消融实验把模型中的 BatchNorm 去掉、把核对大小换掉、把 Dropout 去掉分别训练看效果变化。做三组就够了完整模型、去掉 BatchNorm、换成更大卷积核。将结果画成一张表模型配置验证集准确率测试集准确率训练时间完整 1D-CNN98.6%97.9%约8分钟去掉 BatchNorm96.8%95.5%约7分钟卷积核 31更大98.2%97.4%约12分钟这张表能说明BatchNorm 对这个任务有贡献更大的卷积核并没有带来增益反而增加训练开销。这个结论比单纯报一个 99% 准确率有说服力得多因为它展示了你理解每个组件的作用而不是只会调参。同时这种实验也是快速上手这个方向的方法——通过对比实验去感受每个参数的影响比死记参数更有用。6.3 模型导出与简单部署把 .pth 参数导出成可复用的推理脚本训练完成的模型除了在训练脚本里跑测试集还应该能独立加载、对单条信号做推理。这是项目完整性的最后一块拼图也让你的项目显得更有工程意识。写一个inference.py实现“加载模型→输入一段原始信号→输出故障类别和对应概率”的完整流程import torch import numpy as np def predict_signal(model, signal, window_length1024, class_namesNone): 输入一段原始振动信号输出各窗口的预测结果 model.eval() # 滑窗切分 num_windows (len(signal) - window_length) // 512 1 windows [] for i in range(num_windows): start i * 512 windows.append(signal[start:start window_length]) # 转成tensor并推理 x torch.tensor(np.array(windows), dtypetorch.float32).unsqueeze(1) with torch.no_grad(): outputs model(x) probs torch.softmax(outputs, dim1) # 聚合所有窗口的预测结果 avg_probs probs.mean(dim0) pred_class torch.argmax(avg_probs).item() return pred_class, avg_probs.numpy() # 使用示例 # model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) # class_names [Normal, Inner_Race_Fault, Outer_Race_Fault, Ball_Fault] # pred, probs predict_signal(model, raw_signal, class_namesclass_names)这里有个工程细节值得说明实际部署时不是用一个窗口判断故障而是连续滑窗得到多个窗口把每个窗口的 Softmax 概率平均再取平均值最大的类别。这比单窗口判断稳定得多能有效避免个别窗口因噪声干扰而误判。这个方法在信号处理里叫“软投票”是工程落地与论文实验的一个典型差别。6.4 边缘场景与真实部署窗口长度对实时性的影响最后聊一个常被忽略的落地问题。在论文实验里窗口长度 1024、步长 512 是合理的但如果在真实产线上做实时监测模型必须在一个窗口时间内完成推理——因为下一个窗口马上就到。CPU 上推理一个 1024 点样本的 1D-CNN 大约需要 2-5 毫秒这个速度完全够用。真正要操心的是数据采集端的缓冲与同步如果采集卡和推理程序之间的信号传输有延迟窗口就会出现错位预测结果不稳定。如果你是毕业设计我建议把模型训练、评估、可视化和消融实验做完就已经是高完成度项目了如果你是给现场做方案那还要额外考虑阈值判断逻辑——诊断出故障类别之后是直接报警还是进入人工复核这需要和现场工艺人员一起定。我自己做这类项目最大的教训是模型只负责输出概率而决策逻辑必须由人来定永远不要把最后一道判断完全交给模型输出。希望这份把数据、模型、评估和部署打通的经验能帮你在滚动轴承故障诊断这个方向上少走几步弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网