在本系列教程中,我们将使用python mne + pyTorch进行基本的EEG分析。案例研究将在脑电情绪识别的基准数据集DEAP上进行。在第1部分中,我们将重点关注数据集。
本教程假设:
1、您已经对Python有了基本的了解 2、你有一些scikit-learn的经验,也有一些机器学习的知识 3、你有一些使用pyTorch的经验,也有一些关于深度学习的知识
在这个数据集中,共有32个参与者,每个参与者观看40个1分钟的视频。因此s01.dat 持有40批。总样品40*32=1280批。 查看每个dat文件(例如,s01),它包含数据和标签。
- 数据---- 40 x 40 x 8064[视频/批次x通道x样品]
- 标签---- 40x4
在40个通道中,32个通道是EEG,其余8个通道来自其他传感器,如EOG(见原文6.1节)。我们将只提取前32个通道。对于8064,由于数据被降采样到128Hz,因此一秒大约包含128个样本,因此一分钟也就是60秒,大约包含7680个样本。这篇论文并没有讲太多,但很可能在前后还有1.5秒,总共8064个样本(128 Hz * 63秒)。
这四个标签对应valence, arousal, liking, and dominance,按这个顺序排列。我们将只使用valence and arousal,因此将提取索引0和1的标签。
import torch
import os
import pickle
import numpy as np
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("Configured device: ", device)
#加载数据
class Dataset(torch.utils.data.Dataset):
def __init__(self, path, stim):
_, _, filenames = next(os.walk(path))
filenames = sorted(filenames)
all_data = []
all_label = []
for dat in filenames:
temp = pickle.load(open(os.path.join(path,dat), 'rb'), encoding='latin1')
all_data.append(temp['data'])
if stim == "Valence":
all_label.append(temp['labels'][:,:1]) #the first index is valence
elif stim == "Arousal":
all_label.append(temp['labels'][:,1:2]) # Arousal #the second index is arousal
self.data = np.vstack(all_data) #shape: (1280, 40, 8064) ==> 1280 samples / 40 samples = 32 participants
self.label = np.vstack(all_label) #(1280, ) ==> 1280 samples, each with a unique label (depend on the param "stim")
del temp, all_data, all_label
def __len__(self):
return self.data.shape[0]
def __getitem__(self, idx):
single_data = self.data[idx]
single_label = (self.label[idx] > 5).astype(float) #convert the scale to either 0 or 1 (to classification problem)
batch = {
'data': torch.Tensor(single_data),
'label': torch.Tensor(single_label)
}
return batch
#create a folder "data", and inside put s01.dat,....,s32.dat inside from the preprocessed folder from the DEAP dataset
path = "data"
#我们可以尝试使用索引查看一个样本。这将自动映射到<code>__getitem__</code>函数在<code>Dataset</code>类。
dataset_valence = Dataset(path, "Valence")
dataset_arousal = Dataset(path, "Arousal")
dataset_valence[0]
print("Shape of data: ", dataset_valence[0]['data'].shape) #40 channels of data, 8064 samples in 1 minute
print("Shape of label: ", dataset_valence[0]['label'].shape) #just 1 single label; 0 or 1
#让我们看看我们的数据和标签分布。
data = dataset_valence[:]['data']
label = dataset_valence[:]['label']
#所以我们得到了1280次trial(40个视频* 32个参与者= 1280,每个视频有40个数据通道,每个视频包含8064个脑电图样本)
data.shape
#所以我们得到了1280个标签,也就是说,每个视频一个标签
label.shape
#让我们数一下valence dataset里有多少个0和1,看看是否存在不平衡。
cond_1 = label == 1
cond_0 = label == 0
print("Labels 1 in valence dataset: ", len(label[cond_1]))
print("Labels 0 in valence dataset: ", len(label[cond_0]))
#Labels 1 in valence dataset: 708
#Labels 0 in valence dataset: 572
#为了确认前32个通道为EEG,其余8个通道为其他通道,我们检查每个通道的中值,看是否有模式。
for i in range(40):
print(f"Median of {i} data: {torch.median(data[:, i, :])}")
#我们可以看到,0到31的数据索引是明显的EEG,而32之后的数据则不是。
# #总结
我们处理数据集的方式有三个重要的观察结果需要修正:
- 首先,确保我们只采集32通道的脑电图。当然,您也可以随意使用其他数据通道,但本教程主要关注的是EEG。
- 数据分割是创建更多数据段的过程。例如,在一个1分钟的视频中,我们可以分成12个5秒的片段,这样可以大大增加样本的数量,从而大大增加更好的预测的机会。步骤是:
- a.重塑使(1280,32 8064)变成(1280,32,672,12)
- b.然后置换(1280,32,672,12)为(1280,12,32,672)
- c.然后重塑至(1280* 12,32,672)
请注意,由于作者已经对数据进行了预处理,所以我们不需要再做任何事情,但我们很自然地就会进行预处理,例如min-max归一化、陷波滤波器、带通滤波器等。
|