☰
基于Paddle的一维CNN轴承故障识别:从数据管线到模型验收的完整复现
2026/10/26 4:06:01 网站建设 项目流程

简介:这份资源面向希望用深度学习做机械故障诊断的学习者与工程实践者,围绕西储大学轴承数据集,基于Paddle框架自定制一维卷积神经网络完成故障识别,并复现《基于卷积神经网络的轴承故障诊断算法研究》论文思路。压缩包共156个文件、约606.09MB,以52个pdparams与52个pdopt模型权重及优化器状态、40个mat振动信号数据、8个log训练日志为主,另含py脚本与ipynb笔记本,便于直接加载权重、复现训练与对照实验。已有1553人学习下载。读者可从中获得完整的1D CNN建模与训练流程、多组训练检查点与日志记录,用于分析收敛过程、调整卷积核与池化结构、比较优化器与损失函数设置,并借助mat数据验证正常与多种故障状态的分类效果,为设备预防性维护研究提供可复用的实验基础。

1. 从一份“跑不动”的复现说起:一维CNN做轴承故障识别到底难在哪

很多人拿到“用一维卷积神经网络做轴承故障识别”这个题目,第一反应是找论文、抄结构、堆层数,结果训练 loss 一路下降、验证集准确率却卡在 60% 上下不动,换数据集、调学习率、加 Dropout 全试一遍还是玄学。问题往往不在网络本身,而在数据切分方式和标签构造这两步就已经埋了雷。这份方案围绕西储大学轴承数据集,用 Paddle 框架自定制一维 CNN,把“论文里的算法”拆成可复现的数据管线、网络结构、训练脚本和验收指标四块,适合正在做故障诊断方向毕设或课设、需要一份能跑通也能讲清楚的设计参考的人。下面按需求、设计、实现、避坑、验收的顺序展开,关键处给出可直接改参数复用的代码。

2. 需求与数据管线设计:先把西储大学轴承数据集切成模型能吃的样子

2.1 任务定义与输入输出约定

这个项目的本质是一个多类别时序分类问题:输入是一段固定长度的振动加速度信号,输出是该信号所属的故障类别。西储大学轴承数据集(CWRU)的原始文件是.mat 格式,采样频率分 12k 和 48k 两档,包含正常状态(Normal)、内圈故障(Inner Race)、外圈故障(Outer Race)、滚动体故障(Ball)四大类,每类下又有 0.007、0.014、0.021 英寸等不同故障直径,组合起来常见做法是取 10 类或 4 类。我一般建议毕设阶段先做 10 类,答辩时能体现细粒度区分能力;如果时间紧,4 类也能讲清楚方法。

输入长度需要先定死。工程上的经验值是每个样本覆盖 1 到 2 个转频周期,12k 采样下取 1024 点比较稳妥,48k 采样下取 2048 点。这个长度既能让卷积核捕捉到冲击成分,又不至于让单样本太大拖慢训练。标签用整数编码,0 到 9 对应十个类别,训练时再转 one-hot 或直接交给交叉熵损失。

2.2 滑动窗口切分与数据集划分

原始信号是一条长序列,不能整条喂给网络,必须用滑动窗口切分成样本。窗口长度 1024、步长 512 是常用配置,重叠 50% 能增加样本量并缓解边界信息丢失。切分时有一个血泪经验:先按文件划分训练集和测试集,再各自切窗口,而不是先切窗口再随机划分。后者会让同一段信号的重叠窗口同时出现在训练和测试里,准确率虚高到 99%,答辩时被问一句“有没有数据泄漏”就翻车。

import numpy as np import scipy.io as sio def load_signal(mat_path, key): # 读取 CWRU 的 DE_time 驱动端加速度信号 data = sio.loadmat(mat_path) return data[key].flatten() def sliding_window(signal, win=1024, step=512): # 按窗口长度和步长切分,返回 (N, win) 数组 samples = [] for start in range(0, len(signal) - win + 1, step): samples.append(signal[start:start + win]) return np.array(samples, dtype=np.float32) def normalize(samples): # 逐样本 z-score,消除不同负载下的幅值差异 mean = samples.mean(axis=1, keepdims=True) std = samples.std(axis=1, keepdims=True) + 1e-8 return (samples - mean) / std

这段代码里win和step是最该先调的参数:窗口太短(小于 512)时冲击特征被截断,太长则单类样本数不够。normalize用逐样本标准化而不是全局标准化,是因为不同负载工况下信号幅值差异大,逐样本处理能让模型学到波形形状而非绝对幅值。划分比例按 7:2:1 给训练、验证、测试,验证集用于早停,测试集只在最后跑一次。

2.3 数据增强与类别平衡

CWRU 各类样本数并不均衡,正常状态文件少、故障类文件多。直接训练会让模型偏向多数类。通常的做法是对少数类做窗口重叠率更高的重采样,或者加轻微的高斯噪声增强。噪声标准差取原始信号标准差的 0.05 倍左右,太大就改变了故障特征。这一步不是必须,但如果测试集上某几类召回率明显偏低,先查类别分布再决定要不要增强。

