简介一套基于深度学习的设备故障诊断入门示例面向希望将神经网络应用于工业设备监测、异常识别与预测维护的初学者。示例覆盖数据预处理、模型搭建、模型训练与评估优化全流程。数据端涉及清洗、标准化、特征工程与训练/验证/测试集划分模型端介绍CNN、RNN/LSTM、自编码器等常用结构训练端包含优化器选择、学习率调整、Dropout正则化及准确率/召回率/F1等指标评估帮助读者建立完整技术框架。压缩包共57个文件包含40个mat工况数据文件、10张示意图、3个Python训练脚本、2个已训练h5模型和2个说明文档整体46.13MB目录按数据、模型、代码等模块组织。目前已有908人学习下载可直接对照说明复现并跑通入门实验适合初次接触深度学习故障诊断、需要完整参考实现的工程师与学生。1. 入门第一个问题深度学习和故障诊断到底有什么关系机器设备出故障之前往往不是没声音而是声音里藏着“暗号”——轴承磨损会有高频冲击齿轮断齿会产生周期性振动电机转子不平衡会让整个机体抖得越来越厉害。过去老师傅靠耳朵听、靠手感摸现在设备越来越精密、工况越来越复杂单靠人已经听不过来了。深度学习要做的就是把这套诊断经验“教”给计算机给它大量带标签的历史振动数据让它自己找出故障信号里的隐藏规律。这篇入门文章会带你完整走一遍“数据预处理→模型搭建→模型训练”的流程写一个能直接跑通的故障诊断小示例。适合那种不想只背理论、想亲手写出第一个模型的新手。2. 为什么偏偏是深度学习先搞懂传统方法到底难在哪2.1 传统方法为什么越来越不够用故障诊断不是新词工厂里早就有一套成熟做法。最传统的是阈值报警在设备上装传感器测振动幅值超过某个设定值就报警。听起来简单实际用起来却很头疼——不同转速、不同负载下正常设备的振动幅值差别很大阈值设高了漏报设低了误报。一条产线几十台设备每台都得单独调阈值调完还得经常复查这是典型的“人肉运维”。后来有了频谱分析把振动信号用傅里叶变换拆成不同频率的分量看哪个频率上能量变大了。这比阈值高明不少但问题也明显真实设备里的干扰源太多齿轮啮合频率、轴承固有频率、电磁噪声叠在一起想单独揪出故障特征频率需要不少经验。而且设备状态一变频率成分跟着变原来总结的“规律”可能很快就失效。再往后走一步就是“人工特征 传统机器学习”。先用统计方法算出信号的均值、峰值、峭度、能量等几十个特征再用支持向量机或随机森林做分类。这个方法的问题不在分类器而在特征——特征靠专家手工设计换一台设备、换一种工况特征可能就不灵了。我第一次学的时候就在想能不能让模型自己去找特征别再让人盯着频谱图猜了2.2 深度学习到底改变了什么从“人找特征”到“特征自己长出来”深度学习最大的变化是把“特征工程”这件事从人手里接了过去。你不再需要告诉模型“峭度大于多少算故障”而是把原始振动信号直接喂进去模型通过一层层卷积和池化自己学会看哪些波形片段更可疑。这个过程叫“端到端学习”原始信号进分类结果出中间没有人工设计的特征环节。放在故障诊断场景里这意味着三件事。第一换设备时不用重新设计特征只要重新准备数据训练一次模型就行。第二模型能发现人看不懂的微弱规律比如某种很隐蔽的调制信号肉眼在频谱图上根本看不出来。第三深度网络能同时处理多通道数据把振动、温度、电流一起送进去做多源融合诊断。当然这不是玄学也不是有了深度学习就万事大吉。它带来的新要求也很明确需要带标签的历史数据需要一点 Python 基础需要愿意调试模型的心态。对刚入门的人来说先用模拟数据把流程跑通再挑战真实数据是阻力最小的路径。下面这张表把传统方法和深度学习的区别摆在一起看完会更清楚为什么要学这个对比维度传统机器学习深度学习特征来源专家手工设计模型自动学习调参难度特征和分类器都要调主要调网络结构和超参数据需求小样本也能跑数据越多优势越明显换设备迁移特征经常失效靠重新训练适配可解释性特征含义明确内部逻辑像黑匣子2.3 先把环境跑起来装好 Python 和深度学习库“怎么做”的第一步不是写模型而是让电脑准备好接收代码。这里用到的都是开源工具装起来很快也不挑电脑配置——没 GPU 也能跑只是慢一点。打开终端先装两个基础库pip install torch numpy装好后验证一下能不能正常导入python -c import torch; print(torch.__version__)如果能看到类似2.4.0这样的版本号说明环境没问题。看到ModuleNotFoundError也没关系多数情况是 pip 装到了别的 Python 环境里检查一下是不是用了同一个终端、同一个 Python。这一步卡住的人不少九成都是“装完了换了个窗口跑”导致的。整个入门过程后续代码只要这两样东西就能跑不用额外装任何复杂依赖。3. 数据预处理模型能不能学明白一半看这里3.1 先造一份能练手的模拟数据深度学习需要数据但新手一上来就找真实故障数据集往往会被格式、采样率、标签名搞晕。我建议先用模拟数据跑通全程自己写代码生成“正常”和“故障”两类振动信号确保标签是准的再去看模型能不能把两类分开。模拟振动信号不复杂。正常状态下设备振动像一条平缓的正弦波上叠一点随机噪声故障状态下会出现明显的冲击成分。下面这段代码会生成两份一秒长的信号一份是正常状态一份是故障状态import numpy as np fs 2000 # 采样率每秒采 2000 个点 t np.arange(0, 1, 1/fs) # 1 秒的时间轴 # 正常信号50Hz 正弦波 小幅噪声 normal_signal np.sin(2*np.pi*50*t) 0.05*np.random.randn(len(t)) # 故障信号正常信号基础上在 0.5 秒处叠加一次冲击 fault_signal np.sin(2*np.pi*50*t) \ 0.8*np.exp(-((t-0.5)**2)/0.0001) \ 0.05*np.random.randn(len(t))这里用高斯函数np.exp(-((t-0.5)**2)/0.0001)模拟冲击它在 0.5 秒附近形成一个尖锐的脉冲幅值是正常噪声的十几倍肉眼就能在波形图上看到一根“刺”。真实轴承故障的振动信号里这种冲击特征非常常见模拟信号抓的就是这个本质。3.2 预处理第一步归一化把原始信号直接送进神经网络之前必须先做归一化。原因很实际模型计算时依赖数值的尺度和分布如果不同样本的幅值一个是一百、一个是零点一训练会很不稳定甚至出现 loss 变成 NaN 的情况。归一化的做法很多故障诊断里最常用的是 z-score 标准化把每个窗口内的数据减去均值、除以标准差让数据变成“均值为 0、标准差为 1”的分布。下面这个函数就是干这件事的def normalize(windows): return (windows - windows.mean()) / (windows.std() 1e-6)末尾加了一个极小值1e-6是为了防止某段信号完全没有波动、标准差为零时出现除零报错。这是代码里很常见的防御性写法。注意一点归一化要在切完窗口之后、送进模型之前做而且只能用每个窗口自身的数据来算均值标准差不能拿整个数据集一起算否则会破坏窗口之间的独立性。3.3 预处理第二步滑窗切片一秒的信号有 2000 个点直接整个送进模型也能跑但信息太粗糙。故障冲击只出现在某个瞬间把整秒信号压成一个点细节全丢了。更合理的做法是用“滑窗”把长信号切成一小段一小段每段是一个样本。窗口大小取 256 个点窗口之间滑动步长取 64 个点。这意味着相邻两个窗口有大量重叠好处是样本量变大模型见过的波形形态更多训练效果更稳。实现代码很朴素def make_windows(signal, window_size256, step64): windows [] for i in range(0, len(signal) - window_size, step): windows.append(signal[i:iwindow_size]) return np.array(windows)循环从信号头部开始每次往后挪step个点取出一段长度是window_size的子序列直到不够一个完整窗口为止。一个关键参数是step它控制两个窗口的重叠程度参数含义本例取值太小会怎样太大会怎样window_size每个样本长度256可能截断冲击特征单个样本信息太粗step窗口滑动步长64样本重复度过高样本数量太少对初学者而言先记住“窗口要能包住故障冲击步长别太大”就够了后面拿到真实数据再慢慢调。3.4 组装数据集把信号变成模型能吃的样子接下来把前面几步串起来生成一个完整的训练数据集。这里要注意PyTorch 模型要求输入的形状是“样本数 × 通道数 × 序列长度”振动信号是单通道所以要在中间加一个维度从(样本数, 256)变成(样本数, 1, 256)。all_windows [] all_labels [] # 对两类信号分别切窗、归一化、打标签 for signal, label in [(normal_signal, 0), (fault_signal, 1)]: for w in make_windows(signal): w normalize(w) all_windows.append(w) all_labels.append(label) X np.array(all_windows).reshape(-1, 1, 256) # -1 表示自动计算样本数 y np.array(all_labels) print(样本数量:, X.shape[0]) print(样本形状:, X.shape) print(标签分布: 正常 0 有, (y0).sum(), 个, 故障 1 有, (y1).sum(), 个)跑完这段屏幕上会显示约 58 个样本其中正常和故障各一半。样本量不大但足够验证后续模型能不能把两类区分开。真实项目里的数据预处理会比这个复杂得多比如去噪、重采样、数据均衡但核心流程就是“切窗 → 归一化 → 组装”把这一步想透了后面全是沿用。4. 模型搭建你的第一个一维卷积网络4.1 模型到底是个什么东西先拆掉黑匣子很多人第一次听到“神经网络”就觉得很高深其实可以把它理解成一台有很多旋钮的计算器。输入是一段信号输出是一个打分模型内部有无数个“旋钮”叫权重训练的过程就是不断转动这些旋钮让打分结果越来越接近真实标签。至于怎么转、转多少是靠梯度下降计算出来的——这一点暂时不用深究先把机器的形状搭出来。故障诊断里最常用的网络结构是一维卷积神经网络名字听着吓人本质就是让一小段卷积核在信号上滑动每到一处就算一次“局部匹配分”。这种结构特别适合振动信号分析因为故障冲击往往只有几个采样点宽用整条信号去算反而抓不住重点。卷积核只在局部看信号天然就是干这个的。4.2 用 PyTorch 搭一个适合新手的一维卷积网络下面就是完整模型代码。网络很小只有两个卷积层加一个全连接层跑起来非常快方便新手观察中间结果。import torch import torch.nn as nn class FaultCNN(nn.Module): def __init__(self): super().__init__() self.net nn.Sequential( # 输入通道 1输出通道 16卷积核长度 5步长 2 nn.Conv1d(1, 16, kernel_size5, stride2), nn.ReLU(), # 输入通道 16输出通道 32卷积核长度 5步长 2 nn.Conv1d(16, 32, kernel_size5, stride2), nn.ReLU(), # 自适应全局平均池化无论前面多长都压成 1 nn.AdaptiveAvgPool1d(1), nn.Flatten(), # 全连接层输出 2 个值对应两类 nn.Linear(32, 2) ) def forward(self, x): return self.net(x) model FaultCNN() print(model)这段代码定义了模型的结构。nn.Sequential表示按顺序执行每一层数据从前往后流过中间不需要人为干预。Conv1d接收三个关键参数输入通道数、输出通道数、卷积核长度。输入通道为 1 是因为振动信号是一维单通道16 和 32 是输出通道数相当于让模型同时从 16 个、32 个不同角度去观察这段信号。卷积核长度设为 5意味着每次只看 5 个连续采样点。4.3 逐层拆解每层的计算逻辑初学者最容易懵的是“数据经过每一层之后形状变成了什么样”。把形状变化搞清楚后面搭任何网络都不慌。下面这张表列出输入为(批量大小32, 通道1, 长度256)时每一层的输出形状层输入形状输出形状做了什么事Conv1d(1→16)(32, 1, 256)(32, 16, 126)16 个卷积核各自提取局部模式信号缩短约一半ReLU(32, 16, 126)(32, 16, 126)把负数变 0增强非线性表达能力Conv1d(16→32)(32, 16, 126)(32, 32, 61)在上一轮特征上继续提取更高层模式AdaptiveAvgPool1d(1)(32, 32, 61)(32, 32, 1)取每个通道的平均值只留 32 个数Flatten(32, 32, 1)(32, 32)把多维特征拉平成向量Linear(32→2)(32, 32)(32, 2)综合 32 个特征输出两类打分这个表格值得多看两遍。特别是AdaptiveAvgPool1d(1)这一层无论前面的卷积把信号压缩到多长它都能强制压成 1 个值这样最后一个全连接层的输入维数是固定的不会因为输入信号长度变化而报错。这种设计让模型可以接受不同长度的输入非常实用。跑一下model的打印结果会发现它已经自动算好了每一层的参数总量。这里的模型只有大约 2800 个参数比那些动辄上亿参数的大模型小得多在 CPU 上几秒就能完成一轮训练。5. 模型训练第一次跑通五个常见问题避坑指南5.1 训练一个模型需要哪些零件模型搭好之后还需要三样东西才能开始训练数据加载器、损失函数、优化器。数据加载器负责把之前准备好的一堆窗口按批次喂给模型一次喂 32 个省内存又能加快计算。损失函数用来衡量模型输出和真实标签的差距分类任务常用交叉熵。优化器负责根据差距反向调整模型里的 2800 多个权重。这三样东西的角色可以类比成学开车数据是练习路段损失函数是教练打分优化器是学员调整方向盘的动作。缺任何一样训练都无从谈起。下面的代码把这三样一次性配齐import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # 把 NumPy 数组转成 PyTorch 张量 X_tensor torch.tensor(X, dtypetorch.float32) y_tensor torch.tensor(y, dtypetorch.long) dataset TensorDataset(X_tensor, y_tensor) loader DataLoader(dataset, batch_size32, shuffleTrue) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001)lr0.001是学习率控制每次调整权重的步子大小。步子太大容易在最优解附近来回震荡步子太小训练半天没动静。0.001是深度学习里最常见的默认值新手先照抄跑通后再考虑调成 0.0005 或 0.0001 试试。5.2 跑起来第一个训练循环代码训练循环本身不长核心逻辑就五步取一批数据 → 计算模型输出 → 计算损失 → 清空旧梯度 → 反向传播更新权重。下面是一份可直接运行的训练代码for epoch in range(5): for xb, yb in loader: # 前向计算模型对每批数据打分 output model(xb) # 计算预测打分和真实标签的差距 loss criterion(output, yb) # 清空上一次留下的梯度 optimizer.zero_grad() # 反向传播计算每个权重的调整方向 loss.backward() # 更新所有权重 optimizer.step() print(f第 {epoch1} 轮, loss {loss.item():.4f})epoch表示把整个数据集完整过一遍的次数这里设成 5。跑完后会看到类似下面的输出第 1 轮, loss 0.6831 第 2 轮, loss 0.6018 第 3 轮, loss 0.4862 第 4 轮, loss 0.3785 第 5 轮, loss 0.3059loss 从 0.68 一路降到 0.30说明模型在逐步学会区分正常和故障信号。如果把第 5 轮后的模型拿去测试准确率能达到 90% 以上。第一次看到这个结果时你会明显感觉到“原来深度学习没有想象中那么神秘”。注意这里没有打印测试集准确率因为模拟数据量太小重点在于跑通流程。5.3 新手最容易踩的 5 个坑坑 1loss 变成 NaN现象训练到某一轮时loss 突然变成nan后面全部跟着变nan。原因最常见的是输入数据里有过大的数值或缺失值比如没做归一化、信号里有极大毛刺或者学习率设置得太大导致梯度爆炸。解决先检查数据预处理确认每个样本都过了归一化再检查学习率如果原来是0.01就降到0.001或0.0005。按“先查数据、再降学习率”的顺序排查九成情况能解决。坑 2训练准确率虚高测试却崩了现象训练集上模型表现完美几乎 100% 分对一到新数据就拉胯。原因典型的信息泄漏。很多新人先对全部数据做归一化再切训练集和测试集导致测试集的信息早就被模型“偷看”过了准确率是假的。解决必须先把数据按时间顺序切出训练集、验证集、测试集再对各自的部分单独做归一化。原则只有一句话任何基于全量数据的统计计算都必须放在切分之后再单独执行。坑 3准确率一直卡在某个水平上不去现象模型训练了好久loss 不再下降准确率像焊死了一样卡在 60% 或 70%。原因在故障诊断里最常见的情况是类别不平衡故障样本远远少于正常样本模型干脆全部预测成多数类照样能获得很低的损失。解决先打印标签分布确认两类样本数量是否接近。如果不平衡可以用CrossEntropyLoss的weight参数给少数类更高的权重或者用采样策略让每批数据里两类数量均衡。坑 4改完输入维度测试时就报形状错误现象训练跑得好好的换到新数据上测试模型直接报RuntimeError: size mismatch。原因新数据可能采样率变了、窗口长度变了或者通道数不一样导致输入形状和模型期望的形状对不上。解决把“读数据 → 切窗 → 归一化 → 重组形状”封装成一个函数训练和测试都调用同一份代码不要手写两遍。我第一次学的时候就是图省事测试脚本里重新写了一遍预处理结果少 reshape 了一个维度排查了半小时才发现。坑 5CPU 上训练太慢以为代码写错了现象几百个样本训练一轮要好几分钟人开始怀疑是模型写复杂了。原因把批量大小设成 1或者循环里每次重复加载数据都会拖慢训练速度。如果你没写shuffleTrue模型每轮看到的数据顺序完全相同收敛也会变慢。解决把batch_size提到 32 或 64用DataLoader管理数据全程只加载一次。第一批样本量通常只有几百到几千个CPU 训练完全够用不要一开始就上 GPU先把流程调通更重要。6. 下一步怎么学从跑通到真正能解决实际问题6.1 从二分类到多分类加一种故障类型试试现在你的模型只能回答“正常还是故障”真实设备的问题是“是哪种故障”可能有好几种。下一步可以修改模拟数据生成三种故障信号轴承内圈故障、外圈故障、滚动体故障把最后的全连接层输出从 2 改成 4把标签从 0/1 改成 0/1/2/3。这个修改只涉及数据生成和最后输出层全程不超过十行代码但会让你彻底明白多分类是怎么运作的。6.2 换真实数据集找一份公开轴承数据模拟数据跑通后建议找一份公开的真实轴承故障数据来练手。这类数据通常来自实验台包含不同故障类型、不同负载工况下的振动信号格式和噪声远比模拟信号复杂。拿到手后先别急着训练先画一段波形看看观察冲击长什么样再按第三、四章的方法走一遍预处理和训练。等你发现模型在真实数据上准确率掉到 70% 左右时就已经开始踩坑了——这是好事说明你进入了工程师阶段。下一步就是学会调参数、做数据增强、尝试更深的网络。6.3 再往后把模型用起来学习建议按下面的方向逐个展开阶段做什么解决什么问题第一阶段多分类 真实数据从玩具示例走向实际问题第二阶段调整网络结构加批量归一化、Dropout缓解过拟合、提升泛化第三阶段处理不平衡数据、做交叉验证让评估结果更可信第四阶段模型部署导出 ONNX 格式离开笔记本跑进现场我第一次学的时候就死盯着训练准确率模型在模拟数据上跑到了 100% 就以为自己全懂了结果换成真实数据直接翻车。从那以后我每次搭模型都强制走一遍“先看数据分布 → 做最小预处理 → 训练小模型 → 验证集评估”的流程不追求一次到位先让它短小精悍地跑通再逐步加复杂度。这一步看似不起眼却让后面省掉无数个深夜调试。希望这篇入门文章能帮你少走弯路也希望你能在第一次跑通 loss 下降时感受到那份“原来我也能做到”的踏实感。本文还有配套的精品资源点击获取