news 2026/10/26 2:01:47

基于DEAP与SEED-IV的EEG情绪检测:从预处理到通道级SVM分类全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于DEAP与SEED-IV的EEG情绪检测:从预处理到通道级SVM分类全流程

简介:该资源是一篇面向情感计算与脑机接口方向学习者的研究论文,聚焦基于EEG信号的情绪检测,适合做毕业设计、课程研究或入门脑电信号处理的学生参考。论文以DEAP和SEED-IV两个公开数据库为数据来源,采用离散小波变换将预处理信号分解为5个频段,提取功率、能量、微分熵和时域等特征,并构建通道级SVM分类器与通道组合器完成二维情绪模型下的状态识别,四类分类准确率在DEAP上为74%、86%、72%、84%,在SEED-IV上为79%、76%、77%、74%。资源包共1个PDF文件,约701KB,内容为完整的英文论文正文,涵盖摘要、引言、相关工作、方法设计与实验结果,可帮助读者理解EEG情绪检测的完整技术路线与特征工程思路。目前已有346人学习,适合需要复现实验流程或撰写相关论文的研究者参考。

1. 从一份公开数据集出发:EEG 情绪检测方案要解决什么

做毕设最怕的不是算法难,而是数据拿不到、流程跑不通、结果说不清。这套方案要解决的就是这件事:用 DEAP 和 SEED-IV 两个公开脑电数据库,把「原始 EEG 信号 → 预处理 → 特征提取 → 通道级 SVM 分类 → 通道融合 → 四类情绪判定」这条链路完整走通,最终给出可复现的分类准确率。它适合正在做脑机接口、情感计算方向毕业设计或课程设计的学生,也适合需要快速搭一套生理信号分类基线的从业者。整套方案不依赖自采设备,全部基于公开数据,硬件门槛为零,难点集中在信号处理和特征工程上。

2. 数据与预处理设计:两个数据库的差异怎么抹平

DEAP 和 SEED-IV 的采集协议完全不同,直接混用会翻车。DEAP 是 32 通道、500Hz 采样、32 名被试、每人 40 段 1 分钟视频刺激;SEED-IV 是 64 通道、1000Hz 采样、15 名被试、每人 3 个 session、每个 session 24 段 2 分钟视频。通道数、采样率、事件时长全对不上,所以方案里必须先把「统一口径」这件事做扎实,否则后面特征维度对不齐,SVM 直接报错。

2.1 事件切分与通道对齐

DEAP 原始数据是连续记录,需要按采集协议把每段视频对应的时间窗切出来,转成 mat 文件;SEED-IV 官方给的就是已经切好的 mat,事件是预分离的。这一步的关键是只保留两个库共有的通道,方案里统一取 32 通道作为特征计算的基础,SEED-IV 多出来的通道在预处理阶段就裁掉。采样率方面,SEED-IV 的 1000Hz 数据在读取后做降采样到与 DEAP 一致的量级,保证后续 DWT 分解的频带边界含义一致。

import scipy.io as sio import numpy as np from scipy.signal import resample def load_deap(path): # DEAP 的 mat 里 data 字段形状为 [ trials, channels, samples ] raw = sio.loadmat(path) data = raw['data'] # 40 x 40 x 8064,前 32 通道是 EEG eeg = data[:, :32, :] # 只取 32 个 EEG 通道 return eeg def load_seed4(path, target_fs=500): raw = sio.loadmat(path) eeg = raw['data'] # 已切分的事件数据 orig_fs = 1000 if orig_fs != target_fs: n_samples = int(eeg.shape[-1] * target_fs / orig_fs) eeg = resample(eeg, n_samples, axis=-1) return eeg

上面两个函数把「通道对齐」和「采样率对齐」一次做完。data[:, :32, :]这行是血泪经验:DEAP 的 mat 里第 33 到 40 通道是外周生理信号(眼电、肌电等),不裁掉会污染特征。resample的axis=-1表示沿时间轴重采样,别写成axis=0,否则会把通道数改掉。