3. 一维CNN网络结构设计:用 Paddle 自定制三层卷积加全局池化

3.1 整体结构选型与论文对照

参考的那篇卷积神经网络轴承故障诊断论文,核心思路是用一维卷积直接处理原始时序信号,省掉人工特征提取。我在这份复现里把结构定为:三层一维卷积(通道数 16→32→64)+ 每层后接 BatchNorm 和 ReLU + 最大池化 + 全局平均池化 + 全连接输出。选三层而不是更深,是因为 1024 点输入经过三次池化后特征图长度降到 128 左右,再深就容易过拟合,而且训练时间翻倍,课设环境扛不住。

卷积核大小统一取 16,这个尺寸能覆盖约 1.3ms 的信号片段(12k 采样下),刚好对应轴承故障冲击的典型持续时间。第一层用较大核(32)捕捉低频冲击、后两层用 16 和 8 逐步提取细节,也是一种常见变体,但为了和论文结构对齐,这里统一用 16,答辩时好解释。

3.2 Paddle 网络定义与参数说明

import paddle import paddle.nn as nn class BearingCNN(nn.Layer): def __init__(self, num_classes=10): super().__init__() self.features = nn.Sequential( # 输入 (B, 1, 1024) nn.Conv1D(1, 16, kernel_size=16, stride=1, padding=8), nn.BatchNorm1D(16), nn.ReLU(), nn.MaxPool1D(2), # -> (B, 16, 512) nn.Conv1D(16, 32, kernel_size=16, stride=1, padding=8), nn.BatchNorm1D(32), nn.ReLU(), nn.MaxPool1D(2), # -> (B, 32, 256) nn.Conv1D(32, 64, kernel_size=16, stride=1, padding=8), nn.BatchNorm1D(64), nn.ReLU(), nn.MaxPool1D(2), # -> (B, 64, 128) ) self.pool = nn.AdaptiveAvgPool1D(1) # 全局平均池化 -> (B, 64, 1) self.fc = nn.Linear(64, num_classes) def forward(self, x): x = self.features(x) x = self.pool(x).squeeze(-1) return self.fc(x)

padding=8配合kernel_size=16保证卷积后长度不变,池化负责降维,这样每层输出尺寸可预测,调试时不用反复算。AdaptiveAvgPool1D(1)替代展平接大全连接,参数量从几万降到几十,小数据集上过拟合风险明显下降。num_classes按实际类别数改,做 4 类就传 4。如果显存紧张,把第一层通道从 16 降到 8 也能跑,但准确率会掉 1 到 2 个点。

3.3 损失函数与优化器配置

分类任务用交叉熵损失,优化器选 Adam,初始学习率 1e-3,配合余弦退火衰减。批大小 64 是 1024 点输入下的折中值,太小梯度噪声大,太大泛化变差。训练轮数设 50,加早停(验证集 loss 连续 8 轮不降就停),避免无意义地跑满。

model = BearingCNN(num_classes=10) loss_fn = nn.CrossEntropyLoss() lr_scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=1e-3, T_max=50) optimizer = paddle.optimizer.Adam(learning_rate=lr_scheduler, parameters=model.parameters())

这里T_max要和训练轮数一致,否则学习率衰减节奏对不上。早停逻辑单独写在训练循环里,监控验证集 loss 而不是准确率,因为小数据集上准确率波动大,loss 更稳定。

4. 训练流程与评估实现:从数据加载到混淆矩阵输出

4.1 Paddle Dataset 封装与批加载

把前面切好的 numpy 数组包成 Paddle 的 Dataset,训练时打乱、验证测试不打乱。输入需要从 (N, 1024) 扩成 (N, 1, 1024),因为 Conv1D 要求通道维。

from paddle.io import Dataset, DataLoader class BearingDataset(Dataset): def __init__(self, samples, labels): self.samples = samples[:, None, :] # (N, 1, 1024) self.labels = labels.astype('int64') def __getitem__(self, idx): return self.samples[idx], self.labels[idx] def __len__(self): return len(self.labels) train_loader = DataLoader(BearingDataset(X_train, y_train), batch_size=64, shuffle=True) val_loader = DataLoader(BearingDataset(X_val, y_val), batch_size=64, shuffle=False)

shuffle=True只给训练集,验证和测试必须关掉,否则评估结果不可复现。labels转 int64 是 Paddle 交叉熵的硬性要求,忘了转会在第一个 batch 就报类型错误。

4.2 训练循环与指标记录

训练循环里每轮记录训练 loss、验证 loss 和验证准确率,保存验证 loss 最低的模型权重。这一步是后面答辩展示训练曲线的数据来源,别省。

