캡쳐 사진 및 글작성에 대한 도움 출저 : 유튜브 - 허민석님


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

PCA는 언제 많이 사용할까?

바로 시각화를 위해 사용한다. 여태껏 시각화를 위해 feature 2개만 사용하는 2~3차원 정도의 데이터만 사용했다.
이 차원축소를 이용하면 다차원의 feature도 시각화가 가능해진다.


  1. 차원축소를 이용하여, 고차원 데이터를 3차원 이하로 축소시킬 수 있고, 시각화가 가능해진다
  2. 그 이외에 이미지의 noise제거에도 사용되며,
  3. 정보를 적은차원에 적은메모리로 저장되어, 퍼포먼스 향상에도 사용된다.


PCA의 원리

PCA는 어떻게 동작할까?
2차원을 1차원으로 줄이는 예를 보면서 이해해보자.


아래와 같이 2차원 공간안에 x1, x2 2개의 feature가 축으로 설정되어있다. 그리고 7개의 점이 존재한다.

image


어떻게 2차원을 1차원으로 줄일 수 있을까?
가장 쉬운방법은 하나의 축을 선택하여, 점을 몰아넣는 방법이다.
image


이러한 방식을 사용하면 몇개의 점은 서로 겹치게 되는 문제점이 발생한다. 이 경우, 정보의 유실이 있을 수 있다.
image
image



정보의 유실을 막으면서 차원을 줄이는 방법은 무엇일까?
과학적인 방법으로서, 분산이 가장 넓은 지역을 찾는 것이다.
image


분산이 가장 넓은 곳을 직선으로 표시하였다. 여기로 점들을 옮기게 된다면
각 점들이 퍼져있는 정도를 지켜줄 수 있게 된다. 그 결과 점들은 겹치지 않게 된다.

image

image


이게 바로 PCA 알고리즘이다.
위에서 찾은 분산이 가장 넓은 지역을 PC(Princal Component)라 한다.
이 PC를 긋는 방법은 각 점들이 겹치지 않을 수 있게 살려서 긋는다.
이 때, x축으로 모은 거리나 y축으로 모은 거리보다 긴 것을 확인할 수 있다.

image



머신러닝에서 수학적으로 구하는 PC

image

수학적으로 PC를 구하는 방법은, 각 점들이 가지고 있는 feature(x1, x2)들의 Covariance Matrix에 있는 Eigen Vector이다.


EigenVector(PC)2차원에서는 2개가 존재한다.
4차원이라면 4개가 존재할 것이다.

image


여러개의 EigenVector 중 어느 것을 선택해야할까?

가장 넓게 퍼지게 만드는 EigenVector를 찾아야한다. 즉, 분산이 큰 것을 의미하는 Eigen value가 높은 값을 찾아야한다.

image

우리는 Covariance Matrix 중 Eigen value가 높은 값을 찾아 EigenVector를 찾고, 그 것을 기준으로 데이터들을 옮겨 차원을 축소한다.

image


이 과정을 파이썬으로 실습해보자.


실습


3. PCA 알고리즘
In [1]:
from IPython.core.display import display, HTMLdisplay(HTML("<style> .container{width:90% !important;}</style>"))
In [1]:
import pandas as pd

1. 데이터 만들기, 식습관들(feature)에 따른 ---> 체형(label)

In [3]:
df = pd.DataFrame(columns=['calory', 'breakfast', 'lunch', 'dinner', 'exercise', 'body_shape'])
In [4]:
df.loc[0] = [1200, 1, 0, 0, 2, 'Skinny']df.loc[1] = [2800, 1, 1, 1, 1, 'Normal']df.loc[2] = [3500, 2, 2, 1, 0, 'Fat']df.loc[3] = [1400, 0, 1, 0, 3, 'Skinny']df.loc[4] = [5000, 2, 2, 2, 0, 'Fat']df.loc[5] = [1300, 0, 0, 1, 2, 'Skinny']df.loc[6] = [3000, 1, 0, 1, 1, 'Normal']df.loc[7] = [4000, 2, 2, 2, 0, 'Fat']df.loc[8] = [2600, 0, 2, 0, 0, 'Normal']df.loc[9] = [3000, 1, 2, 1, 1, 'Fat']
In [5]:
df
Out[5]:
calory breakfast lunch dinner exercise body_shape
0 1200 1 0 0 2 Skinny
1 2800 1 1 1 1 Normal
2 3500 2 2 1 0 Fat
3 1400 0 1 0 3 Skinny
4 5000 2 2 2 0 Fat
5 1300 0 0 1 2 Skinny
6 3000 1 0 1 1 Normal
7 4000 2 2 2 0 Fat
8 2600 0 2 0 0 Normal
9 3000 1 2 1 1 Fat

