简介:一维卷积神经网络在时间序列、音频信号和文本等一维数据的分类与特征提取任务中应用广泛。这份MATLAB源码实现了一维卷积神经网络的典型结构,面向深度学习初学者,可直接在深度学习工具箱环境中运行;压缩包内共一个M文件,包体大小约2KB,代码精简易读。文件完整展示了输入层、卷积层、ReLU激活、最大池化、全连接层、softmax与分类层的串联方式,并包含训练参数设置和分类预测流程,便于读者学习一维卷积特征提取和下采样原理。已有四千六百七十六人学习下载;通过阅读并运行该脚本,可掌握一个可运行的一维卷积神经网络训练与预测基线,并在此基础上调整滤波器宽度、池化步长、批量大小等参数,完成自己的时序或信号分类任务。
1. 一维卷积与MATLAB:时序信号任务里最该先试的模型
“CNN0926_一维卷积_matlab1D-CNN”这个标题,我第一眼扫过去就知道,这是用MATLAB实现一维卷积神经网络(1D-CNN)的项目任务,0926大概率是数据批次或模型版本号。在振动故障诊断、生理信号分类、频谱识别这类场景里,1D-CNN几乎是投入产出比最高的深度学习起点:不需要手工设计特征,不需要把波形转成图像做二维卷积,拿原始序列直接训练就行。配合MATLAB的深度学习工具箱,从搭建网络到跑通第一个模型,一个晚上足够。这篇文章按实际项目的推进顺序写:网络怎么搭、数据怎么准备、参数怎么调、坑在哪,最后给出一套可复用的验证与推理流程。适合正在做信号分类、故障诊断或时序预测,想在MATLAB里快速落地1D-CNN的开发者。
2. 1D-CNN模型搭建:从卷积机制到MATLAB网络定义
2.1 一维卷积的运作机制与二维卷积的本质区别
1D-CNN与图像领域常用的2D-CNN最大区别在卷积核的形状。2D-CNN的卷积核是h×w的矩阵,在图像的宽和高两个方向滑动;1D-CNN的卷积核是长度k的向量,只沿序列的时间轴滑动。每次卷积计算,是卷积核与当前窗口内长度为k的片段做逐元素乘累加,加偏置后过激活函数,产生一个输出值;把卷积核沿整个序列逐步移动,就得到一条一维特征图。
这个看似只是少了一个维度的差别,让1D-CNN在时序任务上具备两个优势。第一是参数效率高:卷积核共享意味着参数量只由核大小和通道数决定,不随序列长度增长。输入是一千点还是一万点,卷积层的参数量不变。第二是局部平移不变性:同一模式出现在序列不同位置都能被同一组卷积核激活,这对振动冲击、心拍波形这类重复出现的局部特征非常友好。我在做滚动轴承故障诊断时,原始振动信号里同一个故障特征在每个转频周期反复出现,1D-CNN不需要任何对齐预处理就能学到这种周期性模式。
感受野的计算也值得提一笔:第l层感受野 = 前层感受野 + (核大小-1)×前面各层步长之积。理解这个公式,就不难明白为什么长序列要多堆卷积块——只有层数够深,最后一层才能看到足够长的上下文。实际项目里如果序列长度是几千点,两个卷积块通常是远不够的,至少要到四个块,感受野才覆盖得了完整的信号周期。
MATLAB里实现1D-CNN的核心层有三类:卷积层convolution1dLayer、池化层maxPooling1dLayer和averagePooling1dLayer,再配合reluLayer、batchNormalizationLayer组成基础块。序列输入用sequenceInputLayer声明特征维度。不建议用convolution2dLayer把一维序列伪装成二维图像处理——虽然理论上可以排成单行图像,但宽度方向的卷积失去意义,网络变成一个别扭的结构,训练表现反而不如正规的1D-CNN,排错也更费劲。
2.2 网络结构设计:卷积层数、池化方式与输出层选择
结构设计决定模型上限。我一般按“输入层→卷积块→池化→卷积块→池化→展平→全连接→输出层”来组织。卷积层数取决于序列长度:几百个采样点的短序列,两到三个卷积块足够;几千甚至上万点的长序列,需要堆到四五个卷积块,让感受野逐层扩大,深层才能捕捉长距离依赖。信号长度不足时盲目加深,只会让网络尾部因为序列被压缩过狠而丢掉有效信息。
每个卷积块我习惯固定为“卷积+批归一化+ReLU”,池化单独放。批归一化放在卷积和激活之间,让每层输入分布更稳定,训练会稳很多。池化选择上,我的经验是分类任务优先最大池化,它保留的是某个局部区域是否出现了强特征,对冲击类信号敏感;回归任务或需要保留幅度信息的场景,平均池化更稳。池化窗口不要超过4,步长通常取2。窗口过大意味着信息压缩过狠,模型会对微小特征的变化完全不敏感,之后再想通过加深网络找回细节,成本很高。
输出层取决于任务类型。多分类任务用fullyConnectedLayer加softmaxLayer加classificationLayer,全连接层神经元数等于类别数;回归任务只接一个输出维度等于目标维度的fullyConnectedLayer,最后用regressionLayer。常见的翻车点有两个:一是分类任务漏写softmax层,直接把全连接输出交给classificationLayer,MATLAB不会报错,但loss曲线和准确率的含义变得不好解释;二是回归任务错用classificationLayer,训练时直接报标签类型不匹配,白跑一轮。
有人会问序列任务为什么不用LSTM。实际体验是LSTM训练慢、参数多、小样本下不稳定,而1D-CNN在小样本时序分类上通常收敛更快、结果更稳。如果对时间顺序的长程依赖有硬需求,再叠加LSTM或Transformer,否则先用1D-CNN跑基线,绝大多数场景已经够用。结构设计阶段建议先用analyzeNetwork(layers)检查网络,它列出每层输出尺寸和参数量。以256点单通道输入为例,一个常见的三卷积块结构如下表:
| 层 | 输出序列长度 | 通道数 | 输出形状 |
|---|---|---|---|
| sequenceInputLayer | 256 | 1 | 1×256×1 |
| conv1d(16,32,same) | 256 | 32 | 1×256×32 |
| maxpool(2,2) | 128 | 32 | 1×128×32 |
| conv1d(8,64,same) | 128 | 64 | 1×128×64 |
| maxpool(2,2) | 64 | 64 | 1×64×64 |
| conv1d(4,128,same) | 64 | 128 | 1×64×128 |
| globalAvgPool | 1 | 128 | 1×128 |
2.3 搭建1D-CNN的MATLAB代码:从序列输入到分类输出
下面给出一段我实际在用的分类网络定义,输入是单通道或多通道等长序列:
% 构建1D-CNN分类网络 numFeatures = 1; % 输入特征维度:单通道为1,多传感器则等于传感器数 numClasses = 5; % 分类类别数 layers = [ sequenceInputLayer(numFeatures, 'MinLength', 256) % 声明序列输入,限制最小长度 convolution1dLayer(16, 32, 'Padding', 'same') % 核大小16,32个输出通道 batchNormalizationLayer reluLayer maxPooling1dLayer(2, 'Stride', 2) % 池化窗口2,步长2 convolution1dLayer(8, 64, 'Padding', 'same') % 核大小8,64个输出通道 batchNormalizationLayer reluLayer maxPooling1dLayer(2, 'Stride', 2) convolution1dLayer(4, 128, 'Padding', 'same') % 核大小4,128个输出通道 batchNormalizationLayer reluLayer globalAveragePooling1dLayer % 全局平均池化,替代手动展平 fullyConnectedLayer(numClasses) softmaxLayer classificationLayer ];sequenceInputLayer的MinLength参数用于提前拦截过短序列,避免卷积和池化把长度压成非正数。三个卷积层的核大小从16递减到8再到4,是一种从粗到细的特征提取节奏:前层看大的局部轮廓,后层看细的局部差异。convolution1dLayer前两个参数分别叫filterSize和numFilters,前者是核长度,后者是输出通道数。'Padding','same'保证卷积后序列长度不变,省去手算边界影响,后续每层输出长度只由池化步长决定。
最后一个卷积块之后用了globalAveragePooling1dLayer,它对每个通道的整条序列做全局平均,输出一个长度等于通道数的向量。这样全连接层的输入维度与序列长度完全解耦——无论输入是256点还是512点,全连接层输入都是128维。如果不用全局平均池化,就得自己算展平维度,序列长度一变就得改代码,维度坑踩几次就老实了。
提示:这段网络定义默认输入是“特征×序列长度”的矩阵格式。如果你的数据是多传感器的同步采集,每个样本就是一个
C×L矩阵,numFeatures设为C即可。
3. 数据准备与训练流程:从原始波形到可用模型
3.1 数据组织格式:MATLAB如何接收序列数据
在trainNetwork流程里,分类任务的输入通常组织成两个部分:X是1×N的cell数组,N是样本数,每个cell里放一个numFeatures×sequenceLength的数值矩阵,行是特征维度,列是时间/序列步长;Y是对应的类别标签,用categorical向量表示。回归任务的Y是数值矩阵或cell数组。
这里有个容易搞混的点:单特征序列时,每个cell是1×L的行向量;多传感器同步采集时,每个cell是C×L的矩阵,C是传感器数。我第一次用时就犯过直接把N×L矩阵喂进去的错,trainNetwork立刻报输入维度不符合预期。排查的方式其实很简单——用whos看一眼变量的维度,cell数组的维度是1×N,不是N×1。MATLAB对这两种维度很敏感,经常有人因为转了cell数组但方向不对,卡在数据格式上半天。
预处理方面,1D-CNN对幅度尺度的敏感度比想象中高。不同样本之间幅值差一个量级,训练很容易出现loss振荡。我一般先做z-score标准化,也就是每个样本减去自身均值再除以标准差;如果样本有明确的物理单位,也可以做全局缩放,让幅度落在0到1之间。有一个必须记住的坑:归一化只能用训练集的均值和标准差去处理验证集和测试集,不能各自独立算。否则训练集和测试集的数据分布被改得不对等,验证准确率要么虚高要么失真,整个调参过程都建立在错误前提上。我做错过一次,排查了一整天才意识到是归一化泄漏。
3.2 trainingOptions关键参数:学习率、批次与验证策略
trainingOptions是训练选项的总入口。我常用的参数组合如下表:
| 参数 | 常用值 | 说明 |
|---|---|---|
| solver | 'adam' | 自适应优化器,时序数据收敛稳 |
| InitialLearnRate | 0.001~0.01 | 学习率过大loss震荡,过小收敛慢 |
| MaxEpochs | 30~100 | 小数据集30,大数据集50~100 |
| MiniBatchSize | 16~128 | 取决于显存和序列长度 |
| ValidationData | {XVal, YVal} | 监控过拟合,不能省 |
| ValidationFrequency | 20~50 | 每隔指定迭代次数验证一次 |
| L2Regularization | 0.0001~0.001 | 权重正则化,抑制过拟合 |
| Shuffle | 'every-epoch' | 每个epoch前打乱样本顺序 |
学习率是训练过程里最敏感的参数。用Adam时,0.01对很多信号分类任务来说偏大,0.001是更安稳的起点。如果训练loss前几步不降反升,优先把学习率除以10,而不是去改网络结构。批次大小和序列长度共同决定内存占用,序列很长(比如4096个采样点)时批次要相应减小。我在某项目中用4096点振动序列,batch设32已经比较吃显存,改成16才流畅,准确率并没有因此下降。
验证集一定不能省。指定ValidationData后,MATLAB会按ValidationFrequency周期性计算验证集上的loss和准确率,并画出训练进度图。判断训练是否健康的习惯是:训练loss下降、验证loss同步下降,正常;训练loss下降、验证loss上升,过拟合,应该回看正则化参数或降低模型容量,而不是继续加epoch。训练进度图里还有学习率曲线,能直接看到学习率在不同epoch的变化,这对判断是否手动干预非常有帮助。
3.3 完整训练流程与首个验证准确率
把网络和参数组合起来,一个完整的训练流程如下:
% 假设 XTrain / YTrain / XVal / YVal 已准备为 cell 数组和 categorical 向量 options = trainingOptions('adam', ... 'InitialLearnRate', 0.001, ... 'MaxEpochs', 60, ... 'MiniBatchSize', 32, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 30, ... 'L2Regularization', 0.0005, ... 'Plots', 'training-progress', ... % 打开训练过程可视化 'Verbose', true); % 命令行输出迭代信息 net = trainNetwork(XTrain, YTrain, layers, options); % 测试集评估 YPred = classify(net, XTest); accuracy = sum(YPred == YTest) / numel(YTest); fprintf('测试准确率: %.2f%%\n', accuracy * 100);trainNetwork前两个参数是训练数据和标签,第三个是层定义,第四个是options。训练结束后返回的net是SeriesNetwork或DAGNetwork对象,可以直接用classify做类别预测,也可以用predict拿每个类别的概率。classify返回的是categorical标签,predict返回N×numClasses的概率矩阵,做置信度筛选和混淆矩阵分析时用predict更合适。
训练完成后,第一件事不是急着部署,而是看验证集准确率与训练集准确率的差距。差距在5个百分点以内,模型基本健康;差距明显拉大,回第5章排查。我一般会在训练完把模型连同预处理参数一起存成.mat文件,省得每次跑实验都重新训练。如果数据量很大、训练动辄几小时,这一步更是必备操作。
4. 1D-CNN必调参数:卷积核、通道数与学习率的实用选法
4.1 卷积核大小怎么定:从采样率与信号周期推导
卷积核大小决定第一层能看到多长的局部窗口。核太小,网络只能看到几个采样点,形成不了有意义的局部模式;核太大,参数增多且容易丢失细节。一个可操作的推导思路是:先确定关心的最小特征周期。比如旋转机械故障诊断,转频是30Hz,采样率是3000Hz,那么一个振动周期有100个采样点,第一层卷积核取16到32,能看到约1/6到1/3个周期,既捕捉冲击特征,又不会把整个周期囫囵吞下。
深层卷积核可以逐渐缩小。第一层用大一倍的核做粗粒度特征,后面几层用较小的核做细粒度特征,配合池化扩大感受野。我常用的节奏是16→8→4,或者32→16→8,具体在验证集上试一次就能看出差异。那个“卷积核大小取奇数还是偶数更好”的争论,在1D-CNN里影响不大,不必为了对齐padding纠结,重点是核长度必须明显小于信号周期长度。
核大小对训练曲线的影响很直观。核太小时,训练loss下降慢,验证准确率上限偏低,因为单层感受野不够;核太大时,训练前期波动明显,模型更容易记住局部噪声。如果验证准确率始终上不去,先按信号周期算出合理范围,再在范围内试两个值,通常就找到合适的区间。调这个参数不需要做网格搜索,手动试两三轮足够。
4.2 通道数与网络宽度:参数量与数据量的平衡
通道数本质是网络提取独立特征的数量。第一层通道太少,学不到足够丰富的模式;太多则参数量暴涨,训练变慢且容易过拟合。我的一般起点是第一层32个卷积核,每经过一次池化翻倍,即32→64→128。如果数据量只有几百个样本,建议从16起步并加强正则化;数据量上万,64起步也不为过。通道数翻倍是主流做法,因为它控制着每层特征图的容量,翻倍节奏简单且容易调。
判断通道数合不合适,看验证准确率曲线的形态。如果验证准确率在训练后期还在明显上升,说明模型容量不够,可以加通道或加深网络;如果验证准确率很早平台化且训练loss持续下降,说明容量过剩,应该减通道或提前停。analyzeNetwork会直接显示每一层的输出尺寸和参数量,改结构之前跑一下,能快速发现瓶颈在哪一层。比如你发现第一层卷积的输出尺寸很大,意味着序列长度很长,活跃特征被压缩得很快,这时就该考虑增加池化层而不是盲目加通道。
4.3 学习率与优化器:训练稳定的最后一道闸
优化器选Adam基本不会错,它对学习率的容忍度比SGD高不少,省掉了手动调动量项。学习率我建议从0.001起步;如果训练曲线平滑且稳定下降,可以试着调到0.003或0.005加速收敛;如果loss前几步飙升或剧烈震荡,就降到0.0003再试。调学习率时看前50个迭代的走势就可以判断方向,不需要等100个epoch跑完。
除了基础学习率,L2Regularization和dropout是稳定训练的辅助手段。L2正则项加在损失函数上,惩罚过大权重,默认0.0001到0.001之间;验证准确率与训练准确率差值超过10个百分点时,优先把L2调到0.001以上,或在池化层后加dropoutLayer(0.5)。Dropout放在全连接层之前抑制过拟合效果最明显,放在卷积层之间则容易让特征提取不稳定,我一般只在网络尾部加一层。
调这些参数时一次只动一个。如果同时改学习率、通道数和正则化系数,模型效果变了也定位不到是哪个参数引起的。每次只改一个,跑完一轮训练,记录验证准确率,再动下一个,整个过程看起来慢,实际是最快收敛到最优参数组合的方式。
4.4 一组可复用的起步参数组合
如果你不想从零开始试,下面这组参数是我在多组振动和生理信号分类项目里的起点配置:三卷积块、核大小16→8→4、通道32→64→128、Adam、初始学习率0.001、批次32、L2正则化0.0005。序列长度256点以上、样本量几千时,这套组合通常能在一小时内跑到80%以上的验证准确率。之后再围绕验证集做单向参数调整,基本都能稳定提升。
5. 1D-CNN常见问题排查:训练不收敛、过拟合与维度报错
5.1 训练loss不下降或剧烈震荡
现象:训练进度图中loss曲线横着走,或者上下剧烈跳动,验证准确率始终徘徊在随机水平。这是1D-CNN入门时最常见的失败现场。
原因优先排查两点。第一,学习率是否过大。Adam下0.01对很多一维信号任务就偏大,表现是loss前期下降后突然跳高,训练曲线像锯齿。第二,数据标准化是否到位,原始波形幅值跨数量级时,梯度更新方向会被大样本主导,小样本特征完全学不到。第三,标签是否有错误或类别严重不平衡,某类样本占90%以上时,网络偏向学多数类,loss看似下降但少数类准确率为0。
解决路径是:把学习率降为原来的1/10,重跑20个epoch观察;检查预处理是否让每个样本幅度在同一尺度;统计类别数量,如果失衡,可在损失函数上做类别加权,或在数据准备阶段做重采样。我遇到过一次loss卡在固定值完全不动的情况,最后发现是标签文件里的类别编号从0开始,而MATLAB的categorical默认从1开始,错位之后网络学到的是完全错乱的映射关系。
5.2 验证集准确率远低于训练集
现象:训练集准确率超过98%,验证集只有70%左右,且差距随epoch增加而扩大。这是典型过拟合。
原因有两个方向。一是模型容量超过数据量承载能力,参数太多、样本太少,网络把训练集里的噪声也记住了。二是数据划分不当,一维信号数据里,同一设备不同批次采集的数据可能含有状态漂移,如果不做随机划分,验证集恰好落在与训练集差异很大的时间段,准确率就会虚低。
解决时先确认数据划分方式。我一般用cvpartition做分层抽样,保证每类样本在训练集和验证集中的比例一致。然后减模型容量或加正则化:减少卷积层通道数、把L2正则化从0.0001提高到0.001、加dropout。如果数据本身包含多个工况,尽量让训练集覆盖所有工况,验证集只做最终评估。过拟合问题里,数据划分不合理和模型容量过大同时存在的概率很高,不要只改一边。
5.3 维度不匹配报错
现象:训练或analyzeNetwork时报错,提示卷积层输出维度与下一层预期维度不一致,或者fullyConnectedLayer的输入维度与类别数不匹配。
原因在于1D-CNN里的维度变化主要来自池化层下采样。使用'Padding','same'时卷积层输出长度不变,但池化层每层都把序列长度减半。如果序列长度不是2^n的倍数,经过多层池化后长度可能变成非整数,或者被压缩过狠。fullyConnectedLayer期望输入是特征向量,如果前面是池化层输出而没有展平或全局池化,维度就对不上。
解决方法是三层保险:在sequenceInputLayer里显式设置MinLength;在网络最后用globalAveragePooling1dLayer,它把任意长度的特征图压成长度等于通道数的向量;每次改动结构后先跑一次analyzeNetwork(layers),它会列出每层输出尺寸并定位到具体是哪一层接不上。维度报错是最容易避免的一类问题,因为它完全可以在训练前由工具查出来,不要等到训练跑一半才回头看。
5.4 训练速度慢或显存不足
现象:GPU占用率低,训练一个epoch要几分钟;或者直接报显存不足,训练中断。
原因有两类。显存不足通常是序列长度×批次大小的乘积过大。1D-CNN参数量虽小,但每一层的激活值都要驻留显存用于反向传播,序列越长、批次越大,激活值越多。GPU利用率低则可能是数据读取和预处理在CPU上来回搬运,瓶颈不在计算而在数据管线。
解决时先减MiniBatchSize到16或8,观察显存占用;再检查输入序列是否过长,如果原始信号是4096点,先降采样到1024点训练,前提是降采样不丢关键特征。数据预处理可以预先做好并保存为.mat文件,避免每次训练都重复计算。某次我在一张老显卡上跑512点肌电信号分类,batch从64降到16后显存占用少了六成,训练速度反而更稳。不要一上来就换机器,先把batch和序列长度降下来。
6. 验证与落地:混淆矩阵、特征可视化与单样本推理
模型训练完并不等于任务结束。我每轮实验固定做的第一件事是画混淆矩阵,看错误集中在哪些类别之间,用的是confusionchart:
cm = confusionchart(YTest, YPred);如果发现类别A被大量误判为类别B,先看A和B的波形特征是否本来就高度相似。这时候不要急着调网络,可以先用activations提取网络某层卷积输出的特征图,看两类样本在特征空间里是否真的重叠:
featTrain = activations(net, XTrain, 'lastConv', 'OutputAs', 'rows');第二件事是保存模型和预处理参数,封装推理函数。我不喜欢每次预测时把标准化逻辑重写一遍,而是把均值、标准差和网络一起存进一个结构体,推理时一次性加载:
modelStruct.net = net; modelStruct.mu = muTrain; modelStruct.sigma = sigmaTrain; save('trained_1dcnn.mat', 'modelStruct'); % 新数据的在线推理 function label = predictOne(model, rawSignal) xNorm = (rawSignal - model.mu) ./ model.sigma; label = classify(model.net, {xNorm}); end上面这个函数处理的是单样本推理,但实际项目中我更推荐改成批量推理,并附带置信度阈值——当最大分类概率低于0.8时返回“未知类别”,而不是硬给一个标签。处理多分类信号时,这个习惯帮我在不少真实场景里拦下了大量垃圾输入,也让我意识到1D-CNN的“自信”不等于“正确”。
回看几轮调参经历,1D-CNN在MATLAB里最深的体会是:网络结构平台期很快,真正决定效果的是数据和前处理。我一度花了两周在卷积核大小上反复试,结果把标准化从“每个样本除以最大值”改成“训练集均值方差标准化”后,准确率直接跳了8个百分点。机器学习里很多看似玄学的东西,根上还是数据工程问题。以后遇到模型练不出来的情况,我会先问自己三个问题:数据长度对不对、标签对不对、标准化对不对,然后才去动网络。希望帮到你。
本文还有配套的精品资源,点击获取