본문 바로가기
#02.천재교육 빅데이터/+06.머신러닝 기초

[천재교육] 차원축소 (PCA, LDA)

by 돌비오 2023. 3. 16.
728x90
차원의 저주 (The curse of dimensionality)

차원이 증가할 수록 데이터 간 빈 공간이 생기게 됨으로써 생기는 문제들.

 

 

 

 

 


 

차원 축소

차원의 저주 문제를 해결하기 위해 특성(Feature) 수를 줄여서 학습 불가능한 문제를 학습 가능한 문제로 만드는 기법.
즉, 정보의 손실이 크지 않은 방향으로 고차원의 데이터를 저차원의 데이터셋으로 변환시키는 것이다.

 

 

 

사영기법

n차원의 데이터셋을 차원이 낮은 d차원 데이터 셋으로 사영(Projection)하는 기법

 

 

 

 

 

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

 학습 데이터 셋을 특정 초평면(3차원 이상의 고차원에 존재하는 저차원 공간, Hyperplane) 에 사영하는 기법

 

 

 

 

 

 

 

PCA 의 활용

 

 

 

 

 


 

2. LDA (선형 판별 분석, Linear Discriminant Analysis)

- PCA는 입력 데이터의 변동성이 가장 큰 축을 찾음 (분산분포)
- LDA는 입력 데이터의 결정값 클래스를 최대한 분리할 수 있는 축을 찾음

 

 

 

다양한 차원축소 모델

 

 

 

 

 


 

붓꽃 데이터 PCA 변환을 위한 데이터 로딩 및 시각화
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

# 사이킷런 내장 데이터 셋 API 호출. 그 유명한 붓꽃데이터!
iris = load_iris()


# 넘파이 데이터 셋을 Pandas DataFrame으로 변환
# 붓꽃데이터는 data라는 key에 x value가 저장되어있다.
# 컬럼은 기본적으로 0,1,2,3 이런식으로 저장되어있기에 컬럼명으로 변경
# sepal_length = 꽃받침 길이 / petal_width = 꽃잎 넓이

columns = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(iris.data , columns=columns) 


# 붓꽃데이터에 target 이라는 key에 붓꽃의 종류가 들어가 있다.
# Iris-Setosa, Iris-Versicolour, Iris-Virginica
# 그래서 붓꽃종류 컬럼추가
irisDF['target']=iris.target
irisDF.head(3)

=>



# setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers=['^', 's', 'o']


# setosa의 target 값은 0, versicolor는 1, virginica는 2.
# 각 target 별로 다른 shape으로 scatter plot 

for i, marker in enumerate(markers):
    x_axis_data = irisDF[irisDF['target']==i]['sepal_length']
    y_axis_data = irisDF[irisDF['target']==i]['sepal_width']
    plt.scatter(x_axis_data, y_axis_data, marker=marker,label=iris.target_names[i])

plt.legend()
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.show()

=>

 

 

 

 

붓꽃데이터 PCA 변환
<<평균이 0, 분산이 1인 정규 분포로 원본 데이터를 변환>>

from sklearn.preprocessing import StandardScaler

# Target 값을 제외한 모든 속성 값을 StandardScaler를 이용하여 표준 정규 분포를 가지는 값들로 변환
# -1 인 이유는 타겟값은 빼고 해야하니까

iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])

# 4개의 컬럼을 가진 데이터 (4차원데이터)
iris_scaled.shape
=>
(150, 4)




<<PCA 변환 수행>>

from sklearn.decomposition import PCA

# 4개의 컬럼 즉, 4차원 데이터를 2차원으로 줄이기
pca = PCA(n_components=2)

# fit( )과 transform( ) 을 호출하여 PCA 변환 데이터 반환
pca.fit(iris_scaled)                       # PCA는 x 값만 넣어줌
iris_pca = pca.transform(iris_scaled)


# 원래 피쳐 4개(sepal_length, sepal_width, petal_length, petal_width)였는데 X가 2개인 데이터로 변환됨
# 즉, 4 ㅡ> 2차원 차원축소
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
=>





<PCA로 차원 축소된 피처들로 데이터 산포도 시각화>

# 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 Component별 변동성 비율 >>

# 각 주성분에 대한 분산비율
# 차원축소 후 분산비율을 확인해보는 것
print(pca.explained_variance_ratio_)

=> [0.72962445 0.22850762]              # [PC1, PC2]




<< 원본 데이터와 PCA 변환된 데이터 기반에서 예측 성능 비교 >>

# PCA ==> 원본데이터보다 정보량이 줄어듦.
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import numpy as np

 
# 학습 : 테스트 = 8:2  ==> 첫번째시도 8:2 / 두번째 8:2 / 세번째 8:2
# 총 세번의 테스트 결과를 교차검증하고 그때의 정확도를 위해 cross_val_score 사용
# 랜덤포레스트 모델에 넣기

rcf = RandomForestClassifier(random_state=156)

# cv=3 ㅡㅡ> 3번에 걸쳐 정확도를 뽑아보기   
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 )


PCA 변환 데이터 교차 검증 개별 정확도
print(scores_pca)
=>         
[0.88 0.88 0.88]


# PCA 변환 데이터 평균 정확도
print(np.mean(scores_pca))

=>
0.88



## 차원축소하니 역시 성능은 떨어짐. 그러나 4차원 -> 2차원으로 줄였음 ##

 

 

 

 


붓꽃 데이터로 LDA 변환을 위한 데이터 로딩 및 시각화
# PCA : X를 가지고 FIT
# LDA : Y의 정보도 넣고 FIT

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

# 2차원으로 줄이기 위해 2개의 주성분을 사용하겠다는 뜻
lda = LinearDiscriminantAnalysis(n_components=2)
  
# fit()호출 시 target값 입력 
lda.fit(iris_scaled, iris.target)               # X값과 Y값을 넣었다.
iris_lda = lda.transform(iris_scaled)    # 변환된 값 저장
print(iris_lda.shape)
=>
(150, 2)                                                # 2개의 주성분을 사용해서 변환된 값


import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

lda_columns=['lda_component_1','lda_component_2']
irisDF_lda = pd.DataFrame(iris_lda,columns=lda_columns)
irisDF_lda['target']=iris.target

# setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers=['^', 's', 'o']

# setosa의 target 값은 0, versicolor는 1, virginica는 2. 각 target 별로 다른 shape으로 scatter plot
for i, marker in enumerate(markers):
    x_axis_data = irisDF_lda[irisDF_lda['target']==i]['lda_component_1']
    y_axis_data = irisDF_lda[irisDF_lda['target']==i]['lda_component_2']

    plt.scatter(x_axis_data, y_axis_data, marker=marker,label=iris.target_names[i])

plt.legend(loc='upper right')
plt.xlabel('lda_component_1')
plt.ylabel('lda_component_2')
plt.show()

=>

 

728x90