본문 바로가기

데이터/머신러닝

Chapter 06-3 주성분 분석

<해당 글은 혼자 공부하는 머신러닝+딥러닝을 공부하고 정리한 내용입니다.>

1. 차원과 차원 축소

  • 특성 : 데이터가 가진 속성

 

  • 머신러닝에서는 이런 특성을 차원이라고도 부른다. 차원을 줄일 수 있으면 저장 공간을 크게 절약가능

 

  • 차원 축소 : 데이턱를 가장 잘 나타내는 일부 특성을 선택하여 데이터 크기를 줄이고 지도 학습 모델의 성능을 향상시킬 수 있는 방법

 

  • 줄어든 차원에서 다시 원본 차원으로 손실을 최대한 줄이면서 복원할 수도 있다.

 

  • 주성분 분석(PCA) : 대표적인 차원 축소 알고리즘

 

 

 

2. 주성분 분석 소개

  • 주성분  분석은 데이터에 있는 분산이 큰 방향을 찾는 것으로 이해할 수 있다.

 

  • 분산이 큰 방향을 데이터로 잘 표현하는 벡터로 생각할 수 있다. 이 벡터를 주성분이라 부른다.

 

  • 주성분 벡터는 원본 데이터에 있는 어떤 방향이므로 주성분 벡터의 원소 개수는 원본 데이터셋에 있는 특성 개수와 같다.

 

  • 하지만 원본 데이터는 주성분을 사용해 차원을 줄일 수 있다.

 

  • 첫 번째 주성분을 찾은 다음 이 벡터에 수직이고 분산이 가장 큰 다음 방향을 찾는데 그 벡터가 두 번째 주성분, 이는 2차원이므로 두 번째 주성분의 방향은 1가지

 

  • 일반적으로 주성분은 원본 특성의 개수만큼 찾을 수 있다.

 

 

 

3. PCA 클래스

import numpy as np

fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100)

from sklearn.decomposition import PCA

#pca클래스의 객체를 만들 때 n_components 매개변수에 주성분의 개수를 지정해야 한다.
pca = PCA(n_components=50)

#비지도 학습이기 때문에 fit()메서드에 타깃값을 제공하지 않는다.
pca.fit(fruits_2d)

 

#pca클래스가 찾은 주성분은 components_속성에 저장
#shape로 배열의 크기 확인
print(pca.components_.shape)

 

  • n_components=50으로 지정했기 때문에 pca.componenets_배열의 첫 번째 차원이 50으로 50개의 주성분을 찾았다.

 

  • 주성분을 그림으로 그려보기
import matplotlib.pyplot as plt

def draw_fruits(arr, ratio=1):
    n = len(arr)    # n은 샘플 개수
    # 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산
    rows = int(np.ceil(n/10))
    # 행이 1개 이면 열 개수는 샘플 개수, 그렇지 않으면 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:    # 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))

원본 데이터에서 가장 분산이 큰 방향을 순서대로 나타낸 것(데이터셋에 있는 어떤 특징을 잡아낸 것)

 

 

  • 주성분을 찾았으므로 원본 데이터를 주성분에 투영하여 특성의 개수를 10,000개에서 50개로 줄일 수 있다. 원본 데이터를 각 주성분으로 분해하는 것과 비슷하다. transform 메서드 사용
print(fruits_2d.shape)

10000개 특성의 300개 이미지

 

 

fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape)

 

 

 

4. 원본 데이터 재구성

  • 손실 발생했지만 최대한 분산이 큰 방향으로 데이터를 투영했기 때문에 원본 데이터를 상당 부분 재구성할 수 있다. 이를 위해 inverse_transform메서드 제공
fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape)

 

 

  • 복원된 10,000개의 특성을 100×100크기로 바꾸어 100개씩 나누어 출력
fruits_reconstruct = fruits_inverse.reshape(-1, 100, 100)
for start in [0, 100, 200]:
    draw_fruits(fruits_reconstruct[start:start+100])
    print("\n")

 

 

  • 거의 모든 과일이 잘 복원되었는데 그 이유는 이 50개의 특성이 분산을 가장 잘 보존하도록 변환된 것이기 때문이다. 

 

 

 

