다운로드
작성자: admin 작성일시: 2016-06-16 23:47:29 조회수: 6563 다운로드: 313
카테고리: 머신 러닝 태그목록:

PCA

PCA(Principal Component Analysis)는 주성분 분석이라고도 하며 차원 축소를 통해 최소 차원의 정보로 원래 차원의 정보를 모사(approximate)하려는 작업을 말한다.

차원 축소

차원 축소란(Dimension Reduction) 고차원 벡터에서 일부 차원의 값을 모두 0으로 만들어(truncation) 저차원 벡터로 줄이는 것을 말한다. 다만 원래의 고차원 벡터의 특성을 최대한 살리기 위해 가장 분산이 높은 방향으로 회전 변환(rotation transform)을 한다.

PCA와 고유값 분해

$D$ 차원의 데이터 $x$가 $N$개 있으면 이 데이터는 특징 행렬 $X \in \mathbf{R}^{N\times D}$로 나타낼 수 있다. 이 데이터를 가능한한 쓸모있는 정보를 유지하면서 더 적은 차원인 $M (M < D)$ 차원의 차원축소 벡터 $\hat{x}$으로 변환하고자 한다.

PCA(Principal Component Analysis) 방법은 선형 변환을 사용한다. 다음과 같이 변환 행렬 $W \in \mathbf{R}^{M \times D}$을 사용하는 선형 변환을 생각하자.

$$ \hat{x}_i = W x_i \;\;\;(x \in \mathbf{R}^D,\; W \in \mathbf{R}^{M \times D},\; \hat{x} \in \mathbf{R}^M) $$

이를 행렬식으로 바꾸면 다음과 같다.

$$ \hat{X} = XW^{T} \;\;\;(X \in \mathbf{R}^{N \times D},\; \hat{X} \in \mathbf{R}^{N \times M}, W^T \in \mathbf{R}^{D \times M}) $$

PCA의 목표는 변환 결과인 차원축소 벡터 $\hat{x}_i$가 정보가 원래의 벡터 $x_i$가 가졌던 정보와 가장 유사하게 되는 $W$ 값을 찾는 것이다.

그러나 $\hat{x}_i$는 $M(M < D)$ 차원 벡터로 원래의 $D$ 차원 벡터 $x_i$와 차원이 다르기 때문에 직접 두 벡터를 비교할 수는 없고 $\hat{x}_i$를 도로 $D$ 차원 벡터로 선형 변형하는 최적의 변환 행렬 $U \in \mathbf{R}^{D \times M}$를 찾아야 한다.

이런 역변환 행렬 $U$가 존재한다고 가정하자. 그러면 원래의 데이터 벡터 $x$를 더 낮은 차원의 데이터 $\hat{x} = Wx $으로 변환했다가 다시 원래의 차원으로 되돌릴 수 있다. 도로 $D$차원으로 변환된 벡터를 $\hat{\hat{x}}$라고 하자.

$$ \hat{\hat{x}} = U \hat{x} \;\;\;(\hat{x} \in \mathbf{R}^M,\; U \in \mathbf{R}^{D \times M},\; \hat{\hat{x}} \in \mathbf{R}^D) $$

물론 이렇게 변환과 역변환을 통해 원래의 차원으로 되돌린 벡터 $U \hat{x}$은 원래의 벡터 $x$와 비슷할 뿐 정확히 같지는 않다. 다만 이 값을 다시 한번 차원 축소 변환하면 도로 $\hat{x}$가 된다. 즉,

$$ W \hat{\hat{x}} = W U \hat{x} = \hat{x} $$

따라서 $W$와 $U$는 다음 관계가 있다.

$$ WU = I $$

역변환 행렬 $U$을 알고 있다고 가정하고 역변환을 했을 때 원래 벡터 $x$와 가장 비슷해지는 차원축소 벡터 $\hat{x}$를 다음과 같이 최적화를 이용하여 찾는다.

$$ \arg\min_{\hat{x}} || x - U \hat{x} ||^2 $$

목적함수는 다음과 같이 바꿀 수 있다.

