简介这份资源面向生物医学工程、信号处理方向的学生与研究者提供一套基于Matlab 2016的帕金森脑电识别系统实现方案用于解决脑电信号采集、特征提取与疾病辅助诊断的完整流程问题。压缩包共18个文件约21.95MB包含6个mat数据文件、6个m脚本、5个fig界面文件及1个doc说明文档分别对应脑电数据存储、算法逻辑、GUI界面布局与使用说明配合eeglab工具包可完成信号预处理与特征值计算。资源以Matlab GUI形式组织将数据读取、特征提取与SVM分类诊断串联为可视化操作流程便于读者理解从原始脑电到诊断结论的工程实现路径。目前已有157人学习下载适合具备一定Matlab基础、希望快速搭建脑电识别实验框架的读者参考也可作为相关课程设计或课题研究的起步模板。1. 帕金森脑电识别系统从“手抖才就医”到提前数年捕捉信号帕金森病最麻烦的地方在于它早期几乎不“吭声”。临床上常用的统一评分量表往往要等到患者出现明显静止性震颤、运动迟缓、肌强直才给出一个相对确定的判断——而这时候黑质多巴胺能神经元已经损失了一大半。换句话说我们一直在用“症状”当探针可症状本身就是滞后的。帕金森脑电识别系统要解决的正是这个滞后问题。它不靠问诊、不靠量表而是直接采集头皮上的脑电信号EEG用信号处理和机器学习的方法从毫秒级的振荡节律里找出帕金森特有的异常模式。适合谁做做生物医学信号处理的工程师、想入门神经疾病辅助诊断的算法同学、以及手里有脑电数据但不知道怎么建模的科研人员。它不需要你懂神经内科但需要你愿意把“数据—特征—模型—验证”这条链路老老实实走一遍。2. 脑电信号里到底藏着什么帕金森识别的生理依据与数据形态2.1 为什么偏偏是脑电而不是影像或步态先说选型。帕金森的诊断辅助手段不少结构核磁看萎缩、DAT-SPECT看多巴胺转运体、步态分析看运动模式。但它们各有硬伤——核磁对早期帕金森敏感度有限DAT显像成本高且有放射性步态分析又依赖患者能走。脑电的优势在于三点无创、时间分辨率高毫秒级、成本相对可控。更关键的是帕金森不只是运动障碍它本质上是一种网络同步异常。基底节—丘脑—皮层环路里的β频段13–30 Hz过度同步是帕金森非常核心的电生理特征。这种异常在运动症状出现前就可能存在而且会随着病情和用药状态波动。所以脑电不是“顺便能用”而是恰好卡在了帕金森病理机制的时间尺度上。常见做法是采集静息态脑电睁眼和闭眼各若干分钟。闭眼状态下枕区α节律8–13 Hz更稳定适合做基线睁眼状态则能观察α阻断是否正常。对帕金森来说额中央区的β功率、β/α比值、以及跨通道相干性往往比单纯看某个频段功率更有区分度。2.2 一条脑电样本从采集到入模的完整形态在动手写代码前得先搞清楚数据长什么样。假设你拿到的是某实验室共享的静息态脑电数据集常见格式是EDF或SET采样率500 Hz或1000 Hz通道数从16到64不等。原始数据不能直接喂给模型中间要经过一条固定流水线。import mne import numpy as np # 读取一份静息态脑电数据假设为EDF格式 raw mne.io.read_raw_edf(resting_eeg.edf, preloadTrue) # 1. 选通道保留头皮脑电剔除心电、眼电等辅助通道 # 常见做法是只保留标准10-20系统通道 eeg_channels [ch for ch in raw.ch_names if ch.startswith((F, C, P, O, T))] raw.pick_channels(eeg_channels) # 2. 带通滤波0.5-45 Hz去掉基线漂移和高频肌电 raw.filter(l_freq0.5, h_freq45.0, fir_designfirwin) # 3. 陷波去掉50 Hz工频干扰国内电网频率 raw.notch_filter(freqs50.0) # 4. 重参考常用平均参考或乳突参考 raw.set_eeg_reference(average, projectionFalse) # 5. 降采样从1000 Hz降到250 Hz减少计算量且保留主要节律 raw.resample(250) # 6. 分段切成2秒一段重叠50% events mne.make_fixed_length_events(raw, duration2.0, overlap1.0) epochs mne.Epochs(raw, events, tmin0, tmax2.0, baselineNone, preloadTrue) # 7. 伪迹剔除自动拒绝振幅超过150微伏的段 epochs.drop_bad(rejectdict(eeg150e-6)) print(f剩余可用片段数: {len(epochs)}) print(f数据维度: {epochs.get_data().shape}) # (段数, 通道数, 时间点)这段代码的逻辑是先做通道筛选把非脑电信号排除再滤波去噪保留0.5–45 Hz这个脑电主要能量区间陷波处理工频重参考统一基准降采样控制数据量最后切段并剔除明显伪迹。参数上滤波下限0.5 Hz是为了保留慢波但去掉直流漂移上限45 Hz是因为再高基本是肌电和噪声。分段长度2秒是一个折中——太短则频率分辨率不够太长则平稳性假设不成立。重叠50%是为了增加样本量但要注意后续划分训练测试集时不能把重叠段分到不同集合否则会数据泄漏。注意伪迹剔除阈值150微伏不是绝对的。如果被试者紧张、出汗多可以放宽到200微伏但要在论文或报告中说明。宁可少几个样本也不要让眼动和肌电污染特征。3. 特征工程把脑电波形变成模型能吃的数字3.1 频带功率与比值特征最稳的起步方案原始脑电是时序波形直接送进分类器效果通常不好因为维度太高、噪声太大。常见做法是先提取频域特征。帕金森最相关的几个频带是δ1–4 Hz、θ4–8 Hz、α8–13 Hz、β13–30 Hz、γ30–45 Hz。其中β频段是重点观察对象。from scipy.signal import welch def extract_bandpower(epochs, sfreq250): 提取每个通道各频带的绝对功率和相对功率 data epochs.get_data() # (n_epochs, n_channels, n_times) bands { delta: (1, 4), theta: (4, 8), alpha: (8, 13), beta: (13, 30), gamma: (30, 45), } features [] for epoch in data: epoch_feat [] for ch_data in epoch: freqs, psd welch(ch_data, fssfreq, npersegsfreq*2) total_power np.trapz(psd, freqs) band_powers {} for name, (low, high) in bands.items(): idx np.logical_and(freqs low, freqs high) bp np.trapz(psd[idx], freqs[idx]) band_powers[name] bp epoch_feat.append(bp) # 相对功率每个频带除以总功率 for name in bands: epoch_feat.append(band_powers[name] / (total_power 1e-12)) # 关键比值beta/alpha帕金森常升高 epoch_feat.append(band_powers[beta] / (band_powers[alpha] 1e-12)) features.append(epoch_feat) return np.array(features)这里用Welch法估计功率谱密度比直接FFT更平滑。每个通道提取5个绝对功率、5个相对功率、1个β/α比值共11维。假设有16个通道那每个样本就是176维。参数上npersegsfreq*2表示用2秒窗做谱估计频率分辨率0.5 Hz对区分相邻频带够用。np.trapz做梯形积分得到频带能量。为什么相对功率和比值重要因为绝对功率受电极阻抗、个体差异影响大而相对功率和比值能部分归一化这些因素。β/α比值升高是帕金森比较一致的发现可以作为核心特征之一。3.2 相干性与网络特征捕捉跨通道同步异常帕金森是网络病单通道特征不够。跨通道相干性coherence能衡量两个通道在某个频段上的同步程度。β频段相干性升高是帕金森电生理的一个标志。from scipy.signal import coherence def extract_coherence_features(epochs, sfreq250, band(13, 30)): 计算所有通道对的beta频段相干性均值 data epochs.get_data() n_epochs, n_channels, n_times data.shape features [] for epoch in data: pair_feats [] for i in range(n_channels): for j in range(i1, n_channels): f, cxy coherence(epoch[i], epoch[j], fssfreq, npersegsfreq*2) idx np.logical_and(f band[0], f band[1]) pair_feats.append(np.mean(cxy[idx])) features.append(pair_feats) return np.array(features)通道对数量是C(n,2)16通道就是120对。如果通道更多这个维度会爆炸常见做法是先做通道聚类或只选感兴趣区域比如额中央区的通道对。参数上nperseg同样用2秒窗保证频率分辨率。相干性值在0到1之间越接近1表示同步越强。把频带功率特征和相干性特征拼接起来就得到一个高维特征向量。接下来可以送进SVM、随机森林或简单的全连接网络。如果样本量不大几十到几百人SVM配RBF核往往是稳的如果样本上千可以上深度学习。注意相干性计算对伪迹非常敏感。如果某段数据有肌电高频相干性会虚高。所以前面伪迹剔除那一步不能省而且最好在计算相干性前再做一次独立成分分析ICA去掉眼动成分。4. 模型训练与验证别让“高准确率”骗了你4.1 划分数据集时最容易犯的错脑电数据有个特点同一个被试者会贡献很多段样本。如果你随机把所有段打乱再划分训练测试集那同一个人的段可能同时出现在两边模型会“记住”这个人的特征测试准确率虚高。这是血泪经验里最常见的一个坑。正确做法是按被试者划分训练集用一部分人的数据测试集用另一部分人的数据。这叫被试者独立验证subject-independent更接近真实筛查场景。如果样本量实在不够至少要做被试者内交叉验证并且明确说明结论不能直接推广到新人。from sklearn.model_selection import GroupKFold from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline from sklearn.metrics import accuracy_score, roc_auc_score # 假设 X 是特征矩阵 (n_samples, n_features) # y 是标签 (0健康对照, 1帕金森) # groups 是每个样本对应的被试者ID groups np.array([...]) # 被试者编号 cv GroupKFold(n_splits5) aucs [] for train_idx, test_idx in cv.split(X, y, groups): X_train, X_test X[train_idx], X[test_idx] y_train, y_test y[train_idx], y[test_idx] clf make_pipeline( StandardScaler(), SVC(kernelrbf, C1.0, gammascale, probabilityTrue) ) clf.fit(X_train, y_train) y_prob clf.predict_proba(X_test)[:, 1] aucs.append(roc_auc_score(y_test, y_prob)) print(f被试者独立AUC: {np.mean(aucs):.3f} ± {np.std(aucs):.3f})这里用GroupKFold保证同一被试者的样本不会跨折。StandardScaler做标准化因为SVM对特征尺度敏感。SVM的C控制正则强度gamma控制核宽度先用默认的scale再在验证集上微调。评价指标用AUC而不是准确率因为如果健康对照和帕金森样本比例不均衡准确率会误导。4.2 参数怎么调几个真正影响结果的旋钮特征侧滤波上限从45 Hz降到40 Hz有时能去掉残余肌电AUC可能提升一两个点。分段长度从2秒改成4秒频率分辨率提高但样本数减半要权衡。β频段范围从13–30 Hz缩到15–25 Hz有些研究报告区分度更好因为帕金森β异常主要集中在低频β。模型侧SVM的C在0.1到10之间搜gamma在0.001到1之间搜。随机森林的树数量500到1000最大深度限制在5到10防止过拟合。如果上深度学习输入可以是原始段或时频图但样本量小于500时通常打不过SVM。验证侧除了AUC还要看敏感度和特异度。筛查场景下敏感度更重要——宁可误报不可漏报。可以调整分类阈值来平衡。注意如果AUC在0.9以上先别高兴。检查一下有没有数据泄漏特征提取时有没有用到测试集信息标准化是不是在划分之前做的被试者ID有没有对齐这些地方翻车的人不在少数。5. 避坑与排查帕金森脑电识别里最容易翻车的五件事现象一模型在训练集上准确率99%测试集只有60%。原因过拟合且大概率是数据泄漏。同一个人的段被分到了训练和测试两边模型记住了个人特征而非疾病特征。 解决用GroupKFold按被试者划分标准化只在训练集上拟合再应用到测试集。特征维度太高时先做特征选择或降维。现象二β频段特征和文献方向相反别人报告升高你这里降低。原因参考电极选择不同。平均参考和乳突参考下β功率的绝对值甚至相对值都可能反向。另外用药状态没控制——帕金森患者服药后β功率会下降。 解决固定参考方案记录被试者用药状态开期/关期最好在同一状态下比较。报告时写清楚参考方式和用药信息。现象三相干性特征全部接近1没有区分度。原因没做伪迹剔除肌电或眼动导致高频同步虚高。或者分段太长非平稳信号导致相干性估计失真。 解决先做ICA去眼动再严格剔除高振幅段。相干性计算用2秒窗不要用整段数据。现象四换一个数据集AUC掉到0.5完全失效。原因通道蒙太奇不同、采样率不同、预处理流程不同。在一个数据集上调好的参数换数据集就不适用。 解决统一预处理流程把数据重参考到共同蒙太奇重采样到相同频率。如果通道数不同只取共同通道或做通道插值。跨数据集验证是检验泛化性的硬标准。现象五训练时损失正常下降但验证集AUC波动巨大。原因样本量太小且类别不均衡。少数类样本在不同折里分布不均。 解决用分层分组交叉验证确保每折里两类比例接近。或者用SMOTE做少数类过采样但要在训练折内做不能提前做。6. 从能跑到好用一个提升泛化性的具体技巧前面五章走完你大概能得到一个在自己数据集上AUC 0.8–0.9的模型。但真正决定这个系统能不能落地的是它换一批人、换一台设备还灵不灵。这里分享一个我常用的技巧在特征层面做被试者内标准化而不是全局标准化。具体做法是对每个被试者先计算他所有段的特征均值然后用每个段减去这个均值。这样做的效果是去掉个体间的基线差异保留段与段之间的相对变化。帕金森的诊断信息往往不在“你的β功率比别人高多少”而在“你的β功率在静息态下是否异常稳定或异常波动”。被试者内标准化能把个体差异这个最大的干扰源压下去。def subject_wise_normalize(X, groups): 按被试者做特征标准化每个样本减去该被试者均值 X_norm np.zeros_like(X) for g in np.unique(groups): mask groups g X_norm[mask] X[mask] - X[mask].mean(axis0, keepdimsTrue) return X_norm # 在划分训练测试集之前先对被试者内标准化 X_norm subject_wise_normalize(X, groups)注意这一步要在划分之前做因为它只用到了每个被试者自己的信息不涉及跨被试者泄漏。做完之后再按被试者划分送进模型。在我的经验里这个操作通常能把跨被试者AUC提升3到8个点尤其是当被试者年龄、性别、颅骨厚度差异大的时候。另一个配套技巧是不要只报告一个AUC报告AUC的分布。用自助法bootstrap重采样被试者算1000次AUC看95%置信区间。如果区间下限低于0.7说明模型还不够稳别急着写论文说“效果显著”。最后说一个我自己的教训早期做这个方向时我花了很多时间调模型结构从SVM换到深度学习AUC涨了两个点就沾沾自喜。后来才发现把预处理里的滤波参数改一下效果比换模型大得多。脑电这行数据质量决定上限模型只是逼近上限。所以如果你刚开始做先把采集和预处理做扎实再谈模型。希望帮到你。本文还有配套的精品资源点击获取