2.2 三级滤波链的设计逻辑

预处理不是随便套个带通就完事。方案里用三级滤波串联:先 50Hz 陷波去工频干扰(国内电网 50Hz,用 60Hz 会漏),再做平均参考重参考,最后 0.1–60Hz 带通把信号限制在 EEG 有效频段内。中间还插了一级 10 阶一维中值滤波去脉冲噪声。顺序不能乱——陷波必须在带通之前,否则 50Hz 成分会被带通部分保留;中值滤波放在重参考之后,避免参考电极引入的直流量被中值滤波误判成脉冲。

from scipy.signal import iirnotch, filtfilt, medfilt, butter def preprocess(eeg, fs=500): # 1) 50Hz 陷波 b, a = iirnotch(50, 30, fs) eeg = filtfilt(b, a, eeg, axis=-1) # 2) 平均参考 eeg = eeg - eeg.mean(axis=1, keepdims=True) # 3) 10 阶中值滤波去脉冲 eeg = medfilt(eeg, kernel_size=(1, 1, 11)) # 4) 0.1-60Hz 带通,20 阶 b, a = butter(10, [0.1, 60], btype='band', fs=fs) eeg = filtfilt(b, a, eeg, axis=-1) return eeg

iirnotch(50, 30, fs)里第二个参数是品质因数,30 对应较窄的陷波带宽,既能压住工频又不会把邻近频段削掉。medfilt的kernel_size=(1,1,11)表示只在时间维做 11 点中值,通道维不动。带通的butter(10, ...)是 10 阶,对应原文的 20 阶滤波器(butter的阶数是实际阶数的一半语义,这里按等效陡度取 10)。filtfilt做零相位滤波,避免相位延迟导致事件时间窗错位——这一点在情绪检测里很关键,因为情绪响应本身就有几百毫秒的潜伏期。

注意:中值滤波的核长必须是奇数,写成 10 会直接抛异常;另外medfilt对多维数组的kernel_size要写全维度,只写一个整数会作用到所有轴。

3. 特征工程与通道级分类器:34 维特征向量怎么来

特征提取是整个方案里最影响准确率的一环。原文用 8 层 db8 小波分解出 delta、theta、alpha、beta、gamma 五个频带,再在每个频带上算能量、功率谱密度、微分熵、平均带功率,加上时域统计量和 Hjorth 参数,凑成 34 维特征。这个维度不是拍脑袋定的,是「时域 12 维 + 频域 20 维 + 其他 2 维」的组合,刚好能喂给每个通道一个独立 SVM。

3.1 五频带 DWT 分解与频域特征

db8 小波做 8 层分解,采样率 500Hz 时第 8 层近似系数对应 0–1Hz,细节系数逐层对应 1–2、2–4、4–8、8–16、16–32、32–64Hz。要拿到 delta(1–3)、theta(4–7)、alpha(8–13)、beta(14–30)、gamma(31–60) 这五段,需要把相邻层的细节系数按频带重组,而不是简单取某一层。

import pywt import numpy as np def band_features(segment, fs=500): # 8 层 db8 分解 coeffs = pywt.wavedec(segment, 'db8', level=8) feats = [] # 按频带重组:delta/theta/alpha/beta/gamma bands = { 'delta': (1, 3), 'theta': (4, 7), 'alpha': (8, 13), 'beta': (14, 30), 'gamma': (31, 60) } for name, (lo, hi) in bands.items(): # 用带通滤波后的信号算该频带能量与微分熵 from scipy.signal import butter, filtfilt b, a = butter(4, [lo, hi], btype='band', fs=fs) band_sig = filtfilt(b, a, segment) energy = np.sum(band_sig ** 2) # 微分熵:高斯假设下 DE = 0.5*log(2*pi*e*var) de = 0.5 * np.log(2 * np.pi * np.e * np.var(band_sig) + 1e-8) feats.extend([energy, de]) return np.array(feats) # 5 频带 x 2 特征 = 10 维

