본문 바로가기

Data Analysis

기계학습 - Spark(8) - PCA 코드분석2

Spark, .../mlib/feature/PCA.scala 코드를 분석한 글이다. PCA의 원리에 대해 이해를 하기 위해 소스를 분석했는데 조금은 원리를 알게 되었지만, 수학으로 인한 장벽은 여전했다.


SPARK's PCA.sala

1. 'pc'로 명칭된 값과 Explained Variance을 구한다.

val mat = new RowMatrix(sources)     

val (pc, explainedVariance) = mat.computePrincipalComponentsAndExplainedVariance(k)


변수 pc : K값으로 보정된 SVD의 U행렬

변수 explainedVariance : Explained Variance


2. 입력된 벡터들을 차례대로 pc에 곱한 결과를 구한다.

pc.transpose.multiply(입력벡터)


변수명 pc로 정의된 값은 변환하고자 하는 차원값 K값이 반영되었기 때문에 입력된 벡터를 곱하면 원했던 차원의 변환된 벡터를 구할 수 있다.  여기서 사소한 배열 형식의 변경 코드등의 설명은 배제했다. 내용을 보면 아주 간단하다. 실재로는 PCA.scala에는 중요한 것은 없고, RowMatrix객체의 computePrincipalComponentsAndExplainedVariance함수를 호출하는 것이 전부이다.


RowMatix#computePrincipalComponentsAndExplainedVariance

// 1. 공분산(Covariance)를 구한다.    


val Cov = computeCovariance().asBreeze.asInstanceOf[BDM[Double]]


// 2. 공분산의 SVD의 u, s를 구한다. v는 처리안함.


val brzSvd.SVD(u: BDM[Double], s: BDV[Double], _) = brzSvd(Cov)


// 3. s 벡터의 각항목별 전체대비 비중을 구하고, 이를 explained variance로 한다.


val eigenSum = s.data.sum

val explainedVariance = s.data.map(_ / eigenSum)


// u벡터를 k열만큼 추린 배열과 explained variance를 반환한다.


if (k == n) {

      (Matrices.dense(n, k, u.data), Vectors.dense(explainedVariance))

} else {

      (Matrices.dense(n, k, Arrays.copyOfRange(u.data, 0, n * k)),

        Vectors.dense(Arrays.copyOfRange(explainedVariance, 0, k)))

}



RowMatrix 또한 breeze 라이브러리에서 제공하는 SVD를 구하는 함수를 호출하는 것이 거의 전부이다.

초보적인 실험

아주 초보적인 실험을 했다. 우선 2차원에 일렬로 늘어선 값들을 1차원으로 변환해봤다. 
(1, 1), (2, 2), (3, 3)일 경우
===> -1.4142..., -2.8282..., -4.2426... 
(1, 2), (2, 1), (3, 3)일 경우 
===> -2.1213..., -2.1213..., -4.2426...

이를 그림으로 설명하면 아래와 같다.


파란점은 입력 벡터이고 빨간점은 설명을 위한 가상의 변환된 벡터이다. 이 실험을 한 이유는 어떤 벡터가 변환이 될 때, 그림-1 처럼 회전하는 것인지, 그림-2처럼 투영되는 것인지 알고 싶었다. 그림-1을 보면 회전하는 것처럼 보이지만 그림-2처럼 투영된다. 즉 변환된 값은 A+까지의 길이값이 아니라, A*이다.


마치며

실재 PCA는 SVD를 기반으로 한다. 그 중에서 U행렬을 사용한다. 그러나 S-벡터를 이용해서 구한 explained variance는 코드에 선언만 되어있지 사용하지 않고 있다. 이 점은 미심쩍긴 하다. SVD는 쉽게 이해되기 어려운 상태이다. 개념은 이해되지만... 다만 SVD가 이미지 변환등에 많이 쓰인다는 것이 마치 우리의 지능이 이미지 변환같은 것이 아닐까 하는 생각을 했다.

또한 spark뿐만이 아니라 대부분의 오픈소스 코드를 보면 이미 누군가 구현한 라이브러리를 많이 사용한다. SVD모듈도 spark측에서 구현한게 아니다. 그런데, 이들은 이런 라이브러리가 어디에 있다는 것을 어떻게 아는 것인지... 우리나라에서는 미진한, 오픈소스의 활발한 발전이 신념의 문제가 아니라 인프라의 문제일 수도 있겠다는 생각 또한 들었다.