简介围绕脑电信号的情绪识别研究这份 PDF 论文以 DEAP 与 SEED-IV 两个公开数据库为实验对象面向情感计算、脑机接口方向的学生与研究者尤其适合作为支持向量机分类、课程设计及毕业设计的参考文献。文中先梳理基于公开数据的 EEG 情绪检测相关工作再给出二维效价—唤醒模型下的监督学习流程采用离散小波变换对预处理信号做五频段分解提取功率、能量、微分熵及时域特征并构建通道级 SVM 分类器与通道组合器完成情绪状态判别DEAP 四类分类率为 74%、86%、72%、84%SEED-IV 为 79%、76%、77%、74%可作为方法复现、特征工程对比与结果核对的参考。资源包仅含 1 个 PDF 文件大小约 701KB轻量便于检索、打印与归档正文覆盖相关工作总结、方法流程与实验指标适合快速把握 EEG 情绪检测的整体研究框架。目前已有 347 人学习可作为入门综述与实验设计参考。1. 32 个单通道分类器拼起来为什么“分开处理”反而优于多通道大特征EEG 情绪检测这个题目每年都会出现在毕业设计的选题清单里DEAP 和 SEED-IV 两个公开数据库几乎是默认选项SVM 则是多数人第一个能跑通的分类器。多数人的第一反应是把 32 个通道的 34 维特征拼成一条 1088 维的长向量一次性丢进模型。2019 年那篇在 DEAP 和 SEED-IV 上做四类情绪判别的论文走的却是反方向每个通道单独训一个 SVM一共 32 个再用一个通道融合器把 32 个判决合成最终标签DEAP 上拿到 74%~86%SEED-IV 上 74%~79%。通道越多、特征越全模型反而越该拆开用——这个结论值得细看因为它牵扯到脑电信号最本质的两个属性空间上的强相关和时间上的非平稳。把这两点想清楚你才知道后面每一级滤波、每一次小波分解、每一个通道级分类器究竟在解决什么问题。2. 数据侧的隐形工程量采样率、事件切分与三级滤波2.1 两个公开库的结构差异决定了后面每一步的形状一份情绪脑电流水线的上限在数据加载完成的那一刻基本就定了。DEAP 和 SEED-IV 这两个库的采集协议完全不同凡是后面需要“对齐”的地方根源都在这里。维度DEAPSEED-IV被试规模32 人15 人 × 3 次会话 45 组通道数32 导64 导采样率500 Hz1000 Hz刺激单元40 段 1 分钟视频每会话 24 段 2 分钟视频标签来源被试自评效价、唤醒、喜好视频诱发的既定情绪类别论文中的训练/测试划分22 人训练 / 10 人测试32 组训练 / 13 组测试通道数不一致取交集就是 32 导采样率不一致SEED-IV 必须先降采样到 500 Hz 才能和 DEAP 走同一条特征管线。这两个动作看起来只是格式转换实际上第二个直接决定了小波分解的层数够不够用第 3 章会算这笔账。更关键的是划分方式。论文按“人”划分而不是按“试次”随机打乱。DEAP 的 40 段视频对同一个被试是连续采集的相邻两段的脑电状态高度自相关如果随机按试次切 70/30测试集里几乎每一段都能在训练集里找到“几分钟前的自己”准确率会虚高十几个点。按被试划分意味着测试集里的人模型从没见过这才是跨被试泛化的真实难度也是 74%~86% 这个数字看起来“不够惊艳”的原因——它是在更严格的条件下取得的。样本量的账也要算清。DEAP 训练集 22 人 × 40 段 880 条每条 34 维SEED-IV 论文里写的是 742 条而 32 组 × 24 段按名义应该是 768 条差的这 26 条通常是某些会话里被判定为无效的片段被剔除了。这类“名义值对不上实际值”的情况在公开库里很常见复现时不要拿名义值去硬套。提示事件切分环节DEAP 给的是连续记录需要按采集协议自己按视频起止点切开并转成 matSEED-IV 则是已经切好的 mat。前者容易把刺激前基线错位几秒后者省事但要注意每段内部还带着视频开始阶段的诱发延迟。2.2 从 50Hz 陷波到零相位带通每级滤波器究竟在去除什么预处理链是三级顺序不能乱。这套顺序不是随手排的每一级都在为下一级清障。第一级是 50/60 Hz 陷波。工频干扰来自供电环境50 Hz 恰好落在 gamma 频带31~60 Hz的正中间。gamma 在情绪研究里被认为和高级认知加工相关如果陷波没做或者做在后面gamma 频带的能量会被工频彻底污染分类器最后学到的其实是“这台设备插在什么电网上”。论文按 50 Hz、230 V 的供电环境选了 50 Hz 陷波这个参数必须跟着采集环境走搬到 60 Hz 电网的数据上直接失效。第二级是重参考。原始数据用的是固定参考或共同参考电极散布在整个头皮上共同参考会把这一个参考点的噪声注入所有通道。改成平均参考后各通道减掉全脑均值共模成分被压掉。论文用的是滑动平均重参考好处是不依赖某一个物理电极的状态——一旦那个参考电极接触不良固定参考方案会全线报废。第三级是 1 维 10 阶中值滤波加 20 阶带通。中值滤波针对的是脉冲噪声电极瞬间跳变、眨眼造成的尖峰、运动伪迹。这类噪声的幅度远大于脑电本身但持续时间只有几个采样点用均值类滤波会把尖峰“抹开”成一片污染中值滤波则是直接把它替换成邻域中位数边缘保留得好。带通用的是 0.1~60 Hz、20 阶下限定在 0.1 Hz 而不是 0是为了滤掉直流漂移和缓慢的汗电位——这类慢漂移会让整段信号的均值偏移直接毁掉时域均值类特征。带通那一步用的是零相位实现MATLAB 的filtfiltPython 里对应scipy.signal.filtfilt。这一点容易被忽略普通 IIR 滤波会引入相移不同频率成分的延迟不一样波形在时间轴上被“拉歪”。如果后面只提取频带能量相移影响不大但论文的 34 维特征里有大量时域差分特征一阶差分、二阶差分、Hjorth 参数这些特征对波形的时间结构很敏感相位一歪差分值就全变了。filtfilt的做法是正向滤一遍、反向再滤一遍两次相移相互抵消幅频响应变成平方代价是边缘需要做延拓处理太短的片段会在两端出现畸变。import numpy as np from scipy.signal import filtfilt, iirnotch, butter, medfilt FS 500.0 def preprocess(x, fsFS, line50.0): # 1) 工频陷波Q 值决定陷波宽度Q 越大 notch 越窄 b, a iirnotch(line, Q30.0, fsfs) x filtfilt(b, a, x) # 2) 平均重参考多通道时对通道维求均值再减掉 # x.shape (n_channels, n_samples) x x - x.mean(axis0, keepdimsTrue) # 3) 中值滤波去脉冲窗口取奇数点500Hz 下 5 点约 10ms x np.apply_along_axis(lambda s: medfilt(s, kernel_size5), -1, x) # 4) 零相位带通 0.1-60Hz4 阶 butter 前后各滤一次等效 8 阶 b, a butter(4, [0.1, 60.0], btypebandpass, fsfs) x filtfilt(b, a, x, axis-1) return x代码里iirnotch的 Q 值值得单独说Q 太小陷波带太宽会把 50 Hz 附近的真实 gamma 一起削掉Q 太大又压不住工频漂移。30 左右是常见起点实际要看 PSD 图上 50 Hz 的峰压到什么程度。butter用的是 4 阶而不是论文里的 20 阶因为filtfilt会把阶数效果平方4 阶零相位等效的滚降已经够用直接上 20 阶反而容易在数值上不稳。中值窗口也一样500 Hz 下 5 点约 10 ms能盖住眨眼尖峰又不至于把 alpha 波形削平。3. 8 层 db8 小波分解5 个情绪频带的边界是怎么算出来的3.1 二进划分与采样率的绑定关系脑电是非平稳信号一段 1 分钟的数据里alpha 的强弱随时在变直接做 FFT 得到的是整段平均时间信息全丢了。小波分解的好处是同时在时间和频率上做局部化而且二进划分的频带边界和脑电的经典频段天然对得上。关键在于层数和采样率是绑死的。第 j 层细节系数覆盖的频率范围是fs/2^(j1)到fs/2^j。DEAP 采样率 500 Hz逐层算下来细节层频率范围fs 500 Hz对应频带cD1125 ~ 250 Hz与情绪无关的高频噪声cD262.5 ~ 125 Hz肌电主要分布区cD331.25 ~ 62.5 Hzgamma31 ~ 60cD415.6 ~ 31.25 Hzbeta14 ~ 30cD57.8 ~ 15.6 Hzalpha8 ~ 13cD63.9 ~ 7.8 Hztheta4 ~ 7cD71.95 ~ 3.9 Hzdelta1 ~ 3cD80.98 ~ 1.95 Hzdelta 下沿通常丢弃8 层刚好把 500 Hz 拆到 1 Hz 附近最低那层和 delta 的下界重合。这就是为什么论文选 8 层、选 db8也解释了为什么 SEED-IV 必须先降采样到 500 Hz如果直接拿 1000 Hz 做 8 层cD8 落在 1.95~3.9 Hzdelta 频段根本覆盖不到cD7 是 3.9~7.8 也只压到 theta 的上半截整个低频段是错位的。降采样这个动作本质上是为了让二进划分的格点重新对齐到脑电频段上。要说明的是db8 不是理想砖墙滤波器相邻层之间有过渡带重叠能量会泄漏。所以“cD3 就是 gamma”这个说法只是近似实际计算中频带能量会有几个百分点的串扰。这也是为什么有些实现会在小波分解之后再加一层带通细化或者干脆用带通滤波器组代替小波。论文选小波看中的是它的时频局部化能力代价就是频带边界的模糊。import numpy as np import pywt # 500Hz 采样下8 层分解的细节层索引映射 # coeffs [cA8, cD8, cD7, ..., cD1]长度 9cD_j 在索引 -j LEVEL_MAP {gamma: 3, beta: 4, alpha: 5, theta: 6, delta: 7} def decompose(x, waveletdb8, level8): coeffs pywt.wavedec(x, wavelet, levellevel) return {name: coeffs[-lvl] for name, lvl in LEVEL_MAP.items()} def band_features(x): feats [] for name, c in decompose(x).items(): energy float(np.sum(c ** 2)) # 频带能量 avg_power energy / max(len(c), 1) # 平均带功率 de 0.5 * np.log(2 * np.pi * np.e * np.var(c) 1e-12) # 微分熵高斯假设 feats.extend([energy, avg_power, de]) return np.asarray(feats, dtypenp.float32)索引映射是最容易写错的地方。pywt.wavedec返回的列表是“近似系数在前、细节系数从高到低”cD1在最后一位所以cD3要取coeffs[-3]。写成coeffs[3]就变成cD6了频带整体错位两层delta 的特征会跑到 alpha 上模型照样能训出 70% 多的准确率但你已经不知道它在学什么。3.2 34 维特征向量的构成12 20 2 的账要算清论文里反复出现“34 个特征”但这个数字不是拍脑袋定的拆开来是三块。时域统计特征 12 个均值、均方根、标准差、一阶差分、归一化一阶差分、二阶差分、归一化二阶差分、峰度、偏度、方差加上 Hjorth 的 mobility 和 complexity。差分类特征衡量的是信号变化的快慢对唤醒度arousal这个维度特别敏感——唤醒度高时脑电整体频率上移、变化更剧烈一阶差分的幅度会明显变大。Hjorth 的两个参数则是用方差和导数的方差反推信号的“平均频率”和“频率分散度”mobility sqrt(var(x)/var(x))complexity mobility(x)/mobility(x)不需要做任何变换就能拿到频域信息计算量极小。频域特征 20 个5 个频带 × 4 个量频带能量、功率谱密度、微分熵、平均带功率。这里有个冗余问题值得点出来对高斯分布的信号微分熵 DE 0.5·log(2πeσ²)也就是 log 能量加一个常数和频带能量是单调对应的。换句话说能量和 DE 提供的信息高度重叠。这不算错SVM 对冗余特征有容忍度但如果你发现某个频带的权重异常大很可能不是因为它真有用而是几个相关特征在互相放大。其余 2 个Hurst 指数和排列熵。Hurst 衡量长程相关性排列熵衡量时间序列的复杂度和不规则性这两个都不依赖幅度、只看结构在区分“平静”和“紧张”这类状态时能补上能量特征看不到的信息。12 20 2 34账对上了。每一维都是有出处的这也是手工特征工程的特点可解释、可追溯、每一维都能对应到一个生理意义。代价是维度低、表达力有限且高度依赖设计者的先验。4. SVM 判决与通道融合四象限标签怎么落到一个被试身上4.1 二维模型与四个类别情绪在这里不是“开心/难过”的离散标签而是放在效价valence和唤醒arousal两个轴上。效价从负到正唤醒从低到高两轴一交叉就是四个象限高唤醒高效价HAHV、高唤醒低效价HALV、低唤醒低效价LALV、低唤醒高效价LAHV。论文的四个类别就是这四个象限。这个建模方式的好处是连续可插值坏处是两轴的标注质量不一样。DEAP 的标签来自被试看完视频后的自评打分1~9需要自己二值化阈值取 5 还是取中位数会直接改变类别分布。人在自评时普遍偏向高效价四个象限往往不均匀HAHV 和 LAHV 的样本明显多于另外两个。如果不做类别权重SVM 会倾向于把样本往多数类上推。论文对每个通道单独训练时用了正则化、gamma 和核函数这几个超参实际调的时候 RBF 核几乎是默认选择因为脑电特征和情绪状态之间的关系显然不是线性的。class_weightbalanced这个参数在复现时强烈建议加上它按类别频率反比加权能明显缓解少数类被吞掉的问题。4.2 从 32 个单通道判决到最终标签论文里的做法是“通道级分类器 通道融合”。32 个通道各自训练一个 SVM每个模型只看自己那一路信号的 34 维特征。测试时 32 个模型各给出一个判决再由融合器合成最终标签。这个结构乍看很浪费——明明可以把 32×34 拼成 1088 维一次训完。但拆开有拆开的道理。脑电通道之间的相关性极高相邻电极的间距只有几厘米测到的信号大半是共享的。1088 维里有大量共线成分SVM 的核矩阵会接近奇异超平面解不唯一模型表现对随机种子极其敏感。拆成 32 个 34 维的小问题之后每个子问题都是良态的样本量和维度比从 880:1088 变成 880:34学习难度下降一个量级。而且通道级模型天然可解释哪个通道的准确率高就说明那个脑区对当前任务有信息量枕区 alpha 和额区不对称性通常是最突出的两个。代价是丢掉了通道间的联合信息。两个通道单独看都没什么区分度但它们的差值比如左右额区的不对称性可能信息量极大这种特征在通道独立的结构里永远学不到。import numpy as np from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler def fit_channel_models(X, y, n_channels32): X: (n_samples, n_channels, n_features) y: (n_samples,) models [] for ch in range(n_channels): scaler StandardScaler().fit(X[:, ch, :]) # 每通道独立标准化 clf SVC(kernelrbf, C1.0, gammascale, probabilityTrue, # 开 Platt 缩放才能输出概率 class_weightbalanced) # 缓解四象限类别不均 clf.fit(scaler.transform(X[:, ch, :]), y) models.append((scaler, clf)) return models def fuse(models, x): x: (n_channels, n_features) - 最终类别 proba np.zeros((len(models), 4)) for ch, (scaler, clf) in enumerate(models): proba[ch] clf.predict_proba(scaler.transform(x[ch][None, :]))[0] return int(proba.mean(axis0).argmax()) # 概率平均融合probabilityTrue会触发内部的 Platt 校准用交叉验证把 SVM 的输出间隔映射成概率这会额外增加训练时间但没有它就拿不到predict_proba只能做硬投票。融合策略上概率平均比硬投票稳因为它保留了每个通道的置信度更细的做法是按通道的验证准确率给权重让靠谱的通道说话更响。论文只说了“channel fusion”没展开具体规则从结果看 DEAP 的 HALV 类到了 86%说明融合确实在起作用——单通道很难到这个水平。结果层面还有一个值得琢磨的现象DEAP 四类里最高 86%、最低 72%差距 14 个点SEED-IV 则是 79% 到 74%只差 5 个点明显更均衡。原因大概率在标签来源。DEAP 是自评被试对“高唤醒低效价”这种状态的自我认知本来就不一致标注噪声大SEED-IV 是视频诱发的既定类别标注是客观的四类分布也更均匀。跨库的时候如果忽略这个差异直接把 DEAP 上调好的超参搬到 SEED-IV效果往往不如预期。5. 复现这条路时会踩的坑五个现象、五个原因5.1 数据与预处理环节的两处翻车现象一gamma 频带的能量高得反常模型几乎只依赖 gamma 就能分类。原因通常是工频没除干净或者陷波器放在了带通之后。50 Hz 正落在 gamma 中间带通的上限是 60 Hz不会把它滤掉必须靠陷波单独处理。检查方法很直接对预处理后的信号做一次 PSD看 50 Hz 和 100 Hz 处有没有尖峰有就是没除掉。 解决办法是把陷波提到整条链路的最前面且 Q 值别设太大否则会把 50 Hz 两侧的真实 gamma 一起削掉。现象二换到 SEED-IV 后 delta 特征几乎全为零四类准确率集体掉到 60% 出头。原因是 1000 Hz 采样直接做 8 层小波分解最低层只到 1.95~3.9 Hzdelta 的 1~3 Hz 根本没被覆盖。很多人以为是特征提取代码写错了实际上是分解层数和采样率的对应关系没算。 解决办法只有两个把 SEED-IV 降采样到 500 Hz 再用 8 层或者保持 1000 Hz 但把层数加到 9 层。前者的额外好处是两个库的管线完全统一超参可以直接复用。注意降采样之前一定要先做抗混叠低通直接隔点抽取会把 250 Hz 以上的成分折叠回低频污染整个频谱而这种污染在时域波形上几乎看不出来。5.2 特征与分类环节的三处翻车现象三准确率飙到 95% 以上兴奋了两天换一批数据立刻打回原形。九成是数据泄漏。按试次随机划分 70/30而同一个被试的连续试次之间脑电状态高度相关测试集里的样本在训练集里有“近亲”。DEAP 每个被试 40 段连续采集这个问题尤其严重。 解决办法是严格按被试划分训练集里的人一个都不能出现在测试集。按这个标准重跑准确率会掉到 70%~85% 区间这才是真实水平。现象四中值滤波之后波形出现“台阶”高频细节被抹平beta 和 gamma 的特征值明显偏小。窗口开太大了。10 阶中值在 500 Hz 下意味着 20 ms 的窗口而 beta 的一个周期才 30~70 ms窗口和信号周期同量级等于把信号本身当噪声滤掉了。 解决办法是把窗口压到 3~5 点或者改用 Hampel 滤波——它基于滑动窗口的中位数和 MAD 做判别只替换超出阈值的点对正常波形零改动。现象五换个随机种子测试准确率差 5 个点汇报时不知道该写哪个数。样本量太小SEED-IV 训练集只有 742 条单次 70/30 划分的方差极大叠加 SVM 对 C 和 gamma 的敏感性结果自然飘。 解决办法是改用被试级交叉验证报告均值和标准差而不是单次结果超参用内层交叉验证选不要拿测试集调参。另外混淆矩阵比单一准确率更能说明问题——如果四类里有一类的召回率接近 0那多半是类别不平衡没处理而不是模型不行。6. 从 74% 到 86%这套管线在设计上换来了什么这套方案最值得琢磨的取舍是它放弃了通道间的联合建模换来每个子问题的良态和可解释。32 个独立的 34 维 SVM参数量加起来不小但因为彼此不共享模型无法表达“左额 alpha 减右额 alpha”这类跨通道特征。如果情绪判别的主要信息恰好藏在这种空间模式里通道独立的结构就是天花板。后来很多工作转向 CSP 类空间滤波或者直接上 CNN 吃原始多通道张量本质上都是在补这个短板。第二个取舍是手工特征对深度特征。34 维特征每一维都能对应到生理意义样本效率极高——880 条数据就能训出 80% 左右这在端到端模型上是难以想象的。代价是表达能力受限于设计者的先验且换一个数据集就要重新调特征。2019 年前后深度模型在 DEAP 上的成绩普遍在 75%~86% 区间和这套手工管线咬得很紧说明在样本量只有几百到几千这个量级上特征工程带来的收益并不比模型容量小。第三个是没解决的问题跨库迁移。DEAP 的标签是被试自评SEED-IV 是诱发类别两者的四象限定义在语义上并不等价直接把 DEAP 上训好的模型搬到 SEED-IV 上测掉点是必然的。真正想做跨库得先做标签对齐和域适应而这套管线里没有任何一步是为迁移设计的。想验证这套流程是否真的 work我的建议是做三件事一是置换检验把标签随机打乱重跑准确率应该掉到 25% 附近如果还停在 60%说明有泄漏二是画混淆矩阵看是不是某一类在撑场面三是把通道级模型的准确率排序画出来如果高准确率的通道集中在少数几个位置且符合生理预期枕区、额区那这套特征大概率是真学到了东西如果均匀分布或者集中在边缘电极那就要怀疑参考和伪迹了。我自己最早复现这类工作时图省事用了默认的试次级随机划分跑出 94% 还写进了报告后来按被试重跑掉到 76%那一版的结论全得推翻。从那以后我每次碰生理信号数据集划分脚本都强制先打印一遍“训练集被试 ID 集合”和“测试集被试 ID 集合”的交集交集非空就直接报错退出不管这个检查看起来多多余。希望这个习惯能帮你少走一段弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?