Applying Machine Learning Methods to EEG Data on Group Level
Single-Participant Analysis라는 자습서에서 동일한 분류 작업을 수행 하지만 이번에는 참가자 그룹의 EEG 데이터를 고려하여 그룹 수준에서 분석이 수행된다.
Dataset
단일 참가자 분석에서는 '감정-선행 평가 검사 : 참신함과 쾌적함을 위한 EEG 및 EMG 데이터 세트'의 유일한 참가자 데이터가 사용되었다.
|
Part - 1
def warn(*args, **kwargs):
pass
import warnings
warnings.warn = warn
import mne
from os.path import isfile, join
from os import listdir
import numpy as np
import statistics
from mne.decoding import Vectorizer
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score, train_test_split, GridSearchCV, StratifiedKFold, cross_val_predict
from sklearn.metrics import precision_recall_fscore_support, accuracy_score
from scipy import stats
# Models
from sklearn import svm
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.linear_model import LogisticRegression
#Loading dataset
data_folder = '../../study1/study1_eeg/epochdata/'
files = [data_folder+f for f in listdir(data_folder) if isfile(join(data_folder, f)) and '.DS_Store' not in f]
#extract ids from file names
ids = [int(f[len(data_folder)+2:-4]) for f in files]
epochs = [mne.read_epochs(f, verbose='error') for f in files]
epochs_UN = [e['FU', 'FN'] for e in epochs]
epochs_UP = [e['FU', 'FP'] for e in epochs]
epochs_NP = [e['FN', 'FP'] for e in epochs]
# Dataset with unpleasant and neutral events
data_UN = [e.get_data() for e in epochs_UN]
labels_UN = [e.events[:,-1] for e in epochs_UN]
data_UP = [e.get_data() for e in epochs_UP]
labels_UP = [e.events[:,-1] for e in epochs_UP]
data_NP = [e.get_data() for e in epochs_NP]
labels_NP = [e.events[:,-1] for e in epochs_NP]
Example #1 : Classification between Unpleasant and Pleasant Events
성능을 비교하기 위해 모든 분류자에 대한 파이프라인을 만들고 모든 분류자에 대해 교차 검증을 실행한다.
def applyCrossValidation(models, model_names, data, labels, kfold):
results = []
if np.all(np.isfinite(data)) == True and np.any(np.isnan(data)) == False:
for i in range(len(models)):
#print(model_names[i])
cv_accuracy = cross_val_score(models[i], data, labels, cv=kfold)
results.append(cv_accuracy)
#print('CV accuracy of model ' + model_names[i] + ': ' + str(cv_accuracy))
else:
print('Data has infinite or NaN value!')
return results
results_perParticipant_UP = []
model_names = [ 'LR', 'LDA']
kfold = StratifiedKFold(n_splits=3, random_state=42)
for i in range(len(ids)):
# Linear Discriminant Analysis
clf_lda_pip = make_pipeline(Vectorizer(), StandardScaler(), LinearDiscriminantAnalysis(solver='svd'))
#Logistic Regression
clf_lr_pip = make_pipeline(Vectorizer(), StandardScaler(), LogisticRegression(penalty='l1', random_state=42))
models = [ clf_lr_pip, clf_lda_pip]
scores = applyCrossValidation(models, model_names, data_UP[i], labels_UP[i], kfold)
results_perParticipant_UP.append(scores)
다음으로 다음 함수를 통해 각 모델에 대한 교차 검증 점수의 평균을 계산한다.
def calculateAvgResults(model_names, results_perParticipant):
avg_results_perParticipant = []
for i in range(len(ids)):
score = []
for j in range(len(model_names)):
score.append(statistics.mean(results_perParticipant[i][j]))
avg_results_perParticipant.append(score)
return avg_results_perParticipant
avg_results_perParticipant_UP = calculateAvgResults(model_names, results_perParticipant_UP)
다음 함수 플롯은 각 참여자에 대한 모델의 교차 검증 점수를 의미한다. 분류자의 성과는 참가자에 따라 다르다는 것을 관찰할 수 있다.
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plotCVScores_perParticipant(ids, results, model_names):
# Fonts for the axis and title
title_font = {'fontname':'Arial', 'size':'24', 'color':'black', 'weight':'normal'}
axis_font = {'fontname':'Arial', 'size':'22'}
width = 0.2 # the width of the bars
#participants = ['P-'+str(i) for i in unique_ids]
fig = plt.figure(num=None, figsize=(30, 10), dpi=150)
# Set position of bar on X axis
rects1 = np.arange(len(ids))
rects2 = [x + width for x in rects1]
plt.bar(rects1, list(zip(*results))[0], color='#87CEFA', width=width, edgecolor='white', label=model_names[0])
plt.bar(rects2, list(zip(*results))[1], color='#FFE4E1', width=width, edgecolor='white', label=model_names[1])
plt.xticks([r + width/2 for r in range(len(ids))], ids)
plt.xlabel('Participant IDs', **axis_font)
plt.ylabel('Accuracy', **axis_font)
plt.title('CV Accuracy Scores per Participant', **title_font)
plt.legend(bbox_to_anchor=(1.01, 1), loc='upper left', prop={'size': 20})
plt.show()
#sort ids results together to plot nicely.
ids_sorted, avg_results_perParticipant_UP = zip(*sorted(zip(ids, avg_results_perParticipant_UP)))
plotCVScores_perParticipant(ids_sorted, avg_results_perParticipant_UP, model_names)
마지막 단계로 통계 테스트를 적용하여 모델을 비교한다. 기계 학습에서 통계 테스트를 사용하여 두 분류기의 성능 간에 유의한 차이가 있는지 여부를 평가할 수 있다. paired non-parametric test인 Wilcoxon test를 선택한다.
각 분류기를 구축하고 테스트하는 데 동일한 데이터가 사용되기 때문에 Wilcoxon과 같은 쌍 테스트가 우리의 경우 적용 가능하다. 또한, 비교할 분포에 대한 가정을 고려해야 한다. 가정이 없기 때문에 테스트 옵션을 비모수 테스트로 좁힙니다. 어떤 분포가 관찰될 것인지 가정할 수 있는 경우 모수 검정을 선택할 수 있다.
통계적 검정을 결정한 후에는 유의성 비교를 위한 기준점을 결정해야 한다. 통계 테스트가 p < 0.05의 p-값을 제공하는 경우 크게 다른 분류 성능으로 간주한다.
import scipy.stats as stats
def applyStatisticalTest(results, model_names):
if len(results) < 2:
print('Not enough values for statistical test!')
else:
results = list(zip(*results))
for i in range(len(results)):
for j in range(i+1,len(results)):
t, p = stats.wilcoxon(results[i],results[j])
print("p = {0} for Wilcoxon test between {1} and {2}".format(p, model_names[i], model_names[j]))
applyStatisticalTest(avg_results_perParticipant_UP, model_names)
p = 4.072914334952533e-05 for Wilcoxon test between LR and LDA
유도된 p-값이 0.05보다 작기 때문에 불쾌한 사건과 즐거운 사건을 분류하는 작업에서 LDA의 성능과 LR의 성능 사이에 상당한 차이가 있으며 이 작업에서 로지스틱 회귀가 더 잘 수행된다는 결론을 내릴 수 있다.
'Brain Engineering > MNE' 카테고리의 다른 글
Machine Learning : Group-Level Analysis (Part - 2) (3) (0) | 2022.04.05 |
---|---|
Machine Learning : Group-Level Analysis (Part - 1) (2) (0) | 2022.04.05 |
Machine Learning : Single-Participant Analysis (2) (0) | 2022.04.05 |
Machine Learning : Single-Participant Analysis (1) (0) | 2022.04.05 |
Machine Learning : Formatting a dataset (0) | 2022.04.05 |