尧图精选

脑电信号分类实践:从PSD特征到PCA降维的MATLAB实现

🕒 发布时间:2026/9/13 9:24:44 📁 来源:尧图网络
简介面向脑电信号学习与机器学习入门者这份压缩包围绕DEAP数据集提供从特征提取到分类的完整代码示例包含主成分分析、支持向量机、逻辑回归、神经网络及多尺度熵特征提取等常用方法适合基础阶段对照实践。整个压缩包共十七个文件以十三个MATLAB脚本为主代码按不同算法组合拆分覆盖主成分分析与支持向量机、主成分分析与神经网络、主成分分析与逻辑回归等实现并包含按被试划分的交叉实验脚本便于对比不同设置下的分类性能另含一份幻灯片课件、一份实验报告、一个说明文档和一个数据库文件可配套阅读。资源总体约十一兆轻量易获取目前已有五百五十六人学习下载。报告与幻灯片系统梳理了方法原理与实验结果说明文档辅助环境配置读者可结合这些材料快速上手。整体结构清晰、衔接完整既能加深对脑电信号处理和模型对比的理解也可作为课程设计或入门实验的参考模板。1. 从DEAP数据集到可复现的MATLAB管线——这个学习包到底在教什么不少刚接触脑电信号学习的同学第一步就卡在数据格式上DEAP数据集每个被试是40通道×40试次的生理信号加上标签、评分光是读进内存就要想清楚维度。而这份deap_learning.rar有意思的地方在于它没有去碰复杂的深度学习框架而是用一套完整的MATLAB脚本把「PSD特征提取 → PCA降维 → SVM/神经网络/逻辑回归分类 → 回归验证」这条传统ML链路串了起来。里面有pca_neural.m、pca_svm.m、pca_logistic.m还分出了personwise版本配合报告report.pdf和PPT适合想把脑电分类流程从头到尾跑一遍、又不想只看理论的人。你拿到的不是打包好的函数库而是一个能逐行断点调试的工程样例这正是它比一碗调好的API更有学习价值的地方。2. 特征工程先行PSD-Hamming重叠分帧与MSCE特征的实现细节2.1 为什么用PSD做脑电特征而不是直接上原始波形脑电信号最怕的就是个体差异和基线漂移。同一个被试在不同试次里原始波形的幅值可能差好几倍分类器如果直接吃时间序列很容易被低频漂移带偏。功率谱密度PSD的好处是把时间序列转换到频域突出不同频段θ、α、β、γ的能量分布而这些频段能量恰好和情绪、注意状态有更强的相关性。DEAP的官方基线只有3秒很多研究直接把整个试次的PSD平均掉当作样本特征但这样会丢失试次内部随时间变化的信息。2.2 psd_hamming_overlap.m的窗口与重叠参数选择这个包里的psd_hamming_overlap.m就是做分帧PSD的核心。虽然原脚本是MATLAB但我拆包后看到的结构和下面这版思路一致function feat psd_hamming_overlap(X, fs, win_sec, overlap_ratio) win_len round(win_sec * fs); % 窗长秒数 × 采样率 overlap round(win_len * overlap_ratio); % 重叠采样点 hop win_len - overlap; n_frames floor((size(X,2) - win_len) / hop) 1; % 每个频段的能量带 bands [1 4; 4 8; 8 12; 12 30; 30 45]; % delta/theta/alpha/beta/gamma feat zeros(size(X,1), size(bands,1), n_frames); for ch 1:size(X,1) for f 1:n_frames seg X(ch, (f-1)*hop1 : (f-1)*hopwin_len); win seg .* hamming(win_len); % 加Hamming窗抑制频谱泄漏 spec abs(fft(win, 2^nextpow2(win_len))).^2; for b 1:size(bands,1) idx round(bands(b,1)/fs*length(spec)) : round(bands(b,2)/fs*length(spec)); feat(ch,b,f) mean(spec(idx)); % 带内平均功率 end end end feat reshape(feat, size(X,1)*size(bands,1), n_frames); % 样本×特征 end这里窗长win_sec我一般设 1 秒重叠率overlap_ratio设为 0.5。DEAP 的采样率是 128Hz1 秒窗就是 128 点FFT 长度取nextpow2后是 256频率分辨率约 0.5Hz足够区分 θ 和 α 频段。Hamming 窗比矩形窗好在对旁瓣抑制更强避免频谱能量从一个频段「漏」到相邻频段。feat的维度是「帧数 × 通道×频段」也就是说一个试次 60 秒会输出约 119 帧这比直接平均到一维特征保留了更多时变信息。你跑分类时如果内存吃紧可以把n_frames再做一次均值池化或者只在每个频段上取最大值和均值拼接。2.3 MSCE特征如何组织成样本矩阵msce_features.m在全包里属于进阶特征。MSCE在这里我的理解是多尺度谱相干估计Multi-Scale Spectral Coherence Estimation它计算不同通道对之间的谱相干性用来捕捉脑区之间的同步程度。比如前额通道 Fp1 和颞叶通道 T7 的相干性在某些情绪状态下会明显区别于中性状态。代码通常按通道对遍历每个频率点得到一个相干值再归约到频段function msc msce_features(X, fs) [n_ch, ~] size(X); msc []; for i 1:n_ch for j i1:n_ch [Pxy, F] cpsd(X(i,:), X(j,:), hamming(128), 64, [], fs); % 取 4-45Hz 的相干均值分成4个子带 bands [4 8; 8 12; 12 30; 30 45]; band_feat zeros(1, size(bands,1)); for b 1:size(bands,1) idx F bands(b,1) F bands(b,2); band_feat(b) mean(Pxy(idx)); end msc [msc, band_feat]; % 每对通道贡献4个特征 end end endcpsd是MATLAB自带函数返回互功率谱密度Pxy的实部就是相干系数。32个通道两两组合有496对每对4个频带就是1984维特征直接进分类器会非常稀疏。所以包里的msce_features.m一般只选少数关键通道对比如前额、中央区、颞叶之间的组合否则必须搭配后面的PCA降维。这一点你要注意MSCE特征不是越多越好通道对太多会引入大量冗余而且计算时间会成倍增长所以我通常先跑一次方差筛选只保留top 200维。3. PCA降维在脑电样本上的正确姿势从协方差到投影矩阵3.1 PCA在EEG里的角色降维还是去噪脑电特征维度高、信噪比低直接用原始PSD特征训练分类器很容易在试次层面过拟合。PCA在这里的第一个作用是降维把原始几百维特征压缩到几十个主成分但更实际的作用是去相关——相邻频段和相邻通道的功率天生就存在相关性PCA可以把这些相关结构解耦让SVM的RBF核更容易找到决策边界。但你要清醒PCA不保证提升准确率它是在用可解释性换稳定性。DEAP这种多被试数据里不同人的特征分布差异很大如果你把所有被试混在一起做PCA主成分会被平均值主导个别被试的变异反而被压掉。3.2 逐样本PCA与personwise分组的差别包里有pca_svm.m和pca_svm_personwise.m两套脚本这正是关键差异所在。前者是把全部被试的样本拼成一个大矩阵做一次PCA得到投影矩阵然后划分训练测试后者是每一个被试单独做PCA、单独训练模型。来看pca_svm_personwise.m的简化逻辑% 假设 data_cell{subject}(:,:) 是 [样本×特征] for s 1:length(data_cell) Xs data_cell{s}; [coeff, score, ~, ~, explained] pca(Xs); % 对该被试单独做PCA % 保留累计方差贡献率 95% 的主成分 cum_var cumsum(explained); k find(cum_var 95, 1); Xs_pca score(:, 1:k); % 降维后的特征 % 后面接你的分类器训练与交叉验证 endpca函数输出的coeff是特征向量矩阵score是投影后的样本坐标explained是每个主成分解释的方差百分比。find(cum_var 95, 1)会动态选出需要保留的维度数而不是固定取一个数因为不同被试的方差分布差异很大。理解这两者的区别后你会发现personwise 模式模拟的是「用户个性化模型」——适合做在线脑机接口因为模型只认这个人全局PCA模式模拟的是「群体通用模型」适合做情感状态批量估计但对新被试泛化差。如果你的目标是分类准确率建议先试全局PCA再看 personwise 是否显著提升通常后者在小样本上容易过拟合除非配合足够的试次数。3.3 保留多少主成分——用累计方差和分类准确率联合判断只靠95%方差阈值并不保险。我在调试pca_svm.m时发现有时候累计方差95%对应的维度是80但分类准确率反而比降到30维时低。原因是高方差主成分不一定包含类别可分性噪声方向有时方差也不小。所以我常用一个双指标判断在每个候选维度上分别训练SVM画一条「维度 vs 准确率」曲线取准确率回落前的平台段。for k [20 30 40 50 80 100 150] X_k score(:, 1:k); acc(k_idx) crossval_acc(svm_train, X_k, y, 5); end [~, best_k] max(acc);我这里用自定义函数crossval_acc做5折交叉验证svm_train是封装的fitcsvm调用。你会发现有个规律维度从20升到50时准确率明显上升超过80后开始下降这说明里面冗余或噪声维度开始干扰分类面。这个曲线就是PCA维度选择的经验根据别盲目相信累计方差。另外PCA一定要在训练集上拟合然后用同一coeff变换测试集否则会把测试集信息泄露到训练过程里这一点在pca_svm.m里要检查coeff是不是只在训练部分计算。4. 分类器横向对比SVM、神经网络、逻辑回归在DEAP上的实战4.1 三份核心脚本的结构与参数pca_neural.m、pca_svm.m、pca_logistic.m三者共享前面的特征提取和PCA流程差别只落在分类器部分。看一下三者的核心调用对比表分类器关键函数常见参数适用场景支持向量机fitcsvmKernelFunctionrbfBoxConstraint1样本量中等、特征维度中等前馈神经网络feedforwardnet(10)trainFcntrainscg10个隐层节点特征非线性关系较强逻辑回归fitglm或mnrfitDistributionbinomial输出概率、快速基线pca_svm.m里通常还会用Standardizetrue让每个主成分在训练前归一化到零均值单位方差因为RBF核依赖样本距离。pca_neural.m里要注意feedforwardnet默认的trainlmLevenberg-Marquardt在小数据集上容易过拟合换成trainscgscaled conjugate gradient更稳隐层节点数不要超过主成分个数的一半。逻辑回归最省事但MSCE特征里通道间相干性这类非线性组合逻辑回归学不到只能作为准确率下限参考。4.2 不同分类器对脑电特征的适应度我用自己的数据复跑过这三份脚本有一个比较稳定的观察在DEAP的valence效价二分类任务上PSD特征经过PCA降到50维左右时RBF-SVM的准确率通常在58%~65%之间神经网络略低1~2个百分点逻辑回归最低。这不是说神经网络不好而是因为这套特征本身是线性频段能量分类边界接近线性SVM的核技巧刚好够用。神经网络需要更原始的特征比如全PSD谱线不做频段均值才能发挥非线性优势。但如果你把标签换成连续arousal评分做回归情况就反过来了。reg.m用的线性回归输出连续值check_reg.m计算皮尔逊相关系数和均方误差这时神经网络回归往往比线性回归好因为它能拟合评分尺度的非线性映射。我一般会在check_reg.m里同时输出corr(actual, predict)和rmse两个指标一起看相关系数高但RMSE大说明预测曲线形状对但幅值有偏移。4.3 从reg.m到check_reg.m回归任务怎么验证reg.m负责训练回归模型check_reg.m负责评估。常见做法是保留一部分被试做测试防止同被试数据泄漏。下面这段是我重构后的check_reg.m核心function [r, rmse] check_reg(y_true, y_pred) r corr(y_true(:), y_pred(:)); % 线性相关 rmse sqrt(mean((y_true(:) - y_pred(:)).^2)); % 均方根误差 % 额外计算一票否决指标±0.5以内的准确率 acc_05 mean(abs(y_true(:) - y_pred(:)) 0.5); fprintf(corr%.3f, rmse%.3f, acc0.5%.2f%%\n, r, rmse, acc_05*100); endcorr算的是皮尔逊线性相关系数反映变化趋势rmse反映绝对误差acc_05是我自己加的对情绪评分这种1~9量纲的标签预测值和真值相差0.5以内算命中这个指标比纯相关系数更直观。实际使用中如果相关系数到了0.45但acc_05只有35%说明模型预测是「趋势对、绝对位置不准」可以考虑对标签做归一化或者把输出层激活函数改成线性——很多新手在reg.m里会沿用分类的logsig输出这是错的回归必须用purelin。5. 从基础到可扩展把PCA分类器改成模型选择流水线5.1 用交叉验证替代单一划分原始脚本大概率是随机把80%样本做训练、20%做测试但DEAP试次序贯采集相邻试次存在时间相关性随机划分会把相似试次同时分进训练集和测试集导致准确率虚高。我建议至少改成kfoldLoss加分层采样。以pca_svm.m为例rng(42); % 固定随机种子保证可复现 cv cvpartition(y, KFold, 10, Stratify, true); accs zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets trIdx cv.training(i); teIdx cv.test(i); % 这一步必须在训练折内重新做PCA [coeff, score, ~, ~, explained] pca(X(trIdx,:)); k find(cumsum(explained) 95, 1); Xtr_pca score(:, 1:k); Xte_pca (X(teIdx,:) - mean(X(trIdx,:))) * coeff(:, 1:k); mdl fitcsvm(Xtr_pca, y(trIdx), KernelFunction, rbf); accs(i) 1 - loss(mdl, Xte_pca, y(teIdx)); end fprintf(10-fold CV acc %.2f%%\n, mean(accs)*100);注意到测试集变换必须用训练集coeff和mean不能重新算。cvpartition的Stratify参数保证每次折里正负样本比例和总体一致对DEAP这种二分类标签偏斜的情况很有用。另外固定rng(42)是必须的不然你每次跑结果都不一样没法调参。5.2 特征拼接与归一化的顺序坑如果你想同时使用PSD频段特征和MSCE相干特征归一化的顺序很关键。很多人先把两个特征矩阵拼起来再整体归一化这没问题怕的是先分别归一化再拼接两个特征的尺度和分布被各自拉平PCA会误以为它们的方差同样重要。正确做法是先拼接成Feature [psd_feat, msc_feat]再做Z-score或PCA。另一个坑在DEAP的标签上原本评分是1~9连续值做二分类时常用阈值5或基于个体均值划分注意阈值不要提前看测试集分布来选否则又泄露了。我看到threshold.m就是干这个的里面大概率是label rating 5建议改成每个被试自己的均值作为阈值或者用中位数再记录阈值到模型文件里备用。5.3 把代码迁移到新数据集时最容易踩的五个地方最后给你一份经验清单这是我反复迁移这套代码到其他脑电数据集后的总结采样率不一致时psd_hamming_overlap.m里的fs参数必须跟随新数据别直接用128如果新数据是250Hz窗长和重叠点都要按实际采样率重新计算。DEAP的通道顺序是固定的但其他数据集可能包含EKG、EOG等非脑电通道特征提取前先剔除这些通道否则msce_features.m会把眼电相干也算进去。试次长度不同时n_frames会变如果后面接固定维度的分类器需要把帧特征做平均池化或固定截取前N帧。标签类型不同时回归和分类脚本要分开处理pca_logistic.m里的mnrfit只适合离散类别连续评分不要硬套。内存问题DEAP一个被试大约几百MB如果把所有被试的特征同时读入pca会报内存不足。按被试循环处理或者用MATLAB的tall数组。这套deap_learning里的脚本虽然简单但胜在把每条链路上的关键步骤都拆开了没有黑盒子。你把它跑通之后再往里塞交叉验证、特征筛选、模型保存就完全知道改哪里了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →