[1주 BCI 기초 다지기] 8일차: SVM, 로지스틱 회귀, EEG 데이터 분류 실습, 운동 상상 데이터 분석
8일차: 선형 분류 모델
1. SVM 개념
SVM이란?
SVM은 데이터를 두 클래스(또는 다중 클래스)로 구분하는 초평면(Hyperplane)을 찾는 알고리즘이다.
초평면은 두 클래스 간 거리를 최대화하는 방향으로 위치하며, 선형적으로 분리되지 않는 경우 커널(Kernel) 트릭을 사용하여 고차원 공간에서 선형 분리가 가능하게 한다.
SVM의 주요 개념
- 초평면(Hyperplane)
- 데이터를 분류하는 경계선(또는 경계면).
- 초평면의 방정식은 w⋅x+b=0
https://m.blog.naver.com/tommybee/221790549191
- 마진(Margin)
- 초평면과 가장 가까운 데이터 포인트(서포트 벡터) 간 거리.
- SVM은 이 마진을 최대화하여 분류 성능을 향상시킴.
https://www.ecopro.ai/entry/%EB%B9%85%EB%B6%84%EA%B8%B0-%EC%8A%A4%ED%84%B0%EB%94%94%EB%AA%A8%EC%9E%84-%EB%B0%9C%ED%91%9C%EC%A4%80%EB%B9%84-20240309-%EC%84%9C%ED%8F%AC%ED%8A%B8%EB%B2%A1%ED%84%B0%EB%A8%B8%EC%8B%A0SVM
- 서포트 벡터(Support Vector)
- 초평면 근처에 위치한 데이터 포인트로, 초평면을 정의하는 데 중요한 역할을 함.
- 커널(Kernel) 트릭
- 데이터를 고차원 공간으로 매핑하여 비선형 데이터를 선형 분리 가능하게 함.
- 예: 선형 커널, 다항식 커널, RBF(방사형 기저 함수) 커널.
SVM의 장단점
- 장점
- 고차원 데이터에서도 잘 작동.
- 커널을 사용해 비선형 데이터도 분류 가능.
- 과적합에 강함.
- 단점
- 큰 데이터셋에서는 느릴 수 있음.
- 다중 클래스 분류에서 직접적으로 확장되지 않아 별도의 기법이 필요함. (One-vs-One 또는 One-ve-Rest 등)
2. Logistic Regression 개념
Logistic Regression란?
Logistic Regression은 이름에 "Regression"이 들어가지만, **분류(Classification)**에 사용되는 알고리즘이다.
이진 분류(Binary Classification) 문제를 해결하는 데 적합하며, 데이터를 두 클래스 중 하나로 분류한다.
주요 개념
- 로지스틱 함수(Logistic Function): Logistic Regression은 로지스틱 함수(또는 시그모이드 함수)를 사용하여 데이터를 0~1 사이의 확률로 매핑.
- 결정 경계(Decision Boundary):
- 출력값(확률)이 0.5보다 크면 Class 1, 작으면 Class 0으로 분류.
- 결정 경계는 선형 함수로 나타남.
Logistic Regression의 손실 함수
Logistic Regression은 크로스 엔트로피 손실(Cross-Entropy Loss)을 최소화하도록 모델을 학습한다.
가중치 w와 절편 b를 초기화하고, 경사 하강법을 사용하여 손실 함수를 최소화하며 최적의 w와 b를 찾음으로써 데이터를 분류한다.
Logistic Regression의 장단점
- 장점:
- 구현이 간단하고 해석 가능.
- 선형 분리 가능 데이터에서 잘 작동.
- 확률 값 제공(결과 해석에 유용).
- 단점:
- 비선형 데이터에 한계(비선형 커널 사용 불가).
- 고차원 데이터에서는 성능 저하.
3. SVM vs Logistic Regression
특성 | SVM | Logistic Regression |
분류 방식 | 초평면(마진 극대화) 기반 | 확률(로지스틱 함수) 기반 |
비선형 데이터 처리 | 커널 트릭으로 가능 | 비선형 데이터 처리 제한 |
다중 클래스 확장 | One-vs-One, One-vs-Rest 필요 | 직접 다중 클래스 확장 가능 |
계산 복잡도 | 더 복잡함 | 상대적으로 간단함 |
4. MATLAB에서의 실습
EEG 데이터 준비 및 전처리
오늘의 실습에서는 “GigaDB(https://gigadb.org/dataset/view/id/100542/File_page/15)의 sess01_subj36_EEG_MI.mat”를 사용하고자 한다. 해당 데이터는 오른손/왼손을 움켜쥐는 상상을 했을 때의 뇌파를 포함한다.
6일차 실습과 동일하게 아래의 코드를 통해 데이터를 불러온 후 확인해 보면, train과 test 데이터가 포함된 것을 알 수 있다.
load('sess01_subj36_EEG_MI.mat');
이 중 train 구조체 필드를 확인해 보면, 아래와 같이 이전과 동일한 변수들이 포함된 것을 확인할 수 있다.
이 중 y_dec, y_logic, y_class는 동일한 정보를 다른 형식으로 표현한 것인데, 오늘 실습에서는 y_dec을 사용할 것이다. class에 나와있는 대로, 1이 오른손 움직임 상상, 2가 왼손 움직임 상상을 의미한다.
EEG 데이터를 준비한 후, y_dec를 사용해 데이터를 class별로 나누어 저장해 보자.
% EEG 데이터 전처리 (CAR 처리)
EEG = EEG_MI_train.x'; % EEG 데이터 (채널 x 시간)
EEG = EEG - repmat(mean(EEG, 1), size(EEG, 1), 1); % 공통 평균 참조(CAR)
% 이벤트 시간 추출
events{1} = EEG_MI_train.t(EEG_MI_train.y_dec == 1); % 오른손 상상
events{2} = EEG_MI_train.t(EEG_MI_train.y_dec == 2); % 왼손 상상
이렇게 y_dec를 사용해 데이터를 클래스별로 나누고 나면, 각 이벤트를 기준으로 에포킹(Epoching)을 진행해야 한다. 에포킹은 이벤트 발생 시점을 기준으로 일정 시간 전후의 데이터를 추출하는 과정으로, 오늘 실습에서는 -1초부터 +4초까지의 데이터를 추출하여 뇌파 신호를 분석 가능하도록 전처리할 것이다.
에포킹은 아래의 코드로 진행할 수 있다.
% 에포킹 (Window: -1초 ~ +4초)
sf = EEG_MI_train.fs; % 샘플링 주파수
wnd_size = [-1 4]; % 윈도우 크기 (초)
for i = 1:length(events)
for tr = 1:length(events{i})
start_idx = round(events{i}(tr) + wnd_size(1)*sf);
end_idx = round(events{i}(tr) + wnd_size(2)*sf);
e_EEG{class_idx}(:, :, trial_idx) = EEG_data(:, start_idx:end_idx);
end
end
다음으로, 지금까지 배운 Band-pass Filtering 및 파워 계산을 진행해 보자.
% 주파수 대역 및 시간 설정
bpf = [8 26]; % 주파수 대역 (8~26 Hz)
dur = ([0 4] - wnd_size(1)) * sf; % 특징 추출 시간 (0~4초)
% 필터링 및 신호 파워 계산
for i = 1:length(events)
for ch = 1:size(e_EEG{i}, 1)
for tr = 1:size(e_EEG{i}, 3)
fil_EEG{i}(ch, :, tr) = bandpass(e_EEG{i}(ch, dur(1):dur(2), tr), bpf, sf); % Band-pass Filtering
P{i}(ch, tr) = mean(fil_EEG{i}(ch, :, tr).^2); % Power 계산
end
end
end
% 데이터를 하나의 행렬로 결합 (X: 데이터, Y: 레이블)
X_train = [P{1}'; P{2}']; % 오른손과 왼손 데이터를 결합
Y_train = [zeros(size(P{1}, 2), 1); ones(size(P{2}, 2), 1)]; % 레이블 생성 (0: 오른손, 1: 왼손)
이제 생성한 train 데이터로 SVM 모델을 학습시키고, test 데이터를 생성 후 예측해 볼 것이다.
% SVM 모델 학습
SVMModel = fitcsvm(X_train, Y_train, 'KernelScale', 'auto', 'Standardize', true);
test 데이터도 동일하게 전처리 후 예측하면 끝이다.
% 테스트 데이터 준비 및 전처리
EEG_test = EEG_MI_test.x'; % 테스트 EEG 데이터
EEG_test = EEG_test - repmat(mean(EEG_test, 1), size(EEG_test, 1), 1); % CAR 처리
% 테스트 이벤트 추출 및 에포킹
events_test{1} = EEG_MI_test.t(EEG_MI_test.y_dec == 1); % 오른손
events_test{2} = EEG_MI_test.t(EEG_MI_test.y_dec == 2); % 왼손
for i = 1:length(events_test)
for tr = 1:length(events_test{i})
e_EEG_test{i}(:, :, tr) = EEG_test(:, round(events_test{i}(tr) + wnd_size(1)*sf):round(events_test{i}(tr) + wnd_size(2)*sf));
end
end
% 테스트 데이터 필터링 및 파워 계산
for i = 1:length(events_test)
for ch = 1:size(e_EEG_test{i}, 1)
for tr = 1:size(e_EEG_test{i}, 3)
fil_EEG_test{i}(ch, :, tr) = bandpass(e_EEG_test{i}(ch, dur(1):dur(2), tr), bpf, sf); % Band-pass Filtering
P_test{i}(ch, tr) = mean(fil_EEG_test{i}(ch, :, tr).^2); % Power 계산
end
end
end
% 테스트 데이터를 하나의 행렬로 결합
X_test = [P_test{1}'; P_test{2}'];
Y_test = [zeros(size(P_test{1}, 2), 1); ones(size(P_test{2}, 2), 1)];
% SVM 모델을 사용한 예측
Y_pred = predict(SVMModel, X_test);
% 정확도 계산
accuracy = mean(Y_pred == Y_test) * 100;
disp(['SVM 분류 정확도: ', num2str(accuracy), '%']);
Logistic Regression 모델의 경우 이미 데이터셋을 준비했으니 바로 학습할 수 있다.
% Logistic Regression 모델 학습
LogisticModel = fitclinear(X_train, Y_train, 'Learner', 'logistic');
% Logistic Regression 모델을 사용한 예측
Y_pred_log = predict(LogisticModel, X_test);
% 정확도 계산
accuracy_log = mean(Y_pred_log == Y_test) * 100;
disp(['Logistic Regression 분류 정확도: ', num2str(accuracy_log), '%']);