1-1. feature와 label 나누기

In [7]:
X = df[['calory', 'breakfast', 'lunch', 'dinner', 'exercise']]X
Out[7]:
calory breakfast lunch dinner exercise
0 1200 1 0 0 2
1 2800 1 1 1 1
2 3500 2 2 1 0
3 1400 0 1 0 3
4 5000 2 2 2 0
5 1300 0 0 1 2
6 3000 1 0 1 1
7 4000 2 2 2 0
8 2600 0 2 0 0
9 3000 1 2 1 1
In [8]:
Y = df[['body_shape']]Y
Out[8]:
body_shape
0 Skinny
1 Normal
2 Fat
3 Skinny
4 Fat
5 Skinny
6 Normal
7 Fat
8 Normal
9 Fat

1-2. 수치가 굉장히 높은 calory칼럼때문에, 전체데이터를 Rescaling해주기

  • 사이킷런의 StrandardScaler를 이용하여, 각 feature가 같은범위내에 있도록 바꿔준다.
  • Normalization을 사용해도된다
In [9]:
from sklearn.preprocessing import StandardScalerx_std = StandardScaler().fit_transform(X)
In [10]:
x_std
Out[10]:
array([[-1.35205803,  0.        , -1.3764944 , -1.28571429,  1.        ],       [ 0.01711466,  0.        , -0.22941573,  0.14285714,  0.        ],       [ 0.61612771,  1.29099445,  0.91766294,  0.14285714, -1.        ],       [-1.18091145, -1.29099445, -0.22941573, -1.28571429,  2.        ],       [ 1.89972711,  1.29099445,  0.91766294,  1.57142857, -1.        ],       [-1.26648474, -1.29099445, -1.3764944 ,  0.14285714,  1.        ],       [ 0.18826125,  0.        , -1.3764944 ,  0.14285714,  0.        ],       [ 1.04399418,  1.29099445,  0.91766294,  1.57142857, -1.        ],       [-0.15403193, -1.29099445,  0.91766294, -1.28571429, -1.        ],       [ 0.18826125,  0.        ,  0.91766294,  0.14285714,  0.        ]])

2. feature들의 Covariance Matrix 만들기

  • 각 칼럼별로 존재하는 feature들을 row순으로 변형시키는 과정이 필요하다. 그래야 covariance Matrix를 만들 수 있음
In [12]:
import numpy as np

2-1. feature들을 칼럼방향이 아닌 row방향으로 변환시킨다.

In [13]:
features = x_std.T

2-2. 각 feature들이 row방향으로 향해있다면, np.cov를 이용해 covariance matrix를 만들 수 있다.

In [14]:
covariance_matrix = np.cov(features)print(covariance_matrix)
[[ 1.11111111  0.88379717  0.76782385  0.89376551 -0.93179808] [ 0.88379717  1.11111111  0.49362406  0.81967902 -0.71721914] [ 0.76782385  0.49362406  1.11111111  0.40056715 -0.76471911] [ 0.89376551  0.81967902  0.40056715  1.11111111 -0.63492063] [-0.93179808 -0.71721914 -0.76471911 -0.63492063  1.11111111]]

3. c_matrix를 이용해 EigenValue의 최대값인 EigenVector(PC) 구하기

  • 이 과정에서는 np.linalg.eig()이 사용된다. 이 때 eigen_value와 eigen_vector 리스트를 반환해준다.
  • feature가 5개인 5차원이므로, eigen_vector도 5개가 나온다.