这里用带通滤波重组频带而不是直接取小波系数,是因为 db8 的频带边界和标准 EEG 频段不完全重合,直接取系数会让 alpha 和 beta 的边界模糊。1e-8是防止方差为零时 log 发散,这个后悔药必须加,否则遇到平坦段直接出 inf。微分熵按高斯分布公式算,是 EEG 情绪检测里的常用近似,比直接算香农熵稳定。

3.2 时域统计量与 Hjorth 参数

时域部分取均值、RMS、标准差、一阶差分、归一化一阶差分、二阶差分、归一化二阶差分、峰度、偏度、方差,再加 Hjorth 的 mobility 和 complexity。Hjorth 三个参数里 activity 就是方差,所以实际新增的是 mobility 和 complexity 两个。

from scipy.stats import kurtosis, skew def time_features(x): d1 = np.diff(x) d2 = np.diff(x, n=2) feats = [ np.mean(x), np.sqrt(np.mean(x**2)), np.std(x), np.mean(np.abs(d1)), np.mean(np.abs(d1)) / (np.std(x) + 1e-8), np.mean(np.abs(d2)), np.mean(np.abs(d2)) / (np.std(x) + 1e-8), kurtosis(x), skew(x), np.var(x) ] # Hjorth mobility / complexity mob = np.std(d1) / (np.std(x) + 1e-8) comp = (np.std(d2) / (np.std(d1) + 1e-8)) / (mob + 1e-8) feats.extend([mob, comp]) return np.array(feats) # 12 维

归一化一阶差分除以标准差,是为了消除不同被试信号幅度差异带来的量纲漂移——这是跨被试分类能不能稳住的关键。分母的1e-8同样是防零除。峰度和偏度用scipy.stats直接算,注意kurtosis默认是 Fisher 定义(正态分布为 0),如果和别的库对比结果对不上,先查这个。

3.3 通道级 SVM 与通道融合策略

原文的核心设计是「每个通道一个 SVM」,DEAP 32 通道就是 32 个分类器,每个分类器输入该通道的 34 维特征,输出四类情绪之一。然后做通道融合,把 32 个通道的预测结果合成最终情绪状态。融合方式原文没细说,通常的做法是多数投票,或者按通道历史准确率加权投票。

from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from collections import Counter def train_channel_svms(X_train, y_train): # X_train: [n_samples, n_channels, n_features] models = [] for ch in range(X_train.shape[1]): scaler = StandardScaler() Xc = scaler.fit_transform(X_train[:, ch, :]) clf = SVC(kernel='rbf', C=1.0, gamma='scale') clf.fit(Xc, y_train) models.append((scaler, clf)) return models def predict_fusion(models, X_test): votes = [] for ch, (scaler, clf) in enumerate(models): pred = clf.predict(scaler.transform(X_test[:, ch, :])) votes.append(pred) votes = np.array(votes) # [n_channels, n_samples] # 多数投票 final = [Counter(votes[:, i]).most_common(1)[0][0] for i in range(votes.shape[1])] return np.array(final)

SVC(kernel='rbf', C=1.0, gamma='scale')是基线配置,gamma='scale'会按特征数自动缩放,比手写固定值稳。每个通道单独StandardScaler很重要——不同通道的绝对幅度差异大,不归一化的话 RBF 核的距离度量会被大幅值通道主导。多数投票用Counter实现,平票时most_common返回先出现的类别,如果要更严谨可以改成按通道验证集准确率加权。

注意:训练集和测试集必须按被试划分,不能按样本随机划分。DEAP 用 22 人训练、10 人测试,SEED-IV 用 32 人训练、13 人测试。按样本随机划分会让同一被试的数据同时出现在训练和测试里,准确率虚高十几个点,答辩时一问就露馅。

4. 实验配置与结果复现:四类情绪怎么定义和评估

四类情绪按二维 valence-arousal 模型划分:HAHV(高唤醒高效价)、HALV(高唤醒低效价)、LALV(低唤醒低效价)、LAHV(低唤醒高效价)。DEAP 的标签来自被试自评,SEED-IV 的标签来自视频刺激的预设情绪类别,两个库的标签体系不同,所以方案里是分别训练两个独立模型,不混训。

