BCI/구글링

[MATLAB] Common Spatial Pattern, CSP 알고리즘이란?

_\oyo/_ 2023. 8. 31. 21:43

 

 

데이터 분석 시 CSP를 사용한다는 내용을 강의에서 들었는데, CSP가 무엇인지 검색해보니 클라우드 서비스 제공업체만 나와서 나름대로 찾아본 결과를 정리합니다. CSP에는 다양한 알고리즘이 있으며, 이 글은 단순한 하나의 알고리즘에 대한 설명이라는 점을 기억해 주시길 바랍니다. 또한, 공부하는 과정에서 기록한 것이기에 오개념이 있을 수도 있습니다.

 

01. CSP의 기본 개념

1. CSP(Common Spatial Pattern)란?

공통 공간 패턴, 즉 CSP는 뇌파 신호나 다른 공간적 신호를 분석할 때 사용하는 기법이다. CSP의 목적은 다변량 신호에서 두 개 이상의 클래스에 대한 공간 필터를 찾아 신호의 분산을 최대화하는 것이다.
여기에서 다변량 신호란 여러 채널을 통해 측정된 신호를 의미하며, 클래스란 서로 다른 상태나 조건을 의미한다. 예를 들어, 여러 전극을 사용하여 측정한 뇌파는 다변량 신호를 형성하고, Motor Imagery BCI에서 '왼손 움직임 상상'과 '오른손 움직임 상상'은 서로 다른 클래스가 될 수 있다.
쉽게 말하여, CSP는 복잡한 신호를 여러 부분으로 나누어서 각 클래스가 가지고 있는 특징의 차이를 더 잘 드러낼 수 있도록 하는 방법이다.

2.  PCA(Principal Component Analysis)와의 차이

위의 설명만 읽었을 때는  주성분 분석(PCA)과 별반 다를 거 없어 보인다. PCA와 CSP 모두 다변량 데이터에서 패턴을 찾아내는 데 사용되는 건 맞지만, 분명 다른 개념이다. 각각의 개념을 정리해 보면 다음과 같다.

  • PCA는 데이터의 분산을 최대화하는 주성분을 찾아, 데이터의 차원을 축소하는 것이 목적이다.
    그 결과, 데이터의 주요 특성을 설명하는 성분들이 추출된다.
  • CSP는 두 개 이상의 클래스 사이에서 신호 분산의 차이를 최대화하여 클래스를 구별하는 것이 목적이다.
    그 결과, 두 클래스의 신호를 잘 구별할 수 있는 새로운 공간 필터가 생성된다.

즉,  PCA는 전체 데이터셋의 주요 특성을 추출하는 데 초점을 맞춘 반면, CSP는 특정 클래스 간의 차이를 최대화하는 데 초점을 맞춘다.
조금 더 예시를 들면, 뇌파 데이터와 같이 노이즈가 많거나, 차원이 너무 높은 경우에는 PCA를 사용하여 주요 성분만 남긴 뒤, 그 다음에 CSP를 적용하기도 한다.

3. CSP의 장단점

개인적으로 느낀 CSP의 장단점은 다음과 같다. 
장점

  • 클래스 간 구별에 용이
    : CSP는 두 개 이상의 클래스 간 신호 분산의 차이를 최대화하여, 클래스를 구별하는 데 뛰어난 성능을 보인다.
  • 특징 추출 가능
    : 다변량 시계열 데이터에서도 중요한 공간적 특징을 추출할 수 있어, 신호 분석의 정확도를 높인다.

단점

  • 전처리에 민감
    : 신호의 품질이 낮거나 노이즈가 많은 경우, 성능이 크게 저하된다.
  • 데이터에 따라 다른 성능
    : 데이터의 분포와 특성에 따라 성능이 크게 달라질 수 있다.

 

02. CSP의 수학적 기초