$$ \begin{eqnarray} || x - U \hat{x} ||^2 &=& (x - U \hat{x})^T (x - U \hat{x}) \\ &=& x^Tx - \hat{x}^T U^T {x} - x^T U \hat{x} + \hat{x}^T U^T U \hat{x} \\ &=& x^Tx - 2 x^T U \hat{x} + \hat{x}^T \hat{x} \\ \end{eqnarray} $$

이 식을 $\hat{x}$로 미분하면,

$$ - 2 U^Tx + 2\hat{x} = 0$$$$ \hat{x} = U^Tx $$

가 된다. 원래의 변환식

$$ \hat{x} = Wx $$

과 비교하면

$$ U = W^{T} $$

임을 알 수 있다.

이 때 다음 식이 성립한다.

$$ WW^{T} = I $$

이제 남은 문제는 최적의 변환 행렬 $W$을 찾는 것이다. 이 경우의 최적화 문제는 다음과 같이 된다.

$$ \arg\min_{W} \sum_{i=1}^N || x_i - W^{T} W x_i ||^2 $$

모든 데이터에 대해 적용하면 목적함수는 다음처럼 바뀐다.

$$ \arg\min_{W} || X - X W^{T} W ||^2 $$

여기에서 $$ || A || = || A ||_F = \sqrt{ \sum_i \sum_j a_{ij}^2 } = \sqrt{ tr (A^TA) } $$

가장 단순한 경우로 축소된 차원 $M=1$인 경우를 생각하자. 이 때는 $W=w^T$ 벡터가 된다. $WW^T=I$ 조건은 $w^Tw=1$이 된다.

$$ \arg\min_{W} || X - X W^{T} W ||^2_{F} \\ = \arg\min_{W} || X - X ww^T ||^2_{F} \\ = \arg\min_{W} tr (X - X ww^T)^T(X - X ww^T) \\ = \arg\min_{W} tr (X^T - ww^T X^T)(X - X ww^T) \\ = \arg\min_{W} tr (X^TX - X^T X ww^T - ww^T X^T X + ww^T X^T X ww^T) \\ = \arg\min_{W} -2 tr (X^T X ww^T) + tr(ww^T X^T X ww^T) \\ = \arg\min_{W} -2 tr (X^T X ww^T) + tr(X^T X ww^T ww^T) \\ = \arg\min_{W} -2 tr (X^T X ww^T) + tr(X^T X ww^T) \\ = \arg\min_{W} - tr (X^T X ww^T) \\ = \arg\max_{W} tr (X^T X ww^T) \\ = \arg\max_{W} tr (w^T X^T X w) \\ $$

$X^TX$를 고유 분해하여 대각화하면 $V^T\Sigma V$가 된다

$$ \arg\max_{W} tr (w^T X^T X w) \\ = \arg\max_{W} tr ((Vw)^T\Sigma (Vw)) $$

이 값이 가장 커지려면 $w$가 $X^TX$의 가장 큰 고유값에 대응하는 고유 벡터가 되어야 한다. 따라서 이 최적화 문제의 답 $W^{\ast}$은 공분산 행렬 $XX^T$의 고유값 분해를 사용하여 찾을 수 있다.

축소 차원 $M$이 1보다 커지면 $W^{\ast}$의 각 열은 가장 큰 고유값부터 $\hat{D}$개의 순차적인 고유값에 대응하는 고유 벡터로 이루어진다.

Scikit-Learn 의 PCA 기능

Scikit-Learn 의 decomposition 서브패키지는 PCA분석을 위한 PCA 클래스를 제공한다. 사용법은 다음과 같다.

  • 입력 인수:
    • n_components : 정수
      • 최종 성분의 수
  • 속성:
    • components_
      • 주성분 축
    • n_components_
      • 주성분의 수
    • mean_ :
      • 각 성분의 평균
    • explained_variance_ratio_
      • 각 성분의 분산 비율

2차원 PCA의 예

In:
X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
plt.scatter(X[:,0], X[:,1], s=100)
plt.xlim(-4,4)
plt.ylim(-3,3)
plt.title("original data")
plt.show()