best_loss = float('inf') for epoch in range(50): model.train() for x, y in train_loader: logits = model(x) loss = loss_fn(logits, y) loss.backward() optimizer.step() optimizer.clear_grad() lr_scheduler.step() model.eval() val_loss, correct, total = 0.0, 0, 0 with paddle.no_grad(): for x, y in val_loader: logits = model(x) val_loss += loss_fn(logits, y).item() correct += (logits.argmax(1) == y).sum().item() total += y.shape[0] val_loss /= len(val_loader) acc = correct / total if val_loss < best_loss: best_loss = val_loss paddle.save(model.state_dict(), 'best_model.pdparams')

optimizer.clear_grad()放在 step 之后是 Paddle 动态图的写法,和 PyTorch 的zero_grad位置一致。paddle.no_grad()包住验证段,省显存也提速。保存的是state_dict而不是整个模型对象,换环境加载更稳。

4.3 测试集评估与混淆矩阵

测试集只跑一次,输出总体准确率、每类召回率和混淆矩阵。混淆矩阵能直接看出哪两类容易混,比如内圈 0.007 和 0.014 经常互相误判,答辩时主动指出这一点比被问出来强。

from sklearn.metrics import confusion_matrix, classification_report model.set_state_dict(paddle.load('best_model.pdparams')) model.eval() preds, trues = [], [] with paddle.no_grad(): for x, y in test_loader: preds.extend(model(x).argmax(1).numpy().tolist()) trues.extend(y.numpy().tolist()) print(classification_report(trues, preds, digits=4)) print(confusion_matrix(trues, preds))

classification_report给出 precision、recall、f1,比单一准确率更有说服力。混淆矩阵用 sklearn 算,不依赖 Paddle,输出格式也通用。

5. 避坑与常见问题排查:这几处最容易让复现结果失真

5.1 准确率虚高到 99% 以上

现象是测试集准确率异常高,几乎满分。原因基本是先切窗口再随机划分,重叠窗口跨了训练测试边界,模型等于见过测试样本。解决办法是按原始文件划分数据集,再各自切窗口,重叠率控制在 50% 以内,并在报告里写明划分方式。

5.2 训练 loss 不降或震荡剧烈

现象是前几轮 loss 在 2.3 附近(10 类的随机水平)不动。原因通常是学习率过大或数据没标准化。先检查normalize有没有生效,再把学习率从 1e-3 降到 3e-4 试一轮。如果还不行,看 BatchNorm 的momentum默认值在小 batch 下是否合适,批大小低于 32 时可以把 momentum 调到 0.9。

5.3 验证集准确率高但测试集掉十几个点

现象是验证集 95%、测试集 80%。原因是验证集和测试集分布不一致,常见于按负载划分时某一负载样本过少。解决办法是划分时保证每个故障类别在每个集合里都有样本,按类别分层抽样,而不是纯随机。

5.4 换一台机器加载模型报错

现象是paddle.load后set_state_dict报键不匹配。原因是保存时用了model.state_dict()但加载时模型结构参数(如num_classes)和保存时不一致。加载前先按保存时的类别数实例化模型,再set_state_dict,顺序不能反。

5.5 训练速度慢到无法接受

现象是单轮超过 5 分钟。原因多是数据在 CPU 上逐样本标准化、没开num_workers。把标准化提前到 Dataset 构建阶段一次性做完,DataLoader加num_workers=2,速度通常能提升一倍以上。

6. 验收与答辩:用一张表把方案讲清楚

验收这套方案,我一般按四个维度准备材料,答辩时按这个顺序讲,评委顺着听不会乱。

验收维度具体指标合格线展示方式
数据管线划分方式、窗口参数、类别分布无数据泄漏,各类样本数可查划分脚本输出 + 类别统计表
模型结构层数、通道数、参数量参数量在 10 万以内结构打印 + 参数量统计
训练过程训练/验证 loss 曲线、早停轮次验证 loss 收敛且无剧烈震荡曲线图 + 最佳轮次说明
测试结果总体准确率、每类召回率、混淆矩阵10 类任务 90% 以上分类报告 + 混淆矩阵

答辩时最容易被追问的是“为什么用一维卷积而不是把信号转成时频图用二维卷积”。回答要点是:一维卷积直接在原始时序上做,省掉 STFT 的参数选择,端到端更干净,而且参数量小、训练快;时频图方法在低转速下分辨率受限,一维卷积对冲击成分更敏感。另一个高频问题是“换一个数据集还能不能用”,如实说需要重新调窗口长度和卷积核大小,因为不同采样率下冲击持续时间不同,不能直接套参数。

还有一个具体技巧:把混淆矩阵里误判最多的两类单独拿出来,画一段原始信号对比图,说明它们在波形上的相似性,这比空谈“模型还有提升空间”有说服力得多。评委看到你主动分析错误样本,会认为你真的跑过、想过,而不是只贴了个准确率数字。

从那以后我每次带人做这类复现,都强制先跑一遍数据划分检查脚本,确认训练集和测试集的文件名没有交集,再开始训练。这个习惯帮我省掉了至少两次答辩前夜返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询