MATLAB一维卷积神经网络(1D-CNN)滚动轴承故障诊断实战全解析
简介一维卷积神经网络简称1D CNN是处理时间序列、音频信号和文本数据等一维输入的常用深度学习模型。这份MATLAB源码包面向希望快速掌握1D CNN结构、有一定MATLAB基础的工程师和研究人员直接提供可运行的.m脚本完整展示了输入层、一维卷积层、ReLU激活、池化层、全连接层与softmax分类层的搭建过程并配有训练函数和预测函数的调用示例帮助读者理解卷积核滑动提取特征、下采样降维和输出概率分布等关键机制。资源仅含1个m文件压缩包大小2KB轻量精炼、便于逐行研读且无需额外数据集即可直接运行可自行调整卷积核数量、池化步长等超参数观察效果。目前已有4676人学习下载适合作为在MATLAB环境中构建一维卷积神经网络的入门参考也可修改后用于故障诊断、语音识别、金融时间序列分析等具体任务。1. 一维卷积在 MATLAB 里怎么用先从一段振动信号说起我第一次做滚动轴承故障诊断时手里只有一串几十万点的振动波形想用深度网络分类出正常、内圈故障、外圈故障和滚动体故障。图像分类那套 2D-CNN 思路根本放不进去把波形画成图再丢进网络不仅多了一道转换还会把时域上的连续性打散。后来换成了 matlab 1D-CNN也就是一维卷积神经网络直接在原始序列上做卷积效果出乎意料地稳定。这个方向的实用价值在于语音、振动、心电、工业传感器这类“一条数据就是一个样本”的场景1D-CNN 是投入产出比最高的模型之一不需要 GPU 集群不需要海量标注一台普通电脑用 MATLAB 就能把训练、验证、调参整条链路跑通。这篇笔记围绕一个模拟项目 X 的滚动轴承故障分类实验展开把数据准备、网络搭建、参数设置和踩坑过程完整拆开讲。2. 数据准备序列长度、通道组织和训练集划分2.1 sequenceInputLayer 要的数据格式cell 数组还是三维数组MATLAB 的sequenceInputLayer接受两种典型输入。第一种是 cell 数组每个 cell 里放一个numFeatures × sequenceLength的矩阵适合每个样本长度不一致的场景第二种是numFeatures × sequenceLength × numObservations的三维数组适合所有样本等长的情况训练时内存访问更紧凑。我第一次跑实验时想当然地把数据按“样本数 × 时间点数”组织结果网络第一层就报维度不匹配检查了半天才发现 MATLAB 仿真是按通道在前、时间在中的顺序来的。% 假设有 4 类故障每类 500 个样本每个样本 1024 个点单通道 numClasses 4; numSamplesPerClass 500; seqLen 1024; numFeatures 1; % 方式一cell 数组适合变长序列 seqs cell(numClasses * numSamplesPerClass, 1); labels strings(numClasses * numSamplesPerClass, 1); for c 1:numClasses for i 1:numSamplesPerClass seqs((c-1)*numSamplesPerClass i) {randn(numFeatures, seqLen)}; % 占位数据 labels((c-1)*numSamplesPerClass i) string(c); end end labels categorical(labels); % 方式二三维数组等长序列时更省内存 % dataArray randn(numFeatures, seqLen, numClasses*numSamplesPerClass);这段代码里最关键的是seqs的结构外层是样本数内层矩阵第一维是特征通道数第二维是时间步数。如果数据源本身是seqLen × numFeatures的格式读进来后要立刻用data data转置。我在某项目里接过一批从采集卡导出的 CSV每行是一个采样时刻每列是一个通道直接喂给sequenceInputLayer就报错转置之后问题立刻消失。cell 数组的好处是灵活但训练速度比三维数组慢我在样本量超过 2 万条时会优先考虑等长截断然后转成三维数组。2.2 从原始信号到数据集定长截取、标签与随机划分工业采集的原始信号往往是一条很长的连续记录不能直接拿去训练需要先按固定长度滑窗截取。截取长度没有一个万能值我一般围绕转动频率来定先找到信号的主频周期 T然后保证每个窗口里至少包含 10 到 20 个完整周期。窗口太短故障特征被切碎窗口太长样本数减少而且计算量上升。截取完成后还要做归一化常用的做法是每个窗口独立做 z-score避免不同工况下幅值差异影响卷积核的学习。function seqs slidingWindowExtract(data, winLen, step) % data: 1 x N 原始信号 % winLen: 窗口长度 % step: 滑动步长一般取 winLen/2 保证样本量 n numel(data); idx 1:step:n-winLen1; seqs cell(numel(idx), 1); for k 1:numel(idx) seg data(idx(k):idx(k)winLen-1); seg (seg - mean(seg)) / std(seg); % z-score 归一化 seqs{k} seg(:); % 转为 1 x winLen 的行向量 end end这里的滑窗步长直接决定样本量。步长取窗长一半样本量约等于原始长度的两倍除以窗长步长越小重叠越多训练样本越充足但相邻窗口高度相关验证集划分时必须小心。我建议按连续时间块做划分而不是随机打乱否则同一段原始信号截出来的窗口会同时出现在训练集和验证集里验证分数虚高得离谱。划分代码用randperm前先按块编号分组保证验证集里的窗口在原始信号时间轴上与训练集完全不相邻。% 按块划分避免同一转子的相邻窗口串进训练集和验证集 numBlocks numel(seqs) / 10; blockIdx repelem(1:numBlocks, 10); valBlocks randperm(numBlocks, round(numBlocks*0.2)); valMask ismember(blockIdx, valBlocks); trainSeqs seqs(~valMask); trainLabels labels(~valMask); valSeqs seqs(valMask); valLabels labels(valMask);这段划分代码的思路是先按连续 10 个窗口归为一个块再从块层面随机抽 20% 做验证。很多人在这步偷懒直接randperm每个样本结果 80% 的验证准确率上线后一换新数据就跌到 60%。这不是模型问题是数据划分泄漏。块状划分牺牲了一点训练样本量但换来的是验证指标可信这笔交易很划算。2.3 可选的预处理滤波、去除基线漂移与重采样并不是所有预处理都必要。卷积层本身能学习到一定的频率特征但传感器采集的原始信号经常带有直流偏移和低频漂移这会占掉卷积核的输出动态范围。我通常只做两件事一是去掉均值二是用高通滤波器滤掉 5 Hz 以下的趋势项。重采样只在信号长度不一致且无法定长截取时使用因为重采样会改变故障特征频率成分能不用就不用。% 高通滤波去除基线漂移 fs 25600; % 采样率按实际设备设 [b, a] butter(2, 5/(fs/2), high); dataFiltered filtfilt(b, a, dataRaw);滤波器和滑动窗口的顺序有讲究。先滤波再截窗避免边界效应在每个窗口里重复出现。filtfilt是零相位滤波不会产生相位偏移处理振动信号时比普通filter靠谱得多。如果数据是多个传感器通道每个通道独立做同样的预处理但别在通道之间做归一化否则不同通道的量纲差异会被抹掉反而损失了关键判别信息。3. 搭建 1D-CNN 网络从卷积核到全局池化的每一层配置3.1 标准骨架四层卷积加两层池化为什么够用一个能出结果的 1D-CNN 并不需要堆砌太深。我常用的骨架是序列输入层 → 两到三层convolution1dLayer→ 每层后接批量归一化和 ReLU → 中间插一层maxPooling1dLayer→ 最后用globalAveragePooling1dLayer把时间维度压平 → 全连接 → softmax。这里有一个容易误解的地方全局平均池化不是必须的但加上之后模型参数少了很多而且对序列长度变化的容忍度更高。layers [ sequenceInputLayer(1) convolution1dLayer(32, 64, Padding, same) batchNormalizationLayer reluLayer maxPooling1dLayer(2, Stride, 2) convolution1dLayer(16, 32, Padding, same) batchNormalizationLayer reluLayer globalAveragePooling1dLayer fullyConnectedLayer(4) softmaxLayer classificationLayer ];每一层的作用需要说清楚。第一个卷积层用 64 个卷积核去捕捉短时波形模式这相当于一组可学习的带通滤波器池化层把时间分辨率减半降低计算量同时增加感受野第二个卷积层在池化后的特征上提取更高层级的组合模式。globalAveragePooling1dLayer把每个通道在所有时间步上的值求平均变成 32 维的特征向量接一个 4 分类的全连接层细节处其实等价于把每个特征图压缩成一个数然后做分类。这个结构在 1024 点输入下参数量很小训练一轮几十秒非常适合在 CPU 上迭代实验。3.2 关键参数filterSize、numFilters、stride 与膨胀filterSize决定卷积核在时间轴上覆盖多远。针对 25600 Hz 采样率、主频 30 Hz 左右的振动信号一个周期约 853 个点。经验上卷积核覆盖 1/4 到 1/2 个周期能提取到完整的形态特征所以我通常从 16 到 64 之间选。核太小只看到局部毛刺核太大会把几个周期的信息平均掉。numFilters则是特征图的宽度64 起步数据集大可以翻倍但 CPU 训练时 128 以上速度明显下降。参数作用我的常用设置注意事项filterSize卷积核时间长度16~64与信号主频周期匹配numFilters输出特征图数量32~128太多会过拟合且训练慢Stride卷积核滑动步长1步长大于 1 会显著丢信息Padding边界填充samecausal 用于时序预测场景Dilation空洞间隔1 或 2大于 2 后感受野跳跃太大stride默认就是 1很多人调 stride 省计算量但在短序列上不划算信息丢失比省下的时间更值钱。Dilation参数我在语音和长序列场景用过设置为 2 可以扩大感受野而不增加参数量但如果原始信号已经做了池化再用膨胀就容易跳过关键局部特征。这里的取舍原则是先固定其他参数只调 filterSize得到一组不错结果后再试 Dilation不要一开始就多维同时乱改。3.3 R2022b 之前没有 convolution1dLayer 的替代写法这是老版本 MATLAB 用户的经典痛点。convolution1dLayer是相对晚近才加入的官方层如果你的环境比较旧一个稳定替代方案是把输入从序列层换成特征层再用convolution2dLayer把宽带维度设为 1实现等效的一维卷积。% 老版本替代方案用 2D 卷积模拟一维卷积 layers [ featureInputLayer(1) % 注意这里换成 featureInputLayer convolution2dLayer([1, 32], 64, Padding, same) batchNormalizationLayer reluLayer maxPooling2dLayer([1, 2], Stride, [1, 2]) % ... 后续结构类似 ];对应的数据预处理要调整为四维数组格式是numFeatures × 1 × seqLen × numObservations。也就是说把时间维度当成二维图像的长边高为 1。这个方案能跑但代价是数据在内存里多了一个冗余维而且某些针对序列的优化手段不可用。如果项目允许我还是建议升级到带官方一维层的版本MATLAB 官方的原生活性脚本都是基于新接口写的。模拟项目 X 一开始就卡在版本兼容上换新环境后同样的代码训练速度快了大约 30%。4. 训练与验证训练选项、混淆矩阵和错误样本回看4.1 trainingOptions 必调参数学习率、批次与早停训练选项是 1D-CNN 实验里最常被忽视的部分。trainingOptions(adam)全家桶里只有几个参数值得认真调其他默认值在大多数数据集上表现得不错。首先要调的是学习率InitialLearnRate默认 0.01但我在序列分类任务上几乎总是降到 0.001 左右否则训练几轮 loss 就开始震荡。其次是MiniBatchSizeCPU 训练时 32 到 64 是甜点区间太大内存吃紧太小梯度不稳定。最后是ValidationFrequency按迭代次数而不是轮数设置这样能在训练初期就观察到过拟合迹象。options trainingOptions(adam, ... MiniBatchSize, 32, ... MaxEpochs, 50, ... InitialLearnRate, 0.001, ... ValidationData, {valSeqs, valLabels}, ... ValidationFrequency, 20, ... Shuffle, every-epoch, ... Plots, training-progress, ... Verbose, 1); net trainNetwork(trainSeqs, trainLabels, layers, options);ValidationData用 cell 数组把验证集序列和标签包在一起格式必须和训练集一致。Shuffle, every-epoch每一轮重新打乱训练样本这是防止模型记住训练顺序的关键。训练进度图里要盯两条曲线的走向训练 loss 一直降但验证准确率停滞说明模型开始死记样本验证 loss 先降后升是过拟合的经典信号这时候应该调小 epoch 或增大L2Regularization。4.2 训练过程怎么看loss 曲线翻车的几类迹象没有可视化工具时直接看命令行打印的每轮数据也能判断。训练 loss 从 1.3 附近开始逐步往下走属于正常如果第一轮 loss 就在 0.3 以下大概率是标签和数据错位了。更常见的情况是 loss 下降很快然后卡住比如从 1.1 掉到 0.2 后不再变化这通常是卷积核数量不够或者网络容量见底需要加宽而不是加深。还有一种翻车是 loss 直接变成 NaN多半是学习率过大导致梯度爆炸或者输入数据里含有 NaN 值。% 训练前先检查数据是否有 NaN for i 1:numel(trainSeqs) if any(isnan(trainSeqs{i}(:))) fprintf(样本 %d 含 NaN\n, i); end end训练完成后的第一件事不是看准确率而是看混淆矩阵。准确率 90% 可能掩盖了某一类完全没学会的问题。我在模拟项目 X 里出现过 97% 的总体准确率但展开混淆矩阵发现“滚动体故障”这个类别 80% 被分成了“正常”因为该类样本数量少且特征幅度弱。这种情况下要优先补充该类样本或者对振动幅值做增强单纯调网络参数作用有限。4.3 验证与部署混淆矩阵、导出与预测延迟验证阶段用classify跑一遍验证集输出分类标签后画混淆矩阵。对误分类样本我的习惯是逐条回看原始波形定位是特征重叠还是噪声干扰。predLabels classify(net, valSeqs); figure; confusionchart(valLabels, predLabels);% 误分类样本回看 misIdx find(predLabels ~ valLabels); for i 1:min(5, numel(misIdx)) figure; plot(valSeqs{misIdx(i)}); title(sprintf(真值: %s, 预测: %s, ... string(valLabels(misIdx(i))), string(predLabels(misIdx(i))))); end部署到工程环境时先把训练好的网络保存下来然后在新的预测代码里用load重新加载。save的格式是普通 mat 文件工程上紧凑的做法是只保存干净的网络对象不带训练选项和中间变量。单条 1024 点信号的预测时间通常在毫秒级完全满足产线上的实时分类要求。5. 一维卷积在 MATLAB 里的避坑清单我踩过的 6 个真坑5.1 维度报错一直在说“数据格式不对”但没往转置想现象sequenceInputLayer后接convolution1dLayer训练报错说输入维度与层不匹配。原因数据从 CSV 读进来是时间 × 通道而 MATLAB 序列层期望通道 × 时间。解决统一用transpose对数据转置或者在读取阶段直接按矩阵转置读入。我在模拟项目 X 里把这个转置逻辑单独封装成一个函数所有数据入口都过一遍之后再也没有因为这个问题中断训练。5.2 老版本没有官方一维卷积层现象代码里写convolution1dLayer报错“未定义函数或变量”。原因低版本 MATLAB 没有这个内置层。解决先用ver(nnet)查看工具箱版本低于支持范围的用convolution2dLayer加宽度 1 的等效方案数据 reshape 成四维数组即可。如果项目周期长建议直接升级环境替代方案虽然能跑但在模型迭代和部署阶段会一直挖坑。5.3 随机划分带来的数据泄漏现象验证准确率 95%换一批新数据直接掉到 70%感觉模型白训练了。原因相邻滑窗是同一段原始信号切出来的随机划分后彼此高度相似验证集相当于“开卷考试”。解决按连续时间块划分训练集和验证集在时间轴上不能重叠。代码见 2.2 节的blockIdx方案。5.4 类别不均衡让准确率失真现象总体准确率 90% 以上但混淆矩阵里少数类几乎全军覆没。原因模型倾向于把所有样本都判成多数类。解决先看每一类的召回率用混淆矩阵而不是总体准确率做评判然后对少数类做滑窗增密采撈或者按类加权损失。加权损失在 MATLAB 里需要自定义分类层最简单的落地方案是直接增加少数类的训练样本数量。5.5 loss 出 NaN排查预处理比调参更重要现象训练到第二轮 loss 变成 NaN之后所有数值都不再更新。原因输入数据里有 NaN 或 Inf或者学习率过大导致梯度值溢出。解决训练前扫描整个数据集剔除异常样本把学习率从 0.01 降到 0.001 重试。如果两个排查都不解决问题用一个非常浅的网络先跑通再逐步加深网络定位是哪一层引起的数值不稳定。5.6 模型上线后预测不对回查发现训练集没做同样的归一化现象离线验证表现好部署到新设备上发现预测结果异常。原因部署数据的均值方差和训练时不一致z-score 参数用了全量数据而不是每个窗口独立计算。解决把训练阶段的均值方差保存下来部署预测时用同一组参数做归一化坚决不能在预测时重新计算统计量。这一点在数据分布会随工况漂移的场景里尤其重要我在离线实验中吃过一次亏后就把它写进了代码规范。6. 进阶玩法把 1D-CNN 配进一个能持续迭代的流程里模型跑通只是第一步真正能落地的是搭一套快速迭代的流程。我现在的做法是把数据准备、网络搭建、训练验证拆成三个独立脚本数据脚本负责从原始信号滑窗生成数据集并保存为 mat 文件网络脚本只读数据训练完把模型存成单独文件验证脚本加载模型和验证数据输出混淆矩阵和误分类波形。这样每次调整网络参数或者补充新数据都不需要从头跑一遍。多通道数据在 1D-CNN 里处理起来很直接把所有传感器通道堆叠成numFeatures × seqLen的矩阵即可通道数对应特征通道数。比如三轴加速度计的三路信号可以直接当 3 通道输入卷积核在每个时间点同时看到三个方向的信息不需要额外做特征融合。如果任务从故障分类变成剩余寿命预测需要把分类层换成回归层并在训练选项中把损失函数改成回归对应的指标。% 保存训练统计量部署预测时复用 scaler.mean globalMean; scaler.std globalStd; save(scalerParams.mat, scaler);我把 1D-CNN 当序列数据建模的第一选择已经有一段时间了。它不算花哨但胜在训练快、参数少、可解释性强数据量不够时也扛得住。某次实验里我用它替代一个两层的 LSTM 网络参数量少了一个数量级准确率反而提升了两个百分点。从那以后我的流程就定了先花十几分钟跑通一版 1D-CNN 作为基线再来判断值不值得上更复杂的模型。希望这篇笔记能帮你在自己的数据上少走几个弯特别是维度和数据划分这两块值得宁可慢一点也要把基础打牢。希望帮到你。本文还有配套的精品资源点击获取