갈루아의 반서재

비지도 학습 (1) - 주성분 분석(Principal Component Analysis, PCA)


주성분 분석(Principal Component Analysis, PCA)


주성분 분석은 상관된 변수의 집합을 가능한 한 상관되지 않는 변수의 집합으로 변환하는 직교 선형 변환이다. 주성분 분석은 데이터를 한개의 축으로 사상시켰을 때 그 분산이 가장 커지는 축을 첫 번째 주성분, 두 번째로 커지는 축을 두 번째 주성분으로 놓이도록 새로운 좌표계로 데이터를 선형 변환한다. 이와 같이 표본의 차이를 가장 잘 나타내는 성분들로 분해함으로써 여러가지 응용이 가능하다. 이 변환은 첫째 주성분이 가장 큰 분산을 가지고, 이후의 주성분들은 이전의 주성분들과 직교한다는 제약 아래에 가장 큰 분산을 갖고 있다는 식으로 정의되어있다. 중요한 성분들은 공분산 행렬의 고유 벡터이기 때문에 직교하게 된다.

기계학습에서 PCA는 가능한 한 분산을 그대로 유지하며 고차원에서 자차원으로 축소할 수 있도록 한다. 변환하는데 목적범주가 필요하지 않고 학습 속성의 값만 의존하기 때문에 비지도 학습이다. 시각화와 속성 선택에 유용하다.


본 포스팅에서 작업할 예제는 각 인스턴스가 64개의 속성으로 구성된 8*8 픽셀 매트릭스로 디지털화된 손글씨 숫자 데이터셋이다. 한 번에 64차원을 가시화하는 것은 불가능하다. 그래서 PCA를 통해서 인스턴스를 2차원으로 줄이고 산점도로 분포를 시각화한다.

먼저 데이터를 로드한다.

1
2
3
from sklearn.datasets import load_digits
digits = load_digits()
X_digits, y_digits = digits.data, digits.target
cs

sklearn.datasets.load_digits(n_class=10, return_X_y=False)

Load and return the digits dataset (classification).

Each datapoint is a 8x8 image of a digit.

Classes 10
Samples per class ~180
Samples total 1797
Dimensionality 64
Features integers 0-16


digits 속성 이름을 출력한다.

1
2
3
>>> print digits.keys()
 
['images''data''target_names''DESCR''target']

cs


인스턴스에 해당하는 숫자번호인 target 벡터와 64개의 속성의 인스턴스를 포함한 data 매트릭스를 사용한다. digits 출력을 통해 인스턴스가 나타나는 모습을 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> %matplotlib inline
>>> 
>>> import matplotlib.pyplot as plt
>>> n_row, n_col = 25
>>> 
>>> def print_digits(images, y, max_n=10):
>>>     fig = plt.figure(figsize=(2. * n_col, 2.26 * n_row))
>>>     i=0
>>>     while i < max_n and i < images.shape[0]:
>>>         p = fig.add_subplot(n_row, n_col, i + 1, xticks=[], yticks=[])
>>>         p.imshow(images[i], cmap=plt.cm.bone, interpolation='nearest')
>>>         p.text(0-1str(y[i]))
>>>         i = i + 1
>>> 
>>> print_digits(digits.images, digits.target, max_n=10)
cs

figsize : 폭과 너비의 튜플로 단위는 인치이다.

add_subplot() : 예를 들어, add_subplot(2, 3, 4)는 2행 3열의 그리드에서 4번째 subplot 이라는 의미다.

imshow(interpolation='nearest') : interpolation 메소드 중의 하나로 라는 것은 화면의 해상도가 이미지의 해상도와 일치하지 않을 때 처리 방식에 대한 것이다. 메소드별 차이는 아래 링크 참조.

smoothing between pixels of imagesc\imshow in matlab like the matplotlib imshow http://stackoverflow.com/questions/14722540/smoothing-between-pixels-of-imagesc-imshow-in-matlab-like-the-matplotlib-imshow

imshow(cmap=plt.cm.bone) : 컬러맵의 종류 설정. 컬러맵에 관한 다양한 예시는 다음 링크 참조. 아래 이미지는 plt.cm.bone의 경우이다. http://chrisalbon.com/python/set_the_color_of_a_matplotlib.html

