Machine learning

[기계학습]PCA (Principal Conponents Analysis) 주성분 분석

Acdong 2020. 7. 2. 21:52
728x90

 

pcaPractice.py
0.00MB

 

PCA(Principal Conponents Analysis)란

 

차원을 축소 즉 변수(feature)들의 갯수를 함축시키는 방법이다.

 

예를 들면 국어 성적과 영어성적을 합쳐서 문과적능력으로 합치는 것과 같다.

국어 , 영어 성적 ( 2차원) --> 문과적능력(1차원)으로 차원을 축소시킨다. 

 

차원축소는 언제 사용하는가?

  • Visualization - 시각화
    3차원 이하의 데이터로 만들어 차트로 보여줘 데이터의 이해를 돕는다.

  • reduce noise - 이미지의 노이즈 감소
  • preserve useful info in low memory - 메모리 절약
  • less time complexity - 시간절약
  • less space complexity  - 공간절약

차원축소법(PCA)

2차원 공간상의 점을 1차원으로 줄인다고 가정할 때.

 

 

첫 번째 방법은 하나의 축(X1)으로 몰아넣는다.

 

 

이렇게 될 경우 위의 그림과 같이 정보가 겹치게되서 유실이된다.

 

 

X2 축으로 데이터를 몰아넣을 경우에도 X1보다는 덜하지만 겹치는 부분이 있어 이 역시도 정보가 유실된다.

 

그렇다면!

유실을 줄이는 가장 좋은 방법은 무엇일까?

 

좀 더 과학적인 방법은 분산이 가장 넓은 지역을 찾는 것이다.

 

 

 

분산이 가장 넓은 하나의 축에다가 점들을 놓으면 퍼져있는 정도를 지켜줌으로써

점들이 서로 최대한 겹치지않고 모이게 된다.

 

이것이 바로 PCA 알고리즘이다.

 

 

그럼 축 PC(Principal Component)를 어떻게 그었을까?

 

점들이 어떻게 가장 잘 퍼져있는 정도(분산)을 계산해가지고 가장 살릴 수 있는 방향으로 축을 긋는다.

 

PC(Principla Component)는 수학적으로 접근했을 때

PC = Eigen Vector from Covariance Matrix

 

점들이 갖고 있는 변수(feature)들의 공분산행렬(Covariance Matrix)의 고유벡터(Eigen Vector)이다.


그럼 공분산행렬(covariance matrix)이란 무엇일까?

 

데이터 구조적 의미 : 각 feature의 변동이 얼마나 닮았나

 

행렬에서 열들의 합이 0이라고 가정한다.

열들의 합이 0 이라는 뜻은 feature들의 데이터값의 평균을 뺀 상태

 

키의 평균이 170이라고 할때 180인 사람의 값은 10이고 160인 사람은 -10이다.

그래서 결국 열을 다 더하게 되면 0이됨.

 

 

 

변동의 닮은 정도를 보기위해선 내적이 필요하게 되고 그 과정은 위의 식처럼 행렬의 곱으로 표현가능하다.

 

행렬의 곱을하게되면

 

 

 

이런식으로 분산 dot(X1,X1)과 공분산 dot(X1,X2) 의 모양이 나타나게 된다. ( 완전한 모양은 아님 )

위의 행렬은 symmetric matrix로 대칭행렬이다. dot(X1,X2)와 dot(X2,X1)의 값이 같다.

 

위의 식 (XTX)ij 는 i번째 feature와 j 번째 feature의 변동이 닮은 정도를 말해주고 있다.

 

하지만 sample의 수(n)가 많아질 수록 위의 값은 커지고 이 문제를 방지해주기 위해 값은 n으로 나눠주면

공분산행렬의 값을 얻을 수 있다.

 

*행렬의 곱은 곱해서 더해주는 형식임으로 샘플이 많아지면 + 횟수가 증가하게 되어 값은 커지게된다.

 

 

공분산 행렬의 수학적 의미 : 선형 변환 (shearing)

 

 

 

선형 변환을 하기전에 데이터 분포

 

 

데이터셋을 공분산 행렬로 변환하게 되면 위의 분포처럼 선형의 모양으로 변경된다.

 

PCA 알고리즘은 데이터의 구조를 잘 살려주면서 차원 감소를 할 수 있게끔 하는 방법이다.

2차원 평면 상의 산점도를 1차원에 정사영시켜 차원을 감소시켜준다고 하면

어떤 벡터에 정사영 시키는 것이 가장 좋을까?

 

정사영이란?

n차원의 데이터를 줄이면서 한 축으로 데이터를 옮기는 과정

 

 

2.1 벡터로 정사영 시킬 경우 variance(퍼진정도)가 4.1106이다.

 

 

variance(퍼진정도)가 크면 클수록 데이터 유실이 적게되고 좋은 벡터(설명력이 뛰어난)라고 볼 수 있다.

공분산 행렬을 통해 선형변환 할 때의 주축(PC)에 대해 정사영 하는 것이 제일 좋다.

 