4.1 训练/测试划分与特征矩阵形状

DEAP 训练集特征矩阵是 880×34:22 名被试 × 40 个事件 = 880 个样本,每个样本 34 维。SEED-IV 训练集是 742×34:32 名被试 × 24 个事件 ≈ 768,去掉部分无效事件后约 742。测试集分别是 10 人 × 40 = 400 和 13 人 × 24 ≈ 312。这个形状对不上是正常的,因为两个库事件数不同,关键是每个通道的分类器输入维度一致。

配置项DEAPSEED-IV
通道数3232(从 64 裁剪)
采样率500Hz降采样至 500Hz
训练被试2232
测试被试1013
每被试事件数4024
特征维度3434
分类器数量3232

4.2 分类结果与解读

原文报告的四类准确率:DEAP 为 74%、86%、72%、84%,SEED-IV 为 79%、76%、77%、74%。DEAP 的 HALV 类到 86%,明显高于其他类,通常是因为该类样本在特征空间里更可分;SEED-IV 四类比较均衡,在 74–79% 之间,说明跨 session 的分布漂移被平均掉了。整体落在 72–86% 区间,和文献综述里 74–90% 的范围一致,属于合理水平。

from sklearn.metrics import classification_report, confusion_matrix def evaluate(models, X_test, y_test): y_pred = predict_fusion(models, X_test) print(classification_report(y_test, y_pred, target_names=['HAHV', 'HALV', 'LALV', 'LAHV'])) print(confusion_matrix(y_test, y_pred)) return y_pred

跑完classification_report先看每类的 precision 和 recall 是否均衡。如果某一类 recall 特别低,多半是训练集里该类样本太少,或者通道融合的投票被多数类带偏。confusion_matrix用来定位混淆对——比如 HALV 和 LAHV 互相混淆多,说明 valence 维度的特征区分度不够,需要回头加强频域特征。

提示:复现时如果准确率比原文低 5 个点以上,先查三件事——陷波频率是不是 50Hz、训练测试有没有按被试划分、每个通道有没有单独标准化。这三处是最常见的翻车点。

5. 避坑与排查:这套流程里最容易出错的五个地方

5.1 准确率虚高,一换被试就崩

现象是训练集和测试集随机划分时准确率能到 90% 以上,按被试划分后掉到 70% 左右。原因是同一被试的 EEG 片段在统计上高度相关,随机划分造成信息泄漏。解决办法是严格按被试 ID 划分,训练集和测试集的被试集合不相交,这一点在代码里用subject_id做 group split 就能保证。

5.2 DWT 分解后频带对不上

现象是提取出来的 alpha 频带能量异常大或异常小。原因通常是采样率没统一——SEED-IV 原始 1000Hz 直接做 8 层分解,频带边界会整体偏移一倍。解决方法是先降采样到 500Hz 再做 DWT,或者在分解前用pywt.scale2frequency核对每层对应的实际频率范围。

5.3 通道融合后结果反而变差

现象是单通道最高准确率有 80%,融合后掉到 70%。原因是某些通道信噪比极低,多数投票被噪声通道拖累。解决办法是给每个通道按验证集准确率设权重,低于随机水平的通道直接剔除,或者改用加权投票而不是等权投票。

5.4 中值滤波把有效信号削掉

现象是预处理后波形变得过于平滑,高频成分丢失。原因是中值滤波核长设得太大,或者作用到了通道维。解决方法是核长控制在 5–11 点之间,kernel_size明确写成(1, 1, k),只沿时间轴滤波。

5.5 标签映射错位

现象是混淆矩阵里类别完全对不上,准确率接近随机。原因是 DEAP 的自评标签和 SEED-IV 的预设标签阈值不同,valence/arousal 的高低分界点没统一。解决办法是在各自库内独立做二值化,DEAP 用被试自评的中位数做阈值,SEED-IV 用官方标签,两个库的类别编号在代码里显式映射,不共用一套 label encoder。