다시 정리하여, CSP는 (주로)두 개의 다변량 신호 사이의 분산을 최대화하여 클래스를 분류한다. 이는 두 공분산 행렬을 동시에 대각화하여 해결되며, 이러한 대각화는 두 행렬의 고유값 분해로 해결할 수 있다.
먼저, m개의 채널에 대해 $n$개의 샘플을 얻어, $n*m$의 행렬 데이터를 얻게 되었다고 하자. (이 행렬은 각 실험의 횟수만큼 존재할 것이다.)

1. 공분산 행렬(Covariance Matrix) 계산

공분산 행렬은 데이터 세트 내의 두 변수 간의 관계를 나타내는 행렬로, 공분산을 원소로 한다. 이는 변수 사이의 변동성을 측정하여, 서로 얼마나 연관되어 있는지를 보여준다.
한 클래스의 데이터 행렬 $X$에 대한 공분산 행렬 $C$는 다음과 같이 계산한다.
$$C = \frac{1}{n} (X-\bar{X})^T(X-\bar{X})$$
데이터의 평균을 빼주는 이유는, 데이터를 중심화(centralize)하기 위함이다. 중심화는 모든 데이터 포인트에서 평균을 빼주어 데이터의중심을 원점으로 옮기는 과정을 말한다. 그렇기에 데이터의 평균을 빼지 않고 공분산을 계산하게 되면, 실제로는 두 변수 간의 상관관계를 나타내는 분산이 아닌, 단순히 두 변수 값을 곱한 것의 평균을 계산하게 된다. 그렇기에 데이터의 평균을 빼고 계산해야 한다.
이 공분산 행렬 $C$는 $m*m$크기로 나타낼 수 있으며, 각 시행마다 하나의 공분산 행렬이 계산된다.
이때의 공분산 행렬들은 신호 내 채널 간의 상관관계를 나타낸다. 그렇기에 합성 공분산 행렬을 통해 클래스 간의 공간적 패턴의 차이를 구한다.
$$C_{\text{sum}} = C_1 + C_2$$

2. 고유값 분해(Eigenvalue Decomposition)

이제 이 합성 공분산 행렬을 고유값 분해해야 한다. 고유값 분해는 정방행렬을 고유 벡터와 고유값으로 분해하는 과정으로, 공분산 행렬의 주요한 변동 방향을 찾아내어 클래스 간의 차이를 나타내는 가장 중요한 공간적 패턴을 추출할 수 있다.
고유값 분해는 $A \mathbf{v} = \lambda \mathbf{v}$와 같은 형태로 나타낼 수 있다.
여기서 $A$는 정방행렬, $\mathbf{v}$는 $A$의 고유벡터, $\lambda$는 해당 고유벡터에 대응하는 고유값을 의미한다.
이를 CSP에 적용하여 합성 공분산 행렬을 고유값 분해하는 과정은 다음과 같이 표현할 수 있다.
$$C_{sum} \mathbf{v}_i = \lambda_i \mathbf{v}_i$$
여기서 $\mathbf{v}$는 $C_sum$의 $i$번째 고유벡터이고, $\lambda_i$는 해당 고유벡터에 대응하는 고유값이다. 이때 모든 고유벡터들을 열벡터로 하는 행렬을 $P$, 모든 고유값을 대각선 원소로 하는 대각 행렬을 $\Lambda$라고 한다.
$$P = [\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_m]$$
$$\Lambda = \text{diag}(\lambda_1, \lambda_2, \ldots, \lambda_m)$$

3. 백색화 변환 (Whitening)

백색화 변환은 데이터의 상관관계나 변동을 제거하여 서로 독립적이고 동일한 분산을 갖도록 하는 변환이다. 우리는 각 행렬을 나타내는 고유 특성을 구하길 원하므로, 백색화를 통해 클래스 간의 차이를 극대화시킨다. 즉, 공분산 행렬들을 독립적이고 동일한 분산을 갖도록 변환하여 데이터 내의 상관 관계를 제거하게 되며, 각 특성의 분산을 1로 만들어 그 특징을 더 잘 드러내게 해준다.
$$P_{\text{white}} =  \Lambda^{-\frac{1}{2}}P^T$$

이 변환 행렬을 사용하여 데이터를 변환하면, 변환된 데이터의 공분산 행렬은 단위 행렬이 된다. 즉, 모든 변수가 서로 독립적이며, 분산이 1이 된다.
다음으로 각 클래스의 공분산 행렬을 백색화 변환한다.
$$S = P_{\text{white}}^T C P_{\text{white}}$$
즉, 왼손 움직임과 오른손 움직임 구별 등 클래스의 수가 2인 상황을 가정한다면 각 클래스에 대해 백색화된 공분산 행렬은 다음과 같다.
$$S_1 = P_{\text{white}}^T C_1 P_{\text{white}}, \quad S_2 = P_{\text{white}}^T C_2 P_{\text{white}}$$
이때 $S_1-S_2$(또는 $S_2-S_1$)을 계산하면, 두 클래스 간의 공간 패턴의 차이가 강조된다. 이 차이 행렬을 고유값 분해하면, 두 클래스 간의 차이를 가장 잘 표현하는 방향(고유벡터)와 그 차이의 크기(고유값)을 얻을 수 있다.
고유값이 큰 고유벡터는 한 클래스의 신호가 다른 클래스의 신호보다 더 강하게 나타나는 방향을 나타내며, 반대로 고유값이 작은 고유벡터는 다른 클래스의 신호가 더 강하게 나타나는 방향을 나타낸다.
예를 들어, $S_1-S_2$에서 고유값이 크다는 것은 $S_1$에서의 해당 방향의 분산이 $S_2$에서의 분산보다 훨씬 더 크다는 것을 의미한다. 즉, 첫 번째 클래스의 신호가 해당 방향에서 더 강하게 나타난다는 걸 의미한다.
이 행렬은 CSP 필터 계산에 사용되며, 데이터에서 클래스 간의 차이를 더 잘 구별할 수 있는 공간적 패턴을 추출할 수 있다.

4. CSP 필터 계산 및 적용

CSP 필터 $W$는 다음과 같이 계산된다.
$$W = P_{\text{white}} \cdot V$$
여기서 $P_{\text{white}}$는 이전 단계에서 계산한 백색화 변환 행렬이고, $V$는 $S_1 - S_2$의 고유벡터 행렬이다. 이 필터를 사용하면 원래 신호에서 두 클래스를 가장 잘 구분하는 공간적 특징을 추출할 수 있게 된다.
최종적으로 CSP 필터를 사용하여, 원래 신호에서 특징을 추출한다. 각 클래스의 데이터 행렬 $X_1$과 $X_2$에 CSP 필터 $W$를 적용하여 특징 행렬 $Z_1$과 $Z_2$를 계산한다.
$$Z_1 = X_1 \cdot W, \quad Z_2 = X_2 \cdot W$$
이렇게 CSP를 통해 두 클래스 간의 차이를 나타내는 공간적 특징을 추출하고, 이를 사용하여 더 정확하게 두 클래스를 구분할 수 있게 된다.
위의 과정을 간단하게 말하면, CSP의 개념은 다음과 같다.

하나의 데이터에 대해서는 분산이 최대, 다른 하나의 데이터에 대해서는 분산이 최소가 되는 벡터를 찾은 다음 원래의 데이터에 곱해주면 분류가 잘 되는 데이터가 된다.

 

03. MATLAB 구현

이제 임의의 두 클래스 데이터를 생성하여 그를 CSP 알고리즘으로 변환해 보자.

1. 임의의 데이터 생성

% 임의의 데이터 생성
rng(1); % 동일 결과를 위한 난수 발생 시드 설정
n_samples = 1000; % 샘플 수
n_channels = 8; % 채널 수

% 클래스 1의 데이터 생성
mu1 = rand(1, n_channels);
A1 = randn(n_channels);
sigma1 = A1' * A1; % 양의 정부호 행렬 생성
X1 = mvnrnd(mu1, sigma1, n_samples);

