코딩뿌셔

차원축소 - 주성분 분석(PCA) 본문

Machine Learning

차원축소 - 주성분 분석(PCA)

He__o 2022. 8. 11. 11:06

차원축소란?

 

주성분 분석이란?

대표적인 차원축소 모델로 지도/비지도학습 모두 사용 가능하다.

단독으로 사용하기보다 어떤 모델을 수행하기 위한 소요시간을 줄이기 위해 데이터 차원을 축소하여 적용하는 개념이다.


n_components는 주성분의 갯수를 지정하는 속성으로 모델에서 찾을 주성분 갯수를 의미한다. 이는 클래스 생성시 최초에 무조건 지정해야 한다.

from sklearn.decomposition import PCA

# 클래스(모델) 생성
pca = PCA(n_components = 50)

# 훈련모델 생성
pca.fit(fruits_2d)

# 찾은 주성분 조회
print(pca.components_.shape)    # (50, 10000)

 

import matplotlib.pyplot as plt

def draw_fruits(arr, ratio = 1) :
    n=len(arr)
    rows = int(np.ceil(n/10))
    cols = n if rows < 2 else 10
    
    fig, axs = plt.subplots(rows, cols, 
                            figsize = (cols*ratio, rows*ratio),
                            squeeze = False)
    
    for i in range(rows) :
        for j in range(cols):
            if i*10+j < n :
                axs[i, j].imshow(arr[i*10+j], cmap='gray_r')
            axs[i, j].axis('off')
        
    plt.show()
    
draw_fruits(pca.components_.reshape(-1, 100, 100))

print(fruits_2d.shape)    # (300, 10000)

 

차원 축소하기

# 10000개를 50개로
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape)    # (300, 50)

원본 데이터 재구성

# 50개를 다시 10000개로
fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape)      # (300, 10000)


# 2d > 3d로 변환
fruits_reconstruct = fruits_inverse.reshape(-1,100,100)
print(fruits_reconstruct.shape)  # (300, 100, 100)
for start in [0, 100, 200] :
    draw_fruits(fruits_reconstruct[start : start+100])
    print('\n')

 

화질은 흐려지고 저하되었지만 잘 나타낸 것을 확인할 수 있다.

 

 

KMeans 모델에 차원축소 데이터를 적용하면

from sklearn.cluster import KMeans

km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_pca)

print(np.unique(km.labels_, return_counts=True))
# (array([0, 1, 2]), array([111,  98,  91], dtype=int64))

 

 

# 클러스터 구성 확인 -> 산점도로 시각화하여 확인
for label in range(0,3) :
    data = fruits_pca[km.labels_ == label]
    plt.scatter(data[:, 0], data[:, 1])
    
plt.legend(['apple', 'pineapple', 'banana'])
plt.show()

 

 

로지스틱 회귀 모델에 차원축소 데이터 적용해보자. 로지스틱 회귀 모델은 지도학습이기 때문에 타겟값을 만들어 주어야 한다. 행이 300개이므로 타겟도 300개를 만들어 주어야 한다. 이때, 타겟값은 행 자료 특성에 맞도록 사과, 파인애플, 바나나 각 100개씩 0, 1, 2로 생성해준다.

target = [0]*100 + [1]*100 + [2]*100

 

이해하기 쉽게 눈으로 확인하고 싶다면 데이터프레임화 하여 확인할 수 있다.

df = pd.DataFrame(fruits_2d)
df['target'] = target
df

 

교차검증으로 성능을 최대화하여 로지스틱회귀 모델에 적용해보면 성능과 훈련 소요시간을 확인할 수 있다.

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_validate

lg = LogisticRegression()

scores = cross_validate(lg, fruits_2d, target)

print(scores)
print(np.mean(scores['test_score']))   # 0.9966666666666667
print(np.mean(scores['fit_time']))     # 0.4458108425140381

 

훈련에 사용된 독립변수를 차원축소를 적용한 2차원 데이터로 변경하여 로지스틱 회귀 모델을 적용해보자. 이번엔 pca 모델을 생성할 때 n_components를 0.5로 지정한다. 이때, 0.5는 분산비로 분산비율이 50%가 되는동안 계속 찾도록 지정한다는 의미다.

pca = PCA(n_components=0.5)
pca.fit(fruits_2d)

print(pca.n_components_)   # 2

# 차원축소
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape)    # (300, 2)

해당 모델이 찾은 주성분의 갯수를 확인해보면 2개로 출력된다. 차원을 축소하면 (300,2) 형태의 2차원 데이터를 얻을 수 있다. 해당 모델로 교차검증을 진행해보자.

scores = cross_validate(lg, fruits_pca, target)

print(scores)
print(np.mean(scores['test_score']))   # 0.9933333333333334
print(np.mean(scores['fit_time']))     # 0.022984743118286133

성능은 거의 동일하게 나타났지만 훈련 소요 시간이 매우 줄어든 것을 확인할 수 있다. 주성분 분석이 성능에는 영향을 미치지 않지만 속도에는 영향을 미친다는 것을 확인할 수 있다. 이에 주성분 분석은 성능은 유지시키되 속도를 향상시키고 싶을 때 사용할 수 있는 모델임을 알 수 있다. 또한, 차원을 축소시킬 수만 있다면 누구든 사용가능한 모델이다.

Comments