In [16]:
eig_vals, eig_vecs = np.linalg.eig(covariance_matrix)
In [17]:
eig_vals
Out[17]:
array([4.0657343 , 0.8387565 , 0.07629538, 0.27758568, 0.2971837 ])
In [18]:
eig_vecs
Out[18]:
array([[ 0.508005  ,  0.0169937 , -0.84711404,  0.11637853,  0.10244985],       [ 0.44660335,  0.36890361,  0.12808055, -0.63112016, -0.49973822],       [ 0.38377913, -0.70804084,  0.20681005, -0.40305226,  0.38232213],       [ 0.42845209,  0.53194699,  0.3694462 ,  0.22228235,  0.58954327],       [-0.46002038,  0.2816592 , -0.29450345, -0.61341895,  0.49601841]])

3-1. eig_vals의 최대값을 눈으로 확인후 가져오고, 전체 중 비율을 구한다.

  • 만약 하나의 eig_vecs만 사용한다면, 5->1차원으로 줄이면서
  • 전체 중 비율값이 = 전체정보 중 그만큼의 정보를 유실없이 가진다는 뜻이다.
In [19]:
eig_vals[0]  /  sum(eig_vals)
Out[19]:
0.7318321731427544

1개의 eigen_vector사용으로 5->1차원 축소시 73% 정보유실이 없는채로 축소할 것이다.

3-2. 해당 eigen_vector를 이용해, 칼럼방향별 feature 데이터를 projection시킨다.

  • projection이란 eigen_vector 직선위에 모든 데이터를 올려놓겠다는 의미이다.(그림생각)
In [23]:
# 칼럼방향의 feautre array를 dot연산을 통해 아이겐벡터 최대값(eig_vecs[0])에 projection시킨다.# 이  때, eig_vecs는 row방향별 feature로 생성한 것이므로, T로 뒤집어준다.projected_X = x_std.dot(eig_vecs.T[0])
In [24]:
projected_X
Out[24]:
array([-2.22600943, -0.0181432 ,  1.76296611, -2.73542407,  3.02711544,       -2.14702579, -0.37142473,  2.59239883, -0.39347815,  0.50902498])

3-3. PCA로 프로젝션된 각 데이터들을 df로 만들자.

In [30]:
result  =  pd.DataFrame(projected_X , columns=['PC1'])result['y-axis'] = 0.0result['label'] = Y
In [31]:
result
Out[31]:
PC1 y-axis label
0 -2.226009 0.0 Skinny
1 -0.018143 0.0 Normal
2 1.762966 0.0 Fat
3 -2.735424 0.0 Skinny
4 3.027115 0.0 Fat
5 -2.147026 0.0 Skinny
6 -0.371425 0.0 Normal
7 2.592399 0.0 Fat
8 -0.393478 0.0 Normal
9 0.509025 0.0 Fat

4. 이제 5차원--> PC(eigen_vector)로 프로젝션--> 1차원 된 데이터들을 시각화

In [32]:
import matplotlib.pyplot as pltimport seaborn as sns%matplotlib inline
In [33]:
sns.lmplot('PC1', 'y-axis', data=result, fit_reg=False,  # x-axis, y-axis, data, no line           scatter_kws={"s": 50}, # marker size           hue="label") # color# titleplt.title('PCA result')
Out[33]:
Text(0.5,1,'PCA result')

5. 사이킷런 PCA 모듈로 한방에 표시하기

In [34]:
from sklearn import decompositionpca = decomposition.PCA(n_components=1)sklearn_pca_x = pca.fit_transform(x_std)
In [35]:
sklearn_result = pd.DataFrame(sklearn_pca_x, columns=['PC1'])sklearn_result['y-axis'] = 0.0sklearn_result['label'] = Ysns.lmplot('PC1', 'y-axis', data=sklearn_result, fit_reg=False,  # x-axis, y-axis, data, no line           scatter_kws={"s": 50}, # marker size           hue="label") # color
Out[35]:
<seaborn.axisgrid.FacetGrid at 0x26516c89ef0>


  1. Thomaspiketty 2021.10.07 04:23

    글 잘 읽었습니다.
    from sklearn.decomposition import PCA
    pca_result = pca.fit_transform(DataFrame)
    혹시 이렇게 라이브러리를 사용해서 결과를 얻은 값과 선생님이 하신 방법과의 차이가 있는지요?

+ Recent posts