이 과정에서 PC를 구하는 방법을 찾게 되고 PC를 찾는 과정에서 Eigen Vector와 Eigen Value가 등장한다.

 

Eigen Vector는 선형변환의 주축이라고 불린다. 

즉, 선형변환을 했을 때 크기만 바뀌고 방향은 바뀌지 않는 벡터를 의미한다.

 

EigenVector의 정사영해야 variance가 가장 큰 결과를 얻을 수 있다.

 

 

주축 Eigen Vector의 정사영 하는 경우는 Variance가 4.552로 가장 높다.

 

 


고유벡터(Eigen Vector)는 2차원이면 2개 , n차원에서는 n개를 가지고 있다.

 

 

 

2차원 공간에서는 2개의 Eigen Vactor가 존재하고 그 중 가장 넓게 퍼져있는

하나의 Eigen Vactor를 선택해야하는데 선택의 기준은 Eigen Value(고유값)을 통해서 선택한다.

 

Eigen Value 가 높으면 가장 넓게 퍼져있다는 뜻이다.

Eigen Value를 기준으로 Eigen Vactor를 선택을해서 점들을 선택한 Eigen Vactor로 옮겨주는 것이 바로 PCA 이다!!!


자 그럼 실습을 해보자!!

Python - Code

import pandas as pd
 
#데이터 프레임 생성
df = pd.DataFrame(columns=['calory','breakfast','lunch','dinner','exercise','body_shape'])
 
#데이터 셋
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']
 
print(df.head(10))

cs

 

생성된 Dataframe

 

= df[['calory','breakfast','lunch','dinner','exercise']]
 
print(x.head(10))
 
= df[['body_shape']]
 
print(y.head(10))

cs

X (독립변수)와 Y(종속변수) 분리

from sklearn.preprocessing import StandardScaler
 
xStd = StandardScaler().fit_transform(x)
 
print(xStd)

cs

여기서 calory data가 나머지 값들에 비해 크기때문에 비슷한 크기로 변환해줘야한다.

비슷한 크기로 변환해주는 함수가 StandardScaler() 함수이다.

 

값들이 비슷한 값으로 전부 바뀐다.

import numpy as np
 
features = xStd.T
 
convarianceMatrix = np.cov(features)
print(convarianceMatrix)
cs

여기서 스탠다드한 행렬을 트렌스포즈(T) 해서 feature라는 변수에 저장해주고 numpy 패키지에 있는

cov( ) 함수를 사용해서 convarianceMatrix 즉 공분산행렬을 구해준다.

 

공분산행렬

 

* 공분산 행렬은 위의내용처럼 대각선 diagonal을 기준으로 대칭행렬이다.

 

자 공분산행렬을 구했으면 우리는 최종목표인 eigenVactor와 eigenValue를 구해야한다.

eigValue , eigVector = np.linalg.eig(convarianceMatrix)
 
print('EigenVactors \n%s' %eigVector)
 
print('\nEigenValues \n%s' %eigValue)
cs

eigenVector 와 eigneValue도 numpy의 linalg.eig( ) 함수를 통해서 구할수있다.

공분산행렬을 인자로 넣어주면된다.

고유값과 고유벡터를 구했으니 이제 PC(Principal Component)의 값을 구해볼까?

firstPC = eigValue[0/ sum(eigValue)
 
print(firstPC)cs

 

 

해석) 첫 번째 PC가 전체데이터의 73%를 설명해주고있다. 즉 , 1개만 사용했을 경우 27%의 데이터유실이있다.

projectedX = xStd.dot(eigVector.T[0])
 
print(projectedX)
 
result = pd.DataFrame(projectedX,columns=['PC1'])
result['y-axis'= 0.0
result['label'= y
 
import matplotlib.pyplot as plt
import seaborn as sns
 
sns.lmplot('PC1','y-axis',data=result,fit_reg=False,
           scatter_kws={"s":50},
           hue="label")
 
plt.title("PCA result")
plt.show()
 

cs

자 이제 dot( ) 함수를 통해 eigenVector에다가 데이터를 올려놓고 projectedX 변수에 넣습니다.

 

그리고 시각화 패키지 matplotlib 를 통해 산점도를 그려보도록 합니다.

 

 

 

짠! PCA 알고리즘에 대해서 알아봤습니다.

 

위의 처럼 구현하는 것도 좋지만 PCA는 이미 패키지로 나와있습니다.

#패키지로 PCA 알고리즘 사용하기
 
from sklearn import decomposition
pca = decomposition.PCA(n_components=1)
sklPcaX = pca.fit_transform(xStd)
 
sklResult = pd.DataFrame(sklPcaX,columns=['PC1'])
sklResult['y-axis'= 0.0
sklResult['label'= y
 
sns.lmplot('PC1','y-axis',data=sklResult,fit_reg=False,
           scatter_kws={"s":50},
           hue="label")
 
plt.title("PCA result")
plt.show()

cs
반응형