차원을 줄이지 않고 PCA를 쓰면 직교하는 좌표계로 변환하기만 한다.

In:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(X)
Out:
PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)
In:
Z = pca.transform(X)
Z
Out:
array([[ 1.38340578,  0.2935787 ],
       [ 2.22189802, -0.25133484],
       [ 3.6053038 ,  0.04224385],
       [-1.38340578, -0.2935787 ],
       [-2.22189802,  0.25133484],
       [-3.6053038 , -0.04224385]])
In:
w, U = np.linalg.eig(pca.get_covariance())
In:
U.T.dot(X.T).T
Out:
array([[-1.38340578, -0.2935787 ],
       [-2.22189802,  0.25133484],
       [-3.6053038 , -0.04224385],
       [ 1.38340578,  0.2935787 ],
       [ 2.22189802, -0.25133484],
       [ 3.6053038 ,  0.04224385]])
In:
plt.scatter(Z[:,0], Z[:,1], c='r', s=100)
plt.xlim(-4,4)
plt.ylim(-3,3)
plt.title("transformed data")
plt.show()

PCA는 이렇게 직교하는 차원에서 분산이 작은 좌표들을 생략하는 것과 같다.

In:
plt.scatter(Z[:,0], np.zeros_like(Z[:,1]), c='g', s=100)
plt.xlim(-4,4)
plt.ylim(-3,3)
plt.title("transformed and truncated data")
plt.show()
In:
z = PCA(n_components=1).fit_transform(X)

plt.scatter(z, np.zeros_like(z), c='g', s=100)
plt.xlim(-4,4)
plt.ylim(-3,3)
plt.title("transformed and truncated data")
plt.show()

IRIS 데이터의 예

In:
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data[:, 2:]
plt.scatter(X[:, 0], X[:, 1], c=iris.target, s=200, cmap=plt.cm.jet);
In:
model2 = PCA(2).fit(X)
X2 = model2.transform(X)
plt.scatter(X2[:, 0], X2[:, 1], c=iris.target, s=200, cmap=plt.cm.jet)
plt.xlim(-6, 6)
plt.show()
In:
model2.components_
Out:
array([[ 0.92154695,  0.38826694],
       [-0.38826694,  0.92154695]])
In:
model1 = PCA(1).fit(X)
X1 = model1.transform(X)
sns.distplot(X1[iris.target==0], color="b", bins=20, rug=True, kde=False)
sns.distplot(X1[iris.target==1], color="g", bins=20, rug=True, kde=False)
sns.distplot(X1[iris.target==2], color="r", bins=20, rug=True, kde=False)
plt.xlim(-6, 6)
plt.show()
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
In:
model1.components_
Out:
array([[ 0.92154695,  0.38826694]])

전체 데이터를 변환하면 다음과 같다.

In:
model1 = PCA(1).fit(iris.data)
X1 = model1.transform(iris.data)
sns.distplot(X1[iris.target==0], color="b", bins=20, rug=True, kde=False)
sns.distplot(X1[iris.target==1], color="g", bins=20, rug=True, kde=False)
sns.distplot(X1[iris.target==2], color="r", bins=20, rug=True, kde=False)
plt.xlim(-6, 6)
plt.show()
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
/home/dockeruser/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
In:
model1.components_
Out:
array([[ 0.36158968, -0.08226889,  0.85657211,  0.35884393]])
In:
model2 = PCA(2).fit(iris.data)
X2 = model2.fit_transform(iris.data)
plt.scatter(X2[:, 0], X2[:, 1], c=iris.target, s=200, cmap=plt.cm.jet);
In:
model2.components_
Out:
array([[ 0.36158968, -0.08226889,  0.85657211,  0.35884393],
       [ 0.65653988,  0.72971237, -0.1757674 , -0.07470647]])
In:
X3 = PCA(3).fit_transform(iris.data)
from mpl_toolkits.mplot3d import Axes3D

def plot_pca(azim):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d');
    ax.scatter(X3[:,0], X3[:,1], X3[:,2], c=iris.target, s=100, cmap=plt.cm.jet, alpha=1)
    ax.view_init(20, azim)

plot_pca(-60)