尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MATLAB实现CNN-Attention一维时序信号分类完整实战

MATLAB实现CNN-Attention一维时序信号分类完整实战 简介基于注意力机制的卷积神经网络数据分类项目提供了完整的Matlab实现代码主要面向计算机、电子信息工程、数学等专业的学生适用于课程设计、期末大作业和毕业设计。资源包内包含六个文件其中有一个可直接运行的主程序、一个电子表格格式的案例数据集以及四张网络结构或分类结果的示意图压缩包总体积约一百六十二千字节非常轻量便捷。代码采用参数化编程方式关键参数可以根据需要灵活修改同时注释详细思路清晰支持从较早版本到近期版本的Matlab环境用户能够直接运行验证效果。目前已有六十七人学习适合刚接触深度学习分类任务的初学者。通过阅读和实践可以理解注意力机制如何提升卷积神经网络的分类性能掌握在Matlab中构建、训练和测试深度学习模型的基本流程为进一步的研究和工程应用打下基础。1. CNN-attention 分类脚本为什么放在 MATLAB 里跑拿到一批传感器时序数据要做故障分类时很多工程师的第一反应是去配 Python 和 PyTorch 环境但这类任务在 MATLAB 里几十行代码就能闭环数据导入、网络定义、训练、混淆矩阵、部署都在同一个 IDE 里完成。标题里的 CNN-attention 指的是在卷积神经网络中插入注意力模块让网络在分类时更关注序列里真正区分类别的局部片段而不是把整条序列同等看待。常见的 SE 通道注意力机制、CBAM 注意力机制在 MATLAB 深度学习工具箱里都能实现R2021a 之后还支持用 dlgradient 对自定义网络结构做自动微分训练。这篇文章从注意力机制原理讲到可直接运行的训练代码适合做一维信号分类、又不想跨语言折腾的工程师也适合第一次接触 CNN-attention 的 MATLAB 用户。2. SE 与 CBAM 注意力模块的原理和 MATLAB 实现选择2.1 通道注意力机制压缩-激励的基本思路通道注意力的出发点是卷积输出的每个通道对应一种特征模式但不同模式对当前分类任务的贡献不一样。SE 模块Squeeze-and-Excitation先把每个通道的空间信息压缩成一个标量再用两个全连接层学习通道之间的依赖关系最后用 sigmoid 输出一组 0 到 1 之间的权重乘回原来的特征图。这里“压缩”通常用全局平均池化完成。假设卷积输出特征图尺寸是 H×W×C对每个通道求空间平均就得到 C 个值。两个全连接层的作用是先用较小的隐层维度把 C 维压缩到 C/rr 是 reduction 比例通常取 4 或 16再扩张回 C 维。中间的 ReLU 引入非线性最后的 sigmoid 保证权重是平滑的放大或抑制系数而不是硬性选择。这样网络在反向传播时能学到“哪些通道值得被放大”相当于给卷积核加了一个可学习的优先级。这里不讨论多头自注意力机制的原因也很实际时序数据分类样本量通常在几千到几万多头自注意力的参数量大、对数据量要求高而通道注意力机制只增加少量参数就能提升分类精度配合一维卷积神经网络使用性价比最高。2.2 CBAM 注意力机制在通道之后再加空间维度CBAM 可以理解为 SE 的增强版。它先做一次通道注意力再做一次空间注意力。空间注意力的输入是特征图在通道维度上的平均池化结果和最大池化结果两者拼接后经过一个 7×7 卷积输出一张空间权重图同样乘回特征图。通道注意力部分 CBAM 和 SE 的差别是CBAM 同时使用全局平均池化和全局最大池化两个分支共享同一个多层感知机输出相加后过 sigmoid。最大池化能捕捉通道内响应最强的位置平均池化捕捉整体响应两者互补。空间注意力部分则让网络学会“特征图的哪些位置重要”这对图像上的目标检测任务更有效对一维传感器时序空间注意力相当于在时间轴上选择关键片段。2.3 MATLAB 里实现注意力的三条路线在 MATLAB 中实现 CNN-attention 有几种常见做法选型时主要看训练流程是否需要自定义。实现路线适用场景MATLAB 版本要求维护成本自定义层 trainNetwork希望继续使用内置训练选项和训练进度图R2019b 起支持自定义层需自己写 backward较高backward 容易出错dlnetwork dlgradient自定义损失、注意力分支、多任务结构R2021a 起支持 dlgradient 自动微分较低只需写 forwardPython 互操作调用 PyTorch 模型已有现成的 Python 注意力模型需要额外配置 Python 环境高两套环境都要维护我推荐大多数场景选第二路。trainNetwork 的层图里要用 multiplication 这类逐元素操作官方没有现成的“特征图按通道缩放”层必须自己写自定义层并实现 backward 函数而 dlgradient 能对 forward 里出现的 mean、max、sigmoid、乘法自动求导省去手写反向传播的工作。下面第三章的完整代码就是基于 dlnetwork 和 dlgradient 的。3. 用 dlconv dlgradient 写出可训练的 CNN-attention 分类代码3.1 数据准备先构造可复现的二分类时序数据为了不依赖外部数据集我用 MATLAB 合成两类一维信号一类是 8Hz 正弦波叠加噪声一类是方波叠加噪声。每类 100 个样本每个样本 512 个采样点。这个数据规模足够让 CNN-attention 在几十个 epoch 内收敛也方便读者替换成自己的 CSV 或 MAT 数据。%% 生成二分类一维信号 rng(0); numSamples 200; seqLen 512; numCh 1; X zeros(numCh, seqLen, numSamples); % C×T×N 格式 Y zeros(numSamples, 1); % 标签 0/1 for i 1:numSamples t (0:seqLen-1) / seqLen; if i numSamples / 2 X(1,:,i) sin(2*pi*8*t) 0.15*randn(1, seqLen); % 正弦类 Y(i) 0; else X(1,:,i) 0.8*sign(sin(2*pi*3*t)) 0.15*randn(1, seqLen); % 方波类 Y(i) 1; end end数据格式是按深度学习工具箱的惯例组织的第一维是通道数第二维是时间长度第三维是样本数。训练时要把每个样本 reshape 成 H×W×C×N 四维格式其中 H 固定为 1W 是序列长度C 是通道数。这样做是为了直接使用 dlconv 做一维卷积本质上是用一个高为 1 的二维卷积核完成时间维上的滑动。3.2 模型前向在卷积之间插入 SE 通道注意力模型结构设计为“卷积 SE 注意力 卷积 全局池化 分类头”。第一层卷积用 16 个长度为 8 的卷积核提取局部模式SE 模块对 16 个通道做权重重标定第二层卷积用 32 个长度为 4 的卷积核提取更高层特征最后全局平均池化后接输出层。function dlY cnnAttentionForward(dlX, params) % dlX: dlarray格式 SSCB尺寸 1×seqLen×1×batch x dlconv(dlX, params.conv1.W, params.conv1.B, Padding, same); x relu(x); % SE 通道注意力全局平均池化 → 降维 → 升维 → sigmoid s mean(x, [1 2]); % 1×1×16×batch s dlconv(s, params.se.fc1.W, params.se.fc1.B); s relu(s); s dlconv(s, params.se.fc2.W, params.se.fc2.B); s sigmoid(s); % 通道权重 x x .* s; % 广播缩放原特征图 x dlconv(x, params.conv2.W, params.conv2.B, Padding, same); x relu(x); x mean(x, [1 2]); % 全局平均池化 dlY dlconv(x, params.fc.W, params.fc.B); % 输出 1×1×2×batch end这里用mean(x, [1 2])完成 SE 中的“压缩”操作把每个通道的空间和时间信息平均成一个值。x .* s是 SE 的核心s 的尺寸是 1×1×16×batchx 的尺寸是 1×seqLen×16×batchMATLAB 的隐式扩展会把通道权重广播到 seqLen 的每一个时间点上。两个全连接用 1×1 卷积替代因为此时特征图空间维度已经是 1×1标准全连接和 1×1 卷积在数学上等价。3.3 参数初始化与梯度计算可学习参数统一放在一个多层结构体 params 里这样 dlgradient 能对整个结构体求梯度训练循环里也能直接用 adamupdate 更新。function params initParams() % 卷积核格式 SSCB: 高1, 宽kernelSize, 输入通道, 输出通道 params.conv1.W dlarray(glorot([1 8 1 16]), SSCB); params.conv1.B dlarray(zeros(16,1), C); % SE 降维层: 16 → 4 params.se.fc1.W dlarray(glorot([1 1 16 4]), SSCB); params.se.fc1.B dlarray(zeros(4,1), C); % SE 升维层: 4 → 16 params.se.fc2.W dlarray(glorot([1 1 4 16]), SSCB); params.se.fc2.B dlarray(zeros(16,1), C); params.conv2.W dlarray(glorot([1 4 16 32]), SSCB); params.conv2.B dlarray(zeros(32,1), C); % 分类头: 32 → 2 params.fc.W dlarray(glorot([1 1 32 2]), SSCB); params.fc.B dlarray(zeros(2,1), C); end function w glorot(sz) fanIn prod(sz(1:end-1)); fanOut sz(end) * prod(sz(1:end-1)); limit sqrt(2 / (fanIn fanOut)); w 2 * limit * rand(sz) - limit; end偏置用dlarray(zeros(16,1), C)声明成 C 格式。在较新的 MATLAB 版本中dlconv 支持这种单列向量偏置如果你使用的版本报偏置维度错误把 bias 改成普通数组zeros(16,1)即可功能不受影响。损失函数和梯度函数放在一起function [loss, grads] modelGradients(dlX, dlY, params) dlYpred cnnAttentionForward(dlX, params); % 1×1×2×batch dlYpred reshape(dlYpred, 2, []); % 转为 2×batch loss crossentropy(softmax(dlYpred), dlY); % dlY 是 1×batch 整数标签 grads dlgradient(loss, params); endcrossentropy 的第一个参数要求是类别数×样本数的概率矩阵所以用 reshape 去掉前两个长度为 1 的空间维度。dlY 使用 1 到 K 的整数编码不需要手动转 one-hot。dlgradient 会自动沿计算图反向传播SE 模块里的 mean、sigmoid、乘法都可以被自动求导覆盖。3.4 自定义训练循环Adam 更新与批次划分训练过程不再调用 trainNetwork而是自己控制 mini-batch 和优化器。每个 epoch 随机打乱样本索引按 miniBatchSize 分组对每一批调用 dlfeval 计算损失和梯度再用 adamupdate 更新参数。%% 训练参数设置 numEpochs 60; miniBatchSize 32; learnRate 0.002; numSamples size(X, 3); numIterPerEpoch floor(numSamples / miniBatchSize); params initParams(); averageGrad []; averageSqGrad []; %% 自定义训练循环 for epoch 1:numEpochs idxShuffle randperm(numSamples); for iter 1:numIterPerEpoch idxBatch idxShuffle((iter-1)*miniBatchSize 1 : iter*miniBatchSize); xBatch reshape(X(:,:,idxBatch), 1, seqLen, 1, miniBatchSize); dlX dlarray(xBatch, SSCB); dlY Y(idxBatch).; [loss, grads] dlfeval(modelGradients, dlX, dlY, params); [params, averageGrad, averageSqGrad] adamupdate(params, grads, ... averageGrad, averageSqGrad, epoch, iter, learnRate); end fprintf(Epoch %d, Loss: %.4f\n, epoch, extractdata(loss)); endadamupdate 是深度学习工具箱自带的优化器更新函数参数结构体里嵌套的子结构体它也能逐字段更新前提是 grads 与 params 字段一一对应。learnRate 取 0.002 而不是默认的 0.001是因为合成数据分布相对简单稍大的学习率收敛更快换成真实工业数据后建议回调到 0.001 再做对比。miniBatchSize 受 GPU 显存影响一维信号占用很小32 是一个保守值。3.5 测试与混淆矩阵训练完成后用全部数据前向计算一次取最大输出索引作为预测类别dlXAll dlarray(reshape(X, 1, seqLen, 1, numSamples), SSCB); dlYPred cnnAttentionForward(dlXAll, params); dlYPred reshape(dlYPred, 2, []); [~, YPred] max(extractdata(dlYPred), [], 1); YPred YPred(:); figure; confusionchart(Y, YPred, RowSummary, row-normalized, ... ColumnSummary, column-normalized);confusionchart 会同时显示每个类别的召回率和精确率。如果两类数据本身均衡准确率在 95% 以上说明注意力机制正常工作如果某类明显偏低优先检查该类别的信号形态是否与其他类别重叠而不是急着加网络层数。4. 训练参数、收敛判据与常见报错的调参记录4.1 attention 层位置和降维比例怎么设SE 注意力机制放在每个卷积块之后是通用做法但并非所有位置都能带来提升。浅层特征图通道数少注意力能学到的通道关系有限深层特征图通道数多权重重标定的效果更明显。常见做法是在第二个卷积块之后插入 SE第一层保持普通卷积这样前层提取基础波形模式后层再根据类别做通道选择。reduction 比例 r 决定 SE 模块中间层的宽度。r4 时中间层参数量大拟合能力强但容易过拟合r16 时参数量小更适合小数据集。实际调参时建议从 r4 开始观察验证集准确率的变化再尝试 r8。对本章的合成数据r4 和 r16 的最终准确率差异不大但真实工业数据上这个参数值得单独做一轮网格搜索。4.2 关键超参数速查表下面这张表总结了训练 CNN-attention 时最常调整的参数及其合理范围适用于一维时序分类。参数建议值调整方向学习率0.001 ~ 0.003Loss 震荡时减半收敛过慢时增大miniBatchSize16 ~ 64GPU 显存不足时减小数据量大时增大卷积核大小5 ~ 15序列采样率高时取大值捕捉长周期模式卷积核数量16/32 逐层翻倍特征复杂时增加注意同时增加正则SE reduction4 ~ 16通道数多偏 4数据量小偏 16dropout 比例0.2 ~ 0.5全连接层之前加防过拟合卷积核大小的直觉是它决定了网络一次能看到的序列长度8 个采样点大约覆盖信号一个周期的 1/64。如果信号周期较短、噪声较强可以缩短到 5如果关注的是缓变趋势可以加长到 15。4.3 训练不收敛时先看这三处Loss 变成 NaN八成是学习率过大导致的梯度爆炸。把 learnRate 降到 0.0005 重跑如果仍然 NaN检查输入数据里是否含有 Inf 或极端离群值。传感器数据常见的问题是某个通道缺失值被填成 00 本身不会造成 NaN但归一化时除以了 0 标准差就会出现问题。Loss 不下降但也没有 NaN首先确认数据有没有正确打乱。很多分类代码的坑在于数据集本身按类别顺序排列训练和测试切分时直接取前 70%导致测试集里只有一类样本。正确做法是在样本维度上随机打乱后再切分而且要保证同一段信号的相邻采样不会同时进入训练集和测试集。注意力模块没有生效的表现是加上 SE 前后准确率完全相同。这种时候打印 SE 模块输出的权重分布会发现所有通道权重都接近 1。原因通常是 reduction 比例过大中间层表达能力不足sigmoid 输出趋于饱和。把 r 从 16 改为 4同时检查 sigmoid 之前的输出是否集中在 0 附近。4.4 版本差异导致的常见报错在较老版本上运行本章代码时最常见报错是 dlconv 不接受 dlarray 格式的偏置。这种现象多见于 R2020a 及更早版本当时对自定义训练循环的支持还不完整。解决办法是把所有 bias 改成普通 double 数组训练时不更新偏置的梯度或者手动拼接梯度。R2023a 之后这些限制基本消失所以如果条件允许建议用新版 MATLAB 运行本代码。另一个常见问题是 dlgradient 遇到不支持的算子会直接报错例如在 forward 里使用了 extractdata 取中间值。注意力模块里尽量不要调用 extractdata让所有操作保持在 dlarray 域内自动微分才能贯穿整个计算图。5. 验证注意力真实效果与部署导出的三个实用技巧5.1 用输入梯度做显著性分析而不是只看准确率准确率提升只能说明模型整体变好不能证明注意力机制起了作用。一个更直接的验证方式是计算损失对输入信号的梯度梯度绝对值大的时间点就是网络分类时重点观察的区域。这个思路类似 Grad-CAM但不需要特征图反向传播实现成本低。function [loss, dX] inputSaliency(dlX, trueLabel, params) dlY cnnAttentionForward(dlX, params); % 单个样本 dlY reshape(dlY, 2, []); loss crossentropy(softmax(dlY), trueLabel); dX dlgradient(loss, dlX); end % 抽样一个测试样本 testIdx 10; dlXSingle dlarray(reshape(X(:,:,testIdx), 1, seqLen, 1, 1), SSCB); [~, dX] dlfeval(inputSaliency, dlXSingle, Y(testIdx)., params); saliency abs(extractdata(dX)); figure; t (0:seqLen-1) / seqLen; plot(t, squeeze(X(1,:,testIdx))); hold on; plot(t, saliency / max(saliency)); % 归一化到 0~1如果注意力机制真正学到了分类依据显著性曲线的高峰应该集中在信号突变沿附近而不是随机分布在整个序列上。对故障分类场景这意味着突变沿对应的特征被网络优先利用。5.2 把训练好的参数保存为 .mat推理脚本与训练脚本分离注意力网络的推理不依赖训练代码只需前向函数和参数文件。训练完成后保存参数结构体推理时单独写一个脚本加载避免每次预测都要重新生成数据。这是一个工程习惯也方便后续把同一套权重部署到多个脚本里。save(trained_cnn_attention.mat, params); % 推理脚本中恢复参数 S load(trained_cnn_attention.mat); params S.params;参数结构体里包含卷积权重和 SE 模块权重文件体积在几百 KB 以内。注意保存时同时保存输入数据的归一化参数例如训练集的均值 meanVal 和标准差 stdVal推理时对新样本做同样的归一化处理否则权重在训练时看到的输入分布和推理时不匹配准确率会明显下降。5.3 向 dlnetwork 迁移为 ONNX 导出做准备本章函数式的参数结构体适合快速验证但导成 ONNX 或部署到硬件时一般需要把模型组装成 dlnetwork 对象。常见做法是用 layerGraph 把卷积层、SE 的缩放操作封装成自定义层然后 assembleNetwork 成 dlnetwork再调用 exportONNXNetwork。函数式代码要改成自定义层需要补 backward这正好对应第二章表格里路线一的工作量。如果只是内部使用不追求跨平台部署那么直接在测试脚本里保留这行即可[~, YPred] max(extractdata(dlYPred), [], 1);这一步得到类别标签后所有验证分析都能继续推进。实际项目中我会把 5.1 的显著性可视化、5.2 的权重保存和这个预测逻辑固化成三个独立函数分别对应调试、存档和部署换新数据集时只需要改数据读取和类别数。代码里 numClasses 2 改成你的实际类别数网络最后一层的输出通道同步修改训练循环无需调整。本文还有配套的精品资源点击获取
返回列表