c13 = plt.scatter(theta, r, c=colors, s=area, cmap=plt.cm.bone)


text(x, y, s, fontsize=12) : 축을 중심으로 텍스트를 넣는다. 0, 0 은 하단 좌측, 그리고 1, 1은 상단 우측을 나타낸다.

matplotlib basic text commands http://matplotlib.org/users/text_intro.html
Putting text in top left corner of matplotlib plot http://stackoverflow.com/questions/8482588/putting-text-in-top-left-corner-of-matplotlib-plot


출력 결과는 다음과 같다.

PCA 변환으로 얻어진 2차원 점을 산점도에 그리는 함수를 정의한다. 데이터점은 범주에 따라 색이 다르며 목적 범주까지는 반환하지 않는다. PCA 실행 후 분포는 다른 범주의 분포를 드러내며 범주가 선명하게 분리되길 바란다.  0부터 9까지의 숫자는 10개 색을 사용한다. 변환기는 fit 메소드를 통해 요소의 개수로 학습하고, 요소 위에 투사해 만든 새로운 데이터를 사용할 수 있다. scikit-learn에는 ProbabilisticPCA, RandomizedPCA, KernelPCA 와 같은 PCA 분해의 여러 종류를 구현한 클래스가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> %matplotlib inline
>>> 
>>> from sklearn.decomposition import PCA
>>> estimator = PCA(n_components=10)
>>> X_pca = estimator.fit_transform(X_digits)
>>> 
>>> def plot_pca_scatter():
>>>     colors = ['black''blue''purple''yellow''white''red''lime''cyan''orange''gray']
>>>     for i in xrange(len(colors)):
>>>         px = X_pca[:, 0][y_digits == i]
>>>         py = X_pca[:, 1][y_digits == i]
>>>         plt.scatter(px, py, c=colors[i])
>>>     plt.legend(digits.target_names)
>>>     plt.xlabel('First Principal Component')
>>>     plt.ylabel('Second Principal Component')
>>> 
>>> plot_pca_scatter()
cs


n_components : 인스턴스의 속성 개수를 명시한다. 예제에서는 64개 속성의 인스턴스를 10개 속성의 인스턴스로 변환하고자 한다. 그래서 n_components 를 10으로 설정한다.

fit_transform(X, y=None) :  X에 대해 지정하면, X의 차원을 줄여준다.


1
2
3
4
for i in xrange(10):
    print i, X_pca[:, 0][y_digits==i], X_pca[:, 1][y_digits==i]
 
>>> 0 [ -1.25949927e+00   1.12150947e+01 ...  6.43540563e+00   2.40942423e-01] [ 21.27494749  16.91977015 ... 19.47706123  26.59297862]
cs

결과는 아래와 같다.

마지막으로 주성분 변환에 대해 살펴본다. components 속성을 통해 에스터메이터의 주성분에 접근할 수 있다. 매트릭스의 각 주성분은 본래 공간에서 변환 공간으로 벡터를 변환하는 데 사용한다. 이전에 그린 산점도에서 첫 두 주성분으로 살펴보자. 본래 데이터처럼 같은 크기의 모든 주성분을 그린다.

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> %matplotlib inline
>>> 
>>> def print_pca_components(images, n_col, n_row):
>>>     plt.figure(figsize=(2. * n_col, 2.26 * n_row))
>>>     for i, comp in enumerate(images):
>>>         plt.subplot(n_row, n_col, i + 1)
>>>         plt.imshow(comp.reshape((88)), interpolation='nearest')
>>>         plt.text(0-1str(i + 1+ '-component')
>>>         plt.xticks(())
>>>         plt.yticks(())
>>> 
>>> n_components = n_row * n_col
>>> print_pca_components(estimator.components_[:n_components], n_col, n_row)
cs

추가적인 주성분을 사용한다면 범주를 새로운 차원으로 구별할 수 있는 특징을 더 얻는다. 예를 들어 세 번째 주성분을 추가하고 3차원 산점도에 인스턴스를 그릴 수도 있다.