脑机接口(BCI)研究中,EEG信号就像大脑活动的"心电图"。想象一下,当我们思考或运动时,大脑会产生微弱的电信号,这些信号被头皮上的电极捕捉后,就形成了EEG数据。GDF格式是BCI竞赛中常见的原始数据容器,类似于一个特殊的"文件夹",里面存放着:
我第一次处理GDF文件时,发现它比常见的CSV复杂得多。官方提供的GDF文件通常包含25个通道,其中22个是EEG通道,3个是EOG(眼电)通道。这就好比你要分析一场音乐会,但录音里混入了观众的咳嗽声——必须先把干扰信号分离出去。
使用Python处理GDF文件,推荐使用pyEDFlib或MNE库。下面这段代码是我在项目中实际使用过的:
python复制import mne
raw = mne.io.read_raw_gdf('A01T.gdf', preload=True)
print(raw.info) # 查看数据基本信息
运行后会显示关键信息:
特别注意要检查通道类型是否正确标注。有次我发现Fz通道被错误标记为EOG,导致后续分析全部出错。可以通过以下代码修正:
python复制raw.set_channel_types({'Fz':'eeg'}) # 修正通道类型
就像拍照时要选对镜头一样,通道选择直接影响信号质量。标准流程:
python复制picks = mne.pick_types(raw.info, eeg=True, eog=False) # 选择EEG通道
raw.set_eeg_reference(ref_channels='average') # 平均参考
EEG信号就像收音机,需要调对频率才能听清内容。我常用这样的滤波组合:
python复制raw.filter(0.5, 40., fir_design='firwin') # 带通滤波
raw.notch_filter(50., fir_design='firwin') # 陷波滤波
特别注意滤波顺序——有次我先做陷波滤波导致信号畸变。建议顺序:带通→陷波。
运动想象范式通常分析2-6秒的数据段(对应提示出现后的想象阶段)。就像剪辑电影片段:
python复制events = mne.events_from_annotations(raw)[0] # 获取事件标记
epochs = mne.Epochs(raw, events, tmin=2, tmax=6,
baseline=(2, 2.5), preload=True) # 基线校正
基线校正就像给照片调亮度,通常用提示出现后0.5秒的数据作为基准。
不同归一化方法效果差异明显。经过多次实验,我发现:
python复制from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data = epochs.get_data() # 获取三维数据(试次×通道×时间)
data = scaler.fit_transform(data.reshape(-1, data.shape[-1])).reshape(data.shape)
深度学习模型需要特定输入维度。以EEGNet为例:
python复制import torch
data = torch.from_numpy(data)
data = data.unsqueeze(1) # 添加通道维度
print(data.shape) # 输出torch.Size([288, 1, 22, 1000])
四分类任务的标签处理要特别注意:
python复制labels = events[:, -1] - 1 # 假设原始标签是1-4
labels = torch.LongTensor(labels)
推荐使用HDF5或PyTorch格式保存预处理数据:
python复制torch.save({'data': data, 'labels': labels}, 'preprocessed_data.pt')
这样下次加载只需一行代码:
python复制dataset = torch.load('preprocessed_data.pt')
在实际项目中我遇到过这些"坑":
一个实用的检查函数:
python复制def check_data_quality(data):
print(f"最大值: {data.max():.2f}μV")
print(f"最小值: {data.min():.2f}μV")
print(f"NaN值数量: {np.isnan(data).sum()}")
结合所有步骤的完整流程:
python复制import mne
import torch
from sklearn.preprocessing import StandardScaler
# 1. 读取数据
raw = mne.io.read_raw_gdf('A01T.gdf', preload=True)
# 2. 通道处理
raw.pick_types(eeg=True)
raw.set_eeg_reference('average')
# 3. 滤波
raw.filter(0.5, 40., fir_design='firwin')
raw.notch_filter(50., fir_design='firwin')
# 4. 分段
events = mne.events_from_annotations(raw)[0]
epochs = mne.Epochs(raw, events, tmin=2, tmax=6, baseline=(2, 2.5))
# 5. 数据转换
data = epochs.get_data()
scaler = StandardScaler()
data = scaler.fit_transform(data.reshape(-1, data.shape[-1])).reshape(data.shape)
# 6. 维度调整
data = torch.from_numpy(data).unsqueeze(1)
labels = torch.LongTensor(events[:, -1] - 1)
# 7. 保存
torch.save({'data': data, 'labels': labels}, 'final_data.pt')
处理完的数据就可以直接输入EEGNet等模型进行训练了。记住,好的预处理相当于成功了一半——在我的项目中,优化预处理流程曾让模型准确率提升15%以上。