2023/1/4 -1/5脑机接口学习内容一览:
这一篇博客里,主要研究脑电信号是如何与机器学习算法结合来完成特征提取并且进行分类的。如果你是脑机接口的初学者,这一篇文章可能对你有一些作用。这项工作主要基于脑机接口社区的文章机器学习算法随机森林判断睡眠类型,在上个星期的学习中,对这一篇文章有了一定程度的理解,但是对其机器学习的部分还未能深入。
def eeg_power_band(epochs):
"""脑电相对功率带特征提取
该函数接受一个""mne.Epochs"对象,
并基于与scikit-learn兼容的特定频带中的相对功率创建EEG特征。
Parameters
----------
epochs : Epochs
The data.
Returns
-------
X : numpy array of shape [n_samples, 5]
Transformed data.
"""
# 特定频带
FREQ_BANDS = {"delta": [0.5, 4.5],
"theta": [4.5, 8.5],
"alpha": [8.5, 11.5],
"sigma": [11.5, 15.5],
"beta": [15.5, 30]}
spectrum = epochs.compute_psd(picks='eeg', fmin=0.5, fmax=30.)
psds, freqs = spectrum.get_data(return_freqs=True)
# 归一化 PSDs
psds /= np.sum(psds, axis=-1, keepdims=True)
X = []
for fmin, fmax in FREQ_BANDS.values():
psds_band = psds[:, :, (freqs >= fmin) & (freqs < fmax)].mean(axis=-1)
X.append(psds_band.reshape(len(psds), -1))
return np.concatenate(X, axis=1)
在这一个函数中,我们主要提取了我们感兴趣的五个频段进行特征提取。接下来我们可以看看这个函数中的一些数据的格式(取其中一项):
this is psds
(2650, 2, 75)
[[[4.62346776e-10 2.26477021e-10 1.94829184e-10 ... 1.00128882e-12
1.10880017e-12 8.26342701e-13]
[1.60227766e-11 8.88329315e-12 4.34734954e-12 ... 1.66580235e-13
1.29914480e-13 6.72933061e-14]]...
[[1.52786136e-10 1.62486911e-10 5.61825255e-11 ... 7.82815711e-13
9.65785851e-13 1.00429737e-12]
[1.26803286e-11 1.83223389e-11 6.63349727e-12 ... 3.06990667e-13
4.93566157e-13 3.79406551e-13]]]this is freqs
(75,)
[ 0.78125 1.171875 1.5625 1.953125 2.34375 2.734375 3.125
3.515625 3.90625 4.296875 4.6875 5.078125 5.46875 5.859375
6.25 ...... 24.21875 24.609375 25.25.390625 25.78125 26.171875 26.5625 26.953125 27.34375 27.734375
28.125 28.515625 28.90625 29.296875 29.6875 ]this is psds_band
(2802, 2)
[[1.20172913e-04 4.41583058e-03]
[8.45255045e-05 2.84102561e-03]
[1.23531186e-04 2.70494384e-03]
...
[1.36989030e-04 1.11141585e-02]
[2.94381867e-04 1.10942809e-02]
[1.61243814e-04 1.07584470e-02]]
this is X
[array([[0.09312066, 0.05313309],
[0.09627721, 0.06235951],
[0.09399389, 0.07056996],
...,
[0.09390306, 0.01738198],
[0.08967367, 0.01784981],
[0.08940769, 0.01977563]]), array([[0.00542081, 0.01795872],
[0.00278196, 0.01454787],
[0.0046737 , 0.01044173],
...,
[1.36989030e-04, 1.11141585e-02],
[2.94381867e-04, 1.10942809e-02],
[1.61243814e-04, 1.07584470e-02]])]
for fmin, fmax in FREQ_BANDS.values():
psds_band = psds[:, :, (freqs >= fmin) & (freqs < fmax)].mean(axis=-1)
X.append(psds_band.reshape(len(psds), -1))
在这个循环中psds_band是在最后一个维度比较每一个频率点的频率,并且求取位于特定频段内的频率点的均值,作为频段的特征。在求取均值过后原来(2650, 2, 75)的psds转化成(2802, 2)的psds_band,然后将psds_band加入X列表中作为其中的一个元素。
这里稍微说明一下,循环每次计算为两个通道各自提取一个单独频段的特征,但是因为通道不同,所以每组的两个特征虽然由相同的频段平均化得出,但是为了便于理解可以看作是两个不同类的特征。举个例子,我们可以将它们看作a1,a2两个特征,字面上看上去有关联,但是实际上不是同一个。
return np.concatenate(X, axis=1)
该函数最后返回一个shape为(2802, 10)格式的数组,10为5*2得到,一共包括2802个数据,每一个数据具有十个频率特征(此返回为测试集格式)。
pipe = make_pipeline(FunctionTransformer(eeg_power_band, validate=False),
RandomForestClassifier(n_estimators=100, random_state=42))
# 训练
y_train = epochs_train.events[:, 2]
pipe.fit(epochs_train, y_train)
# 预测
y_pred = pipe.predict(epochs_test)
# 评估准确率
y_test = epochs_test.events[:, 2]
acc = accuracy_score(y_test, y_pred)
print("Accuracy score: {}".format(acc))
这一块是教程最后的机器学习部分。我们现在来解释一下pipeline中的部分:
eeg_power_band:
即上文我们研究的函数,输入一个epochs,输出一个所要求的数组
validate:
bool量,default=False,输入验证关闭
RandomForestClassifier:
使用随机森林进行预测,第一个参数为决策树的数量,第二个参数为种子
fit:第一个参数为传入数据,第二个为数据的标签即个数据所属类别。这个函数主要传入训练集来训练模型。
predict:转换数据类型,应用最后的估计函数完成预测。调用pipeline中的每一个转换器。最后得到的数据会输入最后一个估计函数中,predict只有当最后的估计函数应用时有效。这个函数主要传入测试集,返回的是每个测试点的分类结果,这里我们的返回格式是(2802,),2802即测试点数量。
classification_report(y_test, y_pred, target_names=event_id.keys())
最后一句生成分类报告的函数貌似是一个很有趣的功能,具体可见参考资料3。
参考资料:
随机森林算法(Random Forest)Python实现
sklearn.metrics中的评价方法 precision_score,recall_score,F分数(f1_score,f_beta)和accuracy_score
python机器学习classification_report()函数 输出模型评估报告