6. 验收与答辩:怎么证明这套方案真的跑通了

验收这套方案,核心不是看准确率数字好不好看,而是看流程是否可复现、划分是否干净、结果是否可解释。答辩时评委最常问的三个问题是:数据怎么切的、特征为什么这么选、准确率有没有水分。提前把这三件事的证据准备好,比背十页 PPT 管用。

具体做法是准备一张「复现检查表」,每项都要有对应的代码位置和输出截图。第一项,数据划分:打印训练集和测试集的被试 ID 列表,证明两者无交集。第二项,预处理:对同一段原始信号,画出滤波前后的波形对比图,标出 50Hz 陷波前后的频谱差异。第三项,特征:给出一个通道的 34 维特征向量样例,说明每一维的来源。第四项,分类:给出四类的混淆矩阵和每类 precision/recall,而不只是总体准确率。第五项,融合:对比单通道最优、等权投票、加权投票三种策略的准确率,说明为什么选当前方案。

验收项证据形式合格标准
被试划分训练/测试被试 ID 列表无交集
预处理滤波前后波形+频谱图50Hz 峰明显下降
特征提取单通道 34 维向量样例维度与表 1 一致
分类结果混淆矩阵+分类报告四类均有 recall
通道融合三种策略对比表融合不低于单通道最优

答辩陈述的顺序建议反过来讲:先给最终混淆矩阵,再倒推「为了得到这个结果,我做了哪几步关键处理」,最后讲遇到的坑和怎么解决的。评委对「踩坑—排查—修正」的过程比对一个漂亮数字更买账,因为这证明你真的动手跑过,而不是抄了个流程。

我自己的习惯是,每次交方案前强制走一遍「换一台机器从零跑」的流程——清空环境、重新装依赖、从头执行脚本,任何一步报错都记下来补进文档。这个习惯救过我好几次,因为本地能跑不代表交付能跑,路径写死、随机种子没固定、依赖版本漂移,都是答辩现场翻车的经典原因。从那以后我每次交付前都强制走一遍干净环境复现,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 2:00:13

园区数字化先别急,把「管」和「问」这两件事分清楚

管现场是一层,问数据是另一层,别糊到一起前阵子和一个做产业园的朋友吃饭,他说他们园区去年刚上了一套管理系统,台账算是理顺了。可老板还是天天追着他要数,上个月哪栋楼空置率升了,哪个租户快到期了。他苦…

作者头像 李华
网站建设 2026/10/26 1:59:11

ASA原理与电子维修实战:11 从曲线反推器件与电路

第11章 从曲线反推器件与电路 从电气响应到候选电路模型 第10章建立了 OFHVC 推理方法,明确了从观察、寻找差异、建立假设、验证到确认的诊断流程。本章进一步研究其中一个核心问题:当我们看到一条尚不清楚来源的 Signature 时,如何利用曲线特征推断可能的电气行为,并逐…

作者头像 李华
网站建设 2026/10/26 1:59:06

560.和为k的子数组

一、题目描述 给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的子数组的个数。子数组是数组中元素的连续非空序列。示例 1: 输入:nums [1,1,1], k 2 输出:2示例 2: 输入:nums [1,2…

作者头像 李华
网站建设 2026/10/26 1:58:16

【RAG项目】02、RAG项目-文本上传WEB服务

离线流程安装streamlit pip install streamlitimport streamlit as st from sympy import false# 添加网页标题 st.title("知识库更新服务")uploader_file st.file_uploader("请上传txt文件",type ["txt"],accept_multiple_filesfalse )if upl…

作者头像 李华
网站建设 2026/10/26 1:56:57

Redis命令:SINTERSTORE

Redis SINTERSTORE 命令详细教程 SINTERSTORE 计算多个集合的交集,并把结果写入指定的目标 Key,返回写入的成员数量。它是 SINTER 的写版本:SINTER 把交集返回给客户端,SINTERSTORE 把交集留在 Redis 里,便于缓存、后…

作者头像 李华