Deep Learning/NLP

08. 주성분 분석

해파리냉채무침 2023. 3. 22. 16:48

차원 축소

차원의 저주는 n x p 일때 p가 많은 경우, 연산이 어렵고 데이터 표현이 어려움

이를 위해 차원을 축소함 차원 축소로 인해 정보 손실이 발생할 수 있음

예시)

국어: 80점, 영어 60점, 수학 90점, 과학 90점

평균 = (80+60+90+80)/4 = 77.5점 

평균이라는 하나의 차원으로 나타냄, 하지만 평균으로 각 과목의 점수가 몇인지는 정확히 알 수 없음.

정보 손실을 최소화하는 것이 관건.

pc1 축으로 하면 좌표들이 겹치지 않고 정보를 가져올 수 있음. 

축에 영세했을 때 가장 많이 퍼져있는 것을 알 수 있음. 축을 찾아내는 과정이 주성분 분석이라고 할 수 있다.

 

 

주성분 분석

고차원 데이터를 저차원 데이터로 환원한다.

분산이 가장 커질때 (= 정보 손실을 최소화 하는)축을 pc1, 그 다음으로 커지는 것을 pc2로 설정하여 데이터를 선형 변환함. 

pc1 : 분산이 가장 큰 (넓게 퍼져있는) 방향을 구하여 축을 구함

pc2 : pc1과 직교하면서 + 분산이 두번째로 큰 방향 

 

주성분 분석 절차

데이터 표준화 (평균0, 표준편차 1) -> 분산도가 큰 방향을 찾아서 pc1 생성 -> 구해진 주성분 축으로 좌표 변환

 

2차원 분류를 1차원으로 축소 시

C영역이 겹침, 정보손실을 최소화 하여 주성분 축을 찾았을 때 c영역이 줄어든 것을 알 수 있음

 

3차원 데이터를 2차원으로 축소

분산도 높게 펼칠 수 있는 2차원 평면 생성

code

import pandas as pd
# Eating, exercise habbit and their body shape
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']

df.head()
#고차원 데이터 축소

#벡터 추출
X = df[['calory','breakfast','lunch','dinner','exercise']]
X. head()

Y = df[['body_shape']]
Y.head()

#벡터 표준화
from sklearn.preprocessing import StandardScaler #정규분포 하에 표준화 
x_std = StandardScaler().fit_transform(X)

x_std
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.        ]])
print(x_std.mean()) #평균 0 
print(x_std.std()) # 표준편차 1
3.552713678800501e-17
1.0
x_std.T

 

array([[-1.35205803,  0.01711466,  0.61612771, -1.18091145,  1.89972711,
        -1.26648474,  0.18826125,  1.04399418, -0.15403193,  0.18826125],
       [ 0.        ,  0.        ,  1.29099445, -1.29099445,  1.29099445,
        -1.29099445,  0.        ,  1.29099445, -1.29099445,  0.        ],
       [-1.3764944 , -0.22941573,  0.91766294, -0.22941573,  0.91766294,
        -1.3764944 , -1.3764944 ,  0.91766294,  0.91766294,  0.91766294],
       [-1.28571429,  0.14285714,  0.14285714, -1.28571429,  1.57142857,
         0.14285714,  0.14285714,  1.57142857, -1.28571429,  0.14285714],
       [ 1.        ,  0.        , -1.        ,  2.        , -1.        ,
         1.        ,  0.        , -1.        , -1.        ,  0.        ]])
#공분산 행렬 분산 -> 주성분 축 찾기

import numpy as np

features = x_std.T
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]]
x_std.T.dot(x_std)/9 #dot은 두 array의 내적을 계산함 (행렬곱)
#표준화 데이터 .T * x_std 내적 / 샘플의수 = 넘파이의 공분산 행렬과 동일함
array([[ 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]])
#고유벡터/ 고유값 산출
eig_vals, eig_vecs = np.linalg.eig(covariance_matrix)
print('Eigenvalues \n%s' %eig_vals) #축 별 정보의 양 
print('Eigenvectors \n%s' %eig_vecs) #주성분 축
Eigenvalues 
[4.0657343  0.8387565  0.07629538 0.27758568 0.2971837 ]
Eigenvectors 
[[-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]]
# 1개 차원으로 축소했을때 73%의 정보를 보유
eig_vals[0] / sum(eig_vals) #첫번째 축/ 전체의 합 = 73%
0.7318321731427544
#분해한 데이터를 1차원에 투영
eig_vecs.T[0]
array([-0.508005  , -0.44660335, -0.38377913, -0.42845209,  0.46002038])
# 첫번쨰 축정보 첫번째 표준화 데이터 내적 계산
projected_X = x_std.dot(eig_vecs.T[0])
projected_X #처음 데이터를 1차원으로 투영
array([ 2.22600943,  0.0181432 , -1.76296611,  2.73542407, -3.02711544,
        2.14702579,  0.37142473, -2.59239883,  0.39347815, -0.50902498])
result = pd.DataFrame(projected_X, columns=['PC1'])
result['y-axis'] = 0.0
result['label'] = Y
result

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

sns.lmplot(x='PC1',y='y-axis',data= result, fit_reg=False,
          scatter_kws={"s": 50},
          hue = "label")

# title
plt.title('PCA result')

#데이터가 skinny 부분에서 겹침 분산이 가장 넓은 주성분 축을 찾아서 투영했기 때문에 정보손실이 최소화 하면서 차원축소함

 

 

 

 

출처:https://github.com/insightcampus/sesac-nlp