简介:面向机械故障诊断方向的深度学习学习者与工程实践者,这份资源围绕Paddle框架自定制一维卷积神经网络对西储大学轴承数据集进行故障识别,复现自基于卷积神经网络的轴承故障诊断算法研究论文。内容涵盖1D CNN的卷积层、池化层与全连接层结构设计,Paddle动态图与静态图两种模式下的模型搭建与训练,以及西储大学轴承数据集十类故障状态的预处理与输入组织,并涉及优化器、损失函数选择、数据增强与早停策略等训练细节,最后通过准确率、精确率、召回率和F1分数评估模型性能。资源包共156个文件,以52个pdparams模型权重、52个pdopt优化器状态、40个mat格式振动信号数据为主,另含训练日志、Python脚本与Jupyter Notebook,压缩包约606.09MB,便于直接复现实验并对照检查训练过程。已有1553人学习,适合希望掌握一维卷积故障识别完整流程的读者参考。
1. 一维卷积做轴承故障识别:为什么“把振动信号当图像看”反而更准
把一段轴承振动信号丢进二维卷积网络,先转成时频图,这是很多人对故障诊断的第一反应。但有一类实现反其道而行:不画图,直接把一维时序送进一维卷积核,让网络自己在原始波形上学特征。结论先摆出来——在凯斯西储大学轴承数据集这类标注干净、工况分明的场景里,一维 CNN 往往用更少的参数拿到更高的识别率,训练也更快。原因不玄学:振动信号的故障冲击本身就是沿时间轴分布的局部模式,一维卷积核在时间维度上滑动,天然匹配这种“短时冲击 + 周期重复”的结构,不需要先做时频变换把相位信息揉碎。这套复现围绕一维卷积网络展开,把故障识别从“手工提特征”推到“端到端学特征”,值得搞懂的是它每一步为什么这么设计。
2. 从振动信号到一维卷积:卷积核到底在时间轴上抓什么
2.1 轴承故障的物理形态决定了输入该长什么样
滚动轴承出现点蚀、剥落这类局部损伤后,每转一圈,滚动体碾过损伤点就会产生一次冲击,冲击激发结构共振,形成一段高频衰减振荡。这段振荡在时域上表现为周期性出现的脉冲串,脉冲间隔由故障位置决定:外圈故障的冲击重复频率跟轴转频挂钩,内圈故障因为随轴旋转,冲击还会被转频调制。也就是说,故障信息藏在“冲击的重复周期”和“冲击波形的包络形状”里。
传统做法是先做包络解调,把高频共振搬回低频,再看包络谱上的特征频率。这条路依赖人工选带通滤波参数,选错了特征频率就被淹没。一维 CNN 的思路是把这层手工设计交给卷积核:第一层大卷积核在原始波形上滑动,学到的权重组合本质上就是在检测“有没有一段类似冲击的形状”,后续层再把这些局部检测结果按时间间隔组合起来,隐式地学到重复周期。所以输入不需要转成图像,原始一维序列就够,前提是采样率足够覆盖共振频带。
常见的实现思路是:输入张量形状为(batch, 1, signal_length),第一层用较宽的卷积核(比如 16 到 64)配较大步长,快速降采样并提取冲击包络;后面接几层小卷积核(3 到 5)逐层抽象。这个“先宽后窄”的结构不是拍脑袋,宽核对应冲击的持续时间,窄核对应冲击之间的组合关系。
2.2 一维卷积的数学动作:滑动窗口里的加权求和
一维卷积对输入序列 $x$ 和卷积核 $w$ 做的是:
$$ y[i] = \sum_{k=0}^{K-1} w[k] \cdot x[i \cdot s + k] + b $$
其中 $K$ 是核长,$s$ 是步长,$b$ 是偏置。把它拆开看:核长决定“一次看多长的时间窗”,步长决定“多久挪一次”,权重 $w$ 是网络要学的“什么样的局部波形算有意义的冲击”。池化层随后在时间轴上取最大值或均值,最大池化保留最强的冲击响应,均值池化平滑掉噪声,两者对冲击型信号的选择不同——这也是后面调参时要盯的一个点。
用 Paddle 的paddle.nn.Conv1D表达,核心参数就这几个:
import paddle import paddle.nn as nn class BearingCNN(nn.Layer): def __init__(self, num_classes=10): super().__init__() # 第一层宽核:核长64,步长16,快速降采样并抓冲击包络 self.conv1 = nn.Conv1D(in_channels=1, out_channels=16, kernel_size=64, stride=16, padding=24) self.bn1 = nn.BatchNorm1D(16) self.pool1 = nn.MaxPool1D(kernel_size=2, stride=2) # 第二层窄核:核长3,在降采样后的序列上组合局部冲击 self.conv2 = nn.Conv1D(16, 32, kernel_size=3, stride=1, padding=1) self.bn2 = nn.BatchNorm1D(32) self.pool2 = nn.MaxPool1D(kernel_size=2, stride=2) self.relu = nn.ReLU() self.flatten = nn.Flatten() self.fc = nn.Linear(32 * 15, num_classes) # 15 由输入长度推得,见下文 def forward(self, x): x = self.pool1(self.relu(self.bn1(self.conv1(x)))) x = self.pool2(self.relu(self.bn2(self.conv2(x)))) x = self.flatten(x) return self.fc(x)kernel_size=64对应冲击衰减振荡的大致持续时间,stride=16让序列长度缩到约 1/16,既降了计算量又保留了冲击间隔信息;padding=24是为了让边界冲击不被裁掉。fc的输入维度 15 不是写死的魔法数,而是按输入长度逐层算出来的:若原始信号长 2048,经 conv1(核 64、步长 16、padding 24)后长度约(2048+48-64)/16+1 ≈ 128,pool1 后 64,conv2 保持 64,pool2 后 32——这里示例取 15 只是示意,实际必须按真实输入长度重算,否则Linear维度对不上会直接报错。这是复现时第一个容易翻车的地方。
2.3 为什么不用二维卷积:参数效率与相位保持
二维 CNN 处理时频图,需要先做短时傅里叶或小波变换,这一步把一维相位信息丢掉了,而且时频图的分辨率受窗长限制,窗长选大选小都有取舍。一维 CNN 直接在原始序列上学,省掉变换环节,参数也少:同样感受野下,一维核的参数是二维核的 $1/K$ 量级。对轴承这种冲击型信号,相位恰恰携带冲击时刻信息,保留原始波形让网络有机会学到冲击的精确时间位置,再通过多层组合还原重复周期。
代价是一维网络对输入长度和采样率更敏感,信号太长会让第一层计算量偏大,太短又覆盖不了几个冲击周期。所以预处理阶段要保证每个样本至少包含若干个完整的冲击重复周期,常见做法是按转频估算周期,截取长度取周期的整数倍。
3. 数据切分与标签构造:识别率上不去,先怀疑这里
3.1 滑窗切样本:重叠率决定样本数与信息泄漏
原始振动记录是长序列,要切成固定长度样本才能喂网络。常见做法是滑动窗口,窗长固定,步长小于窗长形成重叠。重叠的好处是样本数变多、边界冲击不被浪费;风险是训练集和测试集如果来自同一段连续记录且切分时没按记录分组,重叠窗口会让高度相似的样本同时出现在两边,测试精度虚高。
正确的切法是先按“记录文件/工况段”分组,再在组内切窗,保证测试集的窗口不来自训练集用过的连续片段。这一步不做,后面精度再高也是自欺欺人。样本长度一般取 1024 到 4096 点,取决于采样率和转频,目标是覆盖至少 3 到 5 个冲击周期。
3.2 标签体系:故障类型、损伤直径、负载三者的组合
凯斯西储大学轴承数据集的组织方式是:不同故障位置(内圈、外圈、滚动体)、不同损伤直径、不同负载工况各有对应记录。做分类任务时标签怎么定,直接决定任务难度:
| 标签方案 | 类别数 | 难度 | 适用场景 |
|---|---|---|---|
| 只分正常/故障 | 2 | 低 | 二分类基线 |
| 按故障位置分 | 4 左右 | 中 | 定位故障部件 |
| 位置+损伤直径 | 10 左右 | 高 | 细粒度诊断 |
| 再加负载工况 | 更多 | 很高 | 跨工况泛化 |
复现时如果目标是验证网络结构本身,建议先用“位置+直径”的十类左右方案,类别均衡、区分度明确;想验证泛化能力,再引入跨负载划分,即训练用部分负载、测试用另一部分负载,这时精度会明显下降,下降幅度才是真正值得看的指标。
3.3 归一化与增强:别让幅值差异骗了网络
不同负载下振动幅值差异很大,直接送网络会让模型学到“幅值大就是某类故障”的伪相关。常见做法是每个样本做零均值单位方差归一化,或者按记录做归一化。数据增强在一维信号上可用的是加性高斯噪声、小幅时间平移、幅值缩放,但要注意时间平移会改变冲击的绝对位置,如果标签依赖冲击间隔而非绝对位置,平移是安全的;如果依赖相位对齐,就要慎用。
提示:归一化统计量只能从训练集算,再应用到验证和测试集,否则测试信息泄漏,精度会偏乐观。
4. Paddle 训练循环里的关键实现:损失、优化器与维度对齐
4.1 组网之后先跑通一次前向:形状排查比调参重要
Paddle 的动态图模式下,维度错误会在前向时立刻抛出。建议组网后先用一个假 batch 跑一次前向,打印每层输出形状:
model = BearingCNN(num_classes=10) dummy = paddle.randn([2, 1, 2048]) # batch=2, 单通道, 长2048 out = model(dummy) print(out.shape) # 期望 [2, 10]如果Linear维度报错,就逐层打印x.shape定位是哪一层算错。这一步看着笨,但比训练跑一半崩掉省时间。卷积输出长度公式是(L + 2*padding - kernel_size) / stride + 1,向下取整,把这个公式代进每一层手算一遍,比反复试错快。
4.2 损失与优化器:交叉熵配 Adam 的默认取舍
多分类用nn.CrossEntropyLoss,它内部已经做了 softmax,网络输出直接给 logits,不要再加 softmax 层,否则等于做了两次。优化器用paddle.optimizer.Adam,学习率从 1e-3 起步是常见起点,配合StepDecay或余弦退火在训练后期降学习率。批大小受显存限制,一维信号样本小,64 到 128 一般够用。
loss_fn = nn.CrossEntropyLoss() optimizer = paddle.optimizer.Adam(learning_rate=1e-3, parameters=model.parameters()) scheduler = paddle.optimizer.lr.StepDecay(learning_rate=1e-3, step_size=20, gamma=0.5) optimizer = paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters())StepDecay每 20 个 epoch 把学习率乘 0.5,目的是让模型在后期精细收敛,避免在最优解附近来回跳。注意scheduler要作为learning_rate传进优化器,并在每个 epoch 末调用scheduler.step(),漏掉这一步学习率永远不降,训练曲线会在后期震荡。
4.3 训练循环与评估:准确率之外要看混淆矩阵
训练循环本身不复杂:前向、算损失、反向、更新、清零梯度。评估阶段除了整体准确率,一定要输出混淆矩阵,因为轴承故障里内圈和滚动体的冲击形态接近,这两类最容易互相误判,整体准确率会掩盖这种结构性错误。看混淆矩阵能判断是数据不够、标签有噪声,还是网络感受野不合适。
for epoch in range(epochs): model.train() for batch_x, batch_y in train_loader: logits = model(batch_x) loss = loss_fn(logits, batch_y) loss.backward() optimizer.step() optimizer.clear_grad() scheduler.step() # 每个 epoch 末更新学习率 model.eval() # 验证阶段用 paddle.no_grad() 关闭梯度,省显存model.train()和model.eval()的切换影响 BatchNorm 的行为,训练时用批统计量,评估时用滑动平均统计量,漏切会让评估结果不稳定。paddle.no_grad()在验证时关闭梯度记录,既省显存也避免误累积梯度。
5. 避坑与排查:复现时最容易翻车的几处
5.1 精度虚高:训练测试样本来自同一段连续记录
现象是测试准确率接近 99%,换一批数据就崩。原因是滑窗切分时没按记录分组,重叠窗口让训练和测试共享了几乎相同的片段。解决办法是先按记录文件分组,组间划分训练/验证/测试,组内再切窗,重叠率控制在 50% 以内。
5.2 损失不降:输入没归一化或标签越界
现象是 loss 停在某个值附近不动,或者直接变成 nan。常见原因是输入幅值范围差异大导致梯度爆炸,或者标签索引超出类别数。先检查输入是否做了零均值单位方差归一化,再检查标签最大值是否小于num_classes,最后看学习率是否过大。
5.3 维度报错:Linear 输入维度手算错误
现象是前向时Linear报形状不匹配。原因是卷积层输出长度没按公式逐层算,或者改了输入长度没同步改全连接层。解决办法是用假 batch 跑一次前向,逐层打印形状,把全连接层维度改成实际值,或者在全连接前加AdaptiveAvgPool1D把长度固定下来,避免输入长度变化时反复改代码。
5.4 评估波动大:BatchNorm 统计量没切对模式
现象是同一模型多次评估准确率跳动明显。原因是评估时忘了model.eval(),BatchNorm 还在用当前批的统计量。解决办法是评估前显式切eval(),评估后切回train(),并在验证集上多跑几轮取平均。
5.5 跨工况精度骤降:把工况当成了特征
现象是同一负载内精度很高,换负载就掉一大截。原因是网络学到了负载相关的幅值或频谱偏移,而不是故障本身的冲击周期。解决办法是训练时做幅值归一化、引入多负载数据做增强,或者用对抗式特征对齐思路让不同负载的特征分布靠近。
6. 设计取舍:一维 CNN 的边界在哪,什么时候该换思路
一维 CNN 在凯斯西储大学轴承数据集上能打,核心原因是这个数据集标注干净、工况有限、故障类型明确,冲击型信号的结构和一维卷积的归纳偏置高度匹配。但它的边界也很清楚:变转速工况下冲击重复周期随时间变化,固定核长的一维卷积抓不住这种时变周期,这时要么引入多尺度卷积核,要么换用能建模长程依赖的结构;强噪声环境下冲击被淹没,单靠时域卷积不够,需要频域信息或包络预处理配合。
我一般会这么理解这套复现的价值:它不是要证明一维 CNN 万能,而是把“端到端学特征”这条路径的最小闭环跑通——数据怎么切、标签怎么定、网络怎么搭、维度怎么对齐、评估怎么看。把这几个环节吃透,换任何信号类故障诊断任务都能迁移。常见的实现思路是在这个骨架上做增量:加残差连接缓解深层退化,加注意力模块让网络关注冲击密集区,或者用多分支不同核长并行捕捉多尺度冲击。
验证一个改动是否真的有效,别只看最终准确率,要看三样东西:训练和验证曲线的间距(判断过拟合)、混淆矩阵的对角线分布(判断类别间是否真的可分)、跨工况测试的下降幅度(判断泛化)。这三样比单一数字更能说明问题。从那以后我每次复现这类信号分类任务,都强制先跑一遍假 batch 前向、再按记录分组切数据、最后看混淆矩阵,这三步走完再谈调参。希望帮到你。
本文还有配套的精品资源,点击获取