본문 바로가기
Brain Engineering/MNE Python

Machine Learning : Group-Level Analysis (Part - 1) (1)

by goatlab 2022. 4. 5.
728x90
반응형
SMALL

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의 성능 사이에 상당한 차이가 있으며 이 작업에서 로지스틱 회귀가 더 잘 수행된다는 결론을 내릴 수 있다.

 

https://neuro.inf.unibe.ch/AlgorithmsNeuroscience/Tutorial_files/ApplyingMachineLearningMethods_2.html

 

Group-Level Analysis

Group-Level Analysis Tutorial #5: Applying Machine Learning Methods to EEG Data on Group Level In this tutorial, we are performing the same classi...

neuro.inf.unibe.ch

 

728x90
반응형
LIST