用Python和MNE处理SEED情感脑电数据从原始.mat到机器学习就绪的完整指南当你第一次打开SEED数据集文件夹时那些密密麻麻的.mat文件可能会让你感到无从下手。作为上海交通大学BCMI实验室发布的经典情感脑电数据集SEED包含了15名被试观看情感视频时的62通道EEG记录但它的存储方式对Python开发者并不友好。本文将带你从零开始构建一个完整的处理流程把原始的.mat文件转化为MNE-Python可以直接使用的标准化数据结构并关联情感标签为后续的机器学习模型训练做好准备。1. 理解SEED数据集的结构与挑战SEED数据集采用MATLAB的.mat格式存储每个文件包含15个试次(trial)的脑电数据对应15段情感视频刺激。每个试次的数据以djc_eeg1到djc_eeg15的键名存储形状为62×NN取决于试次长度约47000个采样点。主要技术挑战包括非标准数据格式需要从.mat文件中提取numpy数组多试次嵌套结构每个.mat文件包含15个独立试次通道信息缺失原始数据不包含电极位置等元数据标签分离存储情感标签统一存放在单独的label.mat文件中import scipy.io as sio sample_file dujingcheng_20131027.mat data sio.loadmat(sample_file) print(f文件包含的键: {list(data.keys())[3:15]}) # 前15个是数据键 print(f单个试次数据形状: {data[djc_eeg1].shape})典型输出显示每个试次是62通道×47001时间点的矩阵采样率为200Hz来自原始论文说明。这意味着每5毫秒采集一个数据点每个试次约235秒时长。2. 构建基础数据处理框架2.1 创建MNE兼容的数据结构MNE-Python是处理脑电数据的黄金标准工具我们需要将原始数据转换为它的RawArray格式。关键步骤包括创建包含通道信息的info对象将numpy数组包装为RawArray裁剪掉初始不稳定信号(如前5秒)import mne import numpy as np # 标准62通道名称 (按SEED数据集文档顺序) ch_names [FP1,FPZ,FP2,AF3,AF4,F7,F5,F3,F1,FZ,F2, F4,F6,F8,FT7,FC5,FC3,FC1,FCZ,FC2,FC4, FC6,FT8,T7,C5,C3,C1,CZ,C2,C4,C6,T8, TP7,CP5,CP3,CP1,CPZ,CP2,CP4,CP6,TP8,P7, P5,P3,P1,PZ,P2,P4,P6,P8,PO7,PO5,PO3, POZ,PO4,PO6,PO8,CB1,O1,OZ,O2,CB2] def create_mne_raw(eeg_data, sfreq200): 将numpy数组转换为MNE Raw对象 info mne.create_info( ch_namesch_names, sfreqsfreq, ch_types[eeg]*62 ) return mne.io.RawArray(eeg_data, info).crop(tmin5)2.2 处理标签系统SEED使用统一的情感标签方案1积极(Positive)0中性(Neutral)-1消极(Negative)标签顺序固定为[1,0,-1,-1,0,1,-1,0,1,1,0,-1,0,1,-1]对应每个.mat文件中的15个试次。basic_labels [1,0,-1,-1,0,1,-1,0,1,1,0,-1,0,1,-1] label_mapping {1:positive, 0:neutral, -1:negative}3. 批量处理与数据管道构建3.1 高效批量读取策略处理大量.mat文件时我们需要考虑内存管理和处理效率。以下是一个优化的批量处理方案import os from tqdm import tqdm # 进度条显示 def process_seed_dataset(data_dir, max_filesNone): 处理整个SEED数据集目录 all_raws [] all_labels [] # 获取所有.mat文件 mat_files [f for f in os.listdir(data_dir) if f.endswith(.mat)] if max_files: mat_files mat_files[:max_files] for file in tqdm(mat_files, descProcessing files): file_path os.path.join(data_dir, file) data sio.loadmat(file_path) # 处理每个试次 for trial in range(1, 16): key fdjc_eeg{trial} raw create_mne_raw(data[key]) all_raws.append(raw) all_labels.append(basic_labels[trial-1]) return all_raws, all_labels3.2 数据标准化与增强原始数据已经过200Hz下采样和0-75Hz带通滤波但我们还可以添加常用预处理步骤重参考平均参考陷波滤波去除工频干扰独立成分分析去除眼电等伪迹def apply_standard_preprocessing(raw): 应用标准预处理流程 # 1. 重参考 raw.set_eeg_reference(average) # 2. 陷波滤波 (50Hz工频) raw.notch_filter(50) # 3. 高通滤波 (1Hz去除慢波漂移) raw.filter(1, None) return raw4. 构建机器学习就绪数据集4.1 特征提取策略直接从原始时间序列训练模型通常效果不佳我们需要提取有意义的特征常用EEG特征类型特征类别具体方法维度时域特征均值、方差、Hjorth参数62×N频域特征波段功率(δ,θ,α,β,γ)62×5时频特征小波变换、STFT62×T×F功能连接PLV, Coherence62×62def extract_band_power(raw, bands): 提取各波段相对功率 from mne.time_frequency import psd_welch # 计算功率谱密度 psds, freqs psd_welch(raw, fmin1, fmax50, n_overlap250) # 计算各波段功率 band_power {} for band_name, (fmin, fmax) in bands.items(): band_idx (freqs fmin) (freqs fmax) band_power[band_name] psds[:, band_idx].mean(axis1) return band_power # 定义标准频段 EEG_BANDS { delta: (1, 4), theta: (4, 8), alpha: (8, 13), beta: (13, 30), gamma: (30, 50) }4.2 构建训练数据集将处理后的数据组织为机器学习友好的格式import numpy as np from sklearn.preprocessing import StandardScaler def create_feature_matrix(raws, labels): 创建特征矩阵和标签向量 X [] y [] for raw, label in zip(raws, labels): # 提取特征 (这里以频段功率为例) band_power extract_band_power(raw, EEG_BANDS) features np.concatenate([ band_power[theta], band_power[alpha], band_power[beta] ]) X.append(features) y.append(label) # 标准化特征 scaler StandardScaler() X_scaled scaler.fit_transform(X) return X_scaled, np.array(y)5. 实战技巧与性能优化5.1 内存高效处理策略处理完整SEED数据集(约5GB)时内存管理至关重要逐文件处理不一次性加载所有数据预分配数组避免频繁内存分配使用HDF5处理超大规模数据import h5py def process_large_dataset(data_dir, output_h5): 内存友好的大数据处理方案 with h5py.File(output_h5, w) as hf: mat_files [f for f in os.listdir(data_dir) if f.endswith(.mat)] # 预创建数据集 num_trials len(mat_files) * 15 hf.create_dataset(features, shape(num_trials, 62*3), dtypefloat32) hf.create_dataset(labels, shape(num_trials,), dtypeint8) idx 0 for file in tqdm(mat_files): data sio.loadmat(os.path.join(data_dir, file)) for trial in range(1, 16): raw create_mne_raw(data[fdjc_eeg{trial}]) band_power extract_band_power(raw, EEG_BANDS) # 存储特征 features np.concatenate([ band_power[theta], band_power[alpha], band_power[beta] ]) hf[features][idx] features hf[labels][idx] basic_labels[trial-1] idx 15.2 并行处理加速利用多核CPU加速数据处理from joblib import Parallel, delayed def parallel_process(data_dir, n_jobs4): 并行处理数据 mat_files [f for f in os.listdir(data_dir) if f.endswith(.mat)] def process_file(file): file_path os.path.join(data_dir, file) data sio.loadmat(file_path) file_features [] file_labels [] for trial in range(1, 16): raw create_mne_raw(data[fdjc_eeg{trial}]) band_power extract_band_power(raw, EEG_BANDS) features np.concatenate([ band_power[theta], band_power[alpha], band_power[beta] ]) file_features.append(features) file_labels.append(basic_labels[trial-1]) return file_features, file_labels results Parallel(n_jobsn_jobs)( delayed(process_file)(file) for file in mat_files ) # 合并结果 all_features np.vstack([res[0] for res in results]) all_labels np.concatenate([res[1] for res in results]) return all_features, all_labels6. 质量检查与常见问题排查处理EEG数据时质量检查至关重要。以下是几个关键检查点数据质量检查清单通道一致性确认所有文件通道顺序相同采样率验证检查实际采样率是否符合声明值标签对齐确保数据与标签正确对应异常值检测识别可能损坏的试次def validate_data(raw, expected_channels62, expected_sfreq200): 验证数据基本完整性 assert len(raw.ch_names) expected_channels, 通道数量不符 assert raw.info[sfreq] expected_sfreq, 采样率不符 # 检查数据范围 (单位: μV) data raw.get_data() assert np.max(np.abs(data)) 500, 存在异常幅值 # 检查NaN值 assert not np.isnan(data).any(), 存在NaN值提示在实际项目中建议将约10%的数据保留作为测试集用于最终验证数据处理管道的正确性。