
简介针对工业设备预测性维护与故障诊断需求这份资源提供基于深度学习1DCNN的轴承故障诊断完整Python源码工程适合机械故障诊断研究人员、设备运维工程师和深度学习初学者参考学习。压缩包共50个文件总大小3.64MB以13个Python脚本为核心覆盖数据预处理、1DCNN模型构建、训练测试、t-SNE可视化等关键环节另有17张故障类型示意图、5个配置文件、标注文本及混淆矩阵Excel表格目录结构清晰方便按模块对照学习。目前已有424人学习/下载。源码内置CWRUcnn模型、BasicModule训练封装和config配置模块并包含results目录下的混淆矩阵结果及可视化输出可直接运行或替换数据进行二次开发。通过复现整体流程能够深入理解一维卷积网络如何从振动信号中自动提取特征并完成内圈、外圈、滚动体等故障模式识别为实际工程故障诊断提供可靠参考。1. 一维卷积直接用原始振动信号做故障识别为什么比频谱更占优势CWRU 轴承数据在故障诊断圈的定位约等于 MNIST 在图像识别里。很多人拿到源码第一反应为什么不先做 FFT 再送网络而让 1DCNN 直接啃原始波形答案在物理机制里滚动体磕碰滚道产生的瞬态冲击在时域表现为周期性尖峰做完傅里叶变换冲击相位信息被丢弃。1DCNN 的卷积核天然在时间轴滑动第一层学出的滤波器组等价于一组带通滤波加冲击检测特征工程被做进了网络。这份源码覆盖 .mat 原始数据、滑窗切分、1DCNN 建模到 t-SNE 可视化的完整链路适合跑 CWRU 基线的研究生也适合用 PyTorch 搭时序分类管线的工程师。2. CWRU 数据管线从 .mat 波形到 Dataloader 张量2.1 两种标注文件对应两个难度梯度的任务源码 data 目录下放着 annotations_4.txt 和 annotations_10.txt分别对应两个分类任务。4 分类是经典玩法Normal正常、Inner raceway Fault内圈、Outer raceway Fault外圈、Roll Fault滚动体各算一类images 目录里那几张波形图就是这四类的原始信号预览。这个任务区分的是故障部位冲击形态差异很大四分类准确率通常能到 99% 以上适合验证网络结构和训练管线是否打通。10 分类则把内圈、外圈、滚动体各自的三种损伤直径0.007、0.014、0.021 英寸分别拆成独立类别再加一个正常类共 10 类。关键问题是损伤直径 0.007 和 0.014 造成的振动差异在时域波形上肉眼几乎不可分辨难度和 4 分类完全不同。实际跑下来的经验是4 分类接近满点10 分类如果网络结构不做加深误分会集中在相邻损伤直径之间这符合物理直觉。做论文消融实验时建议两个任务都跑只用 4 分类会被审稿人认为是挑简单任务。任务类别组成识别难度常见结果4 分类正常、内圈、外圈、滚动体低冲击形态差异大99% 以上10 分类3 种缺陷 × 3 种损伤直径 正常高相邻直径难分93%98%视划分方式而定标注文件本身是纯文本每一行通常就是「文件路径 类别索引」的结构加载函数按行解析即可def load_annotations(path): pairs [] with open(path, r) as f: for line in f: line line.strip() if not line: continue file_path, label line.split() pairs.append((file_path, int(label))) return pairs逻辑说明label 在这里是类别索引从 0 开始4 分类就是 0~310 分类就是 0~9。这里有个值得留意的点不同论文对 CWRU 的类别编号定义不完全一致有的把正常类编为 0有的编为 1复现他人结果前先对齐标签顺序否则混淆矩阵怎么看怎么不对。2.2 数据读取与统一采样率CWRU 原始数据是 .mat 格式raw_data 目录存放的就是这些文件。每个 .mat 里通常同时包含驱动端和风扇端的加速度计数据字段名常见为 DE_time 和 FE_time采样率有 12kHz 和 48kHz 两档。文件里的负载信息0、1、2、3 hp在跨负载泛化实验里是关键标签。读取 .mat 的代码通常是这样的from scipy.io import loadmat def load_cwru_mat(path, fieldDE_time): data loadmat(path) return data[field].flatten()这里有一个高频坑同一次实验在 12kHz 和 48kHz 下采集的信号长度不同混在一起切窗会导致 batch 内样本长度不一致DataLoader 直接报错。我一般会在 data_preprocess.py 里先对 48kHz 的数据做降采样from math import gcd from scipy.signal import resample_poly def unify_sr(signal, src_rate, dst_rate12000): if src_rate dst_rate: return signal g gcd(src_rate, dst_rate) return resample_poly(signal, dst_rate // g, src_rate // g)参数说明resample_poly 会先做抗混叠滤波再做抽取所以不需要额外手动低通。src_rate48000、dst_rate12000 时等价于 up1、down4 的整数倍降采样切窗前统一采样率是必须的一步。2.3 滑窗重叠采样把有限的振动数据切成可用样本一个 .mat 文件通常有几十万点直接整段丢进网络有两个问题显存放不下而且整段里包含启停阶段的非平稳信号对分类是纯干扰。常见做法是固定窗口滑切def sliding_window(signal, window_len2048, overlap0.5): stride int(window_len * (1 - overlap)) if stride 1: stride 1 n (len(signal) - window_len) // stride 1 return np.stack([signal[i * stride : i * stride window_len] for i in range(n)])参数说明window_len 决定样本的时间跨度。在 12kHz 采样率下2048 点约 170ms可以覆盖数个冲击周期模型能看到周期性特征如果窗口太短比如 256 点样本里经常只有零到一次冲击模型会去学噪声。overlap 本质是一种数据增强0.5 是平衡点重叠少则样本少重叠太高则相邻样本高度相关训练集和测试集如果按样本随机划分同一个原始片段会被一半训练一半测试测试分数虚高。更严谨的做法是记录每个样本来自哪个原始文件按原始文件划分训练集和测试集或者用 scikit-learn 的 GroupShuffleSplit保证同一个 .mat 的样本不会被同时分到两个集合。2.4 归一化红线与 dataset.py 实现归一化这一步通常落在 data_preprocess.py 和 dataset.py 里。红线只有一条归一化参数必须只用训练集拟合。train_mean, train_std train_data.mean(), train_data.std() train_data (train_data - train_mean) / train_std test_data (test_data - train_mean) / test_std绝对不要先对全量数据做标准化再切分那是典型的数据泄漏。CWRU 四个负载下振动幅度不同全量标准化会把负载信息带入模型测试分数虚高 2~3 个百分点审稿人一眼就能看出来。提示如果模型在测试集上准确率高得异常比如 99.8%第一件事就是检查归一化顺序和样本划分方式再检查别的。dataset.py 里写 PyTorch Dataset 时有一个容易漏的细节class CWRUDataset(Dataset): def __init__(self, X, y): self.X torch.FloatTensor(X).unsqueeze(1) # (N, 1, L) self.y torch.LongTensor(y) def __len__(self): return len(self.y) def __getitem__(self, idx): return self.X[idx], self.y[idx]unsqueeze(1) 是新手最容易漏的一行。Conv1d 要求输入维度是 (batch, channels, length)单通道振动信号要把 (L,) 扩成 (1, L)否则第一个 batch 就会报维度不匹配。标签用 LongTensor 是因为 CrossEntropyLoss 不接收 one-hot只接收类别索引。3. 1DCNN 结构拆解宽卷积核滤波窄卷积核提特征3.1 第一层卷积核为什么偏宽网络结构里的第一层卷积是最讲究的。1DCNN 的输入是一维振动信号时间轴上相邻点之间强相关卷积核太窄比如 kernel3直接堆在原始波形上等价于一个非常局部的差分算子学出来的东西接近求导容易放大高频噪声。把第一层卷积核设成 64 甚至 128在 12kHz 采样下对应约 5~10ms 的时间窗口基本覆盖一次冲击响应的主要能量段。这个窗口天然起到带通滤波和包络提取的作用后面的小卷积核再在滤波后的信号上组合出层次特征。这个设计可以称为宽核 窄核的组合。宽核只在第一层用参数总量可控。真正让 1DCNN 在轴承诊断上站得住的是它的平移等变性冲击发生在什么位置不影响分类结果。轴承故障信号的冲击位置是随机的如果只从频域特征去分类相位信息被丢掉而 1DCNN 保留了这点。3.2 CWRUcnn 的 PyTorch 实现与各层尺寸源码 models 目录下 CWRUcnn.py 是模型定义BasicModule.py 提供基类封装了模型保存和加载的通用逻辑。核心结构如下class CWRUcnn(nn.Module): def __init__(self, num_classes4): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 16, kernel_size64, stride8, padding32), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2), nn.Conv1d(16, 32, kernel_size3, stride1, padding1), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2), nn.Conv1d(32, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1) ) self.classifier nn.Linear(64, num_classes) def forward(self, x): return self.classifier(self.features(x).squeeze(-1))各层输出尺寸变化输入 1×2048层输出尺寸说明Conv1d(1→16, k64, s8, p32)16×257(20482×32-64)/81≈257MaxPool1d(2)16×128257//2128Conv1d(16→32, k3, p1)32×128长度不变MaxPool1d(2)32×64128//264Conv1d(32→64, k3, p1)64×64长度不变AdaptiveAvgPool1d(1)64×1全局平均得到 64 维特征Linear(64→4)4分类输出逻辑说明AdaptiveAvgPool1d(1) 是关键一层它把任意长度的特征图压缩成固定长度向量好处是即使输入长度不是 2048模型也能跑给换采样率或窗口长度的实验留了余地。全局平均池化还能显著减少参数量降低过拟合风险。squeeze(-1) 把 64×1 压成 64 维向量再进 Linear。BatchNorm 放在卷积和激活之间是常见做法先对卷积输出做归一化再过 ReLU梯度更平缓训练更稳。参数说明num_classes 从 config.py 读4 分类和 10 分类切换只改一个参数不动网络结构。stride8 在第一层用特征图尺寸一下子降到八分之一后续计算量可控padding32 保证卷积后边界信息不丢。中间两层小卷积核配合 padding1 保持长度不变由池化负责降维。3.3 为什么不用 2DCNN 或 LSTM见过不少人把 CWRU 数据做 STFT 变成时频图再喂 2DCNN这条路能通但代价很大。STFT 引入窗函数、窗长、hop 三个超参数换一组参数结果波动明显而且时频变换本身是固定的不会像卷积核一样在训练中优化等于在第一层就丢掉了原始分辨率的相位信息。2DCNN 的卷积核在时间和频率两个方向滑动但频率轴上的间隔与故障特征不是线性对应学起来效率低。LSTM 的问题在另一面。轴承故障的瞬态冲击是短时局部事件持续几毫秒LSTM 擅长长程依赖对这类短脉冲反而是浪费而且训练速度慢一个量级序列长度 2048 对 LSTM 来说已经不友好。1DCNN 的卷积核在时间轴上局部滑动计算可高度并行CWRU 一个 4 分类任务在单张普通显卡上几十秒到几分钟就能跑完这个迭代效率对调参非常重要。4. 训练、混淆矩阵与 t-SNE三个视角验证模型是否真的学到了4.1 main.py 的训练流程和超参数配置训练入口在 main.py核心参数集中在 config.py。一个能在 CWRU 上稳定出结果的配置大致是# config.py 关键参数 batch_size 64 epochs 30 lr 1e-3 weight_decay 1e-4 num_classes 4 # 改成 10 就是 10 分类任务训练循环的完整结构model CWRUcnn(num_classesconfig.num_classes) optimizer torch.optim.Adam(model.parameters(), lrconfig.lr, weight_decayconfig.weight_decay) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxconfig.epochs) criterion nn.CrossEntropyLoss() for epoch in range(config.epochs): model.train() for x, y in train_loader: pred model(x) loss criterion(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() scheduler.step() test_acc evaluate(model, test_loader) print(fepoch{epoch} train_loss{loss.item():.4f} test_acc{test_acc:.2f})逻辑说明Adam 加 CosineAnnealingLR 在类别均衡时最稳Adam 的自适应步长能容忍一定学习率偏差余弦退火在后期把学习率降下来减小 loss 曲面小坑里的震荡。weight_decay 对 CWRU 这种样本量只有几千的数据集作用明显设成 1e-4 不会压掉有效特征。参数说明batch_size 设 64 是因为样本长度 2048 点显存占用不高可以提到 128 加速。epochs 设 30 是因为 1DCNN 参数规模相对小CWRU 特征模式显著通常在 15~20 轮已经收敛30 轮是留了余量。训练过程中把每个 epoch 的 train_loss 和 test_acc 存下来后面排错要用。4.2 混淆矩阵重点看误分方向test.py 或 utils/visualize.py 里会跑测试集并产出混淆矩阵源码 results 目录下的 confuse_matrix_rate.xlsx 就是导出结果。核心代码结构from sklearn.metrics import confusion_matrix, classification_report model.eval() y_true, y_pred [], [] with torch.no_grad(): for x, y in test_loader: pred model(x).argmax(dim1) y_true.extend(y.tolist()) y_pred.extend(pred.tolist()) cm confusion_matrix(y_true, y_pred) print(classification_report(y_true, y_pred))关键注意点model.eval() 必须配 with torch.no_grad()前者关掉 Dropout 和 BatchNorm 的统计更新后者关掉梯度计算两者缺一个测试结果都会偏乐观且不稳定。真正值得关注的是分类报告里的 macro F1 而不是准确率因为 10 分类任务里如果某些损伤直径的样本切得少准确率会被多数类拉高F1 能反映少数类的真实表现。混淆矩阵里的非对角线元素指向两类问题相邻损伤直径互相混这是特征太接近内圈和滚动体互相混这是信号调制方式相似。4.3 t-SNE 可视化倒数第二层特征t-SNE.py 做的是把 64 维特征压缩到 2D 平面看类别是否自然聚簇。t-SNE 只保留近邻结构所以解释时不要看簇之间的距离只看簇是否分开。核心代码from sklearn.manifold import TSNE features extract_features(model, test_loader) # 倒数第二层输出形状 (N, 64) tsne TSNE(n_components2, perplexity30, n_iter1000, random_state42) embed tsne.fit_transform(features) scatter(embed[:, 0], embed[:, 1], clabels)perplexity 值得单独说。它是 t-SNE 衡量近邻带宽的参数通常在 5~50 之间。CWRU 测试集通常只有几百到一千多个样本perplexity 设 30 偏大设 15 更稳。如果图里四个簇互相交叠先别急着下结论说模型没学好把 perplexity 调小或换 random_state 再画一次。正常情况下正常类是一坨致密的簇三个故障类会分开但内圈和外圈的距离可能较近物理上合理因为两者的冲击谱在边带结构上有重叠。5. 复现后必做的三个验证数据泄漏、跨负载与 loss 曲线5.1 数据泄漏自查第一个自查是数据泄漏。如果你训练集和测试集是从同一个 mat 文件里随机切出来的相邻样本在时间上连续测试分数完全没有意义。检查方法把样本按来源文件分组用 GroupShuffleSplit 重新划分一遍看准确率掉多少。掉得越狠说明之前泄漏越严重。CWRU 常见做法是留出 3hp 负载的数据做测试其余负载训练这样的分数才敢写进论文。5.2 跨负载泛化测试第二个验证是跨负载。CWRU 有 0、1、2、3hp 四档负载信号幅值和信噪比都不同。在 0hp 上训练 4 分类拿到 99%换到 3hp 测试掉到 75%~85% 非常正常这不是代码 bug是域偏移。修复手段分两层最低成本的是把所有负载数据混合训练再留一个负载做测试让数据增强覆盖负载变化进阶做法是在特征提取器后面加一个域判别器用对抗训练把不同负载的特征分布拉齐这就是域适应。5.3 loss 曲线是最后的排错工具第三个技巧是读 loss 曲线。CWRU 训练有一个健康形态前 5 个 epoch 训练 loss 从 1.1 左右快速降到 0.1~0.01测试准确率同步冲到 95% 以上。如果 train_loss 下降但 test_loss 后期反弹是过拟合不是收敛失败如果 train_loss 从头到尾压不下去优先检查归一化是否用了全量统计量其次查学习率是否超过 1e-2再查标签是否与类别索引对齐。跑完一遍用这三条对照能定位绝大多数复现问题。本文还有配套的精品资源点击获取