ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DEAP脑电数据集情绪二分类:从特征提取到严谨评估的完整实践

DEAP脑电数据集情绪二分类:从特征提取到严谨评估的完整实践 简介情绪识别是脑机接口与人机交互的重要研究方向而脑电信号EEG因其高时间分辨率成为主流数据源。在实际工程中情绪二分类通常需要对原始脑电进行预处理、特征工程与分类建模但仅关注模型精度容易忽略数据划分与评估协议对泛化性能的直接影响。利用公开数据集验证算法时差分熵等频域特征能有效刻画情绪相关脑电活动配合SVM、XGBoost等常用分类器可构建稳定基线。然而若对滑动窗口样本采用随机划分或全局标准化极易引入数据泄漏导致评估结果虚高。更严谨的做法是采用按试次分组或留一受试者交叉验证并逐被试报告准确率与AUC。本文以DEAP脑电数据集为例系统梳理从标签构造、特征提取到模型评估的完整流程为进行可复现的脑电情绪分类实验提供工程化参考。1. DEAP脑电数据集的脑电情绪二分类算法先定评估协议再谈模型精度DEAP脑电数据集是情感计算里被引用最多的公开脑电资源之一但把“基于DEAP脑电数据集的脑电情绪二分类算法”这个流程做扎实难点往往不在模型而在数据准备和验证方式。32名被试、40段带评分刺激、32个EEG通道、128Hz采样这套数据规模看起来够用实际处理时却处处是坑标签需要自己从量表分数切分、同一条trail的相邻时间窗强相关、不同被试的个体差异远大于模型差异。这篇文章按“数据读取 → 标签构造 → 频域特征 → 分类模型 → 严谨评估”的顺序给出一条能直接复现的路线。适合准备在DEAP上做基线实验的研究生也适合想把手头EEG分类流程里的数据泄漏问题一次理清的工程师。思路不限于DEAP换到SEED、MAHNOB等数据集时同样成立。2. DEAP脑电数据集的读取、预处理与二分类标签构造2.1 DEAP脑电数据集的目录结构与加载方式搜索deap数据集下载时官网会提供两个压缩包data_raw和data_preprocessed_matlab。前者是512Hz原始信号后者已经完成去眼电、4.0-45.0Hz带通滤波、平均参考以及128Hz降采样是绝大多数情绪二分类基线实验的起点。预处理包按被试编号保存为s01.mat到s32.mat每个文件里有两个关键变量data形状为(40, 40, 8064)的三维数组分别对应40段试次、40个通道、每段63秒×128Hz的采样点labels形状为(40, 4)的评分矩阵四列依次是valence效价、arousal唤醒度、dominance支配度、liking喜欢程度取值1-9通道排列上前32个是EEG通道按10-20国际导联系统排列后8个是眼电、肌电、皮电等外围信号。做DEAP脑电情绪识别时默认只保留前32个EEG通道外围信号按需使用。from scipy.io import loadmat import numpy as np data_mat loadmat(data_preprocessed_matlab/s01.mat) data data_mat[data] # (40, 40, 8064) labels data_mat[labels] # (40, 4) # 仅保留EEG通道去掉后8个外圈信号 eeg data[:, :32, :] # (40, 32, 8064) # 每段试次前3秒是基线去掉后剩60秒有效信号 baseline_len 3 * 128 eeg_signal eeg[:, :, baseline_len:] # (40, 32, 7680) print(EEG信号形状:, eeg_signal.shape)scipy.io.loadmat在读入mat文件时会把MATLAB的cell和struct自动转成numpy对象DEAP预处理包里的变量是普通数值矩阵直接索引即可。注意如果遇到无法读取的报错先检查该mat文件是否为MATLAB v7.3格式若是则改用h5py按HDF5方式读取但DEAP官网的data_preprocessed_matlab一般不涉及这个问题。2.2 DEAP二分类标签构造valence阈值的选取与影响DEAP官方不提供现成的情绪类别标签二分类标签需要从主观评分中二值化。“情绪二分类”通常指valence的高/低二分valence评分1-9分数越高情绪越正面。最常见的切分方式是valence 5判为正面情绪 5判为负面情绪。阈值取5利用了评分尺度的中位点样本量大体均衡32名被试×40段视频正面和负面样本接近1:1。但这个选择并非唯一标准阈值改到4或6都会改变分类问题的难度因为评分恰好落在边界附近的试次会被重新归类。稳妥做法是先画出全体试次的valence直方图确认边界处的样本分布后再定阈值。def make_binary_labels(valence, threshold5): return (valence threshold).astype(int) y np.concatenate([make_binary_labels(labels[:, 0]) for labels in all_labels])每个被试的评分习惯差异很大有人习惯给7-8分有人倾向4-5分这种主观偏移无法通过阈值消除只能在特征标准化环节缓解。另外合并多个被试数据时要注意labels按试次顺序排列特征矩阵的行顺序必须与标签逐一对齐否则训练时的精确匹配会被打乱。2.3 基线校正与坏试次检查DEAP预处理版本虽然完成了滤波和去眼电但基线漂移依然存在。常见做法是取每段试次前3秒的均值从整个信号中减去再做一次50Hz陷波以排除工频干扰。坏试次检查也值得做如果某段试次的某个通道方差过大或长时间平直说明存在松动或饱和直接删除该试次比后续强行降噪更省事。# 以s01为例逐通道减去基线均值 baseline_mean eeg[:, :, :baseline_len].mean(axis2, keepdimsTrue) eeg_corrected eeg[:, :, :] - baseline_mean # 检查坏通道方差过小的通道直接置为NaN并在后续剔除 channel_var eeg_corrected.var(axis2) bad_channel_mask channel_var 1e-6 print(坏通道数量:, bad_channel_mask.sum())这里减去的是每个试次各自的基线均值保留了个体间的绝对电位差异。如果做跨被试分析后续还需要再次标准化基线校正只负责去掉试次内的漂移不负责对齐个体差异。3. DEAP脑电情绪识别的频域特征差分熵特征矩阵的完整提取3.1 为什么选用差分熵特征而不是原始波形原始EEG信号每个试次60秒×32通道如果直接拉伸成向量喂给分类器维度大、噪声多且不同被试同一通道的幅值分布差异明显。DEAP脑电情绪识别研究中更稳定的做法是把信号转换到频域提取有生理含义的频带特征。差分熵Differential Entropy是情感识别文献中高频出现的特征。对于近似高斯分布的脑电信号差分熵可以解析计算为DE 0.5 * ln(2 * pi * e * sigma^2)其中sigma^2是信号在某频带内的方差也可以由该频带平均功率谱密度近似代替。这个形式本质上把能量特征做了对数压缩既保留了频带能量差异又缩小了幅值尺度的影响对跨被试的分布偏移更鲁棒。DEAP预处理数据已经被4-45Hz带通过滤波因此Delta频带0.5-4Hz大部分被滤除常用的有效频带取Theta4-8Hz、Alpha8-12Hz、Beta12-30Hz、Gamma30-45Hz四个。特征提取的算法流程图可以概括为原始信号 → 滑动分窗 → Welch功率谱估计 → 频带功率聚合 → 对数差分熵。每个试次的60秒有效信号按4秒窗口切分为15个样本样本总量达到32×40×1519200个既扩增了数据量也保持了试次内的时间连续性。3.2 用Welch功率谱计算差分熵特征from scipy.signal import welch from scipy.integrate import trapz def extract_de_features(eeg_trial, fs128, win_sec4): eeg_trial: 形状(3, 8064)单试次全通道信号 返回: 形状(15, 32*4)的差分熵特征矩阵 n_channels eeg_trial.shape[0] signal eeg_trial[:, 3*fs:] # 去掉前3秒基线 win_len win_sec * fs # 4秒 → 512个采样点 n_windows (signal.shape[1] - win_len) // win_len 1 bands {theta: (4, 8), alpha: (8, 12), beta: (12, 30), gamma: (30, 45)} feat_list [] for w in range(n_windows): seg signal[:, w*win_len:(w1)*win_len] window_feat [] for ch in range(n_channels): freqs, psd welch(seg[ch], fsfs, npersegwin_len) for band_name, (lo, hi) in bands.items(): idx np.logical_and(freqs lo, freqs hi) band_power trapz(psd[idx], freqs[idx]) # 差分熵近似对数功率 de 0.5 * np.log(2 * np.pi * np.e * band_power) window_feat.append(de) feat_list.append(window_feat) return np.array(feat_list) # (15, 128)welch的nperseg参数设为与窗口等长频率分辨率正好是1Hz足以分辨4Hz以上的频带边界。trapz的作用是对功率谱密度在频带区间内做数值积分得到该频带的总功率比直接取平均更能反映窄带能量。如果某个频带功率为0np.log会给出负无穷实际数据中不会出现这种情况但代码中建议加一个极小值保护band_power np.maximum(band_power, 1e-12)3.3 特征标准化与个体差异处理DEAP跨被试分类最大的干扰源是被试间的基线差异。有人全脑功率普遍偏高有人整体偏低如果不做处理分类器学到的可能是“被试身份”而不是“情绪状态”。常用的做法是逐被试做z-score标准化即对每个被试的全部分类样本在特征维度上减去该被试的均值并除以标准差。from sklearn.preprocessing import StandardScaler def per_subject_standardize(feature_matrix, subject_id): scaler StandardScaler() X_scaled np.zeros_like(feature_matrix) for sid in np.unique(subject_id): mask subject_id sid X_scaled[mask] scaler.fit_transform(feature_matrix[mask]) return X_scaled注意逐被试标准化必须在划分训练集和测试集之前完成并且测试集标准化时不能使用全体数据拟合的统计量。上面的代码是逐被试独立拟合的不存在泄漏如果换成全局拟合的StandardScaler就会把测试集中被试的均值和方差带入训练过程导致评估结果虚高。4. 二分类模型选型与XGBoost训练评估4.1 在DEAP上比较SVM、XGBoost与浅层网络DEAP特征矩阵规模在2万样本以内特征维度128模型选型的关键不是表达能力而是正则化和训练稳定性。按经验SVM配合RBF核在特征缩放后的数据上表现稳定但高维核矩阵的计算成本会随样本量上升用xgboost做二分类模型近年更流行训练速度快对特征尺度和缺失值不敏感而且能输出特征重要性用于后续分析浅层全连接网络和LSTM也能用但脑电解码任务在小样本下容易出现较大方差需要更严格的调参和重复实验。模型适用规模主要调参对象训练速度过拟合风险SVM RBF2万样本内C、gamma中等低XGBoost2万样本以上max_depth、subsample快中需配合早停全连接网络需5000样本以上层数、dropout中等高LSTM需保留时序结构隐层维度、学习率慢高这里给出一个直接结论在DEAP上做情绪二分类基线先跑SVM和XGBoost两个模型就够了。神经网络留给特征工程之后的增强实验不推荐作为第一版基线因为调参成本高且结果波动大。4.2 用XGBoost训练DEAP脑电情绪二分类模型import xgboost as xgb from sklearn.metrics import roc_auc_score, accuracy_score model xgb.XGBClassifier( n_estimators300, max_depth4, learning_rate0.05, subsample0.8, colsample_bytree0.8, reg_lambda1.0, scale_pos_weightsum(y_train 0) / sum(y_train 1), eval_metriclogloss, early_stopping_rounds30, random_state42 ) model.fit(X_train, y_train, eval_set[(X_val, y_val)], verboseFalse) pred_proba model.predict_proba(X_test)[:, 1] print(准确率:, accuracy_score(y_test, model.predict(X_test))) print(AUC:, roc_auc_score(y_test, pred_proba))max_depth4控制树深度DEAP特征维度只有128深度过大会在早期就拟合训练集噪声learning_rate0.05配合300棵树是平衡迭代次数的常用组合subsample和colsample_bytree都控制在0.8减少每棵树的样本和特征相关性。scale_pos_weight根据正负样本比例自动调整若用阈值5切分valence这个值通常接近1但如果某次划分恰巧让训练集分布倾斜这一项能显著稳定性能。early_stopping_rounds在验证集上监控logloss一旦连续30轮不再下降就提前停止避免过拟合。验证集必须从训练集中单独切出不能复用测试集否则停止时机由测试集决定最终准确率会被乐观偏差污染。4.3 评估协议的三个坑数据泄漏、随机划分与单被试误差DEAP二分类最容易踩的坑有三个。第一按窗口样本做随机划分。滑动窗口切出的特征矩阵中同一个试次的多个窗口共享标签且高度相关随机划分会让模型在训练时“见过”同一试次的兄弟窗口测试时自然表现好。解决方法是按试次分组或直接按被试分组。第二在整批数据上先做标准化再划分。如果StandardScaler在没有分组的情况下拟合了全部数据的均值和方差测试集的信息已经进入训练流程。正确做法是训练集和测试集各自拟合标准化参数或在训练集上拟合后应用到测试集。第三只报告平均准确率。DEAP的32个被试中必然存在某几个人分类准确率明显低于平均线。平均准确率掩盖了这种个体差异也不利于判断模型是否只对特定被试有效。记录每个被试的准确率和AUC比只关心总体均值重要得多。from sklearn.model_selection import GroupKFold gkf GroupKFold(n_splits5) for train_idx, test_idx in gkf.split(X, y, groupstrial_id): # 每组内部是一个完整试次同一个试次的窗口不会跨组 X_train, X_test X[train_idx], X[test_idx] y_train, y_test y[train_idx], y[test_idx]GroupKFold的groups参数传入试次编号确保任何窗口不会同时出现在训练集和测试集。这样得到的准确率比随机划分低几个百分点但更接近真实泛化水平。5. 用留一受试者交叉验证与误差分析给DEAP二分类算法做体检5.1 留一受试者交叉验证的最小实现逐试次分组已经比随机划分严谨但DEAP数据集中同一被试的40段试次仍然共享个体特征。最保守的评估方式是留一受试者交叉验证LOSO每次留下一个被试的全部40段试次作为测试集用其余31个被试训练模型重复32次。这个协议最能反映模型面对陌生人的脑电信号时的表现也是脑电情绪识别论文里认可度最高的验证方式。subject_ids np.repeat(np.arange(32), 40 * 15) # 每个被试40试次*15窗口 acc_list, auc_list [], [] for test_sub in range(32): train_mask subject_ids ! test_sub test_mask subject_ids test_sub clf xgb.XGBClassifier(n_estimators200, max_depth4, learning_rate0.05) clf.fit(X[train_mask], y[train_mask]) proba clf.predict_proba(X[test_mask])[:, 1] acc_list.append(accuracy_score(y[test_mask], (proba 0.5).astype(int))) auc_list.append(roc_auc_score(y[test_mask], proba)) print(f被试{test_sub1:02d} 准确率{acc_list[-1]:.3f} AUC{auc_list[-1]:.3f}) print(f平均准确率: {np.mean(acc_list):.3f} ± {np.std(acc_list):.3f}) print(f平均AUC: {np.mean(auc_list):.3f} ± {np.std(auc_list):.3f})运行32次完整训练大约几分钟时间完全可接受。如果某个被试的valence评分全部集中在阈值一侧测试时会出现单一类别报错可以在循环开头打印该被试的标签分布把明显不平衡的被试单独标记。5.2 用概率校准和最优阈值修正输出XGBoost输出的概率值往往没有经过校准0.7的概率不代表真实有70%的把握。在二分类任务中如果后续要拼接决策阈值或做置信度过滤先用Platt缩放或保序回归校准概率再重新选择阈值。from sklearn.calibration import CalibratedClassifierCV clf_calibrated CalibratedClassifierCV(clf, methodisotonic, cv5) clf_calibrated.fit(X_train, y_train) # 在验证集上选使约登指数最大的阈值 from sklearn.metrics import roc_curve fpr, tpr, thres roc_curve(y_val, clf_calibrated.predict_proba(X_val)[:, 1]) j_index tpr - fpr best_threshold thres[np.argmax(j_index)]阈值在这个场景里通常接近0.5但不会严格等于0.5。当正负样本不完全均衡时调整阈值能挽回几个百分点的F1值代价是准确率可能略降。是否值得调节阈值取决于最终用途如果只是报告算法基线0.5阈值更规范如果要接入实时系统用约登指数找出最优工作点更合理。5.3 单被试误差汇总表与频带层面的错误解释LOSO跑完后把每个被试的准确率、AUC、测试样本数汇总成一张表能快速定位模型失效的具体人群。下面是一个示意结构被试ID测试窗口数准确率AUC标签分布正/负S015850.8410.902296/289S025850.7180.774305/280S035850.7950.861241/344如果某个被试的AUC显著低于全休平均优先检查两个原因一是该被试的频带功率分布是否整体偏移二是其标签是否因阈值切分而严重失衡。此时可以单独打印该被试四个频带的差分熵分布直方图对比分类错误的窗口是否集中在某几个通道。把错误样本定位到通道和频带层级后再决定是调整特征权重还是补充个体化标准化策略。本文还有配套的精品资源点击获取
返回列表