[파이썬 머신러닝 가이드] 차원 축소 - PCA (Principal Component Analysis)

PCA (Principal Component Analysis)

 

data가 많이 있는 축 → 주성분 = principal component(데이터가 주로 분포한 축, 성분)

데이터의 특성을 가장 잘 살릴 수 있는, 가장 많이 분포해있는 축으로 축소시켜야 함.

 

  • Covariance(공분산)이란?

보통 분산은 한 개의 특정한 변수의 데이터 변동을 의미하나, 공분산은 두 변수 간의 변동을 의미함.

Cov(X,Y) > 0 → X가 증가할 때, Y도 증가함을 의미.

공분산 행렬은 여려 변수와 관련된 공분산을 포함하는 정방형 행렬. (분산의 multidimension ver.)

 

  • PCA

= covariance를 SVD 해서 그거의 장축을 고르면 됨.

 

*** PCA는 covariance 구하고, 그 행렬에 SVD 적용해라.
그리고 컬럼벡터(U) 원하는 차원까지 순서대로 (데이터 많은.. 장축) 뽑아와라. 그리고 X에 곱해주면 차원축소 ***

 

X가 N x D이면, input이 D차원인거니까 covariance는 D x D.

SVD 분해한 U 행렬에서 원하는 차원(D’)까지 앞에 있는 column vector들 뽑은걸 U’라 하면, 이건 D x D’ 소속.

즉, 축소시킨 행렬 Z는 XU’니까 (N x D)(D x D’)라서 N x D’ 로 축소됨.

 

 

  • PCA와 SVD 혼합 이해

사실 아까 위에서 AAT를 UΣΣTUT로 바꾼건 PCA 재질.

AAT가 공분산이라면 PCA..

고유벡터(eigen vector) - 행렬 A를 곱하더라도 방향이 변하지 않고 그 크기만 변하는 벡터.

고유벡터는 여러 개가 존재하며, 정방행렬은 최대 그 차원 수 만큼의 고유 벡터를 가질 수 있음.

고유벡터는 행렬이 작용하는 힘의 방향과 관계가 있어서 행렬을 분해하는 데 사용.

공분산 행렬은 정방행렬(square matrix)이며 대칭행렬(symmetric matrix)

정방행렬 중에서도 대각 원소를 중심으로 원소 값이 대칭 되는 행렬, AT= A인 행렬이 대칭행렬.

공분산 행렬은 개별 분산값을 대각 운소로 하는 대칭행렬.

대칭행렬은 항상 고유벡터를 직교행렬(orthogonal matrix)로, 고유값을 정방행렬로 대각화할 수 있다는것

+아까 위에서, AAT는 symmetric하고, 그걸 decomposition하면 무조건 orthonormal 한 basis(합치면 orthogonal matrix)로 튀어나온댔음.

→ 이런 AAT를 UΣΣTUT(U는 고유벡터였고, Σ는 고유값에 루트씌운거였으니..)

 

C = PΣPT
P는 n x n의 직교행렬, Σ는 n x n의 정방행렬, Pt는 행렬 P의 전치행렬

 

공분산 C는 고유벡터 직교행렬 * 고유값 정방행렬 * 고유벡터 직교행렬의 전치 행렬로 분해됨.

 

- e1은 가장 분산이 큰 방향을 가진 고유벡터이며, e2는 e1에 수직이면서 다음으로 가장 분산이 큰 방향을 가진 고유벡터임.

- 입력 데이터의 공분산 행렬이 고유벡터와 고유값으로 분해될 수 있으며, 이렇게 분해된 고유벡터를 이용해 입력 데이터를 선형 변환하는 방식이 PCA

 

  • PCA 구하는 step

1. 입력 데이터 세트의 공분산 행렬 생성

2. 공분산 행렬의 고유벡터와 고유값 계산(SVD 분해)

3. 고유값이 가장 큰 순으로 K개 (PCA 변환 차수만큼) 만큼 고유벡터 추출

