深度学习故障诊断实战:从振动信号到CNN模型训练的完整链路

发布时间:2026/10/20 7:21:34
深度学习故障诊断实战:从振动信号到CNN模型训练的完整链路 简介面向深度学习初学者的故障诊断入门示例包系统演示了从数据预处理、模型搭建到训练评估的完整流程适合刚接触智能运维或故障预测的开发者对照动手实践。资源共57个文件包含40个MAT格式的数据集文件、3个Python训练脚本、2个H5模型权重文件以及10张训练过程可视化图片和2份说明文档整包约46MB工程按数据、代码、模型、可视化分区存放结构清晰。示例以卷积神经网络为核心代码中完整实现了异常值剔除、缺失值处理、Z-score标准化、滑动窗口特征构造以及训练/验证/测试集划分等预处理环节模型搭建部分给出了网络层数、激活函数与Adam优化器的配置方法并涉及学习率动态调整与Dropout正则化。训练完成后利用准确率、精确率、召回率、F1分数等指标的可视化图表直观评估模型表现帮助读者理解每个步骤的输入输出关系。已有908人学习浏览可直接下载MAT数据与Python脚本复现结果再迁移到传感器或设备日志等自有数据上是快速入门深度学习故障诊断的实用资料。1. 深度学习故障诊断入门从振动信号到可跑的完整链路第一次把深度学习用到设备故障诊断时我踩过一个非常典型的坑把某公开轴承数据集的振动信号切巴切巴直接送进卷积网络训练准确率达到 95%心里觉得稳了。结果换了一批现场采集的数据准确率掉到七成出头问题出在数据预处理和训练策略上跟网络结构关系不大。这份基于深度学习的故障诊断入门示例把完整链路拆成数据预处理、模型搭建、模型训练三段每段都有能直接改用的脚本和参数说明。适合刚接触深度学习、想尽快跑通一个端到端故障诊断流程的工程师和研究生。后面的内容就是我按实际跑项目的顺序把这三段逐个拆开讲包括那些容易让人翻车的细节。2. 数据预处理先把一维信号变成模型认识的格式2.1 两种喂法原始信号序列和时频特征图拿到振动信号的第一步是决定模型吃什么。第一种直接把一维原始波形作为输入交给 1D-CNN 或 LSTM 这类序列模型去学端到端但训练成本高而且当设备转速有波动时波形相位漂移会对一维卷积造成干扰。第二种先把信号做时频变换得到二维灰度图再交给 2D-CNN。时频图把时间、频率的分布关系固定下来转速波动的影响被分摊到整张图里特征更稳定也更容易借鉴图像分类的成熟结构。入门阶段我一般建议走第二种。短时傅里叶变换STFT参数少、速度快、结果可解释性强是最适合当起点的时频方法。顺便提一句传统统计特征方案峭度、有效值、峰值因子这些指标在单一工况下能识别故障但转速负载一变阈值就不成立。深度学习的方法本质上是从数据里学特征表征绕开人工定阈值这也是这套示例把重点放在深度模型上的原因。两种输入方案的差异可以直观对比方案输入形式代表模型数据需求对转速波动的鲁棒性原始序列一维波形1D-CNN、LSTM高低时频图二维灰度图2D-CNN中高STFT 的常用参数有三个采样率 fs、窗长 nperseg、重叠 noverlap。公开轴承数据集的采样率多为 12kHz窗长取 256、重叠 128 时频率分辨率约 47Hz如果故障特征频率在低频段把窗长加到 512频率分辨率能细化到约 23Hz但时间分辨率会下降选窗长本质上是在频率分辨率和时间分辨率之间做权衡。2.2 滑动窗口切样本窗口长度、重叠率和类别平衡一条连续信号动辄几十万点整条丢给网络显存和计算量都扛不住另一方面故障冲击往往只在局部出现全序列平均反而会稀释特征。我习惯按旋转周期来定窗口假设设备转速 30Hz、采样率 12kHz转动一圈对应 400 个采样点窗口取 1024 点大约覆盖 2.5 个周期既保留多个冲击周期又不会把不同阶段的工况混进同一段样本。切窗代码很直接核心是控制窗口长度和重叠率import numpy as np def sliding_window(data, label, window1024, overlap0.5): step int(window * (1 - overlap)) samples, sample_labels [], [] for i in range(0, len(data) - window 1, step): samples.append(data[i:i window]) sample_labels.append(label) return np.stack(samples), np.array(sample_labels)这个函数在连续信号上按固定步长切窗data 是某类工况下的一段连续信号label 是该工况对应的故障类别编号。step 由 window 和 overlap 计算默认 50% 重叠时相邻两个窗口共享一半数据点一个 12 万点的文件大约能切出 234 个窗口。overlap 越大样本越多但相邻样本几乎一样训练时容易造成数据泄漏50% 到 75% 之间是常用区间。切完窗口后做 STFT把每个一维窗口变成二维时频图from scipy.signal import stft def to_stft(sample, fs12000, nperseg256, noverlap128): f, t, Zxx stft(sample, fsfs, npersegnperseg, noverlapnoverlap) return np.abs(Zxx)stft 返回频率轴、时间轴和复数谱。故障诊断里一般不关心相位直接取幅值得到形状为频率点数时间帧数的二维矩阵。nperseg256、noverlap128 是 50% 重叠的常用组合采样率必须和采集时一致换成自己的数据时要先确认 fs不能默认照抄。类别平衡也要在这里处理。统计各类样本数后正常类通常远多于故障类导致模型偏向多数类。我一般先看各类样本量对样本多的类别随机抽样对样本少的类别提高重叠率补样本同时留一部分给后面的损失函数加权兜底。2.3 归一化与数据集划分顺序错了全盘皆输先说结论归一化的均值和标准差只能用训练集计算数据划分必须按文件划分而不是按单条样本随机划分。两个都错了验证数据就变成了作弊数据跑出来的准确率没有任何参考价值。归一化用 z-score 方式在训练集上计算参数后应用到所有集合train_mean X[train_idx].mean(axis0) train_std X[train_idx].std(axis0) 1e-8 X_train_norm (X[train_idx] - train_mean) / train_std X_val_norm X[val_idx] - train_mean) / train_std后面那句是刻意写的验证集必须用训练集的均值和方差标准化不能重新算。加 1e-8 是为了防止某些通道的信号接近零方差时除零。划分方式用分组随机划分把同一个原始文件的全部窗口放进同一侧from sklearn.model_selection import GroupShuffleSplit split GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(split.split(X, y, groupsfile_ids))groups 参数传的是每个样本所属的原始文件编号。这样同一个文件的窗口只会出现在训练集或验证集中不会因为相邻窗口内容相似而造成验证准确率虚高。random_state 固定下来方便后续复现结果。划分完之后顺手做个泄漏自检随机抽一个验证集样本在训练集里找有没有近似重复的窗口。可以这样粗略检查sample X_val[0] dups np.sum(np.abs(X_train - sample).sum(axis1) 1e-6) print(近似重复样本数:, dups)如果 dups 大于 0说明同一窗口被分到了两端回去改成分组划分。这个检查只需要跑一次但能避免后面所有评估结论失效。3. 模型搭建为什么首选 CNN骨架长什么样3.1 选型理由CNN 的归纳偏置正好匹配振动信号故障信号里最有辨识度的是周期性冲击、边频带和局部共振。卷积网络的局部连接正好捕捉局部冲击模式池化层提供一定平移容忍度即使转速有小幅波动特征也能被稳定提取。LSTM 的问题在于序列太长1024 点要逐步展开训练慢数据少时还容易过拟合。Transformer 在故障诊断里也有应用但对数据量和调参的要求更高入门阶段不是最优选择。在 CNN 内部还有 1D 和 2D 之分模型类型输入优势短板1D-CNN原始波形模型轻、流水线短频域细节表达弱2D-CNN时频图特征表达强、参考多多一步 STFT 预处理入门阶段默认走 2D-CNN公开参考多出问题容易排查以后换数据也好扩展。这个选择也符合人工智能领域近年的惯例图像分类的成熟结构被大量迁移到时频图诊断场景中。3.2 一个可运行的二维 CNN 骨架结构与参数量下面这个网络结构不大输入是 224×224 的时频图输出是故障类别参数量约 6.5 万CPU 也能跑import torch.nn as nn class FaultCNN(nn.Module): def __init__(self, num_classes4, in_channels1): super().__init__() self.features nn.Sequential( nn.Conv2d(in_channels, 32, kernel_size3, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.Conv2d(32, 32, kernel_size3, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.Conv2d(64, 64, kernel_size3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), ) self.global_pool nn.AdaptiveAvgPool2d(1) self.classifier nn.Linear(64, num_classes) def forward(self, x): x self.features(x) x self.global_pool(x) x torch.flatten(x, 1) return self.classifier(x)输入是 (B, 1, 224, 224)输出 (B, num_classes)。两个卷积块后接最大池化每个块是两层 3×3 卷积加批归一化和 ReLU。两层卷积堆叠能扩大感受野BN 在样本量不大时稳定训练。各层输出尺寸和参数量如下层输出尺寸参数量约Conv2d 3×3, 1→32(B, 32, 224, 224)320Conv2d 3×3, 32→32(B, 32, 224, 224)9248MaxPool2d(B, 32, 112, 112)0Conv2d 3×3, 32→64(B, 64, 112, 112)18496Conv2d 3×3, 64→64(B, 64, 112, 112)36928MaxPool2d(B, 64, 56, 56)0AdaptiveAvgPool2d(B, 64, 1, 1)0Linear 64→num_classes(B, num_classes)260替换 Flatten 的全局平均池化值得多说一句。AdaptiveAvgPool2d(1) 不管输入特征图多大都池化成 1×1全连接层的输入维度固定为 64省去了手算尺寸的麻烦也大幅减少参数量。很多人在这里用 Flatten 后接全连接输入尺寸一变就报错后面避坑章节会专门讲。3.3 模型输入的三个坑通道数、尺寸和预训练权重第一个坑是通道数。STFT 输出是灰度图in_channels 设 1 就够了。有的开发者为了加载 ImageNet 预训练权重把灰度图复制成 3 通道喂进去等于三个通道都是一张图预训练权重也不匹配计算量还翻倍。入门阶段用 1 通道随机初始化训练过程更可控。第二个坑是尺寸。STFT 矩阵的长宽由 nperseg 和样本点数决定不是天然 224×224需要统一尺寸from torchvision.transforms import Resize, ToTensor x Resize((224, 224))(ToTensor()(stft_matrix))Resize 默认双线性插值压缩后时频图的频率峰可能变钝作为入门基线影响不大。如果追求精度可以反过来设计 nperseg 和窗口长度直接生成 256×256 的时频图省掉 resize 这步。第三个坑是预训练权重的认知误区。ImageNet 预训练模型学的是自然图像的边缘纹理时频图里确实有边缘比如冲击引起的竖条纹但分布差异很大迁移效果未必胜过从零训练。小数据集时可以对比试试但别把预训练当作默认选项尤其是输入通道数不一致时强行适配反而得不偿失。4. 模型训练损失函数、优化器与曲线判读4.1 输出层与损失函数CrossEntropy 里的常见误解多分类故障诊断输出节点数和类别数一致。PyTorch 的 CrossEntropyLoss 内部已经做了 softmax 归一化和对数运算模型只需要输出未归一化的 logits。很多教程会在模型最后一层先加 softmax再丢给损失函数数值上不会报错但梯度被压得很小训练明显变慢。正确做法是模型只输出 logits损失函数内部处理。类别不均衡时损失函数可以加权重权重按各类样本数的倒数归一化import torch import torch.nn as nn counts torch.tensor([5000, 300, 300, 300], dtypetorch.float) weights counts.sum() / (counts * 4) criterion nn.CrossEntropyLoss(weightweights.to(device))counts 是四类样本数少数类在损失中的权重自动放大。注意这不是优先方案前面第 2 章的重采样才是首选损失权重是第二道防线。4.2 训练主循环与权重保存策略完整的训练函数并不复杂关键是循环结构和保存策略def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0, 0, 0 for x, y in loader: x, y x.to(device), y.to(device) optimizer.zero_grad() out model(x) loss criterion(out, y) loss.backward() optimizer.step() total_loss loss.item() * y.size(0) correct (out.argmax(1) y).sum().item() total y.size(0) return total_loss / total, correct / totaltrain_one_epoch 返回平均损失和准确率。每批先清零梯度再前向、反传、更新。PyTorch 里 optimizer.step() 前必须调 zero_grad否则梯度会跨批次累积。out.argmax(1) 沿类别维度取每个样本得分最高的索引。验证和保存策略配合 epoch 循环best_acc 0.0 for epoch in range(epochs): train_loss, train_acc train_one_epoch( model, train_loader, optimizer, criterion, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) print(fepoch {epoch}: train_acc{train_acc:.4f}, val_acc{val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pt)只按验证集准确率保存最优权重而不是保留最后一个 epoch 的权重。验证集连续多个 epoch 不涨时可以提前终止训练这里没有额外写早停逻辑先把基线跑通再加不迟。4.3 优化器与学习率参数怎么定、怎么调优化器选型上AdamW 对新手最友好它修正了 Adam 权重衰减的实现方式。SGD 配合动量调好了能达到更好的极值但要调整的点更多。入门阶段不需要纠结直接用 AdamW。初始学习率我一般设 1e-3weight_decay 设 1e-4batch size 32。如果 batch 翻倍学习率可以尝试按比例放大但更稳妥的做法是先按 1e-3 跑 10 个 epoch看 loss 是否稳定下降再决定。学习率这东西有时候有点玄学与其提前精细推导不如让前十个 epoch 暴露问题。调度器推荐余弦退火import torch.optim as optim optimizer optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR( optimizer, T_maxepochs, eta_min1e-5)T_max 等于总 epoch 数eta_min 是最低学习率。余弦退火让学习率先快后慢训练后期趋于稳定比 StepLR 少了每多少轮骤降的额外调参。StepLR 的写法是 scheduler StepLR(optimizer, step_size30, gamma0.1)适合训练周期很长的任务入门用余弦退火就够了。4.4 训练曲线状态判读欠拟合、正常与过拟合训练曲线要同时看 train loss 和 val loss单独的 train acc 没有意义。欠拟合表现为两个损失都高且接近对策是增大模型容量、加深网络或调高学习率。正常状态是 train loss 缓慢下降val loss 同步下降准确率稳步抬升。过拟合表现为 train loss 持续降但 val loss 回升对策是加大 weight decay、做时域加噪等数据增强或者提前停。还有一个高频现象loss 曲线剧烈抖动。如果抖得像锯齿先检查学习率是不是过高其次检查 batch size 是否太小。batch 小梯度噪声大曲线会抖但不一定影响最终效果先看看准确率是否仍在上升再决定动不动参数。5. 训练避坑五个让我翻过车的细节下面五个坑我全部亲手翻过车有的至今还靠检查清单防着。5.1 数据泄漏准确率虚高的重灾区现象训练准确率 99%验证准确率 99%一换现场数据崩到七成。原因把同一条信号切出的窗口随机分配到训练集和验证集相邻窗口来自同一段连续波形内容几乎一样验证集变成了换皮训练集。解决按文件编号做分组划分确保同一个文件的窗口不在两个集合里见第 2.3 节。再跑一遍 5.1 的重复窗口检查确认 dups 为 0。5.2 类别不平衡模型学会了「躺平」现象整体准确率 90%但某个故障类别 F1 为 0预测结果永远是正常类。原因正常类样本占绝大多数模型发现全猜正常就能拿高分。解决先按类别统计样本量对少数类提高滑窗重叠率补样本损失函数加权重兜底评估时用混淆矩阵和各类别的 F1别只看整体准确率。只看准确率这个坑很容易被漂亮的数字盖过去。5.3 尺寸不匹配报错信息在骗你现象RuntimeError: size mismatch for fc.weight。原因之前网络用了 Flatten 接全连接时频图尺寸一变全连接层输入维度对不上。解决改用 AdaptiveAvgPool2d(1) 固定全连接输入维度同时用 print(x.shape) 打印每层输出别靠人肉推算。报错信息里给出的 shape 就是排查线索照着改就行。5.4 损失不降反升先查数据再动结构现象前几个 epoch 损失在 1.5 附近抖动甚至涨到 2.0。原因最常见的是输入数据没有归一化或者标签从 1 开始编码而 CrossEntropyLoss 要求标签从 0 开始。解决打印 x.min() 和 x.max() 确认数值范围检查标签是否从 0 开始把学习率临时降到 1e-4 验证是否参数问题。动了数据没结果之前不要先改模型结构。5.5 换了数据集就崩归一化和工况对齐现象公开数据集上 95%同类设备现场数据只有 70%。原因传感器位置、转速、负载变了数据分布漂移。解决先用训练集的均值和方差对齐新数据确认窗口长度按旋转周期折算后一致再考虑加噪声、时间偏移等数据增强。分布漂移是故障诊断落地的长期问题入门阶段先把归一化和工况对齐做好。6. 把训练好的模型变成可用的诊断函数推理脚本与打包细节6.1 一个拿来即用的推理函数模型训完不是终点能把一条新信号丢进去返回故障类别才是。这里给一个通用的推理封装class DiagnosisPipeline: def __init__(self, model, device, mean, std, label_map): self.model model.to(device).eval() self.device device self.mean mean self.std std self.label_map label_map def predict(self, signal_segment): x to_stft(signal_segment) x Resize((224, 224))(ToTensor()(x)) x (x - self.mean) / self.std x x.unsqueeze(0).to(self.device) with torch.no_grad(): logits self.model(x) prob torch.softmax(logits, dim1)[0] pred prob.argmax().item() return self.label_map[pred], prob[pred].item()推理时同样先做 STFT 再归一化训练集算出的 mean 和 std 必须传进来不能在推理脚本里重新算。ToTensor 对 float 输入只做维度和类型转换不会隐式缩放所以归一化的顺序没有问题。with torch.no_grad() 关掉自动求导省显存、加快推理。验证一致性的技巧拿一条训练时没见过的信号分别用训练脚本里的验证函数和这个 predict 跑一遍输出的类别概率应该完全一致。如果不一致几乎都是预处理参数没对齐。6.2 保存 checkpoint 时别忘了一起打包这些参数有次我在训练脚本里顺手算了归一化均值当时没在意后来模型要换机器推理时重新写脚本那些值找不到了预测结果全是乱的。从那以后我统一用一个字典保存所有现场信息checkpoint { model_state: model.state_dict(), input_size: 224, normalize_mean: train_mean, normalize_std: train_std, label_map: label_map, stft_params: {nperseg: 256, noverlap: 128, fs: 12000}, } torch.save(checkpoint, fault_diagnosis_model.pt)保存的不只是模型权重而是把信号变成预测结果所需的全部参数。换机器加载时所有字段都从 checkpoint 里取不依赖训练脚本里的任何全局变量。那次忘了归一化参数的教训让我养成一个习惯每次保存模型都强制走一遍这个打包流程预处理参数、标签映射、STFT 参数全部写进同一个文件。故障诊断项目的数据来源杂、评估环境多变这套流程能省掉大量返工这份入门示例里的完整脚本和数据组织方式也是按这个思路整理的。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询