5. 설명된 분산

  • 설명된 분산 : 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값

 

  • PCA클래스의 explained_variance_ratio_에 각 주성분의 설명된 분산 비율이 기록

 

  • 당연히 첫 번째 주성분의 설명된 분산이 가장 크고 이 분산 비율을 모두 더하면 50개의 주성분으로 표현하고. 있는 총 분산 비율을 얻을 수 있다.
print(np.sum(pca.explained_variance_ratio_))

 

 

  • 92%가 넘는 분산 → 설명된 분산의 비율을 그래프로 그려 보면 적절한 주성분 개수를 찾는 데 도움이 된다. 
#설명된 분산의 그래프
plt.plot(pca.explained_variance_ratio_)

처음 10개의 주성분이 대부분의 분산 표현

 

 

 

6. 다른 알고리즘과 함께 사용하기

  • 과일 사진 원본 데이터와 PCA로 축소한 데이터를 지도 학습에 적용
  • 3개의 과일 사진을 분류하기 위해 로지스틱 회귀 모델 사용
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
target = np.array([0] * 100 + [1] * 100 + [2] * 100)

from sklearn.model_selection import cross_validate

#원본 데이터 사용, 성능 가늠을 위해 cv로 교차 검증
scores = cross_validate(lr, fruits_2d, target)
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))

과대적합, 0.46초 소요

 

 

  • pca로 축소한 fruits_pca 사용
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))

정확도 100%, 훈련시간 0.03초

 

 

  • PCA로 훈련 데이터의 차원을 축소하면 저장 공간 뿐만 아니라 머신러닝 모델의 훈련 속도도 높일 수 있다.

 

  • 앞의 PCA클래스를 사용할 때는 n_components 매개변수에 주성분의 개수를 지정했는데, 이 대신 원하는 설명된 분산의 비율을 입력할 수도 있다. PCA클래스는 지정된 비율에 도달할 때까지 자동으로 주성분을 찾는다.
#설명된 분산의 50%에 달하는 주성분을 찾도록 PCA모델 만들기
pca = PCA(n_components=0.5)
pca.fit(fruits_2d)

 

#찾은 주성분의 개수 확인
print(pca.n_components_)

 

 

  • 이 모델로 원본 데이터 변환
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape)

 

#2개의 특성을 사용한 교차 검증의 결과 확인
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))

99%의 정확도 달성

 

 

  • 차원 축소된 데이터를 사용한 k-평균 알고리즘으로 클러스터 찾기
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))

 

 

  • 과일 이미지 출력해보기
for label in range(0, 3):
    draw_fruits(fruits[km.labels_ == label])
    print("\n")

 

 

  • 훈련 데이터의 차원을 줄이면 시각화가 쉽다.

 

  • 앞에서 찾은 km.labels를 사용해 클러스터별로 나누어 산점도 그리기
for label in range(0, 3):
    data = fruits_pca[km.labels_ == label]
    plt.scatter(data[:,0], data[:,1])
plt.legend(['apple', 'banana', 'pineapple'])
plt.show()

사과와 파인애플 클러스터의 경계가 가까워 혼동할 수 있다.

 

 

 

7. 주성분 분석으로 차원 축소

  • 차원 축소를 사용하면 데이터셋의 크기를 주링ㄹ 수 있고 비교적 시각화하기 쉽다. 또 차원 축소된 데이터를 지도 학습 알고리즘이나 다른 비지도 학습 알고르짐에 재사용하여 성능을 높이거나 훈련 속도를 빠르게 만들 수 있다.

 

  • PCA 클래스를 사용해 데이터의 특성을 줄일 수 있고 이는 원본 데이터에 있는 분산의 90%이상을 표현하며 설명된 분산이라고 부른다.

 

  • PCA클래스는 자동으로 설명된 분산을 계산하여 제공하고 주성분의 개수를 명시적으로 지정하는 대신 설명된 분산의 비율을 설정하여 원하는 비율만큼 주성분을 찾을 수 있다.

 

  • PCA클래스는 변환된 데이터에서 원본 데이터를 복원하는 메서드도 제공한다.

'데이터 > 머신러닝' 카테고리의 다른 글

Chapter 06-2 k-평균 알고리즘  (0) 2022.03.15
Chapter 06-1 군집 알고리즘  (0) 2022.03.13
Chapter 05-3 트리의 앙상블  (0) 2022.03.12
Chapter 05-2 교차검증과 그리드 서치  (0) 2022.03.10
Chapter 05-1 결정 트리  (0) 2022.03.10