4. 고유값이 가장 큰 순으로 추출된 고유벡터를 이용해 새롭게 입력 데이터 변환 (x 곱해줌(투영))

 

 

  • 코드 구현

PCA는 여러 속성의 값을 연산해야하므로 속성의 스케일에 영향을 받음.

→ 여러 속성을 PCA로 압축하기 전에 각 속성값을 동일한 스케일로 변환해야함.

 

from sklearn.preprocessing import StandardScaler

# Target 값을 제외한 모든 속성 값을 StandardScaler를 이용하여 표준 정규 분포를 가지는 값들로 변환
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])
from sklearn.decomposition import PCA

pca = PCA(n_components=2)

#fit( )과 transform( ) 을 호출하여 PCA 변환 데이터 반환
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled) #iris_pca는 변환된 pca 데이터 세트를 150 x 2 넘파이 행렬로 가지고 있음. 
print(iris_pca.shape) # (150,2)
# PCA 변환된 데이터의 컬럼명을 각각 pca_component_1, pca_component_2로 명명
pca_columns=['pca_component_1','pca_component_2']
irisDF_pca = pd.DataFrame(iris_pca, columns=pca_columns)
irisDF_pca['target']=iris.target
irisDF_pca.head(3)
#setosa를 세모, versicolor를 네모, virginica를 동그라미로 표시
markers=['^', 's', 'o']

#pca_component_1 을 x축, pc_component_2를 y축으로 scatter plot 수행. 
for i, marker in enumerate(markers):
    x_axis_data = irisDF_pca[irisDF_pca['target']==i]['pca_component_1']
    y_axis_data = irisDF_pca[irisDF_pca['target']==i]['pca_component_2']
    plt.scatter(x_axis_data, y_axis_data, marker=marker,label=iris.target_names[i])

plt.legend()
plt.xlabel('pca_component_1')
plt.ylabel('pca_component_2')
plt.show()

pca_componet_1이 원본 데이터의 변동성을 잘 반영하고 있음.

PCA 객체의 explained_variance_ratio_ 속성은 전체 변동성에서 개별 PCA 컴포넌트별로 차지하는 변동성 비율 제공

print(pca.explained_variance_ratio_)

 

[0.72962445 0.22850762]

→ PCA를 2개 요소로만 변환해도 원본 데이터 변동성을 95% 설명가능

 

 

  • 원본 데이터셋과 PCA로 변환된 데이터셋에 각각 분류 적용 후 결과 비교
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import numpy as np

rcf = RandomForestClassifier(random_state=156)
scores = cross_val_score(rcf, iris.data, iris.target,scoring='accuracy',cv=3)
print('원본 데이터 교차 검증 개별 정확도:',scores)
print('원본 데이터 평균 정확도:', np.mean(scores))
원본 데이터 교차 검증 개별 정확도: [0.98 0.94 0.96]
원본 데이터 평균 정확도: 0.96
pca_X = irisDF_pca[['pca_component_1', 'pca_component_2']]
scores_pca = cross_val_score(rcf, pca_X, iris.target, scoring='accuracy', cv=3 )
print('PCA 변환 데이터 교차 검증 개별 정확도:',scores_pca)
print('PCA 변환 데이터 평균 정확도:', np.mean(scores_pca))
PCA 변환 데이터 교차 검증 개별 정확도: [0.88 0.88 0.88]
PCA 변환 데이터 평균 정확도: 0.88

 

+corr()을 이용해 각 속성 간의 상관도를 구한 뒤 이를 seaborn의 heatmap으로 시각화 해보면 상관도를 볼 수 있는데, 높은 상관도를 가진 속성들은 소수의 PCA만으로도 자연스럽게 이 속성들의 변동성을 수용할 수 있음.

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

corr = X_features.corr()
plt.figure(figsize=(14,14))
sns.heatmap(corr, annot=True, fmt='.1g')