% 클래스 2의 데이터 생성
mu2 = rand(1, n_channels) + 1;
A2 = randn(n_channels);
sigma2 = A2' * A2; % 양의 정부호 행렬 생성
X2 = mvnrnd(mu2, sigma2, n_samples);

CSP는 주로 두 개 이상의 클래스 간 뇌 신호 변동성을 최대화하는 데 쓰이기에, CSP를 적용할 데이터를 다변량 정규 분포를 이용하여 생성했다. 이는 CSP의 특징을 보다 명확하게 이해하는 데 도움을 줄 것 같아서인데, 복잡도를 위해 8차원의 다변량 정규 분포 데이터를 생성했다. 이때 평균은 $mu$로, 공분산은 $sigma$로 표시했다.

2. CSP 필터 계산 및 적용

% 공분산 행렬 계산
C1 = cov(X1);
C2 = cov(X2);
C_sum = C1 + C2;

% 고유값 분해
[P, A] = eig(C_sum);

% 백색화 변환 행렬 계산
P_white = A^(-0.5) * P';

% 클래스의 공분산 행렬을 백색화 변환
S1 = P_white' * C1 * P_white;
S2 = P_white' * C2 * P_white;

% 두 클래스 간의 차이 행렬 계산 및 고유값 분해
[S_diff, V] = eig(S1 - S2);

% CSP 필터 계산
W = P_white * V;

% 원래 신호에 CSP 필터 적용
Z1 = X1 * W;
Z2 = X2 * W;

앞선 02 단락에서 사용한 수식을 그대로 적용하였다. 살짝 생소할 수도 있는 부분은 고유값 분해 부분인데, eig()는, matlab에서 지원하는 고유값 분해 함수이다. 직접 구현할 수도 있지만, 안 다룬 내용이기도 하고, 차원이 큰 데이터인 경우 구현이 번거롭기에 간단하게 계산이 가능한 내장함수를 사용하였다.

3. 결과 확인

생각보다 간단한 방법으로 원래의 신호에 CSP 필터를 적용하였다. 이제 이 변환된 신호를 원래의 신호와 비교하여 데이터가 잘 구별되는지 확인해 보자.

% 원래 데이터와 결과 데이터 플롯
figure;
subplot(2, 1, 1);
plot(X1(:, 1), 'b'); hold on;
plot(X2(:, 1), 'r');
title('Original Data (First Channel)');
legend('Class 1', 'Class 2');

subplot(2, 1, 2);
plot(Z1(:, 1), 'b'); hold on;
plot(Z2(:, 1), 'r');
title('CSP Transformed Data (First Component)');
legend('Class 1', 'Class 2');

$X1$과 $X2$가 8차원 데이터였기에, $Z1$과 $Z2$도 8차원 데이터이다. 위의 경우, 첫 번째 CSP 성분을 출력하여 비교하는 코드이다. 그 결과, 아래와 같이 CSP 변환 후에 두 클래스의 데이터가 더 분리된 것을 확인할 수 있다.

4. 결론

직접 CSP 알고리즘을 구현하고 적용해본 것이 처음이라, 맞게 분해가 된 것인지 확실치가 않다.

CSP 변환 후의 데이터에서도 여전히 두 클래스 간의 구별이 완전히 명확하지는 않지만, 보다 분리된 경향을 보인다. 또한, 전체적으로 클래스 1이 클래스 2에 비해 상대적으로 높은 값을 보이는 것을 확인할 수 있다. 이를 통해 어느 정도 분리가 되었다 생각할 수 있다.


기회가 된다면, 보다 명확히 구별할 수 있는 CSP 알고리즘을 찾아보고 다시 글을 올리고자 합니다. 읽어주셔서 감사합니다!

 

 

참고자료: 
https://en.wikipedia.org/wiki/Common_spatial_pattern
https://en.wikipedia.org/wiki/Covariance_matrix
https://en.wikipedia.org/wiki/Diagonalizable_matrix
https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix
 
https://youtube.com/playlist?list=PLiut5PtK_nRwWwZXBZB1GHzzPCI_M8LiK&feature=shared
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=timesea821&logNo=220963603607