资讯动态

帕金森脑电识别MATLAB工程:SVM分类与EEGLAB预处理全解析

发布时间:2026/10/25 14:17:24 来源:尧图企业网站定制
简介这是一套面向脑电信号分析研究者的帕金森病识别系统源码与数据集基于MATLAB GUI与EEGLAB工具完整覆盖从脑电数据采集分析、特征提取到SVM分类诊断的流程。压缩包共18个文件具体包括6个mat数据文件含帕金森患者与正常对照的原始脑电及特征数据、6个m脚本实现特征计算、GUI逻辑与SVM训练、5个fig界面文件可视化操作界面以及1个doc说明文档整体约21.95MB结构清晰便于查阅。目前已有157人参与学习并下载。通过该资源可快速上手构建自己的脑电识别实验学习如何利用EEGLAB预处理数据、设计交互界面、提取特征并调用SVM分类器预览中还提供数据缩放与模型优化相关的辅助脚本便于理解SVM分类前的处理环节适合课程设计、毕业设计或科研入门参考也可作为项目模板。1. 帕金森脑电识别一套不用从零搭的MATLAB完整工程帕金森病的脑电信号分类听着是纯科研方向实际动手时最花时间的往往不是分类算法本身而是数据文件怎么组织、GUI界面怎么调通、特征怎么和SVM的输入格式对齐。这套工程正好把这三件事打包成了一份完整的MATLAB项目基于MATLAB 2016编写集成EEGLAB辅助工具包从帕金森患者脑电数据的采集分析起步到计算需要的特征值再到SVM诊断分类全程都有对应的脚本、界面和测试数据。解压之后不需要再到处找脑电数据wenti.mat、zheng.mat这些文件已经把患者组和正常组分好能直接跑通实验链路。适合正在做脑电课题的学生、想把SVM快速用到真实脑电特征上的研究者以及需要参考MATLAB GUI工程写法的开发人员。2. 工程文件拆解fig、m、mat三类文件的分工和数据链路2.1 入口脚本和fig界面的对应关系解压“帕金森脑电识别系统的建设和研究.rar”以后你大概率会和我一样先愣一下unsstitled.fig、a1.m到a4.m、scaleForSVM.m、wenti.mat、zheng.mat、preictal2.mat……文件名不统一界面文件、脚本、数据文件混在一起。先说结论这不是单个主程序一跑到底的工程而是“GUI总控 分步处理脚本 数据/特征文件”的组合得理清依赖再按顺序运行。先说入口。项目里有四个主要脚本a1.m、a2.m、a3.m、a4.m和一个unsstitled.m分别对应a1.fig、a2.fig、a3.fig、a4.fig和unsstitled.fig。按照MATLAB GUIDE工程惯例fig文件存界面布局m文件存回调逻辑。unsstitled.m大概率是GUI主入口里面通过按钮回调去调用a1到a4完成不同阶段的处理。命名里的unsstitled其实应该是untitled属于打包时保留了GUIDE默认文件名的情况不影响运行但会影响你在GUIDE里重新编辑界面时的显示名称。判断谁是主入口、谁是被调用函数最快的办法是打开unsstitled.m搜索按钮回调函数里有没有process_a1或者a1(这样函数调用痕迹。常见写法是这样% 在GUI按钮回调里调用a1.m里的处理函数 pushbutton1_Callback(hObject, eventdata, handles) % hObject是当前控件句柄eventdata是事件数据handles是全局句柄结构体 % 如果a1.m定义的是function process_a1()直接调用 process_a1(); % 处理完以后刷新界面数据 guidata(hObject, handles); end这里有个关键判断如果a1.m是以function开头的函数文件就可以直接在回调里调用如果它是纯脚本形式没有function关键字那只能通过run(a1.m)或evalin(base, ...)执行变量容易在工作区里冲突。我建议拿到压缩包后先把每一个m文件打开看前两行凡是function开头的就是可调用函数一上来就是变量赋值的就是脚本。这套工程里a1到a4大概率是函数因为GUI回调要传handles结构体纯脚本很难做参数传递。2.2 mat数据文件里到底存了什么数据文件是这套资源最值钱的部分因为不需要额外去医学数据平台找帕金森脑电数据。wenti.mat和zheng.mat分别对应帕金森患者问题组和正常人对照组的脑电数据wenti_tezheng.mat是患者组的特征文件zhengchang_tezheng.mat是正常组的特征文件tezheng就是“特征”的音译。preictal2.mat这个名字有意思preictal是癫痫研究里“发作前期”的术语出现在帕金森工程里大概率是数据集整理时沿用了公共脑电数据集的命名习惯不是分类目标不用纠结。直接load文件看变量结构比猜快得多% 查看特征文件的变量名、维度和类型 clear; clc; load(wenti_tezheng.mat); whos % 假设输出显示变量名为features_wenti类型double大小30x20 % 含义30个患者样本每个样本20个特征 load(zhengchang_tezheng.mat); whos % 正常组特征文件行数代表正常样本个数列数必须和患者组一致 % 假设输出是30x20说明两组样本量正好平衡这里要特别留意维度对齐问题。SVM训练时患者组和正常组拼成一个大矩阵假设患者组是P×M正常组是Q×M那么特征维度M必须完全相同。如果一个文件是20维另一个是21维SVM训练时直接报维度不一致。这个工程的文件命名已经把对应关系标清楚了wenti_tezheng对zhengchang_tezhengwenti对zhenga1.fig对a1.m下载后不建议改任何文件名改了就得同步修改GUI回调里的引用。2.3 推荐运行顺序和样本标签设计把文件职责搞清楚后运行顺序就清晰了。我一般按下面几步走用unsstitled.m打开GUI主界面在界面里触发a1.m加载原始数据wenti.mat和zheng.mat由a2.m完成特征提取生成wenti_tezheng和zhengchang_tezheng用scaleForSVM.m做特征归一化用a3.m或a4.m完成SVM模型的训练和结果可视化如果不想在GUI里一步步点按钮也可以在命令行直接跑。注意每个脚本执行前要确认前置数据已经加载到工作区% 按依赖顺序执行假设各m文件是脚本形式 clear; clc; % 第一步加载患者组和正常组的原始数据 load(wenti.mat); load(zheng.mat); % 第二步提取特征并保存为特征文件 % 这一步假设a2.m内部会生成wenti_tezheng和zhengchang_tezheng两个变量 run(a2.m); % 第三步归一化特征scaleForSVM.m内部会处理 run(scaleForSVM.m); % 第四步设置标签患者组为1正常组为0 labels [ones(size(wenti_tezheng, 1), 1); zeros(size(zhengchang_tezheng, 1), 1)]; features [wenti_tezheng; zhengchang_tezheng]; % 第五步训练SVM模型 model fitcsvm(features, labels, KernelFunction, rbf, Standardize, true);这段代码里labels的设计是核心前一部分患者组是1后一部分正常组是0顺序必须和拼接后的特征矩阵完全对应。如果wenti_tezheng在前而zhengchang_tezheng在后那labels必须先1后0。一旦顺序错位训练出来的模型分类结果就是反的测试时看着准确率正常实际全错。提示fitcsvm是MATLAB自带统计工具箱里的SVM实现scaleForSVM.m是libsvm工具包的配套函数。如果工程统一用了libsvm的svmtrain和svmpredict那建议全程保持一致不要混用两套接口。3. 特征提取与SVM分类从EEG信号到诊断结论的关键逻辑3.1 为什么选SVM做帕金森脑电分类脑电分类任务里样本量通常不大几十到几百例是常态特征维度却可能到几十甚至上百维。这种小样本高维数据场景下SVM的目标函数自带正则化项比BP神经网络更不容易过拟合也比随机森林在小样本上的稳定性更好。线性不可分的问题通过RBF核函数映射到高维空间就能解这是SVM被脑电研究广泛使用的原因。具体到这个工程里特征文件的行数几十、列数二十左右用BP网络很容易出现训练集表现好、测试集崩掉的情况。SVM配合交叉验证和网格搜索在小样本上能给出相对稳定的结果。而且libsvm的matlab接口成熟训练一次只需要零点几秒方便反复调参数。3.2 scaleForSVM.m的归一化处理归一化这一步卡住了不少人。SVM的目标函数里有距离计算如果某个特征的数值范围是0到10000另一个是0到1数值范围大的特征会主导目标函数模型学习不到真正有区分度的特征。scaleForSVM.m做的就是这件事把特征缩放到统一区间。这部分我讲下libsvm配套归一化的常见写法% 最小最大值归一化把特征缩放到[tol, 1]区间 function [scale_data, ps] scaleForSVM(data, ymin, ymax) % data: 原始特征矩阵每一行是一个样本每一列是一个特征 % ymin和ymax是缩放目标的下界和上界通常设0和1 % ps是缩放参数测试数据要用同一组参数变换不能重新计算 [n, m] size(data); scale_data zeros(n, m); ps struct(xmin, zeros(1, m), xmax, zeros(1, m)); for j 1:m xmin min(data(:, j)); xmax max(data(:, j)); ps.xmin(j) xmin; ps.xmax(j) xmax; if xmax xmin % 特征取值恒定时直接置为0避免除零 scale_data(:, j) 0; else scale_data(:, j) (data(:, j) - xmin) / (xmax - xmin) ... * (ymax - ymin) ymin; end end end这里最关键的是ps这个结构体。训练时用患者和正常组的全部特征计算缩放参数测试新样本时必须用同一组xmin和xmax做变换不能拿新样本重新求最小值和最大值。举例来说训练时某个特征的范围是2到10新样本这个特征的值是12如果用新样本重新归一化那12会被缩放到1而训练集里的10对应的是1特征空间完全对不上。这个问题几乎每个做SVM的人都会踩一次后面避坑章节还会再提。3.3 特征矩阵构建和标签对齐脑电特征提取这一步工程里把原始脑电变成了特征矩阵。常用的特征包括功率谱密度、近似熵、样本熵、小波包能量等。帕金森脑电研究里节律功率变化和熵特征是比较常见的分类依据患者组在特定频段上的功率分布和正常人不一样熵值上也有差异。假设a2.m生成的wenti_tezheng是一个N×M矩阵每一行是一条脑电记录每一列是一个特征。正常组也是同样的维度。拼接时要注意把标签和数据的顺序对齐% 假设已经从特征文件读入了wenti_tezheng和zhengchang_tezheng % 患者组行数 n1 size(wenti_tezheng, 1); % 正常组行数 n2 size(zhengchang_tezheng, 1); % 构造标签患者1正常0 labels [ones(n1, 1); zeros(n2, 1)]; features [wenti_tezheng; zhengchang_tezheng]; % 打乱样本顺序防止训练时同类样本集中在一起 rng(42); % 固定随机种子保证可复现 idx randperm(n1 n2); features features(idx, :); labels labels(idx, :);很多人会漏掉打乱这一步。如果不打乱SVM训练时前一半全是患者、后一半全是正常交叉验证划分数据时可能出现某一折全是同一类样本导致验证准确率忽高忽低。使用randperm生成随机索引来洗牌同时记录下索引预测结束后可以回溯每一条样本的原始类别。3.4 交叉验证与c、g参数网格搜索SVM里真正影响结果的是RBF核的两个超参数c惩罚系数和ggammaRBF核宽度相关。c控制对误分类的容忍度c太大容易过拟合c太小容易欠拟合g控制单个样本的影响范围g太大时RBF径向基作用范围窄决策边界曲折g太小则决策边界过于平滑。脑电特征这种噪声较多的数据建议用网格搜索加交叉验证来确定这两个参数。% 用libsvm的网格搜索方式选择c和g % 先准备c和g的搜索范围 c_range 2.^(-5:1:5); % 从2^-5到2^5步长翻倍 g_range 2.^(-7:1:5); % 从2^-7到2^5 best_acc 0; best_c 0; best_g 0; for c c_range for g g_range % 5折交叉验证准确率 acc svmtrain(labels, features, ... sprintf(-t 2 -c %f -g %f -v 5, c, g)); if acc best_acc best_acc acc; best_c c; best_g g; end end end % 用最优参数重新训练最终模型 model svmtrain(labels, features, ... sprintf(-t 2 -c %f -g %f -q, best_c, best_g));网格搜索的两个嵌套循环里-t 2指定RBF核-v 5表示5折交叉验证。acc是交叉验证平均准确率不用它选参数的话很容易选出一个在训练集上接近满分、在测试集上翻车的模型。这份工程里c和g的范围不一定需要改成别的但如果你的数据换了建议把取值范围扩大到2还-10到2的10次方之间结合准确率变化趋势判断边界。提示libsvm参数里的c和g一个是cost一个是gamma网上很多中文资料把g写成“核函数宽度”愿意理解为RBF核的分布宽度参数就好。4. EEGLAB预处理让原始脑电数据满足SVM输入要求的完整做法4.1 MATLAB 2016下的EEGLAB配置EEGLAB是开源的脑电处理工具箱在这个工程里承担的是数据导入和预处理角色。MATLAB 2016版本对应的EEGLAB版本建议选14到17之间的release太新的版本有些函数在2016里会因缺少依赖报错。配置方法很简单把eeglab文件夹放到某一路径下在MATLAB里把路径添加进去。% 在MATLAB命令行里添加EEGLAB路径并启动 addpath(D:\eeglab); % 改成你实际的eeglab文件夹路径 eeglab nogui; % nogui模式不弹图形界面适合批处理addpath添加的是eeglab根目录EEGLAB启动时内部会自己把子目录加进去。如果出现找不到pop_loadset这类函数的错误多半是路径没加对。这里有件事容易被忽视EEGLAB启动时会创建ALLERP和ALLEEG等全局变量如果在启动之前工作区里已经有同名变量可能起冲突所以建议每次启动前先clear all。4.2 滤波、坏导剔除和重参考脑电数据分析的第一步通常是滤波。帕金森研究里常用的带宽是1到50Hz或1到60Hz目的是去掉低频漂移和高频噪声。滤波直接改变后续特征值所以不能随意设置。EEGLAB里用pop_eegfilt即可% 对单个数据集做带通滤波范围1-50Hz EEG pop_eegfilt(EEG, 1, 50, [], 0, 0, 1); % 参数说明1和50是截止频率第5个参数是滤波器阶数设为0表示自动第6个参数是边带 % 第7个参数1表示绘图显示滤波前后对比0则不绘图滤波之后要处理的是坏导联。看各导联的方差和功率谱方差特别大或者功率谱明显异常的通道标记为坏导用周围导联插值替换。EEGLAB里没有一键自动插值函数常见做法是手动识别后调用pop_interp实现球面插值。这一步在帕金森脑电里很重要头皮电极容易受肌肉运动干扰眨眼和咀嚼产生的伪迹会直接影响后续特征值的数值稳定性。重参考一般选平均参考但如果你用的数据已经做了参考处理跳过这一步就行。EEGLAB里重参考的命令是pop_reref(EEG, [])空数组表示平均参考。4.3 分段、伪迹剔除与特征文件对接特征提取前要把连续脑电切成分段。帕金森脑电没有明显的单个事件通常按固定长度切段比如每段2秒或4秒。切段的目的是增加样本量一段30秒的记录切成15个2秒段30次记录就有450个样本。EEGLAB里的操作是% 按固定长度分段每段2秒假设采样率是250Hz EEG pop_epoch(EEG, {}, [-1 1], epochlimits, [-1 1]); % 第一个参数是数据集第二个{}表示不按事件切分 % 第三个[-1 1]是切段区间单位是秒 % 如果数据没有event标记建议直接用reshape或者pop_select更通用的做法是用reshape把连续数据直接切段。采样率250Hz、每段2秒就是每段500个采样点% 假设SPM格式通道数x采样点数 % 输入data是 channels x time 的连续脑电矩阵 data EEG.data; % EEG.data是 [通道数 x 采样点数 x 分段数] 三维数组 [nch, nsamples, nseg] size(data); % 如果已经是epoched数据可以直接计算特征 % 如果是连续数据先reshape nsamples_seg 250 * 2; % 2秒采样率250Hz nseg floor(size(data, 2) / nsamples_seg); data_epoched reshape(data(:, 1:nseg*nsamples_seg), nch, nsamples_seg, nseg);伪迹剔除这一步最简单的做法是基于幅度阈值超过±100微伏的分段删掉更精细的做法是用ICA去掉眼电成分。ICA运行慢且在样本量小的时候不稳定我的建议是先用幅度阈值和标准差阈值粗筛保留足够样本再说。剔除完成后把每个分段的特征计算出来写入wenti_tezheng或zhengchang_tezheng对应的位置就能和SVM流程对接。5. 避坑记录这套工程里我踩过的五个坑5.1 中文路径导致fig文件和数据文件加载失败现象在MATLAB里双击unsstitled.fig界面能开但点击按钮运行时直接报错Unable to read file或者load(wenti.mat)提示文件不存在。原因MATLAB 2016的中文编码处理能力有限路径里含中文或空格时文件读取和函数调用会解析失败。压缩包如果解压到“C:\Users\我\桌面\项目”这种情况几乎必现。解决把整个工程文件夹放到纯英文路径下比如D:\PD_EEG\文件夹名也用英文。所有数据文件、fig文件路径里不能出现中文。改完路径后先clear all再重新运行程序。5.2 SVM训练集准确率100%测试集却只有70%现象用全部特征训练SVM回代训练样本准确率接近100%但换一批数据测试掉到70%上下有时甚至不如随机猜测。原因这是典型过拟合。特征维度可能偏高、样本量偏小或者参数c选得太大模型把训练集的噪声当成规律记下来了。解决先用5折交叉验证评估交叉验证准确率才是可信指标。然后用网格搜索重新选c和g把c的搜索范围往小调比如改成2的-3次方到2的3次方。多观察不同c值下交叉验证准确率的变化曲线选一个训练准确率不是最高、交叉验证却最稳的参数组合。5.3 特征矩阵维度对不上现象load两个特征文件后拼矩阵执行features [wenti_tezheng; zhengchang_tezheng]报错Dimensions of matrices being concatenated are not consistent。原因两个特征文件的行数可以不同但列数必须相同。通常是一边在特征提取时多算或少算了一个特征或者某一边预处理时通道数不一致。解决先whos查看两个文件的尺寸确认列数。多出来的列直接裁掉wenti_tezheng(:, 21) []。如果两边列数都不一样说明特征提取脚本参数有改动需要回到提取特征那一步检查。以后每次生成特征文件后第一时间加一行断言assert(size(wenti_tezheng, 2) size(zhengchang_tezheng, 2), ... 特征维度不一致请检查特征提取脚本);5.4 EEGLAB版本和MATLAB 2016的兼容问题现象eeglab启动时报错提到Undefined function biosig或找不到某个io函数或者pop_eegfilt运行到一半提示变量不存在。原因EEGLAB版本太新内部依赖的第三方插件如biosig、firfilt在MATLAB 2016里语法不兼容。新版EEGLAB有些函数改用arguments块定义2016不支持这种语法。解决换用EEGLAB_14或15版本这两个版本在MATLAB 2016里稳定。如果只是用滤波和重参考功能直接自己用MATLAB的designfilt实现带通滤波不启EEGLAB主界面代码可以安心跑% 不依赖EEGLAB的简单带通滤波 fs 250; % 采样率 [b, a] butter(4, [1 50] / (fs/2), bandpass); filtered_data filtfilt(b, a, raw_data); % 零相位滤波5.5 按钮思想绑定错误改了文件名导致回调失效现象把unsstitled.m重命名成main.m以后GUI按钮点击没有任何反应命令行不报错。原因fig文件内部存的是原文件名对应的回调函数名GUIDE导出时回调名和文件名绑定。文件名一改按钮找不到原来的回调函数入口。解决不要改fig和m文件的对应关系。如果一定要重命名在GUIDE里打开fig逐个按钮的Callback属性重新选择新的m文件关联再用guidata(hObject, handles)保存更新。过程中注意fig文件里可能残留原函数的调用需要全部替换一致。6. 验证与替换把新数据接进这套系统的关键操作工程里的这套链路跑通以后真正要用起来还得做两件事一是验证模型稳定性二是把自己的数据替换进去。稳定性验证我习惯用分层交叉验证保证每一折里患者和正常的比例和整体一致% 分层5折交叉验证 cv cvpartition(labels, KFold, 5); accuracies zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets train_idx cv.training(i); test_idx cv.test(i); % 训练时要对训练数据做归一化再对测试数据用同一组参数 [train_scaled, ps] scaleForSVM(features(train_idx, :), 0, 1); test_scaled (features(test_idx, :) - repmat(ps.xmin, sum(test_idx), 1)) ... ./ repmat(ps.xmax - ps.xmin, sum(test_idx), 1); model_fold svmtrain(labels(train_idx), train_scaled, ... sprintf(-t 2 -c %f -g %f -q, best_c, best_g)); [~, acc, ~] svmpredict(labels(test_idx), test_scaled, model_fold); accuracies(i) acc(1); end % 输出每折准确率和平均值 fprintf(每折准确率%.2f%% %.2f%% %.2f%% %.2f%% %.2f%%\n, accuracies); fprintf(平均准确率%.2f%%\n, mean(accuracies));交叉验证里最容易被忽略的是测试数据的归一化。训练集归一化后得到ps.xmin和ps.xmax测试集必须套同一个ps不能用测试集自己的最小值最大值再算一遍。我刚才避坑章节里说过这一步错了准确率再高都不可信。把新数据换进来时只需要改三处一是load的数据文件名换成你导入预处理好的数据二是特征提取脚本里的特征类型和参数要和你训练时的定义一致三是labels的构造方式患者组1、正常组0的顺序不能乱。新样本单独预测的代码更简单% 新样本预测假设new_data是1xM的特征向量 new_scaled (new_data - ps.xmin) ./ (ps.xmax - ps.xmin); [pred_label, prob_est] svmpredict(1, new_scaled, model); % pred_label为1表示患者0表示正常 % prob_est第一列是类别概率估计这份工程给了我一个很重要的习惯每次换数据源我都会先跑一遍完整交叉验证流程把每折准确率和平均准确率记录下来再开始调参。不再直接用一百多的训练集准确率跟人汇报。从那以后再做脑电数据分类我每次都会强制走一遍归一化参数保存、交叉验证记录、新样本预测验证这三步。希望这套拆解能帮你在自己的